diff --git a/.config/configuration.vsEnterprise.winget b/.config/configuration.vsEnterprise.winget new file mode 100644 index 0000000000..589244063d --- /dev/null +++ b/.config/configuration.vsEnterprise.winget @@ -0,0 +1,43 @@ +# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 +# Reference: https://github.com/microsoft/terminal/blob/main/README.md#developer-guidance +properties: + resources: + - resource: Microsoft.Windows.Settings/WindowsSettings + directives: + description: Enable Developer Mode + allowPrerelease: true + # Requires elevation for the set operation + securityContext: elevated + settings: + DeveloperMode: true + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: powershell + directives: + description: Install PowerShell 7 + # Requires elevation for the set operation (i.e., installation) + securityContext: elevated + settings: + id: Microsoft.PowerShell + source: winget + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: vsPackage + directives: + description: Install Visual Studio 2022 Enterprise (any edition is OK) + # Requires elevation for the set operation (i.e., installation) + securityContext: elevated + settings: + id: Microsoft.VisualStudio.2022.Enterprise + source: winget + - resource: Microsoft.VisualStudio.DSC/VSComponents + dependsOn: + - vsPackage + directives: + description: Install required VS workloads from project .vsconfig file + allowPrerelease: true + # Requires elevation for the get and set operations + securityContext: elevated + settings: + productId: Microsoft.VisualStudio.Product.Enterprise + channelId: VisualStudio.17.Release + vsConfigFile: '${WinGetConfigRoot}\..\.vsconfig' + configurationVersion: 0.2.0 diff --git a/.config/configuration.vsProfessional.winget b/.config/configuration.vsProfessional.winget new file mode 100644 index 0000000000..549b6227bb --- /dev/null +++ b/.config/configuration.vsProfessional.winget @@ -0,0 +1,43 @@ +# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 +# Reference: https://github.com/microsoft/terminal/blob/main/README.md#developer-guidance +properties: + resources: + - resource: Microsoft.Windows.Settings/WindowsSettings + directives: + description: Enable Developer Mode + allowPrerelease: true + # Requires elevation for the set operation + securityContext: elevated + settings: + DeveloperMode: true + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: powershell + directives: + description: Install PowerShell 7 + # Requires elevation for the set operation (i.e., installation) + securityContext: elevated + settings: + id: Microsoft.PowerShell + source: winget + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: vsPackage + directives: + description: Install Visual Studio 2022 Professional (any edition is OK) + # Requires elevation for the set operation (i.e., installation) + securityContext: elevated + settings: + id: Microsoft.VisualStudio.2022.Professional + source: winget + - resource: Microsoft.VisualStudio.DSC/VSComponents + dependsOn: + - vsPackage + directives: + description: Install required VS workloads from project .vsconfig file + allowPrerelease: true + # Requires elevation for the get and set operations + securityContext: elevated + settings: + productId: Microsoft.VisualStudio.Product.Professional + channelId: VisualStudio.17.Release + vsConfigFile: '${WinGetConfigRoot}\..\.vsconfig' + configurationVersion: 0.2.0 diff --git a/.config/configuration.winget b/.config/configuration.winget new file mode 100644 index 0000000000..5d7823a002 --- /dev/null +++ b/.config/configuration.winget @@ -0,0 +1,43 @@ +# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 +# Reference: https://github.com/microsoft/terminal/blob/main/README.md#developer-guidance +properties: + resources: + - resource: Microsoft.Windows.Settings/WindowsSettings + directives: + description: Enable Developer Mode + allowPrerelease: true + # Requires elevation for the set operation + securityContext: elevated + settings: + DeveloperMode: true + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: powershell + directives: + description: Install PowerShell 7 + # Requires elevation for the set operation (i.e., installation) + securityContext: elevated + settings: + id: Microsoft.PowerShell + source: winget + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: vsPackage + directives: + description: Install Visual Studio 2022 Community (any edition is OK) + # Requires elevation for the set operation (i.e., installation) + securityContext: elevated + settings: + id: Microsoft.VisualStudio.2022.Community + source: winget + - resource: Microsoft.VisualStudio.DSC/VSComponents + dependsOn: + - vsPackage + directives: + description: Install required VS workloads from project .vsconfig file + allowPrerelease: true + # Requires elevation for the get and set operations + securityContext: elevated + settings: + productId: Microsoft.VisualStudio.Product.Community + channelId: VisualStudio.17.Release + vsConfigFile: '${WinGetConfigRoot}\..\.vsconfig' + configurationVersion: 0.2.0 diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 4479fcd487..1ee6bf9359 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "XamlStyler.Console": { - "version": "3.2311.2", + "version": "3.2501.8", "commands": [ "xstyler" ] diff --git a/.github/actions/spelling/allow/allow.txt b/.github/actions/spelling/allow/allow.txt index 15717467d5..a99422e8b0 100644 --- a/.github/actions/spelling/allow/allow.txt +++ b/.github/actions/spelling/allow/allow.txt @@ -11,33 +11,25 @@ colorbrewer commandlines consvc copyable -dalet dcs deselection -dialytika diffing Dimidium -dje downsides -dze -dzhe Emacspeak Fitt -FTCS flac +FTCS gantt gfm ghe -gje godbolt hstrings hyperlinking hyperlinks Kbds -kje libfuzzer liga -lje Llast Lmid locl @@ -49,10 +41,8 @@ minimalistic mkmk mnt mru -nje notwrapped NTMTo -ogonek overlined perlw postmodern @@ -60,8 +50,8 @@ Powerline ptys pwn pwshw -QOL qof +QOL qps quickfix rclt @@ -74,17 +64,13 @@ rlig rubyw runtimes servicebus -shcha -similaritytolerance slnt stakeholders subpage sustainability sxn -TLDR -tonos +Tencent toolset -tshe UEFI uiatextrange und @@ -92,8 +78,5 @@ vsdevcmd westus workarounds WSLs -wtconfig XBox YBox -yeru -zhe diff --git a/.github/actions/spelling/allow/apis.txt b/.github/actions/spelling/allow/apis.txt index f7671d04f4..e1307fcfd8 100644 --- a/.github/actions/spelling/allow/apis.txt +++ b/.github/actions/spelling/allow/apis.txt @@ -2,11 +2,8 @@ aalt abvm ACCEPTFILES ACCESSDENIED -acl -aclapi alignas alignof -allocconsolewithoptions APPLYTOSUBMENUS appxrecipe bitfield @@ -15,29 +12,20 @@ BUILDBRANCH BUILDMSG BUILDNUMBER BYCOMMAND -BYPOSITION charconv -CLASSNOTAVAILABLE CLOSEAPP cmdletbinding -COLORPROPERTY colspan COMDLG commandlinetoargv -commoncontrols -comparand COPYFROMRESOURCE cstdint CXICON CYICON -Dacl dataobject -dcomp debugbreak delayimp -DERR dlldata -DNE dnom DONTADDTORECENT DWMSBT @@ -64,90 +52,51 @@ GETHIGHCONTRAST GETMOUSEHOVERTIME GETTEXTLENGTH HARDBREAKS -Hashtable HIGHCONTRASTON HIGHCONTRASTW HIGHQUALITYSCALE hinternet -HINTERNET hotkeys href hrgn HTCLOSE hwinsta -HWINSTA -IActivation -IApp IAppearance -IAsync -IBind -IBox -IClass -IComparable -IComparer ICONINFO -IConnection -ICustom -IDialog IDirect -Idn -IExplorer -IFACEMETHOD -IFile -IGraphics -IImage IInheritable -IMap imm -IObject iosfwd -IPackage isa -ISetup isspace -IStorage istream -IStringable -ITab -ITaskbar -itow -IUri -IVirtual KEYSELECT LCID LINEBREAK -llabs -llu -localtime lround Lsa lsass LSHIFT LTGRAY MAINWINDOW -MAXIMIZEBOX medi -memchr memicmp MENUCOMMAND MENUDATA MENUINFO MENUITEMINFOW MINIMIZEBOX -mmeapi MOUSELEAVE mov -mptt -msappx MULTIPLEUSE NCHITTEST NCLBUTTONDBLCLK NCMOUSELEAVE NCMOUSEMOVE +NCPOINTERUPDATE NCRBUTTONDBLCLK NIF NIN -NOAGGREGATION NOASYNC NOBREAKS NOCHANGEDIR @@ -160,8 +109,6 @@ NOTIFYICONDATA ntprivapi NTSYSCALLAPI numr -oaidl -ocidl ODR offsetof ofstream @@ -171,22 +118,17 @@ OSVERSIONINFOEXW otms OUTLINETEXTMETRICW overridable -PACL PAGESCROLL PALLOC PATINVERT -PEXPLICIT PICKFOLDERS PINPUT pmr -ptstr QUERYENDSESSION rcx REGCLS RETURNCMD rfind -RLO -rnrn ROOTOWNER roundf RSHIFT @@ -205,23 +147,19 @@ SHOWTIP SINGLEUSE SIZENS smoothstep -snprintf SOFTBREAK spsc -sregex SRWLOC srwlock SRWLOCK STDCPP STDMETHOD -strchr strcpy streambuf strtoul Stubless -Subheader -Subpage syscall +syscolors SYSTEMBACKDROP TABROW TASKBARCREATED @@ -236,23 +174,18 @@ tokeninfo tolower toupper TRACKMOUSEEVENT -TTask -TVal +ubrk UChar UFIELD ULARGE UOI UPDATEINIFILE urlmon -userenv USEROBJECTFLAGS Vcpp Viewbox virtualalloc -vsnwprintf wcsnlen -wcsstr -wcstoui WDJ winhttp wininet @@ -262,10 +195,8 @@ winstamin wmemcmp wpc WSF -wsregex WWH wwinmain -xchg XDocument XElement xfacet @@ -289,4 +220,5 @@ xtree xutility YIcon YMax +zstring zwstring diff --git a/.github/actions/spelling/allow/chinese.txt b/.github/actions/spelling/allow/chinese.txt index 6b7ca2a917..02ed0e4bae 100644 --- a/.github/actions/spelling/allow/chinese.txt +++ b/.github/actions/spelling/allow/chinese.txt @@ -1,5 +1,4 @@ CHINESEBIG choseong -Jongseong -Jungseong -ssangtikeut +Choseong +CHOSEONG diff --git a/.github/actions/spelling/allow/colors.txt b/.github/actions/spelling/allow/colors.txt index b56723b368..2b7192cf5b 100644 --- a/.github/actions/spelling/allow/colors.txt +++ b/.github/actions/spelling/allow/colors.txt @@ -1,4 +1,3 @@ -alice aliceblue antiquewhite blanchedalmond @@ -39,7 +38,6 @@ gainsboro ghostwhite greenyellow hotpink -indian indianred lavenderblush lawngreen @@ -74,7 +72,6 @@ mediumvioletred midnightblue mintcream mistyrose -navajo navajowhite navyblue oldlace @@ -88,7 +85,6 @@ papayawhip peachpuff peru powderblue -rebecca rebeccapurple rosybrown royalblue @@ -109,9 +105,4 @@ webgrey webmaroon webpurple whitesmoke -xaroon -xray -xreen -xrey -xurple yellowgreen diff --git a/.github/actions/spelling/allow/fonts.txt b/.github/actions/spelling/allow/fonts.txt index c9931b2fc0..a0bacd0600 100644 --- a/.github/actions/spelling/allow/fonts.txt +++ b/.github/actions/spelling/allow/fonts.txt @@ -1,8 +1,8 @@ Consolas emoji emojis +Emojis Extralight -Gabriola Iosevka MDL Monofur diff --git a/.github/actions/spelling/allow/japanese.txt b/.github/actions/spelling/allow/japanese.txt index 261f45dc2a..6d941c08a5 100644 --- a/.github/actions/spelling/allow/japanese.txt +++ b/.github/actions/spelling/allow/japanese.txt @@ -1,4 +1 @@ -arigatoo -doomo -Kaomojis TATEGAKI diff --git a/.github/actions/spelling/allow/math.txt b/.github/actions/spelling/allow/math.txt index bf8960f008..8c6041d465 100644 --- a/.github/actions/spelling/allow/math.txt +++ b/.github/actions/spelling/allow/math.txt @@ -1,11 +1,2 @@ -atan -CPrime -HBar -HPrime isnan -LPrime -LStep -powf -RSub -sqrtf ULP diff --git a/.github/actions/spelling/allow/microsoft.txt b/.github/actions/spelling/allow/microsoft.txt index 940b0fc29d..f43408388b 100644 --- a/.github/actions/spelling/allow/microsoft.txt +++ b/.github/actions/spelling/allow/microsoft.txt @@ -4,12 +4,11 @@ advapi akv AKV altform -altforms +Altforms appendwttlogging appinstaller appx appxbundle -appxerror appxmanifest ATL autoexec @@ -25,66 +24,44 @@ CPRs cryptbase cscript DACL -DACLs defaultlib diffs disposables -dotnetfeed -DTDs -DWINRT enablewttlogging HOMESHARE Intelli issecret -IVisual libucrt libucrtd -LKG LOCKFILE +LTCG Lxss makepri -mfcribbon microsoft -microsoftonline MSAA msixbundle MSVC MSVCP mtu muxc -netcore -Onefuzz -osgvsowi -PFILETIME pgc pgo pgosweep -powerrename powershell priconfig PRIINFO propkey pscustomobject QWORD -rdpclip regedit resfiles -robocopy SACLs -sdkddkver segoe -Shobjidl sid -Skype SRW sxs symbolrequestprod Sysinternals -sysnative -systemroot -taskkill -tasklist -tdbuildteamid ucrt ucrtd unvirtualized @@ -92,12 +69,9 @@ USERDNSDOMAIN VCRT vcruntime Virtualization -visualstudio vscode VSTHRD WINBASEAPI -winsdkver -wlk wscript wslpath wtl diff --git a/.github/actions/spelling/allow/names.txt b/.github/actions/spelling/allow/names.txt index 11a1f95486..1b52d13a22 100644 --- a/.github/actions/spelling/allow/names.txt +++ b/.github/actions/spelling/allow/names.txt @@ -1,4 +1,3 @@ -Anup arkthur austdi Ballmer @@ -6,13 +5,11 @@ bhoj Bhojwani Bluloco carlos -craigloewen dhowett Diviness dsafa duhowett DXP -ekg eryksun ethanschoonover Firefox @@ -25,70 +22,43 @@ Hernan Howett Illhardt Imms -iquilezles italo jantari jerrysh Kaiyu -kimwalisch -KMehrain -Kodelife -KODELIFE -Kourosh -kowalczyk leonardder -leonmsft -Lepilleur lhecker -lukesampson -Macbook -Manandhar +Lovecraft masserano -mbadolato -Mehrain menger -mgravell -michaelniksa -michkap migrie mikegr mikemaccana -miloush miniksa nguyen niksa nvaccess nvda -oising -oldnewthing -opengl osgwiki Ottosson pabhojwa -panos +Panos paulcam -pauldotknopf PGP Pham Rincewind -rprichard Schoonover shadertoy Shomnipotence simioni -Somuah sonph sonpham stakx -talo thereses Thysell -Walisch WDX Wellons Westerman -Wirt -Wojciech zadjii Zamor zamora @@ -96,4 +66,3 @@ Zamora zljubisic Zoey zorio -Zverovich diff --git a/.github/actions/spelling/candidate.patterns b/.github/actions/spelling/candidate.patterns index 5d93035f91..d405383514 100644 --- a/.github/actions/spelling/candidate.patterns +++ b/.github/actions/spelling/candidate.patterns @@ -1,3 +1,6 @@ +# Repeated letters +\b([a-z])\g{-1}{2,}\b + # marker to ignore all code on line ^.*/\* #no-spell-check-line \*/.*$ # marker to ignore all code on line @@ -7,6 +10,9 @@ # cspell inline ^.*\b[Cc][Ss][Pp][Ee][Ll]{2}:\s*[Dd][Ii][Ss][Aa][Bb][Ll][Ee]-[Ll][Ii][Nn][Ee]\b +# copyright +Copyright (?:\([Cc]\)|)(?:[-\d, ]|and)+(?: [A-Z][a-z]+ [A-Z][a-z]+,?)+ + # patch hunk comments ^@@ -\d+(?:,\d+|) \+\d+(?:,\d+|) @@ .* # git index header @@ -15,6 +21,9 @@ index (?:[0-9a-z]{7,40},|)[0-9a-z]{7,40}\.\.[0-9a-z]{7,40} # file permissions ['"`\s][-bcdLlpsw](?:[-r][-w][-Ssx]){2}[-r][-w][-SsTtx]\+?['"`\s] +# css fonts +\bfont(?:-family|):[^;}]+ + # css url wrappings \burl\([^)]+\) @@ -32,9 +41,6 @@ index (?:[0-9a-z]{7,40},|)[0-9a-z]{7,40}\.\.[0-9a-z]{7,40} # https/http/file urls (?:\b(?:https?|ftp|file)://)[-A-Za-z0-9+&@#/*%?=~_|!:,.;]+[-A-Za-z0-9+&@#/*%=~_|] -# https/http/file urls -(?:\b(?:https?|ftp|file)://)[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|] - # mailto urls mailto:[-a-zA-Z=;:/?%&0-9+@._]{3,} @@ -69,6 +75,8 @@ magnet:[?=:\w]+ # Amazon \bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|) +# AWS ARN +arn:aws:[-/:\w]+ # AWS S3 \b\w*\.s3[^.]*\.amazonaws\.com/[-\w/&#%_?:=]* # AWS execute-api @@ -95,6 +103,8 @@ vpc-\w+ \bgoogle-analytics\.com/collect.[-0-9a-zA-Z?%=&_.~]* # Google APIs \bgoogleapis\.(?:com|dev)/[a-z]+/(?:v\d+/|)[a-z]+/[-@:./?=\w+|&]+ +# Google Artifact Registry +\.pkg\.dev(?:/[-\w]+)+(?::[-\w]+|) # Google Storage \b[-a-zA-Z0-9.]*\bstorage\d*\.googleapis\.com(?:/\S*|) # Google Calendar @@ -130,6 +140,8 @@ themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+. \bscholar\.google\.com/citations\?user=[A-Za-z0-9_]+ # Google Colab Research Drive \bcolab\.research\.google\.com/drive/[-0-9a-zA-Z_?=]* +# Google Cloud regions +(?:us|(?:north|south)america|europe|asia|australia|me|africa)-(?:north|south|east|west|central){1,2}\d+ # GitHub SHAs (api) \bapi.github\.com/repos(?:/[^/\s"]+){3}/[0-9a-f]+\b @@ -168,6 +180,12 @@ GHSA(?:-[0-9a-z]{4}){3} # GitLab commits \bgitlab\.[^/\s"]*/(?:[^/\s"]+/){2}commits?/[0-9a-f]+\b +# #includes +^\s*#include\s*(?:<.*?>|".*?") + +# #pragma lib +^\s*#pragma comment\(lib, ".*?"\) + # binance accounts\.binance\.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]* @@ -220,7 +238,7 @@ accounts\.binance\.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]* \bmedium\.com/@?[^/\s"]+/[-\w]+ # microsoft -\b(?:https?://|)(?:(?:download\.visualstudio|docs|msdn2?|research)\.microsoft|blogs\.msdn)\.com/[-_a-zA-Z0-9()=./%]* +\b(?:https?://|)(?:(?:(?:blogs|download\.visualstudio|docs|msdn2?|research)\.|)microsoft|blogs\.msdn)\.co(?:m|\.\w\w)/[-_a-zA-Z0-9()=./%]* # powerbi \bapp\.powerbi\.com/reportEmbed/[^"' ]* # vs devops @@ -394,7 +412,7 @@ ipfs://[0-9a-zA-Z]{3,} \bgetopts\s+(?:"[^"]+"|'[^']+') # ANSI color codes -(?:\\(?:u00|x)1[Bb]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+|)m +(?:\\(?:u00|x)1[Bb]|\\03[1-7]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+)*m # URL escaped characters %[0-9A-F][A-F](?=[A-Za-z]) @@ -431,10 +449,14 @@ sha\d+:[0-9a-f]*?[a-f]{3,}[0-9a-f]* # pki (base64) LS0tLS1CRUdJT.* +# C# includes +^\s*using [^;]+; + # uuid: \b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b # hex digits including css/html color classes: -(?:[\\0][xX]|\\u|[uU]\+|#x?|%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b +(?:[\\0][xX]|\\u|[uU]\+|#x?|%23|&H)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b + # integrity integrity=(['"])(?:\s*sha\d+-[-a-zA-Z=;:/0-9+]{40,})+\g{-1} @@ -452,7 +474,10 @@ integrity=(['"])(?:\s*sha\d+-[-a-zA-Z=;:/0-9+]{40,})+\g{-1} Name\[[^\]]+\]=.* # IServiceProvider / isAThing -(?:\b|_)(?:(?:ns|)I|isA)(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b)) +(?:(?:\b|_|(?<=[a-z]))I|(?:\b|_)(?:nsI|isA))(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b)) + +# python +\b(?i)py(?!gments|gmy|lon|ramid|ro|th)(?=[a-z]{2,}) # crypt (['"])\$2[ayb]\$.{56}\g{-1} @@ -466,17 +491,14 @@ Name\[[^\]]+\]=.* # machine learning (?) \b(?i)ml(?=[a-z]{2,}) -# python -\b(?i)py(?!gments|gmy|lon|ramid|ro|th)(?=[a-z]{2,}) - # scrypt / argon \$(?:scrypt|argon\d+[di]*)\$\S+ # go.sum \bh1:\S+ -# scala imports -^import (?:[\w.]|\{\w*?(?:,\s*(?:\w*|\*))+\})+ +# imports +^import\s+(?:(?:static|type)\s+|)(?:[\w.]|\{\s*\w*?(?:,\s*(?:\w*|\*))+\s*\})+ # scala modules ("[^"]+"\s*%%?\s*){2,3}"[^"]+" @@ -485,7 +507,7 @@ Name\[[^\]]+\]=.* image: [-\w./:@]+ # Docker images -^\s*FROM\s+\S+:\S+(?:\s+AS\s+\S+|) +^\s*(?i)FROM\s+\S+:\S+(?:\s+AS\s+\S+|) # `docker images` REPOSITORY TAG IMAGE ID CREATED SIZE \s*\S+/\S+\s+\S+\s+[0-9a-f]{8,}\s+\d+\s+(?:hour|day|week)s ago\s+[\d.]+[KMGT]B @@ -501,6 +523,7 @@ content: (['"])[-a-zA-Z=;:/0-9+]*=\g{-1} # The `(?=.*?")` suffix should limit the false positives rate # printf #%(?:(?:(?:hh?|ll?|[jzt])?[diuoxn]|l?[cs]|L?[fega]|p)(?=[a-z]{2,})|(?:X|L?[FEGA])(?=[a-zA-Z]{2,}))(?!%)(?=[_a-zA-Z]+(?!%)\b)(?=.*?['"]) + # Alternative printf # %s %(?:s(?=[a-z]{2,}))(?!%)(?=[_a-zA-Z]+(?!%[^s])\b)(?=.*?['"]) @@ -524,7 +547,7 @@ content: (['"])[-a-zA-Z=;:/0-9+]*=\g{-1} # javascript replace regex \.replace\(/[^/\s"]{3,}/[gim]*\s*, # assign regex -= /[^*].*?(?:[a-z]{3,}|[A-Z]{3,}|[A-Z][a-z]{2,}).*/[gi]?(?=\W|$) += /[^*].*?(?:[a-z]{3,}|[A-Z]{3,}|[A-Z][a-z]{2,}).*/[gim]*(?=\W|$) # perl regex test [!=]~ (?:/.*/|m\{.*?\}|m<.*?>|m([|!/@#,;']).*?\g{-1}) @@ -538,7 +561,7 @@ perl(?:\s+-[a-zA-Z]\w*)+ (?:\d|\bh)to(?!ken)(?=[a-z])|to(?=[adhiklpun]\() # Go regular expressions -regexp?\.MustCompile\(`[^`]*`\) +regexp?\.MustCompile\((?:`[^`]*`|".*"|'.*')\) # regex choice \(\?:[^)]+\|[^)]+\) @@ -586,7 +609,7 @@ urn:shemas-jetbrains-com # xcode # xcodeproject scenes -(?:Controller|destination|ID|id)="\w{3}-\w{2}-\w{3}" +(?:Controller|destination|(?:first|second)Item|ID|id)="\w{3}-\w{2}-\w{3}" # xcode api botches customObjectInstantitationMethod @@ -601,14 +624,17 @@ PrependWithABINamepsace \.fa-[-a-z0-9]+ # bearer auth -(['"])[Bb]ear[e][r] .*?\g{-1} +(['"])[Bb]ear[e][r] .{3,}?\g{-1} # bearer auth -\b[Bb]ear[e][r]:? [-a-zA-Z=;:/0-9+.]+ +\b[Bb]ear[e][r]:? [-a-zA-Z=;:/0-9+.]{3,} # basic auth (['"])[Bb]asic [-a-zA-Z=;:/0-9+]{3,}\g{-1} +# basic auth +: [Bb]asic [-a-zA-Z=;:/0-9+.]{3,} + # base64 encoded content #([`'"])[-a-zA-Z=;:/0-9+]{3,}=\g{-1} # base64 encoded content in xml/sgml @@ -620,6 +646,9 @@ PrependWithABINamepsace # base64 encoded pkcs #\bMII[-a-zA-Z=;:/0-9+]+ +# uuencoded +#[!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_]{40,} + # DNS rr data #(?:\d+\s+){3}(?:[-+/=.\w]{2,}\s*){1,2} @@ -630,7 +659,7 @@ PrependWithABINamepsace \bnumer\b(?=.*denom) # Time Zones -\b(?:Africa|Atlantic|America|Antarctica|Asia|Australia|Europe|Indian|Pacific)(?:/\w+)+ +\b(?:Africa|Atlantic|America|Antarctica|Arctic|Asia|Australia|Europe|Indian|Pacific)(?:/[-\w]+)+ # linux kernel info ^(?:bugs|flags|Features)\s+:.* @@ -676,11 +705,17 @@ TeX/AMS "varsIgnorePattern": ".+" # nolint -nolint:\w+ +nolint:\s*[\w,]+ # Windows short paths [/\\][^/\\]{5,6}~\d{1,2}(?=[/\\]) +# Windows Resources with accelerators +\b[A-Z]&[a-z]+\b(?!;) + +# signed off by +(?i)Signed-off-by: .* + # cygwin paths /cygdrive/[a-zA-Z]/(?:Program Files(?: \(.*?\)| ?)(?:/[-+.~\\/()\w ]+)*|[-+.~\\/()\w])+ @@ -715,29 +750,29 @@ W/"[^"]+" # Compiler flags (Unix, Java/Scala) # Use if you have things like `-Pdocker` and want to treat them as `docker` -#(?:^|[\t ,>"'`=(])-(?:(?:J-|)[DPWXY]|[Llf])(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}) +#(?:^|[\t ,>"'`=(#])-(?:(?:J-|)[DPWXY]|[Llf])(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}) # Compiler flags (Windows / PowerShell) # This is a subset of the more general compiler flags pattern. # It avoids matching `-Path` to prevent it from being treated as `ath` -#(?:^|[\t ,"'`=(])-(?:[DPL](?=[A-Z]{2,})|[WXYlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})) +#(?:^|[\t ,"'`=(#])-(?:[DPL](?=[A-Z]{2,})|[WXYlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})) # Compiler flags (linker) ,-B # libraries -#(?:\b|_)lib(?:re(?=office)|)(?!era[lt]|ero|erty|rar(?:i(?:an|es)|y))(?=[a-z]) - -# WWNN/WWPN (NAA identifiers) -\b(?:0x)?10[0-9a-f]{14}\b|\b(?:0x|3)?[25][0-9a-f]{15}\b|\b(?:0x|3)?6[0-9a-f]{31}\b +#(?:\b|_)[Ll]ib(?:re(?=office)|)(?!era[lt]|ero|erty|rar(?:i(?:an|es)|y))(?=[a-z]) # iSCSI iqn (approximate regex) \biqn\.[0-9]{4}-[0-9]{2}(?:[\.-][a-z][a-z0-9]*)*\b +# WWNN/WWPN (NAA identifiers) +\b(?:0x)?10[0-9a-f]{14}\b|\b(?:0x|3)?[25][0-9a-f]{15}\b|\b(?:0x|3)?6[0-9a-f]{31}\b + # curl arguments \b(?:\\n|)curl(?:\.exe|)(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)* # set arguments -\b(?:bash|sh|set)(?:\s+-[abefimouxE]{1,2})*\s+-[abefimouxE]{3,}(?:\s+-[abefimouxE]+)* +\b(?:bash|sh|set)(?:\s+[-+][abefimouxE]{1,2})*\s+[-+][abefimouxE]{3,}(?:\s+[-+][abefimouxE]+)* # tar arguments \b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+ # tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long... diff --git a/.github/actions/spelling/excludes.txt b/.github/actions/spelling/excludes.txt index 896211b8f1..23511535fb 100644 --- a/.github/actions/spelling/excludes.txt +++ b/.github/actions/spelling/excludes.txt @@ -97,7 +97,7 @@ Resources/(?!en) ^doc/reference/UTF8-torture-test\.txt$ ^doc/reference/windows-terminal-logo\.ans$ ^NOTICE.md -^oss/ +^oss/.*?/ ^samples/PixelShaders/Screenshots/ ^src/cascadia/TerminalSettingsEditor/SegoeFluentIconList.h$ ^src/interactivity/onecore/BgfxEngine\. @@ -121,8 +121,8 @@ Resources/(?!en) ^tools/ReleaseEngineering/ServicingPipeline\.ps1$ ^XamlStyler\.json$ ^\.github/actions/spelling/ +^\.github/workflows/spelling\d*\.yml$ ^\.vsconfig$ -^\Q.github/workflows/spelling.yml\E$ ^\Qbuild/config/release.gdnbaselines\E$ ^\Qdep/WinAppDriver/EULA.rtf\E$ ^\Qdoc/reference/windows-terminal-logo.ans\E$ @@ -133,3 +133,4 @@ Resources/(?!en) ^\Qsrc/terminal/parser/ft_fuzzwrapper/run.bat\E$ ^\Qsrc/tools/lnkd/lnkd.bat\E$ ^\Qsrc/tools/pixels/pixels.bat\E$ +^\Qsrc/cascadia/ut_app/FzfTests.cpp\E$ diff --git a/.github/actions/spelling/expect/alphabet.txt b/.github/actions/spelling/expect/alphabet.txt index 1ff5611c38..73bbdffb3c 100644 --- a/.github/actions/spelling/expect/alphabet.txt +++ b/.github/actions/spelling/expect/alphabet.txt @@ -1,38 +1,19 @@ -AAAAAABBBBBBCCC -AAAAABBBBBBCCC -abcd -ABCDEFGHIJ -abcdefghijk -ABCDEFGHIJKLMNOPQRS -ABCDEFGHIJKLMNOPQRST -ABCDEFGHIJKLMNOPQRSTUVWXY ABCG ABE -abf -BBBBB -BBBBBCCC -BBBBCCCCC +AZZ +BBDM BBGGRR -EFG +CBN +cbt +Ccc +cch efg -EFGh efgh -KLMNOQQQQQQQQQQ -QQQQQQQQQQABCDEFGHIJ -QQQQQQQQQQABCDEFGHIJKLMNOPQRS -QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQ -QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ -QQQQQQQQQQABCDEFGHIJPQRST -QQQQQQQQQQABCDEFGHIJPQRSTQQQQQQQQQQ +fdw +fesb +ffd +FFFD qwerty qwertyuiopasdfg -ZAAZZ -ZABBZ -ZBAZZ -ZBBBZ -ZBBZZ ZYXWVUT -ZZBBZ -ZZZBB -ZZZBZ -ZZZZZ +zzf diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index e78cb97139..949c53b6d1 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -2,7 +2,7 @@ aaaaabbb aabbcc ABANDONFONT abbcc -ABCF +abcc abgr ABORTIFHUNG ACCESSTOKEN @@ -26,13 +26,11 @@ AImpl AInplace ALIGNRIGHT allocing -allocs alpc ALTERNATENAME ALTF ALTNUMPAD ALWAYSTIP -aml ansicpg ANSISYS ANSISYSRC @@ -49,22 +47,17 @@ APIENTRY apiset APPBARDATA appcontainer -appium appletname -applicationmodel APPLMODAL Applocal appmodel +appshellintegration APPWINDOW APPXMANIFESTVERSION APrep -apsect APSTUDIO -archeologists -Argb ARRAYSIZE ARROWKEYS -asan ASBSET ASetting ASingle @@ -72,11 +65,11 @@ ASYNCDONTCARE ASYNCWINDOWPOS atch ATest +atg aumid Authenticode AUTOBUDDY AUTOCHECKBOX -autocrlf autohide AUTOHSCROLL automagically @@ -91,10 +84,8 @@ AZZ backgrounded Backgrounder backgrounding -backported backstory Bazz -bbb bbccb BBDM bbwe @@ -122,12 +113,13 @@ bitmasks BITOPERATION BKCOLOR BKGND +BKMK Bksp Blt blu BLUESCROLL bmi -BODGY +bodgy BOLDFONT Borland boutput @@ -146,9 +138,7 @@ buflen buildsystems buildtransitive BValue -bytebuffer -cac -cacafire +Cacafire CALLCONV CANDRABINDU capslock @@ -159,12 +149,6 @@ catid cazamor CBash cbiex -CBN -cbt -Ccc -CCCBB -CCCDDD -cch CCHAR CCmd ccolor @@ -179,15 +163,14 @@ cfie cfiex cfte CFuzz +cgmanifest cgscrn chafa changelists CHARSETINFO -chh chshdng CHT CLASSSTRING -CLE cleartype CLICKACTIVE clickdown @@ -210,9 +193,7 @@ cmw CNL cnn Codeflow -codenav codepages -codepath coinit colorizing COLORONCOLOR @@ -224,13 +205,8 @@ colortbl colortest colortool COLORVALUE -combaseapi comctl -commandline -commctrl commdlg -COMMITID -componentization conapi conattrs conbufferout @@ -248,7 +224,6 @@ conintegrityuwp coninteractivitybase coninteractivityonecore coninteractivitywin -conio coniosrv CONKBD conlibk @@ -261,13 +236,13 @@ conpropsp conpty conptylib conserv +consoleaccessibility consoleapi CONSOLECONTROL CONSOLEENDTASK consolegit consolehost CONSOLEIME -consoleinternal CONSOLESETFOREGROUND consoletaeftemplates consoleuwp @@ -275,7 +250,6 @@ Consolewait CONSOLEWINDOWOWNER consrv constexprable -constness contentfiles conterm contsf @@ -309,7 +283,6 @@ csbi csbiex CSHORT Cspace -csrmsg CSRSS csrutil CSTYLE @@ -366,16 +339,14 @@ DBGFONTS DBGOUTPUT dbh dblclk +DBUILD Dcd DColor -dcommon +DCOMMON DComposition -dde -DDDCCC DDESHARE DDevice DEADCHAR -dealloc Debian debugtype DECAC @@ -480,18 +451,16 @@ DELAYLOAD DELETEONRELEASE depersist deprioritized -deserializers -desktopwindowxamlsource devicecode Dext DFactory DFF dialogbox +DINLINE directio DIRECTX DISABLEDELAYEDEXPANSION DISABLENOSCROLL -DISPATCHNOTIFY DISPLAYATTRIBUTE DISPLAYCHANGE distros @@ -528,9 +497,10 @@ dsm dsound DSSCL DSwap -DTest DTo DTTERM +DUNICODE +DUNIT dup'ed dvi dwl @@ -540,8 +510,6 @@ dwmapi DWORDs dwrite dxgi -dxgidwm -dxinterop dxsm dxttbmp Dyreen @@ -555,7 +523,6 @@ EDITKEYS EDITTEXT EDITUPDATE Efast -efghijklmn EHsc EINS ELEMENTNOTAVAILABLE @@ -564,7 +531,6 @@ enabledelayedexpansion ENDCAP endptr ENTIREBUFFER -entrypoints ENU ENUMLOGFONT ENUMLOGFONTEX @@ -579,7 +545,6 @@ esrp ESV ETW EUDC -EVENTID eventing evflags evt @@ -601,16 +566,6 @@ FACESIZE FAILIFTHERE fastlink fcharset -FDEA -fdw -FECF -FEEF -fesb -FFAF -ffd -FFDE -FFFD -FFFDb fgbg FGCOLOR FGHIJ @@ -628,7 +583,6 @@ FINDDLG FINDDOWN FINDREGEX FINDSTRINGEXACT -FINDUP FITZPATRICK FIXEDFILEINFO Flg @@ -649,6 +603,7 @@ FONTSTRING FONTTYPE FONTWIDTH FONTWINDOW +foob FORCEOFFFEEDBACK FORCEONFEEDBACK FRAMECHANGED @@ -662,13 +617,14 @@ Ftm Fullscreens Fullwidth FUNCTIONCALL -fuzzer fuzzmain fuzzmap fuzzwrapper +fuzzyfinder fwdecl fwe fwlink +fzf gci gcx gdi @@ -724,6 +680,7 @@ GETWHEELSCROLLCHARS GETWHEELSCROLLLINES Gfun gfx +gfycat GGI GHgh GHIJK @@ -744,6 +701,7 @@ Greyscale gridline gset gsl +Guake guc GUIDATOM GValue @@ -773,7 +731,6 @@ hfind hfont hfontresource hglobal -hhh hhook hhx HIBYTE @@ -789,7 +746,6 @@ HKCU hkey hkl HKLM -hlocal hlsl HMB HMK @@ -808,7 +764,6 @@ HREDRAW hresult hscroll hstr -hstring HTBOTTOMLEFT HTBOTTOMRIGHT HTCAPTION @@ -837,14 +792,12 @@ idl idllib IDOK IDR -idth IDTo IDXGI IFACEMETHODIMP ification IGNORELANGUAGE iid -IInput IIo ILC ILCo @@ -858,20 +811,19 @@ INFOEX inheritcursor INITCOMMONCONTROLSEX INITDIALOG -initguid +INITGUID INITMENU inkscape INLINEPREFIX inproc Inputkeyinfo -inputpaneinterop Inputreadhandledata INPUTSCOPE INSERTMODE INTERACTIVITYBASE INTERCEPTCOPYPASTE +internalevent INTERNALNAME -Interner intsafe INVALIDARG INVALIDATERECT @@ -883,8 +835,6 @@ itermcolors itf Ith IUI -IUnknown -ivalid IWIC IXP jconcpp @@ -908,7 +858,6 @@ keydowns KEYFIRST KEYLAST Keymapping -keyscan keystate keyups Kickstart @@ -918,10 +867,6 @@ kinda KIYEOK KLF KLMNO -KLMNOPQRST -KLMNOPQRSTQQQQQ -KLMNOPQRSTUVWXY -KLMNOPQRSTY KOK KPRIORITY KVM @@ -943,6 +888,8 @@ LCONTROL LCTRL lcx LEFTALIGN +libsancov +libtickit LIMITTEXT LINEDOWN LINESELECTION @@ -1014,6 +961,7 @@ lstatus lstrcmp lstrcmpi LTEXT +lto ltsc LUID luma @@ -1047,6 +995,7 @@ MBUTTONDOWN MBUTTONUP mdmerge MDs +mdtauk MEASUREITEM megamix memallocator @@ -1056,7 +1005,6 @@ MENUCONTROL MENUDROPALIGNMENT MENUITEMINFO MENUSELECT -messageext metaproj Mgrs microsoftpublicsymbols @@ -1073,11 +1021,9 @@ minwindef MMBB mmcc MMCPL -mmsystem MNC MNOPQ MNOPQR -MNOPQRSTUVWXY MODALFRAME MODERNCORE MONITORINFO @@ -1089,7 +1035,6 @@ MOUSEHWHEEL MOVESTART msb msbuildcache -msctf msctls msdata MSDL @@ -1104,10 +1049,9 @@ MSGSELECTMODE msiexec MSIL msix -msrc +MSRC MSVCRTD MTSM -Munged murmurhash muxes myapplet @@ -1146,7 +1090,6 @@ Newtonsoft NEXTLINE nfe NLSMODE -nnn NOACTIVATE NOAPPLYNOW NOCLIP @@ -1199,40 +1142,31 @@ NPFS nrcs NSTATUS ntapi -ntcon -ntcsrdll ntdef NTDEV ntdll ntifs -ntlpcapi ntm -ntrtl ntstatus nttree -nturtl ntuser NTVDM -ntverp nugetversions NUKTA nullness nullonfailure nullopts -numlock NUMSCROLL NUnit nupkg NVIDIA NVT OACR -objbase ocolor oemcp OEMFONT OEMFORMAT OEMs -offboarded OLEAUT OLECHAR onebranch @@ -1246,6 +1180,7 @@ onecoreuuid ONECOREWINDOWS onehalf oneseq +oob openbash opencode opencon @@ -1281,7 +1216,6 @@ PALPC pankaj parentable PATCOPY -pathcch PATTERNID pbstr pcb @@ -1336,7 +1270,6 @@ phicon phwnd pidl PIDLIST -pids pii piml pimpl @@ -1361,11 +1294,9 @@ POINTERUPDATE POINTSLIST policheck POLYTEXTW -poppack POPUPATTR popups PORFLG -positionals POSTCHARBREAKS POSX POSXSCROLL @@ -1398,11 +1329,9 @@ PREVLINE prg pri prioritization -processenv processhost PROCESSINFOCLASS PRODEXT -Productize PROPERTYID PROPERTYKEY propertyval @@ -1414,15 +1343,12 @@ propsys PROPTITLE propvar propvariant -propvarutil psa PSECURITY pseudoconsole -pseudoterminal psh pshn PSHNOTIFY -pshpack PSINGLE psl psldl @@ -1494,8 +1420,6 @@ REGISTERVDM regkey REGSTR RELBINPATH -remoting -renamer rendersize reparented reparenting @@ -1530,19 +1454,16 @@ RIGHTALIGN RIGHTBUTTON riid ris -roadmap robomac rodata rosetta RRF -rrr RRRGGGBB rsas rtcore RTEXT RTLREADING Rtn -ruleset runas RUNDLL runformat @@ -1560,7 +1481,6 @@ rvpa RWIN rxvt safemath -sancov sba SBCS SBCSDBCS @@ -1588,10 +1508,8 @@ SCROLLSCREENBUFFER scursor sddl SDKDDK -securityappcontainer segfault SELCHANGE -SELECTALL SELECTEDFONT SELECTSTRING Selfhosters @@ -1635,14 +1553,10 @@ SFUI sgr SHCo shcore -shellapi shellex -shellscalingapi SHFILEINFO SHGFI SHIFTJIS -shlguid -shlobj shlwapi SHORTPATH SHOWCURSOR @@ -1671,15 +1585,16 @@ SLGP SLIST slmult sln +slnx slpit SManifest SMARTQUOTE SMTO snapcx snapcy +snk SOLIDBOX Solutiondir -somefile sourced SRCAND SRCCODEPAGE @@ -1711,9 +1626,8 @@ STDEXT STDMETHODCALLTYPE STDMETHODIMP STGM -Stringable STRINGTABLE -strsafe +STRSAFE STUBHEAD STUVWX stylecop @@ -1739,32 +1653,27 @@ SYSLIB SYSLINK SYSMENU sysparams -sysparamsext SYSTEMHAND SYSTEMMENU SYSTEMTIME tabview -TAdd taef TARG targetentrypoint TARGETLIBS TARGETNAME targetver -TBase tbc tbi Tbl TBM -tchar +TCHAR TCHFORMAT TCI tcommands tdbuild Tdd -TDelegated TDP -tearoff Teb Techo tellp @@ -1774,7 +1683,6 @@ terminalinput terminalrenderdata TERMINALSCROLLING terminfo -TEs testcon testd testenvs @@ -1786,7 +1694,6 @@ TESTNULL testpass testpasses TEXCOORD -TExpected textattribute TEXTATTRIBUTEID textboxes @@ -1797,39 +1704,29 @@ TEXTMETRIC TEXTMETRICW textmode texttests -TFunction THUMBPOSITION THUMBTRACK -tickit -TIcon tilunittests titlebars TITLEISLINKNAME -TJson -TLambda TLDP TLEN +Tlgg TMAE TMPF -TMult tmultiple -TODOs tofrom -tokenhelpers toolbars TOOLINFO TOOLWINDOW TOPDOWNDIB -TOpt tosign -touchpad tracelogging traceviewpp trackbar trackpad transitioning Trd -TREX triaged triaging TRIMZEROHEADINGS @@ -1837,9 +1734,7 @@ trx tsa tsgr tsm -TStr TSTRFORMAT -TSub TTBITMAP TTFONT TTFONTLIST @@ -1848,7 +1743,6 @@ TTo tvpp tvtseq TYUI -UAC uap uapadmin UAX @@ -1884,7 +1778,6 @@ unk unknwn UNORM unparseable -Unregistering untextured UPDATEDISPLAY UPDOWN @@ -1902,7 +1795,6 @@ USEFILLATTRIBUTE USEGLYPHCHARS USEHICON USEPOSITION -USERDATA userdpiapi Userp userprivapi @@ -1915,7 +1807,6 @@ USRDLL utext utr UVWXY -UVWXYZ uwa uwp uwu @@ -1924,17 +1815,16 @@ Vanara vararg vclib vcxitems -vectorize VERCTRL VERTBAR VFT vga vgaoem viewkind -viewports VIRAMA Virt VIRTTERM +visualstudiosdk vkey VKKEYSCAN VMs @@ -1945,6 +1835,7 @@ VPACKMANIFESTDIRECTORY VPR VREDRAW vsc +vsconfig vscprintf VSCROLL vsdevshell @@ -1992,7 +1883,6 @@ wekyb wewoad wex wextest -wextestclass WFill wfopen WHelper @@ -2003,9 +1893,7 @@ Wiggum wil WImpl WINAPI -winbase winbasep -wincodec wincon winconp winconpty @@ -2020,10 +1908,8 @@ windll WINDOWALPHA windowdpiapi WINDOWEDGE -windowext WINDOWINFO windowio -windowmetrics WINDOWPLACEMENT windowpos WINDOWPOSCHANGED @@ -2031,20 +1917,15 @@ WINDOWPOSCHANGING windowproc windowrect windowsapp -windowsinternalstring WINDOWSIZE windowsshell windowsterminal -windowsx windowtheme winevent -wingdi winget wingetcreate WINIDE -winioctl winmd -winmeta winmgr winmm WINMSAPP @@ -2054,8 +1935,8 @@ WInplace winres winrt winternl +winui winuser -winuserp WINVER wistd wmain @@ -2092,10 +1973,10 @@ WRITECONSOLEINPUT WRITECONSOLEOUTPUT WRITECONSOLEOUTPUTSTRING wrkstr +WRL wrl wrp WRunoff -wsl WSLENV wstr wstrings @@ -2108,7 +1989,7 @@ wtof WTs WTSOFTFONT wtw -wtypes +Wtypes WUX WVerify WWith @@ -2127,7 +2008,6 @@ XBUTTONDOWN XBUTTONUP XCast XCENTER -xchar xcopy XCount xdy @@ -2137,6 +2017,7 @@ XFG XFile XFORM XIn +xkcd XManifest XMath XNamespace @@ -2163,7 +2044,6 @@ YLimit YPan YSubstantial YVIRTUALSCREEN -Zab zabcd Zabcdefghijklmn Zabcdefghijklmnopqrstuvwxyz @@ -2172,4 +2052,3 @@ ZCtrl ZWJs ZYXWVU ZYXWVUTd -zzf diff --git a/.github/actions/spelling/line_forbidden.patterns b/.github/actions/spelling/line_forbidden.patterns index 5617a8af5f..ecfead2f0c 100644 --- a/.github/actions/spelling/line_forbidden.patterns +++ b/.github/actions/spelling/line_forbidden.patterns @@ -8,6 +8,24 @@ # you might not want to check in code where you skip all the other tests. #\bfit\( +# English does not use a hyphen between adverbs and nouns +# https://twitter.com/nyttypos/status/1894815686192685239 +(?:^|\s)[A-Z]?[a-z]+ly-(?=[a-z]{3,})(?:[.,?!]?\s|$) + +# Don't use `requires that` + `to be` +# https://twitter.com/nyttypos/status/1894816551435641027 +\brequires that \w+\b[^.]+to be\b + +# A fully parenthetical sentence’s period goes inside the parentheses, not outside. +# https://twitter.com/nyttypos/status/1898844061873639490 +\([A-Z][a-z]{2,}(?: [a-z]+){3,}\)\.\s + +# Complete sentences shouldn't be in the middle of another sentence as a parenthetical. +(? In formal writing and where contractions are frowned upon, use `cannot`. # > It is possible to write `can not`, but you generally find it only as part of some other construction, such as `not only . . . but also.` # - if you encounter such a case, add a pattern for that case to patterns.txt. -\b[Cc]an not\b +\b[Cc]an not\b(?! only\b) + +# Should be `chart` +(?i)\bhelm\b.*\bchard\b # Do not use `(click) here` links # For more information, see: @@ -56,9 +111,27 @@ # * https://heyoka.medium.com/dont-use-click-here-f32f445d1021 (?i)(?:>|\[)(?:(?:click |)here|link|(?:read |)more)(?:> /etc/apt/sources.list.d/something-distro.list +# ```` +\bapt-key add\b + +# Should be `nearby` +\bnear by\b + # Should probably be a person named `Nick` or the abbreviation `NIC` \bNic\b @@ -153,7 +260,7 @@ \bperform it's\b # Should be `opt-in` -#(? below for the` +(?i)\bfind below the\b + +# Should be `then any` unless there's a comparison before the `,` +, than any\b + # Should be `did not exist` \bwere not existent\b @@ -197,9 +357,18 @@ # Should be `nonexistent` \b[Nn]o[nt][- ]existent\b +# Should be `our` +\bspending out time\b + # Should be `@brief` / `@details` / `@param` / `@return` / `@retval` (?:^\s*|(?:\*|//|/*)\s+`)[\\@](?:breif|(?:detail|detials)|(?:params(?!\.)|prama?)|ret(?:uns?)|retvl)\b +# Should be `more than` or `more, then` +\bmore then\b + +# Should be `Pipeline`/`pipeline` +(?:(?<=\b|[A-Z])p|P)ipeLine(?:\b|(?=[A-Z])) + # Should be `preexisting` [Pp]re[- ]existing @@ -224,14 +393,27 @@ # Should be `reentrant` [Rr]e[- ]entrant +# Should be `room for` +\brooms for (?!lease|rent|sale) + +# Should be `socioeconomic` +# https://dictionary.cambridge.org/us/dictionary/english/socioeconomic +socio-economic + # Should be `strong suit` \b(?:my|his|her|their) strong suite\b +# Should probably be `temperatures` unless actually talking about thermal drafts (things birds may fly on) +\bthermals\b + +# Should be `there are` or `they are` (or `they're`) +(?i)\btheir are\b + # Should be `understand` \bunder stand\b -# Should be `URI` or `uri` unless it refers to a person named `Uri` -#(?|".*?") + +# hit-count: 1131 file-count: 326 +# C# includes +^\s*using [^;]+; + +# hit-count: 128 file-count: 47 # C network byte conversions (?:\d|\bh)to(?!ken)(?=[a-z])|to(?=[adhiklpun]\() -# hit-count: 59 file-count: 36 -# IServiceProvider / isAThing -(?:\b|_)(?:(?:ns|)I|isA)(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b)) +# hit-count: 53 file-count: 10 +# ANSI color codes +(?:\\(?:u00|x)1[Bb]|\\03[1-7]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+)*m -# hit-count: 9 file-count: 6 +# hit-count: 45 file-count: 29 +# version suffix v# +(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_])) + +# hit-count: 30 file-count: 19 +# uuid: +\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b + +# hit-count: 17 file-count: 7 +# File extensions +\*\.[+\w]+, + +# hit-count: 15 file-count: 9 # Markdown anchor links \(#\S*?[a-zA-Z]\S*?\) -# hit-count: 5 file-count: 5 -# libraries -(?:\b|_)lib(?:re(?=office)|)(?!era[lt]|ero|ert(?:ies|y)|rar(?:i(?:an|es)|y))(?=[a-z]) +# hit-count: 14 file-count: 8 +# hex runs +\b[0-9a-fA-F]{16,}\b + +# hit-count: 12 file-count: 8 +# hex digits including css/html color classes: +(?:[\\0][xX]|\\u|[uU]\+|#x?|%23|&H)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b + +# hit-count: 12 file-count: 7 +# tar arguments +\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+ + +# hit-count: 9 file-count: 6 +# Repeated letters +\b([a-z])\g{-1}{2,}\b + +# hit-count: 8 file-count: 2 +# regex choice +\(\?:[^)]+\|[^)]+\) + +# hit-count: 8 file-count: 1 +# latex (check-spelling >= 0.0.22) +\\\w{2,}\{ + +# hit-count: 7 file-count: 4 +# Python string prefix / binary prefix +# Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings +(?)[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}(?:[}"]|"'`=(])-(?:D(?=[A-Z])|W(?!ork)|X|f(?=[ms]))(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}) - -# hit-count: 60 file-count: 35 -# version suffix v# -(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_])) - -# hit-count: 2 file-count: 2 -# This does not cover multiline strings, if your repository has them, -# you'll want to remove the `(?=.*?")` suffix. -# The `(?=.*?")` suffix should limit the false positives rate -# printf -%(?:s)(?!ize)(?=[a-z]{2,}) - -# hit-count: 16 file-count: 10 -# uuid: -\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b - -# hit-count: 13 file-count: 4 -# Non-English -[a-zA-Z]*[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*|[a-zA-Z]{3,}[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]|[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3,} - -# hit-count: 7 file-count: 5 -# hex digits including css/html color classes: -(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b - -# hit-count: 7 file-count: 1 -# regex choice -\(\?:[^)]+\|[^)]+\) - -# hit-count: 4 file-count: 4 -# tar arguments -\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+ - -# hit-count: 4 file-count: 1 -# ANSI color codes -(?:\\(?:u00|x)1[Bb]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+|)m - -# hit-count: 4 file-count: 1 -# Update Lorem based on your content (requires `ge` and `w` from https://github.com/jsoref/spelling; and `review` from https://github.com/check-spelling/check-spelling/wiki/Looking-for-items-locally ) -# grep '^[^#].*lorem' .github/actions/spelling/patterns.txt|perl -pne 's/.*i..\?://;s/\).*//' |tr '|' "\n"|sort -f |xargs -n1 ge|perl -pne 's/^[^:]*://'|sort -u|w|sed -e 's/ .*//'|w|review - -# Warning, while `(?i)` is very neat and fancy, if you have some binary files that aren't proper unicode, you might run into: -## Operation "substitution (s///)" returns its argument for non-Unicode code point 0x1C19AE (the code point will vary). -## You could manually change `(?i)X...` to use `[Xx]...` -## or you could add the files to your `excludes` file (a version after 0.0.19 should identify the file path) -# Lorem -(?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus)\b(?:\w|\s|[,.])* - -# hit-count: 3 file-count: 3 -# mailto urls -mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,} - -# hit-count: 2 file-count: 1 -# Python string prefix / binary prefix -# Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings -(?= 0.0.22) -\\\w{2,}\{ - -# hit-count: 1 file-count: 1 -# tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long... -\btput\s+(?:(?:-[SV]|-T\s*\w+)\s+)*\w{3,5}\b - -# Questionably acceptable forms of `in to` -# Personally, I prefer `log into`, but people object -# https://www.tprteaching.com/log-into-log-in-to-login/ -\b(?:[Ll]og|[Ss]ign) in to\b - -# to opt in -\bto opt in\b # Questionably acceptable forms of `in to` # Personally, I prefer `log into`, but people object @@ -183,6 +210,10 @@ mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,} # to opt in \bto opt in\b + +# pass(ed|ing) in +\bpass(?:ed|ing) in\b + # acceptable duplicates # ls directory listings [-bcdlpsw](?:[-r][-w][-SsTtx]){3}[\.+*]?\s+\d+\s+\S+\s+\S+\s+[.\d]+(?:[KMGT]|)\s+ diff --git a/.github/actions/spelling/reject.txt b/.github/actions/spelling/reject.txt index d98038f96f..a4022cd4b4 100644 --- a/.github/actions/spelling/reject.txt +++ b/.github/actions/spelling/reject.txt @@ -1,10 +1,19 @@ ^attache$ ^attacher$ ^attachers$ -^bellow$ +^bellow?$ benefitting occurences? ^dependan.* +^develope$ +^developement$ +^developpe +^Devers?$ +^devex +^devide +^Devinn?[ae] +^devisal +^devisor ^diables?$ ^oer$ Sorce @@ -12,4 +21,5 @@ Sorce ^Teh$ ^untill$ ^untilling$ +^venders?$ ^wether.* diff --git a/.github/policies/resourceManagement.yml b/.github/policies/resourceManagement.yml index 695df7162d..7bdd1b5d03 100644 --- a/.github/policies/resourceManagement.yml +++ b/.github/policies/resourceManagement.yml @@ -8,7 +8,7 @@ where: configuration: resourceManagementConfiguration: scheduledSearches: - - description: + - description: '"Needs-Author-Feedback" and "No-Recent-Activity" issues are closed after 3 days of inactivity' frequencies: - hourly: hour: 3 @@ -23,7 +23,7 @@ configuration: days: 3 actions: - closeIssue - - description: + - description: '"Needs-Author-Feedback" issues are labelled "No-Recent-Activity" after 4 days of inactivity' frequencies: - hourly: hour: 3 @@ -41,7 +41,7 @@ configuration: label: No-Recent-Activity - addReply: reply: This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for **4 days**. It will be closed if no further activity occurs **within 3 days of this comment**. - - description: + - description: '"Resolution-Duplicate" issues are closed after 1 day of inactivity' frequencies: - hourly: hour: 3 @@ -56,7 +56,7 @@ configuration: - addReply: reply: This issue has been marked as duplicate and has not had any activity for **1 day**. It will be closed for housekeeping purposes. - closeIssue - - description: + - description: '"Needs-Author-Feedback" and "No-Recent-Activity" PRs are closed after 7 days of inactivity' frequencies: - hourly: hour: 3 @@ -71,7 +71,7 @@ configuration: days: 7 actions: - closeIssue - - description: + - description: Add "No-Recent-Activity" label to PRs with "Needs-Author-Feedback" label after 7 days of inactivity frequencies: - hourly: hour: 3 @@ -90,7 +90,8 @@ configuration: - addReply: reply: This pull request has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for **7 days**. It will be closed if no further activity occurs **within 7 days of this comment**. eventResponderTasks: - - if: + - description: Add "Needs-Triage" to new issues + if: - payloadType: Issues - or: - and: @@ -102,8 +103,8 @@ configuration: then: - addLabel: label: Needs-Triage - description: - - if: + - description: Replace "Needs-Author-Feedback" with "Needs-Attention" when author comments + if: - payloadType: Issue_Comment - isAction: action: Created @@ -116,8 +117,8 @@ configuration: label: Needs-Attention - removeLabel: label: Needs-Author-Feedback - description: - - if: + - description: Remove "No-Recent-Activity" when closing an issue + if: - payloadType: Issues - not: isAction: @@ -127,16 +128,16 @@ configuration: then: - removeLabel: label: No-Recent-Activity - description: - - if: + - description: Remove "No-Recent-Activity" when someone comments on an issue + if: - payloadType: Issue_Comment - hasLabel: label: No-Recent-Activity then: - removeLabel: label: No-Recent-Activity - description: - - if: + - description: Add "Needs-Author-Feedback" when changes are requested on a PR + if: - payloadType: Pull_Request_Review - isAction: action: Submitted @@ -145,8 +146,8 @@ configuration: then: - addLabel: label: Needs-Author-Feedback - description: - - if: + - description: Remove "Needs-Author-Feedback" when author performs activity on their PR + if: - payloadType: Pull_Request - isActivitySender: issueAuthor: True @@ -158,8 +159,8 @@ configuration: then: - removeLabel: label: Needs-Author-Feedback - description: - - if: + - description: Remove "Needs-Author-Feedback" when author comments on their issue + if: - payloadType: Issue_Comment - isActivitySender: issueAuthor: True @@ -168,8 +169,8 @@ configuration: then: - removeLabel: label: Needs-Author-Feedback - description: - - if: + - description: Remove "Needs-Author-Feedback" when the author reviews the PR + if: - payloadType: Pull_Request_Review - isActivitySender: issueAuthor: True @@ -178,8 +179,8 @@ configuration: then: - removeLabel: label: Needs-Author-Feedback - description: - - if: + - description: Remove "No-Recent-Activity"" when activity occurs on the PR (aside from closing it) + if: - payloadType: Pull_Request - not: isAction: @@ -189,39 +190,39 @@ configuration: then: - removeLabel: label: No-Recent-Activity - description: - - if: + - description: Remove "No-Recent-Activity" when someone comments on the PR + if: - payloadType: Issue_Comment - hasLabel: label: No-Recent-Activity then: - removeLabel: label: No-Recent-Activity - description: - - if: + - description: Remove "No-Recent-Activity" when someone reviews the PR + if: - payloadType: Pull_Request_Review - hasLabel: label: No-Recent-Activity then: - removeLabel: label: No-Recent-Activity - description: - - if: + - description: Enable auto-merge on PRs with the "AutoMerge" label + if: - payloadType: Pull_Request - hasLabel: label: AutoMerge then: - enableAutoMerge: mergeMethod: Squash - description: - - if: + - description: Disable auto-merge on PRs when the "AutoMerge" label is removed + if: - payloadType: Pull_Request - labelRemoved: label: AutoMerge then: - disableAutoMerge - description: - - if: + - description: Add "Needs-Tag-Fix" label to issues without an Area, Issue, or Product label + if: - payloadType: Issues - or: - and: @@ -238,15 +239,45 @@ configuration: - not: hasLabel: label: Area-Accessibility + - not: + hasLabel: + label: Area-AtlasEngine + - not: + hasLabel: + label: Area-AzureShell - not: hasLabel: label: Area-Build + - not: + hasLabel: + label: Area-Chat + - not: + hasLabel: + label: Area-CmdPal + - not: + hasLabel: + label: Area-CodeHealth + - not: + hasLabel: + label: Area-Commandline + - not: + hasLabel: + label: Area-CookedRead + - not: + hasLabel: + label: Area-DefApp - not: hasLabel: label: Area-Extensibility - not: hasLabel: label: Area-Fonts + - not: + hasLabel: + label: Area-GroupPolicy + - not: + hasLabel: + label: Area-i18n - not: hasLabel: label: Area-Input @@ -256,21 +287,45 @@ configuration: - not: hasLabel: label: Area-Interop + - not: + hasLabel: + label: Area-Localization - not: hasLabel: label: Area-Output - not: hasLabel: label: Area-Performance + - not: + hasLabel: + label: Area-Portable + - not: + hasLabel: + label: Area-Quality + - not: + hasLabel: + label: Area-Remoting - not: hasLabel: label: Area-Rendering + - not: + hasLabel: + label: Area-Schema - not: hasLabel: label: Area-Server - not: hasLabel: label: Area-Settings + - not: + hasLabel: + label: Area-SettingsUI + - not: + hasLabel: + label: Area-ShellExtension + - not: + hasLabel: + label: Area-Suggestions - not: hasLabel: label: Area-TerminalConnection @@ -279,49 +334,19 @@ configuration: label: Area-TerminalControl - not: hasLabel: - label: Area-User Interface + label: Area-Theming + - not: + hasLabel: + label: Area-UserInterface - not: hasLabel: label: Area-VT - - not: - hasLabel: - label: Area-CodeHealth - - not: - hasLabel: - label: Area-Quality - - not: - hasLabel: - label: Area-AzureShell - - not: - hasLabel: - label: Area-Schema - - not: - hasLabel: - label: Area-Commandline - - not: - hasLabel: - label: Area-ShellExtension - - not: - hasLabel: - label: Area-WPFControl - - not: - hasLabel: - label: Area-Settings UI - - not: - hasLabel: - label: Area-DefApp - - not: - hasLabel: - label: Area-Remoting - not: hasLabel: label: Area-Windowing - not: hasLabel: - label: Area-Theming - - not: - hasLabel: - label: Area-Localization + label: Area-WPFControl - and: - not: hasLabel: @@ -408,8 +433,8 @@ configuration: then: - addLabel: label: Needs-Tag-Fix - description: - - if: + - description: Remove "Needs-Tag-Fix" label when an issue is tagged with an Area, Issue, and Product label + if: - payloadType: Issues - and: - isLabeled @@ -417,66 +442,117 @@ configuration: label: Needs-Tag-Fix - and: - or: - - hasLabel: - label: Area-Accessibility - - hasLabel: - label: Area-Build - - hasLabel: - label: Area-Extensibility - - hasLabel: - label: Area-Fonts - - hasLabel: - label: Area-Input - - hasLabel: - label: Area-Interaction - - hasLabel: - label: Area-Interop - - hasLabel: - label: Area-Output - - hasLabel: - label: Area-Performance - - hasLabel: - label: Area-Rendering - - hasLabel: - label: Area-Server - - hasLabel: - label: Area-Settings - - hasLabel: - label: Area-TerminalConnection - - hasLabel: - label: Area-TerminalControl - - hasLabel: - label: Area-User Interface - - hasLabel: - label: Area-VT - - hasLabel: - label: Area-CodeHealth - - hasLabel: - label: Area-Quality - - hasLabel: - label: Area-Schema - - hasLabel: - label: Area-AzureShell - - hasLabel: - label: Area-Commandline - - hasLabel: - label: Area-ShellExtension - - hasLabel: - label: Area-WPFControl - - hasLabel: - label: Area-Settings UI - - hasLabel: - label: Area-DefApp - - hasLabel: - label: Area-Localization - - hasLabel: - label: Area-Windowing - - hasLabel: - label: Area-Theming - - hasLabel: - label: Area-AtlasEngine - - hasLabel: - label: Area-CmdPal + - not: + hasLabel: + label: Area-Accessibility + - not: + hasLabel: + label: Area-AtlasEngine + - not: + hasLabel: + label: Area-AzureShell + - not: + hasLabel: + label: Area-Build + - not: + hasLabel: + label: Area-Chat + - not: + hasLabel: + label: Area-CmdPal + - not: + hasLabel: + label: Area-CodeHealth + - not: + hasLabel: + label: Area-Commandline + - not: + hasLabel: + label: Area-CookedRead + - not: + hasLabel: + label: Area-DefApp + - not: + hasLabel: + label: Area-Extensibility + - not: + hasLabel: + label: Area-Fonts + - not: + hasLabel: + label: Area-GroupPolicy + - not: + hasLabel: + label: Area-i18n + - not: + hasLabel: + label: Area-Input + - not: + hasLabel: + label: Area-Interaction + - not: + hasLabel: + label: Area-Interop + - not: + hasLabel: + label: Area-Localization + - not: + hasLabel: + label: Area-Output + - not: + hasLabel: + label: Area-Performance + - not: + hasLabel: + label: Area-Portable + - not: + hasLabel: + label: Area-Quality + - not: + hasLabel: + label: Area-Remoting + - not: + hasLabel: + label: Area-Rendering + - not: + hasLabel: + label: Area-Schema + - not: + hasLabel: + label: Area-Server + - not: + hasLabel: + label: Area-Settings + - not: + hasLabel: + label: Area-SettingsUI + - not: + hasLabel: + label: Area-ShellExtension + - not: + hasLabel: + label: Area-Suggestions + - not: + hasLabel: + label: Area-TerminalConnection + - not: + hasLabel: + label: Area-TerminalControl + - not: + hasLabel: + label: Area-Theming + - not: + hasLabel: + label: Area-UserInterface + - not: + hasLabel: + label: Area-VT + - not: + hasLabel: + label: Area-Windowing + - not: + hasLabel: + label: Area-WPFControl - or: - hasLabel: label: Issue-Bug @@ -533,14 +609,14 @@ configuration: then: - removeLabel: label: Needs-Tag-Fix - description: - - if: + - description: Add "In-PR" label to issues that are referenced in a PR + if: - payloadType: Pull_Request then: - inPrLabel: label: In-PR - description: - - if: + - description: Remove "Needs-Tag-Fix" label when an issue also has the "Resolution-Duplicate" label + if: - payloadType: Issues - hasLabel: label: Needs-Tag-Fix @@ -549,8 +625,8 @@ configuration: then: - removeLabel: label: Needs-Tag-Fix - description: - - if: + - description: Close issues that are opened and have the template title + if: - payloadType: Issues - or: - titleContains: @@ -576,8 +652,8 @@ configuration: label: Needs-Author-Feedback - addReply: reply: Hi! Thanks for attempting to open an issue. Unfortunately, your title wasn't changed from the original template which makes it very hard for us to track and triage. You are welcome to fix up the title and try again with a new issue. - description: - - if: + - description: Close issues that are opened and have no body + if: - payloadType: Issues - or: - isAction: @@ -595,8 +671,8 @@ configuration: label: Needs-Author-Feedback - addReply: reply: "Hi! Thanks for attempting to open an issue. Unfortunately, you didn't write anything in the body which makes it impossible to understand your concern. You are welcome to fix up the issue and try again by opening another issue with the body filled out. " - description: - - if: + - description: Request a review from the team when a PR is labeled "Needs-Second" + if: - payloadType: Pull_Request - isLabeled - hasLabel: @@ -613,8 +689,8 @@ configuration: reviewer: dhowett - requestReview: reviewer: lhecker - description: - - if: + - description: Remove "Needs-Second" label when a PR is reviewed + if: - payloadType: Pull_Request_Review - not: isOpen - hasLabel: @@ -622,8 +698,8 @@ configuration: then: - removeLabel: label: Needs-Second - description: - - if: + - description: Remove "Help-Wanted" label from issues that are in a PR + if: - payloadType: Issues - hasLabel: label: In-PR @@ -633,8 +709,8 @@ configuration: then: - removeLabel: label: Help-Wanted - description: - - if: + - description: Comments with "/dup", "/dupe", or "/duplicate" will close the issue as a duplicate and remove "Needs-" labels + if: - payloadType: Issue_Comment - commentContains: pattern: '\/dup(licate|e)?(\s+of)?\s+\#[\d]+' @@ -662,8 +738,8 @@ configuration: label: Needs-Repro - removeLabel: label: Needs-Second - description: - - if: + - description: Comments with "/feedback" will direct people to Feedback Hub + if: - payloadType: Issue_Comment - commentContains: pattern: '\/feedback' @@ -680,13 +756,13 @@ configuration: Hi there!

Can you please send us feedback with the [Feedback Hub](https://support.microsoft.com/en-us/windows/send-feedback-to-microsoft-with-the-feedback-hub-app-f59187f8-8739-22d6-ba93-f66612949332) with this issue? Make sure to click the "Start recording" button, then reproduce the issue before submitting the feedback. Once it's submitted, paste the link here so we can more easily find your crash information on the back end?

Thanks!

![image](https://user-images.githubusercontent.com/18356694/140811502-a068f78b-89d2-4587-925a-73e19652b830.png)

![image](https://user-images.githubusercontent.com/18356694/140811557-cdc22a0f-fa6a-4f6a-953e-73b51f5548a3.png)

![image](https://user-images.githubusercontent.com/18221333/62478649-6de55400-b760-11e9-806e-5aab7e085a9f.png) - addLabel: label: Needs-Author-Feedback - description: - - if: + - description: Comments clean the email reply + if: - payloadType: Issue_Comment then: - cleanEmailReply - description: - - if: + - description: Sync labels when a PR event occurs + if: - payloadType: Pull_Request then: - labelSync: @@ -701,8 +777,8 @@ configuration: pattern: Severity- - labelSync: pattern: Impact- - description: - - if: + - description: Comments with "/dup", "/dupe", or "/duplicate" targeting another repo will close the issue as a duplicate and remove "Needs-" labels + if: - payloadType: Issue_Comment - commentContains: pattern: '\/dup(licate|e)?(\s+of)?\s+https' @@ -730,8 +806,8 @@ configuration: label: Needs-Repro - removeLabel: label: Needs-Second - description: - - if: + - description: Comments with "/?" will replace the "Needs-Attention" label with "Needs-Author-Feedback" + if: - payloadType: Issue_Comment - commentContains: pattern: /? @@ -746,6 +822,5 @@ configuration: label: Needs-Attention - addLabel: label: Needs-Author-Feedback - description: onFailure: onSuccess: diff --git a/.github/workflows/spelling2.yml b/.github/workflows/spelling2.yml index 7675d5d004..9c9cb9ea85 100644 --- a/.github/workflows/spelling2.yml +++ b/.github/workflows/spelling2.yml @@ -93,7 +93,7 @@ jobs: steps: - name: check-spelling id: spelling - uses: check-spelling/check-spelling@v0.0.24 + uses: check-spelling/check-spelling@v0.0.25 with: suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }} checkout: true @@ -104,7 +104,7 @@ jobs: report-timing: 1 warnings: bad-regex,binary-file,deprecated-feature,ignored-expect-variant,large-file,limited-references,no-newline-at-eof,noisy-file,non-alpha-in-dictionary,token-is-substring,unexpected-line-ending,whitespace-in-dictionary,minified-file,unsupported-configuration,no-files-to-check,unclosed-block-ignore-begin,unclosed-block-ignore-end experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }} - use_sarif: ${{ (!github.event.pull_request || (github.event.pull_request.head.repo.full_name == github.repository)) && 1 }} + use_sarif: 1 check_extra_dictionaries: "" dictionary_source_prefixes: > { @@ -114,34 +114,33 @@ jobs: cspell:software-terms/softwareTerms.txt cspell:cpp/stdlib-cpp.txt cspell:cpp/stdlib-c.txt - cspell:lorem-ipsum/dictionary.txt + cspell:python/python/python-lib.txt cspell:php/php.txt + cspell:node/node.txt + cspell:dart/dart.txt cspell:filetypes/filetypes.txt cspell:java/java.txt - cspell:node/node.txt - cspell:golang/go.txt - cspell:java/java-terms.txt - cspell:mnemonics/mnemonics.txt + cspell:css/css.txt + cspell:dotnet/dotnet.txt cspell:npm/npm.txt cspell:fullstack/fullstack.txt - cspell:python/python/python-lib.txt - cspell:dotnet/dotnet.txt - cspell:dart/dart.txt - cspell:aws/aws.txt - cspell:python/common/extra.txt - cspell:css/css.txt + cspell:java/java-terms.txt + cspell:r/r.txt + cspell:golang/go.txt cspell:cpp/stdlib-cmath.txt cspell:typescript/typescript.txt + cspell:html/html.txt cspell:cpp/compiler-msvc.txt cspell:django/django.txt - cspell:html/html.txt - cspell:cpp/lang-keywords.txt + cspell:aws/aws.txt + cspell:python/common/extra.txt cspell:cpp/ecosystem.txt - cspell:r/r.txt - cspell:cpp/compiler-clang-attributes.txt - cspell:powershell/powershell.txt + cspell:cpp/lang-keywords.txt cspell:csharp/csharp.txt + cspell:cpp/compiler-clang-attributes.txt cspell:python/python/python.txt + cspell:mnemonics/mnemonics.txt + cspell:powershell/powershell.txt comment-push: name: Report (Push) @@ -154,7 +153,7 @@ jobs: if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push' steps: - name: comment - uses: check-spelling/check-spelling@v0.0.24 + uses: check-spelling/check-spelling@v0.0.25 with: checkout: true spell_check_this: microsoft/terminal@main @@ -172,7 +171,7 @@ jobs: if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request') steps: - name: comment - uses: check-spelling/check-spelling@v0.0.24 + uses: check-spelling/check-spelling@v0.0.25 with: checkout: true spell_check_this: microsoft/terminal@main @@ -198,7 +197,7 @@ jobs: cancel-in-progress: false steps: - name: apply spelling updates - uses: check-spelling/check-spelling@v0.0.24 + uses: check-spelling/check-spelling@v0.0.25 with: experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }} checkout: true diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml index 63464ea4fe..4adf3837b1 100644 --- a/.github/workflows/winget.yml +++ b/.github/workflows/winget.yml @@ -1,4 +1,4 @@ -name: Publish to Winget +name: Publish to WinGet on: release: @@ -6,6 +6,9 @@ on: env: REGEX: 'Microsoft\.WindowsTerminal(?:Preview)?_([\d.]+)_8wekyb3d8bbwe\.msixbundle$' + # winget-create will read the following environment variable to access the GitHub token needed for submitting a PR + # See https://aka.ms/winget-create-token + WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_TOKEN }} jobs: publish: @@ -21,4 +24,4 @@ jobs: $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 }}" + & .\wingetcreate.exe update $wingetPackage -s -v $version -u $wingetRelevantAsset.browser_download_url diff --git a/.vsconfig b/.vsconfig index 4d39a491cc..a3997ad694 100644 --- a/.vsconfig +++ b/.vsconfig @@ -17,7 +17,6 @@ "Microsoft.VisualStudio.Component.AppInsights.Tools", "Microsoft.Net.Component.4.8.TargetingPack", "Microsoft.VisualStudio.Component.DiagnosticTools", - "Microsoft.NetCore.Component.Runtime.6.0", "Microsoft.VisualStudio.Component.ClassDesigner", "Microsoft.VisualStudio.Component.GraphDocument", "Microsoft.VisualStudio.Component.CodeMap", diff --git a/NOTICE.md b/NOTICE.md index bd99dd939c..efe962bc0d 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -285,6 +285,8 @@ specific language governing permissions and limitations under the License. **Source**: [https://github.com/commonmark/cmark](https://github.com/commonmark/cmark) ### License + +``` Copyright (c) 2014, John MacFarlane All rights reserved. @@ -455,6 +457,36 @@ 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. +``` + +## fzf + +### License + +``` +The MIT License (MIT) + +Copyright (c) 2013-2024 Junegunn Choi +Copyright (c) 2021-2025 Simon Hauser + +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. +``` # Microsoft Open Source diff --git a/NuGet.Config b/NuGet.Config index 57c3defd1d..0b270807a8 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -4,7 +4,7 @@ - + diff --git a/OpenConsole.sln b/OpenConsole.sln deleted file mode 100644 index fa94a4f8f0..0000000000 --- a/OpenConsole.sln +++ /dev/null @@ -1,2339 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.2.32422.2 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Terminal", "Terminal", "{59840756-302F-44DF-AA47-441A9D673202}" -EndProject -Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "CascadiaPackage", "src\cascadia\CascadiaPackage\CascadiaPackage.wapproj", "{CA5CAD1A-224A-4171-B13A-F16E576FDD12}" - ProjectSection(ProjectDependencies) = postProject - {F2ED628A-DB22-446F-A081-4CC845B51A2B} = {F2ED628A-DB22-446F-A081-4CC845B51A2B} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.EXE", "src\host\exe\Host.EXE.vcxproj", "{9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}" - ProjectSection(ProjectDependencies) = postProject - {0CF235BD-2DA0-407E-90EE-C467E8BBC714} = {0CF235BD-2DA0-407E-90EE-C467E8BBC714} - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239} = {5D23E8E1-3C64-4CC1-A8F7-6861677F7239} - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PropertiesLibrary", "src\propslib\propslib.vcxproj", "{345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Dependencies", "_Dependencies", "{81C352DB-1818-45B7-A284-18E259F1CC87}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wil", "wil", "{4C8E6BB0-4713-4ADB-BD04-81628ECEAF20}" - ProjectSection(SolutionItems) = preProject - dep\wil\include\wil\common.h = dep\wil\include\wil\common.h - dep\wil\include\wil\filesystem.h = dep\wil\include\wil\filesystem.h - dep\wil\include\wil\functional.h = dep\wil\include\wil\functional.h - dep\wil\include\wil\registry.h = dep\wil\include\wil\registry.h - dep\wil\include\wil\resource.h = dep\wil\include\wil\resource.h - dep\wil\include\wil\result.h = dep\wil\include\wil\result.h - dep\wil\include\wil\resultmacros.h = dep\wil\include\wil\resultmacros.h - dep\wil\include\wil\stl.h = dep\wil\include\wil\stl.h - dep\wil\include\wil\win32helpers.h = dep\wil\include\wil\win32helpers.h - dep\wil\include\wil\wistd_functional.h = dep\wil\include\wil\wistd_functional.h - dep\wil\include\wil\wistd_memory.h = dep\wil\include\wil\wistd_memory.h - dep\wil\include\wil\wistd_type_traits.h = dep\wil\include\wil\wistd_type_traits.h - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Console", "Console", "{D57841D1-8294-4F2B-BB8B-D2A35738DECD}" - ProjectSection(SolutionItems) = preProject - dep\Console\winconp.h = dep\Console\winconp.h - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TextServicesFramework", "src\tsf\tsf.vcxproj", "{2FD12FBB-1DDB-46D8-B818-1023C624CACA}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalParser", "src\terminal\parser\lib\parser.vcxproj", "{3AE13314-1939-4DFA-9C14-38CA0834050C}" - ProjectSection(ProjectDependencies) = postProject - {18D09A24-8240-42D6-8CB6-236EEE820263} = {18D09A24-8240-42D6-8CB6-236EEE820263} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAdapter", "src\terminal\adapter\lib\adapter.vcxproj", "{DCF55140-EF6A-4736-A403-957E4F7430BB}" - ProjectSection(ProjectDependencies) = postProject - {18D09A24-8240-42D6-8CB6-236EEE820263} = {18D09A24-8240-42D6-8CB6-236EEE820263} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalInput", "src\terminal\input\lib\terminalinput.vcxproj", "{1CF55140-EF6A-4736-A403-957E4F7430BB}" - ProjectSection(ProjectDependencies) = postProject - {18D09A24-8240-42D6-8CB6-236EEE820263} = {18D09A24-8240-42D6-8CB6-236EEE820263} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererBase", "src\renderer\base\lib\base.vcxproj", "{AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererGdi", "src\renderer\gdi\lib\gdi.vcxproj", "{1C959542-BAC2-4E55-9A6D-13251914CBB9}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host", "src\host\lib\hostlib.vcxproj", "{06EC74CB-9A12-429C-B551-8562EC954746}" - ProjectSection(ProjectDependencies) = postProject - {0CF235BD-2DA0-407E-90EE-C467E8BBC714} = {0CF235BD-2DA0-407E-90EE-C467E8BBC714} - {18D09A24-8240-42D6-8CB6-236EEE820263} = {18D09A24-8240-42D6-8CB6-236EEE820263} - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.unittest", "src\host\ut_lib\host.unittest.vcxproj", "{06EC74CB-9A12-429C-B551-8562EC954747}" - ProjectSection(ProjectDependencies) = postProject - {18D09A24-8240-42D6-8CB6-236EEE820263} = {18D09A24-8240-42D6-8CB6-236EEE820263} - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.Tests.Unit", "src\host\ut_host\Host.UnitTests.vcxproj", "{531C23E7-4B76-4C08-8AAD-04164CB628C9}" - ProjectSection(ProjectDependencies) = postProject - {06EC74CB-9A12-429C-B551-8562EC954747} = {06EC74CB-9A12-429C-B551-8562EC954747} - {0CF235BD-2DA0-407E-90EE-C467E8BBC714} = {0CF235BD-2DA0-407E-90EE-C467E8BBC714} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TextBuffer.Unit.Tests", "src\buffer\out\ut_textbuffer\TextBuffer.Unit.Tests.vcxproj", "{531C23E7-4B76-4C08-8BBD-04164CB628C9}" - ProjectSection(ProjectDependencies) = postProject - {0CF235BD-2DA0-407E-90EE-C467E8BBC714} = {0CF235BD-2DA0-407E-90EE-C467E8BBC714} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.Tests.Feature", "src\host\ft_host\Host.FeatureTests.vcxproj", "{8CDB8850-7484-4EC7-B45B-181F85B2EE54}" - ProjectSection(ProjectDependencies) = postProject - {18D09A24-8240-42D6-8CB6-236EEE820263} = {18D09A24-8240-42D6-8CB6-236EEE820263} - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A} = {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A} - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} - {FC802440-AD6A-4919-8F2C-7701F2B38D79} = {FC802440-AD6A-4919-8F2C-7701F2B38D79} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalParser.UnitTests", "src\terminal\parser\ut_parser\Parser.UnitTests.vcxproj", "{12144E07-FE63-4D33-9231-748B8D8C3792}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAdapter.UnitTests", "src\terminal\adapter\ut_adapter\Adapter.UnitTests.vcxproj", "{6AF01638-84CF-4B65-9870-484DFFCAC772}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalParser.Fuzzer", "src\terminal\parser\ft_fuzzer\VTCommandFuzzer.vcxproj", "{96927B31-D6E8-4ABD-B03E-A5088A30BEBE}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalParser.FuzzWrapper", "src\terminal\parser\ft_fuzzwrapper\FuzzWrapper.vcxproj", "{F210A4AE-E02A-4BFC-80BB-F50A672FE763}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Propsheet.DLL", "src\propsheet\propsheet.vcxproj", "{5D23E8E1-3C64-4CC1-A8F7-6861677F7239}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Build Common", "_Build Common", "{04170EEF-983A-4195-BFEF-2321E5E38A1E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Server", "src\server\lib\server.vcxproj", "{18D09A24-8240-42D6-8CB6-236EEE820262}" - ProjectSection(ProjectDependencies) = postProject - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Host.Tests.UIA", "src\host\ft_uia\Host.Tests.UIA.csproj", "{C17E1BF3-9D34-4779-9458-A8EF98CC5662}" - ProjectSection(ProjectDependencies) = postProject - {099193A0-1E43-4BBC-BA7F-7B351E1342DF} = {099193A0-1E43-4BBC-BA7F-7B351E1342DF} - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB} = {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VTApp", "src\tools\vtapp\VTApp.csproj", "{099193A0-1E43-4BBC-BA7F-7B351E1342DF}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Tools", "_Tools", "{A10C4720-DCA4-4640-9749-67F4314F527C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Nihilist", "src\tools\nihilist\Nihilist.vcxproj", "{FC802440-AD6A-4919-8F2C-7701F2B38D79}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FontList", "src\tools\fontlist\FontList.vcxproj", "{919544AC-D39B-463F-8414-3C3C67CF727C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Scratch", "src\tools\scratch\Scratch.vcxproj", "{ED82003F-FC5D-4E94-8B36-F480018ED064}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InteractivityWin32", "src\interactivity\win32\lib\win32.LIB.vcxproj", "{06EC74CB-9A12-429C-B551-8532EC964726}" - ProjectSection(ProjectDependencies) = postProject - {1C959542-BAC2-4E55-9A6D-13251914CBB9} = {1C959542-BAC2-4E55-9A6D-13251914CBB9} - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F} = {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "buffersize", "src\tools\buffersize\buffersize.vcxproj", "{ED82003F-FC5D-4E94-8B47-F480018ED064}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InteractivityBase", "src\interactivity\base\lib\InteractivityBase.vcxproj", "{06EC74CB-9A12-429C-B551-8562EC964846}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Interactivity.Win32.Tests.Unit", "src\interactivity\win32\ut_interactivity_win32\Interactivity.Win32.UnitTests.vcxproj", "{D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CloseTest", "src\tools\closetest\CloseTest.vcxproj", "{C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VtPipeTerm", "src\tools\vtpipeterm\VtPipeTerm.vcxproj", "{814DBDDE-894E-4327-A6E1-740504850098}" - ProjectSection(ProjectDependencies) = postProject - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ConEchoKey", "src\tools\echokey\ConEchoKey.vcxproj", "{814CBEEE-894E-4327-A6E1-740504850098}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Types", "src\types\lib\types.vcxproj", "{18D09A24-8240-42D6-8CB6-236EEE820263}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BufferOut", "src\buffer\out\lib\bufferout.vcxproj", "{0CF235BD-2DA0-407E-90EE-C467E8BBC714}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalConnection", "src\cascadia\TerminalConnection\TerminalConnection.vcxproj", "{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}" - ProjectSection(ProjectDependencies) = postProject - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalCore", "src\cascadia\TerminalCore\lib\TerminalCore-lib.vcxproj", "{CA5CAD1A-ABCD-429C-B551-8562EC954746}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Control.Lib", "src\cascadia\TerminalControl\TerminalControlLib.vcxproj", "{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}" - ProjectSection(ProjectDependencies) = postProject - {1CF55140-EF6A-4736-A403-957E4F7430BB} = {1CF55140-EF6A-4736-A403-957E4F7430BB} - {2FD12FBB-1DDB-46D8-B818-1023C624CACA} = {2FD12FBB-1DDB-46D8-B818-1023C624CACA} - {48D21369-3D7B-4431-9967-24E81292CF63} = {48D21369-3D7B-4431-9967-24E81292CF63} - {8222900C-8B6C-452A-91AC-BE95DB04B95F} = {8222900C-8B6C-452A-91AC-BE95DB04B95F} - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F} = {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F} - {CA5CAD1A-ABCD-429C-B551-8562EC954746} = {CA5CAD1A-ABCD-429C-B551-8562EC954746} - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Control", "src\cascadia\TerminalControl\dll\TerminalControl.vcxproj", "{CA5CAD1A-F542-4635-A069-7CAEFB930070}" - ProjectSection(ProjectDependencies) = postProject - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowsTerminal", "src\cascadia\WindowsTerminal\WindowsTerminal.vcxproj", "{CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}" - ProjectSection(ProjectDependencies) = postProject - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} = {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} - {CA5CAD1A-ABCD-429C-B551-8562EC954746} = {CA5CAD1A-ABCD-429C-B551-8562EC954746} - {CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalApp", "src\cascadia\TerminalApp\dll\TerminalApp.vcxproj", "{CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}" - ProjectSection(ProjectDependencies) = postProject - {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} - {CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746} - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} - {CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowsTerminalShellExt", "src\cascadia\ShellExtension\WindowsTerminalShellExt.vcxproj", "{F2ED628A-DB22-446F-A081-4CC845B51A2B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_TerminalCore", "src\cascadia\UnitTests_TerminalCore\UnitTests.vcxproj", "{2C2BEEF4-9333-4D05-B12A-1905CBF112F9}" - ProjectSection(ProjectDependencies) = postProject - {06EC74CB-9A12-429C-B551-8562EC954747} = {06EC74CB-9A12-429C-B551-8562EC954747} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Internal", "src\internal\internal.vcxproj", "{EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gsl", "gsl", "{16376381-CE22-42BE-B667-C6B35007008D}" - ProjectSection(SolutionItems) = preProject - dep\gsl\include\gsl\gsl = dep\gsl\include\gsl\gsl - dep\gsl\include\gsl\gsl_algorithm = dep\gsl\include\gsl\gsl_algorithm - dep\gsl\include\gsl\gsl_assert = dep\gsl\include\gsl\gsl_assert - dep\gsl\include\gsl\gsl_byte = dep\gsl\include\gsl\gsl_byte - dep\gsl\include\gsl\gsl_util = dep\gsl\include\gsl\gsl_util - dep\gsl\include\gsl\multi_span = dep\gsl\include\gsl\multi_span - dep\gsl\include\gsl\pointers = dep\gsl\include\gsl\pointers - dep\gsl\include\gsl\span = dep\gsl\include\gsl\span - dep\gsl\include\gsl\string_span = dep\gsl\include\gsl\string_span - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Virtual Terminal", "Virtual Terminal", "{F1995847-4AE5-479A-BBAF-382E51A63532}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Rendering", "Rendering", "{05500DEF-2294-41E3-AF9A-24E580B82836}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Conhost", "Conhost", "{E8F24881-5E37-4362-B191-A3BA0ED7F4EB}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Buffer", "Buffer", "{1E4A062E-293B-4817-B20D-BF16B979E350}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{89CDCC5C-9F53-4054-97A4-639D99F169CD}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Types.Unit.Tests", "src\types\ut_types\Types.Unit.Tests.vcxproj", "{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfTerminalControl", "src\cascadia\WpfTerminalControl\WpfTerminalControl.csproj", "{376FE273-6B84-4EB5-8B30-8DE9D21B022C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_TerminalApp", "src\cascadia\ut_app\TerminalApp.UnitTests.vcxproj", "{CA5CAD1A-9333-4D05-B12A-1905CBF112F9}" - ProjectSection(ProjectDependencies) = postProject - {CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746} - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAppLib", "src\cascadia\TerminalApp\TerminalAppLib.vcxproj", "{CA5CAD1A-9A12-429C-B551-8562EC954746}" - ProjectSection(ProjectDependencies) = postProject - {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} - {CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_TerminalApp", "src\cascadia\LocalTests_TerminalApp\TerminalApp.LocalTests.vcxproj", "{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}" - ProjectSection(ProjectDependencies) = postProject - {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} = {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} - {CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746} - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererUia", "src\renderer\uia\lib\uia.vcxproj", "{48D21369-3D7B-4431-9967-24E81292CF63}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinRTUtils", "src\cascadia\WinRTUtils\WinRTUtils.vcxproj", "{CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty.LIB", "src\winconpty\lib\winconptylib.vcxproj", "{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty.DLL", "src\winconpty\dll\winconptydll.vcxproj", "{A22EC5F6-7851-4B88-AC52-47249D437A52}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestHostApp", "src\cascadia\LocalTests_TerminalApp\TestHostApp\TestHostApp.vcxproj", "{A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}" - ProjectSection(ProjectDependencies) = postProject - {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{BDB237B6-1D1D-400F-84CC-40A58FA59C8E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "til.unit.tests", "src\til\ut_til\til.unit.tests.vcxproj", "{767268EE-174A-46FE-96F0-EEE698A1BBC9}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "U8U16Test", "src\tools\U8U16Test\U8U16Test.vcxproj", "{A602A555-BAAC-46E1-A91D-3DAB0475C5A1}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common Props", "Common Props", "{53DD5520-E64C-4C06-B472-7CE62CA539C9}" - ProjectSection(SolutionItems) = preProject - src\common.build.post.props = src\common.build.post.props - src\common.build.pre.props = src\common.build.pre.props - src\common.build.tests.props = src\common.build.tests.props - src\common.nugetversions.props = src\common.nugetversions.props - src\common.nugetversions.targets = src\common.nugetversions.targets - common.openconsole.props = common.openconsole.props - src\cppwinrt.build.post.props = src\cppwinrt.build.post.props - src\cppwinrt.build.pre.props = src\cppwinrt.build.pre.props - dep\nuget\packages.config = dep\nuget\packages.config - src\wap-common.build.post.props = src\wap-common.build.post.props - src\wap-common.build.pre.props = src\wap-common.build.pre.props - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "YAML", "YAML", "{6B5A44ED-918D-4747-BFB1-2472A1FCA173}" - ProjectSection(SolutionItems) = preProject - build\pipelines\templates\build-console-audit-job.yml = build\pipelines\templates\build-console-audit-job.yml - build\pipelines\templates\build-console-ci.yml = build\pipelines\templates\build-console-ci.yml - build\pipelines\templates\build-console-int.yml = build\pipelines\templates\build-console-int.yml - build\pipelines\templates\build-console-steps.yml = build\pipelines\templates\build-console-steps.yml - build\pipelines\templates\check-formatting.yml = build\pipelines\templates\check-formatting.yml - build\pipelines\ci.yml = build\pipelines\ci.yml - build\pipelines\templates\release-sign-and-bundle.yml = build\pipelines\templates\release-sign-and-bundle.yml - build\pipelines\release.yml = build\pipelines\release.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{D3EF7B96-CD5E-47C9-B9A9-136259563033}" - ProjectSection(SolutionItems) = preProject - build\scripts\Create-AppxBundle.ps1 = build\scripts\Create-AppxBundle.ps1 - build\scripts\Index-Pdbs.ps1 = build\scripts\Index-Pdbs.ps1 - build\scripts\Invoke-FormattingCheck.ps1 = build\scripts\Invoke-FormattingCheck.ps1 - build\scripts\Run-Tests.ps1 = build\scripts\Run-Tests.ps1 - build\scripts\Test-WindowsTerminalPackage.ps1 = build\scripts\Test-WindowsTerminalPackage.ps1 - EndProjectSection -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("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfTerminalTestNetCore", "src\cascadia\WpfTerminalTestNetCore\WpfTerminalTestNetCore.csproj", "{1588FD7C-241E-4E7D-9113-43735F3E6BAD}" - ProjectSection(ProjectDependencies) = postProject - {CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070} - {A22EC5F6-7851-4B88-AC52-47249D437A52} = {A22EC5F6-7851-4B88-AC52-47249D437A52} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wt", "src\cascadia\wt\wt.vcxproj", "{506FD703-BAA7-4F6E-9361-64F550EC8FCA}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "elevate-shim", "src\cascadia\ElevateShim\elevate-shim.vcxproj", "{416FD703-BAA7-4F6E-9361-64F550EC8FCA}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings.Editor", "src\cascadia\TerminalSettingsEditor\Microsoft.Terminal.Settings.Editor.vcxproj", "{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}" - ProjectSection(ProjectDependencies) = postProject - {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings.Model.Lib", "src\cascadia\TerminalSettingsModel\Microsoft.Terminal.Settings.ModelLib.vcxproj", "{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}" - ProjectSection(ProjectDependencies) = postProject - {CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings.Model", "src\cascadia\TerminalSettingsModel\dll\Microsoft.Terminal.Settings.Model.vcxproj", "{CA5CAD1A-082C-4476-9F33-94B339494076}" - ProjectSection(ProjectDependencies) = postProject - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_SettingsModel", "src\cascadia\UnitTests_SettingsModel\SettingsModel.UnitTests.vcxproj", "{CA5CAD1A-9B68-456A-B13E-C8218070DC42}" - ProjectSection(ProjectDependencies) = postProject - {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} - {CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070} - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wpf", "wpf", "{4DAF0299-495E-4CD1-A982-9BAC16A45932}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenConsoleProxy", "src\host\proxy\Host.Proxy.vcxproj", "{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Window", "Window", "{2D17E75D-2DDC-42C4-AD70-704D95A937AE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Settings", "Settings", "{77875138-BB08-49F9-8BB1-409C2150E0E1}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Control", "Control", "{9921CA0A-320C-4460-8623-3A3196E7F4CB}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.FuzzWrapper", "src\host\ft_fuzzer\Host.FuzzWrapper.vcxproj", "{05D9052F-D78F-478F-968A-2DE38A6DB996}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_Control", "src\cascadia\UnitTests_Control\Control.UnitTests.vcxproj", "{C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}" - ProjectSection(ProjectDependencies) = postProject - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsTerminal.UIA.Tests", "src\cascadia\WindowsTerminal_UIATests\WindowsTerminal.UIA.Tests.csproj", "{F19DACD5-0C6E-40DC-B6E4-767A3200542C}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utils", "Utils", "{61901E80-E97D-4D61-A9BB-E8F2FDA8B40C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererAtlas", "src\renderer\atlas\atlas.vcxproj", "{8222900C-8B6C-452A-91AC-BE95DB04B95F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InteractivityOneCore", "src\interactivity\onecore\lib\onecore.LIB.vcxproj", "{06EC74CB-9A12-428C-B551-8537EC964726}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererWddmCon", "src\renderer\wddmcon\lib\wddmcon.vcxproj", "{75C6F576-18E9-4566-978A-F0A301CAC090}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Audio", "Audio", "{40BD8415-DD93-4200-8D82-498DDDC08CC8}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MidiAudio", "src\audio\midi\lib\midi.vcxproj", "{3C67784E-1453-49C2-9660-483E2CC7F7AD}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TerminalStress", "src\tools\TerminalStress\TerminalStress.csproj", "{613CCB57-5FA9-48EF-80D0-6B1E319E20C4}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RenderingTests", "src\tools\RenderingTests\RenderingTests.vcxproj", "{37C995E0-2349-4154-8E77-4A52C0C7F46D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.UI", "src\cascadia\UIHelpers\UIHelpers.vcxproj", "{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.UI.Markdown", "src\cascadia\UIMarkdown\UIMarkdown.vcxproj", "{7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "benchcat", "src\tools\benchcat\benchcat.vcxproj", "{2C836962-9543-4CE5-B834-D28E1F124B66}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ConsoleMonitor", "src\tools\ConsoleMonitor\ConsoleMonitor.vcxproj", "{328729E9-6723-416E-9C98-951F1473BBE1}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ConsoleBench", "src\tools\ConsoleBench\ConsoleBench.vcxproj", "{BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - AuditMode|Any CPU = AuditMode|Any CPU - AuditMode|ARM64 = AuditMode|ARM64 - AuditMode|x64 = AuditMode|x64 - AuditMode|x86 = AuditMode|x86 - Debug|Any CPU = Debug|Any CPU - Debug|ARM64 = Debug|ARM64 - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Fuzzing|Any CPU = Fuzzing|Any CPU - Fuzzing|ARM64 = Fuzzing|ARM64 - Fuzzing|x64 = Fuzzing|x64 - Fuzzing|x86 = Fuzzing|x86 - Release|Any CPU = Release|Any CPU - Release|ARM64 = Release|ARM64 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.AuditMode|Any CPU.ActiveCfg = Debug|x86 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.AuditMode|x86.ActiveCfg = Release|x86 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Debug|Any CPU.ActiveCfg = Debug|x86 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Debug|ARM64.Build.0 = Debug|ARM64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Debug|ARM64.Deploy.0 = Debug|ARM64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Debug|x64.ActiveCfg = Debug|x64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Debug|x64.Build.0 = Debug|x64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Debug|x64.Deploy.0 = Debug|x64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Debug|x86.ActiveCfg = Debug|x86 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Debug|x86.Build.0 = Debug|x86 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Debug|x86.Deploy.0 = Debug|x86 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Fuzzing|Any CPU.ActiveCfg = Debug|x86 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Fuzzing|x64.ActiveCfg = Debug|x64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Fuzzing|x86.ActiveCfg = Debug|x86 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Release|Any CPU.ActiveCfg = Release|x86 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Release|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Release|ARM64.Build.0 = Release|ARM64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Release|ARM64.Deploy.0 = Release|ARM64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Release|x64.ActiveCfg = Release|x64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Release|x64.Build.0 = Release|x64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Release|x64.Deploy.0 = Release|x64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Release|x86.ActiveCfg = Release|x86 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Release|x86.Build.0 = Release|x86 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Release|x86.Deploy.0 = Release|x86 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|x64.ActiveCfg = Release|x64 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|x86.ActiveCfg = Release|Win32 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|ARM64.Build.0 = Debug|ARM64 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|x64.ActiveCfg = Debug|x64 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|x64.Build.0 = Debug|x64 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|x64.Deploy.0 = Debug|x64 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|x86.ActiveCfg = Debug|Win32 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|x86.Build.0 = Debug|Win32 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Release|Any CPU.ActiveCfg = Release|Win32 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Release|ARM64.ActiveCfg = Release|ARM64 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Release|ARM64.Build.0 = Release|ARM64 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Release|x64.ActiveCfg = Release|x64 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Release|x64.Build.0 = Release|x64 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Release|x86.ActiveCfg = Release|Win32 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Release|x86.Build.0 = Release|Win32 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.AuditMode|x64.ActiveCfg = Release|x64 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.AuditMode|x86.ActiveCfg = Release|Win32 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Debug|ARM64.Build.0 = Debug|ARM64 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Debug|x64.ActiveCfg = Debug|x64 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Debug|x64.Build.0 = Debug|x64 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Debug|x86.ActiveCfg = Debug|Win32 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Debug|x86.Build.0 = Debug|Win32 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Release|Any CPU.ActiveCfg = Release|Win32 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Release|ARM64.ActiveCfg = Release|ARM64 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Release|ARM64.Build.0 = Release|ARM64 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Release|x64.ActiveCfg = Release|x64 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Release|x64.Build.0 = Release|x64 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Release|x86.ActiveCfg = Release|Win32 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Release|x86.Build.0 = Release|Win32 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.AuditMode|x64.ActiveCfg = Release|x64 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.AuditMode|x86.ActiveCfg = Release|Win32 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Debug|ARM64.Build.0 = Debug|ARM64 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Debug|x64.ActiveCfg = Debug|x64 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Debug|x64.Build.0 = Debug|x64 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Debug|x86.ActiveCfg = Debug|Win32 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Debug|x86.Build.0 = Debug|Win32 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Release|Any CPU.ActiveCfg = Release|Win32 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Release|ARM64.ActiveCfg = Release|ARM64 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Release|ARM64.Build.0 = Release|ARM64 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Release|x64.ActiveCfg = Release|x64 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Release|x64.Build.0 = Release|x64 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Release|x86.ActiveCfg = Release|Win32 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Release|x86.Build.0 = Release|Win32 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.AuditMode|x64.Build.0 = AuditMode|x64 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.AuditMode|x86.ActiveCfg = Release|Win32 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Debug|ARM64.Build.0 = Debug|ARM64 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Debug|x64.ActiveCfg = Debug|x64 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Debug|x64.Build.0 = Debug|x64 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Debug|x86.ActiveCfg = Debug|Win32 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Debug|x86.Build.0 = Debug|Win32 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Release|Any CPU.ActiveCfg = Release|Win32 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Release|ARM64.ActiveCfg = Release|ARM64 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Release|ARM64.Build.0 = Release|ARM64 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Release|x64.ActiveCfg = Release|x64 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Release|x64.Build.0 = Release|x64 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Release|x86.ActiveCfg = Release|Win32 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.Release|x86.Build.0 = Release|Win32 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|x64.Build.0 = AuditMode|x64 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|x86.ActiveCfg = Release|Win32 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|ARM64.Build.0 = Debug|ARM64 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|x64.ActiveCfg = Debug|x64 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|x64.Build.0 = Debug|x64 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|x86.ActiveCfg = Debug|Win32 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|x86.Build.0 = Debug|Win32 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|Any CPU.ActiveCfg = Release|Win32 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|ARM64.ActiveCfg = Release|ARM64 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|ARM64.Build.0 = Release|ARM64 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|x64.ActiveCfg = Release|x64 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|x64.Build.0 = Release|x64 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|x86.ActiveCfg = Release|Win32 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|x86.Build.0 = Release|Win32 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|x64.Build.0 = AuditMode|x64 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|x86.ActiveCfg = Release|Win32 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Debug|ARM64.Build.0 = Debug|ARM64 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Debug|x64.ActiveCfg = Debug|x64 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Debug|x64.Build.0 = Debug|x64 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Debug|x86.ActiveCfg = Debug|Win32 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Debug|x86.Build.0 = Debug|Win32 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Release|Any CPU.ActiveCfg = Release|Win32 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Release|ARM64.ActiveCfg = Release|ARM64 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Release|ARM64.Build.0 = Release|ARM64 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Release|x64.ActiveCfg = Release|x64 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Release|x64.Build.0 = Release|x64 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Release|x86.ActiveCfg = Release|Win32 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.Release|x86.Build.0 = Release|Win32 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.AuditMode|x64.ActiveCfg = Release|x64 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.AuditMode|x64.Build.0 = Release|x64 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.AuditMode|x86.ActiveCfg = Release|Win32 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Debug|ARM64.Build.0 = Debug|ARM64 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Debug|x64.ActiveCfg = Debug|x64 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Debug|x64.Build.0 = Debug|x64 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Debug|x86.ActiveCfg = Debug|Win32 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Debug|x86.Build.0 = Debug|Win32 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Release|Any CPU.ActiveCfg = Release|Win32 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Release|ARM64.ActiveCfg = Release|ARM64 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Release|ARM64.Build.0 = Release|ARM64 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Release|x64.ActiveCfg = Release|x64 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Release|x64.Build.0 = Release|x64 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Release|x86.ActiveCfg = Release|Win32 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Release|x86.Build.0 = Release|Win32 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.AuditMode|x64.ActiveCfg = Release|x64 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.AuditMode|x86.ActiveCfg = Release|Win32 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Debug|ARM64.Build.0 = Debug|ARM64 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Debug|x64.ActiveCfg = Debug|x64 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Debug|x64.Build.0 = Debug|x64 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Debug|x86.ActiveCfg = Debug|Win32 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Debug|x86.Build.0 = Debug|Win32 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Release|Any CPU.ActiveCfg = Release|Win32 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Release|ARM64.ActiveCfg = Release|ARM64 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Release|ARM64.Build.0 = Release|ARM64 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Release|x64.ActiveCfg = Release|x64 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Release|x64.Build.0 = Release|x64 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Release|x86.ActiveCfg = Release|Win32 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Release|x86.Build.0 = Release|Win32 - {06EC74CB-9A12-429C-B551-8562EC954746}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {06EC74CB-9A12-429C-B551-8562EC954746}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {06EC74CB-9A12-429C-B551-8562EC954746}.AuditMode|x64.ActiveCfg = Release|x64 - {06EC74CB-9A12-429C-B551-8562EC954746}.AuditMode|x86.ActiveCfg = Release|Win32 - {06EC74CB-9A12-429C-B551-8562EC954746}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {06EC74CB-9A12-429C-B551-8562EC954746}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {06EC74CB-9A12-429C-B551-8562EC954746}.Debug|ARM64.Build.0 = Debug|ARM64 - {06EC74CB-9A12-429C-B551-8562EC954746}.Debug|x64.ActiveCfg = Debug|x64 - {06EC74CB-9A12-429C-B551-8562EC954746}.Debug|x64.Build.0 = Debug|x64 - {06EC74CB-9A12-429C-B551-8562EC954746}.Debug|x86.ActiveCfg = Debug|Win32 - {06EC74CB-9A12-429C-B551-8562EC954746}.Debug|x86.Build.0 = Debug|Win32 - {06EC74CB-9A12-429C-B551-8562EC954746}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {06EC74CB-9A12-429C-B551-8562EC954746}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {06EC74CB-9A12-429C-B551-8562EC954746}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {06EC74CB-9A12-429C-B551-8562EC954746}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {06EC74CB-9A12-429C-B551-8562EC954746}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {06EC74CB-9A12-429C-B551-8562EC954746}.Release|Any CPU.ActiveCfg = Release|Win32 - {06EC74CB-9A12-429C-B551-8562EC954746}.Release|ARM64.ActiveCfg = Release|ARM64 - {06EC74CB-9A12-429C-B551-8562EC954746}.Release|ARM64.Build.0 = Release|ARM64 - {06EC74CB-9A12-429C-B551-8562EC954746}.Release|x64.ActiveCfg = Release|x64 - {06EC74CB-9A12-429C-B551-8562EC954746}.Release|x64.Build.0 = Release|x64 - {06EC74CB-9A12-429C-B551-8562EC954746}.Release|x86.ActiveCfg = Release|Win32 - {06EC74CB-9A12-429C-B551-8562EC954746}.Release|x86.Build.0 = Release|Win32 - {06EC74CB-9A12-429C-B551-8562EC954747}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {06EC74CB-9A12-429C-B551-8562EC954747}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {06EC74CB-9A12-429C-B551-8562EC954747}.AuditMode|x64.ActiveCfg = Release|x64 - {06EC74CB-9A12-429C-B551-8562EC954747}.AuditMode|x86.ActiveCfg = Release|Win32 - {06EC74CB-9A12-429C-B551-8562EC954747}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {06EC74CB-9A12-429C-B551-8562EC954747}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {06EC74CB-9A12-429C-B551-8562EC954747}.Debug|ARM64.Build.0 = Debug|ARM64 - {06EC74CB-9A12-429C-B551-8562EC954747}.Debug|x64.ActiveCfg = Debug|x64 - {06EC74CB-9A12-429C-B551-8562EC954747}.Debug|x64.Build.0 = Debug|x64 - {06EC74CB-9A12-429C-B551-8562EC954747}.Debug|x86.ActiveCfg = Debug|Win32 - {06EC74CB-9A12-429C-B551-8562EC954747}.Debug|x86.Build.0 = Debug|Win32 - {06EC74CB-9A12-429C-B551-8562EC954747}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {06EC74CB-9A12-429C-B551-8562EC954747}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {06EC74CB-9A12-429C-B551-8562EC954747}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {06EC74CB-9A12-429C-B551-8562EC954747}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {06EC74CB-9A12-429C-B551-8562EC954747}.Release|Any CPU.ActiveCfg = Release|Win32 - {06EC74CB-9A12-429C-B551-8562EC954747}.Release|ARM64.ActiveCfg = Release|ARM64 - {06EC74CB-9A12-429C-B551-8562EC954747}.Release|ARM64.Build.0 = Release|ARM64 - {06EC74CB-9A12-429C-B551-8562EC954747}.Release|x64.ActiveCfg = Release|x64 - {06EC74CB-9A12-429C-B551-8562EC954747}.Release|x64.Build.0 = Release|x64 - {06EC74CB-9A12-429C-B551-8562EC954747}.Release|x86.ActiveCfg = Release|Win32 - {06EC74CB-9A12-429C-B551-8562EC954747}.Release|x86.Build.0 = Release|Win32 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.AuditMode|x64.ActiveCfg = Release|x64 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.AuditMode|x86.ActiveCfg = Release|Win32 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Debug|ARM64.Build.0 = Debug|ARM64 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Debug|x64.ActiveCfg = Debug|x64 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Debug|x64.Build.0 = Debug|x64 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Debug|x86.ActiveCfg = Debug|Win32 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Debug|x86.Build.0 = Debug|Win32 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Release|Any CPU.ActiveCfg = Release|Win32 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Release|ARM64.ActiveCfg = Release|ARM64 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Release|ARM64.Build.0 = Release|ARM64 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Release|x64.ActiveCfg = Release|x64 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Release|x64.Build.0 = Release|x64 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Release|x86.ActiveCfg = Release|Win32 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Release|x86.Build.0 = Release|Win32 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.AuditMode|x64.ActiveCfg = Release|x64 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.AuditMode|x86.ActiveCfg = Release|Win32 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Debug|ARM64.Build.0 = Debug|ARM64 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Debug|x64.ActiveCfg = Debug|x64 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Debug|x64.Build.0 = Debug|x64 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Debug|x86.ActiveCfg = Debug|Win32 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Debug|x86.Build.0 = Debug|Win32 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Release|Any CPU.ActiveCfg = Release|Win32 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Release|ARM64.ActiveCfg = Release|ARM64 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Release|ARM64.Build.0 = Release|ARM64 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Release|x64.ActiveCfg = Release|x64 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Release|x64.Build.0 = Release|x64 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Release|x86.ActiveCfg = Release|Win32 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Release|x86.Build.0 = Release|Win32 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.AuditMode|x64.ActiveCfg = Release|x64 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.AuditMode|x86.ActiveCfg = Release|Win32 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Debug|ARM64.Build.0 = Debug|ARM64 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Debug|x64.ActiveCfg = Debug|x64 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Debug|x64.Build.0 = Debug|x64 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Debug|x86.ActiveCfg = Debug|Win32 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Release|Any CPU.ActiveCfg = Release|Win32 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Release|ARM64.ActiveCfg = Release|ARM64 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Release|ARM64.Build.0 = Release|ARM64 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Release|x64.ActiveCfg = Release|x64 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Release|x64.Build.0 = Release|x64 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Release|x86.ActiveCfg = Release|Win32 - {12144E07-FE63-4D33-9231-748B8D8C3792}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {12144E07-FE63-4D33-9231-748B8D8C3792}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {12144E07-FE63-4D33-9231-748B8D8C3792}.AuditMode|x64.ActiveCfg = Release|x64 - {12144E07-FE63-4D33-9231-748B8D8C3792}.AuditMode|x86.ActiveCfg = Release|Win32 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Debug|ARM64.Build.0 = Debug|ARM64 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Debug|x64.ActiveCfg = Debug|x64 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Debug|x64.Build.0 = Debug|x64 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Debug|x86.ActiveCfg = Debug|Win32 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Debug|x86.Build.0 = Debug|Win32 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Release|Any CPU.ActiveCfg = Release|Win32 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Release|ARM64.ActiveCfg = Release|ARM64 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Release|ARM64.Build.0 = Release|ARM64 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Release|x64.ActiveCfg = Release|x64 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Release|x64.Build.0 = Release|x64 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Release|x86.ActiveCfg = Release|Win32 - {12144E07-FE63-4D33-9231-748B8D8C3792}.Release|x86.Build.0 = Release|Win32 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.AuditMode|x64.ActiveCfg = Release|x64 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.AuditMode|x86.ActiveCfg = Release|Win32 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Debug|ARM64.Build.0 = Debug|ARM64 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Debug|x64.ActiveCfg = Debug|x64 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Debug|x64.Build.0 = Debug|x64 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Debug|x86.ActiveCfg = Debug|Win32 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Debug|x86.Build.0 = Debug|Win32 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Release|Any CPU.ActiveCfg = Release|Win32 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Release|ARM64.ActiveCfg = Release|ARM64 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Release|ARM64.Build.0 = Release|ARM64 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Release|x64.ActiveCfg = Release|x64 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Release|x64.Build.0 = Release|x64 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Release|x86.ActiveCfg = Release|Win32 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.Release|x86.Build.0 = Release|Win32 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.AuditMode|x64.ActiveCfg = Release|x64 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.AuditMode|x86.ActiveCfg = Release|Win32 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Debug|ARM64.Build.0 = Debug|ARM64 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Debug|x64.ActiveCfg = Debug|x64 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Debug|x64.Build.0 = Debug|x64 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Debug|x86.ActiveCfg = Debug|Win32 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Debug|x86.Build.0 = Debug|Win32 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Release|Any CPU.ActiveCfg = Release|Win32 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Release|ARM64.ActiveCfg = Release|ARM64 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Release|ARM64.Build.0 = Release|ARM64 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Release|x64.ActiveCfg = Release|x64 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Release|x64.Build.0 = Release|x64 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Release|x86.ActiveCfg = Release|Win32 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Release|x86.Build.0 = Release|Win32 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.AuditMode|x64.ActiveCfg = Release|x64 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.AuditMode|x86.ActiveCfg = Release|Win32 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Debug|ARM64.Build.0 = Debug|ARM64 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Debug|x64.ActiveCfg = Debug|x64 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Debug|x64.Build.0 = Debug|x64 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Debug|x86.ActiveCfg = Debug|Win32 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Debug|x86.Build.0 = Debug|Win32 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Release|Any CPU.ActiveCfg = Release|Win32 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Release|ARM64.ActiveCfg = Release|ARM64 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Release|ARM64.Build.0 = Release|ARM64 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Release|x64.ActiveCfg = Release|x64 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Release|x64.Build.0 = Release|x64 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Release|x86.ActiveCfg = Release|Win32 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Release|x86.Build.0 = Release|Win32 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.AuditMode|x64.ActiveCfg = Release|x64 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.AuditMode|x86.ActiveCfg = Release|Win32 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Debug|ARM64.Build.0 = Debug|ARM64 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Debug|x64.ActiveCfg = Debug|x64 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Debug|x64.Build.0 = Debug|x64 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Debug|x86.ActiveCfg = Debug|Win32 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Debug|x86.Build.0 = Debug|Win32 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Release|Any CPU.ActiveCfg = Release|Win32 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Release|ARM64.ActiveCfg = Release|ARM64 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Release|ARM64.Build.0 = Release|ARM64 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Release|x64.ActiveCfg = Release|x64 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Release|x64.Build.0 = Release|x64 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Release|x86.ActiveCfg = Release|Win32 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Release|x86.Build.0 = Release|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820262}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820262}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {18D09A24-8240-42D6-8CB6-236EEE820262}.AuditMode|x64.ActiveCfg = Release|x64 - {18D09A24-8240-42D6-8CB6-236EEE820262}.AuditMode|x86.ActiveCfg = Release|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Debug|ARM64.Build.0 = Debug|ARM64 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Debug|x64.ActiveCfg = Debug|x64 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Debug|x64.Build.0 = Debug|x64 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Debug|x86.ActiveCfg = Debug|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Debug|x86.Build.0 = Debug|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Release|Any CPU.ActiveCfg = Release|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Release|ARM64.ActiveCfg = Release|ARM64 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Release|ARM64.Build.0 = Release|ARM64 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Release|x64.ActiveCfg = Release|x64 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Release|x64.Build.0 = Release|x64 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Release|x86.ActiveCfg = Release|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820262}.Release|x86.Build.0 = Release|Win32 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.AuditMode|Any CPU.ActiveCfg = Debug|ARM64 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.AuditMode|ARM64.ActiveCfg = Debug|Win32 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.AuditMode|x64.ActiveCfg = Release|x64 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.AuditMode|x86.ActiveCfg = Release|Win32 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Debug|ARM64.ActiveCfg = Debug|Win32 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Debug|x64.ActiveCfg = Debug|x64 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Debug|x64.Build.0 = Debug|x64 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Debug|x86.ActiveCfg = Debug|Win32 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Debug|x86.Build.0 = Debug|Win32 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Fuzzing|Any CPU.ActiveCfg = Debug|x64 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Fuzzing|x64.ActiveCfg = Debug|x64 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Fuzzing|x86.ActiveCfg = Release|Win32 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Release|Any CPU.ActiveCfg = Release|Win32 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Release|ARM64.ActiveCfg = Release|ARM64 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Release|x64.ActiveCfg = Release|x64 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Release|x64.Build.0 = Release|x64 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Release|x86.ActiveCfg = Release|Win32 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Release|x86.Build.0 = Release|Win32 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.AuditMode|Any CPU.ActiveCfg = Debug|ARM64 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.AuditMode|ARM64.ActiveCfg = Debug|Win32 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.AuditMode|x64.ActiveCfg = Release|x64 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.AuditMode|x86.ActiveCfg = Release|Win32 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Debug|ARM64.ActiveCfg = Debug|Win32 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Debug|x64.ActiveCfg = Debug|x64 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Debug|x64.Build.0 = Debug|x64 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Debug|x86.ActiveCfg = Debug|Win32 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Debug|x86.Build.0 = Debug|Win32 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Fuzzing|Any CPU.ActiveCfg = Debug|Win32 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Fuzzing|x64.ActiveCfg = Debug|x64 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Fuzzing|x86.ActiveCfg = Debug|Win32 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Release|Any CPU.ActiveCfg = Release|Win32 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Release|ARM64.ActiveCfg = Release|ARM64 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Release|x64.ActiveCfg = Release|x64 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Release|x64.Build.0 = Release|x64 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Release|x86.ActiveCfg = Release|Win32 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Release|x86.Build.0 = Release|Win32 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.AuditMode|x64.ActiveCfg = Release|x64 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.AuditMode|x86.ActiveCfg = Release|Win32 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Debug|ARM64.Build.0 = Debug|ARM64 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Debug|x64.ActiveCfg = Debug|x64 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Debug|x64.Build.0 = Debug|x64 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Debug|x86.ActiveCfg = Debug|Win32 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Debug|x86.Build.0 = Debug|Win32 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Release|Any CPU.ActiveCfg = Release|Win32 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Release|ARM64.ActiveCfg = Release|ARM64 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Release|ARM64.Build.0 = Release|ARM64 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Release|x64.ActiveCfg = Release|x64 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Release|x64.Build.0 = Release|x64 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Release|x86.ActiveCfg = Release|Win32 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Release|x86.Build.0 = Release|Win32 - {919544AC-D39B-463F-8414-3C3C67CF727C}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {919544AC-D39B-463F-8414-3C3C67CF727C}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {919544AC-D39B-463F-8414-3C3C67CF727C}.AuditMode|x64.ActiveCfg = Release|x64 - {919544AC-D39B-463F-8414-3C3C67CF727C}.AuditMode|x86.ActiveCfg = Release|Win32 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Debug|ARM64.Build.0 = Debug|ARM64 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Debug|x64.ActiveCfg = Debug|x64 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Debug|x64.Build.0 = Debug|x64 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Debug|x86.ActiveCfg = Debug|Win32 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Debug|x86.Build.0 = Debug|Win32 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Release|Any CPU.ActiveCfg = Release|Win32 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Release|ARM64.ActiveCfg = Release|ARM64 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Release|ARM64.Build.0 = Release|ARM64 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Release|x64.ActiveCfg = Release|x64 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Release|x64.Build.0 = Release|x64 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Release|x86.ActiveCfg = Release|Win32 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Release|x86.Build.0 = Release|Win32 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.AuditMode|x64.ActiveCfg = Release|x64 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.AuditMode|x86.ActiveCfg = Release|Win32 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Debug|ARM64.Build.0 = Debug|ARM64 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Debug|x64.ActiveCfg = Debug|x64 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Debug|x64.Build.0 = Debug|x64 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Debug|x86.ActiveCfg = Debug|Win32 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Debug|x86.Build.0 = Debug|Win32 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Release|Any CPU.ActiveCfg = Release|Win32 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Release|ARM64.ActiveCfg = Release|ARM64 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Release|ARM64.Build.0 = Release|ARM64 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Release|x64.ActiveCfg = Release|x64 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Release|x64.Build.0 = Release|x64 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Release|x86.ActiveCfg = Release|Win32 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.Release|x86.Build.0 = Release|Win32 - {06EC74CB-9A12-429C-B551-8532EC964726}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {06EC74CB-9A12-429C-B551-8532EC964726}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {06EC74CB-9A12-429C-B551-8532EC964726}.AuditMode|x64.ActiveCfg = Release|x64 - {06EC74CB-9A12-429C-B551-8532EC964726}.AuditMode|x86.ActiveCfg = Release|Win32 - {06EC74CB-9A12-429C-B551-8532EC964726}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {06EC74CB-9A12-429C-B551-8532EC964726}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {06EC74CB-9A12-429C-B551-8532EC964726}.Debug|ARM64.Build.0 = Debug|ARM64 - {06EC74CB-9A12-429C-B551-8532EC964726}.Debug|x64.ActiveCfg = Debug|x64 - {06EC74CB-9A12-429C-B551-8532EC964726}.Debug|x64.Build.0 = Debug|x64 - {06EC74CB-9A12-429C-B551-8532EC964726}.Debug|x86.ActiveCfg = Debug|Win32 - {06EC74CB-9A12-429C-B551-8532EC964726}.Debug|x86.Build.0 = Debug|Win32 - {06EC74CB-9A12-429C-B551-8532EC964726}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {06EC74CB-9A12-429C-B551-8532EC964726}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {06EC74CB-9A12-429C-B551-8532EC964726}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {06EC74CB-9A12-429C-B551-8532EC964726}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {06EC74CB-9A12-429C-B551-8532EC964726}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {06EC74CB-9A12-429C-B551-8532EC964726}.Release|Any CPU.ActiveCfg = Release|Win32 - {06EC74CB-9A12-429C-B551-8532EC964726}.Release|ARM64.ActiveCfg = Release|ARM64 - {06EC74CB-9A12-429C-B551-8532EC964726}.Release|ARM64.Build.0 = Release|ARM64 - {06EC74CB-9A12-429C-B551-8532EC964726}.Release|x64.ActiveCfg = Release|x64 - {06EC74CB-9A12-429C-B551-8532EC964726}.Release|x64.Build.0 = Release|x64 - {06EC74CB-9A12-429C-B551-8532EC964726}.Release|x86.ActiveCfg = Release|Win32 - {06EC74CB-9A12-429C-B551-8532EC964726}.Release|x86.Build.0 = Release|Win32 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.AuditMode|x64.ActiveCfg = Release|x64 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.AuditMode|x86.ActiveCfg = Release|Win32 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Debug|ARM64.Build.0 = Debug|ARM64 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Debug|x64.ActiveCfg = Debug|x64 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Debug|x64.Build.0 = Debug|x64 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Debug|x86.ActiveCfg = Debug|Win32 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Debug|x86.Build.0 = Debug|Win32 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Release|Any CPU.ActiveCfg = Release|Win32 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Release|ARM64.ActiveCfg = Release|ARM64 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Release|ARM64.Build.0 = Release|ARM64 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Release|x64.ActiveCfg = Release|x64 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Release|x64.Build.0 = Release|x64 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Release|x86.ActiveCfg = Release|Win32 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.Release|x86.Build.0 = Release|Win32 - {06EC74CB-9A12-429C-B551-8562EC964846}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {06EC74CB-9A12-429C-B551-8562EC964846}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {06EC74CB-9A12-429C-B551-8562EC964846}.AuditMode|x64.ActiveCfg = Release|x64 - {06EC74CB-9A12-429C-B551-8562EC964846}.AuditMode|x86.ActiveCfg = Release|Win32 - {06EC74CB-9A12-429C-B551-8562EC964846}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {06EC74CB-9A12-429C-B551-8562EC964846}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {06EC74CB-9A12-429C-B551-8562EC964846}.Debug|ARM64.Build.0 = Debug|ARM64 - {06EC74CB-9A12-429C-B551-8562EC964846}.Debug|x64.ActiveCfg = Debug|x64 - {06EC74CB-9A12-429C-B551-8562EC964846}.Debug|x64.Build.0 = Debug|x64 - {06EC74CB-9A12-429C-B551-8562EC964846}.Debug|x86.ActiveCfg = Debug|Win32 - {06EC74CB-9A12-429C-B551-8562EC964846}.Debug|x86.Build.0 = Debug|Win32 - {06EC74CB-9A12-429C-B551-8562EC964846}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {06EC74CB-9A12-429C-B551-8562EC964846}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {06EC74CB-9A12-429C-B551-8562EC964846}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {06EC74CB-9A12-429C-B551-8562EC964846}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {06EC74CB-9A12-429C-B551-8562EC964846}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {06EC74CB-9A12-429C-B551-8562EC964846}.Release|Any CPU.ActiveCfg = Release|Win32 - {06EC74CB-9A12-429C-B551-8562EC964846}.Release|ARM64.ActiveCfg = Release|ARM64 - {06EC74CB-9A12-429C-B551-8562EC964846}.Release|ARM64.Build.0 = Release|ARM64 - {06EC74CB-9A12-429C-B551-8562EC964846}.Release|x64.ActiveCfg = Release|x64 - {06EC74CB-9A12-429C-B551-8562EC964846}.Release|x64.Build.0 = Release|x64 - {06EC74CB-9A12-429C-B551-8562EC964846}.Release|x86.ActiveCfg = Release|Win32 - {06EC74CB-9A12-429C-B551-8562EC964846}.Release|x86.Build.0 = Release|Win32 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.AuditMode|x64.ActiveCfg = Release|x64 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.AuditMode|x86.ActiveCfg = Release|Win32 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Debug|ARM64.Build.0 = Debug|ARM64 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Debug|x64.ActiveCfg = Debug|x64 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Debug|x64.Build.0 = Debug|x64 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Debug|x86.ActiveCfg = Debug|Win32 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Debug|x86.Build.0 = Debug|Win32 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Release|Any CPU.ActiveCfg = Release|Win32 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Release|ARM64.ActiveCfg = Release|ARM64 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Release|ARM64.Build.0 = Release|ARM64 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Release|x64.ActiveCfg = Release|x64 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Release|x64.Build.0 = Release|x64 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Release|x86.ActiveCfg = Release|Win32 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Release|x86.Build.0 = Release|Win32 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.AuditMode|x64.ActiveCfg = Release|x64 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.AuditMode|x86.ActiveCfg = Release|Win32 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Debug|ARM64.Build.0 = Debug|ARM64 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Debug|x64.ActiveCfg = Debug|x64 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Debug|x64.Build.0 = Debug|x64 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Debug|x86.ActiveCfg = Debug|Win32 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Debug|x86.Build.0 = Debug|Win32 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Release|Any CPU.ActiveCfg = Release|Win32 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Release|ARM64.ActiveCfg = Release|ARM64 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Release|ARM64.Build.0 = Release|ARM64 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Release|x64.ActiveCfg = Release|x64 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Release|x64.Build.0 = Release|x64 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Release|x86.ActiveCfg = Release|Win32 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Release|x86.Build.0 = Release|Win32 - {814DBDDE-894E-4327-A6E1-740504850098}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {814DBDDE-894E-4327-A6E1-740504850098}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {814DBDDE-894E-4327-A6E1-740504850098}.AuditMode|x64.ActiveCfg = Release|x64 - {814DBDDE-894E-4327-A6E1-740504850098}.AuditMode|x86.ActiveCfg = Release|Win32 - {814DBDDE-894E-4327-A6E1-740504850098}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {814DBDDE-894E-4327-A6E1-740504850098}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {814DBDDE-894E-4327-A6E1-740504850098}.Debug|ARM64.Build.0 = Debug|ARM64 - {814DBDDE-894E-4327-A6E1-740504850098}.Debug|x64.ActiveCfg = Debug|x64 - {814DBDDE-894E-4327-A6E1-740504850098}.Debug|x64.Build.0 = Debug|x64 - {814DBDDE-894E-4327-A6E1-740504850098}.Debug|x86.ActiveCfg = Debug|Win32 - {814DBDDE-894E-4327-A6E1-740504850098}.Debug|x86.Build.0 = Debug|Win32 - {814DBDDE-894E-4327-A6E1-740504850098}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {814DBDDE-894E-4327-A6E1-740504850098}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {814DBDDE-894E-4327-A6E1-740504850098}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {814DBDDE-894E-4327-A6E1-740504850098}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {814DBDDE-894E-4327-A6E1-740504850098}.Release|Any CPU.ActiveCfg = Release|Win32 - {814DBDDE-894E-4327-A6E1-740504850098}.Release|ARM64.ActiveCfg = Release|ARM64 - {814DBDDE-894E-4327-A6E1-740504850098}.Release|ARM64.Build.0 = Release|ARM64 - {814DBDDE-894E-4327-A6E1-740504850098}.Release|x64.ActiveCfg = Release|x64 - {814DBDDE-894E-4327-A6E1-740504850098}.Release|x64.Build.0 = Release|x64 - {814DBDDE-894E-4327-A6E1-740504850098}.Release|x86.ActiveCfg = Release|Win32 - {814DBDDE-894E-4327-A6E1-740504850098}.Release|x86.Build.0 = Release|Win32 - {814CBEEE-894E-4327-A6E1-740504850098}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {814CBEEE-894E-4327-A6E1-740504850098}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {814CBEEE-894E-4327-A6E1-740504850098}.AuditMode|x64.ActiveCfg = Release|x64 - {814CBEEE-894E-4327-A6E1-740504850098}.AuditMode|x86.ActiveCfg = Release|Win32 - {814CBEEE-894E-4327-A6E1-740504850098}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {814CBEEE-894E-4327-A6E1-740504850098}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {814CBEEE-894E-4327-A6E1-740504850098}.Debug|ARM64.Build.0 = Debug|ARM64 - {814CBEEE-894E-4327-A6E1-740504850098}.Debug|x64.ActiveCfg = Debug|x64 - {814CBEEE-894E-4327-A6E1-740504850098}.Debug|x64.Build.0 = Debug|x64 - {814CBEEE-894E-4327-A6E1-740504850098}.Debug|x86.ActiveCfg = Debug|Win32 - {814CBEEE-894E-4327-A6E1-740504850098}.Debug|x86.Build.0 = Debug|Win32 - {814CBEEE-894E-4327-A6E1-740504850098}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {814CBEEE-894E-4327-A6E1-740504850098}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {814CBEEE-894E-4327-A6E1-740504850098}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {814CBEEE-894E-4327-A6E1-740504850098}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {814CBEEE-894E-4327-A6E1-740504850098}.Release|Any CPU.ActiveCfg = Release|Win32 - {814CBEEE-894E-4327-A6E1-740504850098}.Release|ARM64.ActiveCfg = Release|ARM64 - {814CBEEE-894E-4327-A6E1-740504850098}.Release|ARM64.Build.0 = Release|ARM64 - {814CBEEE-894E-4327-A6E1-740504850098}.Release|x64.ActiveCfg = Release|x64 - {814CBEEE-894E-4327-A6E1-740504850098}.Release|x64.Build.0 = Release|x64 - {814CBEEE-894E-4327-A6E1-740504850098}.Release|x86.ActiveCfg = Release|Win32 - {814CBEEE-894E-4327-A6E1-740504850098}.Release|x86.Build.0 = Release|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820263}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820263}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {18D09A24-8240-42D6-8CB6-236EEE820263}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {18D09A24-8240-42D6-8CB6-236EEE820263}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {18D09A24-8240-42D6-8CB6-236EEE820263}.AuditMode|x64.Build.0 = AuditMode|x64 - {18D09A24-8240-42D6-8CB6-236EEE820263}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820263}.AuditMode|x86.Build.0 = AuditMode|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Debug|ARM64.Build.0 = Debug|ARM64 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Debug|x64.ActiveCfg = Debug|x64 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Debug|x64.Build.0 = Debug|x64 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Debug|x86.ActiveCfg = Debug|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Debug|x86.Build.0 = Debug|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Release|Any CPU.ActiveCfg = Release|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Release|ARM64.ActiveCfg = Release|ARM64 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Release|ARM64.Build.0 = Release|ARM64 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Release|x64.ActiveCfg = Release|x64 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Release|x64.Build.0 = Release|x64 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Release|x86.ActiveCfg = Release|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820263}.Release|x86.Build.0 = Release|Win32 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.AuditMode|x64.Build.0 = AuditMode|x64 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.AuditMode|x86.Build.0 = AuditMode|Win32 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Debug|ARM64.Build.0 = Debug|ARM64 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Debug|x64.ActiveCfg = Debug|x64 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Debug|x64.Build.0 = Debug|x64 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Debug|x86.ActiveCfg = Debug|Win32 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Debug|x86.Build.0 = Debug|Win32 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Release|Any CPU.ActiveCfg = Release|Win32 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Release|ARM64.ActiveCfg = Release|ARM64 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Release|ARM64.Build.0 = Release|ARM64 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Release|x64.ActiveCfg = Release|x64 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Release|x64.Build.0 = Release|x64 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Release|x86.ActiveCfg = Release|Win32 - {0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Release|x86.Build.0 = Release|Win32 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.AuditMode|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.AuditMode|x64.Build.0 = AuditMode|x64 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.AuditMode|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Debug|ARM64.Build.0 = Debug|ARM64 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Debug|x64.ActiveCfg = Debug|x64 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Debug|x64.Build.0 = Debug|x64 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Debug|x86.ActiveCfg = Debug|Win32 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Debug|x86.Build.0 = Debug|Win32 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Release|Any CPU.ActiveCfg = Release|Win32 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Release|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Release|ARM64.Build.0 = Release|ARM64 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Release|x64.ActiveCfg = Release|x64 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Release|x64.Build.0 = Release|x64 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Release|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Release|x86.Build.0 = Release|Win32 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.AuditMode|x64.Build.0 = AuditMode|x64 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.AuditMode|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Debug|ARM64.Build.0 = Debug|ARM64 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Debug|x64.ActiveCfg = Debug|x64 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Debug|x64.Build.0 = Debug|x64 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Debug|x86.ActiveCfg = Debug|Win32 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Debug|x86.Build.0 = Debug|Win32 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Release|Any CPU.ActiveCfg = Release|Win32 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Release|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Release|ARM64.Build.0 = Release|ARM64 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Release|x64.ActiveCfg = Release|x64 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Release|x64.Build.0 = Release|x64 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Release|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Release|x86.Build.0 = Release|Win32 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.AuditMode|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.AuditMode|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Debug|ARM64.Build.0 = Debug|ARM64 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Debug|x64.ActiveCfg = Debug|x64 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Debug|x64.Build.0 = Debug|x64 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Debug|x86.ActiveCfg = Debug|Win32 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Debug|x86.Build.0 = Debug|Win32 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Release|Any CPU.ActiveCfg = Release|Win32 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Release|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Release|ARM64.Build.0 = Release|ARM64 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Release|x64.ActiveCfg = Release|x64 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Release|x64.Build.0 = Release|x64 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Release|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Release|x86.Build.0 = Release|Win32 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.AuditMode|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.AuditMode|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|ARM64.Build.0 = Debug|ARM64 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|x64.ActiveCfg = Debug|x64 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|x64.Build.0 = Debug|x64 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|x86.ActiveCfg = Debug|Win32 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Debug|x86.Build.0 = Debug|Win32 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|Any CPU.ActiveCfg = Release|Win32 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|ARM64.Build.0 = Release|ARM64 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|x64.ActiveCfg = Release|x64 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|x64.Build.0 = Release|x64 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-F542-4635-A069-7CAEFB930070}.Release|x86.Build.0 = Release|Win32 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|Any CPU.ActiveCfg = Debug|x64 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Debug|ARM64.Build.0 = Debug|ARM64 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Debug|x64.ActiveCfg = Debug|x64 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Debug|x64.Build.0 = Debug|x64 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Debug|x86.ActiveCfg = Debug|Win32 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Debug|x86.Build.0 = Debug|Win32 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Release|Any CPU.ActiveCfg = Release|Win32 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Release|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Release|ARM64.Build.0 = Release|ARM64 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Release|x64.ActiveCfg = Release|x64 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Release|x64.Build.0 = Release|x64 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Release|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Release|x86.Build.0 = Release|Win32 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.AuditMode|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.AuditMode|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Debug|ARM64.Build.0 = Debug|ARM64 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Debug|x64.ActiveCfg = Debug|x64 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Debug|x64.Build.0 = Debug|x64 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Debug|x86.ActiveCfg = Debug|Win32 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Debug|x86.Build.0 = Debug|Win32 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Release|Any CPU.ActiveCfg = Release|Win32 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Release|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Release|ARM64.Build.0 = Release|ARM64 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Release|x64.ActiveCfg = Release|x64 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Release|x64.Build.0 = Release|x64 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Release|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Release|x86.Build.0 = Release|Win32 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.AuditMode|Any CPU.ActiveCfg = Release|Win32 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.AuditMode|x64.ActiveCfg = Release|x64 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.AuditMode|x64.Build.0 = Release|x64 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.AuditMode|x86.ActiveCfg = Release|Win32 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Debug|ARM64.Build.0 = Debug|ARM64 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Debug|x64.ActiveCfg = Debug|x64 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Debug|x64.Build.0 = Debug|x64 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Debug|x86.ActiveCfg = Debug|Win32 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Debug|x86.Build.0 = Debug|Win32 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Release|Any CPU.ActiveCfg = Release|Win32 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Release|ARM64.ActiveCfg = Release|ARM64 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Release|ARM64.Build.0 = Release|ARM64 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Release|x64.ActiveCfg = Release|x64 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Release|x64.Build.0 = Release|x64 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Release|x86.ActiveCfg = Release|Win32 - {F2ED628A-DB22-446F-A081-4CC845B51A2B}.Release|x86.Build.0 = Release|Win32 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Debug|ARM64.Build.0 = Debug|ARM64 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Debug|x64.ActiveCfg = Debug|x64 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Debug|x64.Build.0 = Debug|x64 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Debug|x86.ActiveCfg = Debug|Win32 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Debug|x86.Build.0 = Debug|Win32 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Release|Any CPU.ActiveCfg = Release|Win32 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Release|ARM64.ActiveCfg = Release|ARM64 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Release|ARM64.Build.0 = Release|ARM64 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Release|x64.ActiveCfg = Release|x64 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Release|x64.Build.0 = Release|x64 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Release|x86.ActiveCfg = Release|Win32 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Release|x86.Build.0 = Release|Win32 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.AuditMode|x64.Build.0 = AuditMode|x64 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.AuditMode|x86.Build.0 = AuditMode|Win32 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Debug|ARM64.Build.0 = Debug|ARM64 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Debug|x64.ActiveCfg = Debug|x64 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Debug|x64.Build.0 = Debug|x64 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Debug|x86.ActiveCfg = Debug|Win32 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Debug|x86.Build.0 = Debug|Win32 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Release|Any CPU.ActiveCfg = Release|Win32 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Release|ARM64.ActiveCfg = Release|ARM64 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Release|ARM64.Build.0 = Release|ARM64 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Release|x64.ActiveCfg = Release|x64 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Release|x64.Build.0 = Release|x64 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Release|x86.ActiveCfg = Release|Win32 - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Release|x86.Build.0 = Release|Win32 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Debug|ARM64.Build.0 = Debug|ARM64 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Debug|x64.ActiveCfg = Debug|x64 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Debug|x64.Build.0 = Debug|x64 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Debug|x86.ActiveCfg = Debug|Win32 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Debug|x86.Build.0 = Debug|Win32 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Release|Any CPU.ActiveCfg = Release|Win32 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Release|ARM64.ActiveCfg = Release|ARM64 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Release|ARM64.Build.0 = Release|ARM64 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Release|x64.ActiveCfg = Release|x64 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Release|x64.Build.0 = Release|x64 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Release|x86.ActiveCfg = Release|Win32 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Release|x86.Build.0 = Release|Win32 - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.AuditMode|Any CPU.ActiveCfg = Release|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.AuditMode|ARM64.ActiveCfg = Debug|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.AuditMode|ARM64.Build.0 = Debug|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.AuditMode|x64.ActiveCfg = Debug|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.AuditMode|x64.Build.0 = Debug|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.AuditMode|x86.ActiveCfg = Debug|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.AuditMode|x86.Build.0 = Debug|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Debug|ARM64.Build.0 = Debug|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Debug|x64.ActiveCfg = Debug|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Debug|x64.Build.0 = Debug|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Debug|x86.ActiveCfg = Debug|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Debug|x86.Build.0 = Debug|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Fuzzing|Any CPU.ActiveCfg = Debug|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Fuzzing|ARM64.ActiveCfg = Fuzzing|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Fuzzing|x64.ActiveCfg = Debug|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Fuzzing|x86.ActiveCfg = Fuzzing|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Release|Any CPU.Build.0 = Release|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Release|ARM64.ActiveCfg = Release|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Release|ARM64.Build.0 = Release|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Release|x64.ActiveCfg = Release|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Release|x64.Build.0 = Release|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Release|x86.ActiveCfg = Release|Any CPU - {376FE273-6B84-4EB5-8B30-8DE9D21B022C}.Release|x86.Build.0 = Release|Any CPU - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Debug|ARM64.Build.0 = Debug|ARM64 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Debug|x64.ActiveCfg = Debug|x64 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Debug|x64.Build.0 = Debug|x64 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Debug|x86.ActiveCfg = Debug|Win32 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Debug|x86.Build.0 = Debug|Win32 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Release|Any CPU.ActiveCfg = Release|Win32 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Release|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Release|ARM64.Build.0 = Release|ARM64 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Release|x64.ActiveCfg = Release|x64 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Release|x64.Build.0 = Release|x64 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Release|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Release|x86.Build.0 = Release|Win32 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.AuditMode|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.AuditMode|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Debug|ARM64.Build.0 = Debug|ARM64 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Debug|x64.ActiveCfg = Debug|x64 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Debug|x64.Build.0 = Debug|x64 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Debug|x86.ActiveCfg = Debug|Win32 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Debug|x86.Build.0 = Debug|Win32 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Release|Any CPU.ActiveCfg = Release|Win32 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Release|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Release|ARM64.Build.0 = Release|ARM64 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Release|x64.ActiveCfg = Release|x64 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Release|x64.Build.0 = Release|x64 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Release|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.Release|x86.Build.0 = Release|Win32 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Debug|ARM64.Build.0 = Debug|ARM64 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Debug|x64.ActiveCfg = Debug|x64 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Debug|x64.Build.0 = Debug|x64 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Debug|x86.ActiveCfg = Debug|Win32 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Debug|x86.Build.0 = Debug|Win32 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|Any CPU.ActiveCfg = Release|Win32 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|ARM64.Build.0 = Release|ARM64 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x64.ActiveCfg = Release|x64 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x64.Build.0 = Release|x64 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x86.Build.0 = Release|Win32 - {48D21369-3D7B-4431-9967-24E81292CF63}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {48D21369-3D7B-4431-9967-24E81292CF63}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {48D21369-3D7B-4431-9967-24E81292CF63}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {48D21369-3D7B-4431-9967-24E81292CF63}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {48D21369-3D7B-4431-9967-24E81292CF63}.AuditMode|x64.Build.0 = AuditMode|x64 - {48D21369-3D7B-4431-9967-24E81292CF63}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {48D21369-3D7B-4431-9967-24E81292CF63}.AuditMode|x86.Build.0 = AuditMode|Win32 - {48D21369-3D7B-4431-9967-24E81292CF63}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {48D21369-3D7B-4431-9967-24E81292CF63}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {48D21369-3D7B-4431-9967-24E81292CF63}.Debug|ARM64.Build.0 = Debug|ARM64 - {48D21369-3D7B-4431-9967-24E81292CF63}.Debug|x64.ActiveCfg = Debug|x64 - {48D21369-3D7B-4431-9967-24E81292CF63}.Debug|x64.Build.0 = Debug|x64 - {48D21369-3D7B-4431-9967-24E81292CF63}.Debug|x86.ActiveCfg = Debug|Win32 - {48D21369-3D7B-4431-9967-24E81292CF63}.Debug|x86.Build.0 = Debug|Win32 - {48D21369-3D7B-4431-9967-24E81292CF63}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {48D21369-3D7B-4431-9967-24E81292CF63}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {48D21369-3D7B-4431-9967-24E81292CF63}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {48D21369-3D7B-4431-9967-24E81292CF63}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {48D21369-3D7B-4431-9967-24E81292CF63}.Release|Any CPU.ActiveCfg = Release|Win32 - {48D21369-3D7B-4431-9967-24E81292CF63}.Release|ARM64.ActiveCfg = Release|ARM64 - {48D21369-3D7B-4431-9967-24E81292CF63}.Release|ARM64.Build.0 = Release|ARM64 - {48D21369-3D7B-4431-9967-24E81292CF63}.Release|x64.ActiveCfg = Release|x64 - {48D21369-3D7B-4431-9967-24E81292CF63}.Release|x64.Build.0 = Release|x64 - {48D21369-3D7B-4431-9967-24E81292CF63}.Release|x86.ActiveCfg = Release|Win32 - {48D21369-3D7B-4431-9967-24E81292CF63}.Release|x86.Build.0 = Release|Win32 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.AuditMode|Any CPU.ActiveCfg = Release|x64 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.AuditMode|x64.Build.0 = AuditMode|x64 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.AuditMode|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Debug|ARM64.Build.0 = Debug|ARM64 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Debug|x64.ActiveCfg = Debug|x64 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Debug|x64.Build.0 = Debug|x64 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Debug|x86.ActiveCfg = Debug|Win32 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Debug|x86.Build.0 = Debug|Win32 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Release|Any CPU.ActiveCfg = Release|Win32 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Release|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Release|ARM64.Build.0 = Release|ARM64 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Release|x64.ActiveCfg = Release|x64 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Release|x64.Build.0 = Release|x64 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Release|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}.Release|x86.Build.0 = Release|Win32 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|x64.Build.0 = AuditMode|x64 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|x86.Build.0 = AuditMode|Win32 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|ARM64.Build.0 = Debug|ARM64 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x64.ActiveCfg = Debug|x64 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x64.Build.0 = Debug|x64 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x86.ActiveCfg = Debug|Win32 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x86.Build.0 = Debug|Win32 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|Any CPU.ActiveCfg = Release|Win32 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|ARM64.ActiveCfg = Release|ARM64 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|ARM64.Build.0 = Release|ARM64 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x64.ActiveCfg = Release|x64 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x64.Build.0 = Release|x64 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x86.ActiveCfg = Release|Win32 - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x86.Build.0 = Release|Win32 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.AuditMode|x64.ActiveCfg = Release|x64 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.AuditMode|x86.Build.0 = AuditMode|Win32 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Debug|ARM64.Build.0 = Debug|ARM64 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Debug|x64.ActiveCfg = Debug|x64 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Debug|x64.Build.0 = Debug|x64 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Debug|x86.ActiveCfg = Debug|Win32 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Debug|x86.Build.0 = Debug|Win32 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Release|Any CPU.ActiveCfg = Release|Win32 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Release|ARM64.ActiveCfg = Release|ARM64 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Release|ARM64.Build.0 = Release|ARM64 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Release|x64.ActiveCfg = Release|x64 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Release|x64.Build.0 = Release|x64 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Release|x86.ActiveCfg = Release|Win32 - {A22EC5F6-7851-4B88-AC52-47249D437A52}.Release|x86.Build.0 = Release|Win32 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.AuditMode|Any CPU.ActiveCfg = AuditMode|x64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Debug|ARM64.Build.0 = Debug|ARM64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Debug|ARM64.Deploy.0 = Debug|ARM64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Debug|x64.ActiveCfg = Debug|x64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Debug|x64.Build.0 = Debug|x64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Debug|x64.Deploy.0 = Debug|x64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Debug|x86.ActiveCfg = Debug|Win32 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Debug|x86.Build.0 = Debug|Win32 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Debug|x86.Deploy.0 = Debug|Win32 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Release|Any CPU.ActiveCfg = Release|Win32 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Release|ARM64.ActiveCfg = Release|ARM64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Release|ARM64.Build.0 = Release|ARM64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Release|ARM64.Deploy.0 = Release|ARM64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Release|x64.ActiveCfg = Release|x64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Release|x64.Build.0 = Release|x64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Release|x64.Deploy.0 = Release|x64 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Release|x86.ActiveCfg = Release|Win32 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Release|x86.Build.0 = Release|Win32 - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}.Release|x86.Deploy.0 = Release|Win32 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.AuditMode|x64.ActiveCfg = Release|x64 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.AuditMode|x86.Build.0 = AuditMode|Win32 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Debug|ARM64.Build.0 = Debug|ARM64 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Debug|x64.ActiveCfg = Debug|x64 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Debug|x64.Build.0 = Debug|x64 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Debug|x86.ActiveCfg = Debug|Win32 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Debug|x86.Build.0 = Debug|Win32 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Release|Any CPU.ActiveCfg = Release|Win32 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Release|ARM64.ActiveCfg = Release|ARM64 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Release|ARM64.Build.0 = Release|ARM64 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Release|x64.ActiveCfg = Release|x64 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Release|x64.Build.0 = Release|x64 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Release|x86.ActiveCfg = Release|Win32 - {767268EE-174A-46FE-96F0-EEE698A1BBC9}.Release|x86.Build.0 = Release|Win32 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.AuditMode|Any CPU.ActiveCfg = Release|x64 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.AuditMode|Any CPU.Build.0 = Release|x64 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.AuditMode|ARM64.ActiveCfg = Release|x64 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.AuditMode|ARM64.Build.0 = Release|x64 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.AuditMode|x64.ActiveCfg = Release|x64 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.AuditMode|x64.Build.0 = Release|x64 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.AuditMode|x86.ActiveCfg = Release|Win32 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.AuditMode|x86.Build.0 = Release|Win32 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Debug|ARM64.ActiveCfg = Debug|Win32 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Debug|x64.ActiveCfg = Debug|x64 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Debug|x64.Build.0 = Debug|x64 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Debug|x86.ActiveCfg = Debug|Win32 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Debug|x86.Build.0 = Debug|Win32 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|Any CPU.ActiveCfg = Release|Win32 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|ARM64.ActiveCfg = Release|Win32 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|x64.ActiveCfg = Release|x64 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|x64.Build.0 = Release|x64 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|x86.ActiveCfg = Release|Win32 - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|x86.Build.0 = Release|Win32 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|x64.ActiveCfg = Release|x64 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|x86.Build.0 = AuditMode|Win32 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|ARM64.Build.0 = Debug|ARM64 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|x64.ActiveCfg = Debug|x64 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|x64.Build.0 = Debug|x64 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|x86.ActiveCfg = Debug|Win32 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|x86.Build.0 = Debug|Win32 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|Any CPU.ActiveCfg = Release|Win32 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|ARM64.ActiveCfg = Release|ARM64 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|ARM64.Build.0 = Release|ARM64 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|x64.ActiveCfg = Release|x64 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|x64.Build.0 = Release|x64 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|x86.ActiveCfg = Release|Win32 - {024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|x86.Build.0 = Release|Win32 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|x64.ActiveCfg = Release|x64 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|x86.Build.0 = AuditMode|Win32 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|ARM64.Build.0 = Debug|ARM64 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|x64.ActiveCfg = Debug|x64 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|x64.Build.0 = Debug|x64 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|x86.ActiveCfg = Debug|Win32 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|x86.Build.0 = Debug|Win32 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|Any CPU.ActiveCfg = Release|Win32 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|ARM64.ActiveCfg = Release|ARM64 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|ARM64.Build.0 = Release|ARM64 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|x64.ActiveCfg = Release|x64 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|x64.Build.0 = Release|x64 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|x86.ActiveCfg = Release|Win32 - {067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|x86.Build.0 = Release|Win32 - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.AuditMode|Any CPU.ActiveCfg = Debug|Any CPU - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.AuditMode|ARM64.ActiveCfg = Debug|Any CPU - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.AuditMode|x64.ActiveCfg = Debug|Any CPU - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.AuditMode|x86.ActiveCfg = Debug|Any CPU - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Debug|ARM64.Build.0 = Debug|ARM64 - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Debug|x64.ActiveCfg = Debug|x64 - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Debug|x64.Build.0 = Debug|x64 - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Debug|x86.ActiveCfg = Debug|x86 - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Debug|x86.Build.0 = Debug|x86 - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Fuzzing|Any CPU.ActiveCfg = Debug|Any CPU - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Fuzzing|ARM64.ActiveCfg = Fuzzing|Any CPU - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Fuzzing|x64.ActiveCfg = Debug|x64 - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Fuzzing|x86.ActiveCfg = Fuzzing|x86 - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Release|ARM64.ActiveCfg = Release|ARM64 - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Release|ARM64.Build.0 = Release|ARM64 - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Release|x64.ActiveCfg = Release|x64 - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Release|x64.Build.0 = Release|x64 - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Release|x86.ActiveCfg = Release|x86 - {1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Release|x86.Build.0 = Release|x86 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|x64.Build.0 = AuditMode|x64 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|x86.Build.0 = AuditMode|Win32 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|ARM64.Build.0 = Debug|ARM64 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|x64.ActiveCfg = Debug|x64 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|x64.Build.0 = Debug|x64 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|x86.ActiveCfg = Debug|Win32 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|x86.Build.0 = Debug|Win32 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|Any CPU.ActiveCfg = Release|Win32 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|ARM64.ActiveCfg = Release|ARM64 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|ARM64.Build.0 = Release|ARM64 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x64.ActiveCfg = Release|x64 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x64.Build.0 = Release|x64 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.ActiveCfg = Release|Win32 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.Build.0 = Release|Win32 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|x64.Build.0 = AuditMode|x64 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|x86.Build.0 = AuditMode|Win32 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|ARM64.Build.0 = Debug|ARM64 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|x64.ActiveCfg = Debug|x64 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|x64.Build.0 = Debug|x64 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|x86.ActiveCfg = Debug|Win32 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|x86.Build.0 = Debug|Win32 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|Any CPU.ActiveCfg = Release|Win32 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|ARM64.ActiveCfg = Release|ARM64 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|ARM64.Build.0 = Release|ARM64 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x64.ActiveCfg = Release|x64 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x64.Build.0 = Release|x64 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.ActiveCfg = Release|Win32 - {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.Build.0 = Release|Win32 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.AuditMode|Any CPU.ActiveCfg = Release|x64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.AuditMode|Any CPU.Build.0 = Release|x64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.AuditMode|Any CPU.Deploy.0 = Release|x64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.AuditMode|ARM64.ActiveCfg = Release|x64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.AuditMode|ARM64.Build.0 = Release|x64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.AuditMode|ARM64.Deploy.0 = Release|x64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.AuditMode|x64.Deploy.0 = Release|x64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.AuditMode|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.AuditMode|x86.Build.0 = Release|Win32 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.AuditMode|x86.Deploy.0 = Release|Win32 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|ARM64.Build.0 = Debug|ARM64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|x64.ActiveCfg = Debug|x64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|x64.Build.0 = Debug|x64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|x64.Deploy.0 = Debug|x64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|x86.ActiveCfg = Debug|Win32 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|x86.Build.0 = Debug|Win32 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|x86.Deploy.0 = Debug|Win32 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Release|Any CPU.ActiveCfg = Release|Win32 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Release|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Release|ARM64.Build.0 = Release|ARM64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Release|x64.ActiveCfg = Release|x64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Release|x64.Build.0 = Release|x64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Release|x64.Deploy.0 = Release|x64 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Release|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Release|x86.Build.0 = Release|Win32 - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Release|x86.Deploy.0 = Release|Win32 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|x86.Build.0 = AuditMode|Win32 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|ARM64.Build.0 = Debug|ARM64 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|x64.ActiveCfg = Debug|x64 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|x64.Build.0 = Debug|x64 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|x86.ActiveCfg = Debug|Win32 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|x86.Build.0 = Debug|Win32 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|Any CPU.ActiveCfg = Release|Win32 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|ARM64.Build.0 = Release|ARM64 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|x64.ActiveCfg = Release|x64 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|x64.Build.0 = Release|x64 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|x86.Build.0 = Release|Win32 - {CA5CAD1A-082C-4476-9F33-94B339494076}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {CA5CAD1A-082C-4476-9F33-94B339494076}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {CA5CAD1A-082C-4476-9F33-94B339494076}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {CA5CAD1A-082C-4476-9F33-94B339494076}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-082C-4476-9F33-94B339494076}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {CA5CAD1A-082C-4476-9F33-94B339494076}.AuditMode|x86.Build.0 = AuditMode|Win32 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Debug|ARM64.Build.0 = Debug|ARM64 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Debug|x64.ActiveCfg = Debug|x64 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Debug|x64.Build.0 = Debug|x64 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Debug|x86.ActiveCfg = Debug|Win32 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Debug|x86.Build.0 = Debug|Win32 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Release|Any CPU.ActiveCfg = Release|Win32 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Release|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Release|ARM64.Build.0 = Release|ARM64 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Release|x64.ActiveCfg = Release|x64 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Release|x64.Build.0 = Release|x64 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Release|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-082C-4476-9F33-94B339494076}.Release|x86.Build.0 = Release|Win32 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.AuditMode|x86.Build.0 = AuditMode|Win32 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Debug|ARM64.Build.0 = Debug|ARM64 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Debug|x64.ActiveCfg = Debug|x64 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Debug|x64.Build.0 = Debug|x64 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Debug|x86.ActiveCfg = Debug|Win32 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Debug|x86.Build.0 = Debug|Win32 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|Any CPU.ActiveCfg = Release|Win32 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|ARM64.Build.0 = Release|ARM64 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x64.ActiveCfg = Release|x64 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x64.Build.0 = Release|x64 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x86.Build.0 = Release|Win32 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|x64.Build.0 = AuditMode|x64 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|x86.Build.0 = AuditMode|Win32 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|ARM64.Build.0 = Debug|ARM64 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|x64.ActiveCfg = Debug|x64 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|x64.Build.0 = Debug|x64 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|x86.ActiveCfg = Debug|Win32 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Debug|x86.Build.0 = Debug|Win32 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|Any CPU.ActiveCfg = Release|Win32 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|ARM64.ActiveCfg = Release|ARM64 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|ARM64.Build.0 = Release|ARM64 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|x64.ActiveCfg = Release|x64 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|x64.Build.0 = Release|x64 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|x86.ActiveCfg = Release|Win32 - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.Release|x86.Build.0 = Release|Win32 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.Debug|x64.ActiveCfg = Debug|x64 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.Debug|x86.ActiveCfg = Debug|Win32 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.Release|Any CPU.ActiveCfg = Release|Win32 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.Release|ARM64.ActiveCfg = Release|ARM64 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.Release|x64.ActiveCfg = Release|x64 - {05D9052F-D78F-478F-968A-2DE38A6DB996}.Release|x86.ActiveCfg = Release|Win32 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.AuditMode|x64.ActiveCfg = Release|x64 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.AuditMode|x86.Build.0 = AuditMode|Win32 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Debug|ARM64.Build.0 = Debug|ARM64 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Debug|x64.ActiveCfg = Debug|x64 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Debug|x64.Build.0 = Debug|x64 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Debug|x86.ActiveCfg = Debug|Win32 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Debug|x86.Build.0 = Debug|Win32 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Release|Any CPU.ActiveCfg = Release|Win32 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Release|ARM64.ActiveCfg = Release|ARM64 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Release|ARM64.Build.0 = Release|ARM64 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Release|x64.ActiveCfg = Release|x64 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Release|x64.Build.0 = Release|x64 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Release|x86.ActiveCfg = Release|Win32 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Release|x86.Build.0 = Release|Win32 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|Any CPU.ActiveCfg = Debug|Win32 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|ARM64.ActiveCfg = Debug|ARM64 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|x64.ActiveCfg = Debug|x64 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|x86.ActiveCfg = Debug|Win32 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|ARM64.Build.0 = Debug|ARM64 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|x64.ActiveCfg = Debug|x64 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|x64.Build.0 = Debug|x64 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|x86.ActiveCfg = Debug|Win32 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|x86.Build.0 = Debug|Win32 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|Any CPU.ActiveCfg = Debug|Win32 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|x64.ActiveCfg = Debug|x64 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|x86.ActiveCfg = Debug|Win32 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|Any CPU.ActiveCfg = Release|Win32 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|ARM64.ActiveCfg = Release|ARM64 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|ARM64.Build.0 = Release|ARM64 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x64.ActiveCfg = Release|x64 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x64.Build.0 = Release|x64 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x86.ActiveCfg = Release|Win32 - {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x86.Build.0 = Release|Win32 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|x64.Build.0 = AuditMode|x64 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|x86.Build.0 = AuditMode|Win32 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|ARM64.Build.0 = Debug|ARM64 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|x64.ActiveCfg = Debug|x64 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|x64.Build.0 = Debug|x64 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|x86.ActiveCfg = Debug|Win32 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|x86.Build.0 = Debug|Win32 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|ARM64.Build.0 = Fuzzing|ARM64 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|x86.Build.0 = Fuzzing|Win32 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|Any CPU.ActiveCfg = Release|Win32 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|ARM64.ActiveCfg = Release|ARM64 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|ARM64.Build.0 = Release|ARM64 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|x64.ActiveCfg = Release|x64 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|x64.Build.0 = Release|x64 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|x86.ActiveCfg = Release|Win32 - {8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|x86.Build.0 = Release|Win32 - {06EC74CB-9A12-428C-B551-8537EC964726}.AuditMode|Any CPU.ActiveCfg = AuditMode|x64 - {06EC74CB-9A12-428C-B551-8537EC964726}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {06EC74CB-9A12-428C-B551-8537EC964726}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {06EC74CB-9A12-428C-B551-8537EC964726}.AuditMode|x64.Build.0 = AuditMode|x64 - {06EC74CB-9A12-428C-B551-8537EC964726}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {06EC74CB-9A12-428C-B551-8537EC964726}.Debug|Any CPU.ActiveCfg = Debug|x64 - {06EC74CB-9A12-428C-B551-8537EC964726}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {06EC74CB-9A12-428C-B551-8537EC964726}.Debug|ARM64.Build.0 = Debug|ARM64 - {06EC74CB-9A12-428C-B551-8537EC964726}.Debug|x64.ActiveCfg = Debug|x64 - {06EC74CB-9A12-428C-B551-8537EC964726}.Debug|x64.Build.0 = Debug|x64 - {06EC74CB-9A12-428C-B551-8537EC964726}.Debug|x86.ActiveCfg = Debug|Win32 - {06EC74CB-9A12-428C-B551-8537EC964726}.Debug|x86.Build.0 = Debug|Win32 - {06EC74CB-9A12-428C-B551-8537EC964726}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|x64 - {06EC74CB-9A12-428C-B551-8537EC964726}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {06EC74CB-9A12-428C-B551-8537EC964726}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {06EC74CB-9A12-428C-B551-8537EC964726}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {06EC74CB-9A12-428C-B551-8537EC964726}.Release|Any CPU.ActiveCfg = Release|x64 - {06EC74CB-9A12-428C-B551-8537EC964726}.Release|ARM64.ActiveCfg = Release|ARM64 - {06EC74CB-9A12-428C-B551-8537EC964726}.Release|ARM64.Build.0 = Release|ARM64 - {06EC74CB-9A12-428C-B551-8537EC964726}.Release|x64.ActiveCfg = Release|x64 - {06EC74CB-9A12-428C-B551-8537EC964726}.Release|x64.Build.0 = Release|x64 - {06EC74CB-9A12-428C-B551-8537EC964726}.Release|x86.ActiveCfg = Release|Win32 - {06EC74CB-9A12-428C-B551-8537EC964726}.Release|x86.Build.0 = Release|Win32 - {75C6F576-18E9-4566-978A-F0A301CAC090}.AuditMode|Any CPU.ActiveCfg = AuditMode|x64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Debug|Any CPU.ActiveCfg = Debug|x64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Debug|ARM64.Build.0 = Debug|ARM64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Debug|x64.ActiveCfg = Debug|x64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Debug|x64.Build.0 = Debug|x64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Debug|x86.ActiveCfg = Debug|Win32 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Debug|x86.Build.0 = Debug|Win32 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|x64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Release|Any CPU.ActiveCfg = Release|x64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Release|ARM64.ActiveCfg = Release|ARM64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Release|ARM64.Build.0 = Release|ARM64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Release|x64.ActiveCfg = Release|x64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Release|x64.Build.0 = Release|x64 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Release|x86.ActiveCfg = Release|Win32 - {75C6F576-18E9-4566-978A-F0A301CAC090}.Release|x86.Build.0 = Release|Win32 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|x64.Build.0 = AuditMode|x64 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|x86.Build.0 = AuditMode|Win32 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|ARM64.Build.0 = Debug|ARM64 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|x64.ActiveCfg = Debug|x64 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|x64.Build.0 = Debug|x64 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|x86.ActiveCfg = Debug|Win32 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|x86.Build.0 = Debug|Win32 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|ARM64.Build.0 = Fuzzing|ARM64 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|x64.Build.0 = Fuzzing|x64 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|x86.Build.0 = Fuzzing|Win32 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|Any CPU.ActiveCfg = Release|Win32 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|ARM64.ActiveCfg = Release|ARM64 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|ARM64.Build.0 = Release|ARM64 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|x64.ActiveCfg = Release|x64 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|x64.Build.0 = Release|x64 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|x86.ActiveCfg = Release|Win32 - {3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|x86.Build.0 = Release|Win32 - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.AuditMode|Any CPU.ActiveCfg = Debug|Any CPU - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.AuditMode|ARM64.ActiveCfg = Debug|Any CPU - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.AuditMode|x64.ActiveCfg = Debug|Any CPU - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.AuditMode|x86.ActiveCfg = Debug|Any CPU - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.Debug|x64.ActiveCfg = Debug|Any CPU - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.Debug|x64.Build.0 = Debug|Any CPU - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.Debug|x86.ActiveCfg = Debug|Any CPU - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.Fuzzing|Any CPU.ActiveCfg = Debug|Any CPU - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.Fuzzing|ARM64.ActiveCfg = Debug|Any CPU - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.Fuzzing|x64.ActiveCfg = Debug|Any CPU - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.Fuzzing|x86.ActiveCfg = Debug|Any CPU - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.Release|Any CPU.Build.0 = Release|Any CPU - {613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.Release|ARM64.ActiveCfg = Release|Any CPU - {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|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|ARM64.ActiveCfg = Debug|ARM64 - {37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|x64.ActiveCfg = Debug|x64 - {37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|x86.ActiveCfg = Debug|Win32 - {37C995E0-2349-4154-8E77-4A52C0C7F46D}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {37C995E0-2349-4154-8E77-4A52C0C7F46D}.Fuzzing|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|ARM64.ActiveCfg = Release|ARM64 - {37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|x64.ActiveCfg = Release|x64 - {37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|x86.ActiveCfg = Release|Win32 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.AuditMode|Any CPU.ActiveCfg = AuditMode|x64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.AuditMode|x64.Build.0 = AuditMode|x64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|Any CPU.ActiveCfg = Debug|x64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|ARM64.Build.0 = Debug|ARM64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|x64.ActiveCfg = Debug|x64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|x64.Build.0 = Debug|x64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|x86.ActiveCfg = Debug|Win32 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|x86.Build.0 = Debug|Win32 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|x64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|Any CPU.ActiveCfg = Release|x64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|ARM64.ActiveCfg = Release|ARM64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|ARM64.Build.0 = Release|ARM64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x64.ActiveCfg = Release|x64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x64.Build.0 = Release|x64 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x86.ActiveCfg = Release|Win32 - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x86.Build.0 = Release|Win32 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|Any CPU.ActiveCfg = AuditMode|x64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|Any CPU.Build.0 = AuditMode|x64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|x64.Build.0 = AuditMode|x64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|x86.Build.0 = AuditMode|Win32 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Debug|Any CPU.ActiveCfg = Debug|x64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Debug|ARM64.Build.0 = Debug|ARM64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Debug|x64.ActiveCfg = Debug|x64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Debug|x64.Build.0 = Debug|x64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Debug|x86.ActiveCfg = Debug|Win32 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Debug|x86.Build.0 = Debug|Win32 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|x64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Release|Any CPU.ActiveCfg = Release|x64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Release|ARM64.ActiveCfg = Release|ARM64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Release|ARM64.Build.0 = Release|ARM64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Release|x64.ActiveCfg = Release|x64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Release|x64.Build.0 = Release|x64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Release|x86.ActiveCfg = Release|Win32 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Release|x86.Build.0 = Release|Win32 - {2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|x64.ActiveCfg = Release|x64 - {2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|x86.ActiveCfg = Release|Win32 - {2C836962-9543-4CE5-B834-D28E1F124B66}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {2C836962-9543-4CE5-B834-D28E1F124B66}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {2C836962-9543-4CE5-B834-D28E1F124B66}.Debug|x64.ActiveCfg = Debug|x64 - {2C836962-9543-4CE5-B834-D28E1F124B66}.Debug|x86.ActiveCfg = Debug|Win32 - {2C836962-9543-4CE5-B834-D28E1F124B66}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {2C836962-9543-4CE5-B834-D28E1F124B66}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {2C836962-9543-4CE5-B834-D28E1F124B66}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {2C836962-9543-4CE5-B834-D28E1F124B66}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {2C836962-9543-4CE5-B834-D28E1F124B66}.Release|Any CPU.ActiveCfg = Release|Win32 - {2C836962-9543-4CE5-B834-D28E1F124B66}.Release|ARM64.ActiveCfg = Release|ARM64 - {2C836962-9543-4CE5-B834-D28E1F124B66}.Release|x64.ActiveCfg = Release|x64 - {2C836962-9543-4CE5-B834-D28E1F124B66}.Release|x86.ActiveCfg = Release|Win32 - {328729E9-6723-416E-9C98-951F1473BBE1}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {328729E9-6723-416E-9C98-951F1473BBE1}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {328729E9-6723-416E-9C98-951F1473BBE1}.AuditMode|x64.ActiveCfg = Release|x64 - {328729E9-6723-416E-9C98-951F1473BBE1}.AuditMode|x86.ActiveCfg = Release|Win32 - {328729E9-6723-416E-9C98-951F1473BBE1}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {328729E9-6723-416E-9C98-951F1473BBE1}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {328729E9-6723-416E-9C98-951F1473BBE1}.Debug|x64.ActiveCfg = Debug|x64 - {328729E9-6723-416E-9C98-951F1473BBE1}.Debug|x86.ActiveCfg = Debug|Win32 - {328729E9-6723-416E-9C98-951F1473BBE1}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {328729E9-6723-416E-9C98-951F1473BBE1}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {328729E9-6723-416E-9C98-951F1473BBE1}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {328729E9-6723-416E-9C98-951F1473BBE1}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {328729E9-6723-416E-9C98-951F1473BBE1}.Release|Any CPU.ActiveCfg = Release|Win32 - {328729E9-6723-416E-9C98-951F1473BBE1}.Release|ARM64.ActiveCfg = Release|ARM64 - {328729E9-6723-416E-9C98-951F1473BBE1}.Release|x64.ActiveCfg = Release|x64 - {328729E9-6723-416E-9C98-951F1473BBE1}.Release|x86.ActiveCfg = Release|Win32 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.AuditMode|Any CPU.ActiveCfg = Debug|Win32 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.AuditMode|ARM64.ActiveCfg = Debug|ARM64 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.AuditMode|x64.ActiveCfg = Debug|x64 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.AuditMode|x86.ActiveCfg = Debug|Win32 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.Debug|ARM64.Build.0 = Debug|ARM64 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.Debug|x64.ActiveCfg = Debug|x64 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.Debug|x64.Build.0 = Debug|x64 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.Debug|x86.ActiveCfg = Debug|Win32 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.Fuzzing|Any CPU.ActiveCfg = Debug|Win32 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.Fuzzing|x64.ActiveCfg = Debug|x64 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.Fuzzing|x86.ActiveCfg = Debug|Win32 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.Release|Any CPU.ActiveCfg = Release|Win32 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.Release|ARM64.ActiveCfg = Release|ARM64 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.Release|ARM64.Build.0 = Release|ARM64 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.Release|x64.ActiveCfg = Release|x64 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.Release|x64.Build.0 = Release|x64 - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48}.Release|x86.ActiveCfg = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {CA5CAD1A-224A-4171-B13A-F16E576FDD12} = {59840756-302F-44DF-AA47-441A9D673202} - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {4C8E6BB0-4713-4ADB-BD04-81628ECEAF20} = {81C352DB-1818-45B7-A284-18E259F1CC87} - {D57841D1-8294-4F2B-BB8B-D2A35738DECD} = {81C352DB-1818-45B7-A284-18E259F1CC87} - {2FD12FBB-1DDB-46D8-B818-1023C624CACA} = {89CDCC5C-9F53-4054-97A4-639D99F169CD} - {3AE13314-1939-4DFA-9C14-38CA0834050C} = {F1995847-4AE5-479A-BBAF-382E51A63532} - {DCF55140-EF6A-4736-A403-957E4F7430BB} = {F1995847-4AE5-479A-BBAF-382E51A63532} - {1CF55140-EF6A-4736-A403-957E4F7430BB} = {F1995847-4AE5-479A-BBAF-382E51A63532} - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F} = {05500DEF-2294-41E3-AF9A-24E580B82836} - {1C959542-BAC2-4E55-9A6D-13251914CBB9} = {05500DEF-2294-41E3-AF9A-24E580B82836} - {06EC74CB-9A12-429C-B551-8562EC954746} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {06EC74CB-9A12-429C-B551-8562EC954747} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {531C23E7-4B76-4C08-8AAD-04164CB628C9} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {531C23E7-4B76-4C08-8BBD-04164CB628C9} = {1E4A062E-293B-4817-B20D-BF16B979E350} - {8CDB8850-7484-4EC7-B45B-181F85B2EE54} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {12144E07-FE63-4D33-9231-748B8D8C3792} = {F1995847-4AE5-479A-BBAF-382E51A63532} - {6AF01638-84CF-4B65-9870-484DFFCAC772} = {F1995847-4AE5-479A-BBAF-382E51A63532} - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE} = {F1995847-4AE5-479A-BBAF-382E51A63532} - {F210A4AE-E02A-4BFC-80BB-F50A672FE763} = {F1995847-4AE5-479A-BBAF-382E51A63532} - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {18D09A24-8240-42D6-8CB6-236EEE820262} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {C17E1BF3-9D34-4779-9458-A8EF98CC5662} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {099193A0-1E43-4BBC-BA7F-7B351E1342DF} = {A10C4720-DCA4-4640-9749-67F4314F527C} - {FC802440-AD6A-4919-8F2C-7701F2B38D79} = {A10C4720-DCA4-4640-9749-67F4314F527C} - {919544AC-D39B-463F-8414-3C3C67CF727C} = {A10C4720-DCA4-4640-9749-67F4314F527C} - {ED82003F-FC5D-4E94-8B36-F480018ED064} = {A10C4720-DCA4-4640-9749-67F4314F527C} - {06EC74CB-9A12-429C-B551-8532EC964726} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {ED82003F-FC5D-4E94-8B47-F480018ED064} = {A10C4720-DCA4-4640-9749-67F4314F527C} - {06EC74CB-9A12-429C-B551-8562EC964846} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB} = {A10C4720-DCA4-4640-9749-67F4314F527C} - {814DBDDE-894E-4327-A6E1-740504850098} = {A10C4720-DCA4-4640-9749-67F4314F527C} - {814CBEEE-894E-4327-A6E1-740504850098} = {A10C4720-DCA4-4640-9749-67F4314F527C} - {18D09A24-8240-42D6-8CB6-236EEE820263} = {89CDCC5C-9F53-4054-97A4-639D99F169CD} - {0CF235BD-2DA0-407E-90EE-C467E8BBC714} = {1E4A062E-293B-4817-B20D-BF16B979E350} - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {59840756-302F-44DF-AA47-441A9D673202} - {CA5CAD1A-ABCD-429C-B551-8562EC954746} = {9921CA0A-320C-4460-8623-3A3196E7F4CB} - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {9921CA0A-320C-4460-8623-3A3196E7F4CB} - {CA5CAD1A-F542-4635-A069-7CAEFB930070} = {9921CA0A-320C-4460-8623-3A3196E7F4CB} - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B} = {2D17E75D-2DDC-42C4-AD70-704D95A937AE} - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} = {59840756-302F-44DF-AA47-441A9D673202} - {F2ED628A-DB22-446F-A081-4CC845B51A2B} = {2D17E75D-2DDC-42C4-AD70-704D95A937AE} - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} - {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {16376381-CE22-42BE-B667-C6B35007008D} = {81C352DB-1818-45B7-A284-18E259F1CC87} - {F1995847-4AE5-479A-BBAF-382E51A63532} = {89CDCC5C-9F53-4054-97A4-639D99F169CD} - {05500DEF-2294-41E3-AF9A-24E580B82836} = {89CDCC5C-9F53-4054-97A4-639D99F169CD} - {1E4A062E-293B-4817-B20D-BF16B979E350} = {89CDCC5C-9F53-4054-97A4-639D99F169CD} - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73} = {89CDCC5C-9F53-4054-97A4-639D99F169CD} - {376FE273-6B84-4EB5-8B30-8DE9D21B022C} = {4DAF0299-495E-4CD1-A982-9BAC16A45932} - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} - {CA5CAD1A-9A12-429C-B551-8562EC954746} = {59840756-302F-44DF-AA47-441A9D673202} - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} - {48D21369-3D7B-4431-9967-24E81292CF63} = {05500DEF-2294-41E3-AF9A-24E580B82836} - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE} = {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C} - {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {A22EC5F6-7851-4B88-AC52-47249D437A52} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} - {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} = {59840756-302F-44DF-AA47-441A9D673202} - {767268EE-174A-46FE-96F0-EEE698A1BBC9} = {89CDCC5C-9F53-4054-97A4-639D99F169CD} - {A602A555-BAAC-46E1-A91D-3DAB0475C5A1} = {A10C4720-DCA4-4640-9749-67F4314F527C} - {53DD5520-E64C-4C06-B472-7CE62CA539C9} = {04170EEF-983A-4195-BFEF-2321E5E38A1E} - {6B5A44ED-918D-4747-BFB1-2472A1FCA173} = {04170EEF-983A-4195-BFEF-2321E5E38A1E} - {D3EF7B96-CD5E-47C9-B9A9-136259563033} = {04170EEF-983A-4195-BFEF-2321E5E38A1E} - {024052DE-83FB-4653-AEA4-90790D29D5BD} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {067F0A06-FCB7-472C-96E9-B03B54E8E18D} = {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C} - {1588FD7C-241E-4E7D-9113-43735F3E6BAD} = {4DAF0299-495E-4CD1-A982-9BAC16A45932} - {506FD703-BAA7-4F6E-9361-64F550EC8FCA} = {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C} - {416FD703-BAA7-4F6E-9361-64F550EC8FCA} = {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C} - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {77875138-BB08-49F9-8BB1-409C2150E0E1} - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {77875138-BB08-49F9-8BB1-409C2150E0E1} - {CA5CAD1A-082C-4476-9F33-94B339494076} = {77875138-BB08-49F9-8BB1-409C2150E0E1} - {CA5CAD1A-9B68-456A-B13E-C8218070DC42} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} - {4DAF0299-495E-4CD1-A982-9BAC16A45932} = {59840756-302F-44DF-AA47-441A9D673202} - {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {2D17E75D-2DDC-42C4-AD70-704D95A937AE} = {59840756-302F-44DF-AA47-441A9D673202} - {77875138-BB08-49F9-8BB1-409C2150E0E1} = {59840756-302F-44DF-AA47-441A9D673202} - {9921CA0A-320C-4460-8623-3A3196E7F4CB} = {59840756-302F-44DF-AA47-441A9D673202} - {05D9052F-D78F-478F-968A-2DE38A6DB996} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} - {F19DACD5-0C6E-40DC-B6E4-767A3200542C} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} - {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C} = {59840756-302F-44DF-AA47-441A9D673202} - {8222900C-8B6C-452A-91AC-BE95DB04B95F} = {05500DEF-2294-41E3-AF9A-24E580B82836} - {06EC74CB-9A12-428C-B551-8537EC964726} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} - {75C6F576-18E9-4566-978A-F0A301CAC090} = {05500DEF-2294-41E3-AF9A-24E580B82836} - {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} - {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F} = {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C} - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F} = {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C} - {2C836962-9543-4CE5-B834-D28E1F124B66} = {A10C4720-DCA4-4640-9749-67F4314F527C} - {328729E9-6723-416E-9C98-951F1473BBE1} = {A10C4720-DCA4-4640-9749-67F4314F527C} - {BE92101C-04F8-48DA-99F0-E1F4F1D2DC48} = {A10C4720-DCA4-4640-9749-67F4314F527C} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271} - EndGlobalSection -EndGlobal diff --git a/OpenConsole.slnx b/OpenConsole.slnx new file mode 100644 index 0000000000..90b231587c --- /dev/null +++ b/OpenConsole.slnx @@ -0,0 +1,1070 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index 71b0daad26..52ebc2521b 100644 --- a/README.md +++ b/README.md @@ -340,6 +340,19 @@ If you would like to ask a question that you feel doesn't warrant an issue ## Prerequisites +You can configure your environment to build Terminal in one of two ways: + +### Using WinGet configuration file + +After cloning the repository, you can use a [WinGet configuration file](https://learn.microsoft.com/en-us/windows/package-manager/configuration/#use-a-winget-configuration-file-to-configure-your-machine) +to set up your environment. The [default configuration file](.config/configuration.winget) installs Visual Studio 2022 Community & rest of the required tools. There are two other variants of the configuration file available in the [.config](.config) directory for Enterprise & Professional editions of Visual Studio 2022. To run the default configuration file, you can either double-click the file from explorer or run the following command: + +```powershell +winget configure .config\configuration.winget +``` + +### Manual configuration + * You must be running Windows 10 2004 (build >= 10.0.19041.0) or later to run Windows Terminal * You must [enable Developer Mode in the Windows Settings @@ -362,7 +375,7 @@ If you would like to ask a question that you feel doesn't warrant an issue ## Building the Code -OpenConsole.sln may be built from within Visual Studio or from the command-line +OpenConsole.slnx may be built from within Visual Studio or from the command-line using a set of convenience scripts & tools in the **/tools** directory: ### Building in PowerShell diff --git a/build/Helix/HelixTestHelpers.cs b/build/Helix/HelixTestHelpers.cs index 67f13937c0..cb8591031e 100644 --- a/build/Helix/HelixTestHelpers.cs +++ b/build/Helix/HelixTestHelpers.cs @@ -28,7 +28,7 @@ namespace HelixTestHelpers public List Screenshots { get; private set; } public List RerunResults { get; private set; } } - + // // Azure DevOps doesn't currently provide a way to directly report sub-results for tests that failed at least once // that were run multiple times. To get around that limitation, we'll mark the test as "Skip" since @@ -49,46 +49,46 @@ namespace HelixTestHelpers // TODO (https://github.com/dotnet/arcade/issues/2773): Once we're able to directly report things in a // more granular fashion than just a binary pass/fail result, we should do that. // - [DataContract] + [DataContract] internal class JsonSerializableTestResults - { + { [DataMember] internal string blobPrefix; - + [DataMember] internal string blobSuffix; - + [DataMember] internal string[] errors; - + [DataMember] internal JsonSerializableTestResult[] results; } - - [DataContract] - internal class JsonSerializableTestResult + + [DataContract] + internal class JsonSerializableTestResult { [DataMember] internal string outcome; [DataMember] internal int duration; - + [DataMember(EmitDefaultValue = false)] internal string log; - + [DataMember(EmitDefaultValue = false)] internal string[] screenshots; - + [DataMember(EmitDefaultValue = false)] internal int errorIndex; } - + public class TestPass { public TimeSpan TestPassExecutionTime { get; set; } public List TestResults { get; set; } - + public static TestPass ParseTestWttFile(string fileName, bool cleanupFailuresAreRegressions, bool truncateTestNames) { using (var stream = File.OpenRead(fileName)) @@ -174,7 +174,7 @@ namespace HelixTestHelpers if (testsExecuting == 1) { string testName = element.Attribute("Title").Value; - + if (truncateTestNames) { const string xamlNativePrefix = "Windows::UI::Xaml::Tests::"; @@ -243,7 +243,7 @@ namespace HelixTestHelpers // The test cleanup errors will often come after the test claimed to have - // 'passed'. We treat them as errors as well. + // 'passed'. We treat them as errors as well. if (inTestCleanup) { currentResult.CleanupPassed = false; @@ -292,7 +292,7 @@ namespace HelixTestHelpers foreach(var screenshot in screenshots) { string fileNameSuffix = string.Empty; - + if (fileName.Contains("_rerun_multiple")) { fileNameSuffix = "_rerun_multiple"; @@ -301,7 +301,7 @@ namespace HelixTestHelpers { fileNameSuffix = "_rerun"; } - + currentResult.Screenshots.Add(screenshot.Replace(".jpg", fileNameSuffix + ".jpg")); } } @@ -313,7 +313,7 @@ namespace HelixTestHelpers testPassStopTime = Int64.Parse(doc.Root.Descendants("WexTraceInfo").Last().Attribute("TimeStamp").Value); var testPassTime = TimeSpan.FromSeconds((double)(testPassStopTime - testPassStartTime) / frequency); - + foreach (TestResult testResult in testResults) { if (testResult.Details != null) @@ -331,13 +331,13 @@ namespace HelixTestHelpers return testpass; } } - + public static TestPass ParseTestWttFileWithReruns(string fileName, string singleRerunFileName, string multipleRerunFileName, bool cleanupFailuresAreRegressions, bool truncateTestNames) { TestPass testPass = ParseTestWttFile(fileName, cleanupFailuresAreRegressions, truncateTestNames); TestPass singleRerunTestPass = File.Exists(singleRerunFileName) ? ParseTestWttFile(singleRerunFileName, cleanupFailuresAreRegressions, truncateTestNames) : null; TestPass multipleRerunTestPass = File.Exists(multipleRerunFileName) ? ParseTestWttFile(multipleRerunFileName, cleanupFailuresAreRegressions, truncateTestNames) : null; - + List rerunTestResults = new List(); if (singleRerunTestPass != null) @@ -377,9 +377,9 @@ namespace HelixTestHelpers public static void OutputFailedTestQuery(string wttInputPath) { var testPass = TestPass.ParseTestWttFile(wttInputPath, cleanupFailuresAreRegressions: true, truncateTestNames: false); - + List failedTestNames = new List(); - + foreach (var result in testPass.TestResults) { if (!result.Passed) @@ -387,23 +387,23 @@ namespace HelixTestHelpers failedTestNames.Add(result.Name); } } - + if (failedTestNames.Count > 0) { string failedTestSelectQuery = "(@Name='"; - + for (int i = 0; i < failedTestNames.Count; i++) { failedTestSelectQuery += failedTestNames[i]; - + if (i < failedTestNames.Count - 1) { failedTestSelectQuery += "' or @Name='"; } } - + failedTestSelectQuery += "')"; - + Console.WriteLine(failedTestSelectQuery); } else @@ -418,7 +418,7 @@ namespace HelixTestHelpers private string testNamePrefix; private string helixResultsContainerUri; private string helixResultsContainerRsas; - + public TestResultParser(string testNamePrefix, string helixResultsContainerUri, string helixResultsContainerRsas) { this.testNamePrefix = testNamePrefix; @@ -430,7 +430,7 @@ namespace HelixTestHelpers { Dictionary subResultsJsonByMethod = new Dictionary(); TestPass testPass = TestPass.ParseTestWttFileWithReruns(wttInputPath, wttSingleRerunInputPath, wttMultipleRerunInputPath, cleanupFailuresAreRegressions: true, truncateTestNames: false); - + foreach (var result in testPass.TestResults) { var methodName = result.Name.Substring(result.Name.LastIndexOf('.') + 1); @@ -488,7 +488,7 @@ namespace HelixTestHelpers int resultCount = results.Count; int passedCount = results.Where(r => r.Passed).Count(); - + // Since we re-run tests on failure, we'll mark every test that failed at least once as "skipped" rather than "failed". // If the test failed sufficiently often enough for it to count as a failed test (determined by a property on the // Azure DevOps job), we'll later mark it as failed during test results processing. @@ -504,15 +504,15 @@ namespace HelixTestHelpers assembly.SetAttributeValue("run-date", DateTime.Now.ToString("yyyy-MM-dd")); // This doesn't need to be completely accurate since it's not exposed anywhere. - // If we need accurate an start time we can probably calculate it from the te.wtl file, but for + // If we need an accurate start time we can probably calculate it from the te.wtl file, but for // now this is fine. assembly.SetAttributeValue("run-time", (DateTime.Now - testPass.TestPassExecutionTime).ToString("hh:mm:ss")); - + assembly.SetAttributeValue("total", resultCount); assembly.SetAttributeValue("passed", passedCount); assembly.SetAttributeValue("failed", failedCount); assembly.SetAttributeValue("skipped", skippedCount); - + assembly.SetAttributeValue("time", (int)testPass.TestPassExecutionTime.TotalSeconds); assembly.SetAttributeValue("errors", 0); root.Add(assembly); @@ -537,9 +537,9 @@ namespace HelixTestHelpers test.SetAttributeValue("method", methodName); test.SetAttributeValue("time", result.ExecutionTime.TotalSeconds); - + string resultString = string.Empty; - + if (result.Passed && !result.Skipped) { resultString = "Pass"; @@ -554,7 +554,7 @@ namespace HelixTestHelpers resultString = "Fail"; } - + if (!result.Passed) { if (result.Skipped) @@ -579,36 +579,36 @@ namespace HelixTestHelpers File.WriteAllText(xunitOutputPath, root.ToString()); } - + private JsonSerializableTestResult ConvertToSerializableResult(TestResult rerunResult, string[] uniqueErrors) { var serializableResult = new JsonSerializableTestResult(); - + serializableResult.outcome = rerunResult.Passed ? "Passed" : "Failed"; serializableResult.duration = (int)Math.Round(rerunResult.ExecutionTime.TotalMilliseconds); - + if (!rerunResult.Passed) { serializableResult.log = Path.GetFileName(rerunResult.SourceWttFile); - + if (rerunResult.Screenshots.Any()) { List screenshots = new List(); - + foreach (var screenshot in rerunResult.Screenshots) { screenshots.Add(Path.GetFileName(screenshot)); } - + serializableResult.screenshots = screenshots.ToArray(); } - + // To conserve space, we'll log the index of the error to index in a list of unique errors rather than // jotting down every single error in its entirety. We'll add one to the result so we can avoid // serializing this property when it has the default value of 0. serializableResult.errorIndex = Array.IndexOf(uniqueErrors, rerunResult.Details) + 1; } - + return serializableResult; } @@ -617,7 +617,7 @@ namespace HelixTestHelpers var filename = Path.GetFileName(filePath); return string.Format("{0}/{1}{2}", helixResultsContainerUri, filename, helixResultsContainerRsas); } - + private string GetTestNameSeparator(string testname) { var separatorString = "."; diff --git a/build/StoreSubmission/Preview/PDPs/de-DE/PDP.xml b/build/StoreSubmission/Preview/PDPs/de-DE/PDP.xml index 488e9d0da7..5e422a72fa 100644 --- a/build/StoreSubmission/Preview/PDPs/de-DE/PDP.xml +++ b/build/StoreSubmission/Preview/PDPs/de-DE/PDP.xml @@ -56,11 +56,11 @@ Dies ist ein Open Source-Projekt, und wir freuen uns über die Teilnahme der Com Version __VERSION_NUMBER__ -- Wir haben der Benutzeroberfläche durchschnittliche Einstellungen hinzugefügt, die nur einmal in der JSON-Datei vorhanden waren, einschließlich einer neuen Seite zum Anpassen des Layouts Ihres Menüs "Neue Registerkarte"! -- Wir haben die Fensterverwaltung zurückgesetzt, um die Zuverlässigkeit zu verbessern; Melden Sie alle Fehler, die mit dem alias "wt.exe" auftreten. -- Profile zeigen jetzt ein Symbol an, wenn sie ausgeblendet wurden oder auf programme verweisen, die deinstalliert wurden. +– Eine komplett neue Erweiterungsseite, die anzeigt, was in Ihrem Terminal installiert ist +– Die Befehlspalette wird jetzt sowohl in Ihrer Muttersprache als auch auf Englisch angezeigt +– Neue VT-Features wie synchronisiertes Rendering, neue Farbschemas, Konfiguration für schnelle Mausaktionen wie Zoomen und mehr -Weitere Details finden Sie auf unserer GitHub-Releasesseite. +Weitere Informationen finden Sie auf unserer GitHub-Releaseseite. diff --git a/build/StoreSubmission/Preview/PDPs/en-US/PDP.xml b/build/StoreSubmission/Preview/PDPs/en-US/PDP.xml index 3e4fa3dbd8..b44683f16b 100644 --- a/build/StoreSubmission/Preview/PDPs/en-US/PDP.xml +++ b/build/StoreSubmission/Preview/PDPs/en-US/PDP.xml @@ -56,9 +56,9 @@ This is an open source project and we welcome community participation. To partic Version __VERSION_NUMBER__ -- We've added dozens of settings to the UI that once only existed in the JSON file, including a new page for customizing the layout of your New Tab menu! -- We have rearchitected window management to improve reliability; please file any bugs you encounter with the wt.exe alias -- Profiles now show an icon if they've been hidden or refer to programs which were uninstalled. +- A whole new Extensions page that shows what has been installed into your Terminal +- Command Palette now shows up in your native language as well as English +- New VT features such as synchronized rendering, new color schemes, configuration for quick mouse actions like zooming, and more Please see our GitHub releases page for additional details. diff --git a/build/StoreSubmission/Preview/PDPs/es-ES/PDP.xml b/build/StoreSubmission/Preview/PDPs/es-ES/PDP.xml index d93f10de27..fbf11ca4e2 100644 --- a/build/StoreSubmission/Preview/PDPs/es-ES/PDP.xml +++ b/build/StoreSubmission/Preview/PDPs/es-ES/PDP.xml @@ -56,11 +56,11 @@ Este es un proyecto de fuente abierta y animamos a la comunidad a participar. Pa Versión __VERSION_NUMBER__ -- Hemos agregado decenas de configuraciones a la interfaz de usuario que solo existían una vez en el archivo JSON, incluida una página nueva para personalizar el diseño del menú Nueva pestaña. -- Tenemos administración de ventanas rearchitecdas para mejorar la confiabilidad; envíe los errores que encuentre con el alias de wt.exe. -- Ahora, los perfiles muestran un icono si se han ocultado o hacen referencia a programas que se han desinstalado. +- Página Extensiones completamente nueva que muestra lo que se ha instalado en tu terminal +- La paleta de comandos ahora se muestra en tu idioma nativo, así como en inglés +- Nuevas características de VT, como la representación sincronizada, nuevos esquemas de color, configuración para acciones rápidas del ratón, como el zoom, y más -Consulte nuestra página de versiones de GitHub para obtener más detalles. +Consulta la página de versiones de GitHub para más información. diff --git a/build/StoreSubmission/Preview/PDPs/fr-FR/PDP.xml b/build/StoreSubmission/Preview/PDPs/fr-FR/PDP.xml index 526be4f3ba..65ca893f6b 100644 --- a/build/StoreSubmission/Preview/PDPs/fr-FR/PDP.xml +++ b/build/StoreSubmission/Preview/PDPs/fr-FR/PDP.xml @@ -54,13 +54,13 @@ Il s’agit d’un projet open source et nous vous invitons à participer dans l - __VERSION_NUMBER__ de version + Version __VERSION_NUMBER__ -- Nous avons ajouté des milliers de paramètres à l’interface utilisateur qui n’existaient auparavant que dans le fichier JSON, y compris une nouvelle page pour personnaliser la disposition de votre menu Nouvel onglet ! -- Nous avons réarchitialiser la gestion des fenêtres pour améliorer la fiabilité ; entrez les bogues rencontrés avec l’alias wt.exe -- Les profils affichent désormais une icône s’ils ont été masqués ou s’ils font référence à des programmes qui ont été désinstallés. +- Une toute nouvelle page Extensions qui montre ce qui a été installé dans votre terminal +- La palette de commandes s’affiche désormais dans votre langue native, ainsi qu’en anglais +- Nouvelles fonctionnalités VT telles que le rendu synchronisé, de nouveaux schémas de couleurs, la configuration pour des actions rapides de la souris comme le zoom, et plus encore -Pour plus d’informations, consultez notre page des mises en production GitHub. +Veuillez consulter notre page des versions GitHub pour découvrir d’autres détails. diff --git a/build/StoreSubmission/Preview/PDPs/it-IT/PDP.xml b/build/StoreSubmission/Preview/PDPs/it-IT/PDP.xml index 8c5ca0a25c..1b0cedc34d 100644 --- a/build/StoreSubmission/Preview/PDPs/it-IT/PDP.xml +++ b/build/StoreSubmission/Preview/PDPs/it-IT/PDP.xml @@ -54,13 +54,13 @@ Si tratta di un progetto open source e la partecipazione della community è molt - Versione __VERSION_NUMBER__ + Versione __VERSION_NUMBER__ -- Sono state aggiunte decine di impostazioni all'interfaccia utente che una volta esisteva solo nel file JSON, inclusa una nuova pagina per personalizzare il layout del menu Nuova scheda. -- È stata riattivata la gestione delle finestre per migliorare l'affidabilità; inserire eventuali bug riscontrati con l'alias wt.exe -- I profili ora mostrano un'icona se sono stati nascosti o fanno riferimento ai programmi che sono stati disinstallati. +- Una pagina Estensioni completamente nuova che mostra ciò che è stato installato nel terminale +- Il riquadro comandi ora viene visualizzato nella tua lingua di origine oltre che in inglese +- Nuove funzionalità VT come il rendering sincronizzato, le nuove combinazioni di colori, la configurazione per azioni rapide del mouse come lo zoom e altro ancora -Per altri dettagli, vedere la pagina delle versioni di GitHub. +Per altri dettagli, vedi la pagina delle release di GitHub. diff --git a/build/StoreSubmission/Preview/PDPs/ja-JP/PDP.xml b/build/StoreSubmission/Preview/PDPs/ja-JP/PDP.xml index 0390ceb54b..86c4ed5a5f 100644 --- a/build/StoreSubmission/Preview/PDPs/ja-JP/PDP.xml +++ b/build/StoreSubmission/Preview/PDPs/ja-JP/PDP.xml @@ -56,11 +56,11 @@ バージョン __VERSION_NUMBER__ -- [新しいタブ] メニューのレイアウトをカスタマイズするための新しいページを含む、一度だけ JSON ファイルに存在した UI に多数の設定を追加しました。 -- 信頼性を向上させるためにウィンドウ管理を再選択しました。wt.exe エイリアスで発生したバグを報告してください -- プロファイルが非表示になっているか、アンインストールされたプログラムを参照している場合にアイコンが表示されるようになりました。 +- ターミナルに何がインストールされているかを表示する新しい [拡張機能] ページ +- コマンド パレットがネイティブ言語と英語で表示されるようになりました +- 同期レンダリング、新しい配色、ズームなどのクイック マウス操作の構成などの、新しい VT 機能 -詳細については、GitHub リリース ページを参照してください。 +詳細については、GitHub リリース ページをご覧ください。 diff --git a/build/StoreSubmission/Preview/PDPs/ko-KR/PDP.xml b/build/StoreSubmission/Preview/PDPs/ko-KR/PDP.xml index 5ca7f5102d..5c3a246c33 100644 --- a/build/StoreSubmission/Preview/PDPs/ko-KR/PDP.xml +++ b/build/StoreSubmission/Preview/PDPs/ko-KR/PDP.xml @@ -56,9 +56,9 @@ 버전 __VERSION_NUMBER__ -- 새 탭 메뉴의 레이아웃을 사용자 지정하기 위한 새 페이지를 포함하여 JSON 파일에 한 번만 존재했던 UI에 수천 개의 설정을 추가했습니다. -- 안정성을 개선하기 위해 창 관리를 다시 보관했습니다. wt.exe 별칭에 발생한 버그를 제출하세요. -- 프로필이 숨겨졌거나 제거된 프로그램을 참조하는 경우 이제 프로필에 아이콘이 표시됩니다. +- 터미널에 설치된 항목을 보여 주는 완전히 새로운 확장 페이지 +- 명령 팔레트가 이제 영어뿐만 아니라 모국어로도 표시 +- 동기화된 렌더링, 새로운 색 구성표, 확대/축소와 같은 빠른 마우스 동작을 위한 구성 등 새로운 VT 기능이 추가 자세한 내용은 GitHub 릴리스 페이지를 참조하세요. diff --git a/build/StoreSubmission/Preview/PDPs/pt-BR/PDP.xml b/build/StoreSubmission/Preview/PDPs/pt-BR/PDP.xml index c6c84a6815..f0cfa9afae 100644 --- a/build/StoreSubmission/Preview/PDPs/pt-BR/PDP.xml +++ b/build/StoreSubmission/Preview/PDPs/pt-BR/PDP.xml @@ -54,13 +54,13 @@ Este é um projeto de código aberto e a participação da comunidade é bem-vin - Versão __VERSION_NUMBER__ + Version __VERSION_NUMBER__ -- Adicionamos várias configurações à interface do usuário que só existiam no arquivo JSON, incluindo uma nova página para personalizar o layout do menu Nova Guia! -- Temos o gerenciamento de janelas rearmado para melhorar a confiabilidade; registre todos os bugs encontrados com o wt.exe alias -- Os perfis agora mostram um ícone se eles foram ocultos ou se referem a programas que foram desinstalados. +– Uma nova página de Extensões que mostra o que foi instalado no seu Terminal +– A Paleta de Comandos agora aparece no seu idioma nativo, além do inglês +– Novos recursos da VT, como renderização sincronizada, novos esquemas de cores, configuração para ações rápidas do mouse, como zoom, e muito mais -Consulte nossa página de versões do GitHub para obter detalhes adicionais. +Confira nossa página de lançamentos no GitHub para obter mais detalhes. diff --git a/build/StoreSubmission/Preview/PDPs/qps-ploc/PDP.xml b/build/StoreSubmission/Preview/PDPs/qps-ploc/PDP.xml index e21d600da0..49fd94316e 100644 --- a/build/StoreSubmission/Preview/PDPs/qps-ploc/PDP.xml +++ b/build/StoreSubmission/Preview/PDPs/qps-ploc/PDP.xml @@ -56,11 +56,11 @@ Vėѓѕіöй __VERSION_NUMBER__ !!! !!! ! -- Ẁē'νё àðđέď đöžзńş öƒ śėŧťїńģš тб тнè ÛĮ ťħąт ŏņ¢з όⁿℓγ έжіѕŧéð іή тђε ЈŠΩŃ ƒїℓė, ĭňĉŀџđіņģ å ňэẅ φâģé ƒøя ςŭśŧŏmïżϊñģ тħέ ĺαŷöυτ öƒ убµř Йέẁ Ţàъ мęήµ! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! -- Ẁè ĥаνė řэąřčħΐŧέсτέð щįлďοш мǻňαĝēмêиť ťô ϊmрябνé ŗĕŀĩāвîĺïтγ; ρŀěăѕе ƒíŀё αⁿу вûġš ÿøú εʼnćōùлťēѓ ẃïτħ ŧћё wt.exe ǻļĭâś !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! -- Рґøƒíŀêŝ ňöẁ šћθẁ ãй ĭčöñ ίƒ ŧħэŷ'νę ъеєл ђіðδэñ őř řєƒěґ ŧσ φяοġгаmŝ ẅђíçĥ ẁ℮гέ џňϊйşťàľĺèð. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! +- Ą ωћόĺé ņέш ∑×τзńşĩōиŝ ρâģε τђат šнòωş ωħąт нǻś ъеēñ įηšтǻľĺéδ ĭʼnтο ўбμŗ Ţзřmĭňāŀ !!! !!! !!! !!! !!! !!! !!! !!! +- €όммаήδ Рдĺēтţĕ пŏẅ şĥŏшś üρ ϊñ ỳоũѓ йαťïνє ļäŋģµаġέ άś ŵєŀľ åś Σиĝℓĭŝђ !!! !!! !!! !!! !!! !!! !!! +- ∏еẅ VΤ ƒэåŧύґέŝ şűçн ăŝ ѕỳňсĥŗǿйìźėð гēŋďзříⁿğ, ηĕш ćôĺõг şĉћěмєѕ, çóńƒіĝџŗáτїöπ ƒοг qũī¢ķ möűšë ąćŧϊόņŝ ľîķє žøōmίйğ, ǻⁿđ мόřε !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! -Рļèāŝє ŝèĕ θџŗ ĢίťĤцъ řέĺэªşэš ρąĝę ƒόř áďđїτϊōπαľ đэŧдįļŝ. !!! !!! !!! !!! !!! !!! +Ρĺęąŝэ ѕєě õμя ĞĭтΗύв řєĺэдšέŝ рάġě ƒοґ àďđϊтїõлаℓ ðêţǻїłş. !!! !!! !!! !!! !!! !!! diff --git a/build/StoreSubmission/Preview/PDPs/qps-ploca/PDP.xml b/build/StoreSubmission/Preview/PDPs/qps-ploca/PDP.xml index e21d600da0..49fd94316e 100644 --- a/build/StoreSubmission/Preview/PDPs/qps-ploca/PDP.xml +++ b/build/StoreSubmission/Preview/PDPs/qps-ploca/PDP.xml @@ -56,11 +56,11 @@ Vėѓѕіöй __VERSION_NUMBER__ !!! !!! ! -- Ẁē'νё àðđέď đöžзńş öƒ śėŧťїńģš тб тнè ÛĮ ťħąт ŏņ¢з όⁿℓγ έжіѕŧéð іή тђε ЈŠΩŃ ƒїℓė, ĭňĉŀџđіņģ å ňэẅ φâģé ƒøя ςŭśŧŏmïżϊñģ тħέ ĺαŷöυτ öƒ убµř Йέẁ Ţàъ мęήµ! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! -- Ẁè ĥаνė řэąřčħΐŧέсτέð щįлďοш мǻňαĝēмêиť ťô ϊmрябνé ŗĕŀĩāвîĺïтγ; ρŀěăѕе ƒíŀё αⁿу вûġš ÿøú εʼnćōùлťēѓ ẃïτħ ŧћё wt.exe ǻļĭâś !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! -- Рґøƒíŀêŝ ňöẁ šћθẁ ãй ĭčöñ ίƒ ŧħэŷ'νę ъеєл ђіðδэñ őř řєƒěґ ŧσ φяοġгаmŝ ẅђíçĥ ẁ℮гέ џňϊйşťàľĺèð. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! +- Ą ωћόĺé ņέш ∑×τзńşĩōиŝ ρâģε τђат šнòωş ωħąт нǻś ъеēñ įηšтǻľĺéδ ĭʼnтο ўбμŗ Ţзřmĭňāŀ !!! !!! !!! !!! !!! !!! !!! !!! +- €όммаήδ Рдĺēтţĕ пŏẅ şĥŏшś üρ ϊñ ỳоũѓ йαťïνє ļäŋģµаġέ άś ŵєŀľ åś Σиĝℓĭŝђ !!! !!! !!! !!! !!! !!! !!! +- ∏еẅ VΤ ƒэåŧύґέŝ şűçн ăŝ ѕỳňсĥŗǿйìźėð гēŋďзříⁿğ, ηĕш ćôĺõг şĉћěмєѕ, çóńƒіĝџŗáτїöπ ƒοг qũī¢ķ möűšë ąćŧϊόņŝ ľîķє žøōmίйğ, ǻⁿđ мόřε !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! -Рļèāŝє ŝèĕ θџŗ ĢίťĤцъ řέĺэªşэš ρąĝę ƒόř áďđїτϊōπαľ đэŧдįļŝ. !!! !!! !!! !!! !!! !!! +Ρĺęąŝэ ѕєě õμя ĞĭтΗύв řєĺэдšέŝ рάġě ƒοґ àďđϊтїõлаℓ ðêţǻїłş. !!! !!! !!! !!! !!! !!! diff --git a/build/StoreSubmission/Preview/PDPs/qps-plocm/PDP.xml b/build/StoreSubmission/Preview/PDPs/qps-plocm/PDP.xml index e21d600da0..49fd94316e 100644 --- a/build/StoreSubmission/Preview/PDPs/qps-plocm/PDP.xml +++ b/build/StoreSubmission/Preview/PDPs/qps-plocm/PDP.xml @@ -56,11 +56,11 @@ Vėѓѕіöй __VERSION_NUMBER__ !!! !!! ! -- Ẁē'νё àðđέď đöžзńş öƒ śėŧťїńģš тб тнè ÛĮ ťħąт ŏņ¢з όⁿℓγ έжіѕŧéð іή тђε ЈŠΩŃ ƒїℓė, ĭňĉŀџđіņģ å ňэẅ φâģé ƒøя ςŭśŧŏmïżϊñģ тħέ ĺαŷöυτ öƒ убµř Йέẁ Ţàъ мęήµ! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! -- Ẁè ĥаνė řэąřčħΐŧέсτέð щįлďοш мǻňαĝēмêиť ťô ϊmрябνé ŗĕŀĩāвîĺïтγ; ρŀěăѕе ƒíŀё αⁿу вûġš ÿøú εʼnćōùлťēѓ ẃïτħ ŧћё wt.exe ǻļĭâś !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! -- Рґøƒíŀêŝ ňöẁ šћθẁ ãй ĭčöñ ίƒ ŧħэŷ'νę ъеєл ђіðδэñ őř řєƒěґ ŧσ φяοġгаmŝ ẅђíçĥ ẁ℮гέ џňϊйşťàľĺèð. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! +- Ą ωћόĺé ņέш ∑×τзńşĩōиŝ ρâģε τђат šнòωş ωħąт нǻś ъеēñ įηšтǻľĺéδ ĭʼnтο ўбμŗ Ţзřmĭňāŀ !!! !!! !!! !!! !!! !!! !!! !!! +- €όммаήδ Рдĺēтţĕ пŏẅ şĥŏшś üρ ϊñ ỳоũѓ йαťïνє ļäŋģµаġέ άś ŵєŀľ åś Σиĝℓĭŝђ !!! !!! !!! !!! !!! !!! !!! +- ∏еẅ VΤ ƒэåŧύґέŝ şűçн ăŝ ѕỳňсĥŗǿйìźėð гēŋďзříⁿğ, ηĕш ćôĺõг şĉћěмєѕ, çóńƒіĝџŗáτїöπ ƒοг qũī¢ķ möűšë ąćŧϊόņŝ ľîķє žøōmίйğ, ǻⁿđ мόřε !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! -Рļèāŝє ŝèĕ θџŗ ĢίťĤцъ řέĺэªşэš ρąĝę ƒόř áďđїτϊōπαľ đэŧдįļŝ. !!! !!! !!! !!! !!! !!! +Ρĺęąŝэ ѕєě õμя ĞĭтΗύв řєĺэдšέŝ рάġě ƒοґ àďđϊтїõлаℓ ðêţǻїłş. !!! !!! !!! !!! !!! !!! diff --git a/build/StoreSubmission/Preview/PDPs/ru-RU/PDP.xml b/build/StoreSubmission/Preview/PDPs/ru-RU/PDP.xml index c5c90ff6da..00a5c2c2b5 100644 --- a/build/StoreSubmission/Preview/PDPs/ru-RU/PDP.xml +++ b/build/StoreSubmission/Preview/PDPs/ru-RU/PDP.xml @@ -56,9 +56,9 @@ Версия __VERSION_NUMBER__ -- Мы добавили в пользовательский интерфейс десятки параметров, которые существовали только в JSON-файле, включая новую страницу для настройки макета меню "Новая вкладка". -- Для повышения надежности мы переупоряхлили управление окнами; создайте все ошибки, обнаруженные с wt.exe псевдонимом -- Профили теперь показывают значок, если они скрыты или ссылаются на программы, которые были удалены. +– Новая страница расширений, на которой отображается информация о том, что было установлено в вашем терминале +– Палитра команд теперь доступна на вашем языке, а также на английском +– Новые функции VT, например синхронизированная отрисовка, новые цветовые схемы, настройка быстрых действий мыши, таких как масштабирование, и т. д. Дополнительные сведения см. на странице выпусков GitHub. diff --git a/build/StoreSubmission/Preview/PDPs/zh-CN/PDP.xml b/build/StoreSubmission/Preview/PDPs/zh-CN/PDP.xml index cd0783394c..036526309a 100644 --- a/build/StoreSubmission/Preview/PDPs/zh-CN/PDP.xml +++ b/build/StoreSubmission/Preview/PDPs/zh-CN/PDP.xml @@ -54,13 +54,13 @@ - 版本 __VERSION_NUMBER__ + Version __VERSION_NUMBER__ -- 我们向用户界面添加了许多设置,这些设置仅存在于 JSON 文件中,包括用于自定义“新建标签页”菜单布局的新页面! -- 我们已重新检测窗口管理以提高可靠性;请将遇到的任何 bug 归档为 wt.exe 别名 -- 如果配置文件已隐藏或引用已卸载的程序,则它们现在将显示一个图标。 +- 一个全新的“扩展”页,显示已安装到终端的内容 +- 命令面板现在以你的母语和英语显示 +- 新的 VT 功能,例如同步渲染、新配色方案、快速鼠标操作(如缩放)的配置等 -有关其他详细信息,请参阅 GitHub 发布页面。 +有关其他详细信息,请参阅我们的 GitHub 发布页面。 diff --git a/build/StoreSubmission/Preview/PDPs/zh-TW/PDP.xml b/build/StoreSubmission/Preview/PDPs/zh-TW/PDP.xml index eeec299d96..0f7e128ff0 100644 --- a/build/StoreSubmission/Preview/PDPs/zh-TW/PDP.xml +++ b/build/StoreSubmission/Preview/PDPs/zh-TW/PDP.xml @@ -54,13 +54,13 @@ - 版本 __VERSION_NUMBER__ + Version __VERSION_NUMBER__ -- 我們已新增數十個只存在於 JSON 檔案中的設定到 UI,包括自定義 [新索引標籤] 功能表版面配置的新頁面! -- 我們已重新設定視窗管理,以改善可靠性;請提出您在 wt.exe 別名遇到的任何錯誤 -- 設定文件現在會在隱藏或參照已卸載的程式時顯示圖示。 +- 全新的延伸模組頁面會顯示已安裝在您終端機中的內容 +- 命令選擇區現在以您的母語和英文顯示 +- 新的 VT 功能,例如同步轉譯、新的色彩配置、快速滑鼠動作 (例如縮放) 設定等等 -如需詳細數據,請參閱我們的 GitHub 版本頁面。 +如需更多詳細資料,請參閱我們的 GitHub 發行版本頁面。 diff --git a/build/StoreSubmission/Preview/SBConfig.json b/build/StoreSubmission/Preview/SBConfig.json index 78facc62bb..0126261767 100644 --- a/build/StoreSubmission/Preview/SBConfig.json +++ b/build/StoreSubmission/Preview/SBConfig.json @@ -20,6 +20,7 @@ "DisableAutoPackageNameFormatting": false }, "appSubmission": { + "appId": "9N8G5RFZ9XK3", "productId": "00014050269303149694", "targetPublishMode": "NotSet", "targetPublishDate": null, diff --git a/build/StoreSubmission/Stable/PDPs/de-DE/PDP.xml b/build/StoreSubmission/Stable/PDPs/de-DE/PDP.xml index 47ff614bd0..c091f84c44 100644 --- a/build/StoreSubmission/Stable/PDPs/de-DE/PDP.xml +++ b/build/StoreSubmission/Stable/PDPs/de-DE/PDP.xml @@ -56,12 +56,9 @@ Dies ist ein Open Source-Projekt, und wir freuen uns über die Teilnahme an der Version __VERSION_NUMBER__ -– Wir haben umgeschrieben, wie Konsolenanwendungen im Terminal gehostet werden! Melden Sie alle auftretenden Fehler. -– Terminal unterstützt jetzt Sixels! -– Sie können jetzt einen angedockten Bereich öffnen, der Ausschnitte von Befehlen enthält, die Sie gespeichert haben, um sie später zu verwenden. -– Für Benutzer der Eingabeaufforderung der neuesten Version von Windows 11 wird möglicherweise ein QuickInfo-Symbol angezeigt, das installierbare Software von WinGet vorschlägt. -– Ausgewählter Text wird jetzt viel sichtbarer (und anpassbarer!). -- Eine Reihe von Zuverlässigkeitsfehlern, Benutzerfreundlichkeitsproblemen und Ärgernissen wurden behoben. +– Wir haben der Benutzeroberfläche Dutzende von Einstellungen hinzugefügt, die nur einmal in der JSON-Datei vorhanden waren, einschließlich einer neuen Seite zum Anpassen des Layouts Ihres Menüs „Neue Registerkarte“! +– Wir haben die Fensterverwaltung umgestaltet, um die Zuverlässigkeit zu verbessern. Melden Sie alle Fehler, die beim alias „wt.exe“ auftreten +– Profile zeigen jetzt ein Symbol an, wenn sie ausgeblendet wurden oder auf Programme verweisen, die deinstalliert wurden. Weitere Informationen finden Sie auf unserer GitHub-Releaseseite. diff --git a/build/StoreSubmission/Stable/PDPs/en-US/PDP.xml b/build/StoreSubmission/Stable/PDPs/en-US/PDP.xml index 993c27e011..95ae5f243c 100644 --- a/build/StoreSubmission/Stable/PDPs/en-US/PDP.xml +++ b/build/StoreSubmission/Stable/PDPs/en-US/PDP.xml @@ -54,14 +54,11 @@ This is an open source project and we welcome community participation. To partic - Version __VERSION_NUMBER__ + Version __VERSION_NUMBER__ -- We've rewritten how console applications are hosted inside Terminal! Please report any bugs you encounter. -- Terminal now supports Sixels! -- You can now open a docked panel containing snippets of commands you have saved to use later -- Command Prompt users on the latest Windows 11 release may see a "quick tip" icon that suggests installable software from WinGet -- Selected text will now be much more visible (and customizable!) -- A number of reliabilty bugs, convenience issues and annoyances have been fixed. +- We've added dozens of settings to the UI that once only existed in the JSON file, including a new page for customizing the layout of your New Tab menu! +- We have rearchitected window management to improve reliability; please file any bugs you encounter with the wt.exe alias +- Profiles now show an icon if they've been hidden or refer to programs which were uninstalled. Please see our GitHub releases page for additional details. diff --git a/build/StoreSubmission/Stable/PDPs/es-ES/PDP.xml b/build/StoreSubmission/Stable/PDPs/es-ES/PDP.xml index 84bd2cc399..a0829b4749 100644 --- a/build/StoreSubmission/Stable/PDPs/es-ES/PDP.xml +++ b/build/StoreSubmission/Stable/PDPs/es-ES/PDP.xml @@ -56,12 +56,9 @@ Este es un proyecto de fuente abierta y animamos a la comunidad a participar. Pa Versión __VERSION_NUMBER__ -- Hemos reescrito cómo se hospedan las aplicaciones de consola en Terminal. Informe de los errores que encuentre. -- Terminal ahora admite síxeles. -- Ahora puede abrir un panel acoplado que contenga fragmentos de comandos que haya guardado para usarlos más adelante -- Los usuarios del símbolo del sistema de la versión más reciente de Windows 11 pueden ver un icono de "sugerencia rápida" que sugiere software instalable de WinGet -- El texto seleccionado ahora será mucho más visible (y personalizable) -- Se han corregido varios errores de fiabilidad, problemas de comodidad y molestias. +- Hemos añadido decenas de configuraciones a la interfaz de usuario que antes solo existían en el archivo JSON, incluida una nueva página para personalizar el diseño del menú Nueva pestaña. +- Hemos reestructurado la gestión de ventanas para mejorar la fiabilidad; informe de cualquier error que encuentre con el alias wt.exe +- Ahora, los perfiles muestran un icono si han sido ocultados o hacen referencia a programas que han sido desinstalados. Consulte la página de versiones de GitHub para más información. diff --git a/build/StoreSubmission/Stable/PDPs/fr-FR/PDP.xml b/build/StoreSubmission/Stable/PDPs/fr-FR/PDP.xml index 34c6a13ea8..c20e71bad7 100644 --- a/build/StoreSubmission/Stable/PDPs/fr-FR/PDP.xml +++ b/build/StoreSubmission/Stable/PDPs/fr-FR/PDP.xml @@ -56,12 +56,9 @@ Il s’agit d’un projet open source et nous encourageons la participation à l Version __VERSION_NUMBER__ -– Nous avons réécrit la manière dont les applications de console sont hébergées dans Terminal ! Veuillez signaler tout bogue que vous rencontrez. -– Terminal prend désormais en charge Sixels ! -– Vous pouvez maintenant ouvrir un panneau ancré contenant des extraits de commandes que vous avez enregistrées pour les utiliser ultérieurement -– Les utilisateurs de l’invite de commande sur la dernière version de Windows 11 peuvent voir une icône « astuce rapide » qui suggère un logiciel installable à partir de WinGet -– Le texte sélectionné sera désormais beaucoup plus visible (et personnalisable !) -– Un certain nombre de bogues de fiabilité, de problèmes de commodité et de désagréments ont été corrigés. +- Nous avons ajouté des dizaines de paramètres à l’interface utilisateur qui n’existaient auparavant que dans le fichier JSON, y compris une nouvelle page pour personnaliser la disposition de votre menu Nouvel onglet. +- Nous avons fait une refonte de la gestion des fenêtres pour améliorer la fiabilité. Veuillez signaler les bogues que vous rencontrez avec l’alias wt.exe. +- Les profils affichent désormais une icône s’ils ont été masqués ou s’ils font référence à des programmes qui ont été désinstallés. Veuillez consulter notre page des versions GitHub pour découvrir d’autres détails. diff --git a/build/StoreSubmission/Stable/PDPs/it-IT/PDP.xml b/build/StoreSubmission/Stable/PDPs/it-IT/PDP.xml index 9d7ce218e8..679a697596 100644 --- a/build/StoreSubmission/Stable/PDPs/it-IT/PDP.xml +++ b/build/StoreSubmission/Stable/PDPs/it-IT/PDP.xml @@ -56,12 +56,9 @@ Si tratta di un progetto open source e la partecipazione della community è molt Versione __VERSION_NUMBER__ -- Abbiamo cambiato il modo in cui le applicazioni della console vengono ospitate all’interno di Terminale. Segnala eventuali bug riscontrati. -- Ora Terminale supporta i Sixel. -- Puoi aprire un pannello ancorato contenente frammenti di comandi salvati per usarli in seguito -- Gli utenti che usano il prompt dei comandi nella versione più recente di Windows 11 potrebbero visualizzare un’icona di “suggerimento rapido” che consiglia il software installabile da WinGet -- Il testo selezionato sarà ora molto più visibile, oltre che personalizzabile. -- Sono stati risolti diversi bug di affidabilità e problemi di ordine pratico. +- Abbiamo aggiunto decine di impostazioni all'interfaccia utente che in precedenza esistevano solo nel file JSON, inclusa una nuova pagina per personalizzare il layout del menu Nuova scheda. +- Abbiamo riprogettato la gestione delle finestre per migliorarne l'affidabilità; segnala eventuali bug riscontrati con l'alias wt.exe +- I profili ora mostrano un'icona se sono stati nascosti o se fanno riferimento a programmi disinstallati. Per altri dettagli, vedi la pagina delle release di GitHub. diff --git a/build/StoreSubmission/Stable/PDPs/ja-JP/PDP.xml b/build/StoreSubmission/Stable/PDPs/ja-JP/PDP.xml index 27a38762bb..73de6da4fc 100644 --- a/build/StoreSubmission/Stable/PDPs/ja-JP/PDP.xml +++ b/build/StoreSubmission/Stable/PDPs/ja-JP/PDP.xml @@ -56,12 +56,9 @@ バージョン __VERSION_NUMBER__ -- ターミナル内でのコンソール アプリケーションのホスト方法を書き換えました。発生したバグを報告してください。 -- ターミナルで Sixels がサポートされるようになりました。 -- 後で使用するために保存したコマンドのスニペットを含むドッキング パネルを開けるようになりました -- 最新の Windows 11 リリースのコマンド プロンプト ユーザーには、WinGet からインストール可能なソフトウェアを提案する "クイック ヒント" アイコンが表示される場合があります -- 選択したテキストが大幅に見やすくなりました (カスタマイズも可能です) -- 信頼性に関するバグ、利便性の問題、不快な問題の多くが修正されました。 +- 新しいタブ メニューのレイアウトをカスタマイズするための新しいページなど、以前は JSON ファイルにしかなかった設定が UI に多数追加されました。 +- 信頼性を向上させるために、ウィンドウ管理を再設計しました。wt.exe エイリアスで発生したバグを報告してください +- プロファイルが非表示になっている場合や、アンインストールされたプログラムを参照している場合に、アイコンが表示されるようになりました。 詳細については、GitHub リリース ページをご覧ください。 diff --git a/build/StoreSubmission/Stable/PDPs/ko-KR/PDP.xml b/build/StoreSubmission/Stable/PDPs/ko-KR/PDP.xml index 8dac845667..16b2e850cd 100644 --- a/build/StoreSubmission/Stable/PDPs/ko-KR/PDP.xml +++ b/build/StoreSubmission/Stable/PDPs/ko-KR/PDP.xml @@ -56,12 +56,9 @@ 버전 __VERSION_NUMBER__ -- 콘솔 애플리케이션이 터미널 내에서 호스팅되는 방법을 다시 작성했습니다. 버그가 발생하면 보고해 주세요. -- 터미널에서 이제 Sixels를 지원합니다. -- 이제 나중에 사용하기 위해 저장한 명령 조각이 포함된 도킹된 패널을 열 수 있습니다. -- 최신 Windows 11 릴리스의 명령 프롬프트 사용자는 WinGet에서 설치 가능한 소프트웨어를 추천하는 "간단한 팁" 아이콘을 볼 수 있습니다. -- 이제 선택한 텍스트가 훨씬 더 잘 보입니다(사용자 지정 가능). -- 여러 안정성 버그, 편의성 문제, 불편 사항이 수정되었습니다. +- 새 탭 메뉴의 레이아웃을 사용자 지정하기 위한 새 페이지를 포함하여 JSON 파일에만 존재했던 수십 개의 설정을 UI에 추가 +- 안정성을 개선하기 위해 창 관리 구조를 재구성했습니다. wt.exe 별칭과 관련하여 발생한 버그 신고 +- 프로필이 숨겨졌거나 제거된 프로그램을 참조하는 경우 이제 프로필에 아이콘이 표시됩니다. 자세한 내용은 GitHub 릴리스 페이지를 참조하세요. diff --git a/build/StoreSubmission/Stable/PDPs/pt-BR/PDP.xml b/build/StoreSubmission/Stable/PDPs/pt-BR/PDP.xml index cd3b5ce368..4bdec555c7 100644 --- a/build/StoreSubmission/Stable/PDPs/pt-BR/PDP.xml +++ b/build/StoreSubmission/Stable/PDPs/pt-BR/PDP.xml @@ -54,14 +54,11 @@ Este é um projeto de código aberto e a participação da comunidade é bem-vin - Versão __VERSION_NUMBER__ + Version __VERSION_NUMBER__ -– Reescrevemos a forma como os aplicativos de console são hospedados no Terminal! Relate os bugs encontrados. -– O terminal agora oferece suporte ao Sixels! -– Agora você pode abrir um painel acoplado contendo snippets de comandos que você salvou para usar mais tarde -– Os usuários do Prompt de Comando na versão mais recente do Windows 11 podem ver um ícone de "dica rápida", que sugere softwares instaláveis a partir do WinGet -– O texto selecionado agora ficará muito mais visível (e personalizável!) -– Vários bugs de confiabilidade, problemas de conveniência e incômodos foram resolvidos. +– Adicionamos várias configurações à interface do usuário que antes só existiam no arquivo JSON, incluindo uma nova página para personalizar o layout do seu menu Nova Guia! +– Reestruturamos o gerenciamento de janelas para melhorar a confiabilidade; registre os bugs que você encontrar com o alias wt.exe +– Os perfis agora exibem um ícone se estiverem ocultos ou se referirem a programas que foram desinstalados. Confira nossa página de lançamentos no GitHub para obter mais detalhes. diff --git a/build/StoreSubmission/Stable/PDPs/qps-ploc/PDP.xml b/build/StoreSubmission/Stable/PDPs/qps-ploc/PDP.xml index d911bbd69b..c51b58de58 100644 --- a/build/StoreSubmission/Stable/PDPs/qps-ploc/PDP.xml +++ b/build/StoreSubmission/Stable/PDPs/qps-ploc/PDP.xml @@ -56,14 +56,11 @@ Vėѓѕіöй __VERSION_NUMBER__ !!! !!! ! -- Ẁē'νё ŕéẁѓĭτťёñ ћοώ ĉòπşõℓε άррℓіċªťįõпѕ αяе ĥθѕťэđ įŋšιďé Ţєямїńąℓ! Рļéаšė яёροřτ αņу ьϋģš ýõμ éпćŏџήţęя. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! -- Ţëямΐʼnαļ ńóẃ ŝüррöятš Śїхέłś! !!! !!! !!! -- ¥оų ĉåи ńòŵ θρėñ д đбčĸэď ράńέļ ċőлŧăīņϊňģ śⁿіφφëťś оƒ ςōмmàⁿďş ŷŏũ ĥªν℮ şåνěđ τσ üśε łαťэŗ !!! !!! !!! !!! !!! !!! !!! !!! !!! -- Ćοмmäлđ Рřōmφť üş℮ŗѕ öη τће ļāťëšτ Щīйđôώѕ 11 řёℓеаѕĕ måў ŝэε ά "qůïςκ ŧĭр" ιсôñ τĥдт šűğģєѕŧѕ ίńśŧăłłавļз šôƒţẁαгέ ƒґόm ЩĩйĞéţ !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! -- Śєļèċťєď ţĕжт ωϊŀļ йǿẃ ьέ mџ¢н мǿѓε νĭŝϊъļė (άŋđ сŭŝтŏмΐżдьļē!) !!! !!! !!! !!! !!! !!! ! -- Ä ņϋmъ℮ŗ ŏƒ ѓēŀїаъïļŧÿ ьüĝś, ςôⁿνėηĭ℮иć℮ îѕšůëş ăπð âлňбγдňçėŝ ћªνε ъēёп ƒΐ×еð. !!! !!! !!! !!! !!! !!! !!! !!! +- Ẁē'νё àðđέď đöžзńş öƒ śėŧťїńģš тб тнè ÛĮ ťħąт ŏņ¢з όⁿℓγ έжіѕŧéð іή тђε ЈŠΩŃ ƒїℓė, ĭňĉŀџđіņģ å ňэẅ φâģé ƒøя ςŭśŧŏmïżϊñģ тħέ ĺαŷöυτ öƒ убµř Йέẁ Ţàъ мęήµ! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! +- Ẁè ĥаνė řэąřčħΐŧέсτέð щįлďοш мǻňαĝēмêиť ťô ϊmрябνé ŗĕŀĩāвîĺïтγ; ρŀěăѕе ƒíŀё αⁿу вûġš ÿøú εʼnćōùлťēѓ ẃïτħ ŧћё wt.exe ǻļĭâś !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! +- Рґøƒíŀêŝ ňöẁ šћθẁ ãй ĭčöñ ίƒ ŧħэŷ'νę ъеєл ђіðδэñ őř řєƒěґ ŧσ φяοġгаmŝ ẅђíçĥ ẁ℮гέ џňϊйşťàľĺèð. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! -Ρĺёàŝ℮ ŝез ǿúг ĢīťНŭъ řěłεαśèŝ φāğ℮ ƒóѓ дďδітĭøиąℓ ð℮тªїľŝ. !!! !!! !!! !!! !!! !!! +Рļèāŝє ŝèĕ θџŗ ĢίťĤцъ řέĺэªşэš ρąĝę ƒόř áďđїτϊōπαľ đэŧдįļŝ. !!! !!! !!! !!! !!! !!! diff --git a/build/StoreSubmission/Stable/PDPs/qps-ploca/PDP.xml b/build/StoreSubmission/Stable/PDPs/qps-ploca/PDP.xml index d911bbd69b..c51b58de58 100644 --- a/build/StoreSubmission/Stable/PDPs/qps-ploca/PDP.xml +++ b/build/StoreSubmission/Stable/PDPs/qps-ploca/PDP.xml @@ -56,14 +56,11 @@ Vėѓѕіöй __VERSION_NUMBER__ !!! !!! ! -- Ẁē'νё ŕéẁѓĭτťёñ ћοώ ĉòπşõℓε άррℓіċªťįõпѕ αяе ĥθѕťэđ įŋšιďé Ţєямїńąℓ! Рļéаšė яёροřτ αņу ьϋģš ýõμ éпćŏџήţęя. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! -- Ţëямΐʼnαļ ńóẃ ŝüррöятš Śїхέłś! !!! !!! !!! -- ¥оų ĉåи ńòŵ θρėñ д đбčĸэď ράńέļ ċőлŧăīņϊňģ śⁿіφφëťś оƒ ςōмmàⁿďş ŷŏũ ĥªν℮ şåνěđ τσ üśε łαťэŗ !!! !!! !!! !!! !!! !!! !!! !!! !!! -- Ćοмmäлđ Рřōmφť üş℮ŗѕ öη τће ļāťëšτ Щīйđôώѕ 11 řёℓеаѕĕ måў ŝэε ά "qůïςκ ŧĭр" ιсôñ τĥдт šűğģєѕŧѕ ίńśŧăłłавļз šôƒţẁαгέ ƒґόm ЩĩйĞéţ !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! -- Śєļèċťєď ţĕжт ωϊŀļ йǿẃ ьέ mџ¢н мǿѓε νĭŝϊъļė (άŋđ сŭŝтŏмΐżдьļē!) !!! !!! !!! !!! !!! !!! ! -- Ä ņϋmъ℮ŗ ŏƒ ѓēŀїаъïļŧÿ ьüĝś, ςôⁿνėηĭ℮иć℮ îѕšůëş ăπð âлňбγдňçėŝ ћªνε ъēёп ƒΐ×еð. !!! !!! !!! !!! !!! !!! !!! !!! +- Ẁē'νё àðđέď đöžзńş öƒ śėŧťїńģš тб тнè ÛĮ ťħąт ŏņ¢з όⁿℓγ έжіѕŧéð іή тђε ЈŠΩŃ ƒїℓė, ĭňĉŀџđіņģ å ňэẅ φâģé ƒøя ςŭśŧŏmïżϊñģ тħέ ĺαŷöυτ öƒ убµř Йέẁ Ţàъ мęήµ! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! +- Ẁè ĥаνė řэąřčħΐŧέсτέð щįлďοш мǻňαĝēмêиť ťô ϊmрябνé ŗĕŀĩāвîĺïтγ; ρŀěăѕе ƒíŀё αⁿу вûġš ÿøú εʼnćōùлťēѓ ẃïτħ ŧћё wt.exe ǻļĭâś !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! +- Рґøƒíŀêŝ ňöẁ šћθẁ ãй ĭčöñ ίƒ ŧħэŷ'νę ъеєл ђіðδэñ őř řєƒěґ ŧσ φяοġгаmŝ ẅђíçĥ ẁ℮гέ џňϊйşťàľĺèð. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! -Ρĺёàŝ℮ ŝез ǿúг ĢīťНŭъ řěłεαśèŝ φāğ℮ ƒóѓ дďδітĭøиąℓ ð℮тªїľŝ. !!! !!! !!! !!! !!! !!! +Рļèāŝє ŝèĕ θџŗ ĢίťĤцъ řέĺэªşэš ρąĝę ƒόř áďđїτϊōπαľ đэŧдįļŝ. !!! !!! !!! !!! !!! !!! diff --git a/build/StoreSubmission/Stable/PDPs/qps-plocm/PDP.xml b/build/StoreSubmission/Stable/PDPs/qps-plocm/PDP.xml index d911bbd69b..c51b58de58 100644 --- a/build/StoreSubmission/Stable/PDPs/qps-plocm/PDP.xml +++ b/build/StoreSubmission/Stable/PDPs/qps-plocm/PDP.xml @@ -56,14 +56,11 @@ Vėѓѕіöй __VERSION_NUMBER__ !!! !!! ! -- Ẁē'νё ŕéẁѓĭτťёñ ћοώ ĉòπşõℓε άррℓіċªťįõпѕ αяе ĥθѕťэđ įŋšιďé Ţєямїńąℓ! Рļéаšė яёροřτ αņу ьϋģš ýõμ éпćŏџήţęя. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !! -- Ţëямΐʼnαļ ńóẃ ŝüррöятš Śїхέłś! !!! !!! !!! -- ¥оų ĉåи ńòŵ θρėñ д đбčĸэď ράńέļ ċőлŧăīņϊňģ śⁿіφφëťś оƒ ςōмmàⁿďş ŷŏũ ĥªν℮ şåνěđ τσ üśε łαťэŗ !!! !!! !!! !!! !!! !!! !!! !!! !!! -- Ćοмmäлđ Рřōmφť üş℮ŗѕ öη τће ļāťëšτ Щīйđôώѕ 11 řёℓеаѕĕ måў ŝэε ά "qůïςκ ŧĭр" ιсôñ τĥдт šűğģєѕŧѕ ίńśŧăłłавļз šôƒţẁαгέ ƒґόm ЩĩйĞéţ !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! -- Śєļèċťєď ţĕжт ωϊŀļ йǿẃ ьέ mџ¢н мǿѓε νĭŝϊъļė (άŋđ сŭŝтŏмΐżдьļē!) !!! !!! !!! !!! !!! !!! ! -- Ä ņϋmъ℮ŗ ŏƒ ѓēŀїаъïļŧÿ ьüĝś, ςôⁿνėηĭ℮иć℮ îѕšůëş ăπð âлňбγдňçėŝ ћªνε ъēёп ƒΐ×еð. !!! !!! !!! !!! !!! !!! !!! !!! +- Ẁē'νё àðđέď đöžзńş öƒ śėŧťїńģš тб тнè ÛĮ ťħąт ŏņ¢з όⁿℓγ έжіѕŧéð іή тђε ЈŠΩŃ ƒїℓė, ĭňĉŀџđіņģ å ňэẅ φâģé ƒøя ςŭśŧŏmïżϊñģ тħέ ĺαŷöυτ öƒ убµř Йέẁ Ţàъ мęήµ! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! +- Ẁè ĥаνė řэąřčħΐŧέсτέð щįлďοш мǻňαĝēмêиť ťô ϊmрябνé ŗĕŀĩāвîĺïтγ; ρŀěăѕе ƒíŀё αⁿу вûġš ÿøú εʼnćōùлťēѓ ẃïτħ ŧћё wt.exe ǻļĭâś !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! +- Рґøƒíŀêŝ ňöẁ šћθẁ ãй ĭčöñ ίƒ ŧħэŷ'νę ъеєл ђіðδэñ őř řєƒěґ ŧσ φяοġгаmŝ ẅђíçĥ ẁ℮гέ џňϊйşťàľĺèð. !!! !!! !!! !!! !!! !!! !!! !!! !!! ! -Ρĺёàŝ℮ ŝез ǿúг ĢīťНŭъ řěłεαśèŝ φāğ℮ ƒóѓ дďδітĭøиąℓ ð℮тªїľŝ. !!! !!! !!! !!! !!! !!! +Рļèāŝє ŝèĕ θџŗ ĢίťĤцъ řέĺэªşэš ρąĝę ƒόř áďđїτϊōπαľ đэŧдįļŝ. !!! !!! !!! !!! !!! !!! diff --git a/build/StoreSubmission/Stable/PDPs/ru-RU/PDP.xml b/build/StoreSubmission/Stable/PDPs/ru-RU/PDP.xml index 011e5152d7..c84a1bc57c 100644 --- a/build/StoreSubmission/Stable/PDPs/ru-RU/PDP.xml +++ b/build/StoreSubmission/Stable/PDPs/ru-RU/PDP.xml @@ -56,12 +56,9 @@ Версия __VERSION_NUMBER__ -– Мы переписали, как консольные приложения размещаются внутри Терминала! Сообщайте о любых ошибках, с которыми вы столкнулись. -– Терминал теперь поддерживает форматы Sixel! -– Теперь вы можете открыть закрепленную панель, содержащую фрагменты команд, которые вы сохранили для использования в дальнейшем -– Пользователи командной строки в новейшем выпуске Windows 11 могут увидеть значок "краткой подсказки", который предлагает устанавливаемые программы из WinGet -– Выделенный текст теперь станет более видимым (и настраиваемым!) -– Исправлено несколько ошибок надежности, проблем с удобством, а также устранены раздражающие моменты. +– Мы добавили в пользовательский интерфейс десятки параметров, которые ранее существовали только в JSON-файле, включая новую страницу для настройки макета меню новой вкладки. +– Мы переработали управление окнами для повышения надежности. Сообщайте о любых ошибках, которые вы обнаружите с псевдонимом wt.exe +– Профили теперь отображают значок, если они были скрыты или ссылаются на программы, которые были удалены. Дополнительные сведения см. на странице выпусков GitHub. diff --git a/build/StoreSubmission/Stable/PDPs/zh-CN/PDP.xml b/build/StoreSubmission/Stable/PDPs/zh-CN/PDP.xml index 21db7d2c3d..b96bbe1def 100644 --- a/build/StoreSubmission/Stable/PDPs/zh-CN/PDP.xml +++ b/build/StoreSubmission/Stable/PDPs/zh-CN/PDP.xml @@ -56,12 +56,9 @@ Version __VERSION_NUMBER__ -- 我们已改变主机应用程序在终端内的托管方式!请报告遇到的任何 bug。 -- 终端现在支持 Sixels! -- 现在可以打开一个停靠面板,其中包含已保存供以后使用的命令片段 -- 最新 Windows 11 版本上的命令提示用户可能会看到“快速提示”图标,该图标建议从 WinGet 安装软件 -- 所选文本现在将具有更高的可见性(和可自定义性!) -- 修复了许多可靠性 bug、便利性问题和令人烦恼的问题。 +- 我们向用户界面添加了许多之前仅存在于 JSON 文件中的设置,包括用于自定义“新建标签页”菜单布局的新页面! +- 我们已重新架构窗口管理以提高可靠性; 请使用 wt.exe 别名提交您遇到的任何错误 +- 配置文件如果已被隐藏或引用了已卸载的程序,现在会显示一个图标。 有关其他详细信息,请参阅我们的 GitHub 发布页面。 diff --git a/build/StoreSubmission/Stable/PDPs/zh-TW/PDP.xml b/build/StoreSubmission/Stable/PDPs/zh-TW/PDP.xml index 02e5b69a3c..d64faf5125 100644 --- a/build/StoreSubmission/Stable/PDPs/zh-TW/PDP.xml +++ b/build/StoreSubmission/Stable/PDPs/zh-TW/PDP.xml @@ -54,16 +54,13 @@ - 版本 __VERSION_NUMBER__ + Version __VERSION_NUMBER__ -- 我們已重寫主機應用程式在終端機內託管的方式!請報告您遇到的錯誤。 -- 終端機現在支援 Sixels! -- 現在,您可以開啟包含已儲存命令程式碼片段的固定面板,以供稍後使用 -- 最新 Windows 11 版本中的 [命令提示] 使用者可能會看到「快速提示」圖示,建議可自 WinGet 安裝的軟體 -- 選取的文字現在會更明顯 (且可自訂!) -- 已修正一些可靠性錯誤、便利性問題和令人困擾的問題。 +- 我們已在使用者介面中新增數十個曾經僅存在於 JSON 檔案中的設定,包括一個可自訂新索引標籤選單版面配置的新頁面! +- 我們已重新架構視窗管理以提升可靠性; 如果您在使用 wt.exe 別名時遇到任何錯誤,請提交錯誤回報 +- 如果設定檔已隱藏或參照已解除安裝的程式,現在會顯示圖示。 -如需更多詳細資訊,請參閱我們的 GitHub 發行頁面。 +如需更多詳細資料,請參閱我們的 GitHub 發行版本頁面。 diff --git a/build/StoreSubmission/Stable/SBConfig.json b/build/StoreSubmission/Stable/SBConfig.json index c38d53443b..b498c92ad3 100644 --- a/build/StoreSubmission/Stable/SBConfig.json +++ b/build/StoreSubmission/Stable/SBConfig.json @@ -20,6 +20,7 @@ "DisableAutoPackageNameFormatting": false }, "appSubmission": { + "appId": "9N0DX20HK701", "productId": "00013926773940052066", "targetPublishMode": "NotSet", "targetPublishDate": null, diff --git a/build/config/272MSSharedLibSN2048.snk b/build/config/272MSSharedLibSN2048.snk new file mode 100644 index 0000000000..bd766f84a2 Binary files /dev/null and b/build/config/272MSSharedLibSN2048.snk differ diff --git a/build/config/esrp.build.batch.wpfdotnet.json b/build/config/esrp.build.batch.wpfdotnet.json index 0699353f64..2f36710deb 100644 --- a/build/config/esrp.build.batch.wpfdotnet.json +++ b/build/config/esrp.build.batch.wpfdotnet.json @@ -2,10 +2,24 @@ { "MatchedPath": [ "WpfTerminalControl/net472/Microsoft.Terminal.Wpf.dll", - "WpfTerminalControl/net6.0-windows/Microsoft.Terminal.Wpf.dll" + "WpfTerminalControl/net8.0-windows/Microsoft.Terminal.Wpf.dll" ], "SigningInfo": { "Operations": [ + { + "KeyCode": "CP-233904-SN", + "OperationSetCode": "StrongNameSign", + "ToolName": "sign", + "ToolVersion": "1.0", + "Parameters": [] + }, + { + "KeyCode": "CP-233904-SN", + "OperationSetCode": "StrongNameVerify", + "ToolName": "sign", + "ToolVersion": "1.0", + "Parameters": [] + }, { "KeyCode": "CP-230012", "OperationSetCode": "SigntoolSign", diff --git a/build/config/template.appinstaller b/build/config/template.appinstaller index 7977fd950b..fe94eed78b 100644 --- a/build/config/template.appinstaller +++ b/build/config/template.appinstaller @@ -14,21 +14,21 @@ + Uri="https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.5/Microsoft.UI.Xaml.2.8.x64.appx" /> + Uri="https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.5/Microsoft.UI.Xaml.2.8.x86.appx" /> + Uri="https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.5/Microsoft.UI.Xaml.2.8.arm64.appx" /> diff --git a/build/config/tsa.json b/build/config/tsa.json index f07bc0b535..61924ef4bd 100644 --- a/build/config/tsa.json +++ b/build/config/tsa.json @@ -1,6 +1,6 @@ { "instanceUrl": "https://microsoft.visualstudio.com", "projectName": "OS", - "areaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\SHINE\\Terminal", + "areaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SHINE\\Terminal", "notificationAliases": ["condev@microsoft.com", "duhowett@microsoft.com"] } diff --git a/build/packages.config b/build/packages.config index cc0e0ada0b..3572da0e54 100644 --- a/build/packages.config +++ b/build/packages.config @@ -1,6 +1,5 @@ - diff --git a/build/pipelines/daily-loc-submission.yml b/build/pipelines/daily-loc-submission.yml index 403501ed32..7fddafa6cc 100644 --- a/build/pipelines/daily-loc-submission.yml +++ b/build/pipelines/daily-loc-submission.yml @@ -47,7 +47,7 @@ steps: git config --local core.autocrlf true displayName: Prepare git submission environment -- task: MicrosoftTDBuild.tdbuild-task.tdbuild-task.TouchdownBuildTask@3 +- task: MicrosoftTDBuild.tdbuild-task.tdbuild-task.TouchdownBuildTask@5 displayName: 'Touchdown Build - 7105, PRODEXT' inputs: teamId: 7105 diff --git a/build/pipelines/ob-nightly.yml b/build/pipelines/ob-nightly.yml index 033d075cd6..63eba379d8 100644 --- a/build/pipelines/ob-nightly.yml +++ b/build/pipelines/ob-nightly.yml @@ -50,6 +50,7 @@ extends: parameters: pool: { type: windows } variables: + ob_sdl_prefast_enabled: false # This is a collection of powershell scripts ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: "$(Build.SourcesDirectory)/_none" diff --git a/build/pipelines/templates-v2/job-build-package-wpf.yml b/build/pipelines/templates-v2/job-build-package-wpf.yml index a1f44373d0..098201f0b5 100644 --- a/build/pipelines/templates-v2/job-build-package-wpf.yml +++ b/build/pipelines/templates-v2/job-build-package-wpf.yml @@ -75,9 +75,9 @@ jobs: - template: .\steps-restore-nuget.yml - task: VSBuild@1 - displayName: Build solution OpenConsole.sln for WPF Control (Pack) + displayName: Build solution OpenConsole.slnx for WPF Control (Pack) inputs: - solution: 'OpenConsole.sln' + solution: 'OpenConsole.slnx' msbuildArgs: >- /p:WindowsTerminalReleaseBuild=true;Version=$(XES_PACKAGEVERSIONNUMBER) /p:NoBuild=true diff --git a/build/pipelines/templates-v2/job-build-project.yml b/build/pipelines/templates-v2/job-build-project.yml index 47390bbbf3..7672901763 100644 --- a/build/pipelines/templates-v2/job-build-project.yml +++ b/build/pipelines/templates-v2/job-build-project.yml @@ -170,9 +170,9 @@ jobs: - ${{ parameters.beforeBuildSteps }} - task: VSBuild@1 - displayName: Build OpenConsole.sln + displayName: Build OpenConsole.slnx inputs: - solution: 'OpenConsole.sln' + solution: 'OpenConsole.slnx' msbuildArgs: >- /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} ${{ parameters.additionalBuildOptions }} diff --git a/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml b/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml index e2ed380fd4..518f01c704 100644 --- a/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml +++ b/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml @@ -75,10 +75,22 @@ jobs: } displayName: "Wrangle Unpackaged builds into place, rename" - - powershell: |- - Get-PackageProvider -Name NuGet -ForceBootstrap - Install-Module -Verbose -AllowClobber -Force Az.Accounts, Az.Storage, Az.Network, Az.Resources, Az.Compute - displayName: Install Azure Module Dependencies + - task: PowerShell@2 + displayName: Install Azure Modules from custom PowerShell Gallery Repo + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + inputs: + pwsh: false # We are preparing modules for AzureFileCopy, which uses PowerShell 5.1 + targetType: inline + script: |- + $MachineToken = $env:SYSTEM_ACCESSTOKEN | ConvertTo-SecureString -AsPlainText -Force + $Credential = [PSCredential]::new("ONEBRANCH_TOKEN", $MachineToken) + $MachineToken = $null + $Feed = "https://pkgs.dev.azure.com/shine-oss/terminal/_packaging/TerminalDependencies%40Local/nuget/v3/index.json" + Register-PSResourceRepository -Name "PSGalleryUpstream" -Uri $Feed -Trusted + Get-PSResourceRepository + + Install-PSResource -Name Az.Accounts, Az.Storage, Az.Network, Az.Resources, Az.Compute -Repository "PSGalleryUpstream" -Credential $Credential - task: AzureFileCopy@6 displayName: Publish to Storage Account diff --git a/build/pipelines/templates-v2/job-merge-msix-into-bundle.yml b/build/pipelines/templates-v2/job-merge-msix-into-bundle.yml index 1668dcfb29..261750db8f 100644 --- a/build/pipelines/templates-v2/job-merge-msix-into-bundle.yml +++ b/build/pipelines/templates-v2/job-merge-msix-into-bundle.yml @@ -147,6 +147,10 @@ jobs: ValidateSignature: true Verbosity: 'Verbose' + - pwsh: |- + tar -c -v --format=zip -f "$(JobOutputDirectory)/GroupPolicyTemplates_$(XES_APPXMANIFESTVERSION).zip" -C "$(Build.SourcesDirectory)/policies" * + displayName: Package GPO Templates + - ${{ parameters.afterBuildSteps }} - ${{ if eq(parameters.publishArtifacts, true) }}: diff --git a/build/pipelines/templates-v2/job-pgo-build-nuget-and-publish.yml b/build/pipelines/templates-v2/job-pgo-build-nuget-and-publish.yml index e30e23b88c..513e5c5007 100644 --- a/build/pipelines/templates-v2/job-pgo-build-nuget-and-publish.yml +++ b/build/pipelines/templates-v2/job-pgo-build-nuget-and-publish.yml @@ -30,7 +30,7 @@ jobs: steps: - checkout: self clean: true - # It is important that this be 0, otherwise git will not fetch the branch ref names that the PGO rules require. + # It is important that this be 0; otherwise, git will not fetch the branch ref names that the PGO rules require. fetchDepth: 0 submodules: false persistCredentials: false diff --git a/build/pipelines/templates-v2/job-publish-symbols-using-symbolrequestprod-api.yml b/build/pipelines/templates-v2/job-publish-symbols-using-symbolrequestprod-api.yml index 12b0ddce86..1b34002c50 100644 --- a/build/pipelines/templates-v2/job-publish-symbols-using-symbolrequestprod-api.yml +++ b/build/pipelines/templates-v2/job-publish-symbols-using-symbolrequestprod-api.yml @@ -52,11 +52,6 @@ jobs: itemPattern: '**/*.pdb' targetPath: '$(Build.SourcesDirectory)/bin' - - powershell: |- - Get-PackageProvider -Name NuGet -ForceBootstrap - Install-Module -Verbose -AllowClobber -Force Az.Accounts, Az.Storage, Az.Network, Az.Resources, Az.Compute - displayName: Install Azure Module Dependencies - # Transit the Azure token from the Service Connection into a secret variable for the rest of the pipeline to use. - task: AzurePowerShell@5 displayName: Generate an Azure Token @@ -66,7 +61,7 @@ jobs: pwsh: true ScriptType: InlineScript Inline: |- - $AzToken = (Get-AzAccessToken -ResourceUrl api://30471ccf-0966-45b9-a979-065dbedb24c1).Token + $AzToken = (Get-AzAccessToken -AsSecureString -ResourceUrl api://30471ccf-0966-45b9-a979-065dbedb24c1).Token | ConvertFrom-SecureString -AsPlainText Write-Host "##vso[task.setvariable variable=SymbolAccessToken;issecret=true]$AzToken" diff --git a/build/pipelines/templates-v2/job-submit-windows-vpack.yml b/build/pipelines/templates-v2/job-submit-windows-vpack.yml index 20c2aaa9f2..113542e2d7 100644 --- a/build/pipelines/templates-v2/job-submit-windows-vpack.yml +++ b/build/pipelines/templates-v2/job-submit-windows-vpack.yml @@ -69,10 +69,3 @@ jobs: artifact: $(JobOutputArtifactName) displayName: 'Publish VPack Manifest to Drop' - - task: PkgESFCIBGit@12 - displayName: 'Submit VPack Manifest to Windows' - inputs: - configPath: '$(Build.SourcesDirectory)\build\config\GitCheckin.json' - artifactsDirectory: $(XES_VPACKMANIFESTDIRECTORY) - prTimeOut: 5 - diff --git a/build/pipelines/templates-v2/pipeline-onebranch-full-release-build.yml b/build/pipelines/templates-v2/pipeline-onebranch-full-release-build.yml index ae017a9bec..3746402e3e 100644 --- a/build/pipelines/templates-v2/pipeline-onebranch-full-release-build.yml +++ b/build/pipelines/templates-v2/pipeline-onebranch-full-release-build.yml @@ -78,7 +78,9 @@ extends: template: v2/Microsoft.NonOfficial.yml@templates parameters: featureFlags: - WindowsHostVersion: 1ESWindows2022 + WindowsHostVersion: + Version: 2022 + Network: R1 platform: name: 'windows_undocked' product: 'Windows Terminal' @@ -86,6 +88,9 @@ extends: enabled: false globalSdl: # https://aka.ms/obpipelines/sdl enableCheckCFlags: false # CheckCFlags is broken and exploding our builds; to remove, :g/BAD-FLAGS/d + isNativeCode: true + prefast: + enabled: true asyncSdl: enabled: true tsaOptionsFile: 'build/config/tsa.json' @@ -113,6 +118,8 @@ extends: variables: ob_sdl_checkcflags_enabled: false # BAD-FLAGS ob_sdl_xfgcheck_enabled: false # BAD-FLAGS + ob_sdl_prefast_runDuring: Build + ob_sdl_checkCompliantCompilerWarnings: true ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(JobOutputDirectory) @@ -147,6 +154,7 @@ extends: variables: ob_sdl_checkcflags_enabled: false # BAD-FLAGS ob_sdl_xfgcheck_enabled: false # BAD-FLAGS + ob_sdl_prefast_enabled: false # This is a C# build job ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(JobOutputDirectory) @@ -178,6 +186,7 @@ extends: variables: ob_sdl_checkcflags_enabled: false # BAD-FLAGS ob_sdl_xfgcheck_enabled: false # BAD-FLAGS + ob_sdl_prefast_enabled: false # This is a collection of powershell scripts ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(JobOutputDirectory) @@ -232,6 +241,7 @@ extends: variables: ob_sdl_checkcflags_enabled: false # BAD-FLAGS ob_sdl_xfgcheck_enabled: false # BAD-FLAGS + ob_sdl_prefast_enabled: false # This is a collection of powershell scripts ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(JobOutputDirectory) @@ -250,6 +260,7 @@ extends: variables: ob_sdl_checkcflags_enabled: false # BAD-FLAGS ob_sdl_xfgcheck_enabled: false # BAD-FLAGS + ob_sdl_prefast_enabled: false # This is a collection of powershell scripts ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(JobOutputDirectory) @@ -275,6 +286,7 @@ extends: variables: ob_sdl_checkcflags_enabled: false # BAD-FLAGS ob_sdl_xfgcheck_enabled: false # BAD-FLAGS + ob_sdl_prefast_enabled: false # This is a collection of powershell scripts ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(Build.ArtifactStagingDirectory) diff --git a/build/pipelines/templates-v2/steps-ensure-nuget-version.yml b/build/pipelines/templates-v2/steps-ensure-nuget-version.yml index fb5cd75e4c..ea5eda5557 100644 --- a/build/pipelines/templates-v2/steps-ensure-nuget-version.yml +++ b/build/pipelines/templates-v2/steps-ensure-nuget-version.yml @@ -1,5 +1,12 @@ steps: -- task: NuGetToolInstaller@1 - displayName: Use NuGet 6.6.1 - inputs: - versionSpec: 6.6.1 +- ${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}: + - pwsh: |- + Write-Host "Assuming NuGet is already installed..." + & nuget.exe help + displayName: Assume NuGet is fine + +- ${{ else }}: + - task: NuGetToolInstaller@1 + displayName: Use NuGet 6.6.1 + inputs: + versionSpec: 6.6.1 diff --git a/build/pipelines/templates-v2/steps-restore-nuget.yml b/build/pipelines/templates-v2/steps-restore-nuget.yml index 37018efc1a..b28c45319b 100644 --- a/build/pipelines/templates-v2/steps-restore-nuget.yml +++ b/build/pipelines/templates-v2/steps-restore-nuget.yml @@ -19,14 +19,16 @@ steps: restoreSolution: build/packages.config restoreDirectory: '$(Build.SourcesDirectory)\packages' -- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2 +- task: VSBuild@1 displayName: Restore NuGet packages for solution inputs: - command: restore - feedsToUse: config - configPath: NuGet.config - restoreSolution: OpenConsole.sln - restoreDirectory: '$(Build.SourcesDirectory)\packages' + solution: 'OpenConsole.slnx' + msbuildArgs: /t:Restore + platform: $(BuildPlatform) + configuration: $(BuildConfiguration) + msbuildArchitecture: x64 + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2 displayName: Restore NuGet packages for global nuget diff --git a/build/scripts/Set-LatestVCToolsVersion.ps1 b/build/scripts/Set-LatestVCToolsVersion.ps1 index 3156fff1bc..4ebf3a3eec 100644 --- a/build/scripts/Set-LatestVCToolsVersion.ps1 +++ b/build/scripts/Set-LatestVCToolsVersion.ps1 @@ -1,8 +1,28 @@ $VSInstances = ([xml](& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -include packages -format xml)) $VSPackages = $VSInstances.instances.instance.packages.package -$LatestVCPackage = ($VSInstances.instances.instance.packages.package | ? { $_.id -eq "Microsoft.VisualCpp.Tools.Core" }) +$LatestVCPackage = ($VSPackages | ? { $_.id -eq "Microsoft.VisualCpp.Tools.Core" }) $LatestVCToolsVersion = $LatestVCPackage.version; +$VSRoot = (& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property 'resolvedInstallationPath') +$VCToolsRoot = Join-Path $VSRoot "VC\Tools\MSVC" + +# We have observed a few instances where the VC tools package version actually +# differs from the version on the files themselves. We might as well check +# whether the version we just found _actually exists_ before we use it. +# We'll use whichever highest version exists. +$PackageVCToolPath = Join-Path $VCToolsRoot $LatestVCToolsVersion +If ($Null -Eq (Get-Item $PackageVCToolPath -ErrorAction:Ignore)) { + $VCToolsVersions = Get-ChildItem $VCToolsRoot | ForEach-Object { + [Version]$_.Name + } | Sort -Descending + $LatestActualVCToolsVersion = $VCToolsVersions | Select -First 1 + + If ([Version]$LatestVCToolsVersion -Ne $LatestActualVCToolsVersion) { + Write-Output "VC Tools Mismatch: Directory = $LatestActualVCToolsVersion, Package = $LatestVCToolsVersion" + $LatestVCToolsVersion = $LatestActualVCToolsVersion.ToString(3) + } +} + Write-Output "Latest VCToolsVersion: $LatestVCToolsVersion" Write-Output "Updating VCToolsVersion environment variable for job" Write-Output "##vso[task.setvariable variable=VCToolsVersion]$LatestVCToolsVersion" diff --git a/custom.props b/custom.props index 8e358b3e0b..0ae721c6d4 100644 --- a/custom.props +++ b/custom.props @@ -5,7 +5,10 @@ true 2025 1 - 24 + 25 Windows Terminal + 1033 + + \xa9 Microsoft Corporation. All rights reserved. diff --git a/dep/nuget/packages.config b/dep/nuget/packages.config index a641da7c51..a7c94bbf31 100644 --- a/dep/nuget/packages.config +++ b/dep/nuget/packages.config @@ -4,8 +4,8 @@ - - + + diff --git a/dep/vcpkg-overlay-ports/fmt/fix-write-batch.patch b/dep/vcpkg-overlay-ports/fmt/fix-write-batch.patch new file mode 100644 index 0000000000..6bec3b8e4b --- /dev/null +++ b/dep/vcpkg-overlay-ports/fmt/fix-write-batch.patch @@ -0,0 +1,13 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 88c12148..967b53dd 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -260,7 +260,7 @@ if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio") + join(netfxpath + "C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\" + ".NETFramework\\v4.0") +- file(WRITE run-msbuild.bat " ++ file(WRITE "${CMAKE_BINARY_DIR}/run-msbuild.bat" " + ${MSBUILD_SETUP} + ${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*") + endif () diff --git a/dep/vcpkg-overlay-ports/fmt/portfile.cmake b/dep/vcpkg-overlay-ports/fmt/portfile.cmake new file mode 100644 index 0000000000..42d554a3d5 --- /dev/null +++ b/dep/vcpkg-overlay-ports/fmt/portfile.cmake @@ -0,0 +1,38 @@ +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO fmtlib/fmt + REF "${VERSION}" + SHA512 573b7de1bd224b7b1b60d44808a843db35d4bc4634f72a9edcb52cf68e99ca66c744fd5d5c97b4336ba70b94abdabac5fc253b245d0d5cd8bbe2a096bf941e39 + HEAD_REF master + PATCHES + fix-write-batch.patch +) + +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS + -DFMT_CMAKE_DIR=share/fmt + -DFMT_TEST=OFF + -DFMT_DOC=OFF + -DFMT_PEDANTIC=ON +) + +vcpkg_cmake_install() +vcpkg_cmake_config_fixup() +vcpkg_fixup_pkgconfig() +vcpkg_copy_pdbs() + +if(VCPKG_LIBRARY_LINKAGE STREQUAL dynamic) + vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/fmt/base.h" + "defined(FMT_SHARED)" + "1" + ) +endif() + +file(REMOVE_RECURSE + "${CURRENT_PACKAGES_DIR}/debug/include" + "${CURRENT_PACKAGES_DIR}/debug/share" +) + +file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") +vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE") diff --git a/dep/vcpkg-overlay-ports/fmt/usage b/dep/vcpkg-overlay-ports/fmt/usage new file mode 100644 index 0000000000..e5a9d70480 --- /dev/null +++ b/dep/vcpkg-overlay-ports/fmt/usage @@ -0,0 +1,8 @@ +The package fmt provides CMake targets: + + find_package(fmt CONFIG REQUIRED) + target_link_libraries(main PRIVATE fmt::fmt) + + # Or use the header-only version + find_package(fmt CONFIG REQUIRED) + target_link_libraries(main PRIVATE fmt::fmt-header-only) diff --git a/dep/vcpkg-overlay-ports/fmt/vcpkg.json b/dep/vcpkg-overlay-ports/fmt/vcpkg.json new file mode 100644 index 0000000000..d55fff5aa0 --- /dev/null +++ b/dep/vcpkg-overlay-ports/fmt/vcpkg.json @@ -0,0 +1,17 @@ +{ + "name": "fmt", + "version": "11.1.4", + "description": "{fmt} is an open-source formatting library providing a fast and safe alternative to C stdio and C++ iostreams.", + "homepage": "https://github.com/fmtlib/fmt", + "license": "MIT", + "dependencies": [ + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ] +} diff --git a/dep/vcpkg-overlay-triplets/fuzzing/arm64-windows-static.cmake b/dep/vcpkg-overlay-triplets/fuzzing/arm64-windows-static.cmake new file mode 100644 index 0000000000..41bcac2329 --- /dev/null +++ b/dep/vcpkg-overlay-triplets/fuzzing/arm64-windows-static.cmake @@ -0,0 +1,6 @@ +set(VCPKG_TARGET_ARCHITECTURE arm64) +set(VCPKG_CRT_LINKAGE static) +set(VCPKG_LIBRARY_LINKAGE static) + +set(VCPKG_CXX_FLAGS /fsanitize=address) +set(VCPKG_C_FLAGS /fsanitize=address) diff --git a/dep/vcpkg-overlay-triplets/fuzzing/x64-windows-static.cmake b/dep/vcpkg-overlay-triplets/fuzzing/x64-windows-static.cmake new file mode 100644 index 0000000000..909e747923 --- /dev/null +++ b/dep/vcpkg-overlay-triplets/fuzzing/x64-windows-static.cmake @@ -0,0 +1,6 @@ +set(VCPKG_TARGET_ARCHITECTURE x64) +set(VCPKG_CRT_LINKAGE static) +set(VCPKG_LIBRARY_LINKAGE static) + +set(VCPKG_CXX_FLAGS /fsanitize=address) +set(VCPKG_C_FLAGS /fsanitize=address) diff --git a/dep/vcpkg-overlay-triplets/fuzzing/x86-windows-static.cmake b/dep/vcpkg-overlay-triplets/fuzzing/x86-windows-static.cmake new file mode 100644 index 0000000000..60a3c5e9cb --- /dev/null +++ b/dep/vcpkg-overlay-triplets/fuzzing/x86-windows-static.cmake @@ -0,0 +1,6 @@ +set(VCPKG_TARGET_ARCHITECTURE x86) +set(VCPKG_CRT_LINKAGE static) +set(VCPKG_LIBRARY_LINKAGE static) + +set(VCPKG_CXX_FLAGS /fsanitize=address) +set(VCPKG_C_FLAGS /fsanitize=address) diff --git a/doc/Niksa.md b/doc/Niksa.md index ec6af1f146..d9d82fe39d 100644 --- a/doc/Niksa.md +++ b/doc/Niksa.md @@ -15,7 +15,7 @@ This document serves as a storage point for those posts. ## Why do we avoid changing CMD.exe? `setlocal` doesn't behave the same way as an environment variable. It's a thing that would have to be put in at the top of the batch script that is `somefile.cmd` as one of its first commands to adjust the way that one specific batch file is processed by the `cmd.exe` engine. That's probably not suitable for your needs, but that's the way we have to go. -I don't think anyone is disagreeing with you, @mikemaccana, that this would be a five minute development change to read that environment variable and change the behavior of `cmd.exe`. It absolutely would be a tiny development time. +I don't think anyone is disagreeing with you, @mikemaccana, that this would be a five minute development change to read that environment variable and change the behavior of `cmd.exe`. It absolutely would be a tiny development time. It's just that from our experience, we know there's going to be a 3-24 month bug tail here where we get massive investigation callbacks by some billion dollar enterprise customer who for whatever reason was already using the environment variable we pick for another purpose. Their script that they give their rank-and-file folks will tell them to press Ctrl+C at some point in the batch script to do whatever happens, it will do something different, those people will notice the script doesn't match the computer anymore. They will then halt the production line and tell their supervisor. The supervisor tells some director. Their director comes screaming at their Microsoft enterprise support contract person that we've introduced a change to the OS that is costing them millions if not billions of dollars in shipments per month. Our directors at Microsoft then come bashing down our doors angry with us and make us fix it ASAP or revert it, we don't get to go home at 5pm to our families or friends because we're fixing it, we get stressed the heck out, we have to spin up servicing potentially for already shipped operating systems which is expensive and headache-causing...etc. @@ -27,7 +27,7 @@ I would highly recommend that Gulp convert to using PowerShell scripts and that Original Source: https://github.com/microsoft/terminal/issues/217#issuecomment-404240443 -_Addendum_: cmd.exe is the literal embodiment of [xkcd#1172]([url](https://xkcd.com/1172/)). Every change, no matter how small, will break _someone_. +_Addendum_: cmd.exe is the literal embodiment of [xkcd#1172]([url](https://xkcd.com/1172/)). Every change, no matter how small, will break _someone_. ## Why is typing-to-screen performance better than every other app? @@ -37,33 +37,33 @@ Also, I'm happy to discuss this with you until you're utterly sick of reading it If I had to take an educated guess as to what is making us faster than pretty much any other application on Windows at putting your text on the screen... I would say it is because that is literally our only job! Also probably because we are using darn near the oldest and lowest level APIs that Windows has to accomplish this work. -Pretty much everything else you've listed has some sort of layer or framework involved, or many, many layers and frameworks, when you start talking about Electron and JavaScript. We don't. +Pretty much everything else you've listed has some sort of layer or framework involved, or many, many layers and frameworks, when you start talking about Electron and JavaScript. We don't. -We have one bare, super un-special window with no additional controls attached to it. We get our keys fed into us from just barely above the kernel given that we're processing them from window messages and not from some sort of eventing framework common to pretty much any other more complicated UI framework than ours (WPF, WinForms, UWP, Electron). And we dump our text straight onto the window surface using GDI's [PolyTextOut](https://docs.microsoft.com/en-us/windows/desktop/api/wingdi/nf-wingdi-polytextoutw) with no frills. +We have one bare, super un-special window with no additional controls attached to it. We get our keys fed into us from just barely above the kernel given that we're processing them from window messages and not from some sort of eventing framework common to pretty much any other more complicated UI framework than ours (WPF, WinForms, UWP, Electron). And we dump our text straight onto the window surface using GDI's [PolyTextOut](https://docs.microsoft.com/en-us/windows/desktop/api/wingdi/nf-wingdi-polytextoutw) with no frills. Even `notepad.exe` has multiple controls on its window at the very least and is probably (I haven't looked) using some sort of library framework in the edit control to figure out its text layout (which probably is using another library framework for internationalization support...) -Of course this also means that we have trade offs. We don't support fully international text like pretty much every other application will. RTL? No go zone right now. Surrogate pairs and emoji? We're getting there but not there yet. Indic scripts? Nope. +Of course this also means that we have trade offs. We don't support fully international text like pretty much every other application will. RTL? No go zone right now. Surrogate pairs and emoji? We're getting there but not there yet. Indic scripts? Nope. Why are we like this? For one, `conhost.exe` is old as dirt. It has to use the bare metal bottom layer of everything because it was created before most of those other frameworks were created. And also it maintains as low/bottom level as possible because it is pretty much the first thing that one needs to bring up when bringing up a new operating system edition or device before you have all the nice things like frameworks or what those frameworks require to operate. Also it's written in C/C++ which is about as low and bare metal as we can get. -Will this UI enhancement come to other apps on Windows? Almost certainly not. They have too much going on which is both a good and a bad thing. I'm jealous of their ability to just call one method and layout text in an uncomplicated manner in any language without manually calculating pixels or caring about what styles apply to their font. But my manual pixel calculations, dirty region math, scroll region madness, and more makes it so we go faster than them. I'm also jealous that when someone says "hey can you add a status bar to the bottom of your window" that they can pretty much click and drag that into place with their UI Framework and it will just work where as for us, it's been a backlog item forever and gives me heartburn to think about implementing. +Will this UI enhancement come to other apps on Windows? Almost certainly not. They have too much going on which is both a good and a bad thing. I'm jealous of their ability to just call one method and layout text in an uncomplicated manner in any language without manually calculating pixels or caring about what styles apply to their font. But my manual pixel calculations, dirty region math, scroll region madness, and more makes it so we go faster than them. I'm also jealous that when someone says "hey can you add a status bar to the bottom of your window" that they can pretty much click and drag that into place with their UI Framework and it will just work whereas for us, it's been a backlog item forever and gives me heartburn to think about implementing. -Will we try to keep it from regressing? Yes! Right now it's sort of a manual process. We identify that something is getting slow and then we go haul out [WPR](https://docs.microsoft.com/en-us/windows-hardware/test/wpt/windows-performance-recorder) and start taking traces. We stare down the hot paths and try to reason out what is going on and then improve them. For instance, in the last cycle or two, we focused on heap allocations as a major area where we could improve our end-to-end performance, changing a ton of our code to use stack-constructed iterator-like facades over the underlying request buffer instead of translating and allocating it into a new heap space for each level of processing. +Will we try to keep it from regressing? Yes! Right now it's sort of a manual process. We identify that something is getting slow and then we go haul out [WPR](https://docs.microsoft.com/en-us/windows-hardware/test/wpt/windows-performance-recorder) and start taking traces. We stare down the hot paths and try to reason out what is going on and then improve them. For instance, in the last cycle or two, we focused on heap allocations as a major area where we could improve our end-to-end performance, changing a ton of our code to use stack-constructed iterator-like facades over the underlying request buffer instead of translating and allocating it into a new heap space for each level of processing. As an aside, @bitcrazed wants us to automate performance tests in some conhost specific way, but I haven't quite figured out a controlled environment to do this in yet. The Windows Engineering System runs performance tests each night that give us a coarse-grained way of knowing if we messed something up for the whole operating system, and they technically offer a fine-grained way for us to insert our own performance tests... but I just haven't got around to that yet. If you have an idea for a way for us to do this in an automated fashion, I'm all ears. -If there's anything else you'd like to know, let me know. I could go on all day. I deleted like 15 tangents from this reply before posting it.... +If there's anything else you'd like to know, let me know. I could go on all day. I deleted like 15 tangents from this reply before posting it.... Original Source: https://github.com/microsoft/terminal/issues/327#issuecomment-447391705 ## How are the Windows graphics/messaging stack assembled? -@stakx, I am referring to USER32 and GDI32. +@stakx, I am referring to USER32 and GDI32. I'll give you a cursory overview of what I know off the top of my head without spending hours confirming the details. As such, some of this is subject to handwaving and could be mildly incorrect but is probably in the right direction. Consider every statement to be my personal knowledge on how the world works and subject to opinion or error. -For the graphics part of the pipeline (GDI32), the user-mode portions of GDI are pretty far down. The app calls GDI32, some work is done in that DLL on the user-mode side, then a kernel call jumps over to the kernel and drawing occurs. +For the graphics part of the pipeline (GDI32), the user-mode portions of GDI are pretty far down. The app calls GDI32, some work is done in that DLL on the user-mode side, then a kernel call jumps over to the kernel and drawing occurs. The portion that you're thinking of regarding "silently converted to sit on top of other stuff" is probably that once we hit the kernel calls, a bunch of the kernel GDI stuff tends to be re-platformed on top of the same stuff as DirectX when it is actually handled by the NVIDIA/AMD/Intel/etc. graphics driver and the GPU at the bottom of the stack. I think this happened with the graphics driver re-architecture that came as a part of WDDM for Windows Vista. There's a document out there somewhere about what calls are still really fast in GDI and which are slower as a result of the re-platforming. Last time I found that document and checked, we were using the fast ones. @@ -71,11 +71,11 @@ On top of GDI, I believe there are things like Common Controls or comctl32.dll w As for DirectWrite and D2D and D3D and DXGI themselves, they're a separate set of commands and paths that are completely off to the side from GDI at all both in user and kernel mode. They're not really related other than that there's some interoperability provisions between the two. Most of our other UI frameworks tend to be built on top of the DirectX stack though. XAML is for sure. I think WPF is. Not sure about WinForms. And I believe the composition stack and the window manager are using DirectX as well. -As for the input/interaction part of the pipeline (USER32), I tend to find most other newer things (at least for desktop PCs) are built on top of what is already there. USER32's major concept is windows and window handles and everything is sent to a window handle. As long as you're on a desktop machine (or a laptop or whatever... I mean a classic-style Windows-powered machine), there's a window handle involved and messages floating around and that means we're talking USER32. +As for the input/interaction part of the pipeline (USER32), I tend to find most other newer things (at least for desktop PCs) are built on top of what is already there. USER32's major concept is windows and window handles and everything is sent to a window handle. As long as you're on a desktop machine (or a laptop or whatever... I mean a classic-style Windows-powered machine), there's a window handle involved and messages floating around and that means we're talking USER32. -The window message queue is just a straight up FIFO (more or less) of whatever input has occurred relevant to that window while it's in the foreground + whatever has been sent to the window by other components in the system. +The window message queue is just a straight up FIFO (more or less) of whatever input has occurred relevant to that window while it's in the foreground + whatever has been sent to the window by other components in the system. -The newer technologies and the frameworks like XAML and WPF and WinForms tend to receive the messages from the window message queue one way or another and process them and turn them into event callbacks to various objects that they've provisioned within their world. +The newer technologies and the frameworks like XAML and WPF and WinForms tend to receive the messages from the window message queue one way or another and process them and turn them into event callbacks to various objects that they've provisioned within their world. However, the newer technologies that also work on other non-desktop platforms like XAML tend to have the ability to process stuff off of a completely different non-USER32 stack as well. There's a separate parallel stack to USER32 with all of our new innovations and realizations on how input and interaction should occur that doesn't exactly deal with classic messaging queues and window handles the same way. This is the whole Core* family of things like CoreWindow and CoreMessaging. They also have a different concept of "what is a user" that isn't so centric around your butt in rolling chair in front of a screen with a keyboard and mouse on the desk. @@ -83,7 +83,7 @@ Now, if you're on XAML or one of the other Frameworks... all this intricacy is h The trick is that GDI32 and USER32 were designed for a limited world with a limited set of commands. Desktop PCs were the only thing that existed, single user at the keyboard and mouse, simple graphics output to a VGA monitor. So using them directly at the "low level" like conhost does is pretty easy. The new platforms could be used at the "low level" but they're orders of magnitude more complicated because they now account for everything that has happened with personal computing in 20+ years like different form factors, multiple active users, multiple graphics adapters, and on and on and on and on. So you tend to use a framework when using the new stuff so your head doesn't explode. They handle it for you, but they handle more than they ever did before so they're slower to some degree. -So are GDI32 and USER32 "lower" than the new stuff? Sort of. +So are GDI32 and USER32 "lower" than the new stuff? Sort of. Can you get that low with the newer stuff? Mostly yes, but you probably shouldn't and don't want to. Does new live on top of old or is old replatformed on the new? Sometimes and/or partially. Basically... it's like the answer to anything software... "it's an unmitigated disaster and if we all stepped back a moment, we should be astounded that it works at all." :P @@ -94,7 +94,7 @@ Original Source: https://github.com/microsoft/terminal/issues/327#issuecomment-4 ## Output Processing between "Far East" and "Western" -> +> > ``` > if (WI_IsFlagSet(CharType, C1_CNTRL)) > ``` @@ -120,7 +120,7 @@ Note in both of these, there is a little bit of indirection before `MultiByteToW When we took over the console codebase, this variation between "Western" and "Eastern" countries was especially painful because `conhost.exe` would choose which one it was in based on the `Codepage for Non-Unicode Applications` set in the Control Panel's Regional > Administrative panel and it could only be changed with a reboot. It wouldn't even change properly when you `chcp` to a different codepage. Heck, `chcp` would deny you from switching into many codepages. There was a block in place to prevent going to an "Eastern" codepage if you booted up in a "Western" codepage. There was also a block preventing you from going between "Eastern" codepages, if I recall correctly. In modernizing, I decided a few things: -1. What's good for the "Far East" should be good for the rest of the world. CJK languages that encompassed the "Far East" code have to be able to handle "Western" text as well even if the reverse wasn't true. +1. What's good for the "Far East" should be good for the rest of the world. CJK languages that encompassed the "Far East" code have to be able to handle "Western" text as well even if the reverse wasn't true. 2. We need to scrub all usages of "Far East" from the code. Someone already started that and replaced them with "East Asia" except then they left behind the shorthand of "FE" prefixing dozens of functions which made it hard to follow the code. It took us months to realize "FE" and "East Asia" were the same thing. 3. It's obnoxious that the way this was handled was to literally double-define every output function in the code base to have two definitions, compile them both into the conhost, then choose to run down the SB_ versions or the FE_ versions depending on the startup Non-Unicode codepage. It was a massive pile of complex pre-compilation `#ifdef` and `#else`s that would sometimes surround individual lines in the function bodies. Gross. 4. The fact that the FE_ versions of the functions were way slower than the SB_ ones was unacceptable even for the same output of Latin-character text. @@ -139,13 +139,13 @@ Original Source: https://github.com/microsoft/terminal/issues/166#issuecomment-5 ## Why do we not backport things? -Someone has to prove that this is costing millions to billions of dollars of lost productivity or revenue to outweigh the risks of shipping the fix to hundreds of millions of Windows machines and potentially breaking something. +Someone has to prove that this is costing millions to billions of dollars of lost productivity or revenue to outweigh the risks of shipping the fix to hundreds of millions of Windows machines and potentially breaking something. -Our team generally finds it pretty hard to prove that against the developer audience given that they're only a small portion of the total installed market of Windows machines. +Our team generally finds it pretty hard to prove that against the developer audience given that they're only a small portion of the total installed market of Windows machines. Our only backport successes really come from corporations with massive addressable market (like OEMs shipping PCs) who complain that this is fouling up their manufacturing line (or something of that ilk). Otherwise, our management typically says that the risks don't outweigh the benefits. -It's also costly in terms of time, effort, and testing for us to validate a modification to a released OS. We have a mindbogglingly massive amount of automated machinery dedicated to processing and validating the things that we check in while developing the current OS builds. But it's a special costly ask to spin up some to all of those activities to validate backported fixes. We do it all the time for Patch Tuesday, but in those patches, they only pass through the minimum number of fixes required to maximize the restoration of productivity/security/revenue/etc. because every additional fix adds additional complexity and additional risk. +It's also costly in terms of time, effort, and testing for us to validate a modification to a released OS. We have a mindbogglingly massive amount of automated machinery dedicated to processing and validating the things that we check in while developing the current OS builds. But it's a special costly ask to spin up some to all of those activities to validate backported fixes. We do it all the time for Patch Tuesday, but in those patches, they only pass through the minimum number of fixes required to maximize the restoration of productivity/security/revenue/etc. because every additional fix adds additional complexity and additional risk. So from our little team working hard to make developers happy, we virtually never make the cut for servicing. We're sorry, but we hope you can understand. It's just the reality of the situation to say "nope" when people ask for a backport. In our team's ideal world, you would all be running the latest console bits everywhere every time we make a change. But that's just not how it is today. @@ -189,7 +189,7 @@ _guest speaker @zadjii-msft_ I think there might be a bit of a misunderstanding here - there are two different kinds of applications we're talking about here: * shell applications, like `cmd.exe`, `powershell`, `zsh`, etc. These are text-only applications that emit streams of characters. They don't care at all about how they're eventually rendered to the user. These are also sometimes referred to as "commandline client" applications. -* terminal applications, like the Windows Terminal, gnome-terminal, xterm, iterm2, hyper. These are graphical applications that can be used to render the output of commandline clients. +* terminal applications, like the Windows Terminal, gnome-terminal, xterm, iterm2, hyper. These are graphical applications that can be used to render the output of commandline clients. On Windows, if you just run `cmd.exe` directly, the OS will create an instance of `conhost.exe` as the _terminal_ for `cmd.exe`. The same thing happens for `powershell.exe`, the system will create a new conhost window for any client that's not already connected to a terminal of some sort. This has lead to an enormous amount of confusion for people thinking that a conhost window is actually a "`cmd` window". `cmd` can't have a window, it's just a commandline application. Its window is always some other terminal. diff --git a/doc/WindowsTestPasses.md b/doc/WindowsTestPasses.md index f733f98af5..1689bbb443 100644 --- a/doc/WindowsTestPasses.md +++ b/doc/WindowsTestPasses.md @@ -31,12 +31,12 @@ Prerequisites: 1. Right click the machine name in the `Device Manager` list and choose `Launch T-Shell`. You can also use `Connect via Console` to get a "remote desktop"-like session to the KVM port on the VM. 1. In T-shell, use `testd Microsoft.Console.TestLab.Desktop.testlist` or a command of that format with a different TESTLIST or TESTMD name from our project (see the [UniversalTest.md] documentation). The `testd` utility will automatically resolve the build/branch/flavor information, dig through the build shares for the matching TESTLIST/TESTMD metadata, and attempt to deploy all relevant packages and dependencies on the device. When it's successful, it will move onto running all the tests and giving you the results. On conclusion, the test results should pop up in the web browser or the `Hubble - Log Viewer` tool provided by the Engineering Systems team. -If some of the above things do not work, go to [https://osgwiki.com] and type them into the Search bar. For instance, if T-Shell isn't found or working, you can find out where to get it or download it on `OSGWiki`. The same goes for the other commands besides `testd` to use in T-shell and more information on what `Hubble` or `Nebula` are. +If some of the above things do not work, go to [https://osgwiki.com] and type them into the Search bar. For instance, if T-Shell isn't found or working, you can find out where to get it or download it on `OSGWiki`. The same goes for the other commands besides `testd` to use in T-shell and more information on what `Hubble` or `Nebula` are. Presumably now you have a failure. Or a success. You can attempt to spelunk the logs in `Hubble` and you might come to a conclusion. Or you can move onto debugging directly. -Now that you've relied on `testd` to get everything deployed and orchestrated and run once on the device, you can use `execd` to run things again or to run a smaller subset of things on the remote device through `T-Shell`. +Now that you've relied on `testd` to get everything deployed and orchestrated and run once on the device, you can use `execd` to run things again or to run a smaller subset of things on the remote device through `T-Shell`. -By default, in the `Universal Test` world, everything will be deployed onto the remote machine at `C:\data\test\bin`. In T-Shell, use `cdd C:\data\test\bin` to change to that directory and then `execd te.exe Microsoft.Console.Host.FeatureTests.dll /name:*TestReadFileEcho*` to run just one specific test. Of course you should substitute the file name and test name parameters as makes sense. And of course you can find out more about `cdd` and `execd` on the `T-shell` page of `OSGWiki`. +By default, in the `Universal Test` world, everything will be deployed onto the remote machine at `C:\data\test\bin`. In T-Shell, use `cdd C:\data\test\bin` to change to that directory and then `execd te.exe Microsoft.Console.Host.FeatureTests.dll /name:*TestReadFileEcho*` to run just one specific test. Of course, you should substitute the file name and test name parameters as makes sense. And of course you can find out more about `cdd` and `execd` on the `T-shell` page of `OSGWiki`. Fortunately, running things through `T-shell` in this fashion is exactly the same way that the testlab orchestrates the tests. If you still don't get good data this way, you can use the `Connect via Console` mechanism way above to try to run things under `WinDBG` or the `Visual Studio Remote Debugger` manually on the machine to get them to repro or under the debugger more completely. diff --git a/doc/building.md b/doc/building.md index 60f5334393..af1c7e7510 100644 --- a/doc/building.md +++ b/doc/building.md @@ -7,7 +7,7 @@ This repository uses [git submodules](https://git-scm.com/book/en/v2/Git-Tools-S git submodule update --init --recursive ``` -OpenConsole.sln may be built from within Visual Studio or from the command-line using a set of convenience scripts & tools in the **/tools** directory: +OpenConsole.slnx may be built from within Visual Studio or from the command-line using a set of convenience scripts & tools in the **/tools** directory: When using Visual Studio, be sure to set up the path for code formatting. To download the required clang-format.exe file, follow one of the building instructions below and run: ```powershell @@ -103,7 +103,7 @@ If you want to use .nupkg files instead of the downloaded Nuget package, you can The Terminal is bundled as an `.msix`, which is produced by the `CascadiaPackage.wapproj` project. To build that project from the commandline, you can run the following (from a window you've already run `tools\razzle.cmd` in): ```cmd -"%msbuild%" "%OPENCON%\OpenConsole.sln" /p:Configuration=%_LAST_BUILD_CONF% /p:Platform=%ARCH% /p:AppxSymbolPackageEnabled=false /t:Terminal\CascadiaPackage /m +"%msbuild%" "%OPENCON%\OpenConsole.slnx" /p:Configuration=%_LAST_BUILD_CONF% /p:Platform=%ARCH% /p:AppxSymbolPackageEnabled=false /t:Terminal\CascadiaPackage /m ``` This takes quite some time, and only generates an `msix`. It does not install the msix. To deploy the package: diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 8726d8303c..80309eb49e 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -32,13 +32,21 @@ ] }, "DynamicProfileSource": { - "enum": [ - "Windows.Terminal.Wsl", - "Windows.Terminal.Azure", - "Windows.Terminal.PowershellCore", - "Windows.Terminal.VisualStudio" - ], - "type": "string" + "type": "string", + "anyOf": [ + { + "type": "string" + }, + { + "enum": [ + "Microsoft.WSL", + "Windows.Terminal.Wsl", + "Windows.Terminal.Azure", + "Windows.Terminal.PowershellCore", + "Windows.Terminal.VisualStudio" + ] + } + ] }, "BellStyle": { "oneOf": [ @@ -2294,8 +2302,15 @@ "additionalProperties": false, "properties": { "id": { - "description": "The ID of the command this keybinding should execute.", - "type": "string" + "description": "The ID of the command this keybinding should execute (or null to disable a default).", + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] }, "keys": { "description": "Defines the key combinations used to call the command. It must be composed of...\n -any number of modifiers (ctrl/alt/shift)\n -a non-modifier key", @@ -2368,6 +2383,16 @@ "description": "When set to true, the terminal will focus the pane on mouse hover.", "type": "boolean" }, + "experimental.scrollToZoom": { + "default": true, + "description": "When set to true, holding the Ctrl key while scrolling will increase or decrease the terminal font size.", + "type": "boolean" + }, + "experimental.scrollToChangeOpacity": { + "default": true, + "description": "When set to true, holding the Ctrl and Shift keys while scrolling will change the window opacity.", + "type": "boolean" + }, "compatibility.allowHeadless": { "default": false, "description": "When set to true, Windows Terminal will run in the background. This allows globalSummon and quakeMode actions to work even when no windows are open.", @@ -3114,12 +3139,13 @@ }, "pathTranslationStyle": { "default": "none", - "description": "Controls how file paths are transformed when they are dragged and dropped on the terminal. Possible values are \"none\", \"wsl\", \"cygwin\" and \"msys2\".", + "description": "Controls how file paths are transformed when they are dragged and dropped on the terminal. Possible values are \"none\", \"wsl\", \"cygwin\", \"msys2\" and \"mingw\".", "enum": [ "none", "wsl", "cygwin", - "msys2" + "msys2", + "mingw" ], "type": "string" } diff --git a/doc/creating_a_new_project.md b/doc/creating_a_new_project.md index 0f7f3bd880..2e17b097c6 100644 --- a/doc/creating_a_new_project.md +++ b/doc/creating_a_new_project.md @@ -6,7 +6,7 @@ When creating a new DLL, it was really helpful to reference an existing DLL's `. - [ ] Make sure to `` our pre props at the _top_ of the vcxproj, and our post props at the _bottom_ of the vcxproj. ``` - + @@ -35,7 +35,7 @@ DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE - _Note_: If your new library eventually rolls up as a reference to our Centennial Packaging project `CascadiaPackage`, you don't have to worry about manually adding your definitions to the `AppXManifest.xml` because the Centennial Packaging project automatically enumerates the reference tree of WinMDs and stitches that information into the `AppXManifest.xml`. However, if your new project does _not_ ultimately roll up to a packaging project that will automatically put the references into `AppXManifest`, you will have to add them in manually. ### Troubleshooting -- If you hit an error that looks like this: +- If you hit an error that looks like this: ``` X found processing metadata file ..\blah1\Microsoft.UI.Xaml.winmd, type already exists in file ..\blah\NewDLLProject\Microsoft.UI.Xaml.winmd. ``` @@ -51,4 +51,4 @@ DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE - If you hit a `Class not Registered` error, this might be because a class isn't getting registered in the app manifest. You can go check `src/cascadia/CascadiaPackage/bin/x64/Debug/AppX/AppXManifest.xml` to see if there exist entries to the classes of your newly created DLL. If the references aren't there, double check that you've added `` blocks to both `WindowsTerminal.vcxproj` and `TerminalApp.vcxproj`. -- If you hit an extremely vague error along the lines of `Error in the DLL`, and right before that line you notice that your new DLL is loaded and unloaded right after each other, double check that your new DLL's definitions show up in the `AppXManifest.xml` file. If your new DLL is included as a reference to a project that rolls up to `CascadiaPackage`, double check that you've created a `.def` file for the project. Otherwise if your new project _does not_ roll up to a package that populates the `AppXManifest` references for you, you'll have to add those references yourself. +- If you hit an extremely vague error along the lines of `Error in the DLL`, and right before that line you notice that your new DLL is loaded and unloaded right after each other, double check that your new DLL's definitions show up in the `AppXManifest.xml` file. If your new DLL is included as a reference to a project that rolls up to `CascadiaPackage`, double check that you've created a `.def` file for the project. Otherwise, if your new project _does not_ roll up to a package that populates the `AppXManifest` references for you, you'll have to add those references yourself. diff --git a/doc/specs/#1564 - Settings UI/cascading-settings.md b/doc/specs/#1564 - Settings UI/cascading-settings.md index 78d8fd7bbf..0c358f945e 100644 --- a/doc/specs/#1564 - Settings UI/cascading-settings.md +++ b/doc/specs/#1564 - Settings UI/cascading-settings.md @@ -9,7 +9,7 @@ issue id: 1564 ## Abstract -Windows Terminal's settings model adheres to a cascading settings architecture. This allows a settings object to be defined incrementally across multiple layers of declarations. The value for any global setting like `copyOnSelect`, for example, is set to your settings.json value if one is defined, otherwise defaults.json, and otherwise a system set value. Profiles in particular are more complicated in that they must also take into account the values in `profiles.defaults` and dynamic profile generators. +Windows Terminal's settings model adheres to a cascading settings architecture. This allows a settings object to be defined incrementally across multiple layers of declarations. The value for any global setting like `copyOnSelect`, for example, is set to your settings.json value if one is defined; otherwise, defaults.json, and otherwise a system set value. Profiles in particular are more complicated in that they must also take into account the values in `profiles.defaults` and dynamic profile generators. This spec explores how to represent this feature in the Settings UI. diff --git a/doc/specs/#1595 - Suggestions UI/Snippets.md b/doc/specs/#1595 - Suggestions UI/Snippets.md index f80a9cf52f..39cd15de8a 100644 --- a/doc/specs/#1595 - Suggestions UI/Snippets.md +++ b/doc/specs/#1595 - Suggestions UI/Snippets.md @@ -529,7 +529,7 @@ their own workflows. * `--local`: Save to the `.wt.json` in the CWD, if there is one (or create one) * `--parent`: Save to the `.wt.json` in the first ancestor of the CWD, if - there is one. Otherwise create one here. + there is one. Otherwise, create one here. * `--settings`: Manually save to the settings file? * `--profile`: save to this profile???? Not sure if this is actually possible. Maybe with the `WT_SESSION_ID` env var to figure out which profile is in use diff --git a/doc/specs/#1790 - Font features and axes-spec.md b/doc/specs/#1790 - Font features and axes-spec.md index 26cc5fd40a..bc524a4099 100644 --- a/doc/specs/#1790 - Font features and axes-spec.md +++ b/doc/specs/#1790 - Font features and axes-spec.md @@ -23,7 +23,7 @@ In a similar vein, many fonts allow for setting variations on the font along cer ### Font features -It is already possible to pass in a list of [font feature structs](https://docs.microsoft.com/en-us/windows/win32/api/dwrite/ns-dwrite-dwrite_font_feature) to DWrite for it to handle. A font feature struct contains only 2 things: +It is already possible to pass in a list of [font feature structs](https://docs.microsoft.com/en-us/windows/win32/api/dwrite/ns-dwrite-dwrite_font_feature) to DWrite for it to handle. A font feature struct contains only 2 things: 1. A font feature tag 2. A parameter value @@ -78,7 +78,7 @@ Aside from additional parsing required for the settings file (which inherently o ### Compatibility -Older versions of Windows may not have the DWrite updates that allow for defining font features and axes of variation. We must make sure to fallback to the current implementation in these cases. +Older versions of Windows may not have the DWrite updates that allow for defining font features and axes of variation. We must make sure to fall back to the current implementation in these cases. ### Performance, Power, and Efficiency @@ -102,4 +102,4 @@ We will also need to consider how we want to represent this in the settings UI. [DWRITE_FONT_FEATURE structure](https://docs.microsoft.com/en-us/windows/win32/api/dwrite/ns-dwrite-dwrite_font_feature) -[DWRITE_FONT_AXIS_VALUE structure](https://docs.microsoft.com/en-us/windows/win32/api/dwrite_3/ns-dwrite_3-dwrite_font_axis_value) \ No newline at end of file +[DWRITE_FONT_AXIS_VALUE structure](https://docs.microsoft.com/en-us/windows/win32/api/dwrite_3/ns-dwrite_3-dwrite_font_axis_value) diff --git a/doc/specs/#4066 - Theme-controlled color scheme switch.md b/doc/specs/#4066 - Theme-controlled color scheme switch.md index 9cb41b3571..ed40c9f42d 100644 --- a/doc/specs/#4066 - Theme-controlled color scheme switch.md +++ b/doc/specs/#4066 - Theme-controlled color scheme switch.md @@ -17,7 +17,7 @@ I work remotely as a developer, so I have to spend a lot of hours in front of my Normally I like dark modes in all the programs and apps I use, but when there's too much sunlight, it becomes annoying, and sometimes even painful, to work in dark mode. So, I have all the programs and apps I use (at least, those that can) set to switch their color themes to what the system has. -The company I work for sent me a Macbook Pro, and my personal phone is an Android, both with automatic dark mode at sunset and light mode at sunrise, and in those devices it's been working relatively well. In Windows, as it is known, there's no such feature, so I manually change between dark and light mode when it's needed, and most of the programs and apps I use go along with this change. Windows Terminal, is not one of them. +The company I work for sent me a MacBook Pro, and my personal phone is an Android, both with automatic dark mode at sunset and light mode at sunrise, and in those devices it's been working relatively well. In Windows, as it is known, there's no such feature, so I manually change between dark and light mode when it's needed, and most of the programs and apps I use go along with this change. Windows Terminal, is not one of them. The theme changes just as expected, but in an app like this, this change only affects the top of the window, leaving almost all of the screen at the mercy of what the color scheme is, and it doesn't depend on the theme, which defeats any attempt to make a good use of the `system` theme feature. diff --git a/doc/specs/#5000 - Process Model 2.0/#4472 - Windows Terminal Session Management.md b/doc/specs/#5000 - Process Model 2.0/#4472 - Windows Terminal Session Management.md index 538da23725..625ef51e92 100644 --- a/doc/specs/#5000 - Process Model 2.0/#4472 - Windows Terminal Session Management.md +++ b/doc/specs/#5000 - Process Model 2.0/#4472 - Windows Terminal Session Management.md @@ -94,7 +94,7 @@ configurations: - `"useExisting"`: always glom to the most recent window, regardless of desktop. - `"useExistingOnSameDesktop"`: Only glom if there's an existing window on this - virtual desktop, otherwise create a new window. This will be the new default + virtual desktop; otherwise, create a new window. This will be the new default value. - `"useNew"`: Never glom, always create a new window. This is technically the current behavior of the Terminal. diff --git a/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 2.0.md b/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 2.0.md index 72a672cffa..620e5c30a6 100644 --- a/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 2.0.md +++ b/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 2.0.md @@ -362,7 +362,7 @@ Essentially, the probabilistic elective monarchy will work in the following way: register. 3. After registering as a server for `Monarch`s, attempt to create a `Monarch` using `winrt::create_instance`. -4. Using that `Monarch`, ask it for it's PID. +4. Using that `Monarch`, ask it for its PID. - If that PID is the same as the PID of the current process, then the window process knows that it is the monarch. - If that PID is some other process, then we know that we're not currently @@ -1102,7 +1102,7 @@ launch to use seems like an obvious next step. See also [#961]. - `true` or `"always"`: always glom to the most recent window, regardless of desktop - `"sameDesktop"`: Only glom if there's an existing window on this virtual - desktop, otherwise create a new window + desktop; otherwise, create a new window - `false` or `"never"`: Never glom, always create a new window. diff --git a/doc/specs/#605 - Search/spec.md b/doc/specs/#605 - Search/spec.md index 66a3b6dd54..e828cec0f8 100644 --- a/doc/specs/#605 - Search/spec.md +++ b/doc/specs/#605 - Search/spec.md @@ -13,7 +13,7 @@ This spec is for feature request #605 "Search". It goes over the details of a ne ## Inspiration -One of the superior features of iTerm2 is it's content search. The search comes in two variants: search from active tab and search from all tabs. In almost any editor, there is an roughly equivalent string search. We also want to realize search experience in Terminal. There will be two variants, search within one tab or from multiple tabs. We will start with one-tab search implementation. +One of the superior features of iTerm2 is it's content search. The search comes in two variants: search from active tab and search from all tabs. In almost any editor, there is a roughly equivalent string search. We also want to realize search experience in Terminal. There will be two variants, search within one tab or from multiple tabs. We will start with one-tab search implementation. ## Solution Design diff --git a/doc/specs/#7335 - Console Allocation Policy.md b/doc/specs/#7335 - Console Allocation Policy.md index c094a8edfc..f05df179d1 100644 --- a/doc/specs/#7335 - Console Allocation Policy.md +++ b/doc/specs/#7335 - Console Allocation Policy.md @@ -331,7 +331,7 @@ Are there other allocation policies we need to consider? - requires coordination between tooling teams both within and without Microsoft (regarding any tool that operates on or produces PE files) -- An exported symbol that shells can check for to determine whether to wait for the attached process to exit +- An exported symbol that shells can check for in order to determine whether to wait for the attached process to exit - relies on shells to update and check for this - cracking an executable to look for symbols is probably the last thing shells want to do - we could provide an API to determine whether to wait or return? diff --git a/doc/specs/#754 - Cascading Default Settings.md b/doc/specs/#754 - Cascading Default Settings.md index 73fd1928dc..5d43c2d0c0 100644 --- a/doc/specs/#754 - Cascading Default Settings.md +++ b/doc/specs/#754 - Cascading Default Settings.md @@ -360,7 +360,7 @@ GUID GetNamespaceGuid(IDynamicProfileGenerator& generator); GUID GetGuidForName(IDynamicProfileGenerator& generator, std::wstring& name); ``` -The generator does not _need_ to use `GetGuidForName` to generate guids for it's +The generator does not _need_ to use `GetGuidForName` to generate guids for its profiles. If the generator can determine another way to generate stable GUIDs for its profiles, it's free to use whatever method it wants. `GetGuidForName` is provided as a convenience. diff --git a/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md b/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md index 5426c9716d..6e27d19238 100644 --- a/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md +++ b/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md @@ -151,11 +151,11 @@ void CascadiaSettings::LayerJson(const Json::Value& json) // repeat the same for Profiles... } ``` -For `defaults.json`, `_globals` will now hold all of the values set in `defaults.json`. If any settings were omitted from the `defaults.json`, `_globals` will fallback to its parent (a `GlobalAppSettings` consisting purely of system-defined values). +For `defaults.json`, `_globals` will now hold all of the values set in `defaults.json`. If any settings were omitted from the `defaults.json`, `_globals` will fall back to its parent (a `GlobalAppSettings` consisting purely of system-defined values). -For `settings.json`, `_globals` will only hold the values set in `settings.json`. If any settings were omitted from `settings.json`, `_globals` will fallback to its parent (the `GlobalAppSettings` built from `defaults.json`). +For `settings.json`, `_globals` will only hold the values set in `settings.json`. If any settings were omitted from `settings.json`, `_globals` will fall back to its parent (the `GlobalAppSettings` built from `defaults.json`). -This process becomes a bit more complex for `Profile` because it can fallback in the following order: +This process becomes a bit more complex for `Profile` because it can fall back in the following order: 1. `settings.json` profile 2. `settings.json` `profiles.defaults` 3. (if a dynamic profile) the hard-coded value in the dynamic profile generator diff --git a/doc/specs/drafts/#2634 - Broadcast Input/#2634 - Broadcast Input.md b/doc/specs/drafts/#2634 - Broadcast Input/#2634 - Broadcast Input.md index c201c0bc33..6bf09a5786 100644 --- a/doc/specs/drafts/#2634 - Broadcast Input/#2634 - Broadcast Input.md +++ b/doc/specs/drafts/#2634 - Broadcast Input/#2634 - Broadcast Input.md @@ -101,8 +101,8 @@ The scopes would work as follows: this tab. - **TODO!: FOR DISCUSSION**: Should this disable the tab's "broadcastToAllPanes" setting? Or should it leave that alone? -* `"disableBroadcastInput"`: Set the global setting to false, the tab's setting - to false, and clear the set of panes being broadcasted to for this tab. +* `"disableBroadcastInput"`: For this tab, set the global setting to false, + the tab's setting to false, and clear the set of panes being broadcasted. - **TODO!** This could also just be `"action": "toggleBroadcastInput", "scope": "none"` @@ -161,7 +161,7 @@ As far as actions, we're looking at something like: from the broadcast set. Otherwise, add all the panes from this tab to the broadcast set. * **D** toggle sending input to the current pane - * If this pane is in the broadcast set, remove it. Otherwise add it. + * If this pane is in the broadcast set, remove it. Otherwise, add it. This seems to break down into the following actions: ```json diff --git a/doc/terminal-a11y-2023.md b/doc/terminal-a11y-2023.md index 3686120319..60be666fb9 100644 --- a/doc/terminal-a11y-2023.md +++ b/doc/terminal-a11y-2023.md @@ -8,12 +8,12 @@ Since accessibility is a very broad area, this document is intended to present r ### First-party terminals For many years, Console Host (Conhost) was the only first-party terminal on Windows. In 2019, Windows Terminal was released to the world as an open source first-party terminal. Windows Terminal was distributed through the Microsoft Store and received regular updates throughout the year, much more frequently than Conhost. In October 2022, Windows Terminal was enabled as the default terminal on Windows. -A significant amount of code is shared between Conhost and Windows Terminal to create the terminal area. To enable an accessible experience for this area, a shared UI Automation provider was introduced in 2019[^1], enabling accessibility tools to navigate and read contents from the terminal area. In 2020, Windows Terminal was updated to dispatch UIA events signaling when the cursor position, text output, or selection changed; this left the work of identifying what changed in the output to the attached screen reader application[^2]. In 2022, Windows Terminal was updated to dispatch UIA notifications with a payload of what text was written to the screen[^3]. +A significant amount of code is shared between Conhost and Windows Terminal to create the terminal area. To enable an accessible experience for this area, a shared UI Automation provider was introduced in 2019[^1], enabling accessibility tools to navigate and read contents from the terminal area. In 2020, Windows Terminal was updated to dispatch UIA events signaling when the cursor position, text output, or selection changed; this left the work of identifying what changed in the output to the attached screen reader application[^2]. In 2022, Windows Terminal was updated to dispatch UIA notifications with a payload of what text was written to the screen[^3]. ### Internal Partners There are many first-party command-line applications on Windows. The following are a few examples of those that are regularly updated: - [**GitHub CLI**](https://cli.github.com/): a tool that can be used to query and interact with GitHub repos (open source) -- [**Winget**](https://github.com/microsoft/winget-cli): a tool to install applications and other packages +- [**WinGet**](https://github.com/microsoft/winget-cli): a tool to install applications and other packages - [**PSReadLine**](https://github.com/PowerShell/PSReadLine): a PowerShell module that enhances the input line experience - [**Windows Subsystem for Linux (WSL)**](https://learn.microsoft.com/en-us/windows/wsl/): a tool to manage and run GNU/Linux environments without a traditional virtual machine - [**PowerShell**](https://github.com/PowerShell/PowerShell): a cross-platform command-line shell (open source) @@ -33,7 +33,7 @@ The following examples don't take over the entire viewport: - [**Oh My Posh**](https://ohmyposh.dev/): a tool to customize shell prompts - **git**: a tool for version control The following examples operate as command-line shells: -- [**Bash**](https://www.gnu.org/software/bash/) is the default shell for most Linux distributions +- [**Bash**](https://www.gnu.org/software/bash/) is the default shell for most Linux distributions - [**Fish shell**](https://fishshell.com/) provides a rich shell experience with features like autosuggestion support and VGA colors - [**Z shell**](https://zsh.sourceforge.io/) is an extended Bourne shell @@ -113,7 +113,7 @@ This issue is tracked by [megathread: Scrollbar Marks · Issue #11000](https://g ### Mark Mode support for degenerate range [PR #13053](https://github.com/microsoft/terminal/pull/13053) added support for mark mode in Windows Terminal. Mark mode allows users to create and modify selections by exclusively using the keyboard. However, screen reader users have reported it as a strange experience because it always has a cell of text selected; this results in the screen reader reading "x selected, y unselected" as opposed to the expected "x" when moving the cursor around. -Unfortunately, the changes required to fix this are very extensive because selections are stored as two inclusive terminal coordinates, which makes it impossible to represent an empty selection. +Unfortunately, the changes required to fix this are very extensive because selections are stored as two inclusive terminal coordinates, which makes it impossible to represent an empty selection. This is tracked by [A11y: windows terminal emits selection/deselection events in mark mode when navigating with arrow keys · Issue #13447](https://github.com/microsoft/terminal/issues/13447). @@ -158,7 +158,7 @@ In 2022, Windows Terminal added UI Automation notifications that contained a pay UIA notifications have provided many compatibility benefits since screen readers automatically read notifications they receive. Additionally, this has provided the possibility for major performance enhancements as screen readers may no longer be required to diff the text buffer and figure out what has changed. NVDA has prototyped listening to notifications and ignoring text changed events entirely[^7]. However, it reveals underlying challenges with this new model such as how to handle passwords. The proposals listed in this section are intended to have Windows Terminal achieve improved performance and accessibility quality. #### VT Screen Reader Control -Some command-line applications are simply too difficult to create a consistent accessible experience. Applications that draw decorative content, for example, may have that content read by a screen reader. +Some command-line applications are simply too difficult to create a consistent accessible experience. Applications that draw decorative content, for example, may have that content read by a screen reader. In 2019, Daniel Imms wrote a spec proposing a VT sequence that can partially control the attached screen reader[^8]. This VT sequence consists of three main formats: 1. Stop announcing incoming data to the screen reader. The screen reader will resume announcing incoming data if any key is pressed. @@ -214,4 +214,4 @@ Generally, the reasoning behind these priorities can be broken down as follows: [^5]: [Implement the Delta E algorithm to improve color perception by PankajBhojwani · Pull Request #11095](https://github.com/microsoft/terminal/pull/11095) [^6]: [Change AdjustIndistinguishableColors to an enum setting instead of a boolean setting by PankajBhojwani · Pull Request #13512](https://github.com/microsoft/terminal/pull/13512) [^7]: [Prototype for Windows Terminal: Use notifications instead of monitoring for new text by leonardder · Pull Request #14047 · nvaccess/nvda (github.com)](https://github.com/nvaccess/nvda/pull/14047) -[^8]: [Control Screen Reader from Applications (#18) · Issues · terminal-wg / specifications · GitLab](https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/18) \ No newline at end of file +[^8]: [Control Screen Reader from Applications (#18) · Issues · terminal-wg / specifications · GitLab](https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/18) diff --git a/policies/WindowsTerminal.admx b/policies/WindowsTerminal.admx index 48c36aec7e..47c2f981bb 100644 --- a/policies/WindowsTerminal.admx +++ b/policies/WindowsTerminal.admx @@ -9,6 +9,7 @@ + @@ -24,5 +25,61 @@ + + + + + + + + {00000000-0000-0000-0000-000000000000} + + + + + {00000000-0000-0000-0000-000000000000} + + + + + + + {B23D10C0-E52E-411E-9D5B-C09FDF709C7D} + + + + + {B23D10C0-E52E-411E-9D5B-C09FDF709C7D} + + + + + + + {E12CFF52-A866-4C77-9A90-F570A7AA2C6B} + + + + + {2EACA947-7F5F-4CFA-BA87-8F7FBEEFBE69} + + + + + + + {86633F1F-6454-40EC-89CE-DA4EBA977EE2} + + + + + {06EC847C-C0A5-46B8-92CB-7C92F6E35CD5} + + + + + + + diff --git a/policies/en-US/WindowsTerminal.adml b/policies/en-US/WindowsTerminal.adml index 5516292e12..b0be1c3d57 100644 --- a/policies/en-US/WindowsTerminal.adml +++ b/policies/en-US/WindowsTerminal.adml @@ -7,6 +7,7 @@ Windows Terminal At least Windows Terminal 1.21 + At least Windows 11 22H2 or Windows 10 22H2 (Build 19045.3031, KB5026435) with Windows Terminal 1.17 Disabled Profile Sources Profiles will not be generated from any sources listed here. Source names can be arbitrary strings. Potential candidates can be found as the "source" property on profile definitions in Windows Terminal's settings.json file. @@ -18,11 +19,22 @@ Common sources are: For instance, setting this policy to Windows.Terminal.Wsl will disable the builtin WSL integration of Windows Terminal. Note: Existing profiles will disappear from Windows Terminal after adding their source to this policy. + Default terminal application + Select the default terminal application used in Windows. + +If you select Windows Terminal Preview and it is not installed the system will fall back to the legacy Windows Console Host. (Please note that the settings interfaces showing "Let windows decide" in this case as configuration.) + Automatic selection (Windows Terminal, if available) + Windows Console Host (legacy) + Windows Terminal + Windows Terminal Preview (if available) List of disabled sources (one per line) + + Select from the following options: + diff --git a/samples/ConPTY/GUIConsole/GUIConsole.WPF/MainWindow.xaml b/samples/ConPTY/GUIConsole/GUIConsole.WPF/MainWindow.xaml index 2c67f1cb0e..36cc20b91f 100644 --- a/samples/ConPTY/GUIConsole/GUIConsole.WPF/MainWindow.xaml +++ b/samples/ConPTY/GUIConsole/GUIConsole.WPF/MainWindow.xaml @@ -32,7 +32,7 @@ BasedOn="{StaticResource TitleBarButtonStyle}" TargetType="{x:Type Button}"> - + diff --git a/scratch/ScratchIslandApp/SampleApp/packages.config b/scratch/ScratchIslandApp/SampleApp/packages.config index e970765c71..ac7697d8fc 100644 --- a/scratch/ScratchIslandApp/SampleApp/packages.config +++ b/scratch/ScratchIslandApp/SampleApp/packages.config @@ -1,6 +1,6 @@ - + diff --git a/scratch/ScratchIslandApp/WindowExe/packages.config b/scratch/ScratchIslandApp/WindowExe/packages.config index e970765c71..ac7697d8fc 100644 --- a/scratch/ScratchIslandApp/WindowExe/packages.config +++ b/scratch/ScratchIslandApp/WindowExe/packages.config @@ -1,6 +1,6 @@ - + diff --git a/src/audio/midi/lib/sources.dep b/src/audio/midi/lib/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/audio/midi/lib/sources.dep +++ b/src/audio/midi/lib/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/buffer/out/ImageSlice.cpp b/src/buffer/out/ImageSlice.cpp index 65809e58a0..3500eaf0a5 100644 --- a/src/buffer/out/ImageSlice.cpp +++ b/src/buffer/out/ImageSlice.cpp @@ -186,18 +186,20 @@ bool ImageSlice::_copyCells(const ImageSlice& srcSlice, const til::CoordType src } // The used destination before and after the written area must be erased. - if (dstUsedBegin < dstWriteBegin) + // If this results in the entire range being erased, we return true to let + // the caller know that the slice should be deleted. + if (dstUsedBegin < dstWriteBegin && _eraseCells(dstUsedBegin, dstWriteBegin)) { - _eraseCells(dstUsedBegin, dstWriteBegin); + return true; } - if (dstUsedEnd > dstWriteEnd) + if (dstUsedEnd > dstWriteEnd && _eraseCells(dstWriteEnd, dstUsedEnd)) { - _eraseCells(dstWriteEnd, dstUsedEnd); + return true; } - // If the beginning column is now not less than the end, that means the - // content has been entirely erased, so we return true to let the caller - // know that the slice should be deleted. + // At this point, if the beginning column is not less than the end, that + // means this was an empty slice into which nothing was copied, so we can + // again return true to let the caller know it should be deleted. return _columnBegin >= _columnEnd; } @@ -210,10 +212,19 @@ void ImageSlice::EraseBlock(TextBuffer& buffer, const til::rect rect) } } -void ImageSlice::EraseCells(TextBuffer& buffer, const til::point at, const size_t distance) +void ImageSlice::EraseCells(TextBuffer& buffer, const til::point at, const til::CoordType distance) { - auto& row = buffer.GetMutableRowByOffset(at.y); - EraseCells(row, at.x, gsl::narrow_cast(at.x + distance)); + auto x = at.x; + auto y = at.y; + auto distanceRemaining = distance; + while (distanceRemaining > 0) + { + auto& row = buffer.GetMutableRowByOffset(y); + EraseCells(row, x, x + distanceRemaining); + distanceRemaining -= (static_cast(row.size()) - x); + x = 0; + y++; + } } void ImageSlice::EraseCells(ROW& row, const til::CoordType columnBegin, const til::CoordType columnEnd) diff --git a/src/buffer/out/ImageSlice.hpp b/src/buffer/out/ImageSlice.hpp index 87244abe78..14c0b314ae 100644 --- a/src/buffer/out/ImageSlice.hpp +++ b/src/buffer/out/ImageSlice.hpp @@ -41,7 +41,7 @@ public: static void CopyRow(const ROW& srcRow, ROW& dstRow); static void CopyCells(const ROW& srcRow, const til::CoordType srcColumn, ROW& dstRow, const til::CoordType dstColumnBegin, const til::CoordType dstColumnEnd); static void EraseBlock(TextBuffer& buffer, const til::rect rect); - static void EraseCells(TextBuffer& buffer, const til::point at, const size_t distance); + static void EraseCells(TextBuffer& buffer, const til::point at, const til::CoordType distance); static void EraseCells(ROW& row, const til::CoordType columnBegin, const til::CoordType columnEnd); private: diff --git a/src/buffer/out/Row.cpp b/src/buffer/out/Row.cpp index 63ba83f4cd..8d7fa6837d 100644 --- a/src/buffer/out/Row.cpp +++ b/src/buffer/out/Row.cpp @@ -431,7 +431,7 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const til::CoordType c THROW_HR_IF(E_INVALIDARG, limitRight.value_or(0) >= size()); // If we're given a right-side column limit, use it. Otherwise, the write limit is the final column index available in the char row. - const auto finalColumnInRow = limitRight.value_or(size() - 1); + const auto finalColumnInRow = gsl::narrow_cast(limitRight.value_or(size() - 1)); auto currentColor = it->TextAttr(); uint16_t colorUses = 0; @@ -942,12 +942,12 @@ void ROW::_resizeChars(uint16_t colEndDirty, uint16_t chBegDirty, size_t chEndDi } } -til::small_rle& ROW::Attributes() noexcept +RowAttributes& ROW::Attributes() noexcept { return _attr; } -const til::small_rle& ROW::Attributes() const noexcept +const RowAttributes& ROW::Attributes() const noexcept { return _attr; } diff --git a/src/buffer/out/Row.hpp b/src/buffer/out/Row.hpp index 44156d1b88..a1efe36707 100644 --- a/src/buffer/out/Row.hpp +++ b/src/buffer/out/Row.hpp @@ -14,6 +14,11 @@ class ROW; class TextBuffer; +// Because MarkKind::Output gets set only on the actually written text, +// most rows will end up having at least 2 runs: The start of the line +// with MarkKind::Output and the rest of the line with MarkKind::None. +using RowAttributes = til::small_rle; + enum class DelimiterClass { ControlChar, @@ -149,8 +154,8 @@ public: void ReplaceText(RowWriteState& state); void CopyTextFrom(RowCopyTextFromState& state); - til::small_rle& Attributes() noexcept; - const til::small_rle& Attributes() const noexcept; + RowAttributes& Attributes() noexcept; + const RowAttributes& Attributes() const noexcept; TextAttribute GetAttrByColumn(til::CoordType column) const; std::vector GetHyperlinks() const; ImageSlice* SetImageSlice(ImageSlice::Pointer imageSlice) noexcept; @@ -298,7 +303,7 @@ private: std::span _charOffsets; // _attr is a run-length-encoded vector of TextAttribute with a decompressed // length equal to _columnCount (= 1 TextAttribute per column). - til::small_rle _attr; + RowAttributes _attr; // The width of the row in visual columns. uint16_t _columnCount = 0; // Stores double-width/height (DECSWL/DECDWL/DECDHL) attributes. diff --git a/src/buffer/out/TextAttribute.cpp b/src/buffer/out/TextAttribute.cpp index f95edf6bea..76604686be 100644 --- a/src/buffer/out/TextAttribute.cpp +++ b/src/buffer/out/TextAttribute.cpp @@ -232,6 +232,11 @@ void TextAttribute::SetRightVerticalDisplayed(const bool isDisplayed) noexcept WI_UpdateFlag(_attrs, CharacterAttributes::RightGridline, isDisplayed); } +bool TextAttribute::IsBold(const bool intenseIsBold) const noexcept +{ + return IsIntense() && (intenseIsBold || !_foreground.CanBeBrightened()); +} + bool TextAttribute::IsIntense() const noexcept { return WI_IsFlagSet(_attrs, CharacterAttributes::Intense); diff --git a/src/buffer/out/TextAttribute.hpp b/src/buffer/out/TextAttribute.hpp index b1d69d1f3b..bca0df878c 100644 --- a/src/buffer/out/TextAttribute.hpp +++ b/src/buffer/out/TextAttribute.hpp @@ -115,6 +115,7 @@ public: return memcmp(this, &other, sizeof(TextAttribute)) != 0; } + bool IsBold(const bool intenseIsBold) const noexcept; bool IsLegacy() const noexcept; bool IsIntense() const noexcept; bool IsFaint() const noexcept; diff --git a/src/buffer/out/UTextAdapter.cpp b/src/buffer/out/UTextAdapter.cpp index 717d97812a..1b392f3d23 100644 --- a/src/buffer/out/UTextAdapter.cpp +++ b/src/buffer/out/UTextAdapter.cpp @@ -400,17 +400,6 @@ Microsoft::Console::ICU::unique_utext Microsoft::Console::ICU::UTextFromTextBuff return ut; } -Microsoft::Console::ICU::unique_uregex Microsoft::Console::ICU::CreateRegex(const std::wstring_view& pattern, uint32_t flags, UErrorCode* status) noexcept -{ -#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1). - const auto re = uregex_open(reinterpret_cast(pattern.data()), gsl::narrow_cast(pattern.size()), flags, nullptr, status); - // ICU describes the time unit as being dependent on CPU performance and "typically [in] the order of milliseconds", - // but this claim seems highly outdated already. On my CPU from 2021, a limit of 4096 equals roughly 600ms. - uregex_setTimeLimit(re, 4096, status); - uregex_setStackLimit(re, 4 * 1024 * 1024, status); - return unique_uregex{ re }; -} - // Returns a half-open [beg,end) range given a text start and end position. // This function is designed to be used with uregex_start64/uregex_end64. til::point_span Microsoft::Console::ICU::BufferRangeFromMatch(UText* ut, URegularExpression* re) diff --git a/src/buffer/out/UTextAdapter.h b/src/buffer/out/UTextAdapter.h index 39903627b5..fe6bacafd4 100644 --- a/src/buffer/out/UTextAdapter.h +++ b/src/buffer/out/UTextAdapter.h @@ -9,10 +9,8 @@ class TextBuffer; namespace Microsoft::Console::ICU { - using unique_uregex = wistd::unique_ptr>; using unique_utext = wil::unique_struct; unique_utext UTextFromTextBuffer(const TextBuffer& textBuffer, til::CoordType rowBeg, til::CoordType rowEnd) noexcept; - unique_uregex CreateRegex(const std::wstring_view& pattern, uint32_t flags, UErrorCode* status) noexcept; til::point_span BufferRangeFromMatch(UText* ut, URegularExpression* re); } diff --git a/src/buffer/out/lib/sources.dep b/src/buffer/out/lib/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/buffer/out/lib/sources.dep +++ b/src/buffer/out/lib/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index df30a3ffb2..6f0336e8bc 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -10,6 +10,7 @@ #include "../../types/inc/CodepointWidthDetector.hpp" #include "../renderer/base/renderer.hpp" #include "../types/inc/utils.hpp" +#include #include "search.h" // BODGY: Misdiagnosis in MSVC 17.11: Referencing global constants in the member @@ -2061,14 +2062,6 @@ void TextBuffer::_ExpandTextRow(til::inclusive_rect& textRow) const } } -size_t TextBuffer::SpanLength(const til::point coordStart, const til::point coordEnd) const -{ - const auto bufferSize = GetSize(); - // The coords are inclusive, so to get the (inclusive) length we add 1. - const auto length = bufferSize.CompareInBounds(coordEnd, coordStart) + 1; - return gsl::narrow(length); -} - // Routine Description: // - Retrieves the plain text data between the specified coordinates. // Arguments: @@ -2286,7 +2279,7 @@ std::string TextBuffer::GenHTML(const CopyRequest& req, fmt::format_to(std::back_inserter(htmlBuilder), FMT_COMPILE("color:{};"), fgHex); fmt::format_to(std::back_inserter(htmlBuilder), FMT_COMPILE("background-color:{};"), bgHex); - if (isIntenseBold && attr.IsIntense()) + if (attr.IsBold(isIntenseBold)) { htmlBuilder += "font-weight:bold;"; } @@ -2536,7 +2529,7 @@ std::string TextBuffer::GenRTF(const CopyRequest& req, fmt::format_to(std::back_inserter(contentBuilder), FMT_COMPILE("\\cf{}"), fgIdx); fmt::format_to(std::back_inserter(contentBuilder), FMT_COMPILE("\\chshdng0\\chcbpat{}"), bgIdx); - if (isIntenseBold && attr.IsIntense()) + if (attr.IsBold(isIntenseBold)) { contentBuilder += "\\b"; } @@ -2976,7 +2969,7 @@ void TextBuffer::_SerializeRow(const ROW& row, const til::CoordType startX, cons // parameter and we'll calculate the position of the _end_ of those rows in // the new buffer. The rows's new value is placed back into this parameter. // Return Value: -// - S_OK if we successfully copied the contents to the new buffer, otherwise an appropriate HRESULT. +// - S_OK if we successfully copied the contents to the new buffer; otherwise, an appropriate HRESULT. void TextBuffer::Reflow(TextBuffer& oldBuffer, TextBuffer& newBuffer, const Viewport* lastCharacterViewport, PositionInformation* positionInfo) { const auto& oldCursor = oldBuffer.GetCursor(); @@ -3361,7 +3354,7 @@ std::optional> TextBuffer::SearchText(const std::ws } UErrorCode status = U_ZERO_ERROR; - const auto re = ICU::CreateRegex(needle, icuFlags, &status); + const auto re = til::ICU::CreateRegex(needle, icuFlags, &status); if (status > U_ZERO_ERROR) { return std::nullopt; diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index 775417caab..fca973f50d 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -199,8 +199,6 @@ public: std::wstring GetCustomIdFromId(uint16_t id) const; void CopyHyperlinkMaps(const TextBuffer& OtherBuffer); - size_t SpanLength(const til::point coordStart, const til::point coordEnd) const; - std::wstring GetPlainText(til::point start, til::point end) const; struct CopyRequest diff --git a/src/buffer/out/ut_textbuffer/ReflowTests.cpp b/src/buffer/out/ut_textbuffer/ReflowTests.cpp index e46e415e37..58e7766c89 100644 --- a/src/buffer/out/ut_textbuffer/ReflowTests.cpp +++ b/src/buffer/out/ut_textbuffer/ReflowTests.cpp @@ -573,7 +573,7 @@ namespace TestCase{ // This triggers the cursor being walked forward w/ newlines to maintain // distance from the last char in the buffer - L"SBCS, cursor at end of buffer, otherwise same as previous test", + L"SBCS, cursor at end of buffer; otherwise, same as previous test", { TestBuffer{ { 6, 5 }, diff --git a/src/buffer/out/ut_textbuffer/sources.dep b/src/buffer/out/ut_textbuffer/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/buffer/out/ut_textbuffer/sources.dep +++ b/src/buffer/out/ut_textbuffer/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/cascadia/CascadiaPackage/ProfileGeneratorIcons/AzureCloudShell.png b/src/cascadia/CascadiaPackage/ProfileGeneratorIcons/AzureCloudShell.png new file mode 100644 index 0000000000..c7ee454ce9 Binary files /dev/null and b/src/cascadia/CascadiaPackage/ProfileGeneratorIcons/AzureCloudShell.png differ diff --git a/src/cascadia/CascadiaPackage/ProfileGeneratorIcons/PowerShell.png b/src/cascadia/CascadiaPackage/ProfileGeneratorIcons/PowerShell.png new file mode 100644 index 0000000000..72f5e54e31 Binary files /dev/null and b/src/cascadia/CascadiaPackage/ProfileGeneratorIcons/PowerShell.png differ diff --git a/src/cascadia/CascadiaPackage/ProfileIcons/{550ce7b8-d500-50ad-8a1a-c400c3262db3}.scale-200.png b/src/cascadia/CascadiaPackage/ProfileGeneratorIcons/SSH.png similarity index 100% rename from src/cascadia/CascadiaPackage/ProfileIcons/{550ce7b8-d500-50ad-8a1a-c400c3262db3}.scale-200.png rename to src/cascadia/CascadiaPackage/ProfileGeneratorIcons/SSH.png diff --git a/src/cascadia/CascadiaPackage/ProfileGeneratorIcons/VisualStudio.png b/src/cascadia/CascadiaPackage/ProfileGeneratorIcons/VisualStudio.png new file mode 100644 index 0000000000..c41b6cb581 Binary files /dev/null and b/src/cascadia/CascadiaPackage/ProfileGeneratorIcons/VisualStudio.png differ diff --git a/src/cascadia/CascadiaPackage/ProfileGeneratorIcons/WSL.png b/src/cascadia/CascadiaPackage/ProfileGeneratorIcons/WSL.png new file mode 100644 index 0000000000..b572346ff5 Binary files /dev/null and b/src/cascadia/CascadiaPackage/ProfileGeneratorIcons/WSL.png differ diff --git a/src/cascadia/CascadiaPackage/ProfileIcons/{550ce7b8-d500-50ad-8a1a-c400c3262db3}.scale-100.png b/src/cascadia/CascadiaPackage/ProfileIcons/{550ce7b8-d500-50ad-8a1a-c400c3262db3}.scale-100.png deleted file mode 100644 index 6d57b166f2..0000000000 Binary files a/src/cascadia/CascadiaPackage/ProfileIcons/{550ce7b8-d500-50ad-8a1a-c400c3262db3}.scale-100.png and /dev/null differ diff --git a/src/cascadia/CascadiaResources.build.items b/src/cascadia/CascadiaResources.build.items index cfd11c79f4..336fad5048 100644 --- a/src/cascadia/CascadiaResources.build.items +++ b/src/cascadia/CascadiaResources.build.items @@ -21,6 +21,11 @@ true ProfileIcons\%(RecursiveDir)%(FileName)%(Extension) + + + true + ProfileGeneratorIcons\%(RecursiveDir)%(FileName)%(Extension) + true diff --git a/src/cascadia/ElevateShim/elevate-shim.vcxproj b/src/cascadia/ElevateShim/elevate-shim.vcxproj index 95b6240659..80dccc6b0c 100644 --- a/src/cascadia/ElevateShim/elevate-shim.vcxproj +++ b/src/cascadia/ElevateShim/elevate-shim.vcxproj @@ -7,6 +7,7 @@ elevate-shim elevate-shim Application + Windows Terminal Administrator Launch Helper diff --git a/src/cascadia/LocalTests_TerminalApp/FilteredCommandTests.cpp b/src/cascadia/LocalTests_TerminalApp/FilteredCommandTests.cpp index e96675fd32..c079a547d1 100644 --- a/src/cascadia/LocalTests_TerminalApp/FilteredCommandTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/FilteredCommandTests.cpp @@ -2,8 +2,8 @@ // Licensed under the MIT license. #include "pch.h" -#include "../TerminalApp/CommandLinePaletteItem.h" #include "../TerminalApp/CommandPalette.h" +#include "../TerminalApp/BasePaletteItem.h" #include "CppWinrtTailored.h" using namespace Microsoft::Console; @@ -15,6 +15,20 @@ using namespace winrt::Microsoft::Terminal::Control; namespace TerminalAppLocalTests { + struct StringPaletteItem : winrt::implements, winrt::TerminalApp::implementation::BasePaletteItem + { + StringPaletteItem(std::wstring_view value) : + _value{ value } {} + + winrt::hstring Name() { return _value; } + winrt::hstring Subtitle() { return {}; } + winrt::hstring KeyChordText() { return {}; } + winrt::hstring Icon() { return {}; } + + private: + winrt::hstring _value; + }; + class FilteredCommandTests { BEGIN_TEST_CLASS(FilteredCommandTests) @@ -28,81 +42,81 @@ namespace TerminalAppLocalTests TEST_METHOD(VerifyCompareIgnoreCase); }; + static void _verifySegment(auto&& segments, uint32_t index, uint64_t start, uint64_t end) + { + const auto& segment{ segments.GetAt(index) }; + VERIFY_ARE_EQUAL(segment.Start, start, NoThrowString().Format(L"segment %zu", index)); + VERIFY_ARE_EQUAL(segment.End, end, NoThrowString().Format(L"segment %zu", index)); + } + void FilteredCommandTests::VerifyHighlighting() { auto result = RunOnUIThread([]() { - const auto paletteItem{ winrt::make(L"AAAAAABBBBBBCCC") }; + const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope; + + const auto paletteItem{ winrt::make(L"AAAAAABBBBBBCCC") }; + const auto filteredCommand = winrt::make_self(paletteItem); + { Log::Comment(L"Testing command name segmentation with no filter"); - const auto filteredCommand = winrt::make_self(paletteItem); - auto segments = filteredCommand->_computeHighlightedName().Segments(); - VERIFY_ARE_EQUAL(segments.Size(), 1u); - VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC"); - VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted()); + const auto segments = filteredCommand->NameHighlights(); + + VERIFY_IS_NULL(segments); // No matches = no segments } { Log::Comment(L"Testing command name segmentation with empty filter"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L""; - auto segments = filteredCommand->_computeHighlightedName().Segments(); - VERIFY_ARE_EQUAL(segments.Size(), 1u); - VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC"); - VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted()); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L""))); + const auto segments = filteredCommand->NameHighlights(); + + VERIFY_IS_NULL(segments); // No matches = no segments } { Log::Comment(L"Testing command name segmentation with filter equal to the string"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"AAAAAABBBBBBCCC"; - auto segments = filteredCommand->_computeHighlightedName().Segments(); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L"AAAAAABBBBBBCCC"))); + const auto segments = filteredCommand->NameHighlights(); + VERIFY_ARE_EQUAL(segments.Size(), 1u); - VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC"); - VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted()); + _verifySegment(segments, 0, 0, 14); // one segment for the entire string } { Log::Comment(L"Testing command name segmentation with filter with first character matching"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"A"; - auto segments = filteredCommand->_computeHighlightedName().Segments(); - VERIFY_ARE_EQUAL(segments.Size(), 2u); - VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A"); - VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted()); - VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAABBBBBBCCC"); - VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted()); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L"A"))); + const auto segments = filteredCommand->NameHighlights(); + + VERIFY_ARE_EQUAL(segments.Size(), 1u); // only one bold segment + _verifySegment(segments, 0, 0, 0); // it only covers the first character } { Log::Comment(L"Testing command name segmentation with filter with other case"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"a"; - auto segments = filteredCommand->_computeHighlightedName().Segments(); - VERIFY_ARE_EQUAL(segments.Size(), 2u); - VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A"); - VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted()); - VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAABBBBBBCCC"); - VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted()); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L"a"))); + const auto segments = filteredCommand->NameHighlights(); + + VERIFY_ARE_EQUAL(segments.Size(), 1u); // only one bold segment + _verifySegment(segments, 0, 0, 0); // it only covers the first character } { Log::Comment(L"Testing command name segmentation with filter matching several characters"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"ab"; - auto segments = filteredCommand->_computeHighlightedName().Segments(); - VERIFY_ARE_EQUAL(segments.Size(), 4u); - VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A"); - VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted()); - VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAA"); - VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted()); - VERIFY_ARE_EQUAL(segments.GetAt(2).TextSegment(), L"B"); - VERIFY_IS_TRUE(segments.GetAt(2).IsHighlighted()); - VERIFY_ARE_EQUAL(segments.GetAt(3).TextSegment(), L"BBBBBCCC"); - VERIFY_IS_FALSE(segments.GetAt(3).IsHighlighted()); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L"ab"))); + const auto segments = filteredCommand->NameHighlights(); + + VERIFY_ARE_EQUAL(segments.Size(), 1u); // one bold segment + _verifySegment(segments, 0, 5, 6); // middle 'ab' + } + { + Log::Comment(L"Testing command name segmentation with filter matching several regions"); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L"abcc"))); + const auto segments = filteredCommand->NameHighlights(); + + VERIFY_ARE_EQUAL(segments.Size(), 2u); // two bold segments + _verifySegment(segments, 0, 5, 6); // middle 'ab' + _verifySegment(segments, 1, 12, 13); // start of 'cc' } { Log::Comment(L"Testing command name segmentation with non matching filter"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"abcd"; - auto segments = filteredCommand->_computeHighlightedName().Segments(); - VERIFY_ARE_EQUAL(segments.Size(), 1u); - VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC"); - VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted()); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L"abcd"))); + const auto segments = filteredCommand->NameHighlights(); + + VERIFY_IS_NULL(segments); // No matches = no segments } }); @@ -112,54 +126,38 @@ namespace TerminalAppLocalTests void FilteredCommandTests::VerifyWeight() { auto result = RunOnUIThread([]() { - const auto paletteItem{ winrt::make(L"AAAAAABBBBBBCCC") }; - { - Log::Comment(L"Testing weight of command with no filter"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); - auto weight = filteredCommand->_computeWeight(); - VERIFY_ARE_EQUAL(weight, 0); - } - { - Log::Comment(L"Testing weight of command with empty filter"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L""; - filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); - auto weight = filteredCommand->_computeWeight(); - VERIFY_ARE_EQUAL(weight, 0); - } - { - Log::Comment(L"Testing weight of command with filter equal to the string"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"AAAAAABBBBBBCCC"; - filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); - auto weight = filteredCommand->_computeWeight(); - VERIFY_ARE_EQUAL(weight, 30); // 1 point for the first char and 2 points for the 14 consequent ones + 1 point for the beginning of the word - } - { - Log::Comment(L"Testing weight of command with filter with first character matching"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"A"; - filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); - auto weight = filteredCommand->_computeWeight(); - VERIFY_ARE_EQUAL(weight, 2); // 1 point for the first char match + 1 point for the beginning of the word - } - { - Log::Comment(L"Testing weight of command with filter with other case"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"a"; - filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); - auto weight = filteredCommand->_computeWeight(); - VERIFY_ARE_EQUAL(weight, 2); // 1 point for the first char match + 1 point for the beginning of the word - } - { - Log::Comment(L"Testing weight of command with filter matching several characters"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"ab"; - filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); - auto weight = filteredCommand->_computeWeight(); - VERIFY_ARE_EQUAL(weight, 3); // 1 point for the first char match + 1 point for the beginning of the word + 1 point for the match of "b" - } + const auto paletteItem{ winrt::make(L"AAAAAABBBBBBCCC") }; + const auto filteredCommand = winrt::make_self(paletteItem); + + const auto weigh = [&](const wchar_t* str) { + std::shared_ptr pattern; + if (str) + { + pattern = std::make_shared(fzf::matcher::ParsePattern(str)); + } + filteredCommand->UpdateFilter(std::move(pattern)); + return filteredCommand->Weight(); + }; + + const auto null = weigh(nullptr); + const auto empty = weigh(L""); + const auto full = weigh(L"AAAAAABBBBBBCCC"); + const auto firstChar = weigh(L"A"); + const auto otherCase = weigh(L"a"); + const auto severalChars = weigh(L"ab"); + + VERIFY_ARE_EQUAL(null, 0); + VERIFY_ARE_EQUAL(empty, 0); + VERIFY_IS_GREATER_THAN(full, 100); + + VERIFY_IS_GREATER_THAN(firstChar, 0); + VERIFY_IS_LESS_THAN(firstChar, full); + + VERIFY_IS_GREATER_THAN(otherCase, 0); + VERIFY_IS_LESS_THAN(otherCase, full); + + VERIFY_IS_GREATER_THAN(severalChars, otherCase); + VERIFY_IS_LESS_THAN(severalChars, full); }); VERIFY_SUCCEEDED(result); @@ -168,8 +166,8 @@ namespace TerminalAppLocalTests void FilteredCommandTests::VerifyCompare() { auto result = RunOnUIThread([]() { - const auto paletteItem{ winrt::make(L"AAAAAABBBBBBCCC") }; - const auto paletteItem2{ winrt::make(L"BBBBBCCC") }; + const auto paletteItem{ winrt::make(L"AAAAAABBBBBBCCC") }; + const auto paletteItem2{ winrt::make(L"BBBBBCCC") }; { Log::Comment(L"Testing comparison of commands with no filter"); const auto filteredCommand = winrt::make_self(paletteItem); @@ -181,14 +179,10 @@ namespace TerminalAppLocalTests { Log::Comment(L"Testing comparison of commands with empty filter"); const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L""; - filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); - filteredCommand->_Weight = filteredCommand->_computeWeight(); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L""))); const auto filteredCommand2 = winrt::make_self(paletteItem2); - filteredCommand2->_Filter = L""; - filteredCommand2->_HighlightedName = filteredCommand2->_computeHighlightedName(); - filteredCommand2->_Weight = filteredCommand2->_computeWeight(); + filteredCommand2->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L""))); VERIFY_ARE_EQUAL(filteredCommand->Weight(), filteredCommand2->Weight()); VERIFY_IS_TRUE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2)); @@ -196,16 +190,12 @@ namespace TerminalAppLocalTests { Log::Comment(L"Testing comparison of commands with different weights"); const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"B"; - filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); - filteredCommand->_Weight = filteredCommand->_computeWeight(); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L"B"))); const auto filteredCommand2 = winrt::make_self(paletteItem2); - filteredCommand2->_Filter = L"B"; - filteredCommand2->_HighlightedName = filteredCommand2->_computeHighlightedName(); - filteredCommand2->_Weight = filteredCommand2->_computeWeight(); + filteredCommand2->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L"B"))); - VERIFY_IS_TRUE(filteredCommand->Weight() < filteredCommand2->Weight()); // Second command gets more points due to the beginning of the word + VERIFY_IS_LESS_THAN(filteredCommand->Weight(), filteredCommand2->Weight()); // Second command gets more points due to the beginning of the word VERIFY_IS_FALSE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2)); } }); @@ -216,8 +206,8 @@ namespace TerminalAppLocalTests void FilteredCommandTests::VerifyCompareIgnoreCase() { auto result = RunOnUIThread([]() { - const auto paletteItem{ winrt::make(L"a") }; - const auto paletteItem2{ winrt::make(L"B") }; + const auto paletteItem{ winrt::make(L"a") }; + const auto paletteItem2{ winrt::make(L"B") }; { const auto filteredCommand = winrt::make_self(paletteItem); const auto filteredCommand2 = winrt::make_self(paletteItem2); diff --git a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp index ead353fc90..67b6a17466 100644 --- a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp @@ -5,12 +5,14 @@ #include "../TerminalApp/TerminalPage.h" #include "../UnitTests_SettingsModel/TestUtils.h" +#include "../TerminalSettingsAppAdapterLib/TerminalSettings.h" using namespace Microsoft::Console; using namespace WEX::Logging; using namespace WEX::TestExecution; using namespace WEX::Common; using namespace winrt::TerminalApp; +using namespace winrt::Microsoft::Terminal::Settings; using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::Microsoft::Terminal::Control; @@ -1419,10 +1421,10 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"profile0", terminalArgs.Profile()); VERIFY_IS_NULL(terminalArgs.Elevate()); - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs); const auto termSettings = termSettingsResult.DefaultSettings(); - VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(false, termSettings.Elevate()); + VERIFY_ARE_EQUAL(L"cmd.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(false, termSettings->Elevate()); } { Log::Comment(L"profile.elevate=true, action.elevate=nullopt: DO auto elevate"); @@ -1442,10 +1444,10 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"profile1", terminalArgs.Profile()); VERIFY_IS_NULL(terminalArgs.Elevate()); - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs); const auto termSettings = termSettingsResult.DefaultSettings(); - VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(true, termSettings.Elevate()); + VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(true, termSettings->Elevate()); } { Log::Comment(L"profile.elevate=false, action.elevate=nullopt: don't auto elevate"); @@ -1465,10 +1467,10 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile()); VERIFY_IS_NULL(terminalArgs.Elevate()); - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs); const auto termSettings = termSettingsResult.DefaultSettings(); - VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(false, termSettings.Elevate()); + VERIFY_ARE_EQUAL(L"wsl.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(false, termSettings->Elevate()); } { @@ -1490,10 +1492,10 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(terminalArgs.Elevate()); VERIFY_IS_FALSE(terminalArgs.Elevate().Value()); - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs); const auto termSettings = termSettingsResult.DefaultSettings(); - VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(false, termSettings.Elevate()); + VERIFY_ARE_EQUAL(L"cmd.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(false, termSettings->Elevate()); } { Log::Comment(L"profile.elevate=true, action.elevate=false: don't auto elevate"); @@ -1514,10 +1516,10 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(terminalArgs.Elevate()); VERIFY_IS_FALSE(terminalArgs.Elevate().Value()); - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs); const auto termSettings = termSettingsResult.DefaultSettings(); - VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(false, termSettings.Elevate()); + VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(false, termSettings->Elevate()); } { Log::Comment(L"profile.elevate=false, action.elevate=false: don't auto elevate"); @@ -1538,10 +1540,10 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(terminalArgs.Elevate()); VERIFY_IS_FALSE(terminalArgs.Elevate().Value()); - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs); const auto termSettings = termSettingsResult.DefaultSettings(); - VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(false, termSettings.Elevate()); + VERIFY_ARE_EQUAL(L"wsl.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(false, termSettings->Elevate()); } { @@ -1563,10 +1565,10 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(terminalArgs.Elevate()); VERIFY_IS_TRUE(terminalArgs.Elevate().Value()); - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs); const auto termSettings = termSettingsResult.DefaultSettings(); - VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(true, termSettings.Elevate()); + VERIFY_ARE_EQUAL(L"cmd.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(true, termSettings->Elevate()); } { Log::Comment(L"profile.elevate=true, action.elevate=true: DO auto elevate"); @@ -1586,10 +1588,10 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(terminalArgs.Elevate()); VERIFY_IS_TRUE(terminalArgs.Elevate().Value()); - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs); const auto termSettings = termSettingsResult.DefaultSettings(); - VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(true, termSettings.Elevate()); + VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(true, termSettings->Elevate()); } { Log::Comment(L"profile.elevate=false, action.elevate=true: DO auto elevate"); @@ -1610,10 +1612,10 @@ namespace TerminalAppLocalTests VERIFY_IS_NOT_NULL(terminalArgs.Elevate()); VERIFY_IS_TRUE(terminalArgs.Elevate().Value()); - const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, nullptr); + const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs); const auto termSettings = termSettingsResult.DefaultSettings(); - VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(true, termSettings.Elevate()); + VERIFY_ARE_EQUAL(L"wsl.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(true, termSettings->Elevate()); } } diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp index 941d5fa06f..8036acd594 100644 --- a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp @@ -8,7 +8,7 @@ #include "../TerminalApp/MinMaxCloseControl.h" #include "../TerminalApp/TabRowControl.h" #include "../TerminalApp/ShortcutActionDispatch.h" -#include "../TerminalApp/TerminalTab.h" +#include "../TerminalApp/Tab.h" #include "../TerminalApp/CommandPalette.h" #include "../TerminalApp/ContentManager.h" #include "CppWinrtTailored.h" @@ -66,7 +66,6 @@ namespace TerminalAppLocalTests // can help you identify if something much lower in the stack has // failed. TEST_METHOD(EnsureTestsActivate); - TEST_METHOD(TryCreateSettingsType); TEST_METHOD(TryCreateConnectionType); TEST_METHOD(TryCreateXamlObjects); @@ -131,23 +130,12 @@ namespace TerminalAppLocalTests VERIFY_IS_TRUE(true); } - void TabTests::TryCreateSettingsType() - { - // Verify we can create a WinRT type we authored - // Just creating it is enough to know that everything is working. - TerminalSettings settings; - VERIFY_IS_NOT_NULL(settings); - } - void TabTests::TryCreateConnectionType() { // Verify we can create a WinRT type we authored // Just creating it is enough to know that everything is working. winrt::Microsoft::Terminal::TerminalConnection::EchoConnection conn{}; VERIFY_IS_NOT_NULL(conn); - // We're doing this test separately from the TryCreateSettingsType test, - // to ensure both dependent binaries (TerminalSettings and - // TerminalConnection) both work individually. } void TabTests::TryCreateXamlObjects() @@ -307,7 +295,7 @@ namespace TerminalAppLocalTests // reliably in the unit tests. Log::Comment(L"Ensure we set the first tab as the selected one."); auto tab = page->_tabs.GetAt(0); - auto tabImpl = page->_GetTerminalTabImpl(tab); + auto tabImpl = page->_GetTabImpl(tab); page->_tabView.SelectedItem(tabImpl->TabViewItem()); page->_UpdatedSelectedTab(tab); }); @@ -510,7 +498,7 @@ namespace TerminalAppLocalTests result = RunOnUIThread([&page]() { VERIFY_ARE_EQUAL(1u, page->_tabs.Size()); - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(1, tab->GetLeafPaneCount()); }); VERIFY_SUCCEEDED(result); @@ -520,7 +508,7 @@ namespace TerminalAppLocalTests page->_SplitPane(nullptr, SplitDirection::Automatic, 0.5f, page->_MakePane(nullptr, page->_GetFocusedTab(), nullptr)); VERIFY_ARE_EQUAL(1u, page->_tabs.Size()); - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, tab->GetLeafPaneCount()); }); VERIFY_SUCCEEDED(result); @@ -538,7 +526,7 @@ namespace TerminalAppLocalTests page->_SplitPane(nullptr, SplitDirection::Automatic, 0.5f, page->_MakePane(nullptr, page->_GetFocusedTab(), nullptr)); VERIFY_ARE_EQUAL(1u, page->_tabs.Size()); - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(3, tab->GetLeafPaneCount(), L"We should successfully duplicate a pane hosting a deleted profile."); @@ -706,7 +694,7 @@ namespace TerminalAppLocalTests SplitPaneArgs args{ SplitType::Duplicate }; ActionEventArgs eventArgs{ args }; page->_HandleSplitPane(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_FALSE(firstTab->IsZoomed()); @@ -717,7 +705,7 @@ namespace TerminalAppLocalTests result = RunOnUIThread([&page]() { ActionEventArgs eventArgs{}; page->_HandleTogglePaneZoom(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_TRUE(firstTab->IsZoomed()); }); @@ -727,7 +715,7 @@ namespace TerminalAppLocalTests result = RunOnUIThread([&page]() { ActionEventArgs eventArgs{}; page->_HandleTogglePaneZoom(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_FALSE(firstTab->IsZoomed()); }); @@ -744,7 +732,7 @@ namespace TerminalAppLocalTests SplitPaneArgs args{ SplitType::Duplicate }; ActionEventArgs eventArgs{ args }; page->_HandleSplitPane(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_FALSE(firstTab->IsZoomed()); @@ -758,7 +746,7 @@ namespace TerminalAppLocalTests page->_HandleTogglePaneZoom(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_TRUE(firstTab->IsZoomed()); }); @@ -772,7 +760,7 @@ namespace TerminalAppLocalTests page->_HandleMoveFocus(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_TRUE(firstTab->IsZoomed()); }); @@ -789,7 +777,7 @@ namespace TerminalAppLocalTests SplitPaneArgs args{ SplitType::Duplicate }; ActionEventArgs eventArgs{ args }; page->_HandleSplitPane(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_FALSE(firstTab->IsZoomed()); @@ -803,7 +791,7 @@ namespace TerminalAppLocalTests page->_HandleTogglePaneZoom(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_TRUE(firstTab->IsZoomed()); }); @@ -816,7 +804,7 @@ namespace TerminalAppLocalTests page->_HandleClosePane(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_IS_FALSE(firstTab->IsZoomed()); }); VERIFY_SUCCEEDED(result); @@ -827,7 +815,7 @@ namespace TerminalAppLocalTests Log::Comment(L"Check to ensure there's only one pane left."); result = RunOnUIThread([&page]() { - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(1, firstTab->GetLeafPaneCount()); VERIFY_IS_FALSE(firstTab->IsZoomed()); }); @@ -850,7 +838,7 @@ namespace TerminalAppLocalTests uint32_t firstId = 0, secondId = 0, thirdId = 0, fourthId = 0; TestOnUIThread([&]() { VERIFY_ARE_EQUAL(1u, page->_tabs.Size()); - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); firstId = tab->_activePane->Id().value(); // We start with 1 tab, split vertically to get // ------------------- @@ -876,7 +864,7 @@ namespace TerminalAppLocalTests // | | | // ------------------- page->_SplitPane(nullptr, SplitDirection::Down, 0.5f, page->_MakePane(nullptr, page->_GetFocusedTab(), nullptr)); - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); // Split again to make the 3rd tab thirdId = tab->_activePane->Id().value(); }); @@ -896,13 +884,13 @@ namespace TerminalAppLocalTests // | | | // ------------------- page->_SplitPane(nullptr, SplitDirection::Down, 0.5f, page->_MakePane(nullptr, page->_GetFocusedTab(), nullptr)); - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); fourthId = tab->_activePane->Id().value(); }); Sleep(250); TestOnUIThread([&]() { - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount()); // just to be complete, make sure we actually have 4 different ids VERIFY_ARE_NOT_EQUAL(firstId, fourthId); @@ -936,7 +924,7 @@ namespace TerminalAppLocalTests Sleep(250); TestOnUIThread([&]() { - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount()); // Our currently focused pane should be `4` VERIFY_ARE_EQUAL(fourthId, tab->_activePane->Id().value()); @@ -967,7 +955,7 @@ namespace TerminalAppLocalTests Sleep(250); TestOnUIThread([&]() { - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount()); // Our currently focused pane should be `4` VERIFY_ARE_EQUAL(fourthId, tab->_activePane->Id().value()); @@ -998,7 +986,7 @@ namespace TerminalAppLocalTests Sleep(250); TestOnUIThread([&]() { - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount()); // Our currently focused pane should be `4` VERIFY_ARE_EQUAL(fourthId, tab->_activePane->Id().value()); @@ -1029,7 +1017,7 @@ namespace TerminalAppLocalTests Sleep(250); TestOnUIThread([&]() { - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount()); // Our currently focused pane should be `4` VERIFY_ARE_EQUAL(fourthId, tab->_activePane->Id().value()); @@ -1174,16 +1162,16 @@ namespace TerminalAppLocalTests Log::Comment(L"give alphabetical names to all switch tab actions"); TestOnUIThread([&page]() { - page->_GetTerminalTabImpl(page->_tabs.GetAt(0))->Title(L"a"); + page->_GetTabImpl(page->_tabs.GetAt(0))->Title(L"a"); }); TestOnUIThread([&page]() { - page->_GetTerminalTabImpl(page->_tabs.GetAt(1))->Title(L"b"); + page->_GetTabImpl(page->_tabs.GetAt(1))->Title(L"b"); }); TestOnUIThread([&page]() { - page->_GetTerminalTabImpl(page->_tabs.GetAt(2))->Title(L"c"); + page->_GetTabImpl(page->_tabs.GetAt(2))->Title(L"c"); }); TestOnUIThread([&page]() { - page->_GetTerminalTabImpl(page->_tabs.GetAt(3))->Title(L"d"); + page->_GetTabImpl(page->_tabs.GetAt(3))->Title(L"d"); }); TestOnUIThread([&page]() { @@ -1306,6 +1294,15 @@ namespace TerminalAppLocalTests }); } + static til::color _getControlBackgroundColor(winrt::TerminalApp::implementation::ContentManager* contentManager, + const winrt::Microsoft::Terminal::Control::TermControl& c) + { + auto interactivity{ contentManager->TryLookupCore(c.ContentId()) }; + VERIFY_IS_NOT_NULL(interactivity); + const auto core{ interactivity.Core() }; + return til::color{ core.BackgroundColor() }; + } + void TabTests::TestPreviewCommitScheme() { Log::Comment(L"Preview a color scheme. Make sure it's applied, then committed accordingly"); @@ -1313,14 +1310,12 @@ namespace TerminalAppLocalTests auto page = _commonSetup(); VERIFY_IS_NOT_NULL(page); - TestOnUIThread([&page]() { + TestOnUIThread([&page, this]() { const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings(); - VERIFY_IS_NOT_NULL(controlSettings); - - VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, til::color{ controlSettings.DefaultBackground() }); + const auto backgroundColor{ _getControlBackgroundColor(_contentManager.get(), activeControl) }; + VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, backgroundColor); }); TestOnUIThread([&page]() { @@ -1330,15 +1325,13 @@ namespace TerminalAppLocalTests page->_PreviewAction(actionAndArgs); }); - TestOnUIThread([&page]() { + TestOnUIThread([&page, this]() { const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings(); - VERIFY_IS_NOT_NULL(controlSettings); - Log::Comment(L"Color should be changed to the preview"); - VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, til::color{ controlSettings.DefaultBackground() }); + const auto backgroundColor{ _getControlBackgroundColor(_contentManager.get(), activeControl) }; + VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, backgroundColor); // And we should have stored a function to revert the change. VERIFY_ARE_EQUAL(1u, page->_restorePreviewFuncs.size()); @@ -1352,15 +1345,13 @@ namespace TerminalAppLocalTests page->_HandleSetColorScheme(nullptr, ActionEventArgs{ args }); }); - TestOnUIThread([&page]() { + TestOnUIThread([&page, this]() { const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings(); - VERIFY_IS_NOT_NULL(controlSettings); - Log::Comment(L"Color should be changed"); - VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, til::color{ controlSettings.DefaultBackground() }); + const auto backgroundColor{ _getControlBackgroundColor(_contentManager.get(), activeControl) }; + VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, backgroundColor); // After preview there should be no more restore functions to execute. VERIFY_ARE_EQUAL(0u, page->_restorePreviewFuncs.size()); @@ -1381,14 +1372,12 @@ namespace TerminalAppLocalTests auto page = _commonSetup(); VERIFY_IS_NOT_NULL(page); - TestOnUIThread([&page]() { + TestOnUIThread([&page, this]() { const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings(); - VERIFY_IS_NOT_NULL(controlSettings); - - VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, til::color{ controlSettings.DefaultBackground() }); + const auto backgroundColor{ _getControlBackgroundColor(_contentManager.get(), activeControl) }; + VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, backgroundColor); }); TestOnUIThread([&page]() { @@ -1398,15 +1387,13 @@ namespace TerminalAppLocalTests page->_PreviewAction(actionAndArgs); }); - TestOnUIThread([&page]() { + TestOnUIThread([&page, this]() { const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings(); - VERIFY_IS_NOT_NULL(controlSettings); - Log::Comment(L"Color should be changed to the preview"); - VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, til::color{ controlSettings.DefaultBackground() }); + const auto backgroundColor{ _getControlBackgroundColor(_contentManager.get(), activeControl) }; + VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, backgroundColor); }); TestOnUIThread([&page]() { @@ -1414,15 +1401,13 @@ namespace TerminalAppLocalTests page->_EndPreview(); }); - TestOnUIThread([&page]() { + TestOnUIThread([&page, this]() { const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings(); - VERIFY_IS_NOT_NULL(controlSettings); - Log::Comment(L"Color should be the same as it originally was"); - VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, til::color{ controlSettings.DefaultBackground() }); + const auto backgroundColor{ _getControlBackgroundColor(_contentManager.get(), activeControl) }; + VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, backgroundColor); }); Log::Comment(L"Sleep to let events propagate"); Sleep(250); @@ -1437,14 +1422,12 @@ namespace TerminalAppLocalTests auto page = _commonSetup(); VERIFY_IS_NOT_NULL(page); - TestOnUIThread([&page]() { + TestOnUIThread([&page, this]() { const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings(); - VERIFY_IS_NOT_NULL(controlSettings); - - VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, til::color{ controlSettings.DefaultBackground() }); + const auto backgroundColor{ _getControlBackgroundColor(_contentManager.get(), activeControl) }; + VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, backgroundColor); }); TestOnUIThread([&page]() { @@ -1453,15 +1436,13 @@ namespace TerminalAppLocalTests page->_PreviewColorScheme(args); }); - TestOnUIThread([&page]() { + TestOnUIThread([&page, this]() { const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings(); - VERIFY_IS_NOT_NULL(controlSettings); - Log::Comment(L"Color should be changed to the preview"); - VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, til::color{ controlSettings.DefaultBackground() }); + const auto backgroundColor{ _getControlBackgroundColor(_contentManager.get(), activeControl) }; + VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, backgroundColor); }); TestOnUIThread([&page]() { @@ -1470,15 +1451,13 @@ namespace TerminalAppLocalTests page->_PreviewColorScheme(args); }); - TestOnUIThread([&page]() { + TestOnUIThread([&page, this]() { const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings(); - VERIFY_IS_NOT_NULL(controlSettings); - Log::Comment(L"Color should be changed to the preview"); - VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, til::color{ controlSettings.DefaultBackground() }); + const auto backgroundColor{ _getControlBackgroundColor(_contentManager.get(), activeControl) }; + VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, backgroundColor); }); TestOnUIThread([&page]() { @@ -1489,15 +1468,13 @@ namespace TerminalAppLocalTests page->_HandleSetColorScheme(nullptr, ActionEventArgs{ args }); }); - TestOnUIThread([&page]() { + TestOnUIThread([&page, this]() { const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings(); - VERIFY_IS_NOT_NULL(controlSettings); - Log::Comment(L"Color should be changed"); - VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, til::color{ controlSettings.DefaultBackground() }); + const auto backgroundColor{ _getControlBackgroundColor(_contentManager.get(), activeControl) }; + VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, backgroundColor); }); Log::Comment(L"Sleep to let events propagate"); Sleep(250); diff --git a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj index 91134e3f08..3ad9716b5d 100644 --- a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj +++ b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj @@ -24,6 +24,7 @@ true + true @@ -59,6 +60,9 @@ + + {3c46e2b0-ae6c-4132-9122-6772fb411959} + diff --git a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj index 875fd0a45a..d1bf294dc3 100644 --- a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj +++ b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj @@ -22,6 +22,7 @@ false true + true false + Windows Terminal Open Here Shell Extension true diff --git a/src/cascadia/TerminalApp/AboutDialog.xaml b/src/cascadia/TerminalApp/AboutDialog.xaml index a4257ec904..ff075efb9a 100644 --- a/src/cascadia/TerminalApp/AboutDialog.xaml +++ b/src/cascadia/TerminalApp/AboutDialog.xaml @@ -8,6 +8,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="using:TerminalApp" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:mtu="using:Microsoft.Terminal.UI" xmlns:mux="using:Microsoft.UI.Xaml.Controls" x:Uid="AboutDialog" DefaultButton="Close" @@ -17,6 +18,9 @@ + + + @@ -24,22 +28,22 @@ - - + - - + + + + + + diff --git a/src/cascadia/TerminalApp/IPaletteItem.idl b/src/cascadia/TerminalApp/IPaletteItem.idl new file mode 100644 index 0000000000..7cb73db0b8 --- /dev/null +++ b/src/cascadia/TerminalApp/IPaletteItem.idl @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "TerminalTabStatus.idl"; + +namespace TerminalApp +{ + enum PaletteItemType + { + Action, + CommandLine, + Tab, + }; + + interface IPaletteItem requires Windows.UI.Xaml.Data.INotifyPropertyChanged + { + PaletteItemType Type { get; }; + String Name { get; }; + String Subtitle { get; }; + String KeyChordText { get; }; + String Icon { get; }; + Windows.UI.Xaml.Controls.IconElement ResolvedIcon { get; }; + } + + runtimeclass TabPaletteItem : [default] IPaletteItem, Windows.UI.Xaml.Data.INotifyPropertyChanged + { + TerminalTabStatus TabStatus{ get; }; + } +} diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl index f4c6ce1395..ea2e900ca6 100644 --- a/src/cascadia/TerminalApp/IPaneContent.idl +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -8,7 +8,8 @@ namespace TerminalApp None, Content, MovePane, - Persist, + PersistLayout, + PersistAll }; runtimeclass BellEventArgs diff --git a/src/cascadia/TerminalApp/Jumplist.cpp b/src/cascadia/TerminalApp/Jumplist.cpp index d558c5433f..e713514973 100644 --- a/src/cascadia/TerminalApp/Jumplist.cpp +++ b/src/cascadia/TerminalApp/Jumplist.cpp @@ -19,41 +19,6 @@ DEFINE_PROPERTYKEY(PKEY_AppUserModel_DestListLogoUri, 0x9F4C2855, 0x9F79, 0x4B39 { 0x9F4C2855, 0x9F79, 0x4B39, 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 }, 29 \ } -// Function Description: -// - This function guesses whether a string is a file path. -static constexpr bool _isProbableFilePath(std::wstring_view path) -{ - // "C:X", "C:\X", "\\?", "\\." - // _this function rejects \??\ as a path_ - if (path.size() >= 3) - { - const auto firstColon{ path.find(L':') }; - if (firstColon == 1) - { - return true; - } - - const auto prefix{ path.substr(0, 2) }; - return prefix == LR"(//)" || prefix == LR"(\\)"; - } - return false; -} - -// Function Description: -// - DestListLogoUri cannot take paths that are separated by / unless they're URLs. -// This function uses std::filesystem to normalize strings that appear to be file -// paths to have the "correct" slash direction. -static std::wstring _normalizeIconPath(std::wstring_view path) -{ - const auto fullPath{ wil::ExpandEnvironmentStringsW(path.data()) }; - if (_isProbableFilePath(fullPath)) - { - std::filesystem::path asPath{ fullPath }; - return asPath.make_preferred().wstring(); - } - return std::wstring{ fullPath }; -} - // Method Description: // - Updates the items of the Jumplist based on the given settings. // Arguments: @@ -124,7 +89,7 @@ void Jumplist::_updateProfiles(IObjectCollection* jumplistItems, winrt::Windows: auto args = fmt::format(FMT_COMPILE(L"-p {}"), to_hstring(profile.Guid())); // Create the shell link object for the profile - const auto normalizedIconPath{ _normalizeIconPath(profile.Icon()) }; + const auto normalizedIconPath{ profile.Icon().Resolved() }; const auto shLink = _createShellLink(profile.Name(), normalizedIconPath, args); THROW_IF_FAILED(jumplistItems->AddObject(shLink.get())); } diff --git a/src/cascadia/TerminalApp/MarkdownPaneContent.cpp b/src/cascadia/TerminalApp/MarkdownPaneContent.cpp index f287b2c1c7..200fc4f4a9 100644 --- a/src/cascadia/TerminalApp/MarkdownPaneContent.cpp +++ b/src/cascadia/TerminalApp/MarkdownPaneContent.cpp @@ -78,6 +78,7 @@ namespace winrt::TerminalApp::implementation void MarkdownPaneContent::_loadText() { auto block = WUX::Controls::TextBlock(); + block.ContextFlyout(winrt::Microsoft::Terminal::UI::TextMenuFlyout{}); block.IsTextSelectionEnabled(true); block.FontFamily(WUX::Media::FontFamily{ L"Cascadia Code" }); block.Text(FileContents()); diff --git a/src/cascadia/TerminalApp/MarkdownPaneContent.xaml b/src/cascadia/TerminalApp/MarkdownPaneContent.xaml index 54f732ce51..f67d2d478c 100644 --- a/src/cascadia/TerminalApp/MarkdownPaneContent.xaml +++ b/src/cascadia/TerminalApp/MarkdownPaneContent.xaml @@ -8,6 +8,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="using:TerminalApp" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:mtu="using:Microsoft.Terminal.UI" xmlns:mux="using:Microsoft.UI.Xaml.Controls" mc:Ignorable="d"> @@ -51,7 +52,11 @@ Grid.Column="0" Margin="4" PlaceholderText="Enter a path to a markdown file..." - Text="Z:\dev\simple-test.md" /> + Text="Z:\dev\simple-test.md"> + + + + - @@ -332,6 +333,7 @@ Style="{StaticResource SettingsStackStyle}"> + + + + + + + diff --git a/src/cascadia/TerminalSettingsEditor/EditColorScheme.cpp b/src/cascadia/TerminalSettingsEditor/EditColorScheme.cpp index ce24a89e48..1c1d5f0c84 100644 --- a/src/cascadia/TerminalSettingsEditor/EditColorScheme.cpp +++ b/src/cascadia/TerminalSettingsEditor/EditColorScheme.cpp @@ -42,7 +42,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { _ViewModel = e.Parameter().as(); - NameBox().Text(_ViewModel.Name()); + const auto schemeName = _ViewModel.Name(); + NameBox().Text(schemeName); + + TraceLoggingWrite( + g_hTerminalSettingsEditorProvider, + "NavigatedToPage", + TraceLoggingDescription("Event emitted when the user navigates to a page in the settings UI"), + TraceLoggingValue("colorSchemes.editColorScheme", "PageId", "The identifier of the page that was navigated to"), + TraceLoggingValue(schemeName.data(), "SchemeName", "The name of the color scheme that's being edited"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } void EditColorScheme::ColorPickerChanged(const IInspectable& sender, diff --git a/src/cascadia/TerminalSettingsEditor/EditColorScheme.xaml b/src/cascadia/TerminalSettingsEditor/EditColorScheme.xaml index 2c2ed3de5d..c30287ebaf 100644 --- a/src/cascadia/TerminalSettingsEditor/EditColorScheme.xaml +++ b/src/cascadia/TerminalSettingsEditor/EditColorScheme.xaml @@ -84,16 +84,18 @@ - + - + BorderBrush="{ThemeResource ControlStrongStrokeColorDefaultBrush}" + BorderThickness="1" + CornerRadius="{StaticResource ControlCornerRadius}"> @@ -468,8 +470,7 @@ - diff --git a/src/cascadia/TerminalSettingsEditor/Extensions.cpp b/src/cascadia/TerminalSettingsEditor/Extensions.cpp new file mode 100644 index 0000000000..ecf2cc26ad --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Extensions.cpp @@ -0,0 +1,543 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "Extensions.h" +#include "Extensions.g.cpp" +#include "ExtensionPackageViewModel.g.cpp" +#include "ExtensionsViewModel.g.cpp" +#include "FragmentProfileViewModel.g.cpp" +#include "ExtensionPackageTemplateSelector.g.cpp" + +#include +#include "..\WinRTUtils\inc\Utils.h" + +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Foundation::Collections; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Xaml::Controls; +using namespace winrt::Windows::UI::Xaml::Navigation; + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ + Extensions::Extensions() + { + InitializeComponent(); + + _extensionPackageIdentifierTemplateSelector = Resources().Lookup(box_value(L"ExtensionPackageIdentifierTemplateSelector")).as(); + + Automation::AutomationProperties::SetName(ActiveExtensionsList(), RS_(L"Extensions_ActiveExtensionsHeader/Text")); + Automation::AutomationProperties::SetName(ModifiedProfilesList(), RS_(L"Extensions_ModifiedProfilesHeader/Text")); + Automation::AutomationProperties::SetName(AddedProfilesList(), RS_(L"Extensions_AddedProfilesHeader/Text")); + Automation::AutomationProperties::SetName(AddedColorSchemesList(), RS_(L"Extensions_AddedColorSchemesHeader/Text")); + } + + void Extensions::OnNavigatedTo(const NavigationEventArgs& e) + { + _ViewModel = e.Parameter().as(); + auto vmImpl = get_self(_ViewModel); + vmImpl->ExtensionPackageIdentifierTemplateSelector(_extensionPackageIdentifierTemplateSelector); + vmImpl->LazyLoadExtensions(); + vmImpl->MarkAsVisited(); + + if (vmImpl->IsExtensionView()) + { + const auto currentPkgVM = vmImpl->CurrentExtensionPackage(); + const auto currentPkg = currentPkgVM.Package(); + TraceLoggingWrite( + g_hTerminalSettingsEditorProvider, + "NavigatedToPage", + TraceLoggingDescription("Event emitted when the user navigates to a page in the settings UI"), + TraceLoggingValue("extensions.extensionView", "PageId", "The identifier of the page that was navigated to"), + TraceLoggingValue(currentPkg.Source().c_str(), "FragmentSource", "The source of the fragment included in this extension package"), + TraceLoggingValue(currentPkgVM.FragmentExtensions().Size(), "FragmentCount", "The number of fragments included in this extension package"), + TraceLoggingValue(currentPkgVM.Enabled(), "Enabled", "The enabled status of the extension"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); + } + else + { + TraceLoggingWrite( + g_hTerminalSettingsEditorProvider, + "NavigatedToPage", + TraceLoggingDescription("Event emitted when the user navigates to a page in the settings UI"), + TraceLoggingValue("extensions", "PageId", "The identifier of the page that was navigated to"), + TraceLoggingValue(vmImpl->ExtensionPackages().Size(), "ExtensionPackageCount", "The number of extension packages displayed"), + TraceLoggingValue(vmImpl->ProfilesModified().Size(), "ProfilesModifiedCount", "The number of profiles modified by enabled extensions"), + TraceLoggingValue(vmImpl->ProfilesAdded().Size(), "ProfilesAddedCount", "The number of profiles added by enabled extensions"), + TraceLoggingValue(vmImpl->ColorSchemesAdded().Size(), "ColorSchemesAddedCount", "The number of color schemes added by enabled extensions"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); + } + } + + void Extensions::ExtensionNavigator_Click(const IInspectable& sender, const RoutedEventArgs& /*args*/) + { + const auto extPkgVM = sender.as().Tag().as(); + _ViewModel.CurrentExtensionPackage(extPkgVM); + } + + void Extensions::NavigateToProfile_Click(const IInspectable& sender, const RoutedEventArgs& /*args*/) + { + const auto& profileGuid = sender.as().Tag().as(); + get_self(_ViewModel)->NavigateToProfile(profileGuid); + } + + void Extensions::NavigateToColorScheme_Click(const IInspectable& sender, const RoutedEventArgs& /*args*/) + { + const auto& schemeVM = sender.as().Tag().as(); + get_self(_ViewModel)->NavigateToColorScheme(schemeVM); + } + + ExtensionsViewModel::ExtensionsViewModel(const Model::CascadiaSettings& settings, const Editor::ColorSchemesPageViewModel& colorSchemesPageVM) : + _settings{ settings }, + _colorSchemesPageVM{ colorSchemesPageVM }, + _extensionsLoaded{ false } + { + UpdateSettings(settings, colorSchemesPageVM); + + PropertyChanged([this](auto&&, const PropertyChangedEventArgs& args) { + const auto viewModelProperty{ args.PropertyName() }; + + const bool extensionPackageChanged = viewModelProperty == L"CurrentExtensionPackage"; + const bool profilesModifiedChanged = viewModelProperty == L"ProfilesModified"; + const bool profilesAddedChanged = viewModelProperty == L"ProfilesAdded"; + const bool colorSchemesAddedChanged = viewModelProperty == L"ColorSchemesAdded"; + if (extensionPackageChanged || (!IsExtensionView() && (profilesModifiedChanged || profilesAddedChanged || colorSchemesAddedChanged))) + { + // Use these booleans to track which of our observable vectors need to be refreshed. + // This prevents a full refresh of the UI when enabling/disabling extensions. + // If the CurrentExtensionPackage changed, we want to update all components. + // Otherwise, just update the ones that we were notified about. + const bool updateProfilesModified = extensionPackageChanged || profilesModifiedChanged; + const bool updateProfilesAdded = extensionPackageChanged || profilesAddedChanged; + const bool updateColorSchemesAdded = extensionPackageChanged || colorSchemesAddedChanged; + _UpdateListViews(updateProfilesModified, updateProfilesAdded, updateColorSchemesAdded); + + if (extensionPackageChanged) + { + _NotifyChanges(L"IsExtensionView", L"CurrentExtensionPackageIdentifierTemplate"); + } + else if (profilesModifiedChanged) + { + _NotifyChanges(L"NoProfilesModified"); + } + else if (profilesAddedChanged) + { + _NotifyChanges(L"NoProfilesAdded"); + } + else if (colorSchemesAddedChanged) + { + _NotifyChanges(L"NoSchemesAdded"); + } + } + }); + } + + void ExtensionsViewModel::_UpdateListViews(bool updateProfilesModified, bool updateProfilesAdded, bool updateColorSchemesAdded) + { + // STL vectors to track relevant components for extensions to display in UI + std::vector profilesModifiedTotal; + std::vector profilesAddedTotal; + std::vector colorSchemesAddedTotal; + + // Helper lambda to add the contents of an extension package to the current view. + auto addPackageContentsToView = [&](const Editor::ExtensionPackageViewModel& extPkg) { + auto extPkgVM = get_self(extPkg); + for (const auto& ext : extPkgVM->FragmentExtensions()) + { + if (updateProfilesModified) + { + for (const auto& profile : ext.ProfilesModified()) + { + profilesModifiedTotal.push_back(profile); + } + } + if (updateProfilesAdded) + { + for (const auto& profile : ext.ProfilesAdded()) + { + profilesAddedTotal.push_back(profile); + } + } + if (updateColorSchemesAdded) + { + for (const auto& scheme : ext.ColorSchemesAdded()) + { + colorSchemesAddedTotal.push_back(scheme); + } + } + } + }; + + // Populate the STL vectors that we want to update + if (const auto currentExtensionPackage = CurrentExtensionPackage()) + { + // Update all of the views to reflect the current extension package, if one is selected. + addPackageContentsToView(currentExtensionPackage); + } + else + { + // Only populate the views with components from enabled extensions + for (const auto& extPkg : _extensionPackages) + { + if (extPkg.Enabled()) + { + addPackageContentsToView(extPkg); + } + } + } + + // Sort the lists linguistically for nicer presentation. + // Update the WinRT lists bound to UI. + if (updateProfilesModified) + { + std::sort(profilesModifiedTotal.begin(), profilesModifiedTotal.end(), FragmentProfileViewModel::SortAscending); + _profilesModifiedView = winrt::single_threaded_observable_vector(std::move(profilesModifiedTotal)); + } + if (updateProfilesAdded) + { + std::sort(profilesAddedTotal.begin(), profilesAddedTotal.end(), FragmentProfileViewModel::SortAscending); + _profilesAddedView = winrt::single_threaded_observable_vector(std::move(profilesAddedTotal)); + } + if (updateColorSchemesAdded) + { + std::sort(colorSchemesAddedTotal.begin(), colorSchemesAddedTotal.end(), FragmentColorSchemeViewModel::SortAscending); + _colorSchemesAddedView = winrt::single_threaded_observable_vector(std::move(colorSchemesAddedTotal)); + } + } + + void ExtensionsViewModel::UpdateSettings(const Model::CascadiaSettings& settings, const Editor::ColorSchemesPageViewModel& colorSchemesPageVM) + { + _settings = settings; + _colorSchemesPageVM = colorSchemesPageVM; + _CurrentExtensionPackage = nullptr; + + // The extension packages may not be loaded yet because we want to wait until we actually navigate to the page to do so. + // In that case, omit "updating" them. They'll get the proper references when we lazy load them. + if (_extensionPackages) + { + for (const auto& extPkg : _extensionPackages) + { + get_self(extPkg)->UpdateSettings(_settings); + } + } + } + + void ExtensionsViewModel::LazyLoadExtensions() + { + if (_extensionsLoaded) + { + return; + } + std::vector extensions = wil::to_vector(_settings.Extensions()); + + // these vectors track components all extensions successfully added + std::vector extensionPackages; + std::vector profilesModifiedTotal; + std::vector profilesAddedTotal; + std::vector colorSchemesAddedTotal; + for (const auto& extPkg : extensions) + { + auto extPkgVM = winrt::make_self(extPkg, _settings); + for (const auto& fragExt : extPkg.FragmentsView()) + { + const auto extensionEnabled = GetExtensionState(fragExt.Source(), _settings); + + // these vectors track everything the current extension attempted to bring in + std::vector currentProfilesModified; + std::vector currentProfilesAdded; + std::vector currentColorSchemesAdded; + + if (fragExt.ModifiedProfilesView()) + { + for (const auto&& entry : fragExt.ModifiedProfilesView()) + { + // Ensure entry successfully modifies a profile before creating and registering the object + if (const auto& deducedProfile = _settings.FindProfile(entry.ProfileGuid())) + { + auto vm = winrt::make(entry, fragExt, deducedProfile); + currentProfilesModified.push_back(vm); + if (extensionEnabled) + { + profilesModifiedTotal.push_back(vm); + } + } + } + } + + if (fragExt.NewProfilesView()) + { + for (const auto&& entry : fragExt.NewProfilesView()) + { + // Ensure entry successfully points to a profile before creating and registering the object. + // The profile may have been removed by the user. + if (const auto& deducedProfile = _settings.FindProfile(entry.ProfileGuid())) + { + auto vm = winrt::make(entry, fragExt, deducedProfile); + currentProfilesAdded.push_back(vm); + if (extensionEnabled) + { + profilesAddedTotal.push_back(vm); + } + } + } + } + + if (fragExt.ColorSchemesView()) + { + for (const auto&& entry : fragExt.ColorSchemesView()) + { + for (const auto& schemeVM : _colorSchemesPageVM.AllColorSchemes()) + { + if (schemeVM.Name() == entry.ColorSchemeName()) + { + auto vm = winrt::make(entry, fragExt, schemeVM); + currentColorSchemesAdded.push_back(vm); + if (extensionEnabled) + { + colorSchemesAddedTotal.push_back(vm); + } + } + } + } + } + + // sort the lists linguistically for nicer presentation + std::sort(currentProfilesModified.begin(), currentProfilesModified.end(), FragmentProfileViewModel::SortAscending); + std::sort(currentProfilesAdded.begin(), currentProfilesAdded.end(), FragmentProfileViewModel::SortAscending); + std::sort(currentColorSchemesAdded.begin(), currentColorSchemesAdded.end(), FragmentColorSchemeViewModel::SortAscending); + + extPkgVM->FragmentExtensions().Append(winrt::make(fragExt, currentProfilesModified, currentProfilesAdded, currentColorSchemesAdded)); + extPkgVM->PropertyChanged([&](const IInspectable& sender, const PropertyChangedEventArgs& args) { + const auto viewModelProperty{ args.PropertyName() }; + if (viewModelProperty == L"Enabled") + { + // If the extension was enabled/disabled, + // check if any of its fragments modified profiles, added profiles, or added color schemes. + // Only notify what was affected! + bool hasModifiedProfiles = false; + bool hasAddedProfiles = false; + bool hasAddedColorSchemes = false; + for (const auto& fragExtVM : sender.as()->FragmentExtensions()) + { + const auto profilesModified = fragExtVM.ProfilesModified(); + const auto profilesAdded = fragExtVM.ProfilesAdded(); + const auto colorSchemesAdded = fragExtVM.ColorSchemesAdded(); + hasModifiedProfiles |= profilesModified && profilesModified.Size() > 0; + hasAddedProfiles |= profilesAdded && profilesAdded.Size() > 0; + hasAddedColorSchemes |= colorSchemesAdded && colorSchemesAdded.Size() > 0; + } + if (hasModifiedProfiles) + { + _NotifyChanges(L"ProfilesModified"); + } + if (hasAddedProfiles) + { + _NotifyChanges(L"ProfilesAdded"); + } + if (hasAddedColorSchemes) + { + _NotifyChanges(L"ColorSchemesAdded"); + } + } + }); + } + extensionPackages.push_back(*extPkgVM); + } + + // sort the lists linguistically for nicer presentation + std::sort(extensionPackages.begin(), extensionPackages.end(), ExtensionPackageViewModel::SortAscending); + std::sort(profilesModifiedTotal.begin(), profilesModifiedTotal.end(), FragmentProfileViewModel::SortAscending); + std::sort(profilesAddedTotal.begin(), profilesAddedTotal.end(), FragmentProfileViewModel::SortAscending); + std::sort(colorSchemesAddedTotal.begin(), colorSchemesAddedTotal.end(), FragmentColorSchemeViewModel::SortAscending); + + _extensionPackages = single_threaded_observable_vector(std::move(extensionPackages)); + _profilesModifiedView = single_threaded_observable_vector(std::move(profilesModifiedTotal)); + _profilesAddedView = single_threaded_observable_vector(std::move(profilesAddedTotal)); + _colorSchemesAddedView = single_threaded_observable_vector(std::move(colorSchemesAddedTotal)); + _extensionsLoaded = true; + } + + Windows::UI::Xaml::DataTemplate ExtensionsViewModel::CurrentExtensionPackageIdentifierTemplate() const + { + return _ExtensionPackageIdentifierTemplateSelector.SelectTemplate(CurrentExtensionPackage()); + } + + bool ExtensionsViewModel::DisplayBadge() const noexcept + { + return !Model::ApplicationState::SharedInstance().BadgeDismissed(L"page.extensions"); + } + + // Returns true if the extension is enabled, false otherwise + bool ExtensionsViewModel::GetExtensionState(hstring extensionSource, const Model::CascadiaSettings& settings) + { + if (const auto& disabledExtensions = settings.GlobalSettings().DisabledProfileSources()) + { + uint32_t ignored; + return !disabledExtensions.IndexOf(extensionSource, ignored); + } + // "disabledProfileSources" not defined --> all extensions are enabled + return true; + } + + // Enable/Disable an extension + void ExtensionsViewModel::SetExtensionState(hstring extensionSource, const Model::CascadiaSettings& settings, bool enableExt) + { + // get the current status of the extension + uint32_t idx; + bool currentlyEnabled = true; + const auto& disabledExtensions = settings.GlobalSettings().DisabledProfileSources(); + if (disabledExtensions) + { + currentlyEnabled = !disabledExtensions.IndexOf(extensionSource, idx); + } + + // current status mismatches the desired status, + // update the list of disabled extensions + if (currentlyEnabled != enableExt) + { + // If we're disabling an extension and we don't have "disabledProfileSources" defined, + // create it in the model directly + if (!disabledExtensions && !enableExt) + { + std::vector disabledProfileSources{ extensionSource }; + settings.GlobalSettings().DisabledProfileSources(single_threaded_vector(std::move(disabledProfileSources))); + return; + } + + // Update the list of disabled extensions + if (enableExt) + { + disabledExtensions.RemoveAt(idx); + } + else + { + disabledExtensions.Append(extensionSource); + } + } + } + + Thickness Extensions::CalculateMargin(bool hidden) + { + return ThicknessHelper::FromLengths(/*left*/ 0, + /*top*/ hidden ? 0 : 20, + /*right*/ 0, + /*bottom*/ 0); + } + + void ExtensionsViewModel::NavigateToProfile(const guid profileGuid) + { + NavigateToProfileRequested.raise(*this, profileGuid); + } + + void ExtensionsViewModel::NavigateToColorScheme(const Editor::ColorSchemeViewModel& schemeVM) + { + _colorSchemesPageVM.CurrentScheme(schemeVM); + NavigateToColorSchemeRequested.raise(*this, nullptr); + } + + void ExtensionsViewModel::MarkAsVisited() + { + Model::ApplicationState::SharedInstance().DismissBadge(L"page.extensions"); + _NotifyChanges(L"DisplayBadge"); + } + + bool ExtensionPackageViewModel::SortAscending(const Editor::ExtensionPackageViewModel& lhs, const Editor::ExtensionPackageViewModel& rhs) + { + auto getKey = [&](const Editor::ExtensionPackageViewModel& pkgVM) { + const auto pkg = pkgVM.Package(); + const auto displayName = pkg.DisplayName(); + return displayName.empty() ? pkg.Source() : displayName; + }; + + return til::compare_linguistic_insensitive(getKey(lhs), getKey(rhs)) < 0; + } + + void ExtensionPackageViewModel::UpdateSettings(const Model::CascadiaSettings& settings) + { + const auto oldEnabled = Enabled(); + _settings = settings; + if (oldEnabled != Enabled()) + { + // The enabled state of the extension has changed, notify the UI + _NotifyChanges(L"Enabled"); + } + } + + hstring ExtensionPackageViewModel::Scope() const noexcept + { + return _package.Scope() == Model::FragmentScope::User ? RS_(L"Extensions_ScopeUser") : RS_(L"Extensions_ScopeSystem"); + } + + bool ExtensionPackageViewModel::Enabled() const + { + return ExtensionsViewModel::GetExtensionState(_package.Source(), _settings); + } + + void ExtensionPackageViewModel::Enabled(bool val) + { + if (Enabled() != val) + { + ExtensionsViewModel::SetExtensionState(_package.Source(), _settings, val); + _NotifyChanges(L"Enabled"); + } + } + + // Returns the accessible name for the extension package in the following format: + // ", " + hstring ExtensionPackageViewModel::AccessibleName() const noexcept + { + hstring name; + const auto source = _package.Source(); + if (const auto displayName = _package.DisplayName(); !displayName.empty()) + { + return hstring{ fmt::format(FMT_COMPILE(L"{}, {}"), displayName, source) }; + } + return source; + } + + bool FragmentProfileViewModel::SortAscending(const Editor::FragmentProfileViewModel& lhs, const Editor::FragmentProfileViewModel& rhs) + { + return til::compare_linguistic_insensitive(lhs.Profile().Name(), rhs.Profile().Name()) < 0; + } + + hstring FragmentProfileViewModel::AccessibleName() const noexcept + { + return hstring{ fmt::format(FMT_COMPILE(L"{}, {}"), Profile().Name(), SourceName()) }; + } + + bool FragmentColorSchemeViewModel::SortAscending(const Editor::FragmentColorSchemeViewModel& lhs, const Editor::FragmentColorSchemeViewModel& rhs) + { + return til::compare_linguistic_insensitive(lhs.ColorSchemeVM().Name(), rhs.ColorSchemeVM().Name()) < 0; + } + + hstring FragmentColorSchemeViewModel::AccessibleName() const noexcept + { + return hstring{ fmt::format(FMT_COMPILE(L"{}, {}"), ColorSchemeVM().Name(), SourceName()) }; + } + + DataTemplate ExtensionPackageTemplateSelector::SelectTemplateCore(const IInspectable& item, const DependencyObject& /*container*/) + { + return SelectTemplateCore(item); + } + + DataTemplate ExtensionPackageTemplateSelector::SelectTemplateCore(const IInspectable& item) + { + if (const auto extPkgVM = item.try_as()) + { + if (!extPkgVM.Package().DisplayName().empty()) + { + // Check if the first char of the icon is in the Segoe MDL2 Icons list + const auto ch = til::at(extPkgVM.Package().Icon(), 0); + if (ch >= L'\uE700' && ch <= L'\uF8FF') + { + return ComplexTemplateWithFontIcon(); + } + return ComplexTemplate(); + } + return DefaultTemplate(); + } + return nullptr; + } +} diff --git a/src/cascadia/TerminalSettingsEditor/Extensions.h b/src/cascadia/TerminalSettingsEditor/Extensions.h new file mode 100644 index 0000000000..a9379829d2 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Extensions.h @@ -0,0 +1,194 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "Extensions.g.h" +#include "ExtensionsViewModel.g.h" +#include "ExtensionPackageViewModel.g.h" +#include "FragmentExtensionViewModel.g.h" +#include "FragmentProfileViewModel.g.h" +#include "FragmentColorSchemeViewModel.g.h" +#include "ExtensionPackageTemplateSelector.g.h" +#include "ViewModelHelpers.h" +#include "Utils.h" + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ + struct Extensions : public HasScrollViewer, ExtensionsT + { + public: + Windows::UI::Xaml::Thickness CalculateMargin(bool hidden); + + Extensions(); + + void OnNavigatedTo(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); + + void ExtensionNavigator_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args); + void NavigateToProfile_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args); + void NavigateToColorScheme_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args); + + WINRT_PROPERTY(Editor::ExtensionsViewModel, ViewModel, nullptr); + + private: + Editor::ExtensionPackageTemplateSelector _extensionPackageIdentifierTemplateSelector; + }; + + struct ExtensionsViewModel : ExtensionsViewModelT, ViewModelHelper + { + public: + ExtensionsViewModel(const Model::CascadiaSettings& settings, const Editor::ColorSchemesPageViewModel& colorSchemesPageVM); + + // Properties + Windows::UI::Xaml::DataTemplate CurrentExtensionPackageIdentifierTemplate() const; + bool IsExtensionView() const noexcept { return _CurrentExtensionPackage != nullptr; } + bool NoExtensionPackages() const noexcept { return _extensionPackages.Size() == 0; } + bool NoProfilesModified() const noexcept { return _profilesModifiedView.Size() == 0; } + bool NoProfilesAdded() const noexcept { return _profilesAddedView.Size() == 0; } + bool NoSchemesAdded() const noexcept { return _colorSchemesAddedView.Size() == 0; } + bool DisplayBadge() const noexcept; + + // Views + Windows::Foundation::Collections::IObservableVector ExtensionPackages() const noexcept { return _extensionPackages; } + Windows::Foundation::Collections::IObservableVector ProfilesModified() const noexcept { return _profilesModifiedView; } + Windows::Foundation::Collections::IObservableVector ProfilesAdded() const noexcept { return _profilesAddedView; } + Windows::Foundation::Collections::IObservableVector ColorSchemesAdded() const noexcept { return _colorSchemesAddedView; } + + // Methods + void LazyLoadExtensions(); + void UpdateSettings(const Model::CascadiaSettings& settings, const Editor::ColorSchemesPageViewModel& colorSchemesPageVM); + void NavigateToProfile(const guid profileGuid); + void NavigateToColorScheme(const Editor::ColorSchemeViewModel& schemeVM); + void MarkAsVisited(); + + static bool GetExtensionState(hstring extensionSource, const Model::CascadiaSettings& settings); + static void SetExtensionState(hstring extensionSource, const Model::CascadiaSettings& settings, bool enableExt); + + til::typed_event NavigateToProfileRequested; + til::typed_event NavigateToColorSchemeRequested; + + VIEW_MODEL_OBSERVABLE_PROPERTY(Editor::ExtensionPackageViewModel, CurrentExtensionPackage, nullptr); + WINRT_PROPERTY(Editor::ExtensionPackageTemplateSelector, ExtensionPackageIdentifierTemplateSelector, nullptr); + + private: + Model::CascadiaSettings _settings; + Editor::ColorSchemesPageViewModel _colorSchemesPageVM; + Windows::Foundation::Collections::IObservableVector _extensionPackages; + Windows::Foundation::Collections::IObservableVector _profilesModifiedView; + Windows::Foundation::Collections::IObservableVector _profilesAddedView; + Windows::Foundation::Collections::IObservableVector _colorSchemesAddedView; + bool _extensionsLoaded; + + void _UpdateListViews(bool updateProfilesModified, bool updateProfilesAdded, bool updateColorSchemesAdded); + }; + + struct ExtensionPackageViewModel : ExtensionPackageViewModelT, ViewModelHelper + { + public: + ExtensionPackageViewModel(const Model::ExtensionPackage& pkg, const Model::CascadiaSettings& settings) : + _package{ pkg }, + _settings{ settings }, + _fragmentExtensions{ single_threaded_observable_vector() } {} + + static bool SortAscending(const Editor::ExtensionPackageViewModel& lhs, const Editor::ExtensionPackageViewModel& rhs); + + void UpdateSettings(const Model::CascadiaSettings& settings); + + Model::ExtensionPackage Package() const noexcept { return _package; } + hstring Scope() const noexcept; + bool Enabled() const; + void Enabled(bool val); + hstring AccessibleName() const noexcept; + Windows::Foundation::Collections::IObservableVector FragmentExtensions() { return _fragmentExtensions; } + + private: + Model::ExtensionPackage _package; + Model::CascadiaSettings _settings; + Windows::Foundation::Collections::IObservableVector _fragmentExtensions; + }; + + struct FragmentExtensionViewModel : FragmentExtensionViewModelT, ViewModelHelper + { + public: + FragmentExtensionViewModel(const Model::FragmentSettings& fragment, + std::vector& profilesModified, + std::vector& profilesAdded, + std::vector& colorSchemesAdded) : + _fragment{ fragment }, + _profilesModified{ single_threaded_vector(std::move(profilesModified)) }, + _profilesAdded{ single_threaded_vector(std::move(profilesAdded)) }, + _colorSchemesAdded{ single_threaded_vector(std::move(colorSchemesAdded)) } {} + + Model::FragmentSettings Fragment() const noexcept { return _fragment; } + Windows::Foundation::Collections::IVectorView ProfilesModified() const noexcept { return _profilesModified.GetView(); } + Windows::Foundation::Collections::IVectorView ProfilesAdded() const noexcept { return _profilesAdded.GetView(); } + Windows::Foundation::Collections::IVectorView ColorSchemesAdded() const noexcept { return _colorSchemesAdded.GetView(); } + + private: + Model::FragmentSettings _fragment; + Windows::Foundation::Collections::IVector _profilesModified; + Windows::Foundation::Collections::IVector _profilesAdded; + Windows::Foundation::Collections::IVector _colorSchemesAdded; + }; + + struct FragmentProfileViewModel : FragmentProfileViewModelT, ViewModelHelper + { + public: + FragmentProfileViewModel(const Model::FragmentProfileEntry& entry, const Model::FragmentSettings& fragment, const Model::Profile& deducedProfile) : + _entry{ entry }, + _fragment{ fragment }, + _deducedProfile{ deducedProfile } {} + + static bool SortAscending(const Editor::FragmentProfileViewModel& lhs, const Editor::FragmentProfileViewModel& rhs); + + Model::Profile Profile() const { return _deducedProfile; }; + hstring SourceName() const { return _fragment.Source(); } + hstring Json() const { return _entry.Json(); } + hstring AccessibleName() const noexcept; + + private: + Model::FragmentProfileEntry _entry; + Model::FragmentSettings _fragment; + Model::Profile _deducedProfile; + }; + + struct FragmentColorSchemeViewModel : FragmentColorSchemeViewModelT, ViewModelHelper + { + public: + FragmentColorSchemeViewModel(const Model::FragmentColorSchemeEntry& entry, const Model::FragmentSettings& fragment, const Editor::ColorSchemeViewModel& deducedSchemeVM) : + _entry{ entry }, + _fragment{ fragment }, + _deducedSchemeVM{ deducedSchemeVM } {} + + static bool SortAscending(const Editor::FragmentColorSchemeViewModel& lhs, const Editor::FragmentColorSchemeViewModel& rhs); + + Editor::ColorSchemeViewModel ColorSchemeVM() const { return _deducedSchemeVM; }; + hstring SourceName() const { return _fragment.Source(); } + hstring Json() const { return _entry.Json(); } + hstring AccessibleName() const noexcept; + + private: + Model::FragmentColorSchemeEntry _entry; + Model::FragmentSettings _fragment; + Editor::ColorSchemeViewModel _deducedSchemeVM; + }; + + struct ExtensionPackageTemplateSelector : public ExtensionPackageTemplateSelectorT + { + public: + ExtensionPackageTemplateSelector() = default; + + Windows::UI::Xaml::DataTemplate SelectTemplateCore(const Windows::Foundation::IInspectable& item, const Windows::UI::Xaml::DependencyObject& container); + Windows::UI::Xaml::DataTemplate SelectTemplateCore(const Windows::Foundation::IInspectable& item); + + WINRT_PROPERTY(Windows::UI::Xaml::DataTemplate, DefaultTemplate, nullptr); + WINRT_PROPERTY(Windows::UI::Xaml::DataTemplate, ComplexTemplate, nullptr); + WINRT_PROPERTY(Windows::UI::Xaml::DataTemplate, ComplexTemplateWithFontIcon, nullptr); + }; +}; + +namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation +{ + BASIC_FACTORY(Extensions); + BASIC_FACTORY(ExtensionPackageTemplateSelector); +} diff --git a/src/cascadia/TerminalSettingsEditor/Extensions.idl b/src/cascadia/TerminalSettingsEditor/Extensions.idl new file mode 100644 index 0000000000..6093471014 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Extensions.idl @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "ColorSchemesPageViewModel.idl"; + +namespace Microsoft.Terminal.Settings.Editor +{ + [default_interface] runtimeclass Extensions : Windows.UI.Xaml.Controls.Page + { + Extensions(); + ExtensionsViewModel ViewModel { get; }; + + Windows.UI.Xaml.Thickness CalculateMargin(Boolean hidden); + } + + [default_interface] runtimeclass ExtensionsViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged + { + // Properties + ExtensionPackageViewModel CurrentExtensionPackage; + Windows.UI.Xaml.DataTemplate CurrentExtensionPackageIdentifierTemplate { get; }; + Boolean IsExtensionView { get; }; + Boolean NoExtensionPackages { get; }; + Boolean NoProfilesModified { get; }; + Boolean NoProfilesAdded { get; }; + Boolean NoSchemesAdded { get; }; + Boolean DisplayBadge { get; }; + + // Views + IVector ExtensionPackages { get; }; + IObservableVector ProfilesModified { get; }; + IObservableVector ProfilesAdded { get; }; + IObservableVector ColorSchemesAdded { get; }; + + // Methods + void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings, ColorSchemesPageViewModel colorSchemesPageVM); + + event Windows.Foundation.TypedEventHandler NavigateToProfileRequested; + event Windows.Foundation.TypedEventHandler NavigateToColorSchemeRequested; + } + + [default_interface] runtimeclass ExtensionPackageViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged + { + Microsoft.Terminal.Settings.Model.ExtensionPackage Package { get; }; + Boolean Enabled; + String Scope { get; }; + String AccessibleName { get; }; + IVector FragmentExtensions { get; }; + } + + [default_interface] runtimeclass FragmentExtensionViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged + { + Microsoft.Terminal.Settings.Model.FragmentSettings Fragment { get; }; + IVectorView ProfilesModified { get; }; + IVectorView ProfilesAdded { get; }; + IVectorView ColorSchemesAdded { get; }; + } + + [default_interface] runtimeclass FragmentProfileViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged + { + Microsoft.Terminal.Settings.Model.Profile Profile { get; }; + String SourceName { get; }; + String Json { get; }; + String AccessibleName { get; }; + } + + [default_interface] runtimeclass FragmentColorSchemeViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged + { + ColorSchemeViewModel ColorSchemeVM { get; }; + String SourceName { get; }; + String Json { get; }; + String AccessibleName { get; }; + } + + [default_interface] runtimeclass ExtensionPackageTemplateSelector : Windows.UI.Xaml.Controls.DataTemplateSelector + { + ExtensionPackageTemplateSelector(); + + Windows.UI.Xaml.DataTemplate DefaultTemplate; + Windows.UI.Xaml.DataTemplate ComplexTemplate; + Windows.UI.Xaml.DataTemplate ComplexTemplateWithFontIcon; + } +} diff --git a/src/cascadia/TerminalSettingsEditor/Extensions.xaml b/src/cascadia/TerminalSettingsEditor/Extensions.xaml new file mode 100644 index 0000000000..4ebb03889e --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Extensions.xaml @@ -0,0 +1,558 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.cpp b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.cpp index acb0abcb34..2b220b2ddd 100644 --- a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.cpp +++ b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.cpp @@ -25,5 +25,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void GlobalAppearance::OnNavigatedTo(const NavigationEventArgs& e) { _ViewModel = e.Parameter().as(); + + TraceLoggingWrite( + g_hTerminalSettingsEditorProvider, + "NavigatedToPage", + TraceLoggingDescription("Event emitted when the user navigates to a page in the settings UI"), + TraceLoggingValue("globalAppearance", "PageId", "The identifier of the page that was navigated to"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } } diff --git a/src/cascadia/TerminalSettingsEditor/Interaction.cpp b/src/cascadia/TerminalSettingsEditor/Interaction.cpp index ed3c55fbda..6015cbcea1 100644 --- a/src/cascadia/TerminalSettingsEditor/Interaction.cpp +++ b/src/cascadia/TerminalSettingsEditor/Interaction.cpp @@ -5,6 +5,8 @@ #include "Interaction.h" #include "Interaction.g.cpp" +#include "EnumEntry.h" + using namespace winrt::Windows::UI::Xaml::Navigation; using namespace winrt::Microsoft::Terminal::Settings::Model; @@ -13,10 +15,20 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation Interaction::Interaction() { InitializeComponent(); + + INITIALIZE_BINDABLE_ENUM_SETTING(WarnAboutMultiLinePaste, WarnAboutMultiLinePaste, winrt::Microsoft::Terminal::Control::WarnAboutMultiLinePaste, L"Globals_WarnAboutMultiLinePaste", L"Content"); } void Interaction::OnNavigatedTo(const NavigationEventArgs& e) { _ViewModel = e.Parameter().as(); + + TraceLoggingWrite( + g_hTerminalSettingsEditorProvider, + "NavigatedToPage", + TraceLoggingDescription("Event emitted when the user navigates to a page in the settings UI"), + TraceLoggingValue("interaction", "PageId", "The identifier of the page that was navigated to"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } } diff --git a/src/cascadia/TerminalSettingsEditor/Interaction.h b/src/cascadia/TerminalSettingsEditor/Interaction.h index cbb7846c38..568139fdee 100644 --- a/src/cascadia/TerminalSettingsEditor/Interaction.h +++ b/src/cascadia/TerminalSettingsEditor/Interaction.h @@ -16,6 +16,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation til::property_changed_event PropertyChanged; WINRT_OBSERVABLE_PROPERTY(Editor::InteractionViewModel, ViewModel, PropertyChanged.raise, nullptr); + GETSET_BINDABLE_ENUM_SETTING(WarnAboutMultiLinePaste, winrt::Microsoft::Terminal::Control::WarnAboutMultiLinePaste, ViewModel().WarnAboutMultiLinePaste); }; } diff --git a/src/cascadia/TerminalSettingsEditor/Interaction.idl b/src/cascadia/TerminalSettingsEditor/Interaction.idl index 840fca4a75..3a41823b87 100644 --- a/src/cascadia/TerminalSettingsEditor/Interaction.idl +++ b/src/cascadia/TerminalSettingsEditor/Interaction.idl @@ -9,5 +9,8 @@ namespace Microsoft.Terminal.Settings.Editor { Interaction(); InteractionViewModel ViewModel { get; }; + + IInspectable CurrentWarnAboutMultiLinePaste; + Windows.Foundation.Collections.IObservableVector WarnAboutMultiLinePasteList { get; }; } } diff --git a/src/cascadia/TerminalSettingsEditor/Interaction.xaml b/src/cascadia/TerminalSettingsEditor/Interaction.xaml index 10d45c7899..fe3f67c6eb 100644 --- a/src/cascadia/TerminalSettingsEditor/Interaction.xaml +++ b/src/cascadia/TerminalSettingsEditor/Interaction.xaml @@ -83,6 +83,18 @@ Style="{StaticResource ToggleSwitchInExpanderStyle}" /> + + + + + + + + + + - + diff --git a/src/cascadia/TerminalSettingsEditor/InteractionViewModel.h b/src/cascadia/TerminalSettingsEditor/InteractionViewModel.h index d7c216fe3b..c3e48715f7 100644 --- a/src/cascadia/TerminalSettingsEditor/InteractionViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/InteractionViewModel.h @@ -25,6 +25,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, TrimPaste); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, SnapToGridOnResize); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, FocusFollowMouse); + PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ScrollToZoom); + PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ScrollToChangeOpacity); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, DetectURLs); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, SearchWebDefaultQueryUrl); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, WordDelimiters); diff --git a/src/cascadia/TerminalSettingsEditor/InteractionViewModel.idl b/src/cascadia/TerminalSettingsEditor/InteractionViewModel.idl index c79e7fb94c..ba77aec55f 100644 --- a/src/cascadia/TerminalSettingsEditor/InteractionViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/InteractionViewModel.idl @@ -22,13 +22,15 @@ namespace Microsoft.Terminal.Settings.Editor PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, TrimPaste); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, SnapToGridOnResize); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, FocusFollowMouse); + PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, ScrollToZoom); + PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, ScrollToChangeOpacity); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, DetectURLs); PERMANENT_OBSERVABLE_PROJECTED_SETTING(String, SearchWebDefaultQueryUrl); PERMANENT_OBSERVABLE_PROJECTED_SETTING(String, WordDelimiters); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, ConfirmCloseAllTabs); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, InputServiceWarning); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, WarnAboutLargePaste); - PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, WarnAboutMultiLinePaste); + PERMANENT_OBSERVABLE_PROJECTED_SETTING(Microsoft.Terminal.Control.WarnAboutMultiLinePaste, WarnAboutMultiLinePaste); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, EnableColorSelection); } } diff --git a/src/cascadia/TerminalSettingsEditor/Launch.cpp b/src/cascadia/TerminalSettingsEditor/Launch.cpp index 286ed7c814..9c0a933961 100644 --- a/src/cascadia/TerminalSettingsEditor/Launch.cpp +++ b/src/cascadia/TerminalSettingsEditor/Launch.cpp @@ -43,5 +43,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _ViewModel = e.Parameter().as(); auto innerViewModel{ winrt::get_self(_ViewModel) }; /* coroutine dispatch */ innerViewModel->PrepareStartOnUserLoginSettings(); + + TraceLoggingWrite( + g_hTerminalSettingsEditorProvider, + "NavigatedToPage", + TraceLoggingDescription("Event emitted when the user navigates to a page in the settings UI"), + TraceLoggingValue("startup", "PageId", "The identifier of the page that was navigated to"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } } diff --git a/src/cascadia/TerminalSettingsEditor/Launch.xaml b/src/cascadia/TerminalSettingsEditor/Launch.xaml index 8aeca2f92b..22125d2ac6 100644 --- a/src/cascadia/TerminalSettingsEditor/Launch.xaml +++ b/src/cascadia/TerminalSettingsEditor/Launch.xaml @@ -64,7 +64,7 @@ + IconSource="{x:Bind mtu:IconPathConverter.IconSourceWUX(Icon.Resolved), Mode=OneTime}" /> @@ -116,6 +116,7 @@ @@ -140,8 +141,7 @@ - + @@ -193,8 +193,8 @@ - + @@ -236,7 +236,7 @@ - + diff --git a/src/cascadia/TerminalSettingsEditor/LaunchViewModel.cpp b/src/cascadia/TerminalSettingsEditor/LaunchViewModel.cpp index 440f43211a..cf99cadb9a 100644 --- a/src/cascadia/TerminalSettingsEditor/LaunchViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/LaunchViewModel.cpp @@ -82,16 +82,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return language.NativeName(); } - // Returns whether the language selector is available/shown. - // - // winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride() - // doesn't work for unpackaged applications. The corresponding code in TerminalApp is disabled. - // It would be confusing for our users if we presented a dysfunctional language selector. - bool LaunchViewModel::LanguageSelectorAvailable() - { - return IsPackaged(); - } - // Returns the list of languages the user may override the application language with. // The returned list are BCP 47 language tags like {"und", "en-US", "de-DE", "es-ES", ...}. // "und" is short for "undefined" and is synonymous for "Use system language" in this code. @@ -102,12 +92,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return _languageList; } - if (!LanguageSelectorAvailable()) - { - _languageList = {}; - return _languageList; - } - // In order to return the language list this code does the following: // [1] Get all possible languages we want to allow the user to choose. // We have to acquire languages from multiple sources, creating duplicates. See below at [1]. @@ -179,19 +163,24 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return _currentLanguage; } - if (!LanguageSelectorAvailable()) + winrt::hstring currentLanguage; + if (IsPackaged()) { - _currentLanguage = {}; - return _currentLanguage; + // NOTE: PrimaryLanguageOverride throws if this instance is unpackaged. + currentLanguage = winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride(); + } + else + { + if (_Settings.GlobalSettings().HasLanguage()) + { + currentLanguage = _Settings.GlobalSettings().Language(); + } } - // NOTE: PrimaryLanguageOverride throws if this instance is unpackaged. - auto currentLanguage = winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride(); if (currentLanguage.empty()) { currentLanguage = systemLanguageTag; } - _currentLanguage = winrt::box_value(currentLanguage); return _currentLanguage; } diff --git a/src/cascadia/TerminalSettingsEditor/LaunchViewModel.h b/src/cascadia/TerminalSettingsEditor/LaunchViewModel.h index d96046524f..98851b74dc 100644 --- a/src/cascadia/TerminalSettingsEditor/LaunchViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/LaunchViewModel.h @@ -20,7 +20,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // "Deutsch (Deutschland)". This works independently of the user's locale. static winrt::hstring LanguageDisplayConverter(const winrt::hstring& tag); - bool LanguageSelectorAvailable(); winrt::Windows::Foundation::Collections::IObservableVector LanguageList(); winrt::Windows::Foundation::IInspectable CurrentLanguage(); void CurrentLanguage(const winrt::Windows::Foundation::IInspectable& tag); diff --git a/src/cascadia/TerminalSettingsEditor/LaunchViewModel.idl b/src/cascadia/TerminalSettingsEditor/LaunchViewModel.idl index 3781c24b87..fe39cbccb1 100644 --- a/src/cascadia/TerminalSettingsEditor/LaunchViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/LaunchViewModel.idl @@ -10,7 +10,6 @@ namespace Microsoft.Terminal.Settings.Editor runtimeclass LaunchViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged { static String LanguageDisplayConverter(String tag); - Boolean LanguageSelectorAvailable { get; }; Windows.Foundation.Collections.IObservableVector LanguageList { get; }; IInspectable CurrentLanguage; diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.cpp b/src/cascadia/TerminalSettingsEditor/MainPage.cpp index 5daf396b72..19345746aa 100644 --- a/src/cascadia/TerminalSettingsEditor/MainPage.cpp +++ b/src/cascadia/TerminalSettingsEditor/MainPage.cpp @@ -9,6 +9,7 @@ #include "Compatibility.h" #include "Rendering.h" #include "RenderingViewModel.h" +#include "Extensions.h" #include "Actions.h" #include "ProfileViewModel.h" #include "GlobalAppearance.h" @@ -38,12 +39,14 @@ using namespace winrt::Windows::System; using namespace winrt::Windows::UI::Xaml::Controls; using namespace winrt::Windows::Foundation::Collections; +static const std::wstring_view openJsonTag{ L"OpenJson_Nav" }; static const std::wstring_view launchTag{ L"Launch_Nav" }; static const std::wstring_view interactionTag{ L"Interaction_Nav" }; static const std::wstring_view renderingTag{ L"Rendering_Nav" }; static const std::wstring_view compatibilityTag{ L"Compatibility_Nav" }; static const std::wstring_view actionsTag{ L"Actions_Nav" }; static const std::wstring_view newTabMenuTag{ L"NewTabMenu_Nav" }; +static const std::wstring_view extensionsTag{ L"Extensions_Nav" }; static const std::wstring_view globalProfileTag{ L"GlobalProfile_Nav" }; static const std::wstring_view addProfileTag{ L"AddProfile" }; static const std::wstring_view colorSchemesTag{ L"ColorSchemes_Nav" }; @@ -111,6 +114,33 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } }); + auto extensionsVMImpl = winrt::make_self(_settingsClone, _colorSchemesPageVM); + extensionsVMImpl->NavigateToProfileRequested({ this, &MainPage::_NavigateToProfileHandler }); + extensionsVMImpl->NavigateToColorSchemeRequested({ this, &MainPage::_NavigateToColorSchemeHandler }); + _extensionsVM = *extensionsVMImpl; + _extensionsViewModelChangedRevoker = _extensionsVM.PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& args) { + const auto settingName{ args.PropertyName() }; + if (settingName == L"CurrentExtensionPackage") + { + if (const auto& currentExtensionPackage = _extensionsVM.CurrentExtensionPackage()) + { + const auto& pkg = currentExtensionPackage.Package(); + const auto label = pkg.DisplayName().empty() ? pkg.Source() : pkg.DisplayName(); + const auto crumb = winrt::make(box_value(currentExtensionPackage), label, BreadcrumbSubPage::Extensions_Extension); + _breadcrumbs.Append(crumb); + SettingsMainPage_ScrollViewer().ScrollToVerticalOffset(0); + } + else + { + // If we don't have a current extension package, we're at the root of the Extensions page + _breadcrumbs.Clear(); + const auto crumb = winrt::make(box_value(extensionsTag), RS_(L"Nav_Extensions/Content"), BreadcrumbSubPage::None); + _breadcrumbs.Append(crumb); + } + contentFrame().Navigate(xaml_typename(), _extensionsVM); + } + }); + // Make sure to initialize the profiles _after_ we have initialized the color schemes page VM, because we pass // that VM into the appearance VMs within the profiles _InitializeProfilesList(); @@ -161,6 +191,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // Update the Nav State with the new version of the settings _colorSchemesPageVM.UpdateSettings(_settingsClone); _newTabMenuPageVM.UpdateSettings(_settingsClone); + _extensionsVM.UpdateSettings(_settingsClone, _colorSchemesPageVM); // We'll update the profile in the _profilesNavState whenever we actually navigate to one @@ -182,7 +213,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { // found the one that was selected before the refresh SettingsNav().SelectedItem(item); - _Navigate(*stringTag, crumb->SubPage()); + _Navigate(*breadcrumbStringTag, crumb->SubPage()); return; } } @@ -197,6 +228,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return; } } + else if (const auto& breadcrumbExtensionPackage{ crumb->Tag().try_as() }) + { + if (stringTag == extensionsTag) + { + // navigate to the Extensions page, + // _Navigate() will handle trying to find the right subpage + SettingsNav().SelectedItem(item); + _Navigate(breadcrumbExtensionPackage, BreadcrumbSubPage::Extensions_Extension); + return; + } + } } else if (const auto& profileTag{ tag.try_as() }) { @@ -216,7 +258,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } } - // Couldn't find the selected item, fallback to first menu item + // Couldn't find the selected item, fall back to first menu item // This happens when the selected item was a profile which doesn't exist in the new configuration // We can use menuItemsSTL here because the only things they miss are profile entries. const auto& firstItem{ _menuItemSource.GetAt(0).as() }; @@ -324,6 +366,26 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation if (const auto navString = clickedItemContainer.Tag().try_as()) { + if (*navString == openJsonTag) + { + const auto window = CoreWindow::GetForCurrentThread(); + const auto rAltState = window.GetKeyState(VirtualKey::RightMenu); + const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu); + const auto altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) || + WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down); + const auto target = altPressed ? SettingsTarget::DefaultsFile : SettingsTarget::SettingsFile; + + TraceLoggingWrite( + g_hTerminalSettingsEditorProvider, + "OpenJson", + TraceLoggingDescription("Event emitted when the user clicks the Open JSON button in the settings UI"), + TraceLoggingValue(target == SettingsTarget::DefaultsFile ? "DefaultsFile" : "SettingsFile", "SettingsTarget", "The target settings file"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); + + OpenJson.raise(nullptr, target); + return; + } _Navigate(*navString, BreadcrumbSubPage::None); } else if (const auto profile = clickedItemContainer.Tag().try_as()) @@ -419,7 +481,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } else if (clickedItemTag == compatibilityTag) { - contentFrame().Navigate(xaml_typename(), winrt::make(_settingsClone.GlobalSettings())); + contentFrame().Navigate(xaml_typename(), winrt::make(_settingsClone)); const auto crumb = winrt::make(box_value(clickedItemTag), RS_(L"Nav_Compatibility/Content"), BreadcrumbSubPage::None); _breadcrumbs.Append(crumb); } @@ -445,6 +507,21 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _breadcrumbs.Append(crumb); } } + else if (clickedItemTag == extensionsTag) + { + if (_extensionsVM.CurrentExtensionPackage()) + { + // Setting CurrentExtensionPackage triggers the PropertyChanged event, + // which will navigate to the correct page and update the breadcrumbs appropriately + _extensionsVM.CurrentExtensionPackage(nullptr); + } + else + { + contentFrame().Navigate(xaml_typename(), _extensionsVM); + const auto crumb = winrt::make(box_value(clickedItemTag), RS_(L"Nav_Extensions/Content"), BreadcrumbSubPage::None); + _breadcrumbs.Append(crumb); + } + } else if (clickedItemTag == globalProfileTag) { auto profileVM{ _viewModelForProfile(_settingsClone.ProfileDefaults(), _settingsClone, Dispatcher()) }; @@ -575,31 +652,47 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } } - void MainPage::OpenJsonTapped(const IInspectable& /*sender*/, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& /*args*/) + void MainPage::_Navigate(const Editor::ExtensionPackageViewModel& extPkgVM, BreadcrumbSubPage subPage) { - const auto window = CoreWindow::GetForCurrentThread(); - const auto rAltState = window.GetKeyState(VirtualKey::RightMenu); - const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu); - const auto altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) || - WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down); + _PreNavigateHelper(); - const auto target = altPressed ? SettingsTarget::DefaultsFile : SettingsTarget::SettingsFile; - OpenJson.raise(nullptr, target); - } + contentFrame().Navigate(xaml_typename(), _extensionsVM); + const auto crumb = winrt::make(box_value(extensionsTag), RS_(L"Nav_Extensions/Content"), BreadcrumbSubPage::None); + _breadcrumbs.Append(crumb); - void MainPage::OpenJsonKeyDown(const IInspectable& /*sender*/, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& args) - { - if (args.Key() == VirtualKey::Enter || args.Key() == VirtualKey::Space) + if (subPage == BreadcrumbSubPage::None) { - const auto target = args.KeyStatus().IsMenuKeyDown ? SettingsTarget::DefaultsFile : SettingsTarget::SettingsFile; - OpenJson.raise(nullptr, target); + _extensionsVM.CurrentExtensionPackage(nullptr); + } + else + { + bool found = false; + for (const auto& pkgVM : _extensionsVM.ExtensionPackages()) + { + if (pkgVM.Package().Source() == extPkgVM.Package().Source()) + { + // Take advantage of the PropertyChanged event to navigate + // to the correct extension package and build the breadcrumbs as we go + _extensionsVM.CurrentExtensionPackage(pkgVM); + found = true; + break; + } + } + if (!found) + { + // If we couldn't find a reasonable match, just go back to the root + _extensionsVM.CurrentExtensionPackage(nullptr); + } } } void MainPage::SaveButton_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/) { _settingsClone.LogSettingChanges(false); - _settingsClone.WriteSettingsToDisk(); + if (!_settingsClone.WriteSettingsToDisk()) + { + ShowLoadWarningsDialog.raise(*this, _settingsClone.Warnings()); + } } void MainPage::ResetButton_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/) @@ -621,6 +714,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { _Navigate(*ntmEntryViewModel, subPage); } + else if (const auto extPkgViewModel = tag.try_as()) + { + _Navigate(*extPkgViewModel, subPage); + } else { _Navigate(tag.as(), subPage); @@ -752,7 +849,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation if (auto menuItem{ weakMenuItem.get() }) { const auto& tag{ menuItem.Tag().as() }; - if (args.PropertyName() == L"Icon" || args.PropertyName() == L"EvaluatedIcon") + if (args.PropertyName() == L"Icon") { menuItem.Icon(UI::IconPathConverter::IconWUX(tag.EvaluatedIcon())); } @@ -818,6 +915,35 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return _breadcrumbs; } + void MainPage::_NavigateToProfileHandler(const IInspectable& /*sender*/, winrt::guid profileGuid) + { + for (auto&& menuItem : _menuItemSource) + { + if (const auto& navViewItem{ menuItem.try_as() }) + { + if (const auto& tag{ navViewItem.Tag() }) + { + if (const auto& profileTag{ tag.try_as() }) + { + if (profileTag->OriginalProfileGuid() == profileGuid) + { + SettingsNav().SelectedItem(menuItem); + _Navigate(*profileTag, BreadcrumbSubPage::None); + return; + } + } + } + } + } + // Silently fail if the profile wasn't found + } + + void MainPage::_NavigateToColorSchemeHandler(const IInspectable& /*sender*/, const IInspectable& /*args*/) + { + SettingsNav().SelectedItem(ColorSchemesNavItem()); + _Navigate(hstring{ colorSchemesTag }, BreadcrumbSubPage::ColorSchemes_Edit); + } + winrt::Windows::UI::Xaml::Media::Brush MainPage::BackgroundBrush() { return SettingsNav().Background(); diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.h b/src/cascadia/TerminalSettingsEditor/MainPage.h index 81724effb1..ff87f4910b 100644 --- a/src/cascadia/TerminalSettingsEditor/MainPage.h +++ b/src/cascadia/TerminalSettingsEditor/MainPage.h @@ -30,8 +30,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void UpdateSettings(const Model::CascadiaSettings& settings); - void OpenJsonKeyDown(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& args); - void OpenJsonTapped(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& args); void SettingsNav_Loaded(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args); void SettingsNav_ItemInvoked(const Microsoft::UI::Xaml::Controls::NavigationView& sender, const Microsoft::UI::Xaml::Controls::NavigationViewItemInvokedEventArgs& args); void SaveButton_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args); @@ -45,8 +43,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush(); Windows::Foundation::Collections::IObservableVector Breadcrumbs() noexcept; + Editor::ExtensionsViewModel ExtensionsVM() const noexcept { return _extensionsVM; } til::typed_event OpenJson; + til::typed_event> ShowLoadWarningsDialog; private: Windows::Foundation::Collections::IObservableVector _breadcrumbs; @@ -70,16 +70,21 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void _Navigate(hstring clickedItemTag, BreadcrumbSubPage subPage); void _Navigate(const Editor::ProfileViewModel& profile, BreadcrumbSubPage subPage); void _Navigate(const Editor::NewTabMenuEntryViewModel& ntmEntryVM, BreadcrumbSubPage subPage); + void _Navigate(const Editor::ExtensionPackageViewModel& extPkgVM, BreadcrumbSubPage subPage); + void _NavigateToProfileHandler(const IInspectable& sender, winrt::guid profileGuid); + void _NavigateToColorSchemeHandler(const IInspectable& sender, const IInspectable& args); void _UpdateBackgroundForMica(); void _MoveXamlParsedNavItemsIntoItemSource(); winrt::Microsoft::Terminal::Settings::Editor::ColorSchemesPageViewModel _colorSchemesPageVM{ nullptr }; winrt::Microsoft::Terminal::Settings::Editor::NewTabMenuViewModel _newTabMenuPageVM{ nullptr }; + winrt::Microsoft::Terminal::Settings::Editor::ExtensionsViewModel _extensionsVM{ nullptr }; Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _profileViewModelChangedRevoker; Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _colorSchemesPageViewModelChangedRevoker; Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ntmViewModelChangedRevoker; + Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _extensionsViewModelChangedRevoker; }; } diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.idl b/src/cascadia/TerminalSettingsEditor/MainPage.idl index 0f5a6e49b8..20f3bab869 100644 --- a/src/cascadia/TerminalSettingsEditor/MainPage.idl +++ b/src/cascadia/TerminalSettingsEditor/MainPage.idl @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import "Extensions.idl"; + namespace Microsoft.Terminal.Settings.Editor { // Due to a XAML Compiler bug, it is hard for us to propagate an HWND into a XAML-using runtimeclass. @@ -20,7 +22,8 @@ namespace Microsoft.Terminal.Settings.Editor Profile_Terminal, Profile_Advanced, ColorSchemes_Edit, - NewTabMenu_Folder + NewTabMenu_Folder, + Extensions_Extension }; runtimeclass Breadcrumb : Windows.Foundation.IStringable @@ -36,12 +39,14 @@ namespace Microsoft.Terminal.Settings.Editor void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings); event Windows.Foundation.TypedEventHandler OpenJson; + event Windows.Foundation.TypedEventHandler > ShowLoadWarningsDialog; // Due to the aforementioned bug, we can't use IInitializeWithWindow _here_ either. // Let's just smuggle the HWND in as a UInt64 :| void SetHostingWindow(UInt64 window); Windows.Foundation.Collections.IObservableVector Breadcrumbs { get; }; + ExtensionsViewModel ExtensionsVM { get; }; Windows.UI.Xaml.Media.Brush BackgroundBrush { get; }; } diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.xaml b/src/cascadia/TerminalSettingsEditor/MainPage.xaml index 3a0773062a..77c1bd8488 100644 --- a/src/cascadia/TerminalSettingsEditor/MainPage.xaml +++ b/src/cascadia/TerminalSettingsEditor/MainPage.xaml @@ -17,7 +17,6 @@ - @@ -60,7 +59,6 @@ 0 0 - @@ -73,28 +71,32 @@ TabFocusNavigation="Cycle"> - 15,0,0,0 + 0,4,0,0 - - - - - - - - - 28 - 11,4,12,0 - SemiBold - 16 - - - + + + + + + + + + + + 20 + 8,4,8,0 + SemiBold + 16 + + + + + @@ -120,7 +122,8 @@ - @@ -155,6 +158,17 @@ + + + + + + + + + + SelectsOnInvoked="False" + Tag="OpenJson_Nav"> - + @@ -187,7 +201,8 @@ + Grid.Row="0" + Padding="16,0,16,48"> @@ -201,13 +216,13 @@ - @@ -230,7 +245,7 @@ - @@ -240,7 +255,7 @@ Style="{StaticResource AccentButtonStyle}" /> @@ -321,7 +320,7 @@ - + @@ -359,7 +358,7 @@ Style="{StaticResource SettingContainerWithIcon}"> + Spacing="4"> + IconSource="{x:Bind mtu:IconPathConverter.IconSourceWUX(Icon.Resolved), Mode=OneTime}" /> @@ -448,7 +447,9 @@ - + + (profileEntry); CurrentView().Append(entryVM); - _PrintAll(); return entryVM; } return nullptr; @@ -374,8 +373,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation Model::SeparatorEntry separatorEntry; const auto& entryVM = make(separatorEntry); CurrentView().Append(entryVM); - - _PrintAll(); return entryVM; } @@ -390,8 +387,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // Reset state after adding the entry AddFolderName({}); _folderTreeCache = nullptr; - - _PrintAll(); return entryVM; } @@ -410,7 +405,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation ProfileMatcherSource({}); ProfileMatcherCommandline({}); - _PrintAll(); return entryVM; } @@ -422,7 +416,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _NotifyChanges(L"IsRemainingProfilesEntryMissing"); - _PrintAll(); return entryVM; } @@ -496,134 +489,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return _folderEntry.Icon(); } - void NewTabMenuViewModel::_PrintAll() - { -#ifdef _DEBUG - OutputDebugString(L"---Model:---\n"); - _PrintModel(_Settings.GlobalSettings().NewTabMenu()); - OutputDebugString(L"\n"); - OutputDebugString(L"---VM:---\n"); - _PrintVM(_rootEntries); - OutputDebugString(L"\n"); -#endif - } - -#ifdef _DEBUG - void NewTabMenuViewModel::_PrintModel(Windows::Foundation::Collections::IVector list, std::wstring prefix) - { - if (!list) - { - return; - } - - for (auto&& e : list) - { - _PrintModel(e, prefix); - } - } - - void NewTabMenuViewModel::_PrintModel(const Model::NewTabMenuEntry& e, std::wstring prefix) - { - switch (e.Type()) - { - case NewTabMenuEntryType::Profile: - { - const auto& pe = e.as(); - OutputDebugString(fmt::format(L"{}Profile: {}\n", prefix, pe.Profile().Name()).c_str()); - break; - } - case NewTabMenuEntryType::Action: - { - const auto& actionEntry = e.as(); - OutputDebugString(fmt::format(L"{}Action: {}\n", prefix, actionEntry.ActionId()).c_str()); - break; - } - case NewTabMenuEntryType::Separator: - { - OutputDebugString(fmt::format(L"{}Separator\n", prefix).c_str()); - break; - } - case NewTabMenuEntryType::Folder: - { - const auto& fe = e.as(); - OutputDebugString(fmt::format(L"{}Folder: {}\n", prefix, fe.Name()).c_str()); - _PrintModel(fe.RawEntries(), prefix + L" "); - break; - } - case NewTabMenuEntryType::MatchProfiles: - { - const auto& matchProfilesEntry = e.as(); - OutputDebugString(fmt::format(L"{}MatchProfiles: {}\n", prefix, matchProfilesEntry.Name()).c_str()); - break; - } - case NewTabMenuEntryType::RemainingProfiles: - { - OutputDebugString(fmt::format(L"{}RemainingProfiles\n", prefix).c_str()); - break; - } - default: - break; - } - } - - void NewTabMenuViewModel::_PrintVM(Windows::Foundation::Collections::IVector list, std::wstring prefix) - { - if (!list) - { - return; - } - - for (auto&& e : list) - { - _PrintVM(e, prefix); - } - } - - void NewTabMenuViewModel::_PrintVM(const Editor::NewTabMenuEntryViewModel& e, std::wstring prefix) - { - switch (e.Type()) - { - case NewTabMenuEntryType::Profile: - { - const auto& pe = e.as(); - OutputDebugString(fmt::format(L"{}Profile: {}\n", prefix, pe.ProfileEntry().Profile().Name()).c_str()); - break; - } - case NewTabMenuEntryType::Action: - { - const auto& actionEntry = e.as(); - OutputDebugString(fmt::format(L"{}Action: {}\n", prefix, actionEntry.ActionEntry().ActionId()).c_str()); - break; - } - case NewTabMenuEntryType::Separator: - { - OutputDebugString(fmt::format(L"{}Separator\n", prefix).c_str()); - break; - } - case NewTabMenuEntryType::Folder: - { - const auto& fe = e.as(); - OutputDebugString(fmt::format(L"{}Folder: {}\n", prefix, fe.Name()).c_str()); - _PrintVM(fe.Entries(), prefix + L" "); - break; - } - case NewTabMenuEntryType::MatchProfiles: - { - const auto& matchProfilesEntry = e.as(); - OutputDebugString(fmt::format(L"{}MatchProfiles: {}\n", prefix, matchProfilesEntry.DisplayText()).c_str()); - break; - } - case NewTabMenuEntryType::RemainingProfiles: - { - OutputDebugString(fmt::format(L"{}RemainingProfiles\n", prefix).c_str()); - break; - } - default: - break; - } - } -#endif - NewTabMenuEntryViewModel::NewTabMenuEntryViewModel(const NewTabMenuEntryType type) noexcept : _Type{ type } { @@ -701,7 +566,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation const auto actionID = _ActionEntry.ActionId(); if (const auto& action = _Settings.ActionMap().GetActionByID(actionID)) { - return action.IconPath(); + return action.Icon().Resolved(); } return {}; } diff --git a/src/cascadia/TerminalSettingsEditor/NewTabMenuViewModel.h b/src/cascadia/TerminalSettingsEditor/NewTabMenuViewModel.h index c5486f68b5..b58767707f 100644 --- a/src/cascadia/TerminalSettingsEditor/NewTabMenuViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/NewTabMenuViewModel.h @@ -68,14 +68,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation static bool _IsRemainingProfilesEntryMissing(const Windows::Foundation::Collections::IVector& entries); void _FolderPropertyChanged(const IInspectable& sender, const Windows::UI::Xaml::Data::PropertyChangedEventArgs& args); - - void _PrintAll(); -#ifdef _DEBUG - void _PrintModel(Windows::Foundation::Collections::IVector list, std::wstring prefix = L""); - void _PrintModel(const Model::NewTabMenuEntry& e, std::wstring prefix = L""); - void _PrintVM(Windows::Foundation::Collections::IVector list, std::wstring prefix = L""); - void _PrintVM(const Editor::NewTabMenuEntryViewModel& vm, std::wstring prefix = L""); -#endif }; struct FolderTreeViewEntry : FolderTreeViewEntryT @@ -140,8 +132,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation bool Inlining() const; void Inlining(bool value); + + hstring Icon() const { return _FolderEntry.Icon().Path(); } + GETSET_OBSERVABLE_PROJECTED_SETTING(_FolderEntry, Name); - GETSET_OBSERVABLE_PROJECTED_SETTING(_FolderEntry, Icon); GETSET_OBSERVABLE_PROJECTED_SETTING(_FolderEntry, AllowEmpty); VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector, Entries); diff --git a/src/cascadia/TerminalSettingsEditor/NewTabMenuViewModel.idl b/src/cascadia/TerminalSettingsEditor/NewTabMenuViewModel.idl index 7097f16f92..a46d8c1303 100644 --- a/src/cascadia/TerminalSettingsEditor/NewTabMenuViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/NewTabMenuViewModel.idl @@ -83,7 +83,7 @@ namespace Microsoft.Terminal.Settings.Editor FolderEntryViewModel(Microsoft.Terminal.Settings.Model.FolderEntry folderEntry, Microsoft.Terminal.Settings.Model.CascadiaSettings settings); String Name; - String Icon; + String Icon { get; }; Boolean Inlining; Boolean AllowEmpty; IObservableVector Entries; diff --git a/src/cascadia/TerminalSettingsEditor/NullableColorPicker.xaml b/src/cascadia/TerminalSettingsEditor/NullableColorPicker.xaml index 031b2b6276..6755543311 100644 --- a/src/cascadia/TerminalSettingsEditor/NullableColorPicker.xaml +++ b/src/cascadia/TerminalSettingsEditor/NullableColorPicker.xaml @@ -123,11 +123,11 @@ - + - + @@ -138,7 +138,7 @@ Height="20" Background="{x:Bind mtu:Converters.ColorToBrush(NullColorPreview), Mode=OneWay}" BorderThickness="1" - CornerRadius="{ThemeResource ControlCornerRadius}" /> + CornerRadius="{StaticResource ControlCornerRadius}" /> diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp index e066db5501..5a01ad5963 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp @@ -10,7 +10,9 @@ #include #include "../WinRTUtils/inc/Utils.h" #include "../../renderer/base/FontCache.h" +#include "../TerminalSettingsAppAdapterLib/TerminalSettings.h" #include "SegoeFluentIconList.h" +#include "../../types/inc/utils.hpp" using namespace winrt::Windows::UI::Text; using namespace winrt::Windows::UI::Xaml; @@ -71,7 +73,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { // notify listener that all starting directory related values might have changed // NOTE: this is similar to what is done with BackgroundImagePath above - _NotifyChanges(L"UseParentProcessDirectory", L"UseCustomStartingDirectory"); + _NotifyChanges(L"UseParentProcessDirectory", L"CurrentStartingDirectoryPreview"); } else if (viewModelProperty == L"AntialiasingMode") { @@ -83,7 +85,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } else if (viewModelProperty == L"BellStyle") { - _NotifyChanges(L"IsBellStyleFlagSet"); + _NotifyChanges(L"IsBellStyleFlagSet", L"BellStylePreview"); } else if (viewModelProperty == L"ScrollState") { @@ -94,6 +96,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // _DeduceCurrentIconType() ends with a "CurrentIconType" notification // so we don't need to call _UpdateIconPreview() here _DeduceCurrentIconType(); + // The icon changed; let's re-evaluate it with its new context. + _appSettings.ResolveMediaResources(); } else if (viewModelProperty == L"CurrentIconType") { @@ -105,15 +109,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation L"UsingImageIcon", L"LocalizedIcon", L"IconPreview", + L"IconPath", L"EvaluatedIcon"); } else if (viewModelProperty == L"CurrentBuiltInIcon") { - Icon(unbox_value(_CurrentBuiltInIcon.as().EnumValue())); + IconPath(unbox_value(_CurrentBuiltInIcon.as().EnumValue())); } else if (viewModelProperty == L"CurrentEmojiIcon") { - Icon(CurrentEmojiIcon()); + IconPath(CurrentEmojiIcon()); } else if (viewModelProperty == L"CurrentBellSounds") { @@ -139,6 +144,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _parsedPadding = StringToXamlThickness(_profile.Padding()); _NotifyChanges(L"LeftPadding", L"TopPadding", L"RightPadding", L"BottomPadding"); } + else if (viewModelProperty == L"TabTitle") + { + _NotifyChanges(L"TabTitlePreview"); + } + else if (viewModelProperty == L"AnswerbackMessage") + { + _NotifyChanges(L"AnswerbackMessagePreview"); + } }); // Do the same for the starting directory @@ -174,17 +187,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void ProfileViewModel::_DeduceCurrentIconType() { - const auto& profileIcon = _profile.Icon(); + const auto profileIcon = IconPath(); if (profileIcon == HideIconValue) { _currentIconType = _IconTypes.GetAt(0); } - else if (L"\uE700" <= profileIcon && profileIcon <= L"\uF8B3") + else if (profileIcon.size() == 1 && (L'\uE700' <= til::at(profileIcon, 0) && til::at(profileIcon, 0) <= L'\uF8B3')) { _currentIconType = _IconTypes.GetAt(1); _DeduceCurrentBuiltInIcon(); } - else if (profileIcon.size() <= 2) + else if (::Microsoft::Console::Utils::IsLikelyToBeEmojiOrSymbolIcon(profileIcon)) { // We already did a range check for MDL2 Assets in the previous one, // so if we're out of that range but still short, assume we're an emoji @@ -203,7 +216,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { _UpdateBuiltInIcons(); } - const auto& profileIcon = Icon(); + const auto profileIcon = IconPath(); for (uint32_t i = 0; i < _BuiltInIcons.Size(); i++) { const auto& builtIn = _BuiltInIcons.GetAt(i); @@ -272,9 +285,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { return _parsedPadding.Bottom; } - Model::TerminalSettings ProfileViewModel::TermSettings() const + Control::IControlSettings ProfileViewModel::TermSettings() const { - return Model::TerminalSettings::CreateForPreview(_appSettings, _profile); + // This may look pricey, but it only resolves resources that have not been visited + // and the preview update is debounced. + _appSettings.ResolveMediaResources(); + return *Settings::TerminalSettings::CreateForPreview(_appSettings, _profile); } // Method Description: @@ -413,6 +429,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return RS_(L"Profile_TabTitleNone"); } + hstring ProfileViewModel::AnswerbackMessagePreview() const + { + if (const auto answerbackMessage{ AnswerbackMessage() }; !answerbackMessage.empty()) + { + return answerbackMessage; + } + return RS_(L"Profile_AnswerbackMessageNone"); + } + Editor::AppearanceViewModel ProfileViewModel::DefaultAppearance() { return _defaultAppearanceViewModel; @@ -470,18 +495,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return Feature_ScrollbarMarks::IsEnabled(); } - bool ProfileViewModel::UseParentProcessDirectory() + hstring ProfileViewModel::CurrentStartingDirectoryPreview() const { - return StartingDirectory().empty(); + if (UseParentProcessDirectory()) + { + return RS_(L"Profile_StartingDirectoryUseParentCheckbox/Content"); + } + return StartingDirectory(); } - // This function simply returns the opposite of UseParentProcessDirectory. - // We bind the 'IsEnabled' parameters of the textbox and browse button - // to this because it needs to be the reverse of UseParentProcessDirectory - // but we don't want to create a whole new converter for inverting a boolean - bool ProfileViewModel::UseCustomStartingDirectory() + bool ProfileViewModel::UseParentProcessDirectory() const { - return !UseParentProcessDirectory(); + return StartingDirectory().empty(); } void ProfileViewModel::UseParentProcessDirectory(const bool useParent) @@ -522,7 +547,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { return RS_(L"Profile_IconTypeNone"); } - return Icon(); + return IconPath(); // For display as a string } Windows::UI::Xaml::Controls::IconElement ProfileViewModel::IconPreview() const @@ -544,7 +569,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // Stash the current value of Icon. If the user // switches out of then back to IconType::Image, we want // the path that we display in the text box to remain unchanged. - _lastIconPath = Icon(); + _lastIconPath = IconPath(); } // Set the member here instead of after setting Icon() below! @@ -559,7 +584,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { case IconType::None: { - _profile.Icon(HideIconValue); + IconPath(winrt::hstring{ HideIconValue }); break; } case IconType::Image: @@ -568,7 +593,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { // Conversely, if we switch to Image, // retrieve that saved value and apply it - _profile.Icon(_lastIconPath); + IconPath(_lastIconPath); } break; } @@ -576,7 +601,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { if (_CurrentBuiltInIcon) { - _profile.Icon(unbox_value(_CurrentBuiltInIcon.as().EnumValue())); + IconPath(unbox_value(_CurrentBuiltInIcon.as().EnumValue())); } break; } @@ -613,6 +638,49 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return _currentIconType == _IconTypes.GetAt(3); } + hstring ProfileViewModel::BellStylePreview() const + { + const auto bellStyle = BellStyle(); + if (WI_AreAllFlagsSet(bellStyle, BellStyle::Audible | BellStyle::Window | BellStyle::Taskbar)) + { + return RS_(L"Profile_BellStyleAll/Content"); + } + else if (bellStyle == static_cast(0)) + { + return RS_(L"Profile_BellStyleNone/Content"); + } + + std::vector resultList; + resultList.reserve(3); + if (WI_IsFlagSet(bellStyle, BellStyle::Audible)) + { + resultList.emplace_back(RS_(L"Profile_BellStyleAudible/Content")); + } + if (WI_IsFlagSet(bellStyle, BellStyle::Window)) + { + resultList.emplace_back(RS_(L"Profile_BellStyleWindow/Content")); + } + if (WI_IsFlagSet(bellStyle, BellStyle::Taskbar)) + { + resultList.emplace_back(RS_(L"Profile_BellStyleTaskbar/Content")); + } + + // add in the commas + hstring result{}; + for (auto&& entry : resultList) + { + if (result.empty()) + { + result = entry; + } + else + { + result = result + L", " + entry; + } + } + return result; + } + bool ProfileViewModel::IsBellStyleFlagSet(const uint32_t flag) { return (WI_EnumValue(BellStyle()) & flag) == flag; @@ -653,7 +721,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } } _MarkDuplicateBellSoundDirectories(); - _CheckBellSoundsExistence(); _NotifyChanges(L"CurrentBellSounds"); } @@ -665,19 +732,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { if (!_profile.HasBellSound()) { - std::vector newSounds; + std::vector newSounds; if (const auto inheritedSounds = _profile.BellSound()) { - // copy inherited bell sounds to the current layer - newSounds.reserve(inheritedSounds.Size()); - for (const auto sound : inheritedSounds) - { - newSounds.push_back(sound); - } + newSounds = wil::to_vector(inheritedSounds); } // if we didn't inherit any bell sounds, // we should still set the bell sound to an empty list (instead of null) - _profile.BellSound(winrt::single_threaded_vector(std::move(newSounds))); + _profile.BellSound(winrt::single_threaded_vector(std::move(newSounds))); } } @@ -701,56 +763,35 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } } - // Method Description: - // - Check if the bell sounds exist on disk. Mark any that don't exist - // so that they show the appropriate UI - safe_void_coroutine ProfileViewModel::_CheckBellSoundsExistence() + BellSoundViewModel::BellSoundViewModel(const Model::IMediaResource& resource) : + _resource{ resource } { - co_await winrt::resume_background(); - std::vector markedSounds; - for (auto&& sound : _CurrentBellSounds) + if (_resource.Ok() && _resource.Path() != _resource.Resolved()) { - if (!std::filesystem::exists(std::wstring_view{ sound.Path() })) - { - markedSounds.push_back(sound); - } + // If the resource was resolved to something other than its path, show the path! + _ShowDirectory = true; } - - co_await winrt::resume_foreground(_dispatcher); - for (auto&& sound : markedSounds) - { - get_self(sound)->FileExists(false); - } - } - - BellSoundViewModel::BellSoundViewModel(hstring path) : - _Path{ path } - { - PropertyChanged([this](auto&&, const PropertyChangedEventArgs& args) { - if (args.PropertyName() == L"FileExists") - { - _NotifyChanges(L"DisplayPath", L"SubText"); - } - }); } hstring BellSoundViewModel::DisplayPath() const { - if (_FileExists) + if (_resource.Ok()) { - // filename - const std::filesystem::path filePath{ std::wstring_view{ _Path } }; + // filename; start from the resolved path to show where it actually landed + auto resolvedPath{ _resource.Resolved() }; + const std::filesystem::path filePath{ std::wstring_view{ resolvedPath } }; return hstring{ filePath.filename().wstring() }; } - return _Path; + return _resource.Path(); } hstring BellSoundViewModel::SubText() const { - if (_FileExists) + if (_resource.Ok()) { // Directory - const std::filesystem::path filePath{ std::wstring_view{ _Path } }; + auto resolvedPath{ _resource.Resolved() }; + const std::filesystem::path filePath{ std::wstring_view{ resolvedPath } }; return hstring{ filePath.parent_path().wstring() }; } return RS_(L"Profile_BellSoundNotFound"); @@ -758,17 +799,22 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation hstring ProfileViewModel::BellSoundPreview() { - const auto& currentSound = BellSound(); - if (!currentSound || currentSound.Size() == 0) + if (!_CurrentBellSounds || _CurrentBellSounds.Size() == 0) { return RS_(L"Profile_BellSoundPreviewDefault"); } - else if (currentSound.Size() == 1) + if (_CurrentBellSounds.Size() > 1) { - std::filesystem::path filePath{ std::wstring_view{ currentSound.GetAt(0) } }; - return hstring{ filePath.filename().wstring() }; + return RS_(L"Profile_BellSoundPreviewMultiple"); } - return RS_(L"Profile_BellSoundPreviewMultiple"); + + const auto currentBellSound = _CurrentBellSounds.GetAt(0); + if (currentBellSound.FileExists()) + { + return currentBellSound.DisplayPath(); + } + + return RS_(L"Profile_BellSoundNotFound"); } void ProfileViewModel::RequestAddBellSound(hstring path) @@ -777,9 +823,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // copy it over to the current layer and apply modifications _PrepareModelForBellSoundModification(); - // No need to check if the file exists. We came from the FilePicker. That's good enough. - _CurrentBellSounds.Append(winrt::make(path)); - _profile.BellSound().Append(path); + auto bellResource{ MediaResourceHelper::FromString(path) }; + bellResource.Resolve(path); // No need to check if the file exists. We came from the FilePicker. That's good enough. + _CurrentBellSounds.Append(winrt::make(bellResource)); + _profile.BellSound().Append(bellResource); _NotifyChanges(L"CurrentBellSounds"); } diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h index d02f61291a..4be8d7ca17 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h @@ -30,13 +30,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation struct BellSoundViewModel : BellSoundViewModelT, ViewModelHelper { public: - BellSoundViewModel(hstring path); + BellSoundViewModel(const Model::IMediaResource& resource); + hstring Path() const { return _resource.Path(); } + bool FileExists() const { return _resource.Ok(); } hstring DisplayPath() const; hstring SubText() const; - VIEW_MODEL_OBSERVABLE_PROPERTY(bool, FileExists, true); - VIEW_MODEL_OBSERVABLE_PROPERTY(hstring, Path); VIEW_MODEL_OBSERVABLE_PROPERTY(bool, ShowDirectory); + + private: + Model::IMediaResource _resource; }; struct ProfileViewModel : ProfileViewModelT, ViewModelHelper @@ -49,12 +52,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation static Windows::Foundation::Collections::IVector BuiltInIcons() noexcept { return _BuiltInIcons; }; ProfileViewModel(const Model::Profile& profile, const Model::CascadiaSettings& settings, const Windows::UI::Core::CoreDispatcher& dispatcher); - Model::TerminalSettings TermSettings() const; + Control::IControlSettings TermSettings() const; void DeleteProfile(); void SetupAppearances(Windows::Foundation::Collections::IObservableVector schemesList); // bell style bits + hstring BellStylePreview() const; bool IsBellStyleFlagSet(const uint32_t flag); void SetBellStyleAudible(winrt::Windows::Foundation::IReference on); void SetBellStyleWindow(winrt::Windows::Foundation::IReference on); @@ -80,7 +84,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation winrt::hstring EvaluatedIcon() const { - return _profile.EvaluatedIcon(); + return _profile.Icon().Resolved(); } Windows::Foundation::IInspectable CurrentIconType() const noexcept { @@ -93,11 +97,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation bool UsingBuiltInIcon() const; bool UsingEmojiIcon() const; bool UsingImageIcon() const; + winrt::hstring IconPath() const { return _profile.Icon().Path(); } + void IconPath(const winrt::hstring& path) + { + Icon(Model::MediaResourceHelper::FromString(path)); + _NotifyChanges(L"Icon", L"IconPath"); + } // starting directory - bool UseParentProcessDirectory(); + hstring CurrentStartingDirectoryPreview() const; + bool UseParentProcessDirectory() const; void UseParentProcessDirectory(const bool useParent); - bool UseCustomStartingDirectory(); // general profile knowledge winrt::guid OriginalProfileGuid() const noexcept; @@ -116,6 +126,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation bool Orphaned() const; hstring TabTitlePreview() const; + hstring AnswerbackMessagePreview() const; til::typed_event DeleteProfileRequested; @@ -182,7 +193,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void _InitializeCurrentBellSounds(); void _PrepareModelForBellSoundModification(); void _MarkDuplicateBellSoundDirectories(); - safe_void_coroutine _CheckBellSoundsExistence(); static Windows::Foundation::Collections::IObservableVector _MonospaceFontList; static Windows::Foundation::Collections::IObservableVector _FontList; static Windows::Foundation::Collections::IVector _BuiltInIcons; diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl index 558787c5d6..e68cc23c52 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl @@ -27,7 +27,7 @@ namespace Microsoft.Terminal.Settings.Editor runtimeclass BellSoundViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged { - String Path; + String Path { get; }; String DisplayPath { get; }; String SubText { get; }; Boolean ShowDirectory { get; }; @@ -52,14 +52,13 @@ namespace Microsoft.Terminal.Settings.Editor runtimeclass ProfileViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged { - Microsoft.Terminal.Settings.Model.TerminalSettings TermSettings { get; }; - event Windows.Foundation.TypedEventHandler DeleteProfileRequested; void SetupAppearances(Windows.Foundation.Collections.IObservableVector schemesList); void SetAcrylicOpacityPercentageValue(Double value); + String BellStylePreview { get; }; Boolean IsBellStyleFlagSet(UInt32 flag); void SetBellStyleAudible(Windows.Foundation.IReference on); void SetBellStyleWindow(Windows.Foundation.IReference on); @@ -87,12 +86,13 @@ namespace Microsoft.Terminal.Settings.Editor IInspectable CurrentPathTranslationStyle; Windows.Foundation.Collections.IObservableVector PathTranslationStyleList { get; }; + String CurrentStartingDirectoryPreview { get; }; + Boolean UseParentProcessDirectory; + Boolean CanDeleteProfile { get; }; Boolean FocusDeleteButton; Boolean IsBaseLayer; ProfileSubPage CurrentPage; - Boolean UseParentProcessDirectory; - Boolean UseCustomStartingDirectory { get; }; AppearanceViewModel DefaultAppearance { get; }; Guid OriginalProfileGuid { get; }; Boolean HasUnfocusedAppearance { get; }; @@ -114,11 +114,13 @@ namespace Microsoft.Terminal.Settings.Editor Boolean UsingBuiltInIcon { get; }; Boolean UsingEmojiIcon { get; }; Boolean UsingImageIcon { get; }; + String IconPath; IInspectable CurrentBuiltInIcon; Windows.Foundation.Collections.IVector BuiltInIcons { get; }; String TabTitlePreview { get; }; + String AnswerbackMessagePreview { get; }; void CreateUnfocusedAppearance(); void DeleteUnfocusedAppearance(); @@ -129,7 +131,7 @@ namespace Microsoft.Terminal.Settings.Editor OBSERVABLE_PROJECTED_PROFILE_SETTING(String, Source); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Guid, ConnectionType); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, Hidden); - OBSERVABLE_PROJECTED_PROFILE_SETTING(String, Icon); + OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Settings.Model.IMediaResource, Icon); OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Settings.Model.CloseOnExitMode, CloseOnExit); OBSERVABLE_PROJECTED_PROFILE_SETTING(String, TabTitle); OBSERVABLE_PROJECTED_PROFILE_SETTING(Windows.Foundation.IReference, TabColor); @@ -145,7 +147,7 @@ namespace Microsoft.Terminal.Settings.Editor OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, SnapOnInput); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AltGrAliasing); OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Settings.Model.BellStyle, BellStyle); - OBSERVABLE_PROJECTED_PROFILE_SETTING(Windows.Foundation.Collections.IVector, BellSound); + OBSERVABLE_PROJECTED_PROFILE_SETTING(Windows.Foundation.Collections.IVector, BellSound); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, Elevate); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, ReloadEnvironmentVariables); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, RightClickContextMenu); diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.cpp b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.cpp index 29b9f89108..64bdecd1d4 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.cpp +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.cpp @@ -29,6 +29,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation const auto args = e.Parameter().as(); _Profile = args.Profile(); _windowRoot = args.WindowRoot(); + + TraceLoggingWrite( + g_hTerminalSettingsEditorProvider, + "NavigatedToPage", + TraceLoggingDescription("Event emitted when the user navigates to a page in the settings UI"), + TraceLoggingValue("profile.advanced", "PageId", "The identifier of the page that was navigated to"), + TraceLoggingValue(_Profile.IsBaseLayer(), "IsProfileDefaults", "If the modified profile is the profile.defaults object"), + TraceLoggingValue(static_cast(_Profile.Guid()), "ProfileGuid", "The guid of the profile that was navigated to"), + TraceLoggingValue(_Profile.Source().c_str(), "ProfileSource", "The source of the profile that was navigated to"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } void Profiles_Advanced::OnNavigatedFrom(const NavigationEventArgs& /*e*/) diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml index e46e8179a3..8f4d3ae7c0 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml @@ -32,8 +32,6 @@ @@ -122,7 +121,7 @@ - + @@ -155,7 +154,7 @@ diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.cpp b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.cpp index cdf1e851ae..c338925ee3 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.cpp +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.cpp @@ -28,7 +28,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation if (!_previewControl) { - const auto settings = _Profile.TermSettings(); + const auto settings = winrt::get_self(_Profile)->TermSettings(); _previewConnection->DisplayPowerlineGlyphs(_Profile.DefaultAppearance().HasPowerlineCharacters()); _previewControl = Control::TermControl(settings, settings, *_previewConnection); _previewControl.IsEnabled(false); @@ -44,6 +44,19 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // The Appearances object handles updating the values in the settings UI, but // we still need to listen to the changes here just to update the preview control _AppearanceViewModelChangedRevoker = _Profile.DefaultAppearance().PropertyChanged(winrt::auto_revoke, { this, &Profiles_Appearance::_onProfilePropertyChanged }); + + TraceLoggingWrite( + g_hTerminalSettingsEditorProvider, + "NavigatedToPage", + TraceLoggingDescription("Event emitted when the user navigates to a page in the settings UI"), + TraceLoggingValue("profile.appearance", "PageId", "The identifier of the page that was navigated to"), + TraceLoggingValue(_Profile.IsBaseLayer(), "IsProfileDefaults", "If the modified profile is the profile.defaults object"), + TraceLoggingValue(static_cast(_Profile.Guid()), "ProfileGuid", "The guid of the profile that was navigated to"), + TraceLoggingValue(_Profile.Source().c_str(), "ProfileSource", "The source of the profile that was navigated to"), + TraceLoggingValue(_Profile.DefaultAppearance().BackgroundImageSettingsVisible(), "HasBackgroundImage", "If the profile has a background image defined"), + TraceLoggingValue(_Profile.HasUnfocusedAppearance(), "HasUnfocusedAppearance", "If the profile has an unfocused appearance defined"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } void Profiles_Appearance::OnNavigatedFrom(const NavigationEventArgs& /*e*/) @@ -54,6 +67,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void Profiles_Appearance::CreateUnfocusedAppearance_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*e*/) { + TraceLoggingWrite( + g_hTerminalSettingsEditorProvider, + "CreateUnfocusedAppearance", + TraceLoggingDescription("Event emitted when the user creates an unfocused appearance for a profile"), + TraceLoggingValue(_Profile.IsBaseLayer(), "IsProfileDefaults", "If the modified profile is the profile.defaults object"), + TraceLoggingValue(static_cast(_Profile.Guid()), "ProfileGuid", "The guid of the profile that was navigated to"), + TraceLoggingValue(_Profile.Source().c_str(), "ProfileSource", "The source of the profile that was navigated to"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); + _Profile.CreateUnfocusedAppearance(); } @@ -66,11 +89,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { if (!_updatePreviewControl) { - _updatePreviewControl = std::make_shared>( + _updatePreviewControl = std::make_shared>( winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), - std::chrono::milliseconds{ 100 }, + til::throttled_func_options{ + .delay = std::chrono::milliseconds{ 100 }, + .debounce = true, + .trailing = true, + }, [this]() { - const auto settings = _Profile.TermSettings(); + const auto settings = winrt::get_self(_Profile)->TermSettings(); _previewConnection->DisplayPowerlineGlyphs(_Profile.DefaultAppearance().HasPowerlineCharacters()); _previewControl.UpdateControlSettings(settings, settings); }); diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h index 27c8e455a5..4e52248f43 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h @@ -33,7 +33,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation winrt::com_ptr _previewConnection{ nullptr }; Microsoft::Terminal::Control::TermControl _previewControl{ nullptr }; - std::shared_ptr> _updatePreviewControl; + std::shared_ptr> _updatePreviewControl; Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker; Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _AppearanceViewModelChangedRevoker; Editor::IHostedInWindow _windowRoot; diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.xaml index be2933fbe3..ea991e3b23 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.xaml +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.xaml @@ -50,27 +50,29 @@ - + - + + BorderBrush="{ThemeResource ControlStrongStrokeColorDefaultBrush}" + BorderThickness="1" + CornerRadius="{StaticResource ControlCornerRadius}" /> + CornerRadius="{StaticResource OverlayCornerRadius}"> @@ -204,25 +206,27 @@ + Style="{StaticResource TextBlockSubtitleStyle}" /> diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Terminal.cpp b/src/cascadia/TerminalSettingsEditor/Profiles_Terminal.cpp index 7d42d64d21..fd8899dff1 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Terminal.cpp +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Terminal.cpp @@ -22,6 +22,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void Profiles_Terminal::OnNavigatedTo(const NavigationEventArgs& e) { _Profile = e.Parameter().as(); + + TraceLoggingWrite( + g_hTerminalSettingsEditorProvider, + "NavigatedToPage", + TraceLoggingDescription("Event emitted when the user navigates to a page in the settings UI"), + TraceLoggingValue("profile.terminal", "PageId", "The identifier of the page that was navigated to"), + TraceLoggingValue(_Profile.IsBaseLayer(), "IsProfileDefaults", "If the modified profile is the profile.defaults object"), + TraceLoggingValue(static_cast(_Profile.Guid()), "ProfileGuid", "The guid of the profile that was navigated to"), + TraceLoggingValue(_Profile.Source().c_str(), "ProfileSource", "The source of the profile that was navigated to"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } void Profiles_Terminal::OnNavigatedFrom(const NavigationEventArgs& /*e*/) diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Terminal.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Terminal.xaml index b568aebbaa..dfe5954d4f 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Terminal.xaml +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Terminal.xaml @@ -26,8 +26,6 @@ diff --git a/src/cascadia/TerminalSettingsEditor/Rendering.cpp b/src/cascadia/TerminalSettingsEditor/Rendering.cpp index 5ce0d1285e..836d63ca20 100644 --- a/src/cascadia/TerminalSettingsEditor/Rendering.cpp +++ b/src/cascadia/TerminalSettingsEditor/Rendering.cpp @@ -17,5 +17,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void Rendering::OnNavigatedTo(const NavigationEventArgs& e) { _ViewModel = e.Parameter().as(); + + TraceLoggingWrite( + g_hTerminalSettingsEditorProvider, + "NavigatedToPage", + TraceLoggingDescription("Event emitted when the user navigates to a page in the settings UI"), + TraceLoggingValue("rendering", "PageId", "The identifier of the page that was navigated to"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } } diff --git a/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw index 80b052f11d..05ab7e9f23 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/de-DE/Resources.resw @@ -684,6 +684,10 @@ Aktionen Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. + + Erweiterungen + Header for the "extensions" menu item. This navigates to a page that lets you see and modify extensions for the app. + Hintergrunddeckkraft Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. @@ -942,6 +946,10 @@ Immer An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. + + Automatisch + An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table if and only if high contrast mode is enabled. + Balken ( ┃ ) {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. @@ -1728,6 +1736,14 @@ Bereich automatisch mit dem Mauszeiger fokussieren Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. + + Passen Sie die Schriftgröße des Terminals an, indem Sie die STRG-TASTE gedrückt halten und scrollen + Header for a control to toggle font size changes with scrolling. When enabled, holding the Ctrl key while scrolling will increase or decrease the terminal font size. + + + Passen Sie die Deckkraft des Terminals an, indem Sie scrollen, während Sie die STRG- und UMSCHALTTASTE gedrückt halten + Header for a control to toggle opacity changes with scrolling. When enabled, holding the Ctrl and Shift keys while scrolling will increase or decrease the window opacity. + Bereichsanimationen Header for a control to toggle animations on panes. "Enabled" value enables the animations. @@ -1908,10 +1924,26 @@ Warnen, wenn „Dienst für Bildschirmtastatur und Handschrifteingabefeld“ deaktiviert ist - Warnung beim Versuch, mehr als 5 KiB Zeichen einzufügen + Warnung beim Einfügen von mehr als 5 KiB - Warnung beim Versuch, ein „Zeilenumbruch“-Zeichen einzufügen + Warnung beim Einfügen von Zeilenumbrüchen + + + Wenn Ihre Shell den Modus „Einfügen in Klammern“ nicht unterstützt, wird aus Sicherheitsgründen empfohlen, dies auf „Immer“ festzulegen. + "bracketed paste" is a technical term referring to escape sequences. "Always" is a setting, as in "Always VS Never". + + + Nur, wenn „Klammer-Einfügen“ deaktiviert ist + "bracketed paste" is a technical term referring to terminal escape sequences. + + + Immer + As in: Always VS Never + + + Nie + As in: Always VS Never Windows-Terminal wird im portablen Modus ausgeführt. @@ -2101,7 +2133,7 @@ Header for a control that adds any remaining profiles to the new tab menu. - Fügen Sie eine Gruppe von Profilen hinzu, die mindestens einer der definierten Eigenschaften entsprechen. + Fügen Sie eine Gruppe von Profilen hinzu, die mindestens einer der definierten Eigenschaften für reguläre Ausdrücke entsprechen. Additional information for a control that adds a terminal profile matcher to the new tab menu. Presented near "NewTabMenu_AddMatchProfiles". @@ -2117,15 +2149,15 @@ Header for a control that adds a folder to the new tab menu. - Profilname + Profilname (regulärer Ausdruck) Header for a text box used to define a regex for the names of profiles to add. - Profilquelle + Profilquelle (regulärer Ausdruck) Header for a text box used to define a regex for the sources of profiles to add. - Befehlszeile + Befehlszeile (regulärer Ausdruck) Header for a text box used to define a regex for the commandlines of profiles to add. @@ -2312,6 +2344,10 @@ MSYS2 (C:\ -> /c) {Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting. + + MinGW (C:\ -> C:/) + {Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting. + Profil wird nicht mehr erkannt @@ -2336,4 +2372,91 @@ Diese Option wird durch eine Unternehmensrichtlinie verwaltet und kann hier nicht geändert werden. This is displayed in concordance with Globals_StartOnUserLogin if the enterprise administrator has taken control of this setting. + + Aktive Erweiterungen + + + Geänderte Profile + + + Hinzugefügte Profile + + + Hinzugefügte Farbschemas + + + Weitere Informationen zu Erweiterungen + + + Zum Profil navigieren + + + Zum Profil navigieren + + + Zum Farbschema navigieren + + + Zum Farbschema navigieren + + + Aktueller Benutzer + Label for the installation scope of an extension. + + + Alle Benutzer + Label for the installation scope of an extension + + + Bereich + Header for the installation scope of the extension + + + NEU + Text is used on an info badge for new navigation items. Must be all caps. + + + Weitere Informationen zu regulären Ausdrücken + + + Keine + Text displayed when the background image path is not defined. + + + Keine + Text displayed when the answerback message is not defined. + + + Auf Standardeinstellungen zurücksetzen + + + Cache löschen + + + Im Cache werden Daten gespeichert, die mit gespeicherten Sitzungen und automatisch generierten Profilen zusammenhängen. + + + Zurücksetzen + + + Löschen + + + Diese Aktion wird sofort angewendet und kann nicht rückgängig gemacht werden. + + + Diese Aktion wird sofort angewendet und kann nicht rückgängig gemacht werden. + + + Möchten Sie Ihre Einstellungen zurücksetzen? + + + Möchten Sie den Cache wirklich löschen? + + + Ja, meine Einstellungen zurücksetzen + + + Ja, Cache löschen + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index 5d98ce728e..7b3ebe85a6 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -684,6 +684,10 @@ Actions Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. + + Extensions + Header for the "extensions" menu item. This navigates to a page that lets you see and modify extensions for the app. + Background opacity Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. @@ -942,6 +946,10 @@ Always An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. + + Automatic + An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table if and only if high contrast mode is enabled. + Bar ( ┃ ) {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. @@ -1728,6 +1736,14 @@ Automatically focus pane on mouse hover Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. + + Adjust terminal font size by scrolling while holding the Ctrl key + Header for a control to toggle font size changes with scrolling. When enabled, holding the Ctrl key while scrolling will increase or decrease the terminal font size. + + + Adjust terminal opacity by scrolling while holding the Ctrl and Shift keys + Header for a control to toggle opacity changes with scrolling. When enabled, holding the Ctrl and Shift keys while scrolling will increase or decrease the window opacity. + Pane animations Header for a control to toggle animations on panes. "Enabled" value enables the animations. @@ -1908,10 +1924,26 @@ Warn when "Touch Keyboard and Handwriting Panel Service" is disabled - Warn when trying to paste more than 5 KiB of characters + Warn when pasting more than 5 KiB - Warn when trying to paste a "new line" character + Warn when pasting newlines + + + If your shell does not support "bracketed paste" mode, we recommend setting this to "Always" for security reasons. + "bracketed paste" is a technical term referring to escape sequences. "Always" is a setting, as in "Always VS Never". + + + Only if "bracketed paste" is disabled + "bracketed paste" is a technical term referring to terminal escape sequences. + + + Always + As in: Always VS Never + + + Never + As in: Always VS Never Windows Terminal is running in portable mode. @@ -2105,7 +2137,7 @@ Header for a control that adds any remaining profiles to the new tab menu. - Add a group of profiles that match at least one of the defined properties + Add a group of profiles that match at least one of the defined regular expression properties Additional information for a control that adds a terminal profile matcher to the new tab menu. Presented near "NewTabMenu_AddMatchProfiles". @@ -2121,15 +2153,15 @@ Header for a control that adds a folder to the new tab menu. - Profile name + Profile name (regular expression) Header for a text box used to define a regex for the names of profiles to add. - Profile source + Profile source (regular expression) Header for a text box used to define a regex for the sources of profiles to add. - Commandline + Commandline (regular expression) Header for a text box used to define a regex for the commandlines of profiles to add. @@ -2316,6 +2348,10 @@ MSYS2 (C:\ -> /c) {Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting. + + MinGW (C:\ -> C:/) + {Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting. + Profile no longer detected @@ -2340,4 +2376,91 @@ This option is managed by enterprise policy and cannot be changed here. This is displayed in concordance with Globals_StartOnUserLogin if the enterprise administrator has taken control of this setting. - \ No newline at end of file + + Active Extensions + + + Modified Profiles + + + Added Profiles + + + Added Color Schemes + + + Learn more about extensions + + + Navigate to profile + + + Navigate to profile + + + Navigate to color scheme + + + Navigate to color scheme + + + Current User + Label for the installation scope of an extension. + + + All Users + Label for the installation scope of an extension + + + Scope + Header for the installation scope of the extension + + + NEW + Text is used on an info badge for new navigation items. Must be all caps. + + + Learn more about regular expressions + + + None + Text displayed when the background image path is not defined. + + + None + Text displayed when the answerback message is not defined. + + + Reset to default settings + + + Clear cache + + + The cache stores data related to saved sessions and automatically generated profiles. + + + Reset + + + Clear + + + This action is applied immediately and cannot be undone. + + + This action is applied immediately and cannot be undone. + + + Are you sure you want to reset your settings? + + + Are you sure you want to clear your cache? + + + Yes, reset my settings + + + Yes, clear the cache + + diff --git a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw index f03362e5b4..92b9031215 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw @@ -684,6 +684,10 @@ Acciones Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. + + Extensiones + Header for the "extensions" menu item. This navigates to a page that lets you see and modify extensions for the app. + Opacidad del fondo Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. @@ -942,6 +946,10 @@ Siempre An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. + + Automático + An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table if and only if high contrast mode is enabled. + Barra ( ┃ ) {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. @@ -1728,6 +1736,14 @@ Centrarse automáticamente en el panel al pasar el mouse Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. + + Ajuste el tamaño de fuente del terminal desplazándose mientras mantiene presionada la tecla Ctrl + Header for a control to toggle font size changes with scrolling. When enabled, holding the Ctrl key while scrolling will increase or decrease the terminal font size. + + + Ajuste la opacidad del terminal desplazándose mientras mantiene presionadas las teclas CTRL y Mayús + Header for a control to toggle opacity changes with scrolling. When enabled, holding the Ctrl and Shift keys while scrolling will increase or decrease the window opacity. + Animaciones de panel Header for a control to toggle animations on panes. "Enabled" value enables the animations. @@ -1908,10 +1924,26 @@ Advertir cuando el servicio "Panel de escritura a mano y teclado táctil" está deshabilitado - Advertir al intentar pegar más de 5 KiB de caracteres + Advertir al pegar más de 5 KiB - Advertir al intentar pegar un carácter de "nueva línea" + Advertir al pegar nuevas líneas + + + Si tu shell no admite el modo de "pegar entre corchetes", le recomendamos configurarlo en "Siempre" por motivos de seguridad. + "bracketed paste" is a technical term referring to escape sequences. "Always" is a setting, as in "Always VS Never". + + + Solo si "pegar entre corchetes" está deshabilitado + "bracketed paste" is a technical term referring to terminal escape sequences. + + + Siempre + As in: Always VS Never + + + Nunca + As in: Always VS Never Terminal Windows se ejecuta en modo portátil. @@ -2101,7 +2133,7 @@ Header for a control that adds any remaining profiles to the new tab menu. - Agregar un grupo de perfiles que coincidan con al menos una de las propiedades definidas + Agregar un grupo de perfiles que coincidan al menos con una de las propiedades de expresión regular definidas Additional information for a control that adds a terminal profile matcher to the new tab menu. Presented near "NewTabMenu_AddMatchProfiles". @@ -2117,15 +2149,15 @@ Header for a control that adds a folder to the new tab menu. - Nombre del perfil + Nombre del perfil (expresión regular) Header for a text box used to define a regex for the names of profiles to add. - Origen del perfil + Origen del perfil (expresión regular) Header for a text box used to define a regex for the sources of profiles to add. - Commandline + Línea de comandos (expresión regular) Header for a text box used to define a regex for the commandlines of profiles to add. @@ -2312,6 +2344,10 @@ MSYS2 (C:\ -> /c) {Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting. + + MinGW (C:\ -> C:/) + {Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting. + El perfil ya no se ha detectado @@ -2336,4 +2372,91 @@ Esta opción está administrada por una directiva de empresa y no se puede cambiar aquí. This is displayed in concordance with Globals_StartOnUserLogin if the enterprise administrator has taken control of this setting. + + Extensiones activas + + + Perfiles modificados + + + Perfiles agregados + + + Combinaciones de colores agregadas + + + Más información acerca de las extensiones + + + Navegar al perfil + + + Navegar al perfil + + + Ir a la combinación de colores + + + Ir a la combinación de colores + + + Usuario actual + Label for the installation scope of an extension. + + + Todos los usuarios + Label for the installation scope of an extension + + + Ámbito + Header for the installation scope of the extension + + + NUEVO + Text is used on an info badge for new navigation items. Must be all caps. + + + Más información sobre las expresiones regulares + + + Ninguno + Text displayed when the background image path is not defined. + + + Ninguno + Text displayed when the answerback message is not defined. + + + Restablecer a la configuración predeterminada + + + Borrar caché + + + La caché guarda datos relacionados con las sesiones guardadas y los perfiles generados automáticamente. + + + Restablecer + + + Despejado + + + Esta acción se aplica inmediatamente y no se puede deshacer. + + + Esta acción se aplica inmediatamente y no se puede deshacer. + + + ¿Estás seguro de que quieres restablecer la configuración? + + + ¿Seguro que quieres borrar la memoria caché? + + + Sí, restablecer mi configuración + + + Sí, borrar la caché + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw index c491860f73..ae3461dd1e 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw @@ -684,6 +684,10 @@ Actions Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. + + Extensions + Header for the "extensions" menu item. This navigates to a page that lets you see and modify extensions for the app. + Opacité de l’arrière-plan Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. @@ -942,6 +946,10 @@ Toujours An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. + + Automatique + An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table if and only if high contrast mode is enabled. + Barre ( ┃ ) {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. @@ -1728,6 +1736,14 @@ Mise au point automatique du volet au survol de la souris Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. + + Ajustez la taille de police du terminal en faisant défiler tout en maintenant la touche Ctrl enfoncée + Header for a control to toggle font size changes with scrolling. When enabled, holding the Ctrl key while scrolling will increase or decrease the terminal font size. + + + Ajustez l’opacité du terminal en faisant défiler tout en maintenant les touches Ctrl et Maj enfoncées + Header for a control to toggle opacity changes with scrolling. When enabled, holding the Ctrl and Shift keys while scrolling will increase or decrease the window opacity. + Animations du volet Header for a control to toggle animations on panes. "Enabled" value enables the animations. @@ -1908,10 +1924,26 @@ Avertir lorsque le « service clavier tactile et volet d’écriture manuscrite » est désactivé - Avertissement lors de la tentative de collage de plus de 5 Kio de caractères + Avertir lors du collage de plus de 5 KiO - Avertir lors de la tentative de collage d’un caractère « nouvelle ligne » + Avertir lors du collage de nouvelles lignes + + + Si votre interpréteur de commandes ne prend pas en charge le mode « collage entre crochets », nous vous recommandons de définir ce paramètre sur « Toujours » pour des raisons de sécurité. + "bracketed paste" is a technical term referring to escape sequences. "Always" is a setting, as in "Always VS Never". + + + Uniquement si le collage entre crochets est désactivé + "bracketed paste" is a technical term referring to terminal escape sequences. + + + Toujours + As in: Always VS Never + + + Jamais + As in: Always VS Never Terminal Windows s’exécute en mode portable. @@ -2101,7 +2133,7 @@ Header for a control that adds any remaining profiles to the new tab menu. - Ajouter un groupe de profils qui correspondent à au moins une des propriétés définies + Ajouter un groupe de profils qui correspondent à au moins une des propriétés d’expression régulière définies Additional information for a control that adds a terminal profile matcher to the new tab menu. Presented near "NewTabMenu_AddMatchProfiles". @@ -2117,15 +2149,15 @@ Header for a control that adds a folder to the new tab menu. - Nom de profil + Nom du profil (expression régulière) Header for a text box used to define a regex for the names of profiles to add. - Source du profil + Source du profil (expression régulière) Header for a text box used to define a regex for the sources of profiles to add. - Ligne de commande + Ligne de commande (expression régulière) Header for a text box used to define a regex for the commandlines of profiles to add. @@ -2312,6 +2344,10 @@ MSYS2 (C:\ -> /c) {Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting. + + MinGW (C:\ -> C:/) + {Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting. + Le profil n’est plus détecté @@ -2336,4 +2372,91 @@ Cette option est gérée par la stratégie d’entreprise et ne peut pas être modifiée ici. This is displayed in concordance with Globals_StartOnUserLogin if the enterprise administrator has taken control of this setting. + + Extensions actives + + + Profils modifiés + + + Profils ajoutés + + + Ajout de modèles de couleurs + + + En savoir plus sur les extensions + + + Accéder au profil + + + Accéder au profil + + + Accéder au modèle de couleurs + + + Accéder au modèle de couleurs + + + Utilisateur actuel + Label for the installation scope of an extension. + + + All Users + Label for the installation scope of an extension + + + Étendue + Header for the installation scope of the extension + + + NOUVEAU + Text is used on an info badge for new navigation items. Must be all caps. + + + En savoir plus sur les expressions régulières + + + Aucun + Text displayed when the background image path is not defined. + + + Aucun + Text displayed when the answerback message is not defined. + + + Revenir aux paramètres par défaut + + + Effacer le cache + + + Le cache stocke les données relatives aux sessions enregistrées et aux profils générés automatiquement. + + + Réinitialiser + + + Dégagé + + + Cette action est appliquée immédiatement et ne peut pas être annulée. + + + Cette action est appliquée immédiatement et ne peut pas être annulée. + + + Voulez-vous vraiment réinitialiser vos paramètres ? + + + Voulez-vous vraiment effacer votre cache du navigateur ? + + + Oui, réinitialisez mes paramètres + + + Oui, vider le cache + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw index 1c69bc653f..e0d3fc917c 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw @@ -684,6 +684,10 @@ Azioni Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. + + Estensioni + Header for the "extensions" menu item. This navigates to a page that lets you see and modify extensions for the app. + Opacità sfondo Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. @@ -942,6 +946,10 @@ Sempre An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. + + Automatico + An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table if and only if high contrast mode is enabled. + Bar ( ┃ ) {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. @@ -1728,6 +1736,14 @@ Riquadro di messa a fuoco automatica al passaggio del mouse Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. + + Regola le dimensioni del carattere del terminale scorrendo tenendo premuto il tasto CTRL + Header for a control to toggle font size changes with scrolling. When enabled, holding the Ctrl key while scrolling will increase or decrease the terminal font size. + + + Regola l'opacità del terminale scorrendo mentre tieni premuti i tasti CTRL e MAIUSC + Header for a control to toggle opacity changes with scrolling. When enabled, holding the Ctrl and Shift keys while scrolling will increase or decrease the window opacity. + Riquadro animazioni Header for a control to toggle animations on panes. "Enabled" value enables the animations. @@ -1908,10 +1924,26 @@ Avvisa quando il servizio "Tastiera virtuale e pannello per la grafia" è disabilitato - Avvisa quando si tenta di incollare più di 5 KiB di caratteri + Avvisa quando si incolla più di 5 KiB - Avvisa quando si prova a incollare un carattere "nuova riga" + Avvisa quando si incollano le nuove righe + + + Se la shell non supporta la modalità "incolla tra parentesi quadre", è consigliabile impostarla su "Sempre" per motivi di sicurezza. + "bracketed paste" is a technical term referring to escape sequences. "Always" is a setting, as in "Always VS Never". + + + Solo se "incolla tra parentesi quadre" è disabilitato + "bracketed paste" is a technical term referring to terminal escape sequences. + + + Sempre + As in: Always VS Never + + + Mai + As in: Always VS Never Terminale Windows è in esecuzione in modalità portatile. @@ -2101,7 +2133,7 @@ Header for a control that adds any remaining profiles to the new tab menu. - Aggiungi un gruppo di profili che corrispondono ad almeno una delle proprietà definite + Aggiungi un gruppo di profili che corrispondono ad almeno una delle proprietà dell'espressione regolare definita Additional information for a control that adds a terminal profile matcher to the new tab menu. Presented near "NewTabMenu_AddMatchProfiles". @@ -2117,15 +2149,15 @@ Header for a control that adds a folder to the new tab menu. - Nome profilo + Nome del profilo (espressione regolare) Header for a text box used to define a regex for the names of profiles to add. - Origine profilo + Origine profilo (espressione regolare) Header for a text box used to define a regex for the sources of profiles to add. - Commandline + Riga di comando (espressione regolare) Header for a text box used to define a regex for the commandlines of profiles to add. @@ -2312,6 +2344,10 @@ MSYS2 (C:\ -> /c) {Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting. + + MinGW (C:\ -> C:/) + {Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting. + Profilo non più rilevato @@ -2336,4 +2372,91 @@ Questa opzione è gestita da criteri aziendali e non può essere modificata qui. This is displayed in concordance with Globals_StartOnUserLogin if the enterprise administrator has taken control of this setting. + + Estensioni attive + + + Profili modificati + + + Profili aggiunti + + + Combinazioni colori aggiunte + + + Altre informazioni sulle estensioni + + + Passa al profilo + + + Passa al profilo + + + Passa alla combinazione colori + + + Passa alla combinazione colori + + + Utente corrente + Label for the installation scope of an extension. + + + All Users + Label for the installation scope of an extension + + + Ambito + Header for the installation scope of the extension + + + NUOVO + Text is used on an info badge for new navigation items. Must be all caps. + + + Scopri di più sulle espressioni regolari + + + Nessuno + Text displayed when the background image path is not defined. + + + Nessuno + Text displayed when the answerback message is not defined. + + + Ripristina le impostazioni predefinite + + + Cancella cache + + + La cache archivia i dati relativi alle sessioni salvate e ai profili generati automaticamente. + + + Reimposta + + + Sereno + + + Questa azione viene applicata immediatamente e non può essere annullata. + + + Questa azione viene applicata immediatamente e non può essere annullata. + + + Vuoi reimpostare le impostazioni? + + + Vuoi cancellare la cache? + + + Sì, reimposta le mie impostazioni + + + Sì, cancella la cache + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw index 32c791239a..2aac58e1eb 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw @@ -684,6 +684,10 @@ 操作 Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. + + 拡張機能 + Header for the "extensions" menu item. This navigates to a page that lets you see and modify extensions for the app. + 背景の不透明度 Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. @@ -942,6 +946,10 @@ 常時 An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. + + 自動 + An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table if and only if high contrast mode is enabled. + バー ( ┃ ) {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. @@ -1728,6 +1736,14 @@ マウスをポイントしたときにフォーカス ウィンドウを自動的に表示する Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. + + Ctrl キーを押しながらスクロールして、ターミナルのフォント サイズを調整します + Header for a control to toggle font size changes with scrolling. When enabled, holding the Ctrl key while scrolling will increase or decrease the terminal font size. + + + Ctrl キーと Shift キーを押しながらスクロールして、ターミナルの不透明度を調整します + Header for a control to toggle opacity changes with scrolling. When enabled, holding the Ctrl and Shift keys while scrolling will increase or decrease the window opacity. + ウィンドウのアニメーション Header for a control to toggle animations on panes. "Enabled" value enables the animations. @@ -1908,10 +1924,26 @@ [タッチ キーボードと手書きパネル サービス] が無効になっている場合に警告する - 5 KiB を超える文字を貼り付けようとしたときに警告する + 5 KiB を超える貼り付け時に警告する - "改行" 文字を貼り付けようとしたときに警告する + 改行を貼り付ける際に警告する + + + シェルが ”角かっこで囲まれた貼り付け” モードをサポートしていない場合は、セキュリティ上の理由からこれを [常に] に設定することをお勧めします。 + "bracketed paste" is a technical term referring to escape sequences. "Always" is a setting, as in "Always VS Never". + + + "角かっこで囲まれた貼り付け" が無効になっている場合のみ + "bracketed paste" is a technical term referring to terminal escape sequences. + + + 常時 + As in: Always VS Never + + + なし + As in: Always VS Never Windows ターミナルはポータブル モードで実行されています。 @@ -2101,7 +2133,7 @@ Header for a control that adds any remaining profiles to the new tab menu. - 定義されたプロパティの少なくとも 1 つに一致するプロファイルのグループを追加します + 定義された正規表現プロパティの少なくとも 1 つに一致するプロファイルのグループを追加します Additional information for a control that adds a terminal profile matcher to the new tab menu. Presented near "NewTabMenu_AddMatchProfiles". @@ -2117,15 +2149,15 @@ Header for a control that adds a folder to the new tab menu. - プロファイル名 + プロファイル名 (正規表現) Header for a text box used to define a regex for the names of profiles to add. - プロファイル ソース + プロファイル ソース (正規表現) Header for a text box used to define a regex for the sources of profiles to add. - Commandline + コマンドライン (正規表現) Header for a text box used to define a regex for the commandlines of profiles to add. @@ -2312,6 +2344,10 @@ MSYS2 (C:\ -> /c) {Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting. + + MinGW (C:\ -> C:/) + {Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting. + プロファイルは検出されなくなりました @@ -2336,4 +2372,91 @@ このオプションはエンタープライズ ポリシーによって管理されているため、ここで変更することはできません。 This is displayed in concordance with Globals_StartOnUserLogin if the enterprise administrator has taken control of this setting. + + アクティブな拡張機能 + + + 変更されたプロファイル + + + 追加されたプロファイル + + + 追加された配色 + + + 拡張機能に関する詳細情報 + + + プロファイルに移動 + + + プロファイルに移動 + + + 配色に移動 + + + 配色に移動 + + + 現在のユーザー + Label for the installation scope of an extension. + + + All Users + Label for the installation scope of an extension + + + スコープ + Header for the installation scope of the extension + + + 新規 + Text is used on an info badge for new navigation items. Must be all caps. + + + 正規表現について詳細を表示する + + + なし + Text displayed when the background image path is not defined. + + + なし + Text displayed when the answerback message is not defined. + + + 初期設定にリセット + + + キャッシュのクリア + + + キャッシュには、保存されたセッションと自動生成されたプロファイルに関連するデータが格納されます。 + + + リセット + + + キャンセル + + + この操作はすぐに適用され、元に戻すことはできません。 + + + この操作はすぐに適用され、元に戻すことはできません。 + + + 設定をリセットしますか? + + + キャッシュをクリアしますか? + + + はい、設定をリセットします + + + はい、キャッシュをクリアします + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw index 0e5577a351..d1140bfd3a 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw @@ -684,6 +684,10 @@ 작업 Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. + + 확장 + Header for the "extensions" menu item. This navigates to a page that lets you see and modify extensions for the app. + 배경 불투명도 Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. @@ -942,6 +946,10 @@ 항상 An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. + + 자동 + An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table if and only if high contrast mode is enabled. + 막대( ┃ ) {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. @@ -1728,6 +1736,14 @@ 마우스를 위에 가져다 대면 자동으로 포커스 창이 실행됨 Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. + + Ctrl 키를 누른 상태에서 스크롤하여 터미널 글꼴 크기 조정 + Header for a control to toggle font size changes with scrolling. When enabled, holding the Ctrl key while scrolling will increase or decrease the terminal font size. + + + Ctrl 키와 Shift 키를 누른 상태에서 스크롤하여 터미널 불투명도 조정 + Header for a control to toggle opacity changes with scrolling. When enabled, holding the Ctrl and Shift keys while scrolling will increase or decrease the window opacity. + 창 애니메이션 Header for a control to toggle animations on panes. "Enabled" value enables the animations. @@ -1908,10 +1924,26 @@ "터치 키보드 및 필기 패널 서비스"를 사용할 수 없는 경우 경고 - 5KiB가 넘는 문자를 붙여넣으려고 할 때 경고 + 붙여넣기가 5KiB를 초과하는 경우 경고 - "줄 바꾸기" 문자를 붙여넣을 때 경고 + 붙여넣기에 줄 바꿈이 포함된 경우 경고 + + + 셸이 "대괄호로 묶은 붙여넣기" 모드를 지원하지 않는 경우, 보안상의 이유로 이 설정을 "항상"으로 변경하는 것이 좋습니다. + "bracketed paste" is a technical term referring to escape sequences. "Always" is a setting, as in "Always VS Never". + + + "대괄호로 묶은 붙여넣기"를 사용할 수 없는 경우에만 + "bracketed paste" is a technical term referring to terminal escape sequences. + + + 항상 + As in: Always VS Never + + + 사용 안 함 + As in: Always VS Never Windows 터미널이 이식 가능 모드에서 실행되고 있습니다. @@ -2101,7 +2133,7 @@ Header for a control that adds any remaining profiles to the new tab menu. - 정의된 속성 중 하나 이상과 일치하는 프로필 그룹을 추가합니다. + 정의된 정규식 속성 중 하나 이상과 일치하는 프로필 그룹을 추가합니다. Additional information for a control that adds a terminal profile matcher to the new tab menu. Presented near "NewTabMenu_AddMatchProfiles". @@ -2117,15 +2149,15 @@ Header for a control that adds a folder to the new tab menu. - 프로필 이름 + 프로필 이름(정규식) Header for a text box used to define a regex for the names of profiles to add. - 프로필 원본 + 프로필 원본(정규식) Header for a text box used to define a regex for the sources of profiles to add. - 명령줄 + 명령줄(정규식) Header for a text box used to define a regex for the commandlines of profiles to add. @@ -2312,6 +2344,10 @@ MSYS2(C:\ -> /c) {Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting. + + MinGW(C:\ -> C:/) + {Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting. + 프로필이 더 이상 검색되지 않음 @@ -2336,4 +2372,91 @@ 이 옵션은 엔터프라이즈 정책에 의해 관리되므로 여기에서 변경할 수 없습니다. This is displayed in concordance with Globals_StartOnUserLogin if the enterprise administrator has taken control of this setting. + + 활성 확장 + + + 수정된 프로필 + + + 추가된 프로필 + + + 추가된 색 구성표 + + + 확장에 대한 자세한 정보 + + + 프로필로 이동 + + + 프로필로 이동 + + + 색 구성표로 이동 + + + 색 구성표로 이동 + + + 현재 사용자 + Label for the installation scope of an extension. + + + All Users + Label for the installation scope of an extension + + + 범위 + Header for the installation scope of the extension + + + 새 기능 + Text is used on an info badge for new navigation items. Must be all caps. + + + 정규식에 대한 자세한 정보 + + + 없음 + Text displayed when the background image path is not defined. + + + 없음 + Text displayed when the answerback message is not defined. + + + 기본 설정으로 초기화 + + + 캐시 지우기 + + + 캐시는 저장된 세션과 관련된 데이터 및 자동으로 생성된 프로필을 저장합니다. + + + 다시 설정 + + + 취소 + + + 이 작업은 즉시 적용되며 실행 취소할 수 없습니다. + + + 이 작업은 즉시 적용되며 실행 취소할 수 없습니다. + + + 설정을 초기화하시겠습니까? + + + 캐시를 지우시겠습니까? + + + 예, 설정을 초기화합니다. + + + 예, 캐시를 지웁니다. + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw index e3ab70e74c..2a48f26f7e 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw @@ -684,6 +684,10 @@ Ações Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. + + Extensões + Header for the "extensions" menu item. This navigates to a page that lets you see and modify extensions for the app. + Opacidade da tela de fundo Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. @@ -942,6 +946,10 @@ Sempre An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. + + Automático + An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table if and only if high contrast mode is enabled. + Barra ( ┃ ) {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. @@ -1728,6 +1736,14 @@ Focalizar painel automaticamente ao passar o mouse Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. + + Ajustar o tamanho da fonte do terminal rolando enquanto mantém a tecla Ctrl pressionada + Header for a control to toggle font size changes with scrolling. When enabled, holding the Ctrl key while scrolling will increase or decrease the terminal font size. + + + Ajustar a opacidade do terminal ao rolar enquanto mantém as teclas Ctrl e Shift + Header for a control to toggle opacity changes with scrolling. When enabled, holding the Ctrl and Shift keys while scrolling will increase or decrease the window opacity. + Animações do painel Header for a control to toggle animations on panes. "Enabled" value enables the animations. @@ -1908,10 +1924,26 @@ Avisar quando o "Serviço de Teclado Virtual e Painel de Manuscrito" estiver desabilitado - Avisar ao tentar colar mais de 5 KiB de caracteres + Avisar ao colar mais de 5 KiB - Avisar ao tentar colar um caractere de "nova linha" + Avisar ao colar novas linhas + + + Se o shell não for compatível com o modo "colar entre colchetes", recomendamos defini-lo como "Sempre" por motivos de segurança. + "bracketed paste" is a technical term referring to escape sequences. "Always" is a setting, as in "Always VS Never". + + + Somente se "colar entre colchetes" estiver desabilitado + "bracketed paste" is a technical term referring to terminal escape sequences. + + + Sempre + As in: Always VS Never + + + Nunca + As in: Always VS Never O terminal do Windows está em execução no modo portátil. @@ -2101,7 +2133,7 @@ Header for a control that adds any remaining profiles to the new tab menu. - Adicionar um grupo de perfis que correspondam a pelo menos uma das propriedades definidas + Adicionar um grupo de perfis que correspondam a pelo menos uma das propriedades de expressão regular definidas Additional information for a control that adds a terminal profile matcher to the new tab menu. Presented near "NewTabMenu_AddMatchProfiles". @@ -2117,15 +2149,15 @@ Header for a control that adds a folder to the new tab menu. - Nome do perfil + Nome do perfil (expressão regular) Header for a text box used to define a regex for the names of profiles to add. - Origem do perfil + Origem do perfil (expressão regular) Header for a text box used to define a regex for the sources of profiles to add. - Commandline + Linha de comando (expressão regular) Header for a text box used to define a regex for the commandlines of profiles to add. @@ -2312,6 +2344,10 @@ MSYS2 (C:\ -> /c) {Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting. + + MinGW (C:\ -> C:/) + {Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting. + Perfil não detectado @@ -2336,4 +2372,91 @@ Esta opção é gerenciada pela política corporativa e não pode ser alterada aqui. This is displayed in concordance with Globals_StartOnUserLogin if the enterprise administrator has taken control of this setting. + + Extensões Ativas + + + Perfis Modificados + + + Perfis Adicionados + + + Esquemas de Cores Adicionados + + + Saiba mais sobre extensões + + + Navegar para o perfil + + + Navegar para o perfil + + + Navegar para o esquema de cores + + + Navegar para o esquema de cores + + + Usuário Atual + Label for the installation scope of an extension. + + + Todos os Usuários + Label for the installation scope of an extension + + + Escopo + Header for the installation scope of the extension + + + NOVO + Text is used on an info badge for new navigation items. Must be all caps. + + + Saiba mais sobre expressões regulares + + + Nenhum + Text displayed when the background image path is not defined. + + + Nenhum + Text displayed when the answerback message is not defined. + + + Redefinir para as configurações padrão + + + Limpar cache + + + O cache armazena dados relacionados a sessões salvas e perfis gerados automaticamente. + + + Restaurar + + + Limpar + + + Esta ação é aplicada imediatamente e não pode ser desfeita. + + + Esta ação é aplicada imediatamente e não pode ser desfeita. + + + Tem certeza de que deseja redefinir as configurações? + + + Tem certeza de que deseja limpar o cache? + + + Sim, redefinir minhas configurações + + + Sim, limpar o cache + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw index 38c4dd4bc8..4e705c9e58 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw @@ -684,6 +684,10 @@ Ăςтιòñš !! Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. + + Ěжτëⁿśïōʼnş !!! + Header for the "extensions" menu item. This navigates to a page that lets you see and modify extensions for the app. + βά¢ĸĝŕõúήđ ǿрãĉĩŧÿ !!! !! Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. @@ -942,6 +946,10 @@ Åļωάỳš ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. + + Дüтôмαťί¢ !!! + An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table if and only if high contrast mode is enabled. + Ъâг ( ┃ ) !!! {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. @@ -1728,6 +1736,14 @@ Áϋτомàţīćąłłý ƒόćŭś рåπę öń мõùšě ђöνëŗ !!! !!! !!! !!! Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. + + Аδĵŭŝт ţєŗмιņăℓ ƒøпť ѕįżε вỳ şċѓοℓℓíńğ ẃħïĺє ĥöľδīйģ τђе €ŧřŀ ќëÿ !!! !!! !!! !!! !!! !!! ! + Header for a control to toggle font size changes with scrolling. When enabled, holding the Ctrl key while scrolling will increase or decrease the terminal font size. + + + Ąδĵüšţ ŧęřмìńąĺ ôрǻčĩτў ьγ šćřοļĺįήĝ ẅђϊłę ħōļδîʼnğ ťђз Çťгł àиδ Śђĭƒт ķęÿŝ !!! !!! !!! !!! !!! !!! !!! ! + Header for a control to toggle opacity changes with scrolling. When enabled, holding the Ctrl and Shift keys while scrolling will increase or decrease the window opacity. + Ρǻйě ǻйϊмäţіőηş !!! ! Header for a control to toggle animations on panes. "Enabled" value enables the animations. @@ -1908,10 +1924,26 @@ Ẃǻґņ ώћĕπ "Ŧõũčђ Κзγьοдгď ąʼnð Нáлđẅŕīťίńģ Ρàňεℓ Śεŕνîçé" íš đìşāъŀêδ !!! !!! !!! !!! !!! !!! !! - Ẃăґи ẅнěπ тґўіήĝ τø рáŝťё mοřé ţħäή 5 ΚĩВ òƒ ĉħāґąćτєґѕ !!! !!! !!! !!! !!! ! + Ẃăґи ẅнěπ рǻѕтĭиġ mσŗë ťћåń 5 Κìß !!! !!! !!! - Щāřπ ẃĥєи ťгýīлğ ţõ ρäşŧè ª "пėщ ľїл℮" ςћářǻ¢ţēř !!! !!! !!! !!! !! + Щāřπ ẃĥєи ρаśŧîήĝ ⁿěώŀīиèŝ !!! !!! ! + + + ̃ уοΰґ ѕħęļĺ ðøέş ńőт ŝűрρŏяŧ "ьřäċќéŧéď ρаѕτę" мōðέ, ẅέ ѓ℮¢ōмmзñδ śěτŧìñģ ťћĭś ŧó "Дĺẁαỳş" ƒóґ šέсµгітў ѓêąŝøñŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + "bracketed paste" is a technical term referring to escape sequences. "Always" is a setting, as in "Always VS Never". + + + Óйŀў íƒ "ьґαςќєťēð рãŝţê" îś δ횪вļèδ !!! !!! !!! !! + "bracketed paste" is a technical term referring to terminal escape sequences. + + + Äℓщãỳš ! + As in: Always VS Never + + + Иёνзŗ ! + As in: Always VS Never Ẅїηðσшѕ Ţеřmíńăľ īѕ ґϋййïʼnģ ĭņ рόѓťáъℓė mōďе. !!! !!! !!! !!! ! @@ -2105,7 +2137,7 @@ Header for a control that adds any remaining profiles to the new tab menu. - Аðđ á ġŕôũρ οƒ φřθƒїłєŝ ŧнăť мąţςĥ аτ ĺěàѕτ θņė ŏƒ ţђę δεƒįňèď φŗōрεřтìěş !!! !!! !!! !!! !!! !!! !!! + Аðđ á ġŕôũρ οƒ φřθƒїłєŝ ŧнăť мąţςĥ аτ ĺěàѕτ θņė ŏƒ ţђę δεƒįňèď řêğџŀäř èхρŕéşѕįбñ рřбφĕřţĭęś !!! !!! !!! !!! !!! !!! !!! !!! !!! Additional information for a control that adds a terminal profile matcher to the new tab menu. Presented near "NewTabMenu_AddMatchProfiles". @@ -2121,15 +2153,15 @@ Header for a control that adds a folder to the new tab menu. - Ρґоƒîłĕ ňǻмε !!! + Ρґоƒîłĕ ňǻмε (ŗěğΰłář ёхρѓέŝšĩǿň) !!! !!! !!! Header for a text box used to define a regex for the names of profiles to add. - Ρřοƒϊļë šőµřĉε !!! ! + Ρřοƒϊļë šőµřĉε (гęģůłάґ е×рřĕşšιóⁿ) !!! !!! !!! ! Header for a text box used to define a regex for the sources of profiles to add. - Çόммάηðłϊйё !!! + Çόммάηðłϊйё (я℮ġϋℓąř єжφгèѕśįóп) !!! !!! !!! Header for a text box used to define a regex for the commandlines of profiles to add. @@ -2316,6 +2348,10 @@ MSYS2 (C:\ -> /c) !!! !! {Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting. + + MinGW (C:\ -> C:/) !!! !! + {Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting. + Ρřσƒīŀē ⁿο łøňġèя ðёτęςτęď !!! !!! ! @@ -2340,4 +2376,91 @@ Ţĥîŝ όρтįοñ íş мαпªģéð ъý ĕŋτéřрŗĭšз рôľĩсу алð çąňηōт ье ċħâήğèď ћēяē. !!! !!! !!! !!! !!! !!! !!! This is displayed in concordance with Globals_StartOnUserLogin if the enterprise administrator has taken control of this setting. - \ No newline at end of file + + Ãčŧινε ∑хťėŋѕιόйš !!! !! + + + Мőδіƒí℮δ Ρѓòƒίŀέŝ !!! !! + + + Ǻđδєδ Ρѓбƒïℓέŝ !!! ! + + + Áδďéđ Čǿļöŗ Šсħèm℮ŝ !!! !!! + + + Ļ℮ªѓń môг℮ åъоûţ ē×ţепѕîõπѕ !!! !!! !! + + + Ŋãνīġáťё ţσ ргŏƒĭℓз !!! !!! + + + Ŋãνιġǻťě ťǿ рґøƒĩŀę !!! !!! + + + Ňąνїģǻţė ŧŏ ċőľόŗ şċĥêме !!! !!! ! + + + Ñąνīġåŧє ťò ςŏŀόŕ ѕсђεмė !!! !!! ! + + + Ćύřřëńť Ŭѕέѓ !!! + Label for the installation scope of an extension. + + + Ăļł Ůşεŕś !!! + Label for the installation scope of an extension + + + Şĉòрэ ! + Header for the installation scope of the extension + + + ∏ÉŴ + Text is used on an info badge for new navigation items. Must be all caps. + + + Ľэаŕη mθřє άъőùţ řέĝцŀªř ℮×рřеѕšïоπş !!! !!! !!! ! + + + Иθñé ! + Text displayed when the background image path is not defined. + + + Ŋøπë ! + Text displayed when the answerback message is not defined. + + + Яєšèţ ŧθ ďēƒªũĺť śέττιŋĝş !!! !!! ! + + + Сℓёāř ςàċђë !!! + + + Ŧђё ¢âćћз ѕтõŕèś ďдтä ґєłäťěð τò ѕάνêď śέşŝϊσŋŝ àňđ åùŧóмăτïςάľłÿ ġēʼnęřдţзδ рřŏƒìļëş. !!! !!! !!! !!! !!! !!! !!! !!! ! + + + Ѓęѕεт ! + + + Ćļĕáг ! + + + Ţнįś ā¢тîōń įš āррŀїėđ ίммęđíάτ℮ℓý αηδ čãлηõť вэ џⁿđбήё. !!! !!! !!! !!! !!! ! + + + Ťђìѕ âćťιöη ϊş ąρрľįêδ ìмmεðιдţέĺγ ăʼnď çªňŋŏŧ вέ ũйđόηę. !!! !!! !!! !!! !!! ! + + + Āяě ỳōμ śυřê ŷοц ẁаņτ τô яėşéŧ ўőũґ ŝэтŧîπģŝ? !!! !!! !!! !!! ! + + + Àŗè уоϋ ŝџгє ўŏϋ шåⁿť тο ςℓэāѓ γόџя ¢áсĥє? !!! !!! !!! !!! + + + Ў℮ś, řэŝēŧ мγ ŝεττίήğѕ !!! !!! + + + Υėŝ, ςŀёãř ŧнę сǻςнэ !!! !!! + + diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw index 38c4dd4bc8..4e705c9e58 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw @@ -684,6 +684,10 @@ Ăςтιòñš !! Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. + + Ěжτëⁿśïōʼnş !!! + Header for the "extensions" menu item. This navigates to a page that lets you see and modify extensions for the app. + βά¢ĸĝŕõúήđ ǿрãĉĩŧÿ !!! !! Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. @@ -942,6 +946,10 @@ Åļωάỳš ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. + + Дüтôмαťί¢ !!! + An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table if and only if high contrast mode is enabled. + Ъâг ( ┃ ) !!! {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. @@ -1728,6 +1736,14 @@ Áϋτомàţīćąłłý ƒόćŭś рåπę öń мõùšě ђöνëŗ !!! !!! !!! !!! Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. + + Аδĵŭŝт ţєŗмιņăℓ ƒøпť ѕįżε вỳ şċѓοℓℓíńğ ẃħïĺє ĥöľδīйģ τђе €ŧřŀ ќëÿ !!! !!! !!! !!! !!! !!! ! + Header for a control to toggle font size changes with scrolling. When enabled, holding the Ctrl key while scrolling will increase or decrease the terminal font size. + + + Ąδĵüšţ ŧęřмìńąĺ ôрǻčĩτў ьγ šćřοļĺįήĝ ẅђϊłę ħōļδîʼnğ ťђз Çťгł àиδ Śђĭƒт ķęÿŝ !!! !!! !!! !!! !!! !!! !!! ! + Header for a control to toggle opacity changes with scrolling. When enabled, holding the Ctrl and Shift keys while scrolling will increase or decrease the window opacity. + Ρǻйě ǻйϊмäţіőηş !!! ! Header for a control to toggle animations on panes. "Enabled" value enables the animations. @@ -1908,10 +1924,26 @@ Ẃǻґņ ώћĕπ "Ŧõũčђ Κзγьοдгď ąʼnð Нáлđẅŕīťίńģ Ρàňεℓ Śεŕνîçé" íš đìşāъŀêδ !!! !!! !!! !!! !!! !!! !! - Ẃăґи ẅнěπ тґўіήĝ τø рáŝťё mοřé ţħäή 5 ΚĩВ òƒ ĉħāґąćτєґѕ !!! !!! !!! !!! !!! ! + Ẃăґи ẅнěπ рǻѕтĭиġ mσŗë ťћåń 5 Κìß !!! !!! !!! - Щāřπ ẃĥєи ťгýīлğ ţõ ρäşŧè ª "пėщ ľїл℮" ςћářǻ¢ţēř !!! !!! !!! !!! !! + Щāřπ ẃĥєи ρаśŧîήĝ ⁿěώŀīиèŝ !!! !!! ! + + + ̃ уοΰґ ѕħęļĺ ðøέş ńőт ŝűрρŏяŧ "ьřäċќéŧéď ρаѕτę" мōðέ, ẅέ ѓ℮¢ōмmзñδ śěτŧìñģ ťћĭś ŧó "Дĺẁαỳş" ƒóґ šέсµгітў ѓêąŝøñŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + "bracketed paste" is a technical term referring to escape sequences. "Always" is a setting, as in "Always VS Never". + + + Óйŀў íƒ "ьґαςќєťēð рãŝţê" îś δ횪вļèδ !!! !!! !!! !! + "bracketed paste" is a technical term referring to terminal escape sequences. + + + Äℓщãỳš ! + As in: Always VS Never + + + Иёνзŗ ! + As in: Always VS Never Ẅїηðσшѕ Ţеřmíńăľ īѕ ґϋййïʼnģ ĭņ рόѓťáъℓė mōďе. !!! !!! !!! !!! ! @@ -2105,7 +2137,7 @@ Header for a control that adds any remaining profiles to the new tab menu. - Аðđ á ġŕôũρ οƒ φřθƒїłєŝ ŧнăť мąţςĥ аτ ĺěàѕτ θņė ŏƒ ţђę δεƒįňèď φŗōрεřтìěş !!! !!! !!! !!! !!! !!! !!! + Аðđ á ġŕôũρ οƒ φřθƒїłєŝ ŧнăť мąţςĥ аτ ĺěàѕτ θņė ŏƒ ţђę δεƒįňèď řêğџŀäř èхρŕéşѕįбñ рřбφĕřţĭęś !!! !!! !!! !!! !!! !!! !!! !!! !!! Additional information for a control that adds a terminal profile matcher to the new tab menu. Presented near "NewTabMenu_AddMatchProfiles". @@ -2121,15 +2153,15 @@ Header for a control that adds a folder to the new tab menu. - Ρґоƒîłĕ ňǻмε !!! + Ρґоƒîłĕ ňǻмε (ŗěğΰłář ёхρѓέŝšĩǿň) !!! !!! !!! Header for a text box used to define a regex for the names of profiles to add. - Ρřοƒϊļë šőµřĉε !!! ! + Ρřοƒϊļë šőµřĉε (гęģůłάґ е×рřĕşšιóⁿ) !!! !!! !!! ! Header for a text box used to define a regex for the sources of profiles to add. - Çόммάηðłϊйё !!! + Çόммάηðłϊйё (я℮ġϋℓąř єжφгèѕśįóп) !!! !!! !!! Header for a text box used to define a regex for the commandlines of profiles to add. @@ -2316,6 +2348,10 @@ MSYS2 (C:\ -> /c) !!! !! {Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting. + + MinGW (C:\ -> C:/) !!! !! + {Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting. + Ρřσƒīŀē ⁿο łøňġèя ðёτęςτęď !!! !!! ! @@ -2340,4 +2376,91 @@ Ţĥîŝ όρтįοñ íş мαпªģéð ъý ĕŋτéřрŗĭšз рôľĩсу алð çąňηōт ье ċħâήğèď ћēяē. !!! !!! !!! !!! !!! !!! !!! This is displayed in concordance with Globals_StartOnUserLogin if the enterprise administrator has taken control of this setting. - \ No newline at end of file + + Ãčŧινε ∑хťėŋѕιόйš !!! !! + + + Мőδіƒí℮δ Ρѓòƒίŀέŝ !!! !! + + + Ǻđδєδ Ρѓбƒïℓέŝ !!! ! + + + Áδďéđ Čǿļöŗ Šсħèm℮ŝ !!! !!! + + + Ļ℮ªѓń môг℮ åъоûţ ē×ţепѕîõπѕ !!! !!! !! + + + Ŋãνīġáťё ţσ ргŏƒĭℓз !!! !!! + + + Ŋãνιġǻťě ťǿ рґøƒĩŀę !!! !!! + + + Ňąνїģǻţė ŧŏ ċőľόŗ şċĥêме !!! !!! ! + + + Ñąνīġåŧє ťò ςŏŀόŕ ѕсђεмė !!! !!! ! + + + Ćύřřëńť Ŭѕέѓ !!! + Label for the installation scope of an extension. + + + Ăļł Ůşεŕś !!! + Label for the installation scope of an extension + + + Şĉòрэ ! + Header for the installation scope of the extension + + + ∏ÉŴ + Text is used on an info badge for new navigation items. Must be all caps. + + + Ľэаŕη mθřє άъőùţ řέĝцŀªř ℮×рřеѕšïоπş !!! !!! !!! ! + + + Иθñé ! + Text displayed when the background image path is not defined. + + + Ŋøπë ! + Text displayed when the answerback message is not defined. + + + Яєšèţ ŧθ ďēƒªũĺť śέττιŋĝş !!! !!! ! + + + Сℓёāř ςàċђë !!! + + + Ŧђё ¢âćћз ѕтõŕèś ďдтä ґєłäťěð τò ѕάνêď śέşŝϊσŋŝ àňđ åùŧóмăτïςάľłÿ ġēʼnęřдţзδ рřŏƒìļëş. !!! !!! !!! !!! !!! !!! !!! !!! ! + + + Ѓęѕεт ! + + + Ćļĕáг ! + + + Ţнįś ā¢тîōń įš āррŀїėđ ίммęđíάτ℮ℓý αηδ čãлηõť вэ џⁿđбήё. !!! !!! !!! !!! !!! ! + + + Ťђìѕ âćťιöη ϊş ąρрľįêδ ìмmεðιдţέĺγ ăʼnď çªňŋŏŧ вέ ũйđόηę. !!! !!! !!! !!! !!! ! + + + Āяě ỳōμ śυřê ŷοц ẁаņτ τô яėşéŧ ўőũґ ŝэтŧîπģŝ? !!! !!! !!! !!! ! + + + Àŗè уоϋ ŝџгє ўŏϋ шåⁿť тο ςℓэāѓ γόџя ¢áсĥє? !!! !!! !!! !!! + + + Ў℮ś, řэŝēŧ мγ ŝεττίήğѕ !!! !!! + + + Υėŝ, ςŀёãř ŧнę сǻςнэ !!! !!! + + diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw index 38c4dd4bc8..4e705c9e58 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw @@ -684,6 +684,10 @@ Ăςтιòñš !! Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. + + Ěжτëⁿśïōʼnş !!! + Header for the "extensions" menu item. This navigates to a page that lets you see and modify extensions for the app. + βά¢ĸĝŕõúήđ ǿрãĉĩŧÿ !!! !! Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. @@ -942,6 +946,10 @@ Åļωάỳš ! An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. + + Дüтôмαťί¢ !!! + An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table if and only if high contrast mode is enabled. + Ъâг ( ┃ ) !!! {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. @@ -1728,6 +1736,14 @@ Áϋτомàţīćąłłý ƒόćŭś рåπę öń мõùšě ђöνëŗ !!! !!! !!! !!! Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. + + Аδĵŭŝт ţєŗмιņăℓ ƒøпť ѕįżε вỳ şċѓοℓℓíńğ ẃħïĺє ĥöľδīйģ τђе €ŧřŀ ќëÿ !!! !!! !!! !!! !!! !!! ! + Header for a control to toggle font size changes with scrolling. When enabled, holding the Ctrl key while scrolling will increase or decrease the terminal font size. + + + Ąδĵüšţ ŧęřмìńąĺ ôрǻčĩτў ьγ šćřοļĺįήĝ ẅђϊłę ħōļδîʼnğ ťђз Çťгł àиδ Śђĭƒт ķęÿŝ !!! !!! !!! !!! !!! !!! !!! ! + Header for a control to toggle opacity changes with scrolling. When enabled, holding the Ctrl and Shift keys while scrolling will increase or decrease the window opacity. + Ρǻйě ǻйϊмäţіőηş !!! ! Header for a control to toggle animations on panes. "Enabled" value enables the animations. @@ -1908,10 +1924,26 @@ Ẃǻґņ ώћĕπ "Ŧõũčђ Κзγьοдгď ąʼnð Нáлđẅŕīťίńģ Ρàňεℓ Śεŕνîçé" íš đìşāъŀêδ !!! !!! !!! !!! !!! !!! !! - Ẃăґи ẅнěπ тґўіήĝ τø рáŝťё mοřé ţħäή 5 ΚĩВ òƒ ĉħāґąćτєґѕ !!! !!! !!! !!! !!! ! + Ẃăґи ẅнěπ рǻѕтĭиġ mσŗë ťћåń 5 Κìß !!! !!! !!! - Щāřπ ẃĥєи ťгýīлğ ţõ ρäşŧè ª "пėщ ľїл℮" ςћářǻ¢ţēř !!! !!! !!! !!! !! + Щāřπ ẃĥєи ρаśŧîήĝ ⁿěώŀīиèŝ !!! !!! ! + + + ̃ уοΰґ ѕħęļĺ ðøέş ńőт ŝűрρŏяŧ "ьřäċќéŧéď ρаѕτę" мōðέ, ẅέ ѓ℮¢ōмmзñδ śěτŧìñģ ťћĭś ŧó "Дĺẁαỳş" ƒóґ šέсµгітў ѓêąŝøñŝ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! + "bracketed paste" is a technical term referring to escape sequences. "Always" is a setting, as in "Always VS Never". + + + Óйŀў íƒ "ьґαςќєťēð рãŝţê" îś δ횪вļèδ !!! !!! !!! !! + "bracketed paste" is a technical term referring to terminal escape sequences. + + + Äℓщãỳš ! + As in: Always VS Never + + + Иёνзŗ ! + As in: Always VS Never Ẅїηðσшѕ Ţеřmíńăľ īѕ ґϋййïʼnģ ĭņ рόѓťáъℓė mōďе. !!! !!! !!! !!! ! @@ -2105,7 +2137,7 @@ Header for a control that adds any remaining profiles to the new tab menu. - Аðđ á ġŕôũρ οƒ φřθƒїłєŝ ŧнăť мąţςĥ аτ ĺěàѕτ θņė ŏƒ ţђę δεƒįňèď φŗōрεřтìěş !!! !!! !!! !!! !!! !!! !!! + Аðđ á ġŕôũρ οƒ φřθƒїłєŝ ŧнăť мąţςĥ аτ ĺěàѕτ θņė ŏƒ ţђę δεƒįňèď řêğџŀäř èхρŕéşѕįбñ рřбφĕřţĭęś !!! !!! !!! !!! !!! !!! !!! !!! !!! Additional information for a control that adds a terminal profile matcher to the new tab menu. Presented near "NewTabMenu_AddMatchProfiles". @@ -2121,15 +2153,15 @@ Header for a control that adds a folder to the new tab menu. - Ρґоƒîłĕ ňǻмε !!! + Ρґоƒîłĕ ňǻмε (ŗěğΰłář ёхρѓέŝšĩǿň) !!! !!! !!! Header for a text box used to define a regex for the names of profiles to add. - Ρřοƒϊļë šőµřĉε !!! ! + Ρřοƒϊļë šőµřĉε (гęģůłάґ е×рřĕşšιóⁿ) !!! !!! !!! ! Header for a text box used to define a regex for the sources of profiles to add. - Çόммάηðłϊйё !!! + Çόммάηðłϊйё (я℮ġϋℓąř єжφгèѕśįóп) !!! !!! !!! Header for a text box used to define a regex for the commandlines of profiles to add. @@ -2316,6 +2348,10 @@ MSYS2 (C:\ -> /c) !!! !! {Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting. + + MinGW (C:\ -> C:/) !!! !! + {Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting. + Ρřσƒīŀē ⁿο łøňġèя ðёτęςτęď !!! !!! ! @@ -2340,4 +2376,91 @@ Ţĥîŝ όρтįοñ íş мαпªģéð ъý ĕŋτéřрŗĭšз рôľĩсу алð çąňηōт ье ċħâήğèď ћēяē. !!! !!! !!! !!! !!! !!! !!! This is displayed in concordance with Globals_StartOnUserLogin if the enterprise administrator has taken control of this setting. - \ No newline at end of file + + Ãčŧινε ∑хťėŋѕιόйš !!! !! + + + Мőδіƒí℮δ Ρѓòƒίŀέŝ !!! !! + + + Ǻđδєδ Ρѓбƒïℓέŝ !!! ! + + + Áδďéđ Čǿļöŗ Šсħèm℮ŝ !!! !!! + + + Ļ℮ªѓń môг℮ åъоûţ ē×ţепѕîõπѕ !!! !!! !! + + + Ŋãνīġáťё ţσ ргŏƒĭℓз !!! !!! + + + Ŋãνιġǻťě ťǿ рґøƒĩŀę !!! !!! + + + Ňąνїģǻţė ŧŏ ċőľόŗ şċĥêме !!! !!! ! + + + Ñąνīġåŧє ťò ςŏŀόŕ ѕсђεмė !!! !!! ! + + + Ćύřřëńť Ŭѕέѓ !!! + Label for the installation scope of an extension. + + + Ăļł Ůşεŕś !!! + Label for the installation scope of an extension + + + Şĉòрэ ! + Header for the installation scope of the extension + + + ∏ÉŴ + Text is used on an info badge for new navigation items. Must be all caps. + + + Ľэаŕη mθřє άъőùţ řέĝцŀªř ℮×рřеѕšïоπş !!! !!! !!! ! + + + Иθñé ! + Text displayed when the background image path is not defined. + + + Ŋøπë ! + Text displayed when the answerback message is not defined. + + + Яєšèţ ŧθ ďēƒªũĺť śέττιŋĝş !!! !!! ! + + + Сℓёāř ςàċђë !!! + + + Ŧђё ¢âćћз ѕтõŕèś ďдтä ґєłäťěð τò ѕάνêď śέşŝϊσŋŝ àňđ åùŧóмăτïςάľłÿ ġēʼnęřдţзδ рřŏƒìļëş. !!! !!! !!! !!! !!! !!! !!! !!! ! + + + Ѓęѕεт ! + + + Ćļĕáг ! + + + Ţнįś ā¢тîōń įš āррŀїėđ ίммęđíάτ℮ℓý αηδ čãлηõť вэ џⁿđбήё. !!! !!! !!! !!! !!! ! + + + Ťђìѕ âćťιöη ϊş ąρрľįêδ ìмmεðιдţέĺγ ăʼnď çªňŋŏŧ вέ ũйđόηę. !!! !!! !!! !!! !!! ! + + + Āяě ỳōμ śυřê ŷοц ẁаņτ τô яėşéŧ ўőũґ ŝэтŧîπģŝ? !!! !!! !!! !!! ! + + + Àŗè уоϋ ŝџгє ўŏϋ шåⁿť тο ςℓэāѓ γόџя ¢áсĥє? !!! !!! !!! !!! + + + Ў℮ś, řэŝēŧ мγ ŝεττίήğѕ !!! !!! + + + Υėŝ, ςŀёãř ŧнę сǻςнэ !!! !!! + + diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw index 58109ce443..b6018eff35 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw @@ -684,6 +684,10 @@ Действия Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. + + Расширения + Header for the "extensions" menu item. This navigates to a page that lets you see and modify extensions for the app. + Прозрачность фона Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. @@ -942,6 +946,10 @@ Всегда An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. + + Автоматически + An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table if and only if high contrast mode is enabled. + Вертикальная линия (┃) {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. @@ -1728,6 +1736,14 @@ Автоматически переводить фокус на панель при наведении указателя мыши Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. + + Изменение размера шрифта терминала с помощью прокрутки при удерживании клавиши CTRL + Header for a control to toggle font size changes with scrolling. When enabled, holding the Ctrl key while scrolling will increase or decrease the terminal font size. + + + Настройте прозрачность терминала путем прокрутки при нажатых клавишах CTRL и SHIFT + Header for a control to toggle opacity changes with scrolling. When enabled, holding the Ctrl and Shift keys while scrolling will increase or decrease the window opacity. + Анимация панели Header for a control to toggle animations on panes. "Enabled" value enables the animations. @@ -1908,10 +1924,26 @@ Предупреждать, если параметр "Служба сенсорной клавиатуры и панели рукописного ввода" отключен - Предупреждать при попытке вставки более 5 КиБ символов + Предупреждать при вставке более 5 КиБ - Предупреждать при попытке вставки символа "новая строка" + Предупреждать при вставке новых строк + + + Если ваша оболочка не поддерживает режим "вставки в квадратных скобках", рекомендуется присвоить этому параметру значение "Всегда" по соображениям безопасности. + "bracketed paste" is a technical term referring to escape sequences. "Always" is a setting, as in "Always VS Never". + + + Только если "вставка в квадратных скобках" отключена + "bracketed paste" is a technical term referring to terminal escape sequences. + + + Всегда + As in: Always VS Never + + + Никогда + As in: Always VS Never Терминал Windows работает в переносном режиме. @@ -2101,7 +2133,7 @@ Header for a control that adds any remaining profiles to the new tab menu. - Добавить группу профилей, соответствующих хотя бы одному из определенных свойств + Добавить группу профилей, соответствующих хотя бы одному из определенных свойств регулярного выражения Additional information for a control that adds a terminal profile matcher to the new tab menu. Presented near "NewTabMenu_AddMatchProfiles". @@ -2117,15 +2149,15 @@ Header for a control that adds a folder to the new tab menu. - Имя профиля + Имя профиля (регулярное выражение) Header for a text box used to define a regex for the names of profiles to add. - Источник профиля + Источник профиля (регулярное выражение) Header for a text box used to define a regex for the sources of profiles to add. - Командная строка + Командная строка (регулярное выражение) Header for a text box used to define a regex for the commandlines of profiles to add. @@ -2312,6 +2344,10 @@ MSYS2 (C:\ -> /c) {Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting. + + MinGW (C:\ -> C:/) + {Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting. + Профиль больше не обнаруживается @@ -2336,4 +2372,91 @@ Этот параметр управляется политикой предприятия и не может быть изменен здесь. This is displayed in concordance with Globals_StartOnUserLogin if the enterprise administrator has taken control of this setting. + + Активные расширения + + + Измененные профили + + + Добавленные профили + + + Добавленные цветовые схемы + + + Узнайте больше о расширениях + + + Перейти к профилю + + + Перейти к профилю + + + Перейти к цветовой схеме + + + Перейти к цветовой схеме + + + Текущий пользователь + Label for the installation scope of an extension. + + + Все пользователи + Label for the installation scope of an extension + + + Объем + Header for the installation scope of the extension + + + НОВОЕ + Text is used on an info badge for new navigation items. Must be all caps. + + + Подробнее о регулярных выражениях + + + Нет + Text displayed when the background image path is not defined. + + + Нет + Text displayed when the answerback message is not defined. + + + Восстановить параметры по умолчанию + + + Очистить кэш + + + В кэше хранятся данные, связанные с сохраненными сеансами и автоматически созданными профилями. + + + Сбросить + + + Ясно + + + Это действие применяется немедленно и не может быть отменено. + + + Это действие применяется немедленно и не может быть отменено. + + + Действительно сбросить параметры? + + + Действительно очистить кэш? + + + Да, сбросить параметры + + + Да, очистить кэш + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw index 8e3dd54e60..44bc5abefc 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw @@ -684,6 +684,10 @@ 操作 Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. + + 扩展 + Header for the "extensions" menu item. This navigates to a page that lets you see and modify extensions for the app. + 背景不透明度 Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. @@ -942,6 +946,10 @@ 始终 An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. + + 自动 + An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table if and only if high contrast mode is enabled. + 条形 ( ┃ ) {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. @@ -1728,6 +1736,14 @@ 鼠标悬停时自动聚焦窗格 Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. + + 按住 Ctrl 键时通过滚动调整终端字体大小 + Header for a control to toggle font size changes with scrolling. When enabled, holding the Ctrl key while scrolling will increase or decrease the terminal font size. + + + 按住 Ctrl 和 Shift 键的同时滚动来调整终端不透明度 + Header for a control to toggle opacity changes with scrolling. When enabled, holding the Ctrl and Shift keys while scrolling will increase or decrease the window opacity. + 窗格动画 Header for a control to toggle animations on panes. "Enabled" value enables the animations. @@ -1908,10 +1924,26 @@ 禁用“触摸键盘和手写面板服务”时发出警告 - 尝试粘贴超过 5 KiB 字符时发出警告 + 粘贴超过 5 KiB 时发出警告 - 尝试粘贴“新行”字符时发出警告 + 粘贴新行时发出警告 + + + 如果 shell 不支持“括号粘贴”模式,出于安全原因,建议将其设置为“始终”。 + "bracketed paste" is a technical term referring to escape sequences. "Always" is a setting, as in "Always VS Never". + + + 仅当禁用“括号粘贴”时 + "bracketed paste" is a technical term referring to terminal escape sequences. + + + 始终 + As in: Always VS Never + + + 从不 + As in: Always VS Never Windows 终端正在便携模式下运行。 @@ -2101,7 +2133,7 @@ Header for a control that adds any remaining profiles to the new tab menu. - 添加至少匹配一个已定义属性的配置文件组 + 添加至少匹配一个定义正则表达式属性的配置文件组 Additional information for a control that adds a terminal profile matcher to the new tab menu. Presented near "NewTabMenu_AddMatchProfiles". @@ -2117,15 +2149,15 @@ Header for a control that adds a folder to the new tab menu. - 配置文件名称 + 配置文件名称(正则表达式) Header for a text box used to define a regex for the names of profiles to add. - 配置文件源 + 配置文件来源(正则表达式) Header for a text box used to define a regex for the sources of profiles to add. - 命令行 + 命令行(正则表达式) Header for a text box used to define a regex for the commandlines of profiles to add. @@ -2312,6 +2344,10 @@ MSYS2 (C:\ -> /c) {Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting. + + MinGW (C:\ -> C:/) + {Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting. + 不再检测到配置文件 @@ -2336,4 +2372,91 @@ 此选项由企业策略管理,无法在此处更改。 This is displayed in concordance with Globals_StartOnUserLogin if the enterprise administrator has taken control of this setting. + + 活动扩展 + + + 已修改配置文件 + + + 已添加配置文件 + + + 已添加配色方案 + + + 了解有关扩展的详细信息 + + + 导航到个人资料 + + + 导航到个人资料 + + + 导航到配色方案 + + + 导航到配色方案 + + + 当前用户 + Label for the installation scope of an extension. + + + All Users + Label for the installation scope of an extension + + + 范围 + Header for the installation scope of the extension + + + 新建 + Text is used on an info badge for new navigation items. Must be all caps. + + + 了解有关正则表达式的更多信息 + + + + Text displayed when the background image path is not defined. + + + + Text displayed when the answerback message is not defined. + + + 重置为默认设置 + + + 清除缓存 + + + 缓存会存储与已保存会话及自动生成的配置文件相关的数据。 + + + 重置 + + + 清除 + + + 此作立即应用,无法撤消。 + + + 此作立即应用,无法撤消。 + + + 是否确实要重置设置? + + + 是否确实要清除缓存? + + + 是,重置设置 + + + 是,清除缓存 + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw index 4bf8f371ff..6ae44012ea 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw @@ -684,6 +684,10 @@ 動作 Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. + + 延伸模組 + Header for the "extensions" menu item. This navigates to a page that lets you see and modify extensions for the app. + 背景不透明度 Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. @@ -942,6 +946,10 @@ 一律 An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility. + + 自動 + An option to choose from for the "adjust indistinguishable colors" setting. When selected, we will adjust the text colors for visibility only when the colors are part of this profile's color scheme's color table if and only if high contrast mode is enabled. + 橫條圖 ( ┃ ) {Locked="┃"} An option to choose from for the "cursor shape" setting. When selected, the cursor will look like a vertical bar. The character in the parentheses is used to show what it looks like. @@ -1728,6 +1736,14 @@ 滑鼠暫留時自動聚焦窗格 Header for a control to toggle the "focus follow mouse" setting. When enabled, hovering over a pane puts it in focus. + + 按住 Ctrl 鍵的同時捲動來調整終端字體大小 + Header for a control to toggle font size changes with scrolling. When enabled, holding the Ctrl key while scrolling will increase or decrease the terminal font size. + + + 按住 Ctrl 和 Shift 鍵的同時捲動來調整終端不透明度 + Header for a control to toggle opacity changes with scrolling. When enabled, holding the Ctrl and Shift keys while scrolling will increase or decrease the window opacity. + 窗格動畫 Header for a control to toggle animations on panes. "Enabled" value enables the animations. @@ -1908,10 +1924,26 @@ 停用「觸控式鍵盤和手寫面板服務」時發出警告 - 嘗試貼上超過 5 個 KiB 字元時發出警告 + 在貼上超過 5 KiB 時警告 - 嘗試貼上「新行」字元時發出警告 + 在貼上新行列時警告 + + + 如果您的殼層不支援「括號貼上」模式,基於安全性考量,我們建議您將此設定為「一律」。 + "bracketed paste" is a technical term referring to escape sequences. "Always" is a setting, as in "Always VS Never". + + + 僅在停用「括號式貼上」時 + "bracketed paste" is a technical term referring to terminal escape sequences. + + + 一律 + As in: Always VS Never + + + 一律不要 + As in: Always VS Never Windows 終端機正以可攜式模式執行。 @@ -2101,7 +2133,7 @@ Header for a control that adds any remaining profiles to the new tab menu. - 新增至少符合其中一個已定義屬性的配置檔群組 + 新增至少符合其中一個已定義正則表達式屬性的配置檔群組 Additional information for a control that adds a terminal profile matcher to the new tab menu. Presented near "NewTabMenu_AddMatchProfiles". @@ -2117,15 +2149,15 @@ Header for a control that adds a folder to the new tab menu. - 設定檔名稱 + 設定檔名稱 (規則運算式) Header for a text box used to define a regex for the names of profiles to add. - 設定檔來源 + 設定檔來源 (規則運算式) Header for a text box used to define a regex for the sources of profiles to add. - 命令列 + Commandline (規則運算式) Header for a text box used to define a regex for the commandlines of profiles to add. @@ -2312,6 +2344,10 @@ MSYS2 (C:\ -> /c) {Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting. + + MinGW (C:\ -> C:/) + {Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting. + 已不再偵測到設定檔 @@ -2336,4 +2372,91 @@ 此選項由企業原則管理,無法在此變更。 This is displayed in concordance with Globals_StartOnUserLogin if the enterprise administrator has taken control of this setting. + + 作用中擴充功能 + + + 已修改的設定檔 + + + 已新增設定檔 + + + 已新增色彩配置 + + + 深入了解延伸項目 + + + 瀏覽至設定檔 + + + 瀏覽至設定檔 + + + 瀏覽至色彩配置 + + + 瀏覽至色彩配置 + + + 目前使用者 + Label for the installation scope of an extension. + + + All Users + Label for the installation scope of an extension + + + 範圍 + Header for the installation scope of the extension + + + 新增 + Text is used on an info badge for new navigation items. Must be all caps. + + + 深入了解規則運算式 + + + + Text displayed when the background image path is not defined. + + + + Text displayed when the answerback message is not defined. + + + 重設至預設設定 + + + 清除快取 + + + 快取會儲存與已儲存之工作階段和自動產生設定檔相關的資料。 + + + 重設 + + + 晴朗 + + + 此動作會立即套用且無法復原。 + + + 此動作會立即套用且無法復原。 + + + 確定要重設您的設定嗎? + + + 確定要清除快取嗎? + + + 是的,重設我的設定 + + + 是的,清除快取 + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/SettingContainerStyle.xaml b/src/cascadia/TerminalSettingsEditor/SettingContainerStyle.xaml index c4d3c7a580..280dba68ad 100644 --- a/src/cascadia/TerminalSettingsEditor/SettingContainerStyle.xaml +++ b/src/cascadia/TerminalSettingsEditor/SettingContainerStyle.xaml @@ -14,13 +14,10 @@ - #0F000000 - + Color="{StaticResource TextFillColorSecondary}" /> @@ -83,13 +80,10 @@ - #19000000 - + Color="{StaticResource TextFillColorSecondary}" /> @@ -134,10 +128,11 @@ @@ -179,13 +174,18 @@ + + - @@ -228,6 +228,45 @@ + + + Command @@ -535,9 +774,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { cmdImpl->Name(foundCmdImpl->Name()); } - if (!foundCmdImpl->IconPath().empty() && cmdImpl->IconPath().empty()) + if (!foundCmdImpl->Icon().Path().empty() && cmdImpl->Icon().Path().empty()) { - cmdImpl->IconPath(foundCmdImpl->IconPath()); + cmdImpl->Icon(foundCmdImpl->Icon()); } } } @@ -686,13 +925,31 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return nullptr; } + IVector ActionMap::AllKeyBindingsForAction(const winrt::hstring& cmdID) + { + if (!_ResolvedKeyToActionMapCache) + { + _RefreshKeyBindingCaches(); + } + + std::vector keybindingsList; + for (const auto& [key, ID] : _CumulativeKeyToActionMapCache) + { + if (ID == cmdID) + { + keybindingsList.emplace_back(key); + } + } + return single_threaded_vector(std::move(keybindingsList)); + } + // Method Description: // - Rebinds a key binding to a new key chord // Arguments: // - oldKeys: the key binding that we are rebinding // - newKeys: the new key chord that is being used to replace oldKeys // Return Value: - // - true, if successful. False, otherwise. + // - true, if successful; otherwise, false. bool ActionMap::RebindKeys(const Control::KeyChord& oldKeys, const Control::KeyChord& newKeys) { const auto cmd{ GetActionByKeyChord(oldKeys) }; @@ -741,6 +998,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } } + void ActionMap::AddKeyBinding(Control::KeyChord keys, const winrt::hstring& cmdID) + { + _KeyMap.insert_or_assign(keys, cmdID); + _changeLog.emplace(KeysKey); + _RefreshKeyBindingCaches(); + } + // Method Description: // - Add a new key binding // - If the key chord is already in use, the conflicting command is overwritten. @@ -757,6 +1021,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation AddAction(*cmd, keys); } + void ActionMap::DeleteUserCommand(const winrt::hstring& cmdID) + { + _ActionMap.erase(cmdID); + _RefreshKeyBindingCaches(); + } + // This is a helper to aid in sorting commands by their `Name`s, alphabetically. static bool _compareSchemeNames(const ColorScheme& lhs, const ColorScheme& rhs) { @@ -809,6 +1079,26 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return _ExpandedCommandsCache; } + void ActionMap::ResolveMediaResourcesWithBasePath(const winrt::hstring& basePath, const Model::MediaResourceResolver& resolver) + { + for (const auto& [_, cmd] : _ActionMap) + { + winrt::get_self(cmd)->ResolveMediaResourcesWithBasePath(basePath, resolver); + } + + // Serialize all nested Command objects added in the current layer + for (const auto& [_, cmd] : _NestedCommands) + { + winrt::get_self(cmd)->ResolveMediaResourcesWithBasePath(basePath, resolver); + } + + // Serialize all iterable Command objects added in the current layer + for (const auto& cmd : _IterableCommands) + { + winrt::get_self(cmd)->ResolveMediaResourcesWithBasePath(basePath, resolver); + } + } + #pragma region Snippets std::vector _filterToSnippets(IMapView nameMap, winrt::hstring currentCommandline, @@ -916,14 +1206,58 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation AddAction(*cmd, keys); } - // Update ActionMap's cache of actions for this directory. We'll look for a - // .wt.json in this directory. If it exists, we'll read it, parse it's JSON, - // then take all the sendInput actions in it and store them in our - // _cwdLocalSnippetsCache - std::vector ActionMap::_updateLocalSnippetCache(winrt::hstring currentWorkingDirectory) + void ActionMap::UpdateCommandID(const Model::Command& cmd, winrt::hstring newID) + { + const auto oldID = cmd.ID(); + if (newID.empty()) + { + // if the new ID is empty, that means we need to generate a new one + newID = winrt::get_self(cmd.ActionAndArgs())->GenerateID(); + } + if (newID != oldID) + { + if (const auto foundCmd{ _GetActionByID(newID) }) + { + const auto foundCmdActionAndArgs = foundCmd.ActionAndArgs(); + if (foundCmdActionAndArgs != cmd.ActionAndArgs()) + { + // we found a command that has the same ID as this one, but that command has different ActionAndArgs + // this means that foundCommand's action and/or args have been changed since its ID was generated, + // generate a new one for it + // Note: this is recursive! We're calling UpdateCommandID again wich lands us back in here to resolve any cascading collisions + auto foundCmdNewID = winrt::get_self(foundCmdActionAndArgs)->GenerateID(); + UpdateCommandID(foundCmd, foundCmdNewID); + } + } + winrt::get_self(cmd)->ID(newID); + // update _ActionMap with the ID change + _ActionMap.erase(oldID); + _ActionMap.emplace(newID, cmd); + + // update _KeyMap so that all keys that pointed to the old ID now point to the new ID + std::vector keysToRemap; + for (const auto& [keys, cmdID] : _KeyMap) + { + if (cmdID == oldID) + { + keysToRemap.push_back(keys); + } + } + for (const auto& keys : keysToRemap) + { + _KeyMap.erase(keys); + _KeyMap.emplace(keys, newID); + } + } + _RefreshKeyBindingCaches(); + } + + // Look for a .wt.json file in the given directory. If it exists, + // read it, parse it's JSON, and retrieve all the sendInput actions. + std::unordered_map ActionMap::_loadLocalSnippets(const std::filesystem::path& currentWorkingDirectory) { // This returns an empty string if we fail to load the file. - std::filesystem::path localSnippetsPath{ std::wstring_view{ currentWorkingDirectory + L"\\.wt.json" } }; + std::filesystem::path localSnippetsPath = currentWorkingDirectory / std::filesystem::path{ ".wt.json" }; const auto data = til::io::read_file_as_utf8_string_if_exists(localSnippetsPath); if (data.empty()) { @@ -943,12 +1277,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return {}; } - auto result = std::vector(); + std::unordered_map result; if (auto actions{ root[JsonKey("snippets")] }) { for (const auto& json : actions) { - result.push_back(*Command::FromSnippetJson(json)); + const auto snippet = Command::FromSnippetJson(json); + result.insert_or_assign(snippet->Name(), *snippet); } } return result; @@ -958,34 +1293,89 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring currentCommandline, winrt::hstring currentWorkingDirectory) { + // enumerate all the parent directories we want to import snippets from + std::filesystem::path directory{ std::wstring_view{ currentWorkingDirectory } }; + std::vector directories; + while (!directory.empty()) { - // Check if there are any cached commands in this directory. - const auto& cache{ _cwdLocalSnippetsCache.lock_shared() }; - - const auto cacheIterator = cache->find(currentWorkingDirectory); - if (cacheIterator != cache->end()) + directories.push_back(directory); + auto parentPath = directory.parent_path(); + if (directory == parentPath) { - // We found something in the cache! return it. + break; + } + directory = std::move(parentPath); + } + + { + // Check if all the directories are already in the cache + const auto& cache{ _cwdLocalSnippetsCache.lock_shared() }; + if (std::ranges::all_of(directories, [&](auto&& dir) { return cache->contains(dir); })) + { + // Load snippets from directories in reverse order. + // This ensures that we prioritize snippets closer to the cwd. + // The map makes it easy to avoid duplicates. + std::unordered_map localSnippetsMap; + for (auto rit = directories.rbegin(); rit != directories.rend(); ++rit) + { + // register snippets from cache + for (const auto& [name, snippet] : cache->at(*rit)) + { + localSnippetsMap.insert_or_assign(name, snippet); + } + } + + std::vector localSnippets; + localSnippets.reserve(localSnippetsMap.size()); + std::ranges::transform(localSnippetsMap, + std::back_inserter(localSnippets), + [](const auto& kvPair) { return kvPair.second; }); co_return winrt::single_threaded_vector(_filterToSnippets(NameMap(), currentCommandline, - cacheIterator->second)); + localSnippets)); } } // release the lock on the cache // Don't do I/O on the main thread co_await winrt::resume_background(); - auto result = _updateLocalSnippetCache(currentWorkingDirectory); - if (!result.empty()) + // Load snippets from directories in reverse order. + // This ensures that we prioritize snippets closer to the cwd. + // The map makes it easy to avoid duplicates. + const auto& cache{ _cwdLocalSnippetsCache.lock() }; + std::unordered_map localSnippetsMap; + for (auto rit = directories.rbegin(); rit != directories.rend(); ++rit) { - // We found something! Add it to the cache - auto cache{ _cwdLocalSnippetsCache.lock() }; - cache->insert_or_assign(currentWorkingDirectory, result); + const auto& dir = *rit; + if (const auto cacheIterator = cache->find(dir); cacheIterator != cache->end()) + { + // register snippets from cache + for (const auto& [name, snippet] : cache->at(*rit)) + { + localSnippetsMap.insert_or_assign(name, snippet); + } + } + else + { + // we don't have this directory in the cache, so we need to load it + auto result = _loadLocalSnippets(dir); + cache->insert_or_assign(dir, result); + + // register snippets from cache + std::ranges::for_each(result, [&localSnippetsMap](const auto& kvPair) { + localSnippetsMap.insert_or_assign(kvPair.first, kvPair.second); + }); + } } + std::vector localSnippets; + localSnippets.reserve(localSnippetsMap.size()); + std::ranges::transform(localSnippetsMap, + std::back_inserter(localSnippets), + [](const auto& kvPair) { return kvPair.second; }); co_return winrt::single_threaded_vector(_filterToSnippets(NameMap(), currentCommandline, - result)); + localSnippets)); } #pragma endregion } diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.h b/src/cascadia/TerminalSettingsModel/ActionMap.h index 8da139a30d..e0001d8186 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.h +++ b/src/cascadia/TerminalSettingsModel/ActionMap.h @@ -16,6 +16,7 @@ Author(s): #pragma once #include "ActionMap.g.h" +#include "ActionArgFactory.g.h" #include "IInheritable.h" #include "Command.h" @@ -47,6 +48,16 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } }; + struct ActionArgFactory + { + ActionArgFactory() = default; + + static winrt::hstring GetNameForAction(ShortcutAction action); + static winrt::hstring GetNameForAction(ShortcutAction action, Windows::ApplicationModel::Resources::Core::ResourceContext context); + static Windows::Foundation::Collections::IMap AvailableShortcutActionsAndNames(); + static Model::IActionArgs GetEmptyArgsForAction(Model::ShortcutAction shortcutAction); + }; + struct ActionMap : ActionMapT, IInheritable { void _FinalizeInheritance() override; @@ -56,6 +67,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Windows::Foundation::Collections::IMapView NameMap(); Windows::Foundation::Collections::IMapView GlobalHotkeys(); Windows::Foundation::Collections::IMapView KeyBindings(); + Windows::Foundation::Collections::IVectorView AllCommands(); com_ptr Copy() const; // queries @@ -63,6 +75,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Model::Command GetActionByID(const winrt::hstring& cmdID) const; bool IsKeyChordExplicitlyUnbound(const Control::KeyChord& keys) const; Control::KeyChord GetKeyBindingForAction(const winrt::hstring& cmdID); + Windows::Foundation::Collections::IVector AllKeyBindingsForAction(const winrt::hstring& cmdID); // population void AddAction(const Model::Command& cmd, const Control::KeyChord& keys); @@ -78,8 +91,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // modification bool RebindKeys(const Control::KeyChord& oldKeys, const Control::KeyChord& newKeys); void DeleteKeyBinding(const Control::KeyChord& keys); + void AddKeyBinding(Control::KeyChord keys, const winrt::hstring& cmdID); void RegisterKeyBinding(Control::KeyChord keys, Model::ActionAndArgs action); + void DeleteUserCommand(const winrt::hstring& cmdID); void AddSendInputAction(winrt::hstring name, winrt::hstring input, const Control::KeyChord keys); + void UpdateCommandID(const Model::Command& cmd, winrt::hstring newID); Windows::Foundation::Collections::IVector ExpandedCommands(); void ExpandCommands(const Windows::Foundation::Collections::IVectorView& profiles, @@ -87,6 +103,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::Windows::Foundation::IAsyncOperation> FilterToSnippets(winrt::hstring currentCommandline, winrt::hstring currentWorkingDirectory); + void ResolveMediaResourcesWithBasePath(const winrt::hstring& basePath, const Model::MediaResourceResolver& resolver); + private: Model::Command _GetActionByID(const winrt::hstring& actionID) const; std::optional _GetActionIdByKeyChordInternal(const Control::KeyChord& keys) const; @@ -103,7 +121,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void _TryUpdateActionMap(const Model::Command& cmd); void _TryUpdateKeyChord(const Model::Command& cmd, const Control::KeyChord& keys); - std::vector _updateLocalSnippetCache(winrt::hstring currentWorkingDirectory); + static std::unordered_map _loadLocalSnippets(const std::filesystem::path& currentWorkingDirectory); Windows::Foundation::Collections::IMap _AvailableActionsCache{ nullptr }; Windows::Foundation::Collections::IMap _NameMapCache{ nullptr }; @@ -136,8 +154,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // This is effectively a combination of _CumulativeKeyMapCache and _CumulativeActionMapCache and its purpose is so that // we can give the SUI a view of the key chords and the commands they map to Windows::Foundation::Collections::IMap _ResolvedKeyToActionMapCache{ nullptr }; + Windows::Foundation::Collections::IVector _AllCommandsCache{ nullptr }; - til::shared_mutex>> _cwdLocalSnippetsCache{}; + til::shared_mutex>> _cwdLocalSnippetsCache{}; std::set _changeLog; @@ -146,3 +165,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation friend class SettingsModelUnitTests::TerminalSettingsTests; }; } + +namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation +{ + BASIC_FACTORY(ActionArgFactory); +} diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.idl b/src/cascadia/TerminalSettingsModel/ActionMap.idl index 3c94ac5d92..097275def9 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.idl +++ b/src/cascadia/TerminalSettingsModel/ActionMap.idl @@ -2,9 +2,18 @@ // Licensed under the MIT license. import "Command.idl"; +import "ISettingsModelObject.idl"; namespace Microsoft.Terminal.Settings.Model { + static runtimeclass ActionArgFactory + { + static String GetNameForAction(Microsoft.Terminal.Settings.Model.ShortcutAction action); + static String GetNameForAction(Microsoft.Terminal.Settings.Model.ShortcutAction action, Windows.ApplicationModel.Resources.Core.ResourceContext context); + static Windows.Foundation.Collections.IMap AvailableShortcutActionsAndNames { get; }; + static IActionArgs GetEmptyArgsForAction(Microsoft.Terminal.Settings.Model.ShortcutAction shortcutAction); + } + // This interface ensures that no changes are made to ActionMap interface IActionMapView { @@ -13,23 +22,27 @@ namespace Microsoft.Terminal.Settings.Model Command GetActionByKeyChord(Microsoft.Terminal.Control.KeyChord keys); Command GetActionByID(String cmdID); Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(String cmdID); + IVector AllKeyBindingsForAction(String cmdID); Windows.Foundation.Collections.IMapView AvailableActions { get; }; Windows.Foundation.Collections.IMapView NameMap { get; }; Windows.Foundation.Collections.IMapView KeyBindings { get; }; Windows.Foundation.Collections.IMapView GlobalHotkeys { get; }; + Windows.Foundation.Collections.IVectorView AllCommands { get; }; IVector ExpandedCommands { get; }; Windows.Foundation.IAsyncOperation > FilterToSnippets(String CurrentCommandline, String CurrentWorkingDirectory); }; - [default_interface] runtimeclass ActionMap : IActionMapView + runtimeclass ActionMap : IActionMapView { + void AddAction(Command cmd, Microsoft.Terminal.Control.KeyChord keys); void RebindKeys(Microsoft.Terminal.Control.KeyChord oldKeys, Microsoft.Terminal.Control.KeyChord newKeys); void DeleteKeyBinding(Microsoft.Terminal.Control.KeyChord keys); - + void DeleteUserCommand(String cmdID); + void AddKeyBinding(Microsoft.Terminal.Control.KeyChord keys, String cmdID); void RegisterKeyBinding(Microsoft.Terminal.Control.KeyChord keys, ActionAndArgs action); void AddSendInputAction(String name, String input, Microsoft.Terminal.Control.KeyChord keys); } diff --git a/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp b/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp index 4abfc97f5d..72f7704f1e 100644 --- a/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp +++ b/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp @@ -6,9 +6,12 @@ #include "AppearanceConfig.g.cpp" #include "TerminalSettingsSerializationHelpers.h" #include "JsonUtils.h" +#include "Profile.h" +#include "MediaResourceSupport.h" using namespace winrt::Microsoft::Terminal::Control; using namespace Microsoft::Terminal::Settings::Model; +using namespace winrt::Microsoft::Terminal::Settings; using namespace winrt::Windows::UI::Xaml; using namespace winrt::Microsoft::Terminal::Settings::Model::implementation; @@ -20,12 +23,12 @@ static constexpr std::string_view LegacyAcrylicTransparencyKey{ "acrylicOpacity" static constexpr std::string_view OpacityKey{ "opacity" }; static constexpr std::string_view ColorSchemeKey{ "colorScheme" }; -AppearanceConfig::AppearanceConfig(winrt::weak_ref sourceProfile) : +AppearanceConfig::AppearanceConfig(winrt::weak_ref sourceProfile) : _sourceProfile(std::move(sourceProfile)) { } -winrt::com_ptr AppearanceConfig::CopyAppearance(const AppearanceConfig* source, winrt::weak_ref sourceProfile) +winrt::com_ptr AppearanceConfig::CopyAppearance(const AppearanceConfig* source, winrt::weak_ref sourceProfile) { auto appearance{ winrt::make_self(std::move(sourceProfile)) }; appearance->_Foreground = source->_Foreground; @@ -135,40 +138,35 @@ winrt::Microsoft::Terminal::Settings::Model::Profile AppearanceConfig::SourcePro return _sourceProfile.get(); } -// Method Description: -// - Returns this AppearanceConfig's background image path, if one is set, expanding -// any environment variables in the path, if there are any. -// - Or if "DesktopWallpaper" is set, then gets the path to the desktops wallpaper. -// - This is the same as Profile::ExpandedBackgroundImagePath, but for AppearanceConfig -// - NOTE: This is just placeholder for now, eventually the path will no longer be expanded in the settings model -// Return Value: -// - This profile's expanded background image path / desktops's wallpaper path /the empty string. -winrt::hstring AppearanceConfig::ExpandedBackgroundImagePath() +std::tuple AppearanceConfig::_getSourceProfileBasePathAndOrigin() const { - const auto path{ BackgroundImagePath() }; - if (path.empty()) + winrt::hstring sourceBasePath{}; + OriginTag origin{ OriginTag::None }; + if (const auto profile{ _sourceProfile.get() }) { - return path; + const auto profileImpl{ winrt::get_self(profile) }; + sourceBasePath = profileImpl->SourceBasePath; + origin = profileImpl->Origin(); } - // checks if the user would like to copy their desktop wallpaper - // if so, replaces the path with the desktop wallpaper's path - else if (path == L"desktopWallpaper") - { - WCHAR desktopWallpaper[MAX_PATH]; + return { sourceBasePath, origin }; +} - // "The returned string will not exceed MAX_PATH characters" as of 2020 - if (SystemParametersInfo(SPI_GETDESKWALLPAPER, MAX_PATH, desktopWallpaper, SPIF_UPDATEINIFILE)) - { - return winrt::hstring{ (desktopWallpaper) }; - } - else - { - return {}; - } - } - else +void AppearanceConfig::ResolveMediaResources(const Model::MediaResourceResolver& resolver) +{ + if (const auto [source, resource] = _getBackgroundImagePathOverrideSourceAndValueImpl(); source && resource && *resource) { - return winrt::hstring{ wil::ExpandEnvironmentStringsW(path.c_str()) }; + const auto [sourceBasePath, sourceOrigin]{ source->_getSourceProfileBasePathAndOrigin() }; + ResolveMediaResource(sourceOrigin, sourceBasePath, *resource, resolver); + } + if (const auto [source, resource]{ _getPixelShaderPathOverrideSourceAndValueImpl() }; source && resource && *resource) + { + const auto [sourceBasePath, sourceOrigin]{ source->_getSourceProfileBasePathAndOrigin() }; + ResolveMediaResource(sourceOrigin, sourceBasePath, *resource, resolver); + } + if (const auto [source, resource]{ _getPixelShaderImagePathOverrideSourceAndValueImpl() }; source && resource && *resource) + { + const auto [sourceBasePath, sourceOrigin]{ source->_getSourceProfileBasePathAndOrigin() }; + ResolveMediaResource(sourceOrigin, sourceBasePath, *resource, resolver); } } diff --git a/src/cascadia/TerminalSettingsModel/AppearanceConfig.h b/src/cascadia/TerminalSettingsModel/AppearanceConfig.h index 87beb7ea1b..f9973568d2 100644 --- a/src/cascadia/TerminalSettingsModel/AppearanceConfig.h +++ b/src/cascadia/TerminalSettingsModel/AppearanceConfig.h @@ -20,11 +20,12 @@ Author(s): #include "JsonUtils.h" #include "IInheritable.h" #include "MTSMSettings.h" +#include "MediaResourceSupport.h" #include namespace winrt::Microsoft::Terminal::Settings::Model::implementation { - struct AppearanceConfig : AppearanceConfigT, IInheritable + struct AppearanceConfig : AppearanceConfigT, IInheritable { public: AppearanceConfig(winrt::weak_ref sourceProfile); @@ -35,7 +36,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Model::Profile SourceProfile(); - winrt::hstring ExpandedBackgroundImagePath(); + void ResolveMediaResources(const Model::MediaResourceResolver& resolver); INHERITABLE_NULLABLE_SETTING(Model::IAppearanceConfig, Microsoft::Terminal::Core::Color, Foreground, nullptr); INHERITABLE_NULLABLE_SETTING(Model::IAppearanceConfig, Microsoft::Terminal::Core::Color, Background, nullptr); @@ -57,5 +58,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void _logSettingSet(const std::string_view& setting); void _logSettingIfSet(const std::string_view& setting, const bool isSet); + + std::tuple _getSourceProfileBasePathAndOrigin() const; }; } diff --git a/src/cascadia/TerminalSettingsModel/AppearanceConfig.idl b/src/cascadia/TerminalSettingsModel/AppearanceConfig.idl index 8434c13f8a..45be54f45e 100644 --- a/src/cascadia/TerminalSettingsModel/AppearanceConfig.idl +++ b/src/cascadia/TerminalSettingsModel/AppearanceConfig.idl @@ -5,6 +5,6 @@ import "IAppearanceConfig.idl"; namespace Microsoft.Terminal.Settings.Model { - [default_interface] runtimeclass AppearanceConfig : IAppearanceConfig { + runtimeclass AppearanceConfig : [default] IAppearanceConfig { } } diff --git a/src/cascadia/TerminalSettingsModel/ApplicationState.cpp b/src/cascadia/TerminalSettingsModel/ApplicationState.cpp index 7b6040b141..ed2f69d883 100644 --- a/src/cascadia/TerminalSettingsModel/ApplicationState.cpp +++ b/src/cascadia/TerminalSettingsModel/ApplicationState.cpp @@ -96,7 +96,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation ApplicationState::ApplicationState(const std::filesystem::path& stateRoot) noexcept : _sharedPath{ stateRoot / stateFileName }, _elevatedPath{ stateRoot / elevatedStateFileName }, - _throttler{ std::chrono::seconds(1), [this]() { _write(); } } + _throttler{ + til::throttled_func_options{ + .delay = std::chrono::seconds{ 1 }, + .debounce = true, + .trailing = true, + }, + [this]() { _write(); } + } { _read(); } @@ -309,6 +316,31 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation _throttler(); } + bool ApplicationState::DismissBadge(const hstring& badgeId) + { + bool inserted{ false }; + { + const auto state = _state.lock(); + if (!state->DismissedBadges) + { + state->DismissedBadges = std::unordered_set{}; + } + inserted = state->DismissedBadges->insert(badgeId).second; + } + _throttler(); + return inserted; + } + + bool ApplicationState::BadgeDismissed(const hstring& badgeId) const + { + const auto state = _state.lock_shared(); + if (state->DismissedBadges) + { + return state->DismissedBadges->contains(badgeId); + } + return false; + } + // Generate all getter/setters #define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \ type ApplicationState::name() const noexcept \ diff --git a/src/cascadia/TerminalSettingsModel/ApplicationState.h b/src/cascadia/TerminalSettingsModel/ApplicationState.h index af43f0e999..f998fc5ead 100644 --- a/src/cascadia/TerminalSettingsModel/ApplicationState.h +++ b/src/cascadia/TerminalSettingsModel/ApplicationState.h @@ -40,7 +40,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation X(FileSource::Local, Windows::Foundation::Collections::IVector, PersistedWindowLayouts, "persistedWindowLayouts") \ X(FileSource::Shared, Windows::Foundation::Collections::IVector, RecentCommands, "recentCommands") \ X(FileSource::Shared, Windows::Foundation::Collections::IVector, DismissedMessages, "dismissedMessages") \ - X(FileSource::Local, Windows::Foundation::Collections::IVector, AllowedCommandlines, "allowedCommandlines") + X(FileSource::Local, Windows::Foundation::Collections::IVector, AllowedCommandlines, "allowedCommandlines") \ + X(FileSource::Local, std::unordered_set, DismissedBadges, "dismissedBadges") \ + X(FileSource::Shared, bool, SSHFolderGenerated, "sshFolderGenerated", false) struct WindowLayout : WindowLayoutT { @@ -70,6 +72,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Json::Value ToJson(FileSource parseSource) const noexcept; void AppendPersistedWindowLayout(Model::WindowLayout layout); + bool DismissBadge(const hstring& badgeId); + bool BadgeDismissed(const hstring& badgeId) const; // State getters/setters #define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \ @@ -88,7 +92,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation til::shared_mutex _state; std::filesystem::path _sharedPath; std::filesystem::path _elevatedPath; - til::throttled_func_trailing<> _throttler; + til::throttled_func<> _throttler; void _write() const noexcept; void _read() const noexcept; diff --git a/src/cascadia/TerminalSettingsModel/ApplicationState.idl b/src/cascadia/TerminalSettingsModel/ApplicationState.idl index e35447773a..f4e85d3a0b 100644 --- a/src/cascadia/TerminalSettingsModel/ApplicationState.idl +++ b/src/cascadia/TerminalSettingsModel/ApplicationState.idl @@ -33,6 +33,8 @@ namespace Microsoft.Terminal.Settings.Model void Reset(); void AppendPersistedWindowLayout(WindowLayout layout); + Boolean DismissBadge(String badgeId); + Boolean BadgeDismissed(String badgeId); String SettingsHash; Windows.Foundation.Collections.IVector PersistedWindowLayouts; diff --git a/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp b/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp index 948b1822f8..3d42bdd21c 100644 --- a/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp +++ b/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp @@ -8,16 +8,29 @@ #include "../../inc/DefaultSettings.h" #include "DynamicProfileUtils.h" +#include using namespace ::Microsoft::Terminal::Settings::Model; using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::Microsoft::Terminal::TerminalConnection; +std::wstring_view GENERATOR_ICON_PATH{ L"ms-appx:///ProfileGeneratorIcons/AzureCloudShell.png" }; + std::wstring_view AzureCloudShellGenerator::GetNamespace() const noexcept { return AzureGeneratorNamespace; } +std::wstring_view AzureCloudShellGenerator::GetDisplayName() const noexcept +{ + return RS_(L"AzureCloudShellGeneratorDisplayName"); +} + +std::wstring_view AzureCloudShellGenerator::GetIcon() const noexcept +{ + return GENERATOR_ICON_PATH; +} + // Method Description: // - Checks if the Azure Cloud shell is available on this platform, and if it // is, creates a profile to be able to launch it. diff --git a/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.h b/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.h index f62b66fce7..9ba01bf632 100644 --- a/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.h +++ b/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.h @@ -25,6 +25,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model { public: std::wstring_view GetNamespace() const noexcept override; + std::wstring_view GetDisplayName() const noexcept override; + std::wstring_view GetIcon() const noexcept override; void GenerateProfiles(std::vector>& profiles) const override; }; }; diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp index eeb3d7cc2e..ada39cea72 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include "CascadiaSettings.h" #include "CascadiaSettings.g.cpp" +#include "MatchProfilesEntry.h" #include "DefaultTerminal.h" #include "FileUtils.h" @@ -113,6 +114,10 @@ Model::CascadiaSettings CascadiaSettings::Copy() const settings->_globals = _globals->Copy(); settings->_allProfiles = winrt::single_threaded_observable_vector(std::move(allProfiles)); settings->_activeProfiles = winrt::single_threaded_observable_vector(std::move(activeProfiles)); + + // extension packages don't need a deep clone + // because they're fully immutable. We can just copy the reference over instead. + settings->_extensionPackages = _extensionPackages; } // load errors @@ -173,6 +178,16 @@ IObservableVector CascadiaSettings::ActiveProfiles() const noexc return _activeProfiles; } +IVectorView CascadiaSettings::Extensions() +{ + if (!_extensionPackages) + { + // Lazy load the ExtensionPackage objects + _extensionPackages = winrt::single_threaded_vector(std::move(SettingsLoader::LoadExtensionPackages())); + } + return _extensionPackages.GetView(); +} + // Method Description: // - Returns the globally configured keybindings // Arguments: @@ -429,6 +444,7 @@ void CascadiaSettings::_validateSettings() _validateColorSchemesInCommands(); _validateThemeExists(); _validateProfileEnvironmentVariables(); + _validateRegexes(); } // Method Description: @@ -472,90 +488,159 @@ void CascadiaSettings::_validateAllSchemesExist() } } +extern bool TestHook_CascadiaSettings_ResolveSingleMediaResource(Model::OriginTag origin, std::wstring_view basePath, const Model::IMediaResource& resource); + +static void _resolveSingleMediaResourceInner(Model::OriginTag origin, std::wstring_view basePath, const Model::IMediaResource& resource) +{ + if (TestHook_CascadiaSettings_ResolveSingleMediaResource(origin, basePath, resource)) + { + // See the implementation in TestHooks.cpp. Link-time Code Generation (LTCG) will delete this entire call + // and the test hook function itself. + return; + } + + auto resourcePath{ resource.Path() }; + + if (til::equals_insensitive_ascii(resourcePath, L"desktopWallpaper")) + { + WCHAR desktopWallpaper[MAX_PATH]; + + // "The returned string will not exceed MAX_PATH characters" as of 2020 + if (SystemParametersInfoW(SPI_GETDESKWALLPAPER, MAX_PATH, desktopWallpaper, SPIF_UPDATEINIFILE)) + { + resource.Resolve(winrt::hstring{ &desktopWallpaper[0] }); + } + else + { + resource.Reject(); + } + + return; + } + else if (til::equals_insensitive_ascii(resourcePath, L"none")) + { + // Resolve "none" to the OK Empty string. + resource.Resolve({}); + return; + } + else if (resourcePath.empty()) + { + // Do nothing. + return; + } + + resourcePath = wil::ExpandEnvironmentStringsW(resourcePath.data()); + const auto colon{ std::wstring_view{ resourcePath }.find_first_of(L':') }; + + // URI (contains a :, but only after a reasonable distance for a schema) + if (colon != std::wstring_view::npos && colon >= 4) + { + try + { + const winrt::Windows::Foundation::Uri resourceUri{ resourcePath }; + if (!resourceUri) + { + resource.Reject(); + return; + } + + const auto scheme{ resourceUri.SchemeName() }; + if (til::starts_with_insensitive_ascii(scheme, L"http") || + (til::equals_insensitive_ascii(scheme, L"ms-appx") && !resourceUri.Domain().empty())) + { + // http(s) URLs (WSL Distro AppX fragments) and ms-appx://APPLICATION/ (Julia) URLs decay to fragment-relative paths + const auto path{ resourceUri.Path() }; + const std::wstring_view pathView{ path }; + const std::wstring_view file{ pathView.substr(pathView.find_last_of(L'/') + 1) }; + + resourcePath = winrt::hstring{ file }; + // FALL THROUGH TO TRY FILESYSTEM PATHS + } + else if (til::equals_insensitive_ascii(scheme, L"file")) + { + const auto uriPath{ resourceUri.Path() }; + if (uriPath.size() < 2) + { + resource.Reject(); + return; + } + // Uri mangles file paths to begin with a / (ala /C:/) and escapes special characters such as Space. + // Try to un-mangle it. + resourcePath = til::safe_slice_abs(winrt::Windows::Foundation::Uri::UnescapeComponent(uriPath), 1, SIZE_T_MAX); + // FALL THROUGH TO TRY FILESYSTEM PATHS + } + else if (!til::starts_with_insensitive_ascii(scheme, L"ms-")) + { + // Other non-file and non-ms* URLs are disallowed + resource.Reject(); + return; + } + else + { + // Other URLs (so, file and ms-*) are permissible. + resource.Resolve(resourcePath); + return; + } + } + catch (...) + { + // fall through + } + } + + // Not a URI? Try a path. + try + { + std::filesystem::path resourceAsFilesystemPath{ std::wstring_view{ resourcePath } }; + if (!basePath.empty()) + { + resourceAsFilesystemPath = std::filesystem::path{ basePath } / resourceAsFilesystemPath; + } + + if (!std::filesystem::exists(resourceAsFilesystemPath)) + { + resource.Reject(); + return; + } + + resource.Resolve(winrt::hstring{ resourceAsFilesystemPath.lexically_normal().native() }); + return; + } + catch (...) + { + // fall through + } + + resource.Reject(); +} + +void CascadiaSettings::_resolveSingleMediaResource(OriginTag origin, std::wstring_view basePath, const Model::IMediaResource& resource) +{ + _resolveSingleMediaResourceInner(origin, basePath, resource); + if (!resource.Ok() && (origin == OriginTag::User || origin == OriginTag::ProfilesDefaults)) + { + _foundInvalidUserResources = true; + } +} + // Method Description: -// - Ensures that all specified images resources (icons and background images) are valid URIs. -// This does not verify that the icon or background image files are encoded as an image. -// Arguments: -// - -// Return Value: -// - -// - Appends a SettingsLoadWarnings::InvalidBackgroundImage to our list of warnings if -// we find any invalid background images. -// - Appends a SettingsLoadWarnings::InvalidIconImage to our list of warnings if -// we find any invalid icon images. +// - Ensures that all specified images resources (icons and background images) are valid. void CascadiaSettings::_validateMediaResources() { - auto invalidBackground{ false }; - auto invalidIcon{ false }; + _foundInvalidUserResources = false; - for (auto profile : _allProfiles) + const MediaResourceResolver mediaResourceResolver{ this, &CascadiaSettings::_resolveSingleMediaResource }; + + for (const auto& profile : _allProfiles) { - if (const auto path = profile.DefaultAppearance().ExpandedBackgroundImagePath(); !path.empty()) - { - // Attempt to convert the path to a URI, the ctor will throw if it's invalid/unparseable. - // This covers file paths on the machine, app data, URLs, and other resource paths. - try - { - winrt::Windows::Foundation::Uri imagePath{ path }; - } - catch (...) - { - // reset background image path - profile.DefaultAppearance().ClearBackgroundImagePath(); - invalidBackground = true; - } - } - - if (profile.UnfocusedAppearance()) - { - if (const auto path = profile.UnfocusedAppearance().ExpandedBackgroundImagePath(); !path.empty()) - { - // Attempt to convert the path to a URI, the ctor will throw if it's invalid/unparseable. - // This covers file paths on the machine, app data, URLs, and other resource paths. - try - { - winrt::Windows::Foundation::Uri imagePath{ path }; - } - catch (...) - { - // reset background image path - profile.UnfocusedAppearance().ClearBackgroundImagePath(); - invalidBackground = true; - } - } - } - - // Anything longer than 2 wchar_t's _isn't_ an emoji or symbol, so treat - // it as an invalid path. - // - // Explicitly just use the Icon here, not the EvaluatedIcon. We don't - // want to blow up if we fell back to the commandline and the - // commandline _isn't an icon_. - // GH #17943: "none" is a special value interpreted as "remove the icon" - static constexpr std::wstring_view HideIconValue{ L"none" }; - if (const auto icon = profile.Icon(); icon.size() > 2 && icon != HideIconValue) - { - const auto iconPath{ wil::ExpandEnvironmentStringsW(icon.c_str()) }; - try - { - winrt::Windows::Foundation::Uri imagePath{ iconPath }; - } - catch (...) - { - profile.ClearIcon(); - invalidIcon = true; - } - } + profile.as()->ResolveMediaResources(mediaResourceResolver); } - if (invalidBackground) - { - _warnings.Append(SettingsLoadWarnings::InvalidBackgroundImage); - } + _globals->ResolveMediaResources(mediaResourceResolver); - if (invalidIcon) + if (_foundInvalidUserResources) { - _warnings.Append(SettingsLoadWarnings::InvalidIcon); + _warnings.Append(SettingsLoadWarnings::InvalidMediaResource); } } @@ -583,6 +668,41 @@ void CascadiaSettings::_validateProfileEnvironmentVariables() } } +// Returns true if all regexes in the new tab menu are valid, false otherwise +static bool _validateNTMEntries(const IVector& entries) +{ + if (!entries) + { + return true; + } + for (const auto& ntmEntry : entries) + { + if (const auto& folderEntry = ntmEntry.try_as()) + { + if (!_validateNTMEntries(folderEntry.RawEntries())) + { + return false; + } + } + if (const auto& matchProfilesEntry = ntmEntry.try_as()) + { + if (!winrt::get_self(matchProfilesEntry)->ValidateRegexes()) + { + return false; + } + } + } + return true; +} + +void CascadiaSettings::_validateRegexes() +{ + if (!_validateNTMEntries(_globals->NewTabMenu())) + { + _warnings.Append(SettingsLoadWarnings::InvalidRegex); + } +} + // Method Description: // - Helper to get the GUID of a profile, given an optional index and a possible // "profile" value to override that. @@ -1138,3 +1258,8 @@ void CascadiaSettings::ExpandCommands() { _globals->ExpandCommands(ActiveProfiles().GetView(), GlobalSettings().ColorSchemes()); } + +void CascadiaSettings::UpdateCommandID(const Model::Command& cmd, winrt::hstring newID) +{ + _globals->UpdateCommandID(cmd, newID); +} diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h index e91c43885e..cebf8174df 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h @@ -18,6 +18,10 @@ Author(s): #pragma once #include "CascadiaSettings.g.h" +#include "FragmentSettings.g.h" +#include "FragmentProfileEntry.g.h" +#include "FragmentColorSchemeEntry.g.h" +#include "ExtensionPackage.g.h" #include "GlobalAppSettings.h" #include "Profile.h" @@ -39,6 +43,28 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation std::runtime_error(message) {} }; + struct ExtensionPackage : ExtensionPackageT + { + public: + ExtensionPackage(hstring source, FragmentScope scope) : + _source{ source }, + _scope{ scope }, + _fragments{ winrt::single_threaded_vector() } {} + + hstring Source() const noexcept { return _source; } + FragmentScope Scope() const noexcept { return _scope; } + Windows::Foundation::Collections::IVectorView FragmentsView() const noexcept { return _fragments.GetView(); } + Windows::Foundation::Collections::IVector Fragments() const noexcept { return _fragments; } + + WINRT_PROPERTY(hstring, Icon); + WINRT_PROPERTY(hstring, DisplayName); + + private: + hstring _source; + FragmentScope _scope; + Windows::Foundation::Collections::IVector _fragments; + }; + struct ParsedSettings { winrt::com_ptr globals; @@ -56,21 +82,26 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation struct SettingsLoader { static SettingsLoader Default(const std::string_view& userJSON, const std::string_view& inboxJSON); + static std::vector LoadExtensionPackages(); SettingsLoader(const std::string_view& userJSON, const std::string_view& inboxJSON); void GenerateProfiles(); + void GenerateExtensionPackagesFromProfileGenerators(); void ApplyRuntimeInitialSettings(); void MergeInboxIntoUserSettings(); - void FindFragmentsAndMergeIntoUserSettings(); - void MergeFragmentIntoUserSettings(const winrt::hstring& source, const std::string_view& content); + void FindFragmentsAndMergeIntoUserSettings(bool generateExtensionPackages); + void MergeFragmentIntoUserSettings(const winrt::hstring& source, const winrt::hstring& basePath, const std::string_view& content); void FinalizeLayering(); bool DisableDeletedProfiles(); + bool AddDynamicProfileFolders(); bool RemapColorSchemeForProfile(const winrt::com_ptr& profile); bool FixupUserSettings(); ParsedSettings inboxSettings; ParsedSettings userSettings; + std::unordered_map> extensionPackageMap; bool duplicateProfile = false; + bool sshProfilesGenerated = false; private: struct JsonSettings @@ -81,6 +112,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation const Json::Value& profilesList; const Json::Value& themes; }; + struct ParseFragmentMetadata + { + std::wstring_view jsonFilename; + FragmentScope scope; + }; + SettingsLoader() = default; static std::pair _lineAndColumnFromPosition(const std::string_view& string, const size_t position); static void _rethrowSerializationExceptionWithLocationInfo(const JsonUtils::DeserializationError& e, const std::string_view& settingsString); @@ -88,13 +125,15 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static const Json::Value& _getJSONValue(const Json::Value& json, const std::string_view& key) noexcept; std::span> _getNonUserOriginProfiles() const; void _parse(const OriginTag origin, const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings); - void _parseFragment(const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings); + void _parseFragment(const winrt::hstring& source, const winrt::hstring& sourceBasePath, const std::string_view& content, ParsedSettings& settings, const std::optional& fragmentMeta); static JsonSettings _parseJson(const std::string_view& content); static winrt::com_ptr _parseProfile(const OriginTag origin, const winrt::hstring& source, const Json::Value& profileJson); void _appendProfile(winrt::com_ptr&& profile, const winrt::guid& guid, ParsedSettings& settings); void _addUserProfileParent(const winrt::com_ptr& profile); - void _addOrMergeUserColorScheme(const winrt::com_ptr& colorScheme); - void _executeGenerator(const IDynamicProfileGenerator& generator); + bool _addOrMergeUserColorScheme(const winrt::com_ptr& colorScheme); + static void _executeGenerator(const IDynamicProfileGenerator& generator, std::vector>& profilesList); + winrt::com_ptr _registerFragment(const winrt::Microsoft::Terminal::Settings::Model::FragmentSettings& fragment, FragmentScope scope); + Json::StreamWriterBuilder _getJsonStyledWriter(); std::unordered_set _ignoredNamespaces; std::set themesChangeLog; @@ -127,7 +166,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::Windows::Foundation::Collections::IObservableVector AllProfiles() const noexcept; winrt::Windows::Foundation::Collections::IObservableVector ActiveProfiles() const noexcept; Model::ActionMap ActionMap() const noexcept; - void WriteSettingsToDisk(); + winrt::Windows::Foundation::Collections::IVectorView Extensions(); + void ResetApplicationState() const; + void ResetToDefaultSettings(); + bool WriteSettingsToDisk(); Json::Value ToJson() const; Model::Profile ProfileDefaults() const; Model::Profile CreateNewProfile(); @@ -151,6 +193,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void CurrentDefaultTerminal(const Model::DefaultTerminal& terminal); void ExpandCommands(); + void UpdateCommandID(const Model::Command& cmd, winrt::hstring newID); + void ResolveMediaResources() { _validateMediaResources(); } void LogSettingChanges(bool isJsonLoad) const; @@ -162,6 +206,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::com_ptr _createNewProfile(const std::wstring_view& name) const; Model::Profile _getProfileForCommandLine(const winrt::hstring& commandLine) const; void _refreshDefaultTerminals(); + void _writeSettingsToDisk(std::string_view contents); void _resolveDefaultProfile() const; void _resolveNewTabMenuProfiles() const; @@ -169,12 +214,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void _validateSettings(); void _validateAllSchemesExist(); + void _resolveSingleMediaResource(OriginTag origin, std::wstring_view basePath, const Model::IMediaResource& resource); void _validateMediaResources(); void _validateProfileEnvironmentVariables(); void _validateKeybindings() const; void _validateColorSchemesInCommands() const; bool _hasInvalidColorScheme(const Model::Command& command) const; void _validateThemeExists(); + void _validateRegexes(); void _researchOnLoad(); @@ -184,12 +231,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::com_ptr _baseLayerProfile = winrt::make_self(); winrt::Windows::Foundation::Collections::IObservableVector _allProfiles = winrt::single_threaded_observable_vector(); winrt::Windows::Foundation::Collections::IObservableVector _activeProfiles = winrt::single_threaded_observable_vector(); + winrt::Windows::Foundation::Collections::IVector _extensionPackages = nullptr; std::set _themesChangeLog{}; // load errors winrt::Windows::Foundation::Collections::IVector _warnings = winrt::single_threaded_vector(); winrt::Windows::Foundation::IReference _loadError; winrt::hstring _deserializationErrorMessage; + bool _foundInvalidUserResources{ false }; // defterm winrt::Windows::Foundation::Collections::IObservableVector _defaultTerminals{ nullptr }; @@ -199,6 +248,68 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation mutable std::once_flag _commandLinesCacheOnce; mutable std::vector> _commandLinesCache; }; + + struct FragmentProfileEntry : FragmentProfileEntryT + { + public: + FragmentProfileEntry(winrt::guid profileGuid, hstring json) : + _profileGuid{ profileGuid }, + _json{ json } {} + + winrt::guid ProfileGuid() const noexcept { return _profileGuid; } + hstring Json() const noexcept { return _json; } + + private: + winrt::guid _profileGuid; + hstring _json; + }; + + struct FragmentColorSchemeEntry : FragmentColorSchemeEntryT + { + public: + FragmentColorSchemeEntry(hstring schemeName, hstring json) : + _schemeName{ schemeName }, + _json{ json } {} + + hstring ColorSchemeName() const noexcept { return _schemeName; } + hstring Json() const noexcept { return _json; } + + private: + hstring _schemeName; + hstring _json; + }; + + struct FragmentSettings : FragmentSettingsT + { + public: + FragmentSettings(hstring source, hstring json, hstring filename) : + _source{ source }, + _json{ json }, + _filename{ filename } {} + + hstring Source() const noexcept { return _source; } + hstring Json() const noexcept { return _json; } + hstring Filename() const noexcept { return _filename; } + Windows::Foundation::Collections::IVector ModifiedProfiles() const noexcept { return _modifiedProfiles; } + void ModifiedProfiles(const Windows::Foundation::Collections::IVector& modifiedProfiles) noexcept { _modifiedProfiles = modifiedProfiles; } + Windows::Foundation::Collections::IVector NewProfiles() const noexcept { return _newProfiles; } + void NewProfiles(const Windows::Foundation::Collections::IVector& newProfiles) noexcept { _newProfiles = newProfiles; } + Windows::Foundation::Collections::IVector ColorSchemes() const noexcept { return _colorSchemes; } + void ColorSchemes(const Windows::Foundation::Collections::IVector& colorSchemes) noexcept { _colorSchemes = colorSchemes; } + + // views + Windows::Foundation::Collections::IVectorView ModifiedProfilesView() const noexcept { return _modifiedProfiles ? _modifiedProfiles.GetView() : nullptr; } + Windows::Foundation::Collections::IVectorView NewProfilesView() const noexcept { return _newProfiles ? _newProfiles.GetView() : nullptr; } + Windows::Foundation::Collections::IVectorView ColorSchemesView() const noexcept { return _colorSchemes ? _colorSchemes.GetView() : nullptr; } + + private: + hstring _source; + hstring _json; + hstring _filename; + Windows::Foundation::Collections::IVector _modifiedProfiles; + Windows::Foundation::Collections::IVector _newProfiles; + Windows::Foundation::Collections::IVector _colorSchemes; + }; } namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl b/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl index 2fa41941d6..cf6e258ddb 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl @@ -8,7 +8,13 @@ import "DefaultTerminal.idl"; namespace Microsoft.Terminal.Settings.Model { - [default_interface] runtimeclass CascadiaSettings { + enum FragmentScope + { + User, + Machine + }; + + runtimeclass CascadiaSettings { static CascadiaSettings LoadDefaults(); static CascadiaSettings LoadAll(); @@ -23,7 +29,9 @@ namespace Microsoft.Terminal.Settings.Model CascadiaSettings(String userJSON, String inboxJSON); CascadiaSettings Copy(); - void WriteSettingsToDisk(); + void ResetApplicationState(); + void ResetToDefaultSettings(); + Boolean WriteSettingsToDisk(); void LogSettingChanges(Boolean isJsonLoad); String Hash { get; }; @@ -38,6 +46,7 @@ namespace Microsoft.Terminal.Settings.Model Profile DuplicateProfile(Profile sourceProfile); ActionMap ActionMap { get; }; + Windows.Foundation.Collections.IVectorView Extensions { get; }; IVectorView Warnings { get; }; Windows.Foundation.IReference GetLoadingError { get; }; @@ -55,5 +64,39 @@ namespace Microsoft.Terminal.Settings.Model DefaultTerminal CurrentDefaultTerminal; void ExpandCommands(); + void UpdateCommandID(Command cmd, String newID); + + void ResolveMediaResources(); + } + + runtimeclass FragmentProfileEntry + { + Guid ProfileGuid { get; }; + String Json { get; }; + } + + runtimeclass FragmentColorSchemeEntry + { + String ColorSchemeName { get; }; + String Json { get; }; + } + + runtimeclass FragmentSettings + { + String Source { get; }; + String Json { get; }; + String Filename { get; }; + IVectorView ModifiedProfilesView { get; }; + IVectorView NewProfilesView { get; }; + IVectorView ColorSchemesView { get; }; + } + + runtimeclass ExtensionPackage + { + String Source { get; }; + String DisplayName { get; }; + String Icon { get; }; + FragmentScope Scope { get; }; + IVectorView FragmentsView { get; }; } } diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index ea9bda7616..7d369a9828 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -27,6 +27,7 @@ #include "ProfileEntry.h" #include "FolderEntry.h" #include "MatchProfilesEntry.h" +#include "WtExeUtils.h" using namespace winrt::Windows::Foundation::Collections; using namespace winrt::Windows::ApplicationModel::AppExtensions; @@ -124,6 +125,20 @@ SettingsLoader SettingsLoader::Default(const std::string_view& userJSON, const s return loader; } +std::vector SettingsLoader::LoadExtensionPackages() +{ + SettingsLoader loader{}; + loader.GenerateExtensionPackagesFromProfileGenerators(); + loader.FindFragmentsAndMergeIntoUserSettings(true /*generateExtensionPackages*/); + + std::vector extensionPackages; + for (auto [_, extPkg] : loader.extensionPackageMap) + { + extensionPackages.emplace_back(std::move(*extPkg)); + } + return extensionPackages; +} + // The SettingsLoader class is an internal implementation detail of CascadiaSettings. // Member methods aren't safe against misuse and you need to ensure to call them in a specific order. // See CascadiaSettings::LoadAll() for a specific usage example. @@ -174,16 +189,86 @@ SettingsLoader::SettingsLoader(const std::string_view& userJSON, const std::stri _userProfileCount = userSettings.profiles.size(); } +// This method is used to generate the JSON writer used for writing json in a styled format. +// We use it a few times throughout the loader, so we lazy load it and cache it here. +Json::StreamWriterBuilder SettingsLoader::_getJsonStyledWriter() +{ + static bool jsonWriterInitialized = false; + static Json::StreamWriterBuilder styledWriter; + if (!jsonWriterInitialized) + { + styledWriter["indentation"] = " "; + styledWriter["commentStyle"] = "All"; + styledWriter.settings_["enableYAMLCompatibility"] = true; // suppress spaces around colons + styledWriter.settings_["precision"] = 6; // prevent values like 1.1000000000000001 + jsonWriterInitialized = true; + } + return styledWriter; +} + // Generate dynamic profiles and add them to the list of "inbox" profiles // (meaning profiles specified by the application rather by the user). void SettingsLoader::GenerateProfiles() { - _executeGenerator(PowershellCoreProfileGenerator{}); - _executeGenerator(WslDistroGenerator{}); - _executeGenerator(AzureCloudShellGenerator{}); - _executeGenerator(VisualStudioGenerator{}); + auto generateProfiles = [&](const IDynamicProfileGenerator& generator) { + if (!_ignoredNamespaces.contains(generator.GetNamespace())) + { + const auto oldProfileCount = inboxSettings.profiles.size(); + _executeGenerator(generator, inboxSettings.profiles); + return oldProfileCount != inboxSettings.profiles.size(); + } + return false; + }; + + // Generate profiles for each generator and add them to the inbox settings. + // Be sure to update the same list below. + generateProfiles(PowershellCoreProfileGenerator{}); + generateProfiles(WslDistroGenerator{}); + generateProfiles(AzureCloudShellGenerator{}); + generateProfiles(VisualStudioGenerator{}); #if TIL_FEATURE_DYNAMICSSHPROFILES_ENABLED - _executeGenerator(SshHostGenerator{}); + sshProfilesGenerated = generateProfiles(SshHostGenerator{}); +#endif +} + +// Generate ExtensionPackage objects from the profile generators. +void SettingsLoader::GenerateExtensionPackagesFromProfileGenerators() +{ + auto generateExtensionPackages = [&](const IDynamicProfileGenerator& generator) { + std::vector> profilesList; + _executeGenerator(generator, profilesList); + + // These are needed for the FragmentSettings object + std::vector profileEntries; + Json::Value profilesListJson{ Json::ValueType::arrayValue }; + + for (const auto& profile : profilesList) + { + const auto profileJson = profile->ToJson(); + profilesListJson.append(profileJson); + profileEntries.push_back(winrt::make(profile->Guid(), hstring{ til::u8u16(Json::writeString(_getJsonStyledWriter(), profileJson)) })); + } + + // Manually construct the JSON for the FragmentSettings object + Json::Value json{ Json::ValueType::objectValue }; + json[JsonKey(ProfilesKey)] = profilesListJson; + + auto generatorExtension = winrt::make_self(hstring{ generator.GetNamespace() }, hstring{ til::u8u16(Json::writeString(_getJsonStyledWriter(), json)) }, hstring{ L"settings.json" }); + generatorExtension->NewProfiles(winrt::single_threaded_vector(std::move(profileEntries))); + + auto extPkg = _registerFragment(std::move(*generatorExtension), FragmentScope::Machine); + extPkg->DisplayName(hstring{ generator.GetDisplayName() }); + extPkg->Icon(hstring{ generator.GetIcon() }); + }; + + // Generate extension package objects for each generator. + // Be sure to update the same list above. + generateExtensionPackages(PowershellCoreProfileGenerator{}); + generateExtensionPackages(WslDistroGenerator{}); + generateExtensionPackages(AzureCloudShellGenerator{}); + generateExtensionPackages(VisualStudioGenerator{}); +#if TIL_FEATURE_DYNAMICSSHPROFILES_ENABLED + generateExtensionPackages(SshHostGenerator{}); #endif } @@ -242,21 +327,29 @@ void SettingsLoader::MergeInboxIntoUserSettings() // merge them. Unfortunately however the "updates" key in fragment profiles make this impossible: // The targeted profile might be one that got created as part of SettingsLoader::MergeInboxIntoUserSettings. // Additionally the GUID in "updates" will conflict with existing GUIDs in .inboxSettings. -void SettingsLoader::FindFragmentsAndMergeIntoUserSettings() +void SettingsLoader::FindFragmentsAndMergeIntoUserSettings(bool generateExtensionPackages) { ParsedSettings fragmentSettings; - const auto parseAndLayerFragmentFiles = [&](const std::filesystem::path& path, const winrt::hstring& source) { + const auto parseAndLayerFragmentFiles = [&](const std::filesystem::path& path, const winrt::hstring& source, FragmentScope scope) { + const winrt::hstring sourceBasePath{ path.native() }; for (const auto& fragmentExt : std::filesystem::directory_iterator{ path }) { - if (fragmentExt.path().extension() == jsonExtension) + const auto fragExtPath = fragmentExt.path(); + if (fragExtPath.extension() == jsonExtension) { try { - const auto content = til::io::read_file_as_utf8_string_if_exists(fragmentExt.path()); + const auto content = til::io::read_file_as_utf8_string_if_exists(fragExtPath); if (!content.empty()) { - _parseFragment(source, content, fragmentSettings); + _parseFragment(source, + sourceBasePath, + content, + fragmentSettings, + generateExtensionPackages ? + static_cast>(ParseFragmentMetadata{ fragExtPath.filename().wstring(), scope }) : + std::nullopt); } } CATCH_LOG(); @@ -278,9 +371,11 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings() const auto filename = fragmentExtFolder.path().filename(); const auto& source = filename.native(); - if (!_ignoredNamespaces.contains(std::wstring_view{ source }) && fragmentExtFolder.is_directory()) + if (fragmentExtFolder.is_directory()) { - parseAndLayerFragmentFiles(fragmentExtFolder.path(), winrt::hstring{ source }); + parseAndLayerFragmentFiles(fragmentExtFolder.path(), + winrt::hstring{ source }, + rfid == FOLDERID_LocalAppData ? FragmentScope::User : FragmentScope::Machine); // scope } } } @@ -312,8 +407,13 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings() for (const auto& ext : extensions) { - const auto packageName = ext.Package().Id().FamilyName(); - if (_ignoredNamespaces.contains(std::wstring_view{ packageName })) + const auto& package = ext.Package(); + const auto packageName = package.Id().FamilyName(); + + // If the extension was explicitly disabled, skip over it early to avoid the async API! + // NOTE: only do this if we're NOT generating extension packages. If we are, we need to get all the + // package metadata anyway to display in the settings UI later. + if (!generateExtensionPackages && _ignoredNamespaces.contains(std::wstring_view{ packageName })) { continue; } @@ -334,7 +434,18 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings() if (std::filesystem::is_directory(path)) { - parseAndLayerFragmentFiles(path, packageName); + // MSIX does not support machine-wide scope + // See https://github.com/microsoft/winget-cli/discussions/1983 + parseAndLayerFragmentFiles(path, + packageName, + FragmentScope::User); + + if (generateExtensionPackages) + { + auto extPkg = extensionPackageMap[packageName]; + extPkg->Icon(package.Logo().AbsoluteUri()); + extPkg->DisplayName(package.DisplayName()); + } } } } @@ -342,10 +453,10 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings() // See FindFragmentsAndMergeIntoUserSettings. // This function does the same, but for a single given JSON blob and source // and at the time of writing is used for unit tests only. -void SettingsLoader::MergeFragmentIntoUserSettings(const winrt::hstring& source, const std::string_view& content) +void SettingsLoader::MergeFragmentIntoUserSettings(const winrt::hstring& source, const winrt::hstring& basePath, const std::string_view& content) { ParsedSettings fragmentSettings; - _parseFragment(source, content, fragmentSettings); + _parseFragment(source, basePath, content, fragmentSettings, std::nullopt); } // Call this method before passing SettingsLoader to the CascadiaSettings constructor. @@ -428,6 +539,33 @@ bool SettingsLoader::DisableDeletedProfiles() return newGeneratedProfiles; } +// Returns true if something got changed and +// the settings need to be saved to disk. +bool SettingsLoader::AddDynamicProfileFolders() +{ + // Keep track of generated folders to avoid regenerating them + const auto state = get_self(ApplicationState::SharedInstance()); + + // If the SSH generator is enabled, try to create an "SSH" folder with all the generated profiles + if (sshProfilesGenerated && !state->SSHFolderGenerated()) + { + SshHostGenerator sshGenerator; + auto matchProfilesEntry = make_self(); + matchProfilesEntry->Source(hstring{ sshGenerator.GetNamespace() }); + + auto folderEntry = make_self(); + folderEntry->Name(L"SSH"); + folderEntry->Icon(MediaResource::FromString(hstring{ sshGenerator.GetIcon() })); + folderEntry->Inlining(FolderEntryInlining::Auto); + folderEntry->RawEntries(winrt::single_threaded_vector({ *matchProfilesEntry })); + + userSettings.globals->NewTabMenu().Append(folderEntry.as()); + state->SSHFolderGenerated(true); + return true; + } + return false; +} + bool winrt::Microsoft::Terminal::Settings::Model::implementation::SettingsLoader::RemapColorSchemeForProfile(const winrt::com_ptr& profile) { bool modified{ false }; @@ -521,7 +659,7 @@ bool SettingsLoader::FixupUserSettings() { for (auto&& icon : iconsToClearFromVisualStudioProfiles) { - if (profile->Icon() == icon) + if (profile->Icon().Path() == icon) { profile->ClearIcon(); fixedUp = true; @@ -701,7 +839,11 @@ void SettingsLoader::_parse(const OriginTag origin, const winrt::hstring& source settings.baseLayerProfile = Profile::FromJson(json.profileDefaults); // Remove the `guid` member from the default settings. // That will hyper-explode, so just don't let them do that. + // Also remove name, source, and commandline; those are not valid for the profiles defaults object. settings.baseLayerProfile->ClearGuid(); + settings.baseLayerProfile->ClearName(); + settings.baseLayerProfile->ClearSource(); + settings.baseLayerProfile->ClearCommandline(); settings.baseLayerProfile->Origin(OriginTag::ProfilesDefaults); } @@ -724,15 +866,23 @@ void SettingsLoader::_parse(const OriginTag origin, const winrt::hstring& source // Just like _parse, but is to be used for fragment files, which don't support anything but color // schemes and profiles. Additionally this function supports profiles which specify an "updates" key. -void SettingsLoader::_parseFragment(const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings) +// - fragmentMeta: If set, construct and register FragmentSettings objects. Provides metadata necessary for doing so. +// Otherwise, completely skip over that extra work and apply parsed settings to the user settings, if allowed by disabledProfileSources ("_ignoredNamespaces"). +void SettingsLoader::_parseFragment(const winrt::hstring& source, const winrt::hstring& sourceBasePath, const std::string_view& content, ParsedSettings& settings, const std::optional& fragmentMeta) { auto json = _parseJson(content); + const bool buildFragmentSettings = fragmentMeta.has_value(); + const bool applyToUserSettings = !buildFragmentSettings && !_ignoredNamespaces.contains(std::wstring_view{ source }); + winrt::com_ptr fragmentSettings = buildFragmentSettings ? + winrt::make_self(source, hstring{ til::u8u16(Json::writeString(_getJsonStyledWriter(), json.root)) }, hstring{ fragmentMeta->jsonFilename }) : + nullptr; + settings.clear(); + // Load GlobalAppSettings and ColorSchemes { - settings.globals = winrt::make_self(); - + std::vector fragmentColorSchemes; for (const auto& schemeJson : json.colorSchemes) { try @@ -740,72 +890,113 @@ void SettingsLoader::_parseFragment(const winrt::hstring& source, const std::str if (const auto scheme = ColorScheme::FromJson(schemeJson)) { scheme->Origin(OriginTag::Fragment); - // Don't add the color scheme to the Fragment's GlobalSettings; that will - // cause layering issues later. Add them to a staging area for later processing. - // (search for STAGED COLORS to find the next step) - settings.colorSchemes.emplace(scheme->Name(), std::move(scheme)); + if (buildFragmentSettings) + { + fragmentColorSchemes.emplace_back(winrt::make(scheme->Name(), hstring{ til::u8u16(Json::writeString(_getJsonStyledWriter(), schemeJson)) })); + } + if (applyToUserSettings) + { + // Don't add the color scheme to the Fragment's GlobalSettings; that will + // cause layering issues later. Add them to a staging area for later processing. + // (search for STAGED COLORS to find the next step) + settings.colorSchemes.emplace(scheme->Name(), std::move(scheme)); + } } } CATCH_LOG() } - // Parse out actions from the fragment. Manually opt-out of keybinding - // parsing - fragments shouldn't be allowed to bind actions to keys - // directly. We may want to revisit circa GH#2205 - settings.globals->LayerActionsFrom(json.root, OriginTag::Fragment, false); + if (buildFragmentSettings) + { + fragmentSettings->ColorSchemes(fragmentColorSchemes.empty() ? nullptr : single_threaded_vector(std::move(fragmentColorSchemes))); + } + if (applyToUserSettings) + { + // Parse out actions from the fragment. Manually opt-out of keybinding + // parsing - fragments shouldn't be allowed to bind actions to keys + // directly. We may want to revisit circa GH#2205 + settings.globals = winrt::make_self(); + settings.globals->SourceBasePath = sourceBasePath; + settings.globals->LayerActionsFrom(json.root, OriginTag::Fragment, false); + } } + // Load new and modified profiles { - const auto size = json.profilesList.size(); - settings.profiles.reserve(size); - settings.profilesByGuid.reserve(size); + if (applyToUserSettings) + { + const auto size = json.profilesList.size(); + settings.profiles.reserve(size); + settings.profilesByGuid.reserve(size); + } + std::vector newProfiles; + std::vector modifiedProfiles; for (const auto& profileJson : json.profilesList) { try { - auto profile = _parseProfile(OriginTag::Fragment, source, profileJson); // GH#9962: Discard Guid-less, Name-less profiles, but... // allow ones with an Updates field, as those are special for fragments. // We need to make sure to only call Guid() if HasGuid() is true, // as Guid() will dynamically generate a return value otherwise. + auto profile = _parseProfile(OriginTag::Fragment, source, profileJson); const auto guid = profile->HasGuid() ? profile->Guid() : profile->Updates(); + auto destinationSet = profile->HasGuid() ? &newProfiles : &modifiedProfiles; if (guid != winrt::guid{}) { - _appendProfile(std::move(profile), guid, settings); + profile->SourceBasePath = sourceBasePath; + if (buildFragmentSettings) + { + destinationSet->emplace_back(winrt::make(guid, hstring{ til::u8u16(Json::writeString(_getJsonStyledWriter(), profileJson)) })); + } + if (applyToUserSettings) + { + _appendProfile(std::move(profile), guid, settings); + } } } CATCH_LOG() } + if (buildFragmentSettings) + { + fragmentSettings->NewProfiles(newProfiles.empty() ? nullptr : single_threaded_vector(std::move(newProfiles))); + fragmentSettings->ModifiedProfiles(modifiedProfiles.empty() ? nullptr : single_threaded_vector(std::move(modifiedProfiles))); + _registerFragment(std::move(*fragmentSettings), fragmentMeta->scope); + } } - for (const auto& fragmentProfile : settings.profiles) + // Merge profiles, color schemes, and globals into the user settings (aka inheritance) + if (applyToUserSettings) { - if (const auto updates = fragmentProfile->Updates(); updates != winrt::guid{}) + for (const auto& fragmentProfile : settings.profiles) { - if (const auto it = userSettings.profilesByGuid.find(updates); it != userSettings.profilesByGuid.end()) + if (const auto updates = fragmentProfile->Updates(); updates != winrt::guid{}) { - it->second->AddMostImportantParent(fragmentProfile); + if (const auto it = userSettings.profilesByGuid.find(updates); it != userSettings.profilesByGuid.end()) + { + it->second->AddMostImportantParent(fragmentProfile); + } + } + else + { + _addUserProfileParent(fragmentProfile); } } - else + + // STAGED COLORS are processed here: we merge them into the partially-loaded + // settings directly so that we can resolve conflicts between user-generated + // color schemes and fragment-originated ones. + for (const auto& [_, fragmentColorScheme] : settings.colorSchemes) { - _addUserProfileParent(fragmentProfile); + _addOrMergeUserColorScheme(fragmentColorScheme); } - } - // STAGED COLORS are processed here: we merge them into the partially-loaded - // settings directly so that we can resolve conflicts between user-generated - // color schemes and fragment-originated ones. - for (const auto& fragmentColorScheme : settings.colorSchemes) - { - _addOrMergeUserColorScheme(fragmentColorScheme.second); + // Add the parsed fragment globals as a parent of the user's settings. + // Later, in FinalizeInheritance, this will result in the action map from + // the fragments being applied before the user's own settings. + userSettings.globals->AddLeastImportantParent(settings.globals); } - - // Add the parsed fragment globals as a parent of the user's settings. - // Later, in FinalizeInheritance, this will result in the action map from - // the fragments being applied before the user's own settings. - userSettings.globals->AddLeastImportantParent(settings.globals); } SettingsLoader::JsonSettings SettingsLoader::_parseJson(const std::string_view& content) @@ -861,7 +1052,7 @@ void SettingsLoader::_appendProfile(winrt::com_ptr&& profile, const win } // If the given ParsedSettings instance contains a profile with the given profile's GUID, -// the profile is added as a parent. Otherwise a new child profile is created. +// the profile is added as a parent. Otherwise, a new child profile is created. void SettingsLoader::_addUserProfileParent(const winrt::com_ptr& profile) { if (const auto [it, inserted] = userSettings.profilesByGuid.emplace(profile->Guid(), nullptr); !inserted) @@ -905,7 +1096,8 @@ void SettingsLoader::_addUserProfileParent(const winrt::com_ptr& newScheme) +// returns whether the scheme was successfully added +bool SettingsLoader::_addOrMergeUserColorScheme(const winrt::com_ptr& newScheme) { // On entry, all the user color schemes have been loaded. Therefore, any insertions of inbox or fragment schemes // will fail; we can leverage this to detect when they are equivalent and delete the user's duplicate copies. @@ -931,36 +1123,33 @@ void SettingsLoader::_addOrMergeUserColorScheme(const winrt::com_ptrName(), newName); // And re-add it to the end. userSettings.colorSchemes.emplace(newName, std::move(existingScheme)); + return true; } } + return false; } + return true; } // As the name implies it executes a generator. // Generated profiles are added to .inboxSettings. Used by GenerateProfiles(). -void SettingsLoader::_executeGenerator(const IDynamicProfileGenerator& generator) +void SettingsLoader::_executeGenerator(const IDynamicProfileGenerator& generator, std::vector>& profilesList) { const auto generatorNamespace = generator.GetNamespace(); - if (_ignoredNamespaces.contains(generatorNamespace)) - { - return; - } - - const auto previousSize = inboxSettings.profiles.size(); - + const auto previousSize = profilesList.size(); try { - generator.GenerateProfiles(inboxSettings.profiles); + generator.GenerateProfiles(profilesList); } CATCH_LOG_MSG("Dynamic Profile Namespace: \"%.*s\"", gsl::narrow(generatorNamespace.size()), generatorNamespace.data()) // If the generator produced some profiles we're going to give them default attributes. // By setting the Origin/Source/etc. here, we deduplicate some code and ensure they aren't missing accidentally. - if (inboxSettings.profiles.size() > previousSize) + if (profilesList.size() > previousSize) { const winrt::hstring source{ generatorNamespace }; - for (const auto& profile : std::span(inboxSettings.profiles).subspan(previousSize)) + for (const auto& profile : std::span(profilesList).subspan(previousSize)) { profile->Origin(OriginTag::Generated); profile->Source(source); @@ -968,6 +1157,26 @@ void SettingsLoader::_executeGenerator(const IDynamicProfileGenerator& generator } } +winrt::com_ptr SettingsLoader::_registerFragment(const winrt::Microsoft::Terminal::Settings::Model::FragmentSettings& fragment, FragmentScope scope) +{ + winrt::com_ptr extPkg{ nullptr }; + const auto src = fragment.Source(); + const auto found = extensionPackageMap.find(src); + if (found != extensionPackageMap.end()) + { + // retrieve from extensionPackageMap + extPkg = found->second; + } + else + { + // create a new entry in extensionPackageMap + const auto em = extensionPackageMap.emplace(src, winrt::make_self(src, scope)); + extPkg = em.first->second; + } + extPkg->Fragments().Append(fragment); + return extPkg; +} + // Method Description: // - Creates a CascadiaSettings from whatever's saved on disk, or instantiates // a new one with the default values. If we're running as a packaged app, @@ -1025,6 +1234,14 @@ try SettingsLoader loader{ settingsStringView, LoadStringResource(IDR_DEFAULTS) }; + winrt::hstring baseUserSettingsPath{ GetBaseSettingsPath().native() }; + loader.userSettings.baseLayerProfile->SourceBasePath = baseUserSettingsPath; + loader.userSettings.globals->SourceBasePath = baseUserSettingsPath; + for (auto&& userProfile : loader.userSettings.profiles) + { + userProfile->SourceBasePath = baseUserSettingsPath; + } + // Generate dynamic profiles and add them as parents of user profiles. // That way the user profiles will get appropriate defaults from the generators (like icons and such). loader.GenerateProfiles(); @@ -1040,12 +1257,13 @@ try loader.MergeInboxIntoUserSettings(); // Fragments might reference user profiles created by a generator. // --> FindFragmentsAndMergeIntoUserSettings must be called after MergeInboxIntoUserSettings. - loader.FindFragmentsAndMergeIntoUserSettings(); + loader.FindFragmentsAndMergeIntoUserSettings(false /*generateExtensionPackages*/); loader.FinalizeLayering(); // DisableDeletedProfiles returns true whenever we encountered any new generated/dynamic profiles. // Similarly FixupUserSettings returns true, when it encountered settings that were patched up. mustWriteToDisk |= loader.DisableDeletedProfiles(); + mustWriteToDisk |= loader.AddDynamicProfileFolders(); mustWriteToDisk |= loader.FixupUserSettings(); // If this throws, the app will catch it and use the default settings. @@ -1055,15 +1273,7 @@ try // settings string back to the file. if (mustWriteToDisk) { - try - { - settings->WriteSettingsToDisk(); - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - settings->_warnings.Append(SettingsLoadWarnings::FailedToWriteToSettings); - } + settings->WriteSettingsToDisk(); } else { @@ -1118,9 +1328,9 @@ void CascadiaSettings::_researchOnLoad() g_hSettingsModelProvider, "ThemesInUse", TraceLoggingDescription("Data about the themes in use"), - TraceLoggingBool(themeChoice, "Identifier for the theme chosen. 0 is system, 1 is light, 2 is dark, and 3 indicates any custom theme."), - TraceLoggingBool(changedTheme, "True if the user actually changed the theme from the default theme"), - TraceLoggingInt32(numThemes, "Number of themes in the user's settings"), + TraceLoggingInt32(themeChoice, "ThemeClass", "Identifier for the theme chosen. 0 is system (legacySystem = 6), 1 is light (legacyLight = 5), 2 is dark (legacyDark = 4), and 3 indicates any custom theme."), + TraceLoggingBool(changedTheme, "ChangedTheme", "True if the user actually changed the theme from the default theme"), + TraceLoggingInt32(numThemes, "NumberOfThemes", "Number of themes in the user's settings"), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); @@ -1142,7 +1352,7 @@ void CascadiaSettings::_researchOnLoad() g_hSettingsModelProvider, "SendInputUsage", TraceLoggingDescription("Event emitted upon settings load, containing the number of sendInput actions a user has"), - TraceLoggingInt32(collectSendInput(), "Number of sendInput actions in the user's settings"), + TraceLoggingInt32(collectSendInput(), "NumberOfSendInputActions", "Number of sendInput actions in the user's settings"), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); @@ -1159,8 +1369,8 @@ void CascadiaSettings::_researchOnLoad() g_hSettingsModelProvider, "MarksProfilesUsage", TraceLoggingDescription("Event emitted upon settings load, containing the number of profiles opted-in to scrollbar marks"), - TraceLoggingInt32(totalAutoMark, "Number of profiles for which AutoMarkPrompts is enabled"), - TraceLoggingInt32(totalShowMarks, "Number of profiles for which ShowMarks is enabled"), + TraceLoggingInt32(totalAutoMark, "NumberOfAutoMarkPromptsProfiles", "Number of profiles for which AutoMarkPrompts is enabled"), + TraceLoggingInt32(totalShowMarks, "NumberOfShowMarksProfiles", "Number of profiles for which ShowMarks is enabled"), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } @@ -1345,6 +1555,21 @@ winrt::hstring CascadiaSettings::DefaultSettingsPath() return winrt::hstring{ path.native() }; } +void CascadiaSettings::ResetApplicationState() const +{ + auto state = ApplicationState::SharedInstance(); + const auto hash = state.SettingsHash(); + state.Reset(); + state.SettingsHash(hash); + state.Flush(); +} + +void CascadiaSettings::ResetToDefaultSettings() +{ + ApplicationState::SharedInstance().Reset(); + _writeSettingsToDisk(LoadStringResource(IDR_USER_DEFAULTS)); +} + // Method Description: // - Write the current state of CascadiaSettings to our settings file // - Create a backup file with the current contents, if one does not exist @@ -1353,21 +1578,33 @@ winrt::hstring CascadiaSettings::DefaultSettingsPath() // - // Return Value: // - -void CascadiaSettings::WriteSettingsToDisk() +bool CascadiaSettings::WriteSettingsToDisk() { - const auto settingsPath = _settingsPath(); - // write current settings to current settings file Json::StreamWriterBuilder wbuilder; wbuilder.settings_["enableYAMLCompatibility"] = true; // suppress spaces around colons wbuilder.settings_["indentation"] = " "; wbuilder.settings_["precision"] = 6; // prevent values like 1.1000000000000001 - FILETIME lastWriteTime{}; - const auto styledString{ Json::writeString(wbuilder, ToJson()) }; - til::io::write_utf8_string_to_file_atomic(settingsPath, styledString, &lastWriteTime); + try + { + _writeSettingsToDisk(Json::writeString(wbuilder, ToJson())); + } + catch (...) + { + LOG_CAUGHT_EXCEPTION(); + _warnings.Append(SettingsLoadWarnings::FailedToWriteToSettings); + return false; + } + return true; +} - _hash = _calculateHash(styledString, lastWriteTime); +void CascadiaSettings::_writeSettingsToDisk(std::string_view contents) +{ + FILETIME lastWriteTime{}; + til::io::write_utf8_string_to_file_atomic(_settingsPath(), contents, &lastWriteTime); + + _hash = _calculateHash(contents, lastWriteTime); // Persists the default terminal choice // GH#10003 - Only do this if _currentDefaultTerminal was actually initialized. @@ -1668,10 +1905,22 @@ void CascadiaSettings::LogSettingChanges(bool isJsonLoad) const changes.insert(change); } +#if defined(WT_BRANDING_RELEASE) + constexpr uint8_t branding = 3; +#elif defined(WT_BRANDING_PREVIEW) + constexpr uint8_t branding = 2; +#elif defined(WT_BRANDING_CANARY) + constexpr uint8_t branding = 1; +#else + constexpr uint8_t branding = 0; +#endif + const uint8_t distribution = IsPackaged() ? 2 : + IsPortableMode() ? 1 : + 0; + // report changes for (const auto& change : changes) { -#ifndef _DEBUG // A `isJsonLoad ? "JsonSettingsChanged" : "UISettingsChanged"` // would be nice, but that apparently isn't allowed in the macro below. // Also, there's guidance to not send too much data all in one event, @@ -1681,7 +1930,9 @@ void CascadiaSettings::LogSettingChanges(bool isJsonLoad) const TraceLoggingWrite(g_hSettingsModelProvider, "JsonSettingsChanged", TraceLoggingDescription("Event emitted when settings.json change"), - TraceLoggingValue(change.data()), + TraceLoggingValue(change.data(), "Setting"), + TraceLoggingValue(branding, "Branding"), + TraceLoggingValue(distribution, "Distribution"), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } @@ -1690,14 +1941,11 @@ void CascadiaSettings::LogSettingChanges(bool isJsonLoad) const TraceLoggingWrite(g_hSettingsModelProvider, "UISettingsChanged", TraceLoggingDescription("Event emitted when settings change via the UI"), - TraceLoggingValue(change.data()), + TraceLoggingValue(change.data(), "Setting"), + TraceLoggingValue(branding, "Branding"), + TraceLoggingValue(distribution, "Distribution"), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } -#else - OutputDebugStringA(isJsonLoad ? "JsonSettingsChanged - " : "UISettingsChanged - "); - OutputDebugStringA(change.data()); - OutputDebugStringA("\n"); -#endif // !_DEBUG } } diff --git a/src/cascadia/TerminalSettingsModel/ColorScheme.cpp b/src/cascadia/TerminalSettingsModel/ColorScheme.cpp index 6b49fed7d6..5fc9c725ad 100644 --- a/src/cascadia/TerminalSettingsModel/ColorScheme.cpp +++ b/src/cascadia/TerminalSettingsModel/ColorScheme.cpp @@ -164,33 +164,6 @@ void ColorScheme::SetColorTableEntry(uint8_t index, const Core::Color& value) no _table[index] = value; } -winrt::Microsoft::Terminal::Core::Scheme ColorScheme::ToCoreScheme() const noexcept -{ - winrt::Microsoft::Terminal::Core::Scheme coreScheme{}; - - coreScheme.Foreground = Foreground(); - coreScheme.Background = Background(); - coreScheme.CursorColor = CursorColor(); - coreScheme.SelectionBackground = SelectionBackground(); - coreScheme.Black = Table()[0]; - coreScheme.Red = Table()[1]; - coreScheme.Green = Table()[2]; - coreScheme.Yellow = Table()[3]; - coreScheme.Blue = Table()[4]; - coreScheme.Purple = Table()[5]; - coreScheme.Cyan = Table()[6]; - coreScheme.White = Table()[7]; - coreScheme.BrightBlack = Table()[8]; - coreScheme.BrightRed = Table()[9]; - coreScheme.BrightGreen = Table()[10]; - coreScheme.BrightYellow = Table()[11]; - coreScheme.BrightBlue = Table()[12]; - coreScheme.BrightPurple = Table()[13]; - coreScheme.BrightCyan = Table()[14]; - coreScheme.BrightWhite = Table()[15]; - return coreScheme; -} - bool ColorScheme::IsEquivalentForSettingsMergePurposes(const winrt::com_ptr& other) noexcept { // The caller likely only got here if the names were the same, so skip checking that one. diff --git a/src/cascadia/TerminalSettingsModel/ColorScheme.h b/src/cascadia/TerminalSettingsModel/ColorScheme.h index 82d31cea81..cc634f1f09 100644 --- a/src/cascadia/TerminalSettingsModel/ColorScheme.h +++ b/src/cascadia/TerminalSettingsModel/ColorScheme.h @@ -46,8 +46,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static com_ptr FromJson(const Json::Value& json); Json::Value ToJson() const; - winrt::Microsoft::Terminal::Core::Scheme ToCoreScheme() const noexcept; - com_array Table() const noexcept; void SetColorTableEntry(uint8_t index, const Core::Color& value) noexcept; diff --git a/src/cascadia/TerminalSettingsModel/ColorScheme.idl b/src/cascadia/TerminalSettingsModel/ColorScheme.idl index dd55624a53..5ec57f2e75 100644 --- a/src/cascadia/TerminalSettingsModel/ColorScheme.idl +++ b/src/cascadia/TerminalSettingsModel/ColorScheme.idl @@ -21,7 +21,5 @@ namespace Microsoft.Terminal.Settings.Model // we expose the getter as a function. Microsoft.Terminal.Core.Color[] Table(); void SetColorTableEntry(UInt8 index, Microsoft.Terminal.Core.Color value); - - Microsoft.Terminal.Core.Scheme ToCoreScheme(); } } diff --git a/src/cascadia/TerminalSettingsModel/Command.cpp b/src/cascadia/TerminalSettingsModel/Command.cpp index 7ec4d07374..ec03277045 100644 --- a/src/cascadia/TerminalSettingsModel/Command.cpp +++ b/src/cascadia/TerminalSettingsModel/Command.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "KeyChordSerialization.h" @@ -24,10 +25,80 @@ static constexpr std::string_view ProfileNameToken{ "${profile.name}" }; static constexpr std::string_view ProfileIconToken{ "${profile.icon}" }; static constexpr std::string_view SchemeNameToken{ "${scheme.name}" }; +template<> +struct Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait +{ + winrt::Microsoft::Terminal::Settings::Model::implementation::Command::CommandNameOrResource FromJson(const Json::Value& json) + { + if (json.isObject()) + { + if (const auto resourceKey{ JsonUtils::GetValueForKey>(json, "key") }) + { + if (HasLibraryResourceWithName(*resourceKey)) + { + return { .resource = *resourceKey }; + } + } + } + else if (json.isString()) + { + return { .name = JsonUtils::GetValue(json) }; + } + return {}; + } + + bool CanConvert(const Json::Value& json) + { + if (json.isObject()) + { + if (const auto resourceKey{ JsonUtils::GetValueForKey>(json, "key") }) + { + return HasLibraryResourceWithName(*resourceKey); + } + } + return json.isString() || json.isNull(); + } + + Json::Value ToJson(const winrt::Microsoft::Terminal::Settings::Model::implementation::Command::CommandNameOrResource& val) + { + if (!val.resource.empty()) + { + Json::Value json{ Json::objectValue }; + SetValueForKey(json, "key", val.resource); + return json; + } + else if (!val.name.empty()) + { + return til::u16u8(val.name); + } + return Json::Value::nullSingleton(); + } + + std::string TypeDescription() const + { + return "string or valid resource"; + } +}; + namespace winrt::Microsoft::Terminal::Settings::Model::implementation { Command::Command() = default; + Model::Command Command::NewUserCommand() + { + auto newCmd{ winrt::make_self() }; + newCmd->_Origin = OriginTag::User; + return *newCmd; + } + + Model::Command Command::CopyAsUserCommand(const Model::Command& originalCmd) + { + auto command{ winrt::get_self(originalCmd) }; + auto copy{ command->Copy() }; + copy->_Origin = OriginTag::User; + return *copy; + } + com_ptr Command::Copy() const { auto command{ winrt::make_self() }; @@ -35,7 +106,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation command->_Origin = _Origin; command->_ID = _ID; command->_ActionAndArgs = *get_self(_ActionAndArgs)->Copy(); - command->_iconPath = _iconPath; + command->_icon = _icon; command->_IterateOn = _IterateOn; command->_Description = _Description; @@ -93,8 +164,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { if (_name.has_value()) { + if (!_name->resource.empty()) + { + // Resource key overrides name + return GetLibraryResourceString(_name->resource); + } + // name was explicitly set, return that value. - return hstring{ _name.value() }; + return hstring{ _name->name }; } else if (_ActionAndArgs) { @@ -108,11 +185,38 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } } + hstring Command::LanguageNeutralName() const noexcept + { + if (_name.has_value()) + { + // User-specified names **only matter** if they are resource keys (and therefore have a language-neutral version) + if (!_name->resource.empty()) + { + // Resource key overrides name + return EnglishOnlyResourceLoader().GetLocalizedString(_name->resource); + } + // ...if there was no resource, then, return nothing. + } + else if (_ActionAndArgs) + { + // generate a name from our action + return get_self(_ActionAndArgs)->GenerateName(EnglishOnlyResourceLoader().ResourceContext()); + } + + // we have no neutral name + return {}; + } + hstring Command::ID() const noexcept { return hstring{ _ID }; } + void Command::ID(const hstring& ID) noexcept + { + _ID = ID; + } + void Command::GenerateID() { if (_ActionAndArgs) @@ -120,8 +224,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto actionAndArgsImpl{ winrt::get_self(_ActionAndArgs) }; if (const auto generatedID = actionAndArgsImpl->GenerateID(); !generatedID.empty()) { - _ID = generatedID; _IDWasGenerated = true; + ID(generatedID); } } } @@ -133,66 +237,20 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void Command::Name(const hstring& value) { - if (!_name.has_value() || _name.value() != value) + if (!_name.has_value() || _name->name != value) { - _name = value; + _name = CommandNameOrResource{ .name = std::wstring{ value } }; } } - hstring Command::IconPath() const noexcept + IMediaResource Command::Icon() const noexcept { - if (_iconPath.has_value()) - { - return hstring{ *_iconPath }; - } - return {}; + return (_icon && *_icon) ? *_icon : MediaResource::Empty(); } - void Command::IconPath(const hstring& val) + void Command::Icon(const IMediaResource& val) { - if (!_iconPath.has_value() || _iconPath.value() != val) - { - _iconPath = val; - } - } - - // Function Description: - // - attempt to get the name of this command from the provided json object. - // * If the "name" property is a string, return that value. - // * If the "name" property is an object, attempt to lookup the string - // resource specified by the "key" property, to support localizable - // command names. - // Arguments: - // - json: The Json::Value representing the command object we should get the name for. - // Return Value: - // - the empty string if we couldn't find a name, otherwise the command's name. - static std::optional _nameFromJson(const Json::Value& json) - { - if (const auto name{ json[JsonKey(NameKey)] }) - { - if (name.isObject()) - { - if (const auto resourceKey{ JsonUtils::GetValueForKey>(name, "key") }) - { - if (HasLibraryResourceWithName(*resourceKey)) - { - return std::wstring{ GetLibraryResourceString(*resourceKey) }; - } - } - } - else if (name.isString()) - { - return JsonUtils::GetValue(name); - } - } - else if (json.isMember(JsonKey(NameKey))) - { - // { "name": null, "command": "copy" } will land in this case, which - // should also be used for unbinding. - return std::wstring{}; - } - - return std::nullopt; + _icon = val; } // Method Description: @@ -252,7 +310,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation result->_nestedCommand = true; } - JsonUtils::GetValueForKey(json, IconKey, result->_iconPath); + JsonUtils::GetValueForKey(json, IconKey, result->_icon); // If we're a nested command, we can ignore the current action. if (!nested) @@ -276,7 +334,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // currently have. It'll probably generate something like "New tab, // profile: ${profile.name}". This string will only be temporarily // used internally, so there's no problem. - result->_name = _nameFromJson(json); + JsonUtils::GetValueForKey(json, NameKey, result->_name); // Stash the original json value in this object. If the command is // iterable, we'll need to re-parse it later, once we know what all the @@ -302,8 +360,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, IDKey, result->_ID); JsonUtils::GetValueForKey(json, DescriptionKey, result->_Description); - JsonUtils::GetValueForKey(json, IconKey, result->_iconPath); - result->_name = _nameFromJson(json); + JsonUtils::GetValueForKey(json, IconKey, result->_icon); + JsonUtils::GetValueForKey(json, NameKey, result->_name); const auto action{ ShortcutAction::SendInput }; IActionArgs args{ nullptr }; @@ -341,12 +399,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation if (result->ActionAndArgs().Action() == ShortcutAction::Invalid && !result->HasNestedCommands()) { // If there wasn't a parsed command, then try to get the - // name from the json blob. If that name currently + // name that *was* parsed. If that name currently // exists in our list of commands, we should remove it. - const auto name = _nameFromJson(value); - if (name.has_value() && !name->empty()) + const auto name = result->Name(); + if (!name.empty()) { - commands.Remove(*name); + commands.Remove(name); } } else @@ -381,7 +439,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } else { - JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath); + JsonUtils::SetValueForKey(cmdJson, IconKey, _icon); JsonUtils::SetValueForKey(cmdJson, NameKey, _name); if (!_Description.empty()) { @@ -489,7 +547,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // - expandable: the Command to potentially turn into more commands // - profiles: A list of all the profiles that this command should be expanded on. // Return Value: - // - and empty vector if the command wasn't expandable, otherwise a list of + // - and empty vector if the command wasn't expandable; otherwise, a list of // the newly-created commands. std::vector Command::_expandCommand(Command* const expandable, IVectorView profiles, @@ -552,7 +610,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // - Escape the profile name for JSON appropriately auto escapedProfileName = _escapeForJson(til::u16u8(p.Name())); - auto escapedProfileIcon = _escapeForJson(til::u16u8(p.EvaluatedIcon())); + // Use the fully resolved icon here so it doesn't have to go around the resolver again + auto escapedProfileIcon = _escapeForJson(til::u16u8(p.Icon().Resolved())); auto newJsonString = til::replace_needle_in_haystack(oldJsonString, ProfileNameToken, escapedProfileName); @@ -593,6 +652,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return newCommands; } + void Command::ResolveMediaResourcesWithBasePath(const winrt::hstring& basePath, const Model::MediaResourceResolver& resolver) + { + if (_icon && *_icon) + { + ResolveIconMediaResource(_Origin, basePath, *_icon, resolver); + } + } + winrt::Windows::Foundation::Collections::IVector Command::ParsePowerShellMenuComplete(winrt::hstring json, int32_t replaceLength) { if (json.empty()) @@ -612,9 +679,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation std::vector result; const auto parseElement = [&](const auto& element) { - winrt::hstring completionText; - winrt::hstring listText; - winrt::hstring tooltipText; + std::wstring completionText; + std::wstring listText; + std::wstring tooltipText; JsonUtils::GetValueForKey(element, "CompletionText", completionText); JsonUtils::GetValueForKey(element, "ListItemText", listText); JsonUtils::GetValueForKey(element, "ToolTip", tooltipText); @@ -623,12 +690,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring{ fmt::format(FMT_COMPILE(L"{:\x7f^{}}{}"), L"", replaceLength, - static_cast(completionText)) }); + completionText) }); Model::ActionAndArgs actionAndArgs{ ShortcutAction::SendInput, *args }; auto c = winrt::make_self(); - c->_name = listText; + c->_name = CommandNameOrResource{ .name = listText }; c->_Description = tooltipText; c->_ActionAndArgs = actionAndArgs; // Try to assign a sensible icon based on the result type. These are @@ -641,34 +708,34 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation switch (resultType) { case 1: // History -> 0xe81c History - c->_iconPath = L"\ue81c"; + c->_icon = MediaResource::FromString(L"\ue81c"); break; case 2: // Command -> 0xecaa AppIconDefault - c->_iconPath = L"\uecaa"; + c->_icon = MediaResource::FromString(L"\uecaa"); break; case 3: // ProviderItem -> 0xe8e4 AlignLeft - c->_iconPath = L"\ue8e4"; + c->_icon = MediaResource::FromString(L"\ue8e4"); break; case 4: // ProviderContainer -> 0xe838 FolderOpen - c->_iconPath = L"\ue838"; + c->_icon = MediaResource::FromString(L"\ue838"); break; case 5: // Property -> 0xe7c1 Flag - c->_iconPath = L"\ue7c1"; + c->_icon = MediaResource::FromString(L"\ue7c1"); break; case 6: // Method -> 0xecaa AppIconDefault - c->_iconPath = L"\uecaa"; + c->_icon = MediaResource::FromString(L"\uecaa"); break; case 7: // ParameterName -> 0xe7c1 Flag - c->_iconPath = L"\ue7c1"; + c->_icon = MediaResource::FromString(L"\ue7c1"); break; case 8: // ParameterValue -> 0xf000 KnowledgeArticle - c->_iconPath = L"\uf000"; + c->_icon = MediaResource::FromString(L"\uf000"); break; case 10: // Namespace -> 0xe943 Code - c->_iconPath = L"\ue943"; + c->_icon = MediaResource::FromString(L"\ue943"); break; case 13: // DynamicKeyword -> 0xe945 LightningBolt - c->_iconPath = L"\ue945"; + c->_icon = MediaResource::FromString(L"\ue945"); break; } } @@ -733,8 +800,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto command = winrt::make_self(); command->_ActionAndArgs = actionAndArgs; - command->_name = winrt::hstring{ line }; - command->_iconPath = iconPath; + command->_name = CommandNameOrResource{ .name = std::wstring{ line } }; + command->_icon = MediaResource::FromString(iconPath); result.push_back(*command); foundCommands[line] = true; } diff --git a/src/cascadia/TerminalSettingsModel/Command.h b/src/cascadia/TerminalSettingsModel/Command.h index 4d372bea9c..65dd0c9244 100644 --- a/src/cascadia/TerminalSettingsModel/Command.h +++ b/src/cascadia/TerminalSettingsModel/Command.h @@ -44,7 +44,15 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { struct Command : CommandT { + struct CommandNameOrResource + { + std::wstring name; + std::wstring resource; + }; + Command(); + static Model::Command NewUserCommand(); + static Model::Command CopyAsUserCommand(const Model::Command& originalCmd); com_ptr Copy() const; static winrt::com_ptr FromJson(const Json::Value& json, @@ -71,13 +79,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation bool HasName() const noexcept; hstring Name() const noexcept; void Name(const hstring& name); + hstring LanguageNeutralName() const noexcept; hstring ID() const noexcept; + void ID(const hstring& ID) noexcept; void GenerateID(); bool IDWasGenerated(); - hstring IconPath() const noexcept; - void IconPath(const hstring& val); + IMediaResource Icon() const noexcept; + void Icon(const IMediaResource& val); + + void ResolveMediaResourcesWithBasePath(const winrt::hstring& basePath, const Model::MediaResourceResolver& resolver); static Windows::Foundation::Collections::IVector ParsePowerShellMenuComplete(winrt::hstring json, int32_t replaceLength); static Windows::Foundation::Collections::IVector HistoryToCommands(Windows::Foundation::Collections::IVector history, @@ -85,18 +97,18 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation bool directories, hstring iconPath); + WINRT_PROPERTY(Model::ActionAndArgs, ActionAndArgs, Model::ActionAndArgs{}); WINRT_PROPERTY(ExpandCommandType, IterateOn, ExpandCommandType::None); - WINRT_PROPERTY(Model::ActionAndArgs, ActionAndArgs); WINRT_PROPERTY(OriginTag, Origin); WINRT_PROPERTY(winrt::hstring, Description, L""); private: Json::Value _originalJson; Windows::Foundation::Collections::IMap _subcommands{ nullptr }; - std::optional _name; + std::optional _name; std::wstring _ID; bool _IDWasGenerated{ false }; - std::optional _iconPath; + std::optional _icon; bool _nestedCommand{ false }; static std::vector _expandCommand(Command* const expandable, diff --git a/src/cascadia/TerminalSettingsModel/Command.idl b/src/cascadia/TerminalSettingsModel/Command.idl index a364eabb4d..5722b4b918 100644 --- a/src/cascadia/TerminalSettingsModel/Command.idl +++ b/src/cascadia/TerminalSettingsModel/Command.idl @@ -21,7 +21,7 @@ namespace Microsoft.Terminal.Settings.Model #undef ON_ALL_ACTIONS }; - [default_interface] runtimeclass ActionAndArgs { + runtimeclass ActionAndArgs { ActionAndArgs(); ActionAndArgs(ShortcutAction action, IActionArgs args); @@ -32,24 +32,28 @@ namespace Microsoft.Terminal.Settings.Model ShortcutAction Action; }; - [default_interface] runtimeclass Command : ISettingsModelObject + runtimeclass Command : ISettingsModelObject { Command(); + static Command NewUserCommand(); + static Command CopyAsUserCommand(Command originalCmd); - String Name { get; }; + String Name; + Boolean HasName(); + String LanguageNeutralName { get; }; String ID { get; }; + void GenerateID(); String Description { get; }; - ActionAndArgs ActionAndArgs { get; }; + ActionAndArgs ActionAndArgs; - String IconPath; + IMediaResource Icon; Boolean HasNestedCommands { get; }; Windows.Foundation.Collections.IMapView NestedCommands { get; }; static IVector ParsePowerShellMenuComplete(String json, Int32 replaceLength); static IVector HistoryToCommands(IVector commandHistory, String commandline, Boolean directories, String iconPath); - } } diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp index 7895bec7d9..e6e5d25e74 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp @@ -42,6 +42,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation DEFINE_ENUM_MAP(Microsoft::Terminal::Core::MatchMode, MatchMode); DEFINE_ENUM_MAP(Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI); DEFINE_ENUM_MAP(Microsoft::Terminal::Control::TextMeasurement, TextMeasurement); + DEFINE_ENUM_MAP(Microsoft::Terminal::Control::WarnAboutMultiLinePaste, WarnAboutMultiLinePaste); // Profile Settings DEFINE_ENUM_MAP(Model::CloseOnExitMode, CloseOnExitMode); @@ -53,6 +54,22 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation DEFINE_ENUM_MAP(Microsoft::Terminal::Core::AdjustTextMode, AdjustIndistinguishableColors); DEFINE_ENUM_MAP(Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle); + // Actions + DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::ResizeDirection, ResizeDirection); + DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::FocusDirection, FocusDirection); + DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::SplitDirection, SplitDirection); + DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::SplitType, SplitType); + DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::SettingsTarget, SettingsTarget); + DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::MoveTabDirection, MoveTabDirection); + DEFINE_ENUM_MAP(Microsoft::Terminal::Control::ScrollToMarkDirection, ScrollToMarkDirection); + DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::CommandPaletteLaunchMode, CommandPaletteLaunchMode); + DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::SuggestionsSource, SuggestionsSource); + DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::FindMatchDirection, FindMatchDirection); + DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::DesktopBehavior, DesktopBehavior); + DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::MonitorBehavior, MonitorBehavior); + DEFINE_ENUM_MAP(Microsoft::Terminal::Control::ClearBufferType, ClearBufferType); + DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::SelectOutputDirection, SelectOutputDirection); + // FontWeight is special because the JsonUtils::ConversionTrait for it // creates a FontWeight object, but we need to use the uint16_t value. winrt::Windows::Foundation::Collections::IMap EnumMappings::FontWeight() diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.h b/src/cascadia/TerminalSettingsModel/EnumMappings.h index 15fbf50a40..cca552546f 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.h +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.h @@ -38,6 +38,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static winrt::Windows::Foundation::Collections::IMap MatchMode(); static winrt::Windows::Foundation::Collections::IMap GraphicsAPI(); static winrt::Windows::Foundation::Collections::IMap TextMeasurement(); + static winrt::Windows::Foundation::Collections::IMap WarnAboutMultiLinePaste(); // Profile Settings static winrt::Windows::Foundation::Collections::IMap CloseOnExitMode(); @@ -49,6 +50,22 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static winrt::Windows::Foundation::Collections::IMap IntenseTextStyle(); static winrt::Windows::Foundation::Collections::IMap AdjustIndistinguishableColors(); static winrt::Windows::Foundation::Collections::IMap PathTranslationStyle(); + + // Actions + static winrt::Windows::Foundation::Collections::IMap ResizeDirection(); + static winrt::Windows::Foundation::Collections::IMap FocusDirection(); + static winrt::Windows::Foundation::Collections::IMap SplitDirection(); + static winrt::Windows::Foundation::Collections::IMap SplitType(); + static winrt::Windows::Foundation::Collections::IMap SettingsTarget(); + static winrt::Windows::Foundation::Collections::IMap MoveTabDirection(); + static winrt::Windows::Foundation::Collections::IMap ScrollToMarkDirection(); + static winrt::Windows::Foundation::Collections::IMap CommandPaletteLaunchMode(); + static winrt::Windows::Foundation::Collections::IMap SuggestionsSource(); + static winrt::Windows::Foundation::Collections::IMap FindMatchDirection(); + static winrt::Windows::Foundation::Collections::IMap DesktopBehavior(); + static winrt::Windows::Foundation::Collections::IMap MonitorBehavior(); + static winrt::Windows::Foundation::Collections::IMap ClearBufferType(); + static winrt::Windows::Foundation::Collections::IMap SelectOutputDirection(); }; } diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.idl b/src/cascadia/TerminalSettingsModel/EnumMappings.idl index 57735eccd5..7b0ac90e1f 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.idl +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.idl @@ -20,6 +20,7 @@ namespace Microsoft.Terminal.Settings.Model static Windows.Foundation.Collections.IMap MatchMode { get; }; static Windows.Foundation.Collections.IMap GraphicsAPI { get; }; static Windows.Foundation.Collections.IMap TextMeasurement { get; }; + static Windows.Foundation.Collections.IMap WarnAboutMultiLinePaste { get; }; // Profile Settings static Windows.Foundation.Collections.IMap CloseOnExitMode { get; }; @@ -31,5 +32,21 @@ namespace Microsoft.Terminal.Settings.Model static Windows.Foundation.Collections.IMap FontWeight { get; }; static Windows.Foundation.Collections.IMap IntenseTextStyle { get; }; static Windows.Foundation.Collections.IMap PathTranslationStyle { get; }; + + // Actions + static Windows.Foundation.Collections.IMap ResizeDirection { get; }; + static Windows.Foundation.Collections.IMap FocusDirection { get; }; + static Windows.Foundation.Collections.IMap SplitDirection { get; }; + static Windows.Foundation.Collections.IMap SplitType { get; }; + static Windows.Foundation.Collections.IMap SettingsTarget { get; }; + static Windows.Foundation.Collections.IMap MoveTabDirection { get; }; + static Windows.Foundation.Collections.IMap ScrollToMarkDirection { get; }; + static Windows.Foundation.Collections.IMap CommandPaletteLaunchMode { get; }; + static Windows.Foundation.Collections.IMap SuggestionsSource { get; }; + static Windows.Foundation.Collections.IMap FindMatchDirection { get; }; + static Windows.Foundation.Collections.IMap DesktopBehavior { get; }; + static Windows.Foundation.Collections.IMap MonitorBehavior { get; }; + static Windows.Foundation.Collections.IMap ClearBufferType { get; }; + static Windows.Foundation.Collections.IMap SelectOutputDirection { get; }; } } diff --git a/src/cascadia/TerminalSettingsModel/FolderEntry.cpp b/src/cascadia/TerminalSettingsModel/FolderEntry.cpp index c0613c116f..842fede214 100644 --- a/src/cascadia/TerminalSettingsModel/FolderEntry.cpp +++ b/src/cascadia/TerminalSettingsModel/FolderEntry.cpp @@ -25,7 +25,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } FolderEntry::FolderEntry(const winrt::hstring& name) noexcept : - FolderEntryT(NewTabMenuEntryType::Folder), + FolderEntryT(NewTabMenuEntryType::Folder), _Name{ name } { } @@ -35,7 +35,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto json = NewTabMenuEntry::ToJson(); JsonUtils::SetValueForKey(json, NameKey, _Name); - JsonUtils::SetValueForKey(json, IconKey, _Icon); + JsonUtils::SetValueForKey(json, IconKey, _icon); JsonUtils::SetValueForKey(json, EntriesKey, _RawEntries); JsonUtils::SetValueForKey(json, InliningKey, _Inlining); JsonUtils::SetValueForKey(json, AllowEmptyKey, _AllowEmpty); @@ -48,7 +48,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto entry = winrt::make_self(); JsonUtils::GetValueForKey(json, NameKey, entry->_Name); - JsonUtils::GetValueForKey(json, IconKey, entry->_Icon); + JsonUtils::GetValueForKey(json, IconKey, entry->_icon); JsonUtils::GetValueForKey(json, EntriesKey, entry->_RawEntries); JsonUtils::GetValueForKey(json, InliningKey, entry->_Inlining); JsonUtils::GetValueForKey(json, AllowEmptyKey, entry->_AllowEmpty); @@ -127,7 +127,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { auto entry = winrt::make_self(); entry->_Name = _Name; - entry->_Icon = _Icon; + entry->_icon = _icon; entry->_Inlining = _Inlining; entry->_AllowEmpty = _AllowEmpty; @@ -141,4 +141,24 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } return *entry; } + + void FolderEntry::ResolveMediaResourcesWithBasePath(const winrt::hstring& basePath, const Model::MediaResourceResolver& resolver) + { + if (_icon) + { + // TODO GH#19191 (Hardcoded Origin, since that's the only place it could have come from) + ResolveIconMediaResource(OriginTag::User, basePath, _icon, resolver); + } + + if (_RawEntries) + { + for (const auto& entry : _RawEntries) + { + if (const auto resolvable{ entry.try_as() }) + { + resolvable->ResolveMediaResourcesWithBasePath(basePath, resolver); + } + } + } + } } diff --git a/src/cascadia/TerminalSettingsModel/FolderEntry.h b/src/cascadia/TerminalSettingsModel/FolderEntry.h index 0334df04d7..c72da2e977 100644 --- a/src/cascadia/TerminalSettingsModel/FolderEntry.h +++ b/src/cascadia/TerminalSettingsModel/FolderEntry.h @@ -17,10 +17,11 @@ Author(s): #include "pch.h" #include "NewTabMenuEntry.h" #include "FolderEntry.g.h" +#include "MediaResourceSupport.h" namespace winrt::Microsoft::Terminal::Settings::Model::implementation { - struct FolderEntry : FolderEntryT + struct FolderEntry : FolderEntryT { public: FolderEntry() noexcept; @@ -37,11 +38,25 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // entries to be rendered to WinRT. winrt::Windows::Foundation::Collections::IVector Entries() const; + void ResolveMediaResourcesWithBasePath(const winrt::hstring& basePath, const Model::MediaResourceResolver& resolver) override; + + IMediaResource Icon() const noexcept + { + return _icon ? _icon : MediaResource::Empty(); + } + + void Icon(const IMediaResource& val) + { + _icon = val; + } + WINRT_PROPERTY(winrt::hstring, Name); - WINRT_PROPERTY(winrt::hstring, Icon); WINRT_PROPERTY(FolderEntryInlining, Inlining, FolderEntryInlining::Never); WINRT_PROPERTY(bool, AllowEmpty, false); WINRT_PROPERTY(winrt::Windows::Foundation::Collections::IVector, RawEntries); + + private: + IMediaResource _icon; }; } diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 8a699eb865..afb2a3f187 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -9,6 +9,8 @@ #include "GlobalAppSettings.g.cpp" +#include "MediaResourceSupport.h" + #include using namespace Microsoft::Terminal::Settings::Model; @@ -92,6 +94,14 @@ winrt::com_ptr GlobalAppSettings::Copy() const globals->_NewTabMenu->Append(get_self(entry)->Copy()); } } + if (_DisabledProfileSources) + { + globals->_DisabledProfileSources = winrt::single_threaded_vector(); + for (const auto& src : *_DisabledProfileSources) + { + globals->_DisabledProfileSources->Append(src); + } + } for (const auto& parent : _parents) { @@ -370,6 +380,25 @@ bool GlobalAppSettings::ShouldUsePersistedLayout() const return FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; } +void GlobalAppSettings::ResolveMediaResources(const Model::MediaResourceResolver& resolver) +{ + _actionMap->ResolveMediaResourcesWithBasePath(SourceBasePath, resolver); + if (_NewTabMenu) + { + for (const auto& entry : *_NewTabMenu) + { + if (const auto resolvable{ entry.try_as() }) + { + resolvable->ResolveMediaResourcesWithBasePath(SourceBasePath, resolver); + } + } + } + for (auto& parent : _parents) + { + parent->ResolveMediaResources(resolver); + } +} + void GlobalAppSettings::_logSettingSet(const std::string_view& setting) { if (setting == "theme") @@ -430,6 +459,46 @@ void GlobalAppSettings::_logSettingSet(const std::string_view& setting) } } +void GlobalAppSettings::UpdateCommandID(const Model::Command& cmd, winrt::hstring newID) +{ + const auto oldID = cmd.ID(); + _actionMap->UpdateCommandID(cmd, newID); + // newID might have been empty when this function was called, if so actionMap would have generated a new ID, use that + newID = cmd.ID(); + if (_NewTabMenu) + { + // Recursive lambda function to look through all the new tab menu entries and update IDs accordingly + std::function recursiveEntryIdUpdate; + recursiveEntryIdUpdate = [&](const Model::NewTabMenuEntry& entry) { + if (entry.Type() == NewTabMenuEntryType::Action) + { + if (const auto actionEntry{ entry.try_as() }) + { + if (actionEntry.ActionId() == oldID) + { + actionEntry.ActionId(newID); + } + } + } + else if (entry.Type() == NewTabMenuEntryType::Folder) + { + if (const auto folderEntry{ entry.try_as() }) + { + for (const auto& nestedEntry : folderEntry.RawEntries()) + { + recursiveEntryIdUpdate(nestedEntry); + } + } + } + }; + + for (const auto& entry : *_NewTabMenu) + { + recursiveEntryIdUpdate(entry); + } + } +} + void GlobalAppSettings::_logSettingIfSet(const std::string_view& setting, const bool isSet) { if (isSet) diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index 59dde1106b..58147fac44 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -47,6 +47,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Model::ColorScheme DuplicateColorScheme(const Model::ColorScheme& scheme); Model::ActionMap ActionMap() const noexcept; + void UpdateCommandID(const Model::Command& cmd, winrt::hstring newID); static com_ptr FromJson(const Json::Value& json, const OriginTag origin = OriginTag::None); void LayerJson(const Json::Value& json, const OriginTag origin); @@ -75,6 +76,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void LogSettingChanges(std::set& changes, const std::string_view& context) const; + void ResolveMediaResources(const Model::MediaResourceResolver& resolver); + + winrt::hstring SourceBasePath; + INHERITABLE_SETTING(Model::GlobalAppSettings, hstring, UnparsedDefaultProfile, L""); #define GLOBAL_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \ diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index 9bf5267733..e003660f32 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -70,7 +70,7 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_SETTING(Boolean, InputServiceWarning); INHERITABLE_SETTING(Microsoft.Terminal.Control.CopyFormat, CopyFormatting); INHERITABLE_SETTING(Boolean, WarnAboutLargePaste); - INHERITABLE_SETTING(Boolean, WarnAboutMultiLinePaste); + INHERITABLE_SETTING(Microsoft.Terminal.Control.WarnAboutMultiLinePaste, WarnAboutMultiLinePaste); INHERITABLE_SETTING(Boolean, TrimPaste); INHERITABLE_SETTING(LaunchPosition, InitialPosition); INHERITABLE_SETTING(Boolean, CenterOnLaunch); @@ -90,6 +90,8 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_SETTING(Boolean, DisableAnimations); INHERITABLE_SETTING(String, StartupActions); INHERITABLE_SETTING(Boolean, FocusFollowMouse); + INHERITABLE_SETTING(Boolean, ScrollToZoom); + INHERITABLE_SETTING(Boolean, ScrollToChangeOpacity); INHERITABLE_SETTING(WindowingMode, WindowingBehavior); INHERITABLE_SETTING(Boolean, TrimBlockSelection); INHERITABLE_SETTING(Boolean, DetectURLs); diff --git a/src/cascadia/TerminalSettingsModel/IAppearanceConfig.idl b/src/cascadia/TerminalSettingsModel/IAppearanceConfig.idl index f05cb85399..b4b9c22004 100644 --- a/src/cascadia/TerminalSettingsModel/IAppearanceConfig.idl +++ b/src/cascadia/TerminalSettingsModel/IAppearanceConfig.idl @@ -2,6 +2,7 @@ // Licensed under the MIT license. import "Profile.idl"; +import "ISettingsModelObject.idl"; #include "IInheritable.idl.h" #define INHERITABLE_APPEARANCE_SETTING(Type, Name) \ @@ -42,16 +43,15 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_APPEARANCE_SETTING(Microsoft.Terminal.Core.CursorStyle, CursorShape); INHERITABLE_APPEARANCE_SETTING(UInt32, CursorHeight); - INHERITABLE_APPEARANCE_SETTING(String, BackgroundImagePath); - String ExpandedBackgroundImagePath { get; }; + INHERITABLE_APPEARANCE_SETTING(IMediaResource, BackgroundImagePath); INHERITABLE_APPEARANCE_SETTING(Single, BackgroundImageOpacity); INHERITABLE_APPEARANCE_SETTING(Windows.UI.Xaml.Media.Stretch, BackgroundImageStretchMode); INHERITABLE_APPEARANCE_SETTING(ConvergedAlignment, BackgroundImageAlignment); INHERITABLE_APPEARANCE_SETTING(Boolean, RetroTerminalEffect); - INHERITABLE_APPEARANCE_SETTING(String, PixelShaderPath); - INHERITABLE_APPEARANCE_SETTING(String, PixelShaderImagePath); + INHERITABLE_APPEARANCE_SETTING(IMediaResource, PixelShaderPath); + INHERITABLE_APPEARANCE_SETTING(IMediaResource, PixelShaderImagePath); INHERITABLE_APPEARANCE_SETTING(IntenseStyle, IntenseTextStyle); INHERITABLE_APPEARANCE_SETTING(Microsoft.Terminal.Core.AdjustTextMode, AdjustIndistinguishableColors); INHERITABLE_APPEARANCE_SETTING(Single, Opacity); diff --git a/src/cascadia/TerminalSettingsModel/IDynamicProfileGenerator.h b/src/cascadia/TerminalSettingsModel/IDynamicProfileGenerator.h index db57944b10..9144bcb325 100644 --- a/src/cascadia/TerminalSettingsModel/IDynamicProfileGenerator.h +++ b/src/cascadia/TerminalSettingsModel/IDynamicProfileGenerator.h @@ -30,6 +30,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model public: virtual ~IDynamicProfileGenerator() = default; virtual std::wstring_view GetNamespace() const noexcept = 0; + virtual std::wstring_view GetDisplayName() const noexcept = 0; + virtual std::wstring_view GetIcon() const noexcept = 0; virtual void GenerateProfiles(std::vector>& profiles) const = 0; }; }; diff --git a/src/cascadia/TerminalSettingsModel/IInheritable.h b/src/cascadia/TerminalSettingsModel/IInheritable.h index 1099c128bc..aae47f61db 100644 --- a/src/cascadia/TerminalSettingsModel/IInheritable.h +++ b/src/cascadia/TerminalSettingsModel/IInheritable.h @@ -97,7 +97,7 @@ public: \ { \ if (auto source{ parent->_get##name##OverrideSourceImpl() }) \ { \ - return source; \ + return *source; \ } \ } \ \ @@ -136,12 +136,12 @@ private: \ return std::nullopt; \ } \ \ - projectedType _get##name##OverrideSourceImpl() const \ + auto _get##name##OverrideSourceImpl()->decltype(get_strong()) \ { \ /*we have a value*/ \ if (_##name) \ { \ - return *this; \ + return get_strong(); \ } \ \ /*user set value was not set*/ \ @@ -156,6 +156,29 @@ private: \ \ /*no value was found*/ \ return nullptr; \ + } \ + \ + auto _get##name##OverrideSourceAndValueImpl() \ + ->std::pair \ + { \ + /*we have a value*/ \ + if (_##name) \ + { \ + return { get_strong(), _##name }; \ + } \ + \ + /*user set value was not set*/ \ + /*iterate through parents to find one with a value*/ \ + for (const auto& parent : _parents) \ + { \ + if (auto source{ parent->_get##name##OverrideSourceImpl() }) \ + { \ + return { source, source->_##name }; \ + } \ + } \ + \ + /*no value was found*/ \ + return { nullptr, std::nullopt }; \ } // Use INHERITABLE_SETTING and INHERITABLE_NULLABLE_SETTING to implement diff --git a/src/cascadia/TerminalSettingsModel/ISettingsModelObject.idl b/src/cascadia/TerminalSettingsModel/ISettingsModelObject.idl index 21bc4b9c32..1adf3e4766 100644 --- a/src/cascadia/TerminalSettingsModel/ISettingsModelObject.idl +++ b/src/cascadia/TerminalSettingsModel/ISettingsModelObject.idl @@ -17,4 +17,19 @@ namespace Microsoft.Terminal.Settings.Model interface ISettingsModelObject { OriginTag Origin { get; }; } + + interface IMediaResource { + String Path { get; }; + String Resolved { get; }; + void Resolve(String finalValue); + void Reject(); + Boolean Ok { get; }; + }; + + delegate void MediaResourceResolver(OriginTag origin, String basePath, IMediaResource resource); + + static runtimeclass MediaResourceHelper { + static IMediaResource FromString(String s); + static IMediaResource Empty(); + }; } diff --git a/src/cascadia/TerminalSettingsModel/JsonUtils.h b/src/cascadia/TerminalSettingsModel/JsonUtils.h index ab2b93ce96..ef3256f426 100644 --- a/src/cascadia/TerminalSettingsModel/JsonUtils.h +++ b/src/cascadia/TerminalSettingsModel/JsonUtils.h @@ -89,7 +89,7 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils { static constexpr std::optional EmptyV() { return std::nullopt; } static constexpr bool HasValue(const std::optional& o) { return o.has_value(); } - // We can return a reference here because the original value is stored inside an std::optional + // We can return a reference here because the original value is stored inside a std::optional static constexpr auto&& Value(const std::optional& o) { return *o; } }; diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index eb195f2695..9040f89cbf 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -24,6 +24,8 @@ Author(s): X(hstring, WordDelimiters, "wordDelimiters", DEFAULT_WORD_DELIMITERS) \ X(bool, CopyOnSelect, "copyOnSelect", false) \ X(bool, FocusFollowMouse, "focusFollowMouse", false) \ + X(bool, ScrollToZoom, "experimental.scrollToZoom", true) \ + X(bool, ScrollToChangeOpacity, "experimental.scrollToChangeOpacity", true) \ X(winrt::Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI, "rendering.graphicsAPI") \ X(bool, DisablePartialInvalidation, "rendering.disablePartialInvalidation", false) \ X(bool, SoftwareRendering, "rendering.software", false) \ @@ -44,7 +46,7 @@ Author(s): X(bool, InputServiceWarning, "warning.inputService", true) \ X(winrt::Microsoft::Terminal::Control::CopyFormat, CopyFormatting, "copyFormatting", 0) \ X(bool, WarnAboutLargePaste, "warning.largePaste", true) \ - X(bool, WarnAboutMultiLinePaste, "warning.multiLinePaste", true) \ + X(winrt::Microsoft::Terminal::Control::WarnAboutMultiLinePaste, WarnAboutMultiLinePaste, "warning.multiLinePaste", winrt::Microsoft::Terminal::Control::WarnAboutMultiLinePaste::Automatic) \ X(Model::LaunchPosition, InitialPosition, "initialPosition", nullptr, nullptr) \ X(bool, CenterOnLaunch, "centerOnLaunch", false) \ X(Model::FirstWindowPreference, FirstWindowPreference, "firstWindowPreference", FirstWindowPreference::DefaultProfile) \ @@ -85,6 +87,7 @@ Author(s): X(Microsoft::Terminal::Control::ScrollbarState, ScrollState, "scrollbarState", Microsoft::Terminal::Control::ScrollbarState::Visible) \ X(Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, "antialiasingMode", Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale) \ X(hstring, StartingDirectory, "startingDirectory") \ + X(IMediaResource, Icon, "icon", implementation::MediaResource::FromString(L"\uE756")) \ X(bool, SuppressApplicationTitle, "suppressApplicationTitle", false) \ X(guid, ConnectionType, "connectionType") \ X(CloseOnExitMode, CloseOnExit, "closeOnExit", CloseOnExitMode::Automatic) \ @@ -92,7 +95,7 @@ Author(s): X(Model::BellStyle, BellStyle, "bellStyle", BellStyle::Audible) \ X(IEnvironmentVariableMap, EnvironmentVariables, "environment", nullptr) \ X(bool, RightClickContextMenu, "rightClickContextMenu", false) \ - X(Windows::Foundation::Collections::IVector, BellSound, "bellSound", nullptr) \ + X(Windows::Foundation::Collections::IVector, BellSound, "bellSound", nullptr) \ X(bool, Elevate, "elevate", false) \ X(bool, AutoMarkPrompts, "autoMarkPrompts", true) \ X(bool, ShowMarks, "showMarksOnScrollbar", false) \ @@ -131,12 +134,12 @@ Author(s): X(float, BackgroundImageOpacity, "backgroundImageOpacity", 1.0f) \ X(winrt::Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, "backgroundImageStretchMode", winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill) \ X(bool, RetroTerminalEffect, "experimental.retroTerminalEffect", false) \ - X(hstring, PixelShaderPath, "experimental.pixelShaderPath") \ - X(hstring, PixelShaderImagePath, "experimental.pixelShaderImagePath") \ + X(IMediaResource, PixelShaderPath, "experimental.pixelShaderPath", implementation::MediaResource::Empty()) \ + X(IMediaResource, PixelShaderImagePath, "experimental.pixelShaderImagePath", implementation::MediaResource::Empty()) \ X(ConvergedAlignment, BackgroundImageAlignment, "backgroundImageAlignment", ConvergedAlignment::Horizontal_Center | ConvergedAlignment::Vertical_Center) \ - X(hstring, BackgroundImagePath, "backgroundImage") \ + X(IMediaResource, BackgroundImagePath, "backgroundImage", implementation::MediaResource::Empty()) \ X(Model::IntenseStyle, IntenseTextStyle, "intenseTextStyle", Model::IntenseStyle::Bright) \ - X(Core::AdjustTextMode, AdjustIndistinguishableColors, "adjustIndistinguishableColors", Core::AdjustTextMode::Never) \ + X(Core::AdjustTextMode, AdjustIndistinguishableColors, "adjustIndistinguishableColors", Core::AdjustTextMode::Automatic) \ X(bool, UseAcrylic, "useAcrylic", false) // Intentionally omitted Appearance settings: diff --git a/src/cascadia/TerminalSettingsModel/MatchProfilesEntry.cpp b/src/cascadia/TerminalSettingsModel/MatchProfilesEntry.cpp index 6946eb98e8..dcb3057727 100644 --- a/src/cascadia/TerminalSettingsModel/MatchProfilesEntry.cpp +++ b/src/cascadia/TerminalSettingsModel/MatchProfilesEntry.cpp @@ -36,41 +36,71 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto entry = winrt::make_self(); JsonUtils::GetValueForKey(json, NameKey, entry->_Name); + entry->_validateAndPopulateNameRegex(); + JsonUtils::GetValueForKey(json, CommandlineKey, entry->_Commandline); + entry->_validateAndPopulateCommandlineRegex(); + JsonUtils::GetValueForKey(json, SourceKey, entry->_Source); + entry->_validateAndPopulateSourceRegex(); return entry; } + // Returns true if all regexes are valid, false otherwise + bool MatchProfilesEntry::ValidateRegexes() const + { + return !(_invalidName || _invalidCommandline || _invalidSource); + } + +#define DEFINE_VALIDATE_FUNCTION(name) \ + void MatchProfilesEntry::_validateAndPopulate##name##Regex() noexcept \ + { \ + _invalid##name = false; \ + if (_##name.empty()) \ + { \ + /* empty field is valid*/ \ + return; \ + } \ + UErrorCode status = U_ZERO_ERROR; \ + _##name##Regex = til::ICU::CreateRegex(_##name, 0, &status); \ + if (U_FAILURE(status)) \ + { \ + _invalid##name = true; \ + _##name##Regex.reset(); \ + } \ + } + + DEFINE_VALIDATE_FUNCTION(Name); + DEFINE_VALIDATE_FUNCTION(Commandline); + DEFINE_VALIDATE_FUNCTION(Source); + bool MatchProfilesEntry::MatchesProfile(const Model::Profile& profile) { - // We use an optional here instead of a simple bool directly, since there is no - // sensible default value for the desired semantics: the first property we want - // to match on should always be applied (so one would set "true" as a default), - // but if none of the properties are set, the default return value should be false - // since this entry type is expected to behave like a positive match/whitelist. - // - // The easiest way to deal with this neatly is to use an optional, then for any - // property to match we consider a null value to be "true", and for the return - // value of the function we consider the null value to be "false". - auto isMatching = std::optional{}; + auto isMatch = [](const til::ICU::unique_uregex& regex, std::wstring_view text) { + if (text.empty()) + { + return false; + } + UErrorCode status = U_ZERO_ERROR; + uregex_setText(regex.get(), reinterpret_cast(text.data()), static_cast(text.size()), &status); + const auto match = uregex_matches(regex.get(), 0, &status); + return status == U_ZERO_ERROR && match; + }; - if (!_Name.empty()) + if (!_Name.empty() && isMatch(_NameRegex, profile.Name())) { - isMatching = { isMatching.value_or(true) && _Name == profile.Name() }; + return true; } - - if (!_Source.empty()) + else if (!_Source.empty() && isMatch(_SourceRegex, profile.Source())) { - isMatching = { isMatching.value_or(true) && _Source == profile.Source() }; + return true; } - - if (!_Commandline.empty()) + else if (!_Commandline.empty() && isMatch(_CommandlineRegex, profile.Commandline())) { - isMatching = { isMatching.value_or(true) && _Commandline == profile.Commandline() }; + return true; } - - return isMatching.value_or(false); + return false; } Model::NewTabMenuEntry MatchProfilesEntry::Copy() const diff --git a/src/cascadia/TerminalSettingsModel/MatchProfilesEntry.h b/src/cascadia/TerminalSettingsModel/MatchProfilesEntry.h index 815d1be3e8..b16caa4b08 100644 --- a/src/cascadia/TerminalSettingsModel/MatchProfilesEntry.h +++ b/src/cascadia/TerminalSettingsModel/MatchProfilesEntry.h @@ -17,6 +17,30 @@ Author(s): #include "ProfileCollectionEntry.h" #include "MatchProfilesEntry.g.h" +#include + +// This macro defines the getter and setter for a regex property. +// The setter tries to instantiate the regex immediately and caches +// it if successful. If it fails, it sets a boolean flag to track that +// it failed. +#define DEFINE_MATCH_PROFILE_REGEX_PROPERTY(name) \ +public: \ + hstring name() const noexcept \ + { \ + return _##name; \ + } \ + void name(const hstring& value) noexcept \ + { \ + _##name = value; \ + _validateAndPopulate##name##Regex(); \ + } \ + \ +private: \ + void _validateAndPopulate##name##Regex() noexcept; \ + \ + hstring _##name; \ + til::ICU::unique_uregex _##name##Regex; \ + bool _invalid##name{ false }; namespace winrt::Microsoft::Terminal::Settings::Model::implementation { @@ -30,11 +54,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Json::Value ToJson() const override; static com_ptr FromJson(const Json::Value& json); + bool ValidateRegexes() const; bool MatchesProfile(const Model::Profile& profile); - WINRT_PROPERTY(winrt::hstring, Name); - WINRT_PROPERTY(winrt::hstring, Commandline); - WINRT_PROPERTY(winrt::hstring, Source); + DEFINE_MATCH_PROFILE_REGEX_PROPERTY(Name) + DEFINE_MATCH_PROFILE_REGEX_PROPERTY(Commandline) + DEFINE_MATCH_PROFILE_REGEX_PROPERTY(Source) }; } diff --git a/src/cascadia/TerminalSettingsModel/MediaResourceSupport.cpp b/src/cascadia/TerminalSettingsModel/MediaResourceSupport.cpp new file mode 100644 index 0000000000..99077b1dda --- /dev/null +++ b/src/cascadia/TerminalSettingsModel/MediaResourceSupport.cpp @@ -0,0 +1,3 @@ +#include "pch.h" +#include "MediaResourceSupport.h" +#include "MediaResourceHelper.g.cpp" diff --git a/src/cascadia/TerminalSettingsModel/MediaResourceSupport.h b/src/cascadia/TerminalSettingsModel/MediaResourceSupport.h new file mode 100644 index 0000000000..4546801f7a --- /dev/null +++ b/src/cascadia/TerminalSettingsModel/MediaResourceSupport.h @@ -0,0 +1,147 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. +--*/ + +#pragma once + +#include "MediaResourceHelper.g.h" +#include "../types/inc/utils.hpp" + +struct + __declspec(uuid("6068ee1b-1ea0-4804-993a-42ef0c58d867")) + IMediaResourceContainer : public IUnknown +{ + virtual void ResolveMediaResources(const winrt::Microsoft::Terminal::Settings::Model::MediaResourceResolver& resolver) = 0; +}; + +struct + __declspec(uuid("9f11361c-7c8f-45c9-8948-36b66d67eca8")) + IPathlessMediaResourceContainer : public IUnknown +{ + virtual void ResolveMediaResourcesWithBasePath(const winrt::hstring& basePath, const winrt::Microsoft::Terminal::Settings::Model::MediaResourceResolver& resolver) = 0; +}; + +namespace winrt::Microsoft::Terminal::Settings::Model::implementation +{ + struct EmptyMediaResource : winrt::implements + { + // Micro-optimization: having one empty resource that contains no actual paths saves us a few bytes per object + winrt::hstring Path() { return {}; }; + winrt::hstring Resolved() { return {}; } + + bool Ok() const { return false; } + + void Resolve(const winrt::hstring&) + { + assert(false); // Somebody tried to resolve the empty media resource + } + + void Reject() + { + assert(false); // Somebody tried to resolve the empty media resource + } + }; + + /* MEDIA RESOURCES + * + * A media resource is a container for two strings: one pre-validation path and one post-validation path. + * It is expected that, before they are used, they are passed through a resolver. + * + * A resolver may Resolve() a media resource to a path or Reject() it. + * - If it is Resolved, the new path is accessible via the Resolved() method. + * - If it is Rejected, the Resolved() method will return the empty string. + * + * A media resource is considered `Ok` if it has been Resolved to a real path. + * + * As a special case, if it has been neither resolved nor rejected, it will return the pre-validation + * path--this is intended to aid its use in places where the risk of using an unresolved media path + * is fine. + */ + struct MediaResource : winrt::implements + { + MediaResource() {} + MediaResource(const winrt::hstring& p) : + value{ p } {} + + winrt::hstring Path() { return value; }; + winrt::hstring Resolved() { return resolved ? resolvedValue : value; } + + bool Ok() const { return ok; } + + void Resolve(const winrt::hstring& newPath) + { + resolvedValue = newPath; + ok = true; + resolved = true; + } + + void Reject() + { + resolvedValue = {}; + ok = false; + resolved = true; + } + + winrt::hstring value{}; + winrt::hstring resolvedValue{}; + bool ok{ false }; // Path() was transformed into a final and valid Resolved path + bool resolved{ false }; // This resource has been visited by a resolver, regardless of the outcome. + + static IMediaResource Empty() + { + static IMediaResource emptyResource{ winrt::make() }; + return emptyResource; + } + + static IMediaResource FromString(const winrt::hstring& string) + { + return winrt::make(string); + } + }; + + _TIL_INLINEPREFIX void ResolveMediaResource(const winrt::Microsoft::Terminal::Settings::Model::OriginTag origin, const winrt::hstring& basePath, const Model::IMediaResource& resource, const winrt::Microsoft::Terminal::Settings::Model::MediaResourceResolver& resolver) + { + const auto path{ resource.Path() }; + if (path.empty() || resource.Ok()) + { + // Don't resolve empty resources *or* resources which have already been found. + return; + } + resolver(origin, basePath, resource); + } + + _TIL_INLINEPREFIX void ResolveIconMediaResource(const winrt::Microsoft::Terminal::Settings::Model::OriginTag origin, const winrt::hstring& basePath, const Model::IMediaResource& resource, const winrt::Microsoft::Terminal::Settings::Model::MediaResourceResolver& resolver) + { + if (const winrt::hstring path{ resource.Path() }; !path.empty()) + { + if (::Microsoft::Console::Utils::IsLikelyToBeEmojiOrSymbolIcon(path)) + { + resource.Resolve(path); + return; + } + + ResolveMediaResource(origin, basePath, resource, resolver); + } + } + + // This exists to allow external consumers to call this code via WinRT + struct MediaResourceHelper + { + static winrt::Microsoft::Terminal::Settings::Model::IMediaResource FromString(hstring const& s) + { + return MediaResource::FromString(s); + } + static winrt::Microsoft::Terminal::Settings::Model::IMediaResource Empty() + { + return MediaResource::Empty(); + } + }; +} + +namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation +{ + struct MediaResourceHelper : MediaResourceHelperT + { + }; +} diff --git a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj index adf1610f5e..586198744e 100644 --- a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj +++ b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj @@ -46,6 +46,7 @@ NewTabMenuEntry.idl + DefaultTerminal.idl @@ -103,9 +104,6 @@ EnumMappings.idl - - TerminalSettings.idl - TerminalWarnings.idl @@ -176,9 +174,6 @@ FontConfig.idl - - TerminalSettings.idl - EnumMappings.idl @@ -206,11 +201,13 @@ NewTabMenuEntry.idl + + @@ -227,7 +224,6 @@ - diff --git a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj.filters b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj.filters index 3933a24281..18dfb59116 100644 --- a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj.filters +++ b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj.filters @@ -45,6 +45,7 @@ profileGeneration + @@ -108,7 +109,6 @@ - diff --git a/src/cascadia/TerminalSettingsModel/NewTabMenuEntry.idl b/src/cascadia/TerminalSettingsModel/NewTabMenuEntry.idl index c76de6e848..8e1999c728 100644 --- a/src/cascadia/TerminalSettingsModel/NewTabMenuEntry.idl +++ b/src/cascadia/TerminalSettingsModel/NewTabMenuEntry.idl @@ -33,7 +33,7 @@ namespace Microsoft.Terminal.Settings.Model Profile Profile; Int32 ProfileIndex; - String Icon; + IMediaResource Icon; } [default_interface] runtimeclass ActionEntry : NewTabMenuEntry @@ -41,7 +41,7 @@ namespace Microsoft.Terminal.Settings.Model ActionEntry(); String ActionId; - String Icon; + IMediaResource Icon; } enum FolderEntryInlining @@ -56,7 +56,7 @@ namespace Microsoft.Terminal.Settings.Model FolderEntry(String name); String Name; - String Icon; + IMediaResource Icon; FolderEntryInlining Inlining; Boolean AllowEmpty; diff --git a/src/cascadia/TerminalSettingsModel/PowershellCoreProfileGenerator.cpp b/src/cascadia/TerminalSettingsModel/PowershellCoreProfileGenerator.cpp index 14878c8278..01e4c491bd 100644 --- a/src/cascadia/TerminalSettingsModel/PowershellCoreProfileGenerator.cpp +++ b/src/cascadia/TerminalSettingsModel/PowershellCoreProfileGenerator.cpp @@ -15,12 +15,14 @@ #include #include #include +#include static constexpr std::wstring_view POWERSHELL_PFN{ L"Microsoft.PowerShell_8wekyb3d8bbwe" }; static constexpr std::wstring_view POWERSHELL_PREVIEW_PFN{ L"Microsoft.PowerShellPreview_8wekyb3d8bbwe" }; static constexpr std::wstring_view PWSH_EXE{ L"pwsh.exe" }; static constexpr std::wstring_view POWERSHELL_ICON{ L"ms-appx:///ProfileIcons/pwsh.png" }; static constexpr std::wstring_view POWERSHELL_PREVIEW_ICON{ L"ms-appx:///ProfileIcons/pwsh-preview.png" }; +static constexpr std::wstring_view GENERATOR_POWERSHELL_ICON{ L"ms-appx:///ProfileGeneratorIcons/PowerShell.png" }; static constexpr std::wstring_view POWERSHELL_PREFERRED_PROFILE_NAME{ L"PowerShell" }; namespace @@ -294,6 +296,16 @@ std::wstring_view PowershellCoreProfileGenerator::GetNamespace() const noexcept return PowershellCoreGeneratorNamespace; } +std::wstring_view PowershellCoreProfileGenerator::GetDisplayName() const noexcept +{ + return RS_(L"PowershellCoreProfileGeneratorDisplayName"); +} + +std::wstring_view PowershellCoreProfileGenerator::GetIcon() const noexcept +{ + return GENERATOR_POWERSHELL_ICON; +} + // Method Description: // - Checks if pwsh is installed, and if it is, creates a profile to launch it. // Arguments: diff --git a/src/cascadia/TerminalSettingsModel/PowershellCoreProfileGenerator.h b/src/cascadia/TerminalSettingsModel/PowershellCoreProfileGenerator.h index eaec9dacea..d473292e15 100644 --- a/src/cascadia/TerminalSettingsModel/PowershellCoreProfileGenerator.h +++ b/src/cascadia/TerminalSettingsModel/PowershellCoreProfileGenerator.h @@ -26,6 +26,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model static const std::wstring_view GetPreferredPowershellProfileName(); std::wstring_view GetNamespace() const noexcept override; + std::wstring_view GetDisplayName() const noexcept override; + std::wstring_view GetIcon() const noexcept override; void GenerateProfiles(std::vector>& profiles) const override; }; }; diff --git a/src/cascadia/TerminalSettingsModel/Profile.cpp b/src/cascadia/TerminalSettingsModel/Profile.cpp index d1b2efb08b..50aa36ec39 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.cpp +++ b/src/cascadia/TerminalSettingsModel/Profile.cpp @@ -27,7 +27,6 @@ static constexpr std::string_view NameKey{ "name" }; static constexpr std::string_view GuidKey{ "guid" }; static constexpr std::string_view SourceKey{ "source" }; static constexpr std::string_view HiddenKey{ "hidden" }; -static constexpr std::string_view IconKey{ "icon" }; static constexpr std::string_view FontInfoKey{ "font" }; static constexpr std::string_view PaddingKey{ "padding" }; @@ -112,7 +111,6 @@ winrt::com_ptr Profile::CopySettings() const profile->_Hidden = _Hidden; profile->_TabColor = _TabColor; profile->_Padding = _Padding; - profile->_Icon = _Icon; profile->_Origin = _Origin; profile->_FontInfo = *fontInfo; @@ -123,16 +121,10 @@ winrt::com_ptr Profile::CopySettings() const MTSM_PROFILE_SETTINGS(PROFILE_SETTINGS_COPY) #undef PROFILE_SETTINGS_COPY - // BellSound is an IVector, so we need to manually copy it over if (_BellSound) { - std::vector sounds; - sounds.reserve(_BellSound->Size()); - for (const auto& sound : *_BellSound) - { - sounds.emplace_back(sound); - } - profile->_BellSound = single_threaded_vector(std::move(sounds)); + // BellSound is an IVector<>, so we need to make a new vector pointing at the same objects + profile->_BellSound = winrt::single_threaded_vector(wil::to_vector(*_BellSound)); } if (_UnfocusedAppearance) @@ -195,9 +187,6 @@ void Profile::LayerJson(const Json::Value& json) JsonUtils::GetValueForKey(json, HiddenKey, _Hidden); _logSettingIfSet(HiddenKey, _Hidden.has_value()); - JsonUtils::GetValueForKey(json, IconKey, _Icon); - _logSettingIfSet(IconKey, _Icon.has_value()); - // Padding was never specified as an integer, but it was a common working mistake. // Allow it to be permissive. JsonUtils::GetValueForKey(json, PaddingKey, _Padding, JsonUtils::OptionalConverter>{}); @@ -310,7 +299,7 @@ std::wstring Profile::EvaluateStartingDirectory(const std::wstring& directory) } // Function Description: -// - Generates a unique guid for a profile, given the name. For an given name, will always return the same GUID. +// - Generates a unique guid for a profile, given the name. For a given name, will always return the same GUID. // Arguments: // - name: The name to generate a unique GUID from // Return Value: @@ -351,11 +340,6 @@ Json::Value Profile::ToJson() const JsonUtils::SetValueForKey(json, HiddenKey, writeBasicSettings ? Hidden() : _Hidden); JsonUtils::SetValueForKey(json, SourceKey, writeBasicSettings ? Source() : _Source); - // Recall: Icon isn't actually a setting in the MTSM_PROFILE_SETTINGS. We - // defined it manually in Profile, so make sure we only serialize the Icon - // if the user actually changed it here. - JsonUtils::SetValueForKey(json, IconKey, _Icon); - // PermissiveStringConverter is unnecessary for serialization JsonUtils::SetValueForKey(json, PaddingKey, _Padding); @@ -380,102 +364,6 @@ Json::Value Profile::ToJson() const return json; } -// This is the implementation for and INHERITABLE_SETTING, but with one addition -// in the setter. We want to make sure to clear out our cached icon, so that we -// can re-evaluate it as it changes in the SUI. -void Profile::Icon(const winrt::hstring& value) -{ - _evaluatedIcon = std::nullopt; - _Icon = value; -} -winrt::hstring Profile::Icon() const -{ - const auto val{ _getIconImpl() }; - return val ? *val : hstring{ L"\uE756" }; -} - -winrt::hstring Profile::EvaluatedIcon() -{ - // We cache the result here, so we don't search the path for the exe every time. - if (!_evaluatedIcon.has_value()) - { - _evaluatedIcon = _evaluateIcon(); - } - return *_evaluatedIcon; -} - -winrt::hstring Profile::_evaluateIcon() const -{ - // If the profile has an icon, return it. - if (!Icon().empty()) - { - return Icon(); - } - - // Otherwise, use NormalizeCommandLine to find the actual exe name. This - // will actually search for the exe, including spaces, in the same way that - // CreateProcess does. - std::wstring cmdline{ NormalizeCommandLine(Commandline().c_str()) }; - // NormalizeCommandLine will return a string with embedded nulls after each - // arg. We just want the first one. - return winrt::hstring{ cmdline.c_str() }; -} - -bool Profile::HasIcon() const -{ - return _Icon.has_value(); -} - -winrt::Microsoft::Terminal::Settings::Model::Profile Profile::IconOverrideSource() -{ - for (const auto& parent : _parents) - { - if (auto source{ parent->_getIconOverrideSourceImpl() }) - { - return source; - } - } - return nullptr; -} - -void Profile::ClearIcon() -{ - _Icon = std::nullopt; - _evaluatedIcon = std::nullopt; -} - -std::optional Profile::_getIconImpl() const -{ - if (_Icon) - { - return _Icon; - } - for (const auto& parent : _parents) - { - if (auto val{ parent->_getIconImpl() }) - { - return val; - } - } - return std::nullopt; -} - -winrt::Microsoft::Terminal::Settings::Model::Profile Profile::_getIconOverrideSourceImpl() const -{ - if (_Icon) - { - return *this; - } - for (const auto& parent : _parents) - { - if (auto source{ parent->_getIconOverrideSourceImpl() }) - { - return source; - } - } - return nullptr; -} - // Given a commandLine like the following: // * "C:\WINDOWS\System32\cmd.exe" // * "pwsh -WorkingDirectory ~" @@ -633,6 +521,51 @@ void Profile::_logSettingIfSet(const std::string_view& setting, const bool isSet } } +void Profile::ResolveMediaResources(const Model::MediaResourceResolver& resolver) +{ + if (const auto icon{ _getIconImpl() }; icon && *icon) + { + const auto iconSource{ _getIconOverrideSourceImpl() }; + ResolveIconMediaResource(iconSource->_Origin, iconSource->SourceBasePath, *icon, resolver); + + // If the icon was specified at any layer, but fails resolution *or* contains the empty string, + // fall back to the normalized command line at or above this layer. + if (!icon->Ok() || icon->Resolved().empty() && !iconSource->Commandline().empty()) + { + // We want to resolve the icon to the commandline, but we risk that the icon was already + // resolved. We don't want to do that (as MediaResource asserts that it was only resolved + // one time). However, we can't just make a new empty resource and resolve it -- that + // might replace the value in the user's settings when we serialize it again... + // So instead, make a new one derived from the icon we started with, then resolve it + // ourselves. + auto newIcon{ MediaResource::FromString(icon->Path()) }; + const std::wstring cmdline{ NormalizeCommandLine(iconSource->Commandline().c_str()) }; + newIcon.Resolve(cmdline.c_str() /* c_str: give hstring a chance to find the null terminator */); + iconSource->_Icon = std::move(newIcon); + } + } + + if (const auto container{ _DefaultAppearance.as() }) + { + container->ResolveMediaResources(resolver); + } + + if (const auto container{ UnfocusedAppearance().try_as() }) + { + container->ResolveMediaResources(resolver); + } + + if (const auto [bellSoundSource, bellSounds]{ _getBellSoundOverrideSourceAndValueImpl() }; + bellSoundSource && bellSounds && *bellSounds && bellSounds->Size() > 0) + { + for (const auto& bellSound : *bellSounds) + { + ResolveMediaResource(bellSoundSource->_Origin, bellSoundSource->SourceBasePath, bellSound, resolver); + } + // It's important that we keep the invalid bell sounds in the list, because we may want to write it back out to disk + } +} + void Profile::LogSettingChanges(std::set& changes, const std::string_view& context) const { for (const auto& setting : _changeLog) diff --git a/src/cascadia/TerminalSettingsModel/Profile.h b/src/cascadia/TerminalSettingsModel/Profile.h index c0d1697957..96c6ab8f9c 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.h +++ b/src/cascadia/TerminalSettingsModel/Profile.h @@ -50,6 +50,7 @@ Author(s): #include "JsonUtils.h" #include +#include "MediaResourceSupport.h" #include "AppearanceConfig.h" #include "FontConfig.h" @@ -75,7 +76,7 @@ constexpr GUID RUNTIME_GENERATED_PROFILE_NAMESPACE_GUID = { 0xf65ddb7e, 0x706b, namespace winrt::Microsoft::Terminal::Settings::Model::implementation { - struct Profile : ProfileT, IInheritable + struct Profile : ProfileT, IInheritable { public: Profile() noexcept = default; @@ -108,16 +109,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void LogSettingChanges(std::set& changes, const std::string_view& context) const; - // EvaluatedIcon depends on Icon. It allows us to grab the - // icon from an exe path. - // As a result, we can't use the INHERITABLE_SETTING macro for Icon, - // as we manually have to set/unset _evaluatedIcon when Icon changes. - winrt::hstring EvaluatedIcon(); - hstring Icon() const; - void Icon(const hstring& value); - bool HasIcon() const; - Model::Profile IconOverrideSource(); - void ClearIcon(); + void ResolveMediaResources(const Model::MediaResourceResolver& resolver); + + void Icon(const winrt::hstring& path) + { + // Internal Helper (overload version) + Icon(MediaResource::FromString(path)); + } WINRT_PROPERTY(bool, Deleted, false); WINRT_PROPERTY(bool, Orphaned, false); @@ -135,6 +133,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_SETTING(Model::Profile, guid, Guid, _GenerateGuidForProfile(Name(), Source())); INHERITABLE_SETTING(Model::Profile, hstring, Padding, DEFAULT_PADDING); + winrt::hstring SourceBasePath; + public: #define PROFILE_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \ INHERITABLE_SETTING_WITH_LOGGING(Model::Profile, type, name, jsonKey, ##__VA_ARGS__) @@ -145,17 +145,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Model::IAppearanceConfig _DefaultAppearance{ winrt::make(weak_ref(*this)) }; Model::FontConfig _FontInfo{ winrt::make(weak_ref(*this)) }; - std::optional _Icon{ std::nullopt }; - std::optional _evaluatedIcon{ std::nullopt }; std::set _changeLog; static std::wstring EvaluateStartingDirectory(const std::wstring& directory); static guid _GenerateGuidForProfile(const std::wstring_view& name, const std::wstring_view& source) noexcept; - winrt::hstring _evaluateIcon() const; - std::optional _getIconImpl() const; - Model::Profile _getIconOverrideSourceImpl() const; void _logSettingSet(const std::string_view& setting); void _logSettingIfSet(const std::string_view& setting, const bool isSet); diff --git a/src/cascadia/TerminalSettingsModel/Profile.idl b/src/cascadia/TerminalSettingsModel/Profile.idl index 0d1ffe9f36..9d7d93ef99 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.idl +++ b/src/cascadia/TerminalSettingsModel/Profile.idl @@ -32,7 +32,7 @@ namespace Microsoft.Terminal.Settings.Model All = 0xffffffff }; - [default_interface] runtimeclass Profile : Windows.Foundation.IStringable, ISettingsModelObject { + runtimeclass Profile : Windows.Foundation.IStringable, ISettingsModelObject { Profile(); Profile(Guid guid); @@ -44,16 +44,12 @@ namespace Microsoft.Terminal.Settings.Model // True if the user *kept* this Profile, but it disappeared from the system. Boolean Orphaned { get; }; - // Helper for magically using a commandline for an icon for a profile - // without an explicit icon. - String EvaluatedIcon { get; }; - INHERITABLE_PROFILE_SETTING(Guid, Guid); INHERITABLE_PROFILE_SETTING(String, Name); INHERITABLE_PROFILE_SETTING(String, Source); INHERITABLE_PROFILE_SETTING(Boolean, Hidden); INHERITABLE_PROFILE_SETTING(Guid, ConnectionType); - INHERITABLE_PROFILE_SETTING(String, Icon); + INHERITABLE_PROFILE_SETTING(IMediaResource, Icon); INHERITABLE_PROFILE_SETTING(CloseOnExitMode, CloseOnExit); INHERITABLE_PROFILE_SETTING(String, TabTitle); INHERITABLE_PROFILE_SETTING(Windows.Foundation.IReference, TabColor); @@ -80,7 +76,7 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_PROFILE_SETTING(Windows.Foundation.Collections.IMap, EnvironmentVariables); - INHERITABLE_PROFILE_SETTING(Windows.Foundation.Collections.IVector, BellSound); + INHERITABLE_PROFILE_SETTING(Windows.Foundation.Collections.IVector, BellSound); INHERITABLE_PROFILE_SETTING(Boolean, Elevate); INHERITABLE_PROFILE_SETTING(Boolean, AutoMarkPrompts); diff --git a/src/cascadia/TerminalSettingsModel/ProfileEntry.cpp b/src/cascadia/TerminalSettingsModel/ProfileEntry.cpp index 251b7cdc6a..818c806a2f 100644 --- a/src/cascadia/TerminalSettingsModel/ProfileEntry.cpp +++ b/src/cascadia/TerminalSettingsModel/ProfileEntry.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include "ProfileEntry.h" #include "JsonUtils.h" +#include "TerminalSettingsSerializationHelpers.h" #include "ProfileEntry.g.cpp" @@ -20,7 +21,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } ProfileEntry::ProfileEntry(const winrt::hstring& profile) noexcept : - ProfileEntryT(NewTabMenuEntryType::Profile), + ProfileEntryT(NewTabMenuEntryType::Profile), _ProfileName{ profile } { } @@ -47,7 +48,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { JsonUtils::SetValueForKey(json, ProfileKey, _Profile.Guid()); } - JsonUtils::SetValueForKey(json, IconKey, _Icon); + JsonUtils::SetValueForKey(json, IconKey, _icon); return json; } @@ -57,7 +58,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto entry = winrt::make_self(); JsonUtils::GetValueForKey(json, ProfileKey, entry->_ProfileName); - JsonUtils::GetValueForKey(json, IconKey, entry->_Icon); + JsonUtils::GetValueForKey(json, IconKey, entry->_icon); return entry; } @@ -68,7 +69,16 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation entry->_Profile = _Profile; entry->_ProfileIndex = _ProfileIndex; entry->_ProfileName = _ProfileName; - entry->_Icon = _Icon; + entry->_icon = _icon; return *entry; } + + void ProfileEntry::ResolveMediaResourcesWithBasePath(const winrt::hstring& basePath, const Model::MediaResourceResolver& resolver) + { + if (_icon) + { + // TODO GH#19191 (Hardcoded Origin, since that's the only place it could have come from) + ResolveIconMediaResource(OriginTag::User, basePath, _icon, resolver); + } + } } diff --git a/src/cascadia/TerminalSettingsModel/ProfileEntry.h b/src/cascadia/TerminalSettingsModel/ProfileEntry.h index e9274e28f4..dd819e2abf 100644 --- a/src/cascadia/TerminalSettingsModel/ProfileEntry.h +++ b/src/cascadia/TerminalSettingsModel/ProfileEntry.h @@ -17,12 +17,13 @@ Author(s): #include "NewTabMenuEntry.h" #include "ProfileEntry.g.h" +#include "MediaResourceSupport.h" #include "Profile.h" namespace winrt::Microsoft::Terminal::Settings::Model::implementation { - struct ProfileEntry : ProfileEntryT + struct ProfileEntry : ProfileEntryT { public: ProfileEntry() noexcept; @@ -40,13 +41,24 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // then CascadiaSettings::_resolveNewTabMenuProfiles() will populate // the Profile and ProfileIndex properties appropriately winrt::hstring ProfileName() const noexcept { return _ProfileName; }; + void ResolveMediaResourcesWithBasePath(const winrt::hstring& basePath, const Model::MediaResourceResolver& resolver) override; + + IMediaResource Icon() const noexcept + { + return _icon ? _icon : MediaResource::Empty(); + } + + void Icon(const IMediaResource& val) + { + _icon = val; + } WINRT_PROPERTY(Model::Profile, Profile); WINRT_PROPERTY(int, ProfileIndex); - WINRT_PROPERTY(winrt::hstring, Icon); private: winrt::hstring _ProfileName; + IMediaResource _icon; }; } diff --git a/src/cascadia/TerminalSettingsModel/Resources/de-DE/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/de-DE/Resources.resw index 85c516cfee..58a4a6309e 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/de-DE/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/de-DE/Resources.resw @@ -1,17 +1,17 @@  - @@ -415,7 +415,7 @@ Alle Markierungen löschen - Eingabe senden: "{0}" + Eingabe senden: „{0}“ {0} will be replaced with a string of input as defined by the user @@ -740,4 +740,240 @@ Aktuelles Arbeitsverzeichnis öffnen + + Registerkarte schließen + + + Eingabe senden + + + Fokusmodus festlegen + + + Vollbild festlegen + + + Maximiert festlegen + + + Farbschema festlegen + + + Andere Registerkarten schließen + + + Registerkarten schließen nach + + + Registerkarte verschieben + + + Übereinstimmung suchen + + + Nach Text suchen + + + Fokusbereich + + + Exportpuffer + + + Puffer löschen + + + Mehrere Aktionen + + + Deckkraft anpassen + + + Befehl auswählen + + + Ausgabe auswählen + + + Vorschläge + + + Farbauswahl + + + WSL-Verteilungsprofilgenerator + The display name of a dynamic profile generator for WSL distros + + + PowerShell-Profilgenerator + The display name of a dynamic profile generator for PowerShell + + + Azure Cloud Shell-Profilgenerator + The display name of a dynamic profile generator for Azure Cloud Shell + + + Visual Studio-Profilgenerator + The display name of a dynamic profile generator for Visual Studio + + + SSH-Hostprofilgenerator + The display name of a dynamic profile generator for SSH hosts + + + Auswahl schließen + + + Eine Zeile + + + Mit Steuersequenzen + + + Formatierung kopieren + + + Tabindex + + + Fenster + + + Größenänderungsrichtung + + + Fokusrichtung + + + Richtung + + + Delta + + + Eingabe + + + Ziel + + + Fokusmodus + + + Ist maximiert + + + Vollbildmodus + + + Schemaname + + + Registerfarbe + + + Titel + + + Befehlszeile + + + Index + + + Zu scrollende Zeilen + + + Farbe + + + Startmodus + + + Name + + + Tastenkombination + + + Quelle + + + Befehlszeile verwenden + + + Umschaltmodus + + + Abfrage-URL + + + Desktop + + + Monitor + + + Sichtbarkeit umschalten + + + Dropdowndauer + + + ID + + + Pfad + + + Löschen + + + Deckkraft + + + Relativ + + + Vordergrund + + + Hintergrund + + + Übereinstimmungsmodus + + + Startverzeichnis + + + Registerkartentitel + + + Profilindex + + + Profil + + + Anwendungstitel unterdrücken + + + Farbschema + + + Erhöhen + + + Umgebungsvariablen neu laden + + + Teilungsrichtung + + + Teilungsmodus + + + Teilungsgröße + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index b1c24a54d0..16a129c61f 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -1,17 +1,17 @@ - @@ -415,7 +415,7 @@ Clear all marks - Send Input: "{0}" + Send input: "{0}" {0} will be replaced with a string of input as defined by the user @@ -740,4 +740,240 @@ Open current working directory + + Close tab + + + Send input + + + Set focus mode + + + Set full screen + + + Set maximized + + + Set color scheme + + + Close other tabs + + + Close tabs after + + + Move tab + + + Find match + + + Search for text + + + Focus pane + + + Export buffer + + + Clear buffer + + + Multiple actions + + + Adjust opacity + + + Select command + + + Select output + + + Suggestions + + + Color selection + + + WSL Distribution Profile Generator + The display name of a dynamic profile generator for WSL distros + + + PowerShell Profile Generator + The display name of a dynamic profile generator for PowerShell + + + Azure Cloud Shell Profile Generator + The display name of a dynamic profile generator for Azure Cloud Shell + + + Visual Studio Profile Generator + The display name of a dynamic profile generator for Visual Studio + + + SSH Host Profile Generator + The display name of a dynamic profile generator for SSH hosts + + + Dismiss Selection + + + Single Line + + + With Control Sequences + + + Copy Formatting + + + Tab Index + + + Window + + + Resize Direction + + + Focus Direction + + + Direction + + + Delta + + + Input + + + Target + + + Is Focus Mode + + + Is Maximized + + + Is Full Screen + + + Scheme Name + + + Tab Color + + + Title + + + Commandline + + + Index + + + Rows To Scroll + + + Color + + + Launch Mode + + + Name + + + Key Chord + + + Source + + + Use Commandline + + + Switcher Mode + + + Query URL + + + Desktop + + + Monitor + + + Toggle Visibility + + + Dropdown Duration + + + Id + + + Path + + + Clear + + + Opacity + + + Relative + + + Foreground + + + Background + + + Match Mode + + + Starting Directory + + + Tab Title + + + Profile Index + + + Profile + + + Suppress Application Title + + + Color Scheme + + + Elevate + + + Reload Environment Variables + + + Split Direction + + + Split Mode + + + Split Size + diff --git a/src/cascadia/TerminalSettingsModel/Resources/es-ES/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/es-ES/Resources.resw index ec1e158743..48ceccad2b 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/es-ES/Resources.resw @@ -1,17 +1,17 @@  - @@ -740,4 +740,240 @@ Abrir directorio de trabajo actual + + Cerrar pestaña + + + Enviar entrada + + + Establecer modo de enfoque + + + Establecer pantalla completa + + + Establecer maximizado + + + Establecer combinación de colores + + + Cerrar otras pestañas + + + Cerrar pestañas después de + + + Mover pestaña + + + Buscar coincidencia + + + Buscar texto + + + Centrar panel + + + Exportar búfer + + + Borrar búfer + + + Varias acciones + + + Ajustar opacidad + + + Seleccionar comando + + + Seleccionar salida + + + Sugerencias + + + Selección de color + + + Generador de perfiles de distribución WSL + The display name of a dynamic profile generator for WSL distros + + + Generador de perfiles de PowerShell + The display name of a dynamic profile generator for PowerShell + + + Generador de perfiles de Azure Cloud Shell + The display name of a dynamic profile generator for Azure Cloud Shell + + + Generador de perfiles de Visual Studio + The display name of a dynamic profile generator for Visual Studio + + + Generador de perfiles de host SSH + The display name of a dynamic profile generator for SSH hosts + + + Descartar selección + + + Una línea + + + Con secuencias de control + + + Copiar formato + + + Índice de etiqueta + + + Ventana + + + Cambiar el tamaño de la dirección + + + Dirección del foco + + + Dirección + + + Delta + + + Entrada + + + Destino + + + Está en modo de enfoque + + + Está maximizado + + + Es de pantalla completa + + + Nombre de esquema + + + Color de la pestaña + + + Título + + + Commandline + + + Índice + + + Filas para desplazar + + + Color + + + Modo de inicio + + + Nombre + + + Clave de presión + + + Origen + + + Usar línea de comandos + + + Modo de conmutador + + + Dirección URL de consulta + + + Escritorio + + + Supervisar + + + Alternar visibilidad + + + Duración de la lista desplegable + + + Id. + + + Ruta de acceso + + + Despejado + + + Opacidad + + + Relativo + + + Primer plano + + + Fondo + + + Modo de coincidencia + + + Directorio de inicio + + + Título de la pestaña + + + Índice de perfil + + + Perfil + + + Suprimir título de la aplicación + + + Combinación de colores + + + Elevar + + + Volver a cargar variables de entorno + + + Dirección de división + + + Modo de división + + + Tamaño de división + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/fr-FR/Resources.resw index 6cf1eba684..47c89a3eab 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/fr-FR/Resources.resw @@ -1,17 +1,17 @@  - @@ -740,4 +740,240 @@ Ouvrir le répertoire de travail actif + + Fermer l’onglet + + + Envoyer l'entrée + + + Définir le mode Focus + + + Définir le plein écran + + + Définir sur maximisé + + + Définir le modèle de couleurs + + + Fermer les autres onglets + + + Fermer les onglets après + + + Déplacer l’onglet + + + Rechercher la correspondance + + + Rechercher du texte + + + Volet de mise au point + + + Exporter le tampon + + + Effacer la mémoire tampon + + + Actions multiples + + + Ajuster l’opacité + + + Sélectionner la commande + + + Sélectionner la sortie + + + Suggestions + + + La sélection de couleur + + + Générateur de profil de distribution WSL + The display name of a dynamic profile generator for WSL distros + + + Générateur de profil PowerShell + The display name of a dynamic profile generator for PowerShell + + + Générateur de profil Azure Cloud Shell + The display name of a dynamic profile generator for Azure Cloud Shell + + + Générateur de profil Visual Studio + The display name of a dynamic profile generator for Visual Studio + + + Générateur de profil d’hôte SSH + The display name of a dynamic profile generator for SSH hosts + + + Annuler la sélection + + + Une ligne + + + Avec des séquences de contrôle + + + Copier la mise en forme + + + Index de l’onglet + + + Fenêtre + + + Direction de redimensionnement + + + Direction du focus + + + Orientation + + + Delta + + + Entrée + + + Cible + + + Est en mode Focus + + + Est agrandi + + + Est en mode Plein écran + + + Nom du schéma + + + Couleur d’onglet + + + Titre + + + Ligne de commande + + + Index + + + Lignes à faire défiler + + + Couleur + + + Mode de lancement + + + Nom + + + Accord de touches + + + Source + + + Utiliser la ligne de commande + + + Mode sélecteur + + + URL de requête + + + Ordinateur de bureau + + + Superviser + + + Changer la visibilité + + + Durée du menu déroulant + + + ID + + + Chemin d'accès + + + Effacer + + + Opacité + + + Relatif + + + Premier plan + + + Arrière-plan + + + Mode de correspondance + + + Répertoire de démarrage + + + Titre de l’onglet + + + Index du profil + + + Profil + + + Masquer le titre de l’application + + + Modèle de couleurs + + + Élever + + + Recharger les variables d’environnement + + + Direction du fractionnement + + + Mode fractionné + + + Taille fractionnée + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/Resources/it-IT/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/it-IT/Resources.resw index 1f4a1e65a7..71038c62d7 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/it-IT/Resources.resw @@ -1,17 +1,17 @@  - @@ -740,4 +740,240 @@ Apri directory di lavoro corrente + + Chiudi scheda + + + Invia input + + + Imposta modalità messa a fuoco + + + Imposta schermo intero + + + Massimizzato impostato + + + Imposta combinazione colori + + + Chiudi altre schede + + + Chiudi le schede dopo + + + Sposta scheda + + + Trova corrispondenza + + + Cerca testo + + + Riquadro messa a fuoco + + + Buffer esportazione + + + Deseleziona buffer + + + Azioni multiple + + + Regola opacità + + + Seleziona comando + + + Seleziona output + + + Suggerimenti + + + Selezione colore + + + Generatore di profili di distribuzione WSL + The display name of a dynamic profile generator for WSL distros + + + Generatore di profili PowerShell + The display name of a dynamic profile generator for PowerShell + + + Generatore di profili di Azure Cloud Shell + The display name of a dynamic profile generator for Azure Cloud Shell + + + Generatore di profili Visual Studio + The display name of a dynamic profile generator for Visual Studio + + + Generatore di profili host SSH + The display name of a dynamic profile generator for SSH hosts + + + Ignora selezione + + + Riga singola + + + Con sequenze di controllo + + + Copia formattazione + + + Indice scheda + + + Finestra + + + Direzione ridimensionamento + + + Direzione messa a fuoco + + + Direzione + + + Delta + + + Input + + + Destinazione + + + È modalità messa a fuoco + + + È ingrandito + + + È a schermo intero + + + Nome schema + + + Colore scheda + + + Titolo + + + Commandline + + + Indice + + + Righe da scorrere + + + Colore + + + Modalità di avvio + + + Nome + + + Combinazione di tasti + + + Origine + + + Usa Commandline + + + Modalità commutazione + + + URL query + + + Desktop + + + Monitora + + + Attiva/Disattiva visibilità + + + Durata menu a discesa + + + ID + + + Percorso + + + Cancella + + + Opacità + + + Relativo + + + In primo piano + + + Sfondo + + + Modalità corrispondenza + + + Directory iniziale + + + Titolo scheda + + + Indice profilo + + + Profilo + + + Sopprimi titolo applicazione + + + Combinazione colori + + + Eleva + + + Ricarica variabili di ambiente + + + Direzione divisione + + + Modalità divisione + + + Dimensione suddivisione + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/ja-JP/Resources.resw index 9743e14162..c4ec81221a 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/ja-JP/Resources.resw @@ -1,17 +1,17 @@  - @@ -740,4 +740,240 @@ 現在の作業ディレクトリを開く + + タブを閉じる + + + 入力の送信 + + + フォーカス モードを設定 + + + 全画面表示を設定 + + + 最大化を設定 + + + 配色を設定 + + + 他のタブを閉じる + + + の後にタブを閉じる + + + タブの移動 + + + 一致を検索 + + + テキストを検索する + + + ウィンドウをフォーカスする + + + バッファのエクスポート + + + バッファーのクリア + + + 複数のアクション + + + 不透明度の調整 + + + コマンドの選択 + + + 出力の選択 + + + 候補 + + + 色の選択 + + + WSL ディストリビューション プロファイル ジェネレーター + The display name of a dynamic profile generator for WSL distros + + + PowerShell プロファイル ジェネレーター + The display name of a dynamic profile generator for PowerShell + + + Azure Cloud Shell プロファイル ジェネレーター + The display name of a dynamic profile generator for Azure Cloud Shell + + + Visual Studio プロファイル ジェネレーター + The display name of a dynamic profile generator for Visual Studio + + + SSH ホスト プロファイル ジェネレーター + The display name of a dynamic profile generator for SSH hosts + + + 選択を解除する + + + 1 行表示 + + + 制御シーケンスを使用 + + + 書式設定をコピー + + + Tab Index + + + ウィンドウ + + + サイズ変更方向 + + + フォーカス方向 + + + 方向 + + + ブルース + + + 入力 + + + ターゲット + + + フォーカス モード + + + 最大化しました + + + 全画面表示 + + + スキーム名 + + + シート見出しの色 + + + タイトル + + + Commandline + + + インデックス + + + スクロールする行数 + + + + + + 起動モード + + + 名前 + + + キー コード + + + ソース + + + コマンドラインを使用する + + + スイッチャー モード + + + クエリの URL + + + デスクトップ + + + モニター + + + 表示の切り替え + + + ドロップダウンの期間 + + + ID + + + パス + + + キャンセル + + + 不透明度 + + + 相対 + + + 前景 + + + 背景 + + + 一致モード + + + 開始ディレクトリ + + + タブのタイトル + + + プロファイル インデックス + + + プロファイル + + + アプリケーション タイトルを表示しない + + + 配色 + + + 昇格 + + + 環境変数を再度読み込む + + + 分割方向 + + + 分割モード + + + 分割サイズ + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/ko-KR/Resources.resw index 378d4d04bf..647cf58018 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/ko-KR/Resources.resw @@ -1,17 +1,17 @@  - @@ -740,4 +740,240 @@ 현재 작업 디렉터리 열기 + + 탭 닫기 + + + 입력 값 보내기 + + + 포커스 모드 설정 + + + 전체 화면 설정 + + + 최대화 설정 + + + 색 구성표 설정 + + + 다른 탭 닫기 + + + 이후 탭 닫기 + + + 탭 이동 + + + 일치 항목 찾기 + + + 텍스트 검색 + + + 창 포커스 + + + 버퍼 내보내기 + + + 버퍼 지우기 + + + 여러 동작 + + + 불투명도 조정 + + + 명령 선택 + + + 출력 선택 + + + 추천 + + + 색 선택 + + + WSL 배포 프로필 생성기 + The display name of a dynamic profile generator for WSL distros + + + PowerShell 프로필 생성기 + The display name of a dynamic profile generator for PowerShell + + + Azure Cloud Shell 프로필 생성기 + The display name of a dynamic profile generator for Azure Cloud Shell + + + Visual Studio 프로필 생성기 + The display name of a dynamic profile generator for Visual Studio + + + SSH 호스트 프로필 생성기 + The display name of a dynamic profile generator for SSH hosts + + + 선택 영역 해제 + + + 한 줄 + + + 컨트롤 시퀀스 포함 + + + 서식 복사 + + + 탭 인덱스 + + + + + + 방향 크기 조정 + + + 포커스 방향 + + + 방향 + + + 델타 + + + 입력 + + + 대상 + + + 포커스 모드인 경우 + + + 최대화됨 + + + 전체 화면 + + + 체계 이름 + + + 탭 색 + + + 제목 + + + 명령줄 + + + 색인 + + + 스크롤할 행 + + + + + + 시작 모드 + + + 이름 + + + 키 코드 + + + 원본 + + + 명령줄 사용 + + + 전환기 모드 + + + 쿼리 URL: + + + 데스크톱 + + + 모니터 + + + 표시 여부 전환 + + + 드롭다운 기간 + + + ID + + + 경로 + + + 취소 + + + 불투명도 + + + 상대 + + + 전경 + + + 배경 + + + 일치 모드 + + + 시작 디렉터리 + + + 탭 제목 + + + 프로필 인덱스 + + + 프로필 + + + 애플리케이션 제목 숨기기 + + + 색 구성표 + + + 권한 상승 + + + 환경 변수 다시 로드 + + + 분할 방향 + + + 분할 모드 + + + 분할 크기 + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/pt-BR/Resources.resw index 4a28f1aa00..866e4a25bd 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/pt-BR/Resources.resw @@ -1,17 +1,17 @@  - @@ -415,7 +415,7 @@ Limpar todas as marcas - Enviar Entrada: "{0}" + Enviar entrada: "{0}" {0} will be replaced with a string of input as defined by the user @@ -740,4 +740,240 @@ Abrir o diretório de trabalho atual + + Fechar guia + + + Enviar entrada + + + Definir modo de foco + + + Definir tela inteira + + + Definir como maximizado + + + Definir esquema de cores + + + Fechar outras guias + + + Fechar guias após + + + Mover guia + + + Localizar correspondência + + + Pesquisar texto + + + Painel de foco + + + Exportar buffer + + + Limpar o buffer + + + Várias ações + + + Ajustar opacidade + + + Selecionar comando + + + Selecionar saída + + + Sugestões + + + Seleção de cor + + + Gerador de Perfil de Distribuição WSL + The display name of a dynamic profile generator for WSL distros + + + Gerador de Perfil do PowerShell + The display name of a dynamic profile generator for PowerShell + + + Gerador de Perfil do Azure Cloud Shell + The display name of a dynamic profile generator for Azure Cloud Shell + + + Gerador de Perfil do Visual Studio + The display name of a dynamic profile generator for Visual Studio + + + Gerador de Perfil de Host SSH + The display name of a dynamic profile generator for SSH hosts + + + Ignorar Seleção + + + Linha Única + + + Com Sequências de Controle + + + Copiar Formatação + + + Índice de Guia + + + Janela + + + Direção do Redimensionamento + + + Direção do Foco + + + Direção + + + Delta + + + Entrada + + + Destino + + + Está no Modo de Foco + + + Está Maximizado + + + Está em Tela Inteira + + + Nome do Esquema + + + Cor da Guia + + + Título + + + Commandline + + + Índice + + + Linhas para Rolagem + + + Cor + + + Modo de Inicialização + + + Nome + + + Pressionar Tecla Simultaneamente + + + Fonte + + + Usar Linha de Comando + + + Modo do Alternador + + + URL da consulta + + + Área de Trabalho + + + Monitorar + + + Ativar/Desativar Visibilidade + + + Duração do menu suspenso + + + ID + + + Caminho + + + Limpar + + + Opacidade + + + Relativo + + + Primeiro Plano + + + Tela de Fundo + + + Modo de Correspondência + + + Diretório Inicial + + + Título da Guia + + + Índice do Perfil + + + Perfil + + + Suprimir Título do Aplicativo + + + Esquema de Cores + + + Elevar + + + Recarregar Variáveis de Ambiente + + + Direção da Divisão + + + Modo de Divisão + + + Tamanho da Divisão + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/qps-ploc/Resources.resw index afcdd02d71..d6d3c68932 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/qps-ploc/Resources.resw @@ -1,17 +1,17 @@ - @@ -415,7 +415,7 @@ Ċļéāґ àℓĺ màгķś !!! ! - Šêлđ Ìñрµť: "{0}" !!! !! + Šêлđ íñрµť: "{0}" !!! !! {0} will be replaced with a string of input as defined by the user @@ -740,4 +740,240 @@ Öφěп сùяŗĕʼnŧ ώθяќìⁿġ ðїгéćţοŗу !!! !!! !!! + + Ċĺōşě ţав !!! + + + Ѕёņδ ïпφυт !!! + + + Śėτ ƒосµŝ мσðз !!! ! + + + Šеτ ƒũłĺ ѕċřěėŋ !!! ! + + + Şέť mажïmιžėð !!! + + + Ѕеτ ĉōĺόг ѕčĥєм℮ !!! ! + + + Сŀθšê бťћêя тāьş !!! ! + + + Čŀŏšе ťąъś àƒţêґ !!! ! + + + Мôνé τąъ !! + + + ₣īñð mдť¢ĥ !!! + + + Ѕэªř¢ĥ ƒŏř ţежτ !!! ! + + + ₣õçμѕ рáлě !!! + + + È×рθřť вũƒƒεґ !!! + + + Ĉŀέαѓ ъµƒƒěѓ !!! + + + Μūłŧϊφĺэ ǻċţíǿⁿš !!! ! + + + Àđĵûşţ ôρāćìτý !!! ! + + + Şéĺėςτ ¢θmmдņđ !!! ! + + + Ş℮ĺэсŧ бŭţρűţ !!! + + + Şцġĝëşŧιòňŝ !!! + + + Ĉøłоґ ŝєļé¢тíŏл !!! ! + + + ẄŠ₤ Ďΐşţŗĭьúţĭοй Рŗоƒįĺé Ĝèŋëѓάтбŕ !!! !!! !!! ! + The display name of a dynamic profile generator for WSL distros + + + РöшєřŜђěłŀ Ρѓòƒĭļз Ġèпéгâŧθѓ !!! !!! !! + The display name of a dynamic profile generator for PowerShell + + + Δżúѓ℮ Ċℓǿύď Śнéļļ Рŗôƒìŀё Ğēņεřǻŧōґ !!! !!! !!! ! + The display name of a dynamic profile generator for Azure Cloud Shell + + + Vΐšџаℓ Şτϋδίǿ Ρѓőƒϊĺĕ Ğёйěřąтοŕ !!! !!! !!! + The display name of a dynamic profile generator for Visual Studio + + + ŠŚΗ Ħøŝť Рґǿƒιŀέ Ġеπèŕăťθґ !!! !!! ! + The display name of a dynamic profile generator for SSH hosts + + + Ðīŝmіšѕ Ś℮ℓĕċţĩöл !!! !! + + + Ѕíηğłε £íпę !!! + + + Ŵĩтĥ Сōηťяøļ Şėqμєⁿčēś !!! !!! + + + €ǿφỳ ₣θřmдťτιňģ !!! ! + + + Ŧäъ Íʼnðêх !!! + + + Ẃϊńđбώ ! + + + Яèśīžę Ďîґēсťíöń !!! ! + + + ₣όçůѕ Ðįгеĉтїöπ !!! ! + + + Đίяêčţїóń !!! + + + Ďэĺŧä ! + + + Įηρџт ! + + + Ţάѓģėт ! + + + İş ₣ôçûś Μøδé !!! + + + Ĭş Μąхímîźέð !!! + + + Їѕ ₣ùĺł Ŝčŕėéπ !!! ! + + + Śςћεmĕ ∏åmé !!! + + + Тáв Çσłоѓ !!! + + + Ţįţŀè ! + + + Сóммäňðłîйě !!! + + + Ìпðéж ! + + + Гőẃŝ Тŏ Šçѓοŀł !!! ! + + + Čòļóг ! + + + Łªůŋĉћ Мőďè !!! + + + Ñǻmé ! + + + Κėŷ Ċħőŗð !!! + + + Śбυŕсé ! + + + Ūѕè Ċőммáⁿđĺĭʼnз !!! ! + + + Ѕẁìť¢ħęя Μôď℮ !!! + + + Qџëŗу ŪЯ₤ !!! + + + Ďέšĸтбφ !! + + + Μöʼnітоŗ !! + + + Ţòĝĝļè Vĩşĩьîľїŧŷ !!! !! + + + Ðяοрδõщŋ Đŭгåτīόⁿ !!! !! + + + Īδ + + + Ρâтђ ! + + + Сłэàř ! + + + Õράςíţŷ !! + + + Ŗèŀάŧĩνé !! + + + ₣θяēğŗõυήđ !!! + + + ßд¢кĝѓöüпδ !!! + + + Ματ¢н Мσðě !!! + + + Śťǻятìŋģ Đίѓėĉţóŕу !!! !! + + + Тάв Τìтłê !!! + + + Ρřσƒïĺэ Īňďз× !!! + + + Рѓôƒíŀ℮ !! + + + Śцφφŕέšś Αρрľíçáťîои Ŧιŧłé !!! !!! ! + + + €οŀόг Ś¢ђєm℮ !!! + + + Ёŀένăţέ !! + + + Гĕŀõãð Ĕиνīřöŋm℮ηт Vàґίªъŀéš !!! !!! !! + + + Ѕρľїť Đΐŕєċŧĭőʼn !!! ! + + + Ѕφŀĭτ Μσđе !!! + + + Šрļϊŧ Ѕìžє !!! + diff --git a/src/cascadia/TerminalSettingsModel/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/qps-ploca/Resources.resw index afcdd02d71..d6d3c68932 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/qps-ploca/Resources.resw @@ -1,17 +1,17 @@ - @@ -415,7 +415,7 @@ Ċļéāґ àℓĺ màгķś !!! ! - Šêлđ Ìñрµť: "{0}" !!! !! + Šêлđ íñрµť: "{0}" !!! !! {0} will be replaced with a string of input as defined by the user @@ -740,4 +740,240 @@ Öφěп сùяŗĕʼnŧ ώθяќìⁿġ ðїгéćţοŗу !!! !!! !!! + + Ċĺōşě ţав !!! + + + Ѕёņδ ïпφυт !!! + + + Śėτ ƒосµŝ мσðз !!! ! + + + Šеτ ƒũłĺ ѕċřěėŋ !!! ! + + + Şέť mажïmιžėð !!! + + + Ѕеτ ĉōĺόг ѕčĥєм℮ !!! ! + + + Сŀθšê бťћêя тāьş !!! ! + + + Čŀŏšе ťąъś àƒţêґ !!! ! + + + Мôνé τąъ !! + + + ₣īñð mдť¢ĥ !!! + + + Ѕэªř¢ĥ ƒŏř ţежτ !!! ! + + + ₣õçμѕ рáлě !!! + + + È×рθřť вũƒƒεґ !!! + + + Ĉŀέαѓ ъµƒƒěѓ !!! + + + Μūłŧϊφĺэ ǻċţíǿⁿš !!! ! + + + Àđĵûşţ ôρāćìτý !!! ! + + + Şéĺėςτ ¢θmmдņđ !!! ! + + + Ş℮ĺэсŧ бŭţρűţ !!! + + + Şцġĝëşŧιòňŝ !!! + + + Ĉøłоґ ŝєļé¢тíŏл !!! ! + + + ẄŠ₤ Ďΐşţŗĭьúţĭοй Рŗоƒįĺé Ĝèŋëѓάтбŕ !!! !!! !!! ! + The display name of a dynamic profile generator for WSL distros + + + РöшєřŜђěłŀ Ρѓòƒĭļз Ġèпéгâŧθѓ !!! !!! !! + The display name of a dynamic profile generator for PowerShell + + + Δżúѓ℮ Ċℓǿύď Śнéļļ Рŗôƒìŀё Ğēņεřǻŧōґ !!! !!! !!! ! + The display name of a dynamic profile generator for Azure Cloud Shell + + + Vΐšџаℓ Şτϋδίǿ Ρѓőƒϊĺĕ Ğёйěřąтοŕ !!! !!! !!! + The display name of a dynamic profile generator for Visual Studio + + + ŠŚΗ Ħøŝť Рґǿƒιŀέ Ġеπèŕăťθґ !!! !!! ! + The display name of a dynamic profile generator for SSH hosts + + + Ðīŝmіšѕ Ś℮ℓĕċţĩöл !!! !! + + + Ѕíηğłε £íпę !!! + + + Ŵĩтĥ Сōηťяøļ Şėqμєⁿčēś !!! !!! + + + €ǿφỳ ₣θřmдťτιňģ !!! ! + + + Ŧäъ Íʼnðêх !!! + + + Ẃϊńđбώ ! + + + Яèśīžę Ďîґēсťíöń !!! ! + + + ₣όçůѕ Ðįгеĉтїöπ !!! ! + + + Đίяêčţїóń !!! + + + Ďэĺŧä ! + + + Įηρџт ! + + + Ţάѓģėт ! + + + İş ₣ôçûś Μøδé !!! + + + Ĭş Μąхímîźέð !!! + + + Їѕ ₣ùĺł Ŝčŕėéπ !!! ! + + + Śςћεmĕ ∏åmé !!! + + + Тáв Çσłоѓ !!! + + + Ţįţŀè ! + + + Сóммäňðłîйě !!! + + + Ìпðéж ! + + + Гőẃŝ Тŏ Šçѓοŀł !!! ! + + + Čòļóг ! + + + Łªůŋĉћ Мőďè !!! + + + Ñǻmé ! + + + Κėŷ Ċħőŗð !!! + + + Śбυŕсé ! + + + Ūѕè Ċőммáⁿđĺĭʼnз !!! ! + + + Ѕẁìť¢ħęя Μôď℮ !!! + + + Qџëŗу ŪЯ₤ !!! + + + Ďέšĸтбφ !! + + + Μöʼnітоŗ !! + + + Ţòĝĝļè Vĩşĩьîľїŧŷ !!! !! + + + Ðяοрδõщŋ Đŭгåτīόⁿ !!! !! + + + Īδ + + + Ρâтђ ! + + + Сłэàř ! + + + Õράςíţŷ !! + + + Ŗèŀάŧĩνé !! + + + ₣θяēğŗõυήđ !!! + + + ßд¢кĝѓöüпδ !!! + + + Ματ¢н Мσðě !!! + + + Śťǻятìŋģ Đίѓėĉţóŕу !!! !! + + + Тάв Τìтłê !!! + + + Ρřσƒïĺэ Īňďз× !!! + + + Рѓôƒíŀ℮ !! + + + Śцφφŕέšś Αρрľíçáťîои Ŧιŧłé !!! !!! ! + + + €οŀόг Ś¢ђєm℮ !!! + + + Ёŀένăţέ !! + + + Гĕŀõãð Ĕиνīřöŋm℮ηт Vàґίªъŀéš !!! !!! !! + + + Ѕρľїť Đΐŕєċŧĭőʼn !!! ! + + + Ѕφŀĭτ Μσđе !!! + + + Šрļϊŧ Ѕìžє !!! + diff --git a/src/cascadia/TerminalSettingsModel/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/qps-plocm/Resources.resw index afcdd02d71..d6d3c68932 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/qps-plocm/Resources.resw @@ -1,17 +1,17 @@ - @@ -415,7 +415,7 @@ Ċļéāґ àℓĺ màгķś !!! ! - Šêлđ Ìñрµť: "{0}" !!! !! + Šêлđ íñрµť: "{0}" !!! !! {0} will be replaced with a string of input as defined by the user @@ -740,4 +740,240 @@ Öφěп сùяŗĕʼnŧ ώθяќìⁿġ ðїгéćţοŗу !!! !!! !!! + + Ċĺōşě ţав !!! + + + Ѕёņδ ïпφυт !!! + + + Śėτ ƒосµŝ мσðз !!! ! + + + Šеτ ƒũłĺ ѕċřěėŋ !!! ! + + + Şέť mажïmιžėð !!! + + + Ѕеτ ĉōĺόг ѕčĥєм℮ !!! ! + + + Сŀθšê бťћêя тāьş !!! ! + + + Čŀŏšе ťąъś àƒţêґ !!! ! + + + Мôνé τąъ !! + + + ₣īñð mдť¢ĥ !!! + + + Ѕэªř¢ĥ ƒŏř ţежτ !!! ! + + + ₣õçμѕ рáлě !!! + + + È×рθřť вũƒƒεґ !!! + + + Ĉŀέαѓ ъµƒƒěѓ !!! + + + Μūłŧϊφĺэ ǻċţíǿⁿš !!! ! + + + Àđĵûşţ ôρāćìτý !!! ! + + + Şéĺėςτ ¢θmmдņđ !!! ! + + + Ş℮ĺэсŧ бŭţρűţ !!! + + + Şцġĝëşŧιòňŝ !!! + + + Ĉøłоґ ŝєļé¢тíŏл !!! ! + + + ẄŠ₤ Ďΐşţŗĭьúţĭοй Рŗоƒįĺé Ĝèŋëѓάтбŕ !!! !!! !!! ! + The display name of a dynamic profile generator for WSL distros + + + РöшєřŜђěłŀ Ρѓòƒĭļз Ġèпéгâŧθѓ !!! !!! !! + The display name of a dynamic profile generator for PowerShell + + + Δżúѓ℮ Ċℓǿύď Śнéļļ Рŗôƒìŀё Ğēņεřǻŧōґ !!! !!! !!! ! + The display name of a dynamic profile generator for Azure Cloud Shell + + + Vΐšџаℓ Şτϋδίǿ Ρѓőƒϊĺĕ Ğёйěřąтοŕ !!! !!! !!! + The display name of a dynamic profile generator for Visual Studio + + + ŠŚΗ Ħøŝť Рґǿƒιŀέ Ġеπèŕăťθґ !!! !!! ! + The display name of a dynamic profile generator for SSH hosts + + + Ðīŝmіšѕ Ś℮ℓĕċţĩöл !!! !! + + + Ѕíηğłε £íпę !!! + + + Ŵĩтĥ Сōηťяøļ Şėqμєⁿčēś !!! !!! + + + €ǿφỳ ₣θřmдťτιňģ !!! ! + + + Ŧäъ Íʼnðêх !!! + + + Ẃϊńđбώ ! + + + Яèśīžę Ďîґēсťíöń !!! ! + + + ₣όçůѕ Ðįгеĉтїöπ !!! ! + + + Đίяêčţїóń !!! + + + Ďэĺŧä ! + + + Įηρџт ! + + + Ţάѓģėт ! + + + İş ₣ôçûś Μøδé !!! + + + Ĭş Μąхímîźέð !!! + + + Їѕ ₣ùĺł Ŝčŕėéπ !!! ! + + + Śςћεmĕ ∏åmé !!! + + + Тáв Çσłоѓ !!! + + + Ţįţŀè ! + + + Сóммäňðłîйě !!! + + + Ìпðéж ! + + + Гőẃŝ Тŏ Šçѓοŀł !!! ! + + + Čòļóг ! + + + Łªůŋĉћ Мőďè !!! + + + Ñǻmé ! + + + Κėŷ Ċħőŗð !!! + + + Śбυŕсé ! + + + Ūѕè Ċőммáⁿđĺĭʼnз !!! ! + + + Ѕẁìť¢ħęя Μôď℮ !!! + + + Qџëŗу ŪЯ₤ !!! + + + Ďέšĸтбφ !! + + + Μöʼnітоŗ !! + + + Ţòĝĝļè Vĩşĩьîľїŧŷ !!! !! + + + Ðяοрδõщŋ Đŭгåτīόⁿ !!! !! + + + Īδ + + + Ρâтђ ! + + + Сłэàř ! + + + Õράςíţŷ !! + + + Ŗèŀάŧĩνé !! + + + ₣θяēğŗõυήđ !!! + + + ßд¢кĝѓöüпδ !!! + + + Ματ¢н Мσðě !!! + + + Śťǻятìŋģ Đίѓėĉţóŕу !!! !! + + + Тάв Τìтłê !!! + + + Ρřσƒïĺэ Īňďз× !!! + + + Рѓôƒíŀ℮ !! + + + Śцφφŕέšś Αρрľíçáťîои Ŧιŧłé !!! !!! ! + + + €οŀόг Ś¢ђєm℮ !!! + + + Ёŀένăţέ !! + + + Гĕŀõãð Ĕиνīřöŋm℮ηт Vàґίªъŀéš !!! !!! !! + + + Ѕρľїť Đΐŕєċŧĭőʼn !!! ! + + + Ѕφŀĭτ Μσđе !!! + + + Šрļϊŧ Ѕìžє !!! + diff --git a/src/cascadia/TerminalSettingsModel/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/ru-RU/Resources.resw index 147a457dac..d41c3c571d 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/ru-RU/Resources.resw @@ -1,17 +1,17 @@  - @@ -415,7 +415,7 @@ Очистить все метки - Отправить входное значение: "{0}" + Отправить входные данные: "{0}" {0} will be replaced with a string of input as defined by the user @@ -740,4 +740,240 @@ Открыть текущую рабочую папку + + Закрыть вкладку + + + Отправить входные данные + + + Настройка режима фокусировки + + + Настройка полноэкранного режима + + + Установить развернутое + + + Установить цветовую схему + + + Закрыть остальные вкладки + + + Закрыть вкладки после + + + Переместить вкладку + + + Найти совпадение + + + Поиск текста + + + Фокус на панели + + + Экспортировать буфер + + + Очистить буфер + + + Несколько действий + + + Настроить непрозрачность + + + Выбрать команду + + + Выбрать вывод + + + Предложения + + + Выбор цвета + + + Генератор профилей дистрибутивов WSL + The display name of a dynamic profile generator for WSL distros + + + Генератор профилей PowerShell + The display name of a dynamic profile generator for PowerShell + + + Генератор профилей Azure Cloud Shell + The display name of a dynamic profile generator for Azure Cloud Shell + + + Генератор профилей Visual Studio + The display name of a dynamic profile generator for Visual Studio + + + Генератор профилей узлов SSH + The display name of a dynamic profile generator for SSH hosts + + + Закрыть выделение + + + Одна строка + + + С последовательностями управления + + + Копировать формат + + + Индекс перехода по клавише TAB + + + Окно + + + Направление изменения размера + + + Направление фокуса + + + Направление + + + Дельта + + + Входные данные + + + Цель + + + Режим фокусировки + + + Развернуто + + + Во весь экран + + + Имя схемы + + + Цвет вкладки + + + Название + + + Командная строка + + + Индекс + + + Строки для прокрутки + + + Цвет + + + Режим запуска + + + Имя + + + Аккорд клавиш + + + Источник + + + Использовать командную строку + + + Режим переключателя + + + URL-адрес запроса + + + Рабочий стол + + + Мониторинг + + + Изменить видимость + + + Длительность раскрывающегося списка + + + Идентификатор + + + Путь + + + Ясно + + + Прозрачность + + + Относительный + + + Передний план + + + Фон + + + Режим соответствия + + + Начальный каталог + + + Название вкладки + + + Индекс профиля + + + Профиль + + + Скрыть название приложения + + + Цветовая схема + + + Повысить + + + Перезагрузить переменные среды + + + Направление разделения + + + Режим разделения + + + Размер разделения + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/zh-CN/Resources.resw index abef778111..a04a746c26 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/zh-CN/Resources.resw @@ -1,17 +1,17 @@  - @@ -740,4 +740,240 @@ 打开当前工作目录 + + 关闭标签页 + + + 发送输入 + + + 设置专注模式 + + + 设置全屏 + + + 设置最大化 + + + 设置配色方案 + + + 关闭其他标签页 + + + 关闭以下项后的选项卡 + + + 移动选项卡 + + + 查找匹配项 + + + 搜索文本 + + + 焦点窗格 + + + 导出缓冲区 + + + 清除缓冲区 + + + 多项操作 + + + 调整不透明度 + + + 选择命令 + + + 选择输出 + + + 建议 + + + 颜色选择 + + + WSL 发行版配置文件生成器 + The display name of a dynamic profile generator for WSL distros + + + PowerShell 配置文件生成器 + The display name of a dynamic profile generator for PowerShell + + + Azure Cloud Shell 配置文件生成器 + The display name of a dynamic profile generator for Azure Cloud Shell + + + Visual Studio 配置文件生成器 + The display name of a dynamic profile generator for Visual Studio + + + SSH 主机配置文件生成器 + The display name of a dynamic profile generator for SSH hosts + + + 关闭选择 + + + 单行 + + + 使用控件序列 + + + 复制格式 + + + Tab 索引 + + + 窗口 + + + 调整大小方向 + + + 焦点方向 + + + 方向 + + + 三角形 + + + 输入 + + + 目标 + + + 专注模式 + + + 已最大化 + + + 全屏 + + + 方案名称 + + + 工作表标签颜色 + + + 标题 + + + 命令行 + + + 索引 + + + 要滚动的行数 + + + 颜色 + + + 启动模式 + + + 名称 + + + 键组合 + + + + + + 使用命令行 + + + 切换器模式 + + + 查询 URL + + + 桌面 + + + 监视 + + + 切换可见性 + + + 下拉列表持续时间 + + + ID + + + 路径 + + + 清除 + + + 不透明度 + + + 相对 + + + 前景 + + + 背景 + + + 匹配模式 + + + 启动目录 + + + 选项卡标题 + + + 配置文件索引 + + + 个人资料 + + + 抑制应用程序标题 + + + 配色方案 + + + 提升 + + + 重新加载环境变量 + + + 拆分方向 + + + 分屏模式 + + + 拆分大小 + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/zh-TW/Resources.resw index 75edbf8518..cce546ffe8 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/zh-TW/Resources.resw @@ -1,17 +1,17 @@  - @@ -740,4 +740,240 @@ 開啟目前的工作目錄 + + 關閉索引標籤 + + + 傳送輸入 + + + 設定焦點模式 + + + 設定全螢幕 + + + 設定已最大化 + + + 設定色彩配置 + + + 關閉其他索引標籤 + + + 於以下之後關閉索引標籤 + + + 移動索引標籤 + + + 尋找相符項目 + + + 搜尋文字 + + + 焦點窗格 + + + 匯出緩衝區 + + + 清除緩衝區 + + + 多重動作 + + + 調整不透明度 + + + 選取命令 + + + 選取輸出 + + + 建議 + + + 色彩選取範圍 + + + WSL 發佈版設定檔產生器 + The display name of a dynamic profile generator for WSL distros + + + PowerShell 設定檔產生器 + The display name of a dynamic profile generator for PowerShell + + + Azure Cloud Shell 設定檔產生器 + The display name of a dynamic profile generator for Azure Cloud Shell + + + Visual Studio 設定檔產生器 + The display name of a dynamic profile generator for Visual Studio + + + SSH 主機設定檔產生器 + The display name of a dynamic profile generator for SSH hosts + + + 關閉選取範圍 + + + 單一行 + + + 使用控制項順序 + + + 複製格式設定 + + + 標籤索引 + + + 視窗 + + + 調整大小方向 + + + 焦點方向 + + + 方向 + + + Delta + + + 輸入 + + + 目標 + + + 為焦點模式 + + + 已最大化 + + + 為全螢幕 + + + 配置名稱 + + + 索引標籤色彩 + + + 標題 + + + 命令列 + + + 索引 + + + 要捲動的資料列 + + + 顏色 + + + 啟動模式 + + + 名稱 + + + 按鍵同步選取 + + + 來源 + + + 使用命令列 + + + 切換器模式 + + + 查詢 URL + + + 桌面 + + + 監視 + + + 切換可見度 + + + 下拉式清單持續時間 + + + 識別碼 + + + 路徑 + + + 晴朗 + + + 不透明度 + + + 相對 + + + 前景 + + + 背景 + + + 比對模式 + + + 起始目錄 + + + 索引標籤標題 + + + 設定檔索引 + + + 設定檔 + + + 隱藏應用程式標題 + + + 色彩配置 + + + 提升 + + + 重新載入環境變數 + + + 分割方向 + + + 分割模式 + + + 分割大小 + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/SshHostGenerator.cpp b/src/cascadia/TerminalSettingsModel/SshHostGenerator.cpp index 30dc19039f..e3c7ebbcb3 100644 --- a/src/cascadia/TerminalSettingsModel/SshHostGenerator.cpp +++ b/src/cascadia/TerminalSettingsModel/SshHostGenerator.cpp @@ -7,11 +7,13 @@ #include "../../inc/DefaultSettings.h" #include "DynamicProfileUtils.h" +#include static constexpr std::wstring_view SshHostGeneratorNamespace{ L"Windows.Terminal.SSH" }; static constexpr std::wstring_view PROFILE_TITLE_PREFIX = L"SSH - "; -static constexpr std::wstring_view PROFILE_ICON_PATH = L"ms-appx:///ProfileIcons/{550ce7b8-d500-50ad-8a1a-c400c3262db3}.png"; +static constexpr std::wstring_view PROFILE_ICON_PATH = L"\uE977"; // PC1 +static constexpr std::wstring_view GENERATOR_ICON_PATH = L"\uE969"; // StorageNetworkWireless // OpenSSH is installed under System32 when installed via Optional Features static constexpr std::wstring_view SSH_EXE_PATH1 = L"%SystemRoot%\\System32\\OpenSSH\\ssh.exe"; @@ -132,6 +134,16 @@ std::wstring_view SshHostGenerator::GetNamespace() const noexcept return SshHostGeneratorNamespace; } +std::wstring_view SshHostGenerator::GetDisplayName() const noexcept +{ + return RS_(L"SshHostGeneratorDisplayName"); +} + +std::wstring_view SshHostGenerator::GetIcon() const noexcept +{ + return GENERATOR_ICON_PATH; +} + // Method Description: // - Generate a list of profiles for each detected OpenSSH host. // Arguments: diff --git a/src/cascadia/TerminalSettingsModel/SshHostGenerator.h b/src/cascadia/TerminalSettingsModel/SshHostGenerator.h index a355b0c430..d83e62535b 100644 --- a/src/cascadia/TerminalSettingsModel/SshHostGenerator.h +++ b/src/cascadia/TerminalSettingsModel/SshHostGenerator.h @@ -24,6 +24,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model { public: std::wstring_view GetNamespace() const noexcept override; + std::wstring_view GetDisplayName() const noexcept override; + std::wstring_view GetIcon() const noexcept override; void GenerateProfiles(std::vector>& profiles) const override; private: diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h deleted file mode 100644 index 9591ad3487..0000000000 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h +++ /dev/null @@ -1,203 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- TerminalSettings.h - -Abstract: -- The implementation of the TerminalSettings winrt class. Provides both - terminal control settings and terminal core settings. -Author(s): -- Mike Griese - March 2019 - ---*/ -#pragma once - -#include "TerminalSettings.g.h" -#include "TerminalSettingsCreateResult.g.h" -#include "IInheritable.h" -#include -#include - -using IFontAxesMap = winrt::Windows::Foundation::Collections::IMap; -using IFontFeatureMap = winrt::Windows::Foundation::Collections::IMap; -using IEnvironmentVariableMap = winrt::Windows::Foundation::Collections::IMap; - -// fwdecl unittest classes -namespace SettingsModelUnitTests -{ - class TerminalSettingsTests; -} - -namespace winrt::Microsoft::Terminal::Settings::Model::implementation -{ - struct TerminalSettingsCreateResult : - public TerminalSettingsCreateResultT - { - public: - TerminalSettingsCreateResult(Model::TerminalSettings defaultSettings, Model::TerminalSettings unfocusedSettings) : - _defaultSettings(defaultSettings), - _unfocusedSettings(unfocusedSettings) {} - - TerminalSettingsCreateResult(Model::TerminalSettings defaultSettings) : - _defaultSettings(defaultSettings), - _unfocusedSettings(nullptr) {} - - Model::TerminalSettings DefaultSettings() { return _defaultSettings; }; - Model::TerminalSettings UnfocusedSettings() { return _unfocusedSettings; }; - - private: - Model::TerminalSettings _defaultSettings; - Model::TerminalSettings _unfocusedSettings; - }; - - struct TerminalSettings : TerminalSettingsT, IInheritable - { - TerminalSettings() = default; - - static Model::TerminalSettings CreateForPreview(const Model::CascadiaSettings& appSettings, const Model::Profile& profile); - - static Model::TerminalSettingsCreateResult CreateWithProfile(const Model::CascadiaSettings& appSettings, - const Model::Profile& profile, - const Control::IKeyBindings& keybindings); - - static Model::TerminalSettingsCreateResult CreateWithNewTerminalArgs(const Model::CascadiaSettings& appSettings, - const Model::NewTerminalArgs& newTerminalArgs, - const Control::IKeyBindings& keybindings); - - void ApplyColorScheme(const Model::ColorScheme& scheme); - - // --------------------------- Core Settings --------------------------- - // All of these settings are defined in ICoreSettings. - - // GetColorTableEntry needs to be implemented manually, to get a - // particular value from the array. - Microsoft::Terminal::Core::Color GetColorTableEntry(int32_t index) noexcept; - void ColorTable(std::array colors); - std::array ColorTable(); - - INHERITABLE_SETTING(Model::TerminalSettings, til::color, DefaultForeground, DEFAULT_FOREGROUND); - INHERITABLE_SETTING(Model::TerminalSettings, til::color, DefaultBackground, DEFAULT_BACKGROUND); - INHERITABLE_SETTING(Model::TerminalSettings, til::color, SelectionBackground, DEFAULT_FOREGROUND); - INHERITABLE_SETTING(Model::TerminalSettings, int32_t, HistorySize, DEFAULT_HISTORY_SIZE); - INHERITABLE_SETTING(Model::TerminalSettings, int32_t, InitialRows, 30); - INHERITABLE_SETTING(Model::TerminalSettings, int32_t, InitialCols, 80); - - INHERITABLE_SETTING(Model::TerminalSettings, bool, SnapOnInput, true); - INHERITABLE_SETTING(Model::TerminalSettings, bool, AltGrAliasing, true); - INHERITABLE_SETTING(Model::TerminalSettings, hstring, AnswerbackMessage); - INHERITABLE_SETTING(Model::TerminalSettings, til::color, CursorColor, DEFAULT_CURSOR_COLOR); - INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Core::CursorStyle, CursorShape, Core::CursorStyle::Vintage); - INHERITABLE_SETTING(Model::TerminalSettings, uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT); - INHERITABLE_SETTING(Model::TerminalSettings, hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS); - INHERITABLE_SETTING(Model::TerminalSettings, bool, CopyOnSelect, false); - INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::CopyFormat, CopyFormatting, 0); - INHERITABLE_SETTING(Model::TerminalSettings, bool, FocusFollowMouse, false); - INHERITABLE_SETTING(Model::TerminalSettings, bool, AllowVtChecksumReport, false); - INHERITABLE_SETTING(Model::TerminalSettings, bool, TrimBlockSelection, true); - INHERITABLE_SETTING(Model::TerminalSettings, bool, DetectURLs, true); - INHERITABLE_SETTING(Model::TerminalSettings, bool, AllowVtClipboardWrite, true); - - INHERITABLE_SETTING(Model::TerminalSettings, Windows::Foundation::IReference, TabColor, nullptr); - - // When set, StartingTabColor allows to create a terminal with a "sticky" tab color. - // This color is prioritized above the TabColor (that is usually initialized based on profile settings). - // Due to this prioritization, the tab color will be preserved upon settings reload - // (even if the profile's tab color gets altered or removed). - // This property is expected to be passed only once upon terminal creation. - // TODO: to ensure that this property is not populated during settings reload, - // we should consider moving this property to a separate interface, - // passed to the terminal only upon creation. - INHERITABLE_SETTING(Model::TerminalSettings, Windows::Foundation::IReference, StartingTabColor, nullptr); - - INHERITABLE_SETTING(Model::TerminalSettings, bool, IntenseIsBold); - INHERITABLE_SETTING(Model::TerminalSettings, bool, IntenseIsBright); - - INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Core::AdjustTextMode, AdjustIndistinguishableColors, Core::AdjustTextMode::Never); - INHERITABLE_SETTING(Model::TerminalSettings, bool, RainbowSuggestions, false); - - // ------------------------ End of Core Settings ----------------------- - - INHERITABLE_SETTING(Model::TerminalSettings, hstring, ProfileName); - - INHERITABLE_SETTING(Model::TerminalSettings, guid, SessionId); - INHERITABLE_SETTING(Model::TerminalSettings, bool, EnableUnfocusedAcrylic, false); - INHERITABLE_SETTING(Model::TerminalSettings, bool, UseAcrylic, false); - INHERITABLE_SETTING(Model::TerminalSettings, float, Opacity, UseAcrylic() ? 0.5f : 1.0f); - INHERITABLE_SETTING(Model::TerminalSettings, hstring, Padding, DEFAULT_PADDING); - INHERITABLE_SETTING(Model::TerminalSettings, hstring, FontFace, DEFAULT_FONT_FACE); - INHERITABLE_SETTING(Model::TerminalSettings, float, FontSize, DEFAULT_FONT_SIZE); - - INHERITABLE_SETTING(Model::TerminalSettings, winrt::Windows::UI::Text::FontWeight, FontWeight); - INHERITABLE_SETTING(Model::TerminalSettings, IFontAxesMap, FontAxes); - INHERITABLE_SETTING(Model::TerminalSettings, IFontFeatureMap, FontFeatures); - INHERITABLE_SETTING(Model::TerminalSettings, bool, EnableBuiltinGlyphs, true); - INHERITABLE_SETTING(Model::TerminalSettings, bool, EnableColorGlyphs, true); - INHERITABLE_SETTING(Model::TerminalSettings, hstring, CellWidth); - INHERITABLE_SETTING(Model::TerminalSettings, hstring, CellHeight); - - INHERITABLE_SETTING(Model::TerminalSettings, Model::ColorScheme, AppliedColorScheme); - INHERITABLE_SETTING(Model::TerminalSettings, hstring, BackgroundImage); - INHERITABLE_SETTING(Model::TerminalSettings, float, BackgroundImageOpacity, 1.0f); - - INHERITABLE_SETTING(Model::TerminalSettings, winrt::Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill); - INHERITABLE_SETTING(Model::TerminalSettings, winrt::Windows::UI::Xaml::HorizontalAlignment, BackgroundImageHorizontalAlignment, winrt::Windows::UI::Xaml::HorizontalAlignment::Center); - INHERITABLE_SETTING(Model::TerminalSettings, winrt::Windows::UI::Xaml::VerticalAlignment, BackgroundImageVerticalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment::Center); - - INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::IKeyBindings, KeyBindings, nullptr); - - INHERITABLE_SETTING(Model::TerminalSettings, hstring, Commandline); - INHERITABLE_SETTING(Model::TerminalSettings, hstring, StartingDirectory); - INHERITABLE_SETTING(Model::TerminalSettings, hstring, StartingTitle); - INHERITABLE_SETTING(Model::TerminalSettings, bool, SuppressApplicationTitle); - INHERITABLE_SETTING(Model::TerminalSettings, IEnvironmentVariableMap, EnvironmentVariables); - - INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::ScrollbarState, ScrollState, Microsoft::Terminal::Control::ScrollbarState::Visible); - - INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale); - - INHERITABLE_SETTING(Model::TerminalSettings, bool, RetroTerminalEffect, false); - INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI); - INHERITABLE_SETTING(Model::TerminalSettings, bool, DisablePartialInvalidation, false); - INHERITABLE_SETTING(Model::TerminalSettings, bool, SoftwareRendering, false); - INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::TextMeasurement, TextMeasurement); - INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::DefaultInputScope, DefaultInputScope); - INHERITABLE_SETTING(Model::TerminalSettings, bool, UseBackgroundImageForWindow, false); - INHERITABLE_SETTING(Model::TerminalSettings, bool, ForceVTInput, false); - - INHERITABLE_SETTING(Model::TerminalSettings, hstring, PixelShaderPath); - INHERITABLE_SETTING(Model::TerminalSettings, hstring, PixelShaderImagePath); - - INHERITABLE_SETTING(Model::TerminalSettings, bool, Elevate, false); - - INHERITABLE_SETTING(Model::TerminalSettings, bool, AutoMarkPrompts, false); - INHERITABLE_SETTING(Model::TerminalSettings, bool, ShowMarks, false); - INHERITABLE_SETTING(Model::TerminalSettings, bool, RightClickContextMenu, false); - INHERITABLE_SETTING(Model::TerminalSettings, bool, RepositionCursorWithMouse, false); - - INHERITABLE_SETTING(Model::TerminalSettings, bool, ReloadEnvironmentVariables, true); - - INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, Microsoft::Terminal::Control::PathTranslationStyle::None); - - private: - std::optional> _ColorTable; - std::span _getColorTableImpl(); - - static winrt::com_ptr _CreateWithProfileCommon(const Model::CascadiaSettings& appSettings, const Model::Profile& profile); - void _ApplyProfileSettings(const Model::Profile& profile); - - void _ApplyGlobalSettings(const Model::GlobalAppSettings& globalSettings) noexcept; - void _ApplyAppearanceSettings(const Microsoft::Terminal::Settings::Model::IAppearanceConfig& appearance, - const Windows::Foundation::Collections::IMapView& schemes, - const winrt::Microsoft::Terminal::Settings::Model::Theme currentTheme); - - friend class SettingsModelUnitTests::TerminalSettingsTests; - }; -} - -namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation -{ - BASIC_FACTORY(TerminalSettingsCreateResult); - BASIC_FACTORY(TerminalSettings); -} diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.idl b/src/cascadia/TerminalSettingsModel/TerminalSettings.idl deleted file mode 100644 index 32bf611190..0000000000 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.idl +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import "CascadiaSettings.idl"; - -#define COMMA , - -namespace Microsoft.Terminal.Settings.Model -{ - runtimeclass TerminalSettingsCreateResult - { - TerminalSettingsCreateResult(TerminalSettings defaultSettings); - TerminalSettings DefaultSettings { get; }; - TerminalSettings UnfocusedSettings { get; }; - } - - // Class Description: - // TerminalSettings encapsulates all settings that control the - // TermControl's behavior. In these settings there is both the entirety - // of the Core ICoreSettings properties and the IControlSettings - // properties. It's the Profile's responsibility to build this from - // settings it contains, in combination with the global settings. - // The TerminalControl will pull settings it requires from this object, - // and pass along the Core properties to the terminal core. - [default_interface] - runtimeclass TerminalSettings : Microsoft.Terminal.Core.ICoreSettings, - Microsoft.Terminal.Control.IControlSettings - { - TerminalSettings(); - - Windows.Foundation.Collections.IMap EnvironmentVariables; - - static TerminalSettings CreateForPreview(CascadiaSettings appSettings, Profile profile); - static TerminalSettingsCreateResult CreateWithProfile(CascadiaSettings appSettings, Profile profile, Microsoft.Terminal.Control.IKeyBindings keybindings); - static TerminalSettingsCreateResult CreateWithNewTerminalArgs(CascadiaSettings appSettings, NewTerminalArgs newTerminalArgs, Microsoft.Terminal.Control.IKeyBindings keybindings); - - void ApplyColorScheme(ColorScheme scheme); - - ColorScheme AppliedColorScheme; - - // The getters for these are already defined in IControlSettings. So - // we're just adding the setters here, because TerminalApp likes to be - // able to change these at runtime (e.g. when duplicating a pane). - Guid SessionId { set; }; - String Commandline { set; }; - String StartingDirectory { set; }; - - Boolean Elevate; - Boolean ReloadEnvironmentVariables; - }; -} diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h index fa355b39ba..b2355ce4c7 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h @@ -18,6 +18,7 @@ Abstract: #include "JsonUtils.h" #include "SettingsTypes.h" #include "ModelSerializationHelpers.h" +#include "MediaResourceSupport.h" JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Core::CursorStyle) { @@ -35,10 +36,11 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Core::CursorStyle) // - Helper for converting a user-specified adjustTextMode value to its corresponding enum JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Core::AdjustTextMode) { - JSON_MAPPINGS(3) = { + JSON_MAPPINGS(4) = { pair_type{ "never", ValueType::Never }, pair_type{ "indexed", ValueType::Indexed }, pair_type{ "always", ValueType::Always }, + pair_type{ "automatic", ValueType::Automatic }, }; // Override mapping parser to add boolean parsing @@ -766,6 +768,43 @@ struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<::winr } }; +template<> +struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<::winrt::Microsoft::Terminal::Settings::Model::IMediaResource> +{ + ::winrt::Microsoft::Terminal::Settings::Model::IMediaResource FromJson(const Json::Value& json) + { + if (json.isNull()) [[unlikely]] + { + // Do not use Empty here, as Empty is shared across all instances. + return ::winrt::Microsoft::Terminal::Settings::Model::implementation::MediaResource::FromString(L""); + } + + winrt::hstring string{ til::u8u16(Detail::GetStringView(json)) }; + return ::winrt::Microsoft::Terminal::Settings::Model::implementation::MediaResource::FromString(string); + } + + bool CanConvert(const Json::Value& json) + { + return json.isString() || json.isNull(); + } + + Json::Value ToJson(const ::winrt::Microsoft::Terminal::Settings::Model::IMediaResource& val) + { + if (!val || val.Path() == winrt::hstring{}) + { + // empty string becomes null (is this correct?) + return Json::Value::nullSingleton(); + } + + return til::u16u8(val.Path()); + } + + std::string TypeDescription() const + { + return "file path"; + } +}; + JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::GraphicsAPI) { JSON_MAPPINGS(3) = { @@ -794,10 +833,37 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::DefaultInputScope) JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::PathTranslationStyle) { - static constexpr std::array mappings = { + static constexpr std::array mappings = { pair_type{ "none", ValueType::None }, pair_type{ "wsl", ValueType::WSL }, pair_type{ "cygwin", ValueType::Cygwin }, pair_type{ "msys2", ValueType::MSYS2 }, + pair_type{ "mingw", ValueType::MinGW }, }; }; + +JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::WarnAboutMultiLinePaste) +{ + JSON_MAPPINGS(3) = { + pair_type{ "automatic", ValueType::Automatic }, + pair_type{ "always", ValueType::Always }, + pair_type{ "never", ValueType::Never }, + }; + + // Override mapping parser to add boolean parsing + ::winrt::Microsoft::Terminal::Control::WarnAboutMultiLinePaste FromJson(const Json::Value& json) + { + if (json.isBool()) + { + return json.asBool() ? ValueType::Automatic : ValueType::Never; + } + return EnumMapper::FromJson(json); + } + + bool CanConvert(const Json::Value& json) + { + return EnumMapper::CanConvert(json) || json.isBool(); + } + + using EnumMapper::TypeDescription; +}; diff --git a/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl b/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl index aa02d18ff4..74f3737966 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl +++ b/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl @@ -10,8 +10,7 @@ namespace Microsoft.Terminal.Settings.Model MissingDefaultProfile = 0, DuplicateProfile, UnknownColorScheme, - InvalidBackgroundImage, - InvalidIcon, + InvalidMediaResource, AtLeastOneKeybindingWarning, TooManyKeysForChord, MissingRequiredParameter, @@ -25,6 +24,7 @@ namespace Microsoft.Terminal.Settings.Model UnknownTheme, DuplicateRemainingProfilesEntry, InvalidUseOfContent, + InvalidRegex, WARNINGS_SIZE // IMPORTANT: This MUST be the last value in this enum. It's an unused placeholder. }; diff --git a/src/cascadia/TerminalSettingsModel/TestHooks.cpp b/src/cascadia/TerminalSettingsModel/TestHooks.cpp new file mode 100644 index 0000000000..daf6147425 --- /dev/null +++ b/src/cascadia/TerminalSettingsModel/TestHooks.cpp @@ -0,0 +1,14 @@ +#include "pch.h" +#include "winrt/Microsoft.Terminal.Settings.Model.h" + +// Through the power of LTCG, this will all get deleted... and all call +// sites too. +// https://devblogs.microsoft.com/oldnewthing/20250416-00/?p=111077 + +bool TestHook_CascadiaSettings_ResolveSingleMediaResource( + winrt::Microsoft::Terminal::Settings::Model::OriginTag, + std::wstring_view, + const winrt::Microsoft::Terminal::Settings::Model::IMediaResource&) +{ + return false; +} diff --git a/src/cascadia/TerminalSettingsModel/Theme.cpp b/src/cascadia/TerminalSettingsModel/Theme.cpp index 90fd1d1a76..3b79b5297d 100644 --- a/src/cascadia/TerminalSettingsModel/Theme.cpp +++ b/src/cascadia/TerminalSettingsModel/Theme.cpp @@ -372,7 +372,7 @@ winrt::hstring Theme::ToString() // RequestedTheme, this saves some hassle. If there wasn't a `window` defined // for this theme, this'll quickly just return `system`, to use the OS theme. // Return Value: -// - the set applicationTheme for this Theme, otherwise the system theme. +// - the set applicationTheme for this Theme; otherwise, the system theme. winrt::WUX::ElementTheme Theme::RequestedTheme() const noexcept { return _Window ? _Window.RequestedTheme() : winrt::WUX::ElementTheme::Default; diff --git a/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.cpp b/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.cpp index 2fcfb1cc52..69feb6174e 100644 --- a/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.cpp +++ b/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.cpp @@ -6,16 +6,28 @@ #include "VisualStudioGenerator.h" #include "VsDevCmdGenerator.h" #include "VsDevShellGenerator.h" +#include using namespace winrt::Microsoft::Terminal::Settings::Model; std::wstring_view VisualStudioGenerator::Namespace{ L"Windows.Terminal.VisualStudio" }; +static constexpr std::wstring_view IconPath{ L"ms-appx:///ProfileGeneratorIcons/VisualStudio.png" }; std::wstring_view VisualStudioGenerator::GetNamespace() const noexcept { return Namespace; } +std::wstring_view VisualStudioGenerator::GetDisplayName() const noexcept +{ + return RS_(L"VisualStudioGeneratorDisplayName"); +} + +std::wstring_view VisualStudioGenerator::GetIcon() const noexcept +{ + return IconPath; +} + void VisualStudioGenerator::GenerateProfiles(std::vector>& profiles) const { const auto instances = VsSetupConfiguration::QueryInstances(); diff --git a/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.h b/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.h index ef36284db7..c89e7c6301 100644 --- a/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.h +++ b/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.h @@ -28,6 +28,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model public: static std::wstring_view Namespace; std::wstring_view GetNamespace() const noexcept override; + std::wstring_view GetDisplayName() const noexcept override; + std::wstring_view GetIcon() const noexcept override; void GenerateProfiles(std::vector>& profiles) const override; class IVisualStudioProfileGenerator diff --git a/src/cascadia/TerminalSettingsModel/VsDevCmdGenerator.cpp b/src/cascadia/TerminalSettingsModel/VsDevCmdGenerator.cpp index 19d6f50cc8..c4baf36a39 100644 --- a/src/cascadia/TerminalSettingsModel/VsDevCmdGenerator.cpp +++ b/src/cascadia/TerminalSettingsModel/VsDevCmdGenerator.cpp @@ -45,7 +45,15 @@ std::wstring VsDevCmdGenerator::GetProfileCommandLine(const VsSetupConfiguration // The "-startdir" parameter will prevent "vsdevcmd" from automatically // setting the shell path so the path in the profile will be used instead. #if defined(_M_ARM64) - commandLine.append(LR"(" -startdir=none -arch=arm64 -host_arch=x64)"); + commandLine.append(LR"(" -startdir=none -arch=arm64 -host_arch=)"); + if (instance.VersionInRange(L"[17.4,")) + { + commandLine.append(LR"(arm64)"); + } + else + { + commandLine.append(LR"(x64)"); + } #elif defined(_M_AMD64) commandLine.append(LR"(" -startdir=none -arch=x64 -host_arch=x64)"); #else diff --git a/src/cascadia/TerminalSettingsModel/VsDevShellGenerator.cpp b/src/cascadia/TerminalSettingsModel/VsDevShellGenerator.cpp index 22c17cd98a..ce14532107 100644 --- a/src/cascadia/TerminalSettingsModel/VsDevShellGenerator.cpp +++ b/src/cascadia/TerminalSettingsModel/VsDevShellGenerator.cpp @@ -39,17 +39,43 @@ std::wstring VsDevShellGenerator::GetProfileName(const VsSetupConfiguration::VsS std::wstring VsDevShellGenerator::GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const { - // The triple-quotes are a PowerShell path escape sequence that can safely be stored in a JSON object. - // The "SkipAutomaticLocation" parameter will prevent "Enter-VsDevShell" from automatically setting the shell path - // so the path in the profile will be used instead. + // Build this in stages, so reserve space now std::wstring commandLine; commandLine.reserve(256); - commandLine.append(LR"(powershell.exe -NoExit -Command "&{Import-Module """)"); + + // Try to detect if `pwsh.exe` is available in the PATH, if so we want to use that + // Allow some extra space in case user put it somewhere odd + // We do need to allocate space for the full path even if we don't want to paste the whole thing in + wchar_t pwshPath[MAX_PATH] = { 0 }; + const auto pwshExeName = L"pwsh.exe"; + if (SearchPathW(nullptr, pwshExeName, nullptr, MAX_PATH, pwshPath, nullptr)) + { + commandLine.append(pwshExeName); + } + else + { + commandLine.append(L"powershell.exe"); + } + + // The triple-quotes are a PowerShell path escape sequence that can safely be stored in a JSON object. + // The "SkipAutomaticLocation" parameter will prevent "Enter-VsDevShell" from automatically setting the shell path + // so the path in the profile will be used instead + commandLine.append(LR"( -NoExit -Command "&{Import-Module """)"); commandLine.append(GetDevShellModulePath(instance)); commandLine.append(LR"("""; Enter-VsDevShell )"); commandLine.append(instance.GetInstanceId()); #if defined(_M_ARM64) - commandLine.append(LR"( -SkipAutomaticLocation -DevCmdArguments """-arch=arm64 -host_arch=x64"""}")"); + // This part stays constant no matter what + commandLine.append(LR"( -SkipAutomaticLocation -DevCmdArguments """-arch=arm64 -host_arch=)"); + if (instance.VersionInRange(L"[17.4,")) + { + commandLine.append(LR"("arm64 """}")"); + } + // If an old version of VS is installed without ARM64 host support + else + { + commandLine.append(LR"("x64 """}")"); + } #elif defined(_M_AMD64) commandLine.append(LR"( -SkipAutomaticLocation -DevCmdArguments """-arch=x64 -host_arch=x64"""}")"); #else diff --git a/src/cascadia/TerminalSettingsModel/WslDistroGenerator.cpp b/src/cascadia/TerminalSettingsModel/WslDistroGenerator.cpp index cb1d220a90..2e0c81e5a5 100644 --- a/src/cascadia/TerminalSettingsModel/WslDistroGenerator.cpp +++ b/src/cascadia/TerminalSettingsModel/WslDistroGenerator.cpp @@ -8,10 +8,13 @@ #include "../../inc/DefaultSettings.h" #include "DynamicProfileUtils.h" +#include static constexpr std::wstring_view WslHomeDirectory{ L"~" }; static constexpr std::wstring_view DockerDistributionPrefix{ L"docker-desktop" }; static constexpr std::wstring_view RancherDistributionPrefix{ L"rancher-desktop" }; +static constexpr std::wstring_view IconPath{ L"ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png" }; +static constexpr std::wstring_view GeneratorIconPath{ L"ms-appx:///ProfileGeneratorIcons/WSL.png" }; // The WSL entries are structured as such: // HKCU\Software\Microsoft\Windows\CurrentVersion\Lxss @@ -47,6 +50,16 @@ std::wstring_view WslDistroGenerator::GetNamespace() const noexcept return WslGeneratorNamespace; } +std::wstring_view WslDistroGenerator::GetDisplayName() const noexcept +{ + return RS_(L"WslDistroGeneratorDisplayName"); +} + +std::wstring_view WslDistroGenerator::GetIcon() const noexcept +{ + return GeneratorIconPath; +} + static winrt::com_ptr makeProfile(const std::wstring& distName) { const auto WSLDistro{ CreateDynamicProfile(distName) }; @@ -65,7 +78,7 @@ static winrt::com_ptr makeProfile(const std::wstring& d { WSLDistro->StartingDirectory(winrt::hstring{ DEFAULT_STARTING_DIRECTORY }); } - WSLDistro->Icon(L"ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png"); + WSLDistro->Icon(winrt::hstring{ IconPath }); WSLDistro->PathTranslationStyle(winrt::Microsoft::Terminal::Control::PathTranslationStyle::WSL); return WSLDistro; } diff --git a/src/cascadia/TerminalSettingsModel/WslDistroGenerator.h b/src/cascadia/TerminalSettingsModel/WslDistroGenerator.h index b46aac8203..123734523f 100644 --- a/src/cascadia/TerminalSettingsModel/WslDistroGenerator.h +++ b/src/cascadia/TerminalSettingsModel/WslDistroGenerator.h @@ -24,6 +24,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model { public: std::wstring_view GetNamespace() const noexcept override; + std::wstring_view GetDisplayName() const noexcept override; + std::wstring_view GetIcon() const noexcept override; void GenerateProfiles(std::vector>& profiles) const override; }; }; diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json index d8840bb0cd..cc82b1262d 100644 --- a/src/cascadia/TerminalSettingsModel/defaults.json +++ b/src/cascadia/TerminalSettingsModel/defaults.json @@ -85,7 +85,7 @@ "background": "#141414", "foreground": "#BAB7B6", "cursorColor": "#37E57B", - "selectionBackground": "#FFFFFF", + "selectionBackground": "#8DB8E5", "black": "#000000", "red": "#CF494C", "green": "#60B442", @@ -101,7 +101,7 @@ "brightBlue": "#688DFD", "brightPurple": "#ED6FE9", "brightCyan": "#32E0FB", - "brightWhite": "#D3D8D9" + "brightWhite": "#DEE3E4" }, { "name": "Ottosson", @@ -745,6 +745,7 @@ { "keys": "ctrl+shift+pgup", "id": "Terminal.ScrollUpPage" }, { "keys": "ctrl+shift+home", "id": "Terminal.ScrollToTop" }, { "keys": "ctrl+shift+end", "id": "Terminal.ScrollToBottom" }, + { "keys": "ctrl+shift+k", "id": "Terminal.ClearBuffer" }, // Visual Adjustments { "keys": "ctrl+plus", "id": "Terminal.IncreaseFontSize" }, diff --git a/src/cascadia/TerminalSettingsModel/dll/Microsoft.Terminal.Settings.Model.vcxproj b/src/cascadia/TerminalSettingsModel/dll/Microsoft.Terminal.Settings.Model.vcxproj index 6ab7c60689..eaf33503a4 100644 --- a/src/cascadia/TerminalSettingsModel/dll/Microsoft.Terminal.Settings.Model.vcxproj +++ b/src/cascadia/TerminalSettingsModel/dll/Microsoft.Terminal.Settings.Model.vcxproj @@ -11,6 +11,7 @@ true true + Windows Terminal Settings Model Library true diff --git a/src/cascadia/TerminalSettingsModel/pch.h b/src/cascadia/TerminalSettingsModel/pch.h index 2a595f2d4d..dcfb26edf1 100644 --- a/src/cascadia/TerminalSettingsModel/pch.h +++ b/src/cascadia/TerminalSettingsModel/pch.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include diff --git a/src/cascadia/UIHelpers/IconPathConverter.cpp b/src/cascadia/UIHelpers/IconPathConverter.cpp index 4d8bf2b90f..5caa60ddde 100644 --- a/src/cascadia/UIHelpers/IconPathConverter.cpp +++ b/src/cascadia/UIHelpers/IconPathConverter.cpp @@ -4,6 +4,8 @@ #include "Utils.h" +#include "../types/inc/utils.hpp" + #include #include #include @@ -132,10 +134,7 @@ namespace winrt::Microsoft::Terminal::UI::implementation // If we fail to set the icon source using the "icon" as a path, // let's try it as a symbol/emoji. - // - // Anything longer than 2 wchar_t's _isn't_ an emoji or symbol, so - // don't do this if it's just an invalid path. - if (!iconSource && iconPath.size() <= 2) + if (!iconSource && ::Microsoft::Console::Utils::IsLikelyToBeEmojiOrSymbolIcon(iconPath)) { try { diff --git a/src/cascadia/UIHelpers/Resources/de-DE/Resources.resw b/src/cascadia/UIHelpers/Resources/de-DE/Resources.resw new file mode 100644 index 0000000000..147375bc16 --- /dev/null +++ b/src/cascadia/UIHelpers/Resources/de-DE/Resources.resw @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Ausschneiden + "Cut" as contained in a Cut/Copy/Paste menu + + + Kopieren + "Copy" as contained in a Cut/Copy/Paste menu + + + Einfügen + "Paste" as contained in a Cut/Copy/Paste menu + + + Alles auswählen + "Select All" as contained in a Cut/Copy/Paste menu + + \ No newline at end of file diff --git a/src/cascadia/UIHelpers/Resources/en-US/Resources.resw b/src/cascadia/UIHelpers/Resources/en-US/Resources.resw new file mode 100644 index 0000000000..af3a8d1a05 --- /dev/null +++ b/src/cascadia/UIHelpers/Resources/en-US/Resources.resw @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cut + "Cut" as contained in a Cut/Copy/Paste menu + + + Copy + "Copy" as contained in a Cut/Copy/Paste menu + + + Paste + "Paste" as contained in a Cut/Copy/Paste menu + + + Select All + "Select All" as contained in a Cut/Copy/Paste menu + + \ No newline at end of file diff --git a/src/cascadia/UIHelpers/Resources/es-ES/Resources.resw b/src/cascadia/UIHelpers/Resources/es-ES/Resources.resw new file mode 100644 index 0000000000..c4747c4733 --- /dev/null +++ b/src/cascadia/UIHelpers/Resources/es-ES/Resources.resw @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cortar + "Cut" as contained in a Cut/Copy/Paste menu + + + Copiar + "Copy" as contained in a Cut/Copy/Paste menu + + + Pegar + "Paste" as contained in a Cut/Copy/Paste menu + + + Seleccionar todo + "Select All" as contained in a Cut/Copy/Paste menu + + \ No newline at end of file diff --git a/src/cascadia/UIHelpers/Resources/fr-FR/Resources.resw b/src/cascadia/UIHelpers/Resources/fr-FR/Resources.resw new file mode 100644 index 0000000000..25be5734ec --- /dev/null +++ b/src/cascadia/UIHelpers/Resources/fr-FR/Resources.resw @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Couper + "Cut" as contained in a Cut/Copy/Paste menu + + + Copier + "Copy" as contained in a Cut/Copy/Paste menu + + + Coller + "Paste" as contained in a Cut/Copy/Paste menu + + + Sélectionner tout + "Select All" as contained in a Cut/Copy/Paste menu + + \ No newline at end of file diff --git a/src/cascadia/UIHelpers/Resources/it-IT/Resources.resw b/src/cascadia/UIHelpers/Resources/it-IT/Resources.resw new file mode 100644 index 0000000000..a8e144c2ef --- /dev/null +++ b/src/cascadia/UIHelpers/Resources/it-IT/Resources.resw @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Taglia + "Cut" as contained in a Cut/Copy/Paste menu + + + Copia + "Copy" as contained in a Cut/Copy/Paste menu + + + Incolla + "Paste" as contained in a Cut/Copy/Paste menu + + + Seleziona tutto + "Select All" as contained in a Cut/Copy/Paste menu + + \ No newline at end of file diff --git a/src/cascadia/UIHelpers/Resources/ja-JP/Resources.resw b/src/cascadia/UIHelpers/Resources/ja-JP/Resources.resw new file mode 100644 index 0000000000..b87a07f71c --- /dev/null +++ b/src/cascadia/UIHelpers/Resources/ja-JP/Resources.resw @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 切り取り + "Cut" as contained in a Cut/Copy/Paste menu + + + コピー + "Copy" as contained in a Cut/Copy/Paste menu + + + 貼り付け + "Paste" as contained in a Cut/Copy/Paste menu + + + すべて選択 + "Select All" as contained in a Cut/Copy/Paste menu + + \ No newline at end of file diff --git a/src/cascadia/UIHelpers/Resources/ko-KR/Resources.resw b/src/cascadia/UIHelpers/Resources/ko-KR/Resources.resw new file mode 100644 index 0000000000..ef0902c477 --- /dev/null +++ b/src/cascadia/UIHelpers/Resources/ko-KR/Resources.resw @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 잘라내기 + "Cut" as contained in a Cut/Copy/Paste menu + + + 복사 + "Copy" as contained in a Cut/Copy/Paste menu + + + 붙여넣기 + "Paste" as contained in a Cut/Copy/Paste menu + + + 모두 선택 + "Select All" as contained in a Cut/Copy/Paste menu + + \ No newline at end of file diff --git a/src/cascadia/UIHelpers/Resources/pt-BR/Resources.resw b/src/cascadia/UIHelpers/Resources/pt-BR/Resources.resw new file mode 100644 index 0000000000..66f820dc2a --- /dev/null +++ b/src/cascadia/UIHelpers/Resources/pt-BR/Resources.resw @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Recortar + "Cut" as contained in a Cut/Copy/Paste menu + + + Copiar + "Copy" as contained in a Cut/Copy/Paste menu + + + Colar + "Paste" as contained in a Cut/Copy/Paste menu + + + Selecionar Tudo + "Select All" as contained in a Cut/Copy/Paste menu + + \ No newline at end of file diff --git a/src/cascadia/UIHelpers/Resources/qps-ploc/Resources.resw b/src/cascadia/UIHelpers/Resources/qps-ploc/Resources.resw new file mode 100644 index 0000000000..438b4260d9 --- /dev/null +++ b/src/cascadia/UIHelpers/Resources/qps-ploc/Resources.resw @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Ĉύţ + "Cut" as contained in a Cut/Copy/Paste menu + + + Çбφÿ ! + "Copy" as contained in a Cut/Copy/Paste menu + + + Рåşţέ ! + "Paste" as contained in a Cut/Copy/Paste menu + + + Śėľєċŧ Áľľ !!! + "Select All" as contained in a Cut/Copy/Paste menu + + \ No newline at end of file diff --git a/src/cascadia/UIHelpers/Resources/qps-ploca/Resources.resw b/src/cascadia/UIHelpers/Resources/qps-ploca/Resources.resw new file mode 100644 index 0000000000..438b4260d9 --- /dev/null +++ b/src/cascadia/UIHelpers/Resources/qps-ploca/Resources.resw @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Ĉύţ + "Cut" as contained in a Cut/Copy/Paste menu + + + Çбφÿ ! + "Copy" as contained in a Cut/Copy/Paste menu + + + Рåşţέ ! + "Paste" as contained in a Cut/Copy/Paste menu + + + Śėľєċŧ Áľľ !!! + "Select All" as contained in a Cut/Copy/Paste menu + + \ No newline at end of file diff --git a/src/cascadia/UIHelpers/Resources/qps-plocm/Resources.resw b/src/cascadia/UIHelpers/Resources/qps-plocm/Resources.resw new file mode 100644 index 0000000000..438b4260d9 --- /dev/null +++ b/src/cascadia/UIHelpers/Resources/qps-plocm/Resources.resw @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Ĉύţ + "Cut" as contained in a Cut/Copy/Paste menu + + + Çбφÿ ! + "Copy" as contained in a Cut/Copy/Paste menu + + + Рåşţέ ! + "Paste" as contained in a Cut/Copy/Paste menu + + + Śėľєċŧ Áľľ !!! + "Select All" as contained in a Cut/Copy/Paste menu + + \ No newline at end of file diff --git a/src/cascadia/UIHelpers/Resources/ru-RU/Resources.resw b/src/cascadia/UIHelpers/Resources/ru-RU/Resources.resw new file mode 100644 index 0000000000..3be08c4670 --- /dev/null +++ b/src/cascadia/UIHelpers/Resources/ru-RU/Resources.resw @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Вырезать + "Cut" as contained in a Cut/Copy/Paste menu + + + Копировать + "Copy" as contained in a Cut/Copy/Paste menu + + + Вставить + "Paste" as contained in a Cut/Copy/Paste menu + + + Выделить все + "Select All" as contained in a Cut/Copy/Paste menu + + \ No newline at end of file diff --git a/src/cascadia/UIHelpers/Resources/zh-CN/Resources.resw b/src/cascadia/UIHelpers/Resources/zh-CN/Resources.resw new file mode 100644 index 0000000000..4f8bffd80d --- /dev/null +++ b/src/cascadia/UIHelpers/Resources/zh-CN/Resources.resw @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 剪切 + "Cut" as contained in a Cut/Copy/Paste menu + + + 复制 + "Copy" as contained in a Cut/Copy/Paste menu + + + 粘贴 + "Paste" as contained in a Cut/Copy/Paste menu + + + 全选 + "Select All" as contained in a Cut/Copy/Paste menu + + \ No newline at end of file diff --git a/src/cascadia/UIHelpers/Resources/zh-TW/Resources.resw b/src/cascadia/UIHelpers/Resources/zh-TW/Resources.resw new file mode 100644 index 0000000000..ff8daf42f8 --- /dev/null +++ b/src/cascadia/UIHelpers/Resources/zh-TW/Resources.resw @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 剪下 + "Cut" as contained in a Cut/Copy/Paste menu + + + 複製 + "Copy" as contained in a Cut/Copy/Paste menu + + + 貼上 + "Paste" as contained in a Cut/Copy/Paste menu + + + 全選 + "Select All" as contained in a Cut/Copy/Paste menu + + \ No newline at end of file diff --git a/src/cascadia/UIHelpers/TextMenuFlyout.cpp b/src/cascadia/UIHelpers/TextMenuFlyout.cpp new file mode 100644 index 0000000000..b5cb1aaf5e --- /dev/null +++ b/src/cascadia/UIHelpers/TextMenuFlyout.cpp @@ -0,0 +1,206 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "TextMenuFlyout.h" + +#include + +#include "TextMenuFlyout.g.cpp" + +using namespace ::winrt::Windows::UI::Xaml; +using namespace ::winrt::Windows::UI::Xaml::Controls; +using namespace ::winrt::Windows::ApplicationModel::Resources::Core; +using namespace ::winrt::Microsoft::UI::Xaml::Controls; +using namespace ::winrt::Windows::System; +using namespace ::winrt::Windows::UI::Xaml::Input; + +using MenuFlyoutItemClick = void (*)(IInspectable const&, RoutedEventArgs const&); + +constexpr auto NullSymbol = static_cast(0); + +namespace winrt::Microsoft::Terminal::UI::implementation +{ +#pragma warning(suppress : 26455) // Default constructor should not throw. Declare it 'noexcept' (f.6). + TextMenuFlyout::TextMenuFlyout() + { + // Most of the initialization is delayed until the first call to MenuFlyout_Opening. + Opening({ this, &TextMenuFlyout::MenuFlyout_Opening }); + } + + void TextMenuFlyout::MenuFlyout_Opening(IInspectable const&, IInspectable const&) + { + auto target = Target(); + if (!target) + { + return; + } + hstring selection; + bool writable = false; + + // > Common group of selectable controls with common actions + // > The I in MIDL stands for... + // No common interface. + if (const auto box = target.try_as()) + { + // Accessing template children from outside the class is unspecified; GetTemplateChild is + // a protected member. It does work, though. + target = box.as().GetTemplateChild(L"InputBox").as(); + } + if (const auto control = target.try_as()) + { + selection = control.SelectedText(); + } + else if (const auto control = target.try_as()) + { + selection = control.SelectedText(); + writable = true; + } + else if (const auto control = target.try_as()) + { + selection = control.SelectedText(); + } + + if (!_copy) + { + til::small_vector items; + + if (writable) + { + _cut = items.emplace_back(_createMenuItem(Symbol::Cut, RS_(L"Cut"), { this, &TextMenuFlyout::Cut_Click }, VirtualKeyModifiers::Control, VirtualKey::X)); + } + _copy = items.emplace_back(_createMenuItem(Symbol::Copy, RS_(L"Copy"), { this, &TextMenuFlyout::Copy_Click }, VirtualKeyModifiers::Control, VirtualKey::C)); + if (writable) + { + items.emplace_back(_createMenuItem(Symbol::Paste, RS_(L"Paste"), { this, &TextMenuFlyout::Paste_Click }, VirtualKeyModifiers::Control, VirtualKey::V)); + } + items.emplace_back(_createMenuItem(NullSymbol, RS_(L"SelectAll"), { this, &TextMenuFlyout::SelectAll_Click }, VirtualKeyModifiers::Control, VirtualKey::A)); + + Items().ReplaceAll({ items.data(), gsl::narrow_cast(items.size()) }); + } + + const auto visibilityOfItemsRequiringSelection = selection.empty() ? Visibility::Collapsed : Visibility::Visible; + if (_cut) + { + _cut.Visibility(visibilityOfItemsRequiringSelection); + } + _copy.Visibility(visibilityOfItemsRequiringSelection); + } + + void TextMenuFlyout::Cut_Click(IInspectable const&, RoutedEventArgs const&) + { + // NOTE: When the flyout closes, WinUI doesn't disconnect the accelerator keys. + // Since that means we'll get Ctrl+X/C/V callbacks forever, just ignore them. + // The TextBox will still handle those events... + auto target = Target(); + if (!target) + { + return; + } + + if (const auto box = target.try_as()) + { + target = box.as().GetTemplateChild(L"InputBox").as(); + } + if (const auto control = target.try_as()) + { + control.CutSelectionToClipboard(); + } + } + + void TextMenuFlyout::Copy_Click(IInspectable const&, RoutedEventArgs const&) + { + auto target = Target(); + if (!target) + { + return; + } + + if (const auto box = target.try_as()) + { + target = box.as().GetTemplateChild(L"InputBox").as(); + } + if (const auto control = target.try_as()) + { + control.CopySelectionToClipboard(); + } + else if (const auto control = target.try_as()) + { + control.CopySelectionToClipboard(); + } + else if (const auto control = target.try_as()) + { + control.CopySelectionToClipboard(); + } + } + + void TextMenuFlyout::Paste_Click(IInspectable const&, RoutedEventArgs const&) + { + auto target = Target(); + if (!target) + { + return; + } + + if (const auto box = target.try_as()) + { + target = box.as().GetTemplateChild(L"InputBox").as(); + } + if (const auto control = target.try_as()) + { + control.PasteFromClipboard(); + } + } + + void TextMenuFlyout::SelectAll_Click(IInspectable const&, RoutedEventArgs const&) + { + // BODGY: + // Once the flyout was open once, we'll get Ctrl+A events and the TextBox will + // ignore them. As such, we have to dig out the focused element as a fallback, + // because otherwise Ctrl+A will be permanently broken. Put differently, + // this is bodgy because WinUI 2.8 is buggy. There's no other solution here. + IInspectable target = Target(); + if (!target) + { + target = FocusManager::GetFocusedElement(XamlRoot()); + if (!target) + { + return; + } + } + + if (const auto box = target.try_as()) + { + target = box.as().GetTemplateChild(L"InputBox").as(); + } + if (const auto control = target.try_as()) + { + control.SelectAll(); + } + else if (const auto control = target.try_as()) + { + control.SelectAll(); + } + else if (const auto control = target.try_as()) + { + control.SelectAll(); + } + } + + MenuFlyoutItemBase TextMenuFlyout::_createMenuItem(Symbol symbol, hstring text, RoutedEventHandler click, VirtualKeyModifiers modifiers, VirtualKey key) + { + KeyboardAccelerator accel; + accel.Modifiers(modifiers); + accel.Key(key); + + MenuFlyoutItem item; + if (symbol != NullSymbol) + { + item.Icon(SymbolIcon{ std::move(symbol) }); + } + item.Text(std::move(text)); + item.Click(std::move(click)); + item.KeyboardAccelerators().Append(std::move(accel)); + return item; + } +} diff --git a/src/cascadia/UIHelpers/TextMenuFlyout.h b/src/cascadia/UIHelpers/TextMenuFlyout.h new file mode 100644 index 0000000000..87a6d9a3f9 --- /dev/null +++ b/src/cascadia/UIHelpers/TextMenuFlyout.h @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "TextMenuFlyout.g.h" + +namespace winrt::Microsoft::Terminal::UI::implementation +{ + // This custom flyout exists because WinUI 2 only supports 1 text block flyout + // *per thread* not per window. If you have >1 window per 1 thread, as we do, + // the focus will just be delegated to the window the flyout was first opened in. + // Once the first window is gone, WinUI will either do nothing or crash. + // See: GH#18599 + struct TextMenuFlyout : TextMenuFlyoutT + { + TextMenuFlyout(); + + void MenuFlyout_Opening(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& e); + void Cut_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e); + void Copy_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e); + void Paste_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e); + void SelectAll_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e); + + private: + winrt::Windows::UI::Xaml::Controls::MenuFlyoutItemBase _createMenuItem( + winrt::Windows::UI::Xaml::Controls::Symbol symbol, + winrt::hstring text, + winrt::Windows::UI::Xaml::RoutedEventHandler click, + winrt::Windows::System::VirtualKeyModifiers modifiers, + winrt::Windows::System::VirtualKey key); + + // These are always present. + winrt::Windows::UI::Xaml::Controls::MenuFlyoutItemBase _copy{ nullptr }; + // These are only set for writable controls. + winrt::Windows::UI::Xaml::Controls::MenuFlyoutItemBase _cut{ nullptr }; + }; +} + +namespace winrt::Microsoft::Terminal::UI::factory_implementation +{ + BASIC_FACTORY(TextMenuFlyout); +} diff --git a/src/cascadia/UIHelpers/TextMenuFlyout.idl b/src/cascadia/UIHelpers/TextMenuFlyout.idl new file mode 100644 index 0000000000..c43234db29 --- /dev/null +++ b/src/cascadia/UIHelpers/TextMenuFlyout.idl @@ -0,0 +1,7 @@ +namespace Microsoft.Terminal.UI +{ + [default_interface] runtimeclass TextMenuFlyout : Windows.UI.Xaml.Controls.MenuFlyout + { + TextMenuFlyout(); + } +} diff --git a/src/cascadia/UIHelpers/UIHelpers.vcxproj b/src/cascadia/UIHelpers/UIHelpers.vcxproj index 033c511413..61a82db255 100644 --- a/src/cascadia/UIHelpers/UIHelpers.vcxproj +++ b/src/cascadia/UIHelpers/UIHelpers.vcxproj @@ -7,6 +7,7 @@ DynamicLibrary Console true + Windows Terminal UI Support Library true @@ -26,6 +27,9 @@ ResourceString.idl + + TextMenuFlyout.idl + @@ -41,6 +45,9 @@ ResourceString.idl + + TextMenuFlyout.idl + @@ -48,6 +55,13 @@ + + + + + Designer + + @@ -64,15 +78,12 @@ false - %(AdditionalDependencies);user32.lib;shell32.lib - - diff --git a/src/cascadia/UIHelpers/UIHelpers.vcxproj.filters b/src/cascadia/UIHelpers/UIHelpers.vcxproj.filters index 43bbd04bf0..637a4b0e1a 100644 --- a/src/cascadia/UIHelpers/UIHelpers.vcxproj.filters +++ b/src/cascadia/UIHelpers/UIHelpers.vcxproj.filters @@ -31,8 +31,18 @@ + + + + + + + + + Resources\en-US + \ No newline at end of file diff --git a/src/cascadia/UIHelpers/init.cpp b/src/cascadia/UIHelpers/init.cpp index 689f4b67f6..6a22285bac 100644 --- a/src/cascadia/UIHelpers/init.cpp +++ b/src/cascadia/UIHelpers/init.cpp @@ -3,6 +3,8 @@ #include "pch.h" +#include + #pragma warning(suppress : 26440) // Not interested in changing the specification of DllMain to make it noexcept given it's an interface to the OS. BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID /*reserved*/) { @@ -11,11 +13,11 @@ BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID /*reserved*/) case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(hInstDll); break; - case DLL_PROCESS_DETACH: - break; default: break; } return TRUE; } + +UTILS_DEFINE_LIBRARY_RESOURCE_SCOPE(L"Microsoft.Terminal.UI/Resources") diff --git a/src/cascadia/UIHelpers/pch.h b/src/cascadia/UIHelpers/pch.h index 632428fe4e..5d22e5e502 100644 --- a/src/cascadia/UIHelpers/pch.h +++ b/src/cascadia/UIHelpers/pch.h @@ -25,16 +25,15 @@ #include -#include -#include +#include +#include #include #include -#include #include #include -#include +#include #include #include diff --git a/src/cascadia/UIMarkdown/CodeBlock.xaml b/src/cascadia/UIMarkdown/CodeBlock.xaml index 009c965458..38fea69ab8 100644 --- a/src/cascadia/UIMarkdown/CodeBlock.xaml +++ b/src/cascadia/UIMarkdown/CodeBlock.xaml @@ -8,6 +8,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="using:Microsoft.Terminal.UI.Markdown" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:mtu="using:Microsoft.Terminal.UI" xmlns:muxc="using:Microsoft.UI.Xaml.Controls" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" @@ -201,7 +202,11 @@ VerticalAlignment="Stretch"> + Text="{x:Bind Commandlines}"> + + + + diff --git a/src/cascadia/UIMarkdown/MarkdownToXaml.cpp b/src/cascadia/UIMarkdown/MarkdownToXaml.cpp index 81e70155f2..eb515898b2 100644 --- a/src/cascadia/UIMarkdown/MarkdownToXaml.cpp +++ b/src/cascadia/UIMarkdown/MarkdownToXaml.cpp @@ -72,6 +72,7 @@ WUX::Controls::RichTextBlock MarkdownToXaml::Convert(std::string_view markdownTe MarkdownToXaml::MarkdownToXaml(const winrt::hstring& baseUrl) : _baseUri{ baseUrl } { + _root.ContextFlyout(winrt::Microsoft::Terminal::UI::TextMenuFlyout{}); _root.IsTextSelectionEnabled(true); _root.TextWrapping(WUX::TextWrapping::WrapWholeWords); } @@ -155,6 +156,7 @@ void MarkdownToXaml::_EndParagraph() noexcept WUX::Controls::TextBlock MarkdownToXaml::_makeDefaultTextBlock() { WUX::Controls::TextBlock b{}; + b.ContextFlyout(winrt::Microsoft::Terminal::UI::TextMenuFlyout{}); b.IsTextSelectionEnabled(true); b.TextWrapping(WUX::TextWrapping::WrapWholeWords); return b; diff --git a/src/cascadia/UIMarkdown/UIMarkdown.vcxproj b/src/cascadia/UIMarkdown/UIMarkdown.vcxproj index 66d61d3ad7..f347b49979 100644 --- a/src/cascadia/UIMarkdown/UIMarkdown.vcxproj +++ b/src/cascadia/UIMarkdown/UIMarkdown.vcxproj @@ -7,7 +7,6 @@ DynamicLibrary Console true - 4 nested - + Windows Terminal Markdown Control Library true @@ -78,10 +77,11 @@ {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE} false + + {6515f03f-e56d-4db4-b23d-ac4fb80db36f} + - - - + \ No newline at end of file diff --git a/src/cascadia/UIMarkdown/UIMarkdown.vcxproj.filters b/src/cascadia/UIMarkdown/UIMarkdown.vcxproj.filters index ccbde7f1f8..89ab02dcde 100644 --- a/src/cascadia/UIMarkdown/UIMarkdown.vcxproj.filters +++ b/src/cascadia/UIMarkdown/UIMarkdown.vcxproj.filters @@ -25,14 +25,20 @@ Module + + + - + + + + \ No newline at end of file diff --git a/src/cascadia/UIMarkdown/pch.h b/src/cascadia/UIMarkdown/pch.h index aa6b7fa485..562900ebb9 100644 --- a/src/cascadia/UIMarkdown/pch.h +++ b/src/cascadia/UIMarkdown/pch.h @@ -25,21 +25,19 @@ #include -#include #include #include #include -#include -#include #include #include #include #include -#include #include +#include + // Manually include til after we include Windows.Foundation to give it winrt superpowers #include "til.h" diff --git a/src/cascadia/UnitTests_Control/ControlCoreTests.cpp b/src/cascadia/UnitTests_Control/ControlCoreTests.cpp index 999809cb39..5b242b8235 100644 --- a/src/cascadia/UnitTests_Control/ControlCoreTests.cpp +++ b/src/cascadia/UnitTests_Control/ControlCoreTests.cpp @@ -152,14 +152,14 @@ namespace ControlUnitTests if (expectedOpacity < 1.0f) { VERIFY_IS_TRUE(settings->UseAcrylic()); - VERIFY_IS_TRUE(core->_settings->UseAcrylic()); + VERIFY_IS_TRUE(core->_settings.UseAcrylic()); } // GH#603: Adjusting opacity shouldn't change whether or not we // requested acrylic. auto expectedUseAcrylic = expectedOpacity < 1.0f; - VERIFY_IS_TRUE(core->_settings->UseAcrylic()); + VERIFY_IS_TRUE(core->_settings.UseAcrylic()); VERIFY_ARE_EQUAL(expectedUseAcrylic, core->UseAcrylic()); }; core->TransparencyChanged(opacityCallback); @@ -248,7 +248,7 @@ namespace ControlUnitTests _standardInit(core); Log::Comment(L"Print 40 rows of 'Foo', and a single row of 'Bar' " - L"(leaving the cursor afer 'Bar')"); + L"(leaving the cursor after 'Bar')"); for (auto i = 0; i < 40; ++i) { conn->WriteInput(winrt_wstring_to_array_view(L"Foo\r\n")); @@ -285,7 +285,7 @@ namespace ControlUnitTests _standardInit(core); Log::Comment(L"Print 40 rows of 'Foo', and a single row of 'Bar' " - L"(leaving the cursor afer 'Bar')"); + L"(leaving the cursor after 'Bar')"); for (auto i = 0; i < 40; ++i) { conn->WriteInput(winrt_wstring_to_array_view(L"Foo\r\n")); @@ -304,9 +304,9 @@ namespace ControlUnitTests Log::Comment(L"Check the buffer after the clear"); VERIFY_ARE_EQUAL(20, core->_terminal->GetViewport().Height()); - VERIFY_ARE_EQUAL(41, core->ScrollOffset()); + VERIFY_ARE_EQUAL(21, core->ScrollOffset()); VERIFY_ARE_EQUAL(20, core->ViewHeight()); - VERIFY_ARE_EQUAL(61, core->BufferHeight()); + VERIFY_ARE_EQUAL(41, core->BufferHeight()); // In this test, we can't actually check if we cleared the buffer // contents. ConPTY will handle the actual clearing of the buffer @@ -322,7 +322,7 @@ namespace ControlUnitTests _standardInit(core); Log::Comment(L"Print 40 rows of 'Foo', and a single row of 'Bar' " - L"(leaving the cursor afer 'Bar')"); + L"(leaving the cursor after 'Bar')"); for (auto i = 0; i < 40; ++i) { conn->WriteInput(winrt_wstring_to_array_view(L"Foo\r\n")); diff --git a/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp b/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp index 282b1995da..5d886064a6 100644 --- a/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp +++ b/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp @@ -170,7 +170,7 @@ namespace ControlUnitTests // The mouse location and buttons don't matter here. interactivity->MouseWheel(modifiers, - 30, + Core::Point{ 0, 30 }, Core::Point{ 0, 0 }, buttonState); } @@ -188,7 +188,7 @@ namespace ControlUnitTests // The mouse location and buttons don't matter here. interactivity->MouseWheel(modifiers, - -30, + Core::Point{ 0, -30 }, Core::Point{ 0, 0 }, buttonState); } @@ -245,7 +245,7 @@ namespace ControlUnitTests expectedTop = 20; interactivity->MouseWheel(modifiers, - WHEEL_DELTA, + Core::Point{ 0, WHEEL_DELTA }, Core::Point{ 0, 0 }, buttonState); @@ -254,18 +254,18 @@ namespace ControlUnitTests { expectedTop--; interactivity->MouseWheel(modifiers, - WHEEL_DELTA, + Core::Point{ 0, WHEEL_DELTA }, Core::Point{ 0, 0 }, buttonState); } Log::Comment(L"Scrolling up more should do nothing"); expectedTop = 0; interactivity->MouseWheel(modifiers, - WHEEL_DELTA, + Core::Point{ 0, WHEEL_DELTA }, Core::Point{ 0, 0 }, buttonState); interactivity->MouseWheel(modifiers, - WHEEL_DELTA, + Core::Point{ 0, WHEEL_DELTA }, Core::Point{ 0, 0 }, buttonState); @@ -275,7 +275,7 @@ namespace ControlUnitTests Log::Comment(NoThrowString().Format(L"---scroll down #%d---", i)); expectedTop++; interactivity->MouseWheel(modifiers, - -WHEEL_DELTA, + Core::Point{ 0, -WHEEL_DELTA }, Core::Point{ 0, 0 }, buttonState); Log::Comment(NoThrowString().Format(L"internal scrollbar pos:%f", interactivity->_internalScrollbarPosition)); @@ -283,11 +283,11 @@ namespace ControlUnitTests Log::Comment(L"Scrolling down more should do nothing"); expectedTop = 21; interactivity->MouseWheel(modifiers, - -WHEEL_DELTA, + Core::Point{ 0, -WHEEL_DELTA }, Core::Point{ 0, 0 }, buttonState); interactivity->MouseWheel(modifiers, - -WHEEL_DELTA, + Core::Point{ 0, -WHEEL_DELTA }, Core::Point{ 0, 0 }, buttonState); } @@ -444,7 +444,7 @@ namespace ControlUnitTests Log::Comment(L"Scroll up a line, with the left mouse button selected"); interactivity->MouseWheel(modifiers, - WHEEL_DELTA, + Core::Point{ 0, WHEEL_DELTA }, cursorPosition1.to_core_point(), leftMouseDown); @@ -492,55 +492,55 @@ namespace ControlUnitTests const Core::Point mousePos{ 0, 0 }; Control::MouseButtonState state{}; - interactivity->MouseWheel(modifiers, delta, mousePos, state); // 1/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 1/5 VERIFY_ARE_EQUAL(21, core->ScrollOffset()); Log::Comment(L"Scroll up 4 more times. Once we're at 3/5 scrolls, " L"we'll round the internal scrollbar position to scrolling to the next row."); - interactivity->MouseWheel(modifiers, delta, mousePos, state); // 2/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 2/5 VERIFY_ARE_EQUAL(21, core->ScrollOffset()); - interactivity->MouseWheel(modifiers, delta, mousePos, state); // 3/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 3/5 VERIFY_ARE_EQUAL(20, core->ScrollOffset()); - interactivity->MouseWheel(modifiers, delta, mousePos, state); // 4/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 4/5 VERIFY_ARE_EQUAL(20, core->ScrollOffset()); - interactivity->MouseWheel(modifiers, delta, mousePos, state); // 5/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 5/5 VERIFY_ARE_EQUAL(20, core->ScrollOffset()); Log::Comment(L"Jump to line 5, so we can scroll down from there."); interactivity->UpdateScrollbar(5); VERIFY_ARE_EQUAL(5, core->ScrollOffset()); Log::Comment(L"Scroll down 5 times, at which point we should accumulate a whole row of delta."); - interactivity->MouseWheel(modifiers, -delta, mousePos, state); // 1/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, -delta }, mousePos, state); // 1/5 VERIFY_ARE_EQUAL(5, core->ScrollOffset()); - interactivity->MouseWheel(modifiers, -delta, mousePos, state); // 2/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, -delta }, mousePos, state); // 2/5 VERIFY_ARE_EQUAL(5, core->ScrollOffset()); - interactivity->MouseWheel(modifiers, -delta, mousePos, state); // 3/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, -delta }, mousePos, state); // 3/5 VERIFY_ARE_EQUAL(6, core->ScrollOffset()); - interactivity->MouseWheel(modifiers, -delta, mousePos, state); // 4/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, -delta }, mousePos, state); // 4/5 VERIFY_ARE_EQUAL(6, core->ScrollOffset()); - interactivity->MouseWheel(modifiers, -delta, mousePos, state); // 5/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, -delta }, mousePos, state); // 5/5 VERIFY_ARE_EQUAL(6, core->ScrollOffset()); Log::Comment(L"Jump to the bottom."); interactivity->UpdateScrollbar(21); VERIFY_ARE_EQUAL(21, core->ScrollOffset()); Log::Comment(L"Scroll a bit, then emit a line of text. We should reset our internal scroll position."); - interactivity->MouseWheel(modifiers, delta, mousePos, state); // 1/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 1/5 VERIFY_ARE_EQUAL(21, core->ScrollOffset()); - interactivity->MouseWheel(modifiers, delta, mousePos, state); // 2/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 2/5 VERIFY_ARE_EQUAL(21, core->ScrollOffset()); conn->WriteInput(winrt_wstring_to_array_view(L"Foo\r\n")); VERIFY_ARE_EQUAL(22, core->ScrollOffset()); - interactivity->MouseWheel(modifiers, delta, mousePos, state); // 1/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 1/5 VERIFY_ARE_EQUAL(22, core->ScrollOffset()); - interactivity->MouseWheel(modifiers, delta, mousePos, state); // 2/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 2/5 VERIFY_ARE_EQUAL(22, core->ScrollOffset()); - interactivity->MouseWheel(modifiers, delta, mousePos, state); // 3/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 3/5 VERIFY_ARE_EQUAL(21, core->ScrollOffset()); - interactivity->MouseWheel(modifiers, delta, mousePos, state); // 4/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 4/5 VERIFY_ARE_EQUAL(21, core->ScrollOffset()); - interactivity->MouseWheel(modifiers, delta, mousePos, state); // 5/5 + interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 5/5 VERIFY_ARE_EQUAL(21, core->ScrollOffset()); } @@ -709,7 +709,7 @@ namespace ControlUnitTests { expectedTop--; interactivity->MouseWheel(modifiers, - WHEEL_DELTA, + Core::Point{ 0, WHEEL_DELTA }, Core::Point{ 0, 0 }, noMouseDown); } diff --git a/src/cascadia/UnitTests_Control/MockControlSettings.h b/src/cascadia/UnitTests_Control/MockControlSettings.h index 50b71687e8..70785b04e2 100644 --- a/src/cascadia/UnitTests_Control/MockControlSettings.h +++ b/src/cascadia/UnitTests_Control/MockControlSettings.h @@ -13,7 +13,7 @@ using IFontAxesMap = winrt::Windows::Foundation::Collections::IMap + class MockControlSettings : public winrt::implements { // Color Table is special because it's an array std::array _ColorTable; @@ -28,14 +28,14 @@ namespace ControlUnitTests public: MockControlSettings() = default; - winrt::Microsoft::Terminal::Core::Color GetColorTableEntry(int32_t index) noexcept + void SetColorTable(const std::array& colors) { - return _ColorTable.at(index); + _ColorTable = colors; } - void SetColorTableEntry(int32_t index, - winrt::Microsoft::Terminal::Core::Color color) noexcept + + void GetColorTable(winrt::com_array& table) noexcept { - _ColorTable.at(index) = color; + table = winrt::com_array(_ColorTable.begin(), _ColorTable.end()); } }; } diff --git a/src/cascadia/UnitTests_SettingsModel/ColorSchemeTests.cpp b/src/cascadia/UnitTests_SettingsModel/ColorSchemeTests.cpp index b219816067..593ded5d43 100644 --- a/src/cascadia/UnitTests_SettingsModel/ColorSchemeTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/ColorSchemeTests.cpp @@ -869,7 +869,7 @@ namespace SettingsModelUnitTests SettingsLoader loader{ userSettings, inboxSettings }; loader.MergeInboxIntoUserSettings(); - loader.MergeFragmentIntoUserSettings(L"TestFragment", fragment); + loader.MergeFragmentIntoUserSettings(L"TestFragment", {}, fragment); loader.FinalizeLayering(); loader.FixupUserSettings(); const auto settings = winrt::make_self(std::move(loader)); diff --git a/src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp b/src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp index ce674c5fe6..c253a17493 100644 --- a/src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/DeserializationTests.cpp @@ -36,8 +36,6 @@ namespace SettingsModelUnitTests TEST_METHOD(TestHideAllProfiles); TEST_METHOD(TestInvalidColorSchemeName); TEST_METHOD(TestHelperFunctions); - TEST_METHOD(TestProfileBackgroundImageWithEnvVar); - TEST_METHOD(TestProfileBackgroundImageWithDesktopWallpaper); TEST_METHOD(TestCloseOnExitParsing); TEST_METHOD(TestCloseOnExitCompatibilityShim); TEST_METHOD(TestLayerUserDefaultsBeforeProfiles); @@ -899,44 +897,6 @@ namespace SettingsModelUnitTests VERIFY_IS_NULL(settings->FindProfile(fakeGuid)); } - void DeserializationTests::TestProfileBackgroundImageWithEnvVar() - { - const auto expectedPath = wil::ExpandEnvironmentStringsW(L"%WINDIR%\\System32\\x_80.png"); - - static constexpr std::string_view settingsJson{ R"( - { - "profiles": [ - { - "name": "profile0", - "backgroundImage": "%WINDIR%\\System32\\x_80.png" - } - ] - })" }; - - const auto settings = createSettings(settingsJson); - VERIFY_ARE_NOT_EQUAL(0u, settings->AllProfiles().Size()); - VERIFY_ARE_EQUAL(expectedPath, settings->AllProfiles().GetAt(0).DefaultAppearance().ExpandedBackgroundImagePath()); - } - - void DeserializationTests::TestProfileBackgroundImageWithDesktopWallpaper() - { - const winrt::hstring expectedBackgroundImagePath{ L"desktopWallpaper" }; - - static constexpr std::string_view settingsJson{ R"( - { - "profiles": [ - { - "name": "profile0", - "backgroundImage": "desktopWallpaper" - } - ] - })" }; - - const auto settings = createSettings(settingsJson); - VERIFY_ARE_EQUAL(expectedBackgroundImagePath, settings->AllProfiles().GetAt(0).DefaultAppearance().BackgroundImagePath()); - VERIFY_ARE_NOT_EQUAL(expectedBackgroundImagePath, settings->AllProfiles().GetAt(0).DefaultAppearance().ExpandedBackgroundImagePath()); - } - void DeserializationTests::TestCloseOnExitParsing() { static constexpr std::string_view settingsJson{ R"( @@ -1828,16 +1788,18 @@ namespace SettingsModelUnitTests "profiles": { "defaults": { - "name": "PROFILE DEFAULTS" + "tabTitle": "PROFILE DEFAULTS TAB TITLE" }, "list": [ { "guid": "{61c54bbd-1111-5271-96e7-009a87ff44bf}", - "name": "CMD" + "name": "CMD", + "tabTitle": "CMD Tab Title" }, { "guid": "{61c54bbd-2222-5271-96e7-009a87ff44bf}", - "name": "PowerShell" + "name": "PowerShell", + "tabTitle": "PowerShell Tab Title" }, { "guid": "{61c54bbd-3333-5271-96e7-009a87ff44bf}" @@ -1856,25 +1818,30 @@ namespace SettingsModelUnitTests // test profiles VERIFY_ARE_EQUAL(settings->AllProfiles().Size(), copyImpl->AllProfiles().Size()); VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(0).Name(), copyImpl->AllProfiles().GetAt(0).Name()); + VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(0).TabTitle(), copyImpl->AllProfiles().GetAt(0).TabTitle()); VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(1).Name(), copyImpl->AllProfiles().GetAt(1).Name()); + VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(1).TabTitle(), copyImpl->AllProfiles().GetAt(1).TabTitle()); VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(2).Name(), copyImpl->AllProfiles().GetAt(2).Name()); - VERIFY_ARE_EQUAL(settings->ProfileDefaults().Name(), copyImpl->ProfileDefaults().Name()); + VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(2).TabTitle(), copyImpl->AllProfiles().GetAt(2).TabTitle()); + VERIFY_ARE_EQUAL(settings->ProfileDefaults().TabTitle(), copyImpl->ProfileDefaults().TabTitle()); // Modifying profile.defaults should... - VERIFY_ARE_EQUAL(settings->ProfileDefaults().HasName(), copyImpl->ProfileDefaults().HasName()); - copyImpl->ProfileDefaults().Name(L"changed value"); + VERIFY_ARE_EQUAL(settings->ProfileDefaults().HasTabTitle(), copyImpl->ProfileDefaults().HasTabTitle()); + copyImpl->ProfileDefaults().TabTitle(L"changed value"); - // ...keep the same name for the first two profiles + // ...keep the same name and tab title for the first two profiles VERIFY_ARE_EQUAL(settings->AllProfiles().Size(), copyImpl->AllProfiles().Size()); VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(0).Name(), copyImpl->AllProfiles().GetAt(0).Name()); + VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(0).TabTitle(), copyImpl->AllProfiles().GetAt(0).TabTitle()); VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(1).Name(), copyImpl->AllProfiles().GetAt(1).Name()); + VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(1).TabTitle(), copyImpl->AllProfiles().GetAt(1).TabTitle()); // ...but change the name for the one that inherited it from profile.defaults - VERIFY_ARE_NOT_EQUAL(settings->AllProfiles().GetAt(2).Name(), copyImpl->AllProfiles().GetAt(2).Name()); + VERIFY_ARE_NOT_EQUAL(settings->AllProfiles().GetAt(2).TabTitle(), copyImpl->AllProfiles().GetAt(2).TabTitle()); // profile.defaults should be different between the two graphs - VERIFY_ARE_EQUAL(settings->ProfileDefaults().HasName(), copyImpl->ProfileDefaults().HasName()); - VERIFY_ARE_NOT_EQUAL(settings->ProfileDefaults().Name(), copyImpl->ProfileDefaults().Name()); + VERIFY_ARE_EQUAL(settings->ProfileDefaults().HasTabTitle(), copyImpl->ProfileDefaults().HasTabTitle()); + VERIFY_ARE_NOT_EQUAL(settings->ProfileDefaults().TabTitle(), copyImpl->ProfileDefaults().TabTitle()); Log::Comment(L"Test empty profiles.defaults"); static constexpr std::string_view emptyPDJson{ R"( @@ -2131,7 +2098,7 @@ namespace SettingsModelUnitTests implementation::SettingsLoader loader{ std::string_view{}, implementation::LoadStringResource(IDR_DEFAULTS) }; loader.MergeInboxIntoUserSettings(); - loader.MergeFragmentIntoUserSettings(winrt::hstring{ fragmentSource }, fragmentJson); + loader.MergeFragmentIntoUserSettings(winrt::hstring{ fragmentSource }, {}, fragmentJson); loader.FinalizeLayering(); VERIFY_IS_FALSE(loader.duplicateProfile); @@ -2155,7 +2122,7 @@ namespace SettingsModelUnitTests implementation::SettingsLoader loader{ std::string_view{}, implementation::LoadStringResource(IDR_DEFAULTS) }; loader.MergeInboxIntoUserSettings(); - loader.MergeFragmentIntoUserSettings(winrt::hstring{ fragmentSource }, fragmentJson); + loader.MergeFragmentIntoUserSettings(winrt::hstring{ fragmentSource }, {}, fragmentJson); loader.FinalizeLayering(); const auto settings = winrt::make_self(std::move(loader)); @@ -2181,7 +2148,7 @@ namespace SettingsModelUnitTests implementation::SettingsLoader loader{ std::string_view{}, implementation::LoadStringResource(IDR_DEFAULTS) }; loader.MergeInboxIntoUserSettings(); - loader.MergeFragmentIntoUserSettings(winrt::hstring{ fragmentSource }, fragmentJson); + loader.MergeFragmentIntoUserSettings(winrt::hstring{ fragmentSource }, {}, fragmentJson); loader.FinalizeLayering(); const auto settings = winrt::make_self(std::move(loader)); @@ -2215,7 +2182,7 @@ namespace SettingsModelUnitTests implementation::SettingsLoader loader{ std::string_view{}, implementation::LoadStringResource(IDR_DEFAULTS) }; loader.MergeInboxIntoUserSettings(); - loader.MergeFragmentIntoUserSettings(winrt::hstring{ fragmentSource }, fragmentJson); + loader.MergeFragmentIntoUserSettings(winrt::hstring{ fragmentSource }, {}, fragmentJson); loader.FinalizeLayering(); const auto settings = winrt::make_self(std::move(loader)); @@ -2250,7 +2217,7 @@ namespace SettingsModelUnitTests implementation::SettingsLoader loader{ std::string_view{}, implementation::LoadStringResource(IDR_DEFAULTS) }; loader.MergeInboxIntoUserSettings(); - loader.MergeFragmentIntoUserSettings(winrt::hstring{ fragmentSource }, fragmentJson); + loader.MergeFragmentIntoUserSettings(winrt::hstring{ fragmentSource }, {}, fragmentJson); loader.FinalizeLayering(); const auto settings = winrt::make_self(std::move(loader)); @@ -2276,7 +2243,7 @@ namespace SettingsModelUnitTests implementation::SettingsLoader loader{ std::string_view{}, implementation::LoadStringResource(IDR_DEFAULTS) }; loader.MergeInboxIntoUserSettings(); - loader.MergeFragmentIntoUserSettings(winrt::hstring{ fragmentSource }, fragmentJson); + loader.MergeFragmentIntoUserSettings(winrt::hstring{ fragmentSource }, {}, fragmentJson); loader.FinalizeLayering(); const auto settings = winrt::make_self(std::move(loader)); @@ -2303,7 +2270,7 @@ namespace SettingsModelUnitTests implementation::SettingsLoader loader{ std::string_view{}, implementation::LoadStringResource(IDR_DEFAULTS) }; loader.MergeInboxIntoUserSettings(); - loader.MergeFragmentIntoUserSettings(winrt::hstring{ fragmentSource }, fragmentJson); + loader.MergeFragmentIntoUserSettings(winrt::hstring{ fragmentSource }, {}, fragmentJson); loader.FinalizeLayering(); const auto oldSettings = winrt::make_self(std::move(loader)); diff --git a/src/cascadia/UnitTests_SettingsModel/MediaResourceTests.cpp b/src/cascadia/UnitTests_SettingsModel/MediaResourceTests.cpp new file mode 100644 index 0000000000..6b8be97427 --- /dev/null +++ b/src/cascadia/UnitTests_SettingsModel/MediaResourceTests.cpp @@ -0,0 +1,1346 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" + +#include "../TerminalSettingsModel/ColorScheme.h" +#include "../TerminalSettingsModel/CascadiaSettings.h" +#include "JsonTestClass.h" +#include "TestUtils.h" + +using namespace Microsoft::Console; +using namespace WEX::Logging; +using namespace WEX::TestExecution; +using namespace WEX::Common; +using namespace winrt::Microsoft::Terminal::Settings::Model; + +static std::function + g_mediaResolverHook; + +bool TestHook_CascadiaSettings_ResolveSingleMediaResource( + winrt::Microsoft::Terminal::Settings::Model::OriginTag origin, + std::wstring_view basePath, + const winrt::Microsoft::Terminal::Settings::Model::IMediaResource& resource) +{ + if (g_mediaResolverHook) + { + g_mediaResolverHook(origin, basePath, resource); + return true; + } + return false; +} + +static auto requireCalled(auto&& func) +{ + static bool called{ false }; + called = false; + return std::tuple{ + [=](auto&&... a) { + func(a...); + called = true; + }, + wil::scope_exit([] { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + VERIFY_IS_TRUE(called); + }) + }; +} + +static auto requireCalled(int times, auto&& func) +{ + static int called{ 0 }; + called = 0; + return std::tuple{ + [=](auto&&... a) { + ++called; + func(a...); + }, + wil::scope_exit([=] { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + VERIFY_ARE_EQUAL(called, times); + }) + }; +} + +namespace SettingsModelUnitTests +{ + class MediaResourceTests : public JsonTestClass + { + TEST_CLASS(MediaResourceTests); + + TEST_CLASS_SETUP(DisableFSRedirection); + TEST_CLASS_CLEANUP(RestoreFSRedirection); + + TEST_METHOD_CLEANUP(ResetMediaHook); + + // BASIC OPERATION + TEST_METHOD(ValidateResolverCalledForInbox); + TEST_METHOD(ValidateResolverCalledForInboxAndUser); + TEST_METHOD(ValidateResolverCalledForFragments); + TEST_METHOD(ValidateResolverCalledForNewTabMenuEntries); + TEST_METHOD(ValidateResolverCalledIncrementallyOnChange); + TEST_METHOD(ValidateResolverNotCalledForEmojiIcons); + + // PROFILE BEHAVIORS + TEST_METHOD(ProfileDefaultsContainsInvalidIcon); + TEST_METHOD(ProfileSpecifiesInvalidIconAndCommandline); + TEST_METHOD(ProfileSpecifiesInvalidIconAndNoCommandline); + TEST_METHOD(ProfileInheritsInvalidIconAndHasCommandline); + TEST_METHOD(ProfileInheritsInvalidIconAndHasNoCommandline); + TEST_METHOD(ProfileSpecifiesNullIcon); + TEST_METHOD(ProfileSpecifiesNullIconAndHasNoCommandline); + TEST_METHOD(ProfileOverwritesBellSound); + + // FRAGMENT BEHAVIORS + TEST_METHOD(FragmentUpdatesBaseProfile); + TEST_METHOD(FragmentActionResourcesGetResolved); + TEST_METHOD(DisabledFragmentNotResolved); + TEST_METHOD(FragmentAppearanceAndUserAppearanceInteraction); + + // REAL RESOLVER + TEST_METHOD(RealResolverFilePaths); + TEST_METHOD(RealResolverSpecialKeywords); + TEST_METHOD(RealResolverUrlCases); + + static constexpr std::wstring_view pingCommandline{ LR"(C:\Windows\System32\PING.EXE)" }; // Normalized by Profile (this is the casing that Windows stores on disk) + static constexpr std::wstring_view overrideCommandline{ LR"(C:\Windows\System32\cscript.exe)" }; + static constexpr std::wstring_view cmdCommandline{ LR"(C:\Windows\System32\cmd.exe)" }; // The default commandline for a profile + static constexpr std::wstring_view fragmentBasePath1{ LR"(C:\Windows\Media)" }; + + private: + PVOID redirectionFlag{ nullptr }; + + static constexpr std::string_view staticDefaultSettings{ R"({ + "actions": [ + { + "command": "closeWindow", + "icon": "fakeCommandIconPath", + "id": "Terminal.CloseWindow" + } + ], + "profiles": { + "list": [ + { + "backgroundImage": "imagePathFromBase", + "guid": "{862d46aa-cc9c-4e6c-b872-9cadaafcdbbe}", + "icon": "iconFromBase", + "name": "Base", + "bellSound": [ + "C:\\Windows\\Media\\Alarm01.wav", + "C:\\Windows\\Media\\Alarm02.wav" + ] + }, + { + "backgroundImage": "focusedImagePathFromBase", + "experimental.pixelShaderPath": "focusedPixelShaderPathFromBase", + "experimental.pixelShaderImagePath": "focusedPixelShaderImagePathFromBase", + "unfocusedAppearance": { + "backgroundImage": "unfocusedImagePathFromBase", + "experimental.pixelShaderPath": "unfocusedPixelShaderPathFromBase", + "experimental.pixelShaderImagePath": "unfocusedPixelShaderImagePathFromBase", + }, + "guid": "{84f3d5cc-ecd9-49a9-96be-8bced39d4290}", + "name": "BaseFullyLoaded" + }, + ] + }, + "schemes": [ + { + "background": "#0C0C0C", + "black": "#0C0C0C", + "blue": "#0037DA", + "brightBlack": "#767676", + "brightBlue": "#3B78FF", + "brightCyan": "#61D6D6", + "brightGreen": "#16C60C", + "brightPurple": "#B4009E", + "brightRed": "#E74856", + "brightWhite": "#F2F2F2", + "brightYellow": "#F9F1A5", + "cursorColor": "#FFFFFF", + "cyan": "#3A96DD", + "foreground": "#CCCCCC", + "green": "#13A10E", + "name": "Campbell", + "purple": "#881798", + "red": "#C50F1F", + "white": "#CCCCCC", + "yellow": "#C19C00" + } + ] +})" }; + + static constexpr int numberOfMediaResourcesInDefaultSettings{ 11 }; + + struct Fragment + { + std::wstring_view source; + std::wstring_view basePath; + std::string_view content; + }; + + // This is annoyingly fragile because we do not have a test hook helper to do this in SettingsLoader. + static winrt::com_ptr createSettingsWithFragments(const std::string_view& userJSON, std::initializer_list fragments) + { + implementation::SettingsLoader loader{ userJSON, staticDefaultSettings }; + const winrt::hstring baseUserSettingsPath{ LR"(C:\Windows)" }; + loader.userSettings.baseLayerProfile->SourceBasePath = baseUserSettingsPath; + loader.userSettings.globals->SourceBasePath = baseUserSettingsPath; + for (auto&& userProfile : loader.userSettings.profiles) + { + userProfile->SourceBasePath = baseUserSettingsPath; + } + + loader.MergeInboxIntoUserSettings(); + for (const auto& fragment : fragments) + { + loader.MergeFragmentIntoUserSettings(winrt::hstring{ fragment.source }, + winrt::hstring{ fragment.basePath }, + fragment.content); + } + loader.FinalizeLayering(); + loader.FixupUserSettings(); + return winrt::make_self(std::move(loader)); + } + + static winrt::com_ptr createSettings(const std::string_view& userJSON) + { + return createSettingsWithFragments(userJSON, {}); + } + }; + + bool MediaResourceTests::DisableFSRedirection() + { +#if defined(_M_IX86) + // Some of our tests use paths under system32. Just don't redirect them. + Wow64DisableWow64FsRedirection(&redirectionFlag); +#endif + return true; + } + + bool MediaResourceTests::RestoreFSRedirection() + { +#if defined(_M_IX86) + Wow64RevertWow64FsRedirection(redirectionFlag); +#endif + return true; + } + + bool MediaResourceTests::ResetMediaHook() + { + g_mediaResolverHook = nullptr; + return true; + } + +#pragma region Basic Operation + void MediaResourceTests::ValidateResolverCalledForInbox() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + auto [t, e] = requireCalled(numberOfMediaResourcesInDefaultSettings, + [&](auto&& origin, auto&&, auto&& resource) { + VERIFY_ARE_EQUAL(OriginTag::InBox, origin); + resource.Resolve(L"resolved"); + }); + g_mediaResolverHook = t; + auto settings{ createSettings(R"({})") }; + + auto profile{ settings->GetProfileByIndex(0) }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); + VERIFY_ARE_EQUAL(L"resolved", icon.Resolved()); + + auto backgroundImage{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_TRUE(backgroundImage.Ok()); + VERIFY_ARE_EQUAL(L"resolved", backgroundImage.Resolved()); + } + + void MediaResourceTests::ValidateResolverCalledForInboxAndUser() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + std::unordered_map origins; + + winrt::com_ptr settings; + { + // The icon in profiles.defaults erases the icon in the Base Profile and the one on the command; they will not be resolved + // TODO GH#19201: This should be called one fewer time because overriding the command's icon should delete it before it gets resolved + auto [t, e] = requireCalled(numberOfMediaResourcesInDefaultSettings - 1 /* base icon deleted */ + 2 /* icons specified by user */, + [&](auto&& origin, auto&& basePath, auto&& resource) { + if (origin == OriginTag::User || origin == OriginTag::ProfilesDefaults) + { + VERIFY_ARE_NOT_EQUAL(L"", basePath); + } + origins[origin]++; + resource.Resolve(L"resolved"); + }); + g_mediaResolverHook = t; + settings = createSettings(R"({ + "profiles": { + "defaults": { + "icon": "iconFromDefaults" + }, + "list": [ + { + "guid": "{2cdb0be2-f601-4f70-9a6c-3472b3257883}", + "icon": "iconFromUser", + "name": "UserProfile1" + } + ] + }, + "actions": [ + { + "command": { + "action": "sendInput", + "input": "IT CAME FROM BEYOND THE STARS" + }, + "icon": null, + "id": "Terminal.CloseWindow" + } + ], +})"); + } + + // TODO GH#19201: This should be base-2, 1, 1 (because we deleted the InBox command icon) + VERIFY_ARE_EQUAL(origins[OriginTag::InBox], numberOfMediaResourcesInDefaultSettings - 1); // Base profile icon not resolved because of profiles.defaults.icon + VERIFY_ARE_EQUAL(origins[OriginTag::ProfilesDefaults], 1); + VERIFY_ARE_EQUAL(origins[OriginTag::User], 1); + + auto profile0{ settings->GetProfileByName(L"Base") }; + auto icon0{ profile0.Icon() }; + VERIFY_IS_TRUE(icon0.Ok()); + VERIFY_ARE_NOT_EQUAL(L"iconFromBase", icon0.Path()); // the icon was overridden by defaults. + VERIFY_ARE_EQUAL(L"iconFromDefaults", icon0.Path()); // the icon was overridden by defaults. + VERIFY_ARE_EQUAL(L"resolved", icon0.Resolved()); + + auto profile1{ settings->GetProfileByName(L"UserProfile1") }; + auto icon1{ profile1.Icon() }; + VERIFY_IS_TRUE(icon1.Ok()); + VERIFY_ARE_EQUAL(L"iconFromUser", icon1.Path()); + VERIFY_ARE_EQUAL(L"resolved", icon1.Resolved()); + } + + void MediaResourceTests::ValidateResolverCalledForFragments() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + std::unordered_map origins; + + winrt::com_ptr settings; + { + auto [t, e] = requireCalled(numberOfMediaResourcesInDefaultSettings + 2 /* fragment resources */, + [&](auto&& origin, auto&& basePath, auto&& resource) { + if (origin == OriginTag::Fragment) + { + VERIFY_ARE_EQUAL(fragmentBasePath1, basePath); + } + origins[origin]++; + resource.Resolve(L"resolved"); + }); + g_mediaResolverHook = t; + settings = createSettingsWithFragments(R"({})", { Fragment{ L"fragment", fragmentBasePath1, R"( +{ + "profiles": [ + { + "guid": "{4e7c2b36-642f-4694-83f8-8a5052038a23}", + "name": "FragmentProfile", + "commandline": "not_a_real_path", + "icon": "DoesNotMatterIgnoredByMockResolver" + } + ], + "actions": [ + { + "command": { + "action": "sendInput", + "input": "SOME DAY SOMETHING'S COMING" + }, + "icon": "foo.ico", + "id": "Dustin.SendInput" + } + ], +} +)" } }); + } + + VERIFY_ARE_EQUAL(origins[OriginTag::Fragment], 2); + + auto profile{ settings->GetProfileByName(L"FragmentProfile") }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); + VERIFY_ARE_EQUAL(LR"(resolved)", icon.Resolved()); + } + + void MediaResourceTests::ValidateResolverCalledForNewTabMenuEntries() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + std::unordered_map origins; + + winrt::com_ptr settings; + { + auto [t, e] = requireCalled(numberOfMediaResourcesInDefaultSettings + 6 /* menu entry resources */, + [&](auto&& origin, auto&& basePath, auto&& resource) { + if (origin == OriginTag::User) + { + VERIFY_ARE_NOT_EQUAL(L"", basePath); + } + origins[origin]++; + resource.Resolve(L"resolved"); + }); + g_mediaResolverHook = t; + settings = createSettings(R"({ + "newTabMenu": [ + { + "icon": "menuItemIcon1", + "id": "Terminal.CloseWindow", + "type": "action" + }, + { + "icon": "menuItemIcon2", + "profile": "{862d46aa-cc9c-4e6c-b872-9cadaafcdbbe}", + "type": "profile" + }, + { + "allowEmpty": true, + "entries": [ + { + "icon": "menuItemIcon4", + "profile": "{862d46aa-cc9c-4e6c-b872-9cadaafcdbbe}", + "type": "profile" + }, + { + "allowEmpty": true, + "entries": [ + { + "icon": "menuItemIcon6", + "profile": "{862d46aa-cc9c-4e6c-b872-9cadaafcdbbe}", + "type": "profile" + }, + ], + "icon": "menuItemIcon5", + "inline": "never", + "name": "Or was it...?", + "type": "folder" + } + ], + "icon": "menuItemIcon3", + "inline": "never", + "name": "Lovecraft in Brooklyn", + "type": "folder" + } + ] +})"); + } + + VERIFY_ARE_EQUAL(origins[OriginTag::InBox], numberOfMediaResourcesInDefaultSettings); + VERIFY_ARE_EQUAL(origins[OriginTag::User], 6); + } + + void MediaResourceTests::ValidateResolverCalledIncrementallyOnChange() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + std::unordered_map origins; + + winrt::com_ptr settings; + { + // The icon in profiles.defaults erases the icon in the Base Profile; that one will NOT be resolved. + auto [t, e] = requireCalled(numberOfMediaResourcesInDefaultSettings - 1 /* base deleted */ + 2 /* user profile and defaults */, + [&](auto&& origin, auto&& basePath, auto&& resource) { + if (origin == OriginTag::User || origin == OriginTag::ProfilesDefaults) + { + VERIFY_ARE_NOT_EQUAL(L"", basePath); + } + origins[origin]++; + resource.Resolve(L"resolved"); + }); + g_mediaResolverHook = t; + settings = createSettings(R"({ + "profiles": { + "defaults": { + "icon": "iconFromDefaults" + }, + "list": [ + { + "guid": "{2cdb0be2-f601-4f70-9a6c-3472b3257883}", + "icon": "iconFromUser", + "name": "UserProfile1" + } + ] + } +})"); + } + + VERIFY_ARE_EQUAL(origins[OriginTag::InBox], numberOfMediaResourcesInDefaultSettings - 1); // Base profile icon not resolved because of profiles.defaults.icon + VERIFY_ARE_EQUAL(origins[OriginTag::ProfilesDefaults], 1); + VERIFY_ARE_EQUAL(origins[OriginTag::User], 1); + + auto profile{ settings->GetProfileByName(L"Base") }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); + VERIFY_ARE_NOT_EQUAL(L"iconFromBase", icon.Path()); + VERIFY_ARE_EQUAL(L"iconFromDefaults", icon.Path()); + VERIFY_ARE_EQUAL(L"resolved", icon.Resolved()); + + icon = MediaResourceHelper::FromString(L"NewIconFromRuntime"); + profile.Icon(icon); + + // Not OK until resolved! + VERIFY_IS_FALSE(icon.Ok()); + + { + origins.clear(); + // We should be called only once, for the newly changed icon. + auto [t, e] = requireCalled(1, + [&](auto&& origin, auto&&, auto&& resource) { + origins[origin]++; + resource.Resolve(L"newResolvedValue"); + }); + g_mediaResolverHook = t; + settings->ResolveMediaResources(); + } + + VERIFY_ARE_EQUAL(origins[OriginTag::User], 1); // This should be on the User's copy (not Defaults) of the Profile now. + + VERIFY_IS_TRUE(icon.Ok()); + VERIFY_ARE_EQUAL(L"NewIconFromRuntime", icon.Path()); + VERIFY_ARE_EQUAL(L"newResolvedValue", icon.Resolved()); + } + + void MediaResourceTests::ValidateResolverNotCalledForEmojiIcons() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + std::unordered_map origins; + + winrt::com_ptr settings; + { + auto [t, e] = requireCalled(numberOfMediaResourcesInDefaultSettings, // only called for inbox resources, none of the emoji icon profiles + [&](auto&& origin, auto&&, auto&& resource) { + VERIFY_ARE_NOT_EQUAL(OriginTag::User, origin); + origins[origin]++; + resource.Reject(); + }); + g_mediaResolverHook = t; + settings = createSettings(R"({ + "profiles": { + "list": [ + { + "icon": "\u2665", + "name": "Basic" + }, + { + "icon": "\ue720", + "name": "MDL2" + }, + { + "icon": "👨‍👩‍👧‍👦", + "name": "GraphemeCluster" + }, + { + "icon": "🕴️", + "name": "SurrogatePair" + }, + { + "icon": "#\ufe0f\u20e3", + "name": "VariantWithEnclosingCombiner" + }, + ] + } +})"); + } + + VERIFY_ARE_EQUAL(origins[OriginTag::User], 0); + + { + auto profile{ settings->GetProfileByName(L"Basic") }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); + VERIFY_ARE_EQUAL(icon.Resolved(), icon.Path()); + VERIFY_ARE_EQUAL(L"\u2665", icon.Resolved()); + } + + { + auto profile{ settings->GetProfileByName(L"MDL2") }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); + VERIFY_ARE_EQUAL(icon.Resolved(), icon.Path()); + VERIFY_ARE_EQUAL(L"\ue720", icon.Resolved()); + } + + { + auto profile{ settings->GetProfileByName(L"GraphemeCluster") }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); + VERIFY_ARE_EQUAL(icon.Resolved(), icon.Path()); + VERIFY_ARE_EQUAL(L"\U0001F468\u200d\U0001F469\u200d\U0001F467\u200d\U0001F466", icon.Resolved()); + } + + { + auto profile{ settings->GetProfileByName(L"SurrogatePair") }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); + VERIFY_ARE_EQUAL(icon.Resolved(), icon.Path()); + VERIFY_ARE_EQUAL(L"\U0001F574\uFE0F", icon.Resolved()); + } + + { + auto profile{ settings->GetProfileByName(L"VariantWithEnclosingCombiner") }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); + VERIFY_ARE_EQUAL(icon.Resolved(), icon.Path()); + VERIFY_ARE_EQUAL(L"#\uFE0F\u20E3", icon.Resolved()); + } + } +#pragma endregion + +#pragma region Fragment Behaviors + void MediaResourceTests::FragmentUpdatesBaseProfile() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + + winrt::com_ptr settings; + { + auto [t, e] = requireCalled(numberOfMediaResourcesInDefaultSettings - 1 /* base deleted */ + 1 /* icon in fragment */, + [&](auto&&, auto&& basePath, auto&& resource) { + resource.Resolve(basePath); + }); + g_mediaResolverHook = t; + settings = createSettingsWithFragments(R"({})", { Fragment{ L"fragment", fragmentBasePath1, R"( +{ + "profiles": [ + { + "updates": "{862d46aa-cc9c-4e6c-b872-9cadaafcdbbe}", + "icon": "IconFromFragment" + } + ] +} +)" } }); + } + + auto profile{ settings->GetProfileByName(L"Base") }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); + VERIFY_ARE_EQUAL(LR"(IconFromFragment)", icon.Path()); + // This was resolved by the mock resolver to the supplied base path; it's a quick way to check the right one got resolved :) + VERIFY_ARE_EQUAL(fragmentBasePath1, icon.Resolved()); + } + + void MediaResourceTests::FragmentActionResourcesGetResolved() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + + winrt::com_ptr settings; + { + auto [t, e] = requireCalled([&](auto&&, auto&& basePath, auto&& resource) { + resource.Resolve(basePath); + }); + g_mediaResolverHook = t; + settings = createSettingsWithFragments(R"({})", { Fragment{ L"fragment", fragmentBasePath1, R"( +{ + "profiles": [ + { + "updates": "{862d46aa-cc9c-4e6c-b872-9cadaafcdbbe}", + "icon": "IconFromFragment" + } + ], + "actions": [ + { + "command": { + "action": "sendInput", + "input": "FROM WAY OUT BEYOND THE STARS" + }, + "icon": "foo.ico", + "id": "Dustin.SendInput" + } + ], +} +)" } }); + } + + { + auto command{ settings->ActionMap().GetActionByID(L"Dustin.SendInput") }; + auto icon{ command.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); + VERIFY_ARE_EQUAL(LR"(foo.ico)", icon.Path()); + // This was resolved by the mock resolver to the supplied base path; it's a quick way to check the right one got resolved :) + VERIFY_ARE_EQUAL(fragmentBasePath1, icon.Resolved()); + } + } + + void MediaResourceTests::DisabledFragmentNotResolved() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + + winrt::com_ptr settings; + { + // This should only be called baseline number of times, because the fragment is disabled. + auto [t, e] = requireCalled(numberOfMediaResourcesInDefaultSettings, + [&](auto&& origin, auto&&, auto&& resource) { + // If we get a Fragment here, we messed up. + VERIFY_ARE_NOT_EQUAL(origin, OriginTag::Fragment); + resource.Resolve(L"resolved"); + }); + g_mediaResolverHook = t; + settings = createSettingsWithFragments(R"({ "disabledProfileSources": [ "fragment" ] })", + { Fragment{ L"fragment", fragmentBasePath1, R"( +{ + "profiles": [ + { + "guid": "{4e7c2b36-642f-4694-83f8-8a5052038a23}", + "name": "FragmentProfile", + "commandline": "not_a_real_path", + "icon": "DoesNotMatterIgnoredByMockResolver" + } + ] +} +)" } }); + } + + auto profile{ settings->GetProfileByName(L"Base") }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); + VERIFY_ARE_EQUAL(LR"(iconFromBase)", icon.Path()); + VERIFY_ARE_EQUAL(L"resolved", icon.Resolved()); + } + + // This is more of a test of how unfocused appearances are inherited (in whole), but it's worth + // making sure that the fragment appearance doesn't impact and the unfocused appearance's base paths. + void MediaResourceTests::FragmentAppearanceAndUserAppearanceInteraction() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + + winrt::com_ptr settings; + { + auto [t, e] = requireCalled([&](auto&&, auto&& basePath, auto&& resource) { + resource.Resolve(fmt::format(FMT_COMPILE(L"{}-{}"), basePath, resource.Path())); + }); + g_mediaResolverHook = t; + settings = createSettingsWithFragments(R"( +{ + "profiles": [ + { + "guid": "{4e7c2b36-642f-4694-83f8-8a5052038a23}", + "unfocusedAppearance": { + "experimental.pixelShaderPath": "unfocusedPixelShaderPath1" + } + }, + { + "guid": "{94df2990-d645-4675-8d9d-f8c89f842e6b}", + "unfocusedAppearance": { + "backgroundImage": "userSpecifiedUnfocusedBackgroundImage" + } + } + ] +} +)", + { Fragment{ L"fragment", L"FRAGMENT", R"( +{ + "profiles": [ + { + "guid": "{4e7c2b36-642f-4694-83f8-8a5052038a23}", + "name": "FragmentProfileWithUnfocusedBackgroundImage", + "commandline": "not_a_real_path", + "backgroundImage": "focusedBackgroundImage1", + "unfocusedAppearance": { + "backgroundImage": "unfocusedBackgroundImage1" + } + }, + { + "guid": "{94df2990-d645-4675-8d9d-f8c89f842e6b}", + "name": "FragmentProfileWithNoUnfocusedBackgroundImage", + "commandline": "not_a_real_path", + "backgroundImage": "focusedBackgroundImage2", + } + ] +} +)" } }); + } + + // The resolver produces finalized resource paths by taking base paths (c:\windows, or FRAGMENT) and + // combining them with the input paths. This lets us more easily track which resource came from where. + + { + auto profile{ settings->GetProfileByName(L"FragmentProfileWithUnfocusedBackgroundImage") }; + auto defaultAppearance{ profile.DefaultAppearance() }; + auto unfocusedAppearance{ profile.UnfocusedAppearance() }; + + VERIFY_IS_NOT_NULL(unfocusedAppearance); + + auto focusedBackground{ defaultAppearance.BackgroundImagePath() }; + auto unfocusedBackground{ unfocusedAppearance.BackgroundImagePath() }; + auto unfocusedPixelShader{ unfocusedAppearance.PixelShaderPath() }; + + VERIFY_IS_TRUE(focusedBackground.Ok()); + VERIFY_IS_TRUE(unfocusedBackground.Ok()); + VERIFY_IS_TRUE(unfocusedPixelShader.Ok()); + + VERIFY_ARE_EQUAL(LR"(FRAGMENT-focusedBackgroundImage1)", focusedBackground.Resolved()); + // The user changing the unfocusedAppearance object caused it to revert back to the focused one in the profile (!) + VERIFY_ARE_EQUAL(focusedBackground.Resolved(), unfocusedBackground.Resolved()); + const void* focusedBackgroundAbi{ winrt::get_abi(focusedBackground) }; + const void* unfocusedBackgroundAbi{ winrt::get_abi(unfocusedBackground) }; + VERIFY_ARE_EQUAL(focusedBackgroundAbi, unfocusedBackgroundAbi); // Objects should be identical in this case + VERIFY_ARE_EQUAL(LR"(C:\Windows-unfocusedPixelShaderPath1)", unfocusedPixelShader.Resolved()); // This is resolved to the user's base path + } + + { + auto profile{ settings->GetProfileByName(L"FragmentProfileWithNoUnfocusedBackgroundImage") }; + auto defaultAppearance{ profile.DefaultAppearance() }; + auto unfocusedAppearance{ profile.UnfocusedAppearance() }; + + VERIFY_IS_NOT_NULL(unfocusedAppearance); + + auto focusedBackground{ defaultAppearance.BackgroundImagePath() }; + auto unfocusedBackground{ unfocusedAppearance.BackgroundImagePath() }; + + VERIFY_IS_TRUE(focusedBackground.Ok()); + VERIFY_IS_TRUE(unfocusedBackground.Ok()); + + VERIFY_ARE_EQUAL(LR"(FRAGMENT-focusedBackgroundImage2)", focusedBackground.Resolved()); + VERIFY_ARE_EQUAL(LR"(C:\Windows-userSpecifiedUnfocusedBackgroundImage)", unfocusedBackground.Resolved()); + } + } +#pragma endregion + +#pragma region Profile Behaviors + // The invalid resource came from the Defaults profile, which specifies ping as the command line + void MediaResourceTests::ProfileDefaultsContainsInvalidIcon() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + winrt::com_ptr settings; + { + auto [t, e] = requireCalled([&](auto&&, auto&&, auto&& resource) { + // All resources are invalid. + resource.Reject(); + }); + g_mediaResolverHook = t; + settings = createSettings(R"({ + "profiles": { + "defaults": { + "icon": "DoesNotMatter", + } + } +})"); + } + + auto profile{ settings->GetProfileByName(L"Base") }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); // Profile with commandline always has an icon + VERIFY_ARE_EQUAL(cmdCommandline, icon.Resolved()); + } + + // The invalid resource came from the profile itself, which has its own commandline. + void MediaResourceTests::ProfileSpecifiesInvalidIconAndCommandline() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + winrt::com_ptr settings; + { + auto [t, e] = requireCalled([&](auto&&, auto&&, auto&& resource) { + // All resources are invalid. + resource.Reject(); + }); + g_mediaResolverHook = t; + settings = createSettings(R"({ + "profiles": { + "defaults": { + "icon": "DoesNotMatter", + "commandline": "C:\\Windows\\System32\\ping.exe", + }, + "list": [ + { + "guid": "{2cdb0be2-f601-4f70-9a6c-3472b3257883}", + "icon": "DoesNotMatter", + "commandline": "C:\\Windows\\System32\\cscript.exe", + "name": "ProfileSpecifiesInvalidIconAndCommandline" + } + ] + } +})"); + } + + auto profile{ settings->GetProfileByName(L"ProfileSpecifiesInvalidIconAndCommandline") }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); // Profile with commandline always has an icon + VERIFY_ARE_EQUAL(overrideCommandline, icon.Resolved()); + } + + // The invalid resource came from the profile itself, where the commandline is the default value (profile.commandline default value is CMD.exe) + void MediaResourceTests::ProfileSpecifiesInvalidIconAndNoCommandline() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + winrt::com_ptr settings; + { + auto [t, e] = requireCalled([&](auto&&, auto&&, auto&& resource) { + // All resources are invalid. + resource.Reject(); + }); + g_mediaResolverHook = t; + settings = createSettings(R"({ + "profiles": { + "defaults": { + "icon": "DoesNotMatter", + }, + "list": [ + { + "guid": "{af9dec6c-1337-4278-897d-69ca04920b27}", + "icon": "DoesNotMatter", + "name": "ProfileSpecifiesInvalidIconAndNoCommandline" + } + ] + } +})"); + } + + auto profile{ settings->GetProfileByName(L"ProfileSpecifiesInvalidIconAndNoCommandline") }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); + VERIFY_ARE_EQUAL(cmdCommandline, icon.Resolved()); + } + + // The invalid resource came from the Defaults profile, where the commandline falls back to the default value of CMD.exe (PROFILE COMMANDLINE IGNORED) + void MediaResourceTests::ProfileInheritsInvalidIconAndHasCommandline() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + winrt::com_ptr settings; + { + auto [t, e] = requireCalled([&](auto&&, auto&&, auto&& resource) { + // All resources are invalid. + resource.Reject(); + }); + g_mediaResolverHook = t; + settings = createSettings(R"({ + "profiles": { + "defaults": { + "icon": "DoesNotMatter" + }, + "list": [ + { + "guid": "{b0f32281-7173-46ef-aa2f-ddcf36670cf0}", + "commandline": "C:\\Windows\\System32\\cscript.exe", + "name": "ProfileInheritsInvalidIconAndHasCommandline" + } + ] + } +})"); + } + + auto profile{ settings->GetProfileByName(L"ProfileInheritsInvalidIconAndHasCommandline") }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); + VERIFY_ARE_EQUAL(cmdCommandline, icon.Resolved()); + } + + // The invalid resource came from the Defaults profile, which has the default command line of CMD.exe (PROFILE COMMANDLINE MISSING) + void MediaResourceTests::ProfileInheritsInvalidIconAndHasNoCommandline() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + winrt::com_ptr settings; + { + auto [t, e] = requireCalled([&](auto&&, auto&&, auto&& resource) { + // All resources are invalid. + resource.Reject(); + }); + g_mediaResolverHook = t; + settings = createSettings(R"({ + "profiles": { + "defaults": { + "icon": "DoesNotMatter" + }, + "list": [ + { + "guid": "{21c4122a-b094-4436-9e9c-a06f05f35ad2}", + "name": "ProfileInheritsInvalidIconAndHasNoCommandline" + } + ] + } +})"); + } + + auto profile{ settings->GetProfileByName(L"ProfileInheritsInvalidIconAndHasNoCommandline") }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); + VERIFY_ARE_EQUAL(cmdCommandline, icon.Resolved()); + } + + // The invalid resource came from the profile itself, which has its own commandline. + void MediaResourceTests::ProfileSpecifiesNullIcon() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + winrt::com_ptr settings; + { + auto [t, e] = requireCalled([&](auto&&, auto&&, auto&& resource) { + // All resources are invalid. + resource.Reject(); + }); + g_mediaResolverHook = t; + settings = createSettings(R"({ + "profiles": { + "defaults": { + "icon": "DoesNotMatter", + "commandline": "C:\\Windows\\System32\\ping.exe", + }, + "list": [ + { + "guid": "{e1332dad-232c-4019-b3ff-05a4386c8c46}", + "icon": null, + "commandline": "C:\\Windows\\System32\\cscript.exe", + "name": "ProfileSpecifiesNullIcon" + } + ] + } +})"); + } + + auto profile{ settings->GetProfileByName(L"ProfileSpecifiesNullIcon") }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); // Profile with commandline always has an icon + VERIFY_ARE_EQUAL(overrideCommandline, icon.Resolved()); + } + + // The invalid resource came from the profile itself, where the commandline falls back to the default value of CMD.exe + void MediaResourceTests::ProfileSpecifiesNullIconAndHasNoCommandline() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + winrt::com_ptr settings; + { + auto [t, e] = requireCalled([&](auto&&, auto&&, auto&& resource) { + // All resources are invalid. + resource.Reject(); + }); + g_mediaResolverHook = t; + settings = createSettings(R"({ + "profiles": { + "defaults": { + "icon": "DoesNotMatter" + }, + "list": [ + { + "guid": "{b4053177-ae5c-4600-8b77-5f81a5d313e1}", + "icon": null, + "name": "ProfileSpecifiesNullIconAndHasNoCommandline" + }, + ] + } +})"); + } + + auto profile{ settings->GetProfileByName(L"ProfileSpecifiesNullIconAndHasNoCommandline") }; + auto icon{ profile.Icon() }; + VERIFY_IS_TRUE(icon.Ok()); // Profile with commandline always has an icon + VERIFY_ARE_EQUAL(cmdCommandline, icon.Resolved()); + } + + // A profile replaces the bell sounds (2) in the base settings; all bell sounds retained + void MediaResourceTests::ProfileOverwritesBellSound() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + winrt::com_ptr settings; + { + auto [t, e] = requireCalled([&](auto&&, auto&&, auto&& resource) { + // All resources are invalid. + resource.Reject(); + }); + g_mediaResolverHook = t; + settings = createSettings(R"({ + "profiles": { + "list": [ + { + "guid": "{862d46aa-cc9c-4e6c-b872-9cadaafcdbbe}", + "bellSound": [ + "does not matter; resolved rejected" + ], + }, + ] + } +})"); + } + + auto profile{ settings->GetProfileByName(L"Base") }; + auto bellSounds{ profile.BellSound() }; + VERIFY_ARE_EQUAL(1u, bellSounds.Size()); + VERIFY_IS_FALSE(bellSounds.GetAt(0).Ok()); + } +#pragma endregion + +#pragma region Real Resolver Tests + void MediaResourceTests::RealResolverFilePaths() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + + g_mediaResolverHook = nullptr; // Use the real resolver + + // For profile, we test images instead of icon because Icon has a fallback behavior. + auto settings = createSettings(R"({ + "profiles": { + "list": [ + { + "backgroundImage": "C:\\Windows\\System32\\cmd.exe", + "name": "ProfileAbsolutePathImage" + }, + { + "backgroundImage": "C:/Windows/System32/cmd.exe", + "name": "ProfileAbsolutePathImageSlashes" + }, + { + "backgroundImage": "explorer.exe", + "name": "ProfileRelativePathImage" + }, + { + "backgroundImage": "..\\Windows\\explorer.exe", + "name": "ProfileRelativePathImageTraversal" + }, + { + "backgroundImage": "%ComSpec%", + "name": "ProfileEnvironmentVariableImage" + }, + { + "backgroundImage": "X:\foobar.ico", + "name": "ProfileInvalidImage" + }, + ] + } +})"); + + // All relative paths are relative to the fake testing user settings path of C:\Windows + constexpr std::wstring_view expectedPath1{ LR"(C:\Windows\System32\cmd.exe)" }; + constexpr std::wstring_view expectedPath2{ LR"(C:\Windows\explorer.exe)" }; + + { + auto profile{ settings->GetProfileByName(L"ProfileAbsolutePathImage") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_TRUE(image.Ok()); + VERIFY_ARE_EQUAL(expectedPath1, image.Resolved()); + } + + { + auto profile{ settings->GetProfileByName(L"ProfileAbsolutePathImageSlashes") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_TRUE(image.Ok()); + VERIFY_ARE_EQUAL(expectedPath1, image.Resolved()); + } + + { + auto profile{ settings->GetProfileByName(L"ProfileRelativePathImage") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_TRUE(image.Ok()); + VERIFY_ARE_EQUAL(expectedPath2, image.Resolved()); + } + + { + auto profile{ settings->GetProfileByName(L"ProfileRelativePathImageTraversal") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_TRUE(image.Ok()); + VERIFY_ARE_EQUAL(expectedPath2, image.Resolved()); + } + + { + auto profile{ settings->GetProfileByName(L"ProfileEnvironmentVariableImage") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_TRUE(image.Ok()); + // The casing is different on this one... + VERIFY_IS_TRUE(til::equals_insensitive_ascii(LR"(c:\windows\system32\cmd.exe)", image.Resolved()), WEX::Common::NoThrowString{ image.Resolved().c_str() }); + } + + { + auto profile{ settings->GetProfileByName(L"ProfileInvalidImage") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_FALSE(image.Ok()); + VERIFY_ARE_EQUAL(L"", image.Resolved()); + } + } + + static std::optional _getDesktopWallpaper() + { + WCHAR desktopWallpaper[MAX_PATH]; + + // "The returned string will not exceed MAX_PATH characters" as of 2020 + if (SystemParametersInfoW(SPI_GETDESKWALLPAPER, MAX_PATH, desktopWallpaper, SPIF_UPDATEINIFILE)) + { + return winrt::hstring{ &desktopWallpaper[0] }; + } + + return std::nullopt; + } + + void MediaResourceTests::RealResolverSpecialKeywords() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + + g_mediaResolverHook = nullptr; // Use the real resolver + + // For profile, we test images instead of icon because Icon has a fallback behavior. + auto settings = createSettings(R"({ + "profiles": { + "list": [ + { + "backgroundImage": "none", + "name": "ProfileNoneImage" + }, + { + "backgroundImage": "desktopWallpaper", + "name": "ProfileDesktopWallpaperImage" + } + ] + } +})"); + + { + auto profile{ settings->GetProfileByName(L"ProfileNoneImage") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_TRUE(image.Ok()); + VERIFY_ARE_NOT_EQUAL(L"none", image.Resolved()); + VERIFY_ARE_EQUAL(L"", image.Resolved()); + } + + { + auto profile{ settings->GetProfileByName(L"ProfileDesktopWallpaperImage") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + if (const auto desktopWallpaper{ _getDesktopWallpaper() }) + { + VERIFY_IS_TRUE(image.Ok()); + VERIFY_ARE_NOT_EQUAL(L"desktopWallpaper", image.Resolved()); + VERIFY_ARE_EQUAL(*desktopWallpaper, image.Resolved()); + } + else + { + WEX::Logging::Log::Comment(L"No wallpaper is set; testing failure case instead"); + VERIFY_IS_FALSE(image.Ok()); + VERIFY_ARE_EQUAL(L"", image.Resolved()); + } + } + } + + void MediaResourceTests::RealResolverUrlCases() + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + + g_mediaResolverHook = nullptr; // Use the real resolver + + // For profile, we test images instead of icon because Icon has a fallback behavior. + auto settings = createSettings(R"({ + "profiles": { + "list": [ + { + "backgroundImage": "https://contoso.com/explorer.exe", + "name": "ProfileWebUri" + }, + { + "backgroundImage": "https://contoso.com/it_would_be_a_real_surprise_if_windows_added_a_file_named_this.ico", + "name": "ProfileWebUriDoesNotExistLocally" + }, + { + "backgroundImage": "file:///C:/Windows/System32/cmd.exe", + "name": "ProfileAbsoluteFileUri" + }, + { + "backgroundImage": "ms-resource:///ProfileIcons/foo.png", + "name": "ProfileAppResourceUri" + }, + { + "backgroundImage": "ms-appx:///ProfileIcons/foo.png", + "name": "ProfileAppxUriLocal" + }, + { + "backgroundImage": "ms-appx://Microsoft.Burrito/Resources/explorer.exe", + "name": "ProfileAppxUriOtherApp" + }, + { + "backgroundImage": "ftp://0.0.0.0/share/file.png", + "name": "ProfileIllegalWebUri" + }, + { + "backgroundImage": "x://is_this_a_file_or_a_path", + "name": "ProfileIllegalUri1" + }, + { + "backgroundImage": "fake-scheme://foo", + "name": "ProfileIllegalUri2" + }, + { + "backgroundImage": "http:/e/x", + "name": "ProfileIllegalUri3" + }, + ] + } +})"); + + // All relative paths are relative to the fake testing user settings path of C:\Windows + constexpr std::wstring_view expectedCmdPath{ LR"(C:\Windows\System32\cmd.exe)" }; + constexpr std::wstring_view expectedExplorerPath{ LR"(C:\Windows\explorer.exe)" }; + + // http URLs are resolved to the base path (in this case, user settings path of C:\Windows) plus leaf filename. + // ms-appx URLs pointing to *any app* (which implies it is not our app) are treated the same. + + { + auto profile{ settings->GetProfileByName(L"ProfileWebUri") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_TRUE(image.Ok()); + VERIFY_ARE_EQUAL(expectedExplorerPath, image.Resolved()); + } + + { + auto profile{ settings->GetProfileByName(L"ProfileWebUriDoesNotExistLocally") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_FALSE(image.Ok()); + VERIFY_ARE_NOT_EQUAL(image.Resolved(), image.Path()); + } + + { + auto profile{ settings->GetProfileByName(L"ProfileAbsoluteFileUri") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_TRUE(image.Ok()); + VERIFY_ARE_EQUAL(expectedCmdPath, image.Resolved()); + } + + { + auto profile{ settings->GetProfileByName(L"ProfileAppResourceUri") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_TRUE(image.Ok()); + VERIFY_ARE_EQUAL(LR"(ms-resource:///ProfileIcons/foo.png)", image.Resolved()); + } + + { + auto profile{ settings->GetProfileByName(L"ProfileAppxUriLocal") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_TRUE(image.Ok()); + VERIFY_ARE_EQUAL(LR"(ms-appx:///ProfileIcons/foo.png)", image.Resolved()); + } + + { + auto profile{ settings->GetProfileByName(L"ProfileAppxUriOtherApp") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_TRUE(image.Ok()); + VERIFY_ARE_EQUAL(expectedExplorerPath, image.Resolved()); + } + + { + auto profile{ settings->GetProfileByName(L"ProfileIllegalWebUri") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_FALSE(image.Ok()); + VERIFY_ARE_EQUAL(L"", image.Resolved()); + VERIFY_ARE_NOT_EQUAL(image.Resolved(), image.Path()); + } + + { + auto profile{ settings->GetProfileByName(L"ProfileIllegalUri1") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_FALSE(image.Ok()); + VERIFY_ARE_EQUAL(L"", image.Resolved()); + VERIFY_ARE_NOT_EQUAL(image.Resolved(), image.Path()); + } + + { + auto profile{ settings->GetProfileByName(L"ProfileIllegalUri2") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_FALSE(image.Ok()); + VERIFY_ARE_EQUAL(L"", image.Resolved()); + VERIFY_ARE_NOT_EQUAL(image.Resolved(), image.Path()); + } + + { + auto profile{ settings->GetProfileByName(L"ProfileIllegalUri3") }; + auto image{ profile.DefaultAppearance().BackgroundImagePath() }; + VERIFY_IS_FALSE(image.Ok()); + VERIFY_ARE_EQUAL(L"", image.Resolved()); + VERIFY_ARE_NOT_EQUAL(image.Resolved(), image.Path()); + } + } +#pragma endregion +} diff --git a/src/cascadia/UnitTests_SettingsModel/ProfileTests.cpp b/src/cascadia/UnitTests_SettingsModel/ProfileTests.cpp index 97ce4751b4..7697715b8f 100644 --- a/src/cascadia/UnitTests_SettingsModel/ProfileTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/ProfileTests.cpp @@ -30,6 +30,7 @@ namespace SettingsModelUnitTests TEST_METHOD(DuplicateProfileTest); TEST_METHOD(TestGenGuidsForProfiles); TEST_METHOD(TestCorrectOldDefaultShellPaths); + TEST_METHOD(ProfileDefaultsProhibitedSettings); }; void ProfileTests::ProfileGeneratesGuid() @@ -217,32 +218,32 @@ namespace SettingsModelUnitTests const auto profile3Json = VerifyParseSucceeded(profile3String); auto profile0 = implementation::Profile::FromJson(profile0Json); - VERIFY_IS_FALSE(profile0->Icon().empty()); - VERIFY_ARE_EQUAL(L"not-null.png", profile0->Icon()); + VERIFY_IS_FALSE(profile0->Icon().Path().empty()); + VERIFY_ARE_EQUAL(L"not-null.png", profile0->Icon().Path()); Log::Comment(NoThrowString().Format( L"Verify that layering an object the key set to null will clear the key")); profile0->LayerJson(profile1Json); - VERIFY_IS_TRUE(profile0->Icon().empty()); + VERIFY_IS_TRUE(profile0->Icon().Path().empty()); profile0->LayerJson(profile2Json); - VERIFY_IS_TRUE(profile0->Icon().empty()); + VERIFY_IS_TRUE(profile0->Icon().Path().empty()); profile0->LayerJson(profile3Json); - VERIFY_IS_FALSE(profile0->Icon().empty()); - VERIFY_ARE_EQUAL(L"another-real.png", profile0->Icon()); + VERIFY_IS_FALSE(profile0->Icon().Path().empty()); + VERIFY_ARE_EQUAL(L"another-real.png", profile0->Icon().Path()); Log::Comment(NoThrowString().Format( L"Verify that layering an object _without_ the key will not clear the key")); profile0->LayerJson(profile2Json); - VERIFY_IS_FALSE(profile0->Icon().empty()); - VERIFY_ARE_EQUAL(L"another-real.png", profile0->Icon()); + VERIFY_IS_FALSE(profile0->Icon().Path().empty()); + VERIFY_ARE_EQUAL(L"another-real.png", profile0->Icon().Path()); auto profile1 = implementation::Profile::FromJson(profile1Json); - VERIFY_IS_TRUE(profile1->Icon().empty()); + VERIFY_IS_TRUE(profile1->Icon().Path().empty()); profile1->LayerJson(profile3Json); - VERIFY_IS_FALSE(profile1->Icon().empty()); - VERIFY_ARE_EQUAL(L"another-real.png", profile1->Icon()); + VERIFY_IS_FALSE(profile1->Icon().Path().empty()); + VERIFY_ARE_EQUAL(L"another-real.png", profile1->Icon().Path()); } void ProfileTests::LayerProfilesOnArray() @@ -470,4 +471,65 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(L"%SystemRoot%\\System32\\cmd.exe", allProfiles.GetAt(2).Commandline()); VERIFY_ARE_EQUAL(L"cmd.exe", allProfiles.GetAt(3).Commandline()); } + + void ProfileTests::ProfileDefaultsProhibitedSettings() + { + static constexpr std::string_view userProfiles{ R"({ + "profiles": { + "defaults": + { + "guid": "{00000000-0000-0000-0000-000000000000}", + "name": "Default Profile Name", + "source": "Default Profile Source", + "commandline": "foo.exe" + }, + "list": + [ + { + "name" : "PowerShell", + "commandline": "powershell.exe", + "guid" : "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" + }, + { + "name": "Profile with just a name" + }, + { + "guid": "{a0776706-1fa6-4439-b46c-287a65c084d5}", + } + ] + } + })" }; + + const auto settings = winrt::make_self(userProfiles); + + // Profile Defaults should not have a GUID, name, source, or commandline. + const auto profileDefaults = settings->ProfileDefaults(); + VERIFY_IS_FALSE(profileDefaults.HasGuid()); + VERIFY_IS_FALSE(profileDefaults.HasName()); + VERIFY_IS_FALSE(profileDefaults.HasSource()); + VERIFY_IS_FALSE(profileDefaults.HasCommandline()); + + const auto allProfiles = settings->AllProfiles(); + VERIFY_ARE_EQUAL(3u, allProfiles.Size()); + + // Profile settings should be set to the ones set at that layer + VERIFY_ARE_EQUAL(L"PowerShell", allProfiles.GetAt(0).Name()); + VERIFY_ARE_EQUAL(L"%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", allProfiles.GetAt(0).Commandline()); + VERIFY_ARE_EQUAL(Utils::GuidFromString(L"{61c54bbd-c2c6-5271-96e7-009a87ff44bf}"), static_cast(allProfiles.GetAt(0).Guid())); + VERIFY_IS_FALSE(allProfiles.GetAt(0).HasSource()); + + // Profile should not inherit the values attempted to be set on the Profiles Defaults layer + // This profile only has a name set + VERIFY_ARE_EQUAL(L"Profile with just a name", allProfiles.GetAt(1).Name()); + VERIFY_ARE_NOT_EQUAL(Utils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}"), static_cast(allProfiles.GetAt(1).Guid())); + VERIFY_ARE_NOT_EQUAL(L"Default Profile Source", allProfiles.GetAt(1).Source()); + VERIFY_ARE_NOT_EQUAL(L"foo.exe", allProfiles.GetAt(1).Commandline()); + + // Profile should not inherit the values attempted to be set on the Profiles Defaults layer + // This profile only has a guid set + VERIFY_ARE_NOT_EQUAL(L"Default Profile Name", allProfiles.GetAt(2).Name()); + VERIFY_ARE_NOT_EQUAL(Utils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}"), static_cast(allProfiles.GetAt(2).Guid())); + VERIFY_ARE_NOT_EQUAL(L"Default Profile Source", allProfiles.GetAt(2).Source()); + VERIFY_ARE_NOT_EQUAL(L"foo.exe", allProfiles.GetAt(2).Commandline()); + } } diff --git a/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp b/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp index d3d6586479..3112f83939 100644 --- a/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp @@ -58,6 +58,8 @@ namespace SettingsModelUnitTests TEST_METHOD(RoundtripActionsSameNameDifferentCommandsAreRetained); TEST_METHOD(MultipleActionsAreCollapsed); + TEST_METHOD(ProfileWithInvalidIcon); + private: // Method Description: // - deserializes and reserializes a json string representing a settings object model of type T @@ -126,7 +128,7 @@ namespace SettingsModelUnitTests "warning.confirmCloseAllTabs" : true, "warning.inputService" : true, "warning.largePaste" : true, - "warning.multiLinePaste" : true, + "warning.multiLinePaste" : "automatic", "actions": [], "keybindings": [] @@ -1295,4 +1297,32 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(toString(newResult), toString(oldResult)); } + + void SerializationTests::ProfileWithInvalidIcon() + { + static constexpr std::string_view settingsJson{ R"( + { + "defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}", + "profiles": [ + { + "name": "profile0", + "guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}", + "historySize": 1, + "commandline": "cmd.exe", + "icon": "c:\\this_icon_had_better_not_exist.tiff" + } + ] + })" }; + + implementation::SettingsLoader loader{ settingsJson, implementation::LoadStringResource(IDR_DEFAULTS) }; + loader.MergeInboxIntoUserSettings(); + loader.FinalizeLayering(); + const auto settings = winrt::make_self(std::move(loader)); + const auto newResult{ settings->ToJson() }; + + // A profile that specifies an invalid icon will fall back to the commandline on load, but that should + // not be reflected back in settings.json as null *or* as the commandline. The value should be exactly + // what was written in the settings file. + VERIFY_ARE_EQUAL(R"(c:\this_icon_had_better_not_exist.tiff)", newResult["profiles"]["list"][0]["icon"].asString()); + } } diff --git a/src/cascadia/UnitTests_SettingsModel/SettingsModel.UnitTests.vcxproj b/src/cascadia/UnitTests_SettingsModel/SettingsModel.UnitTests.vcxproj index fe92bc08f1..602f2f42bb 100644 --- a/src/cascadia/UnitTests_SettingsModel/SettingsModel.UnitTests.vcxproj +++ b/src/cascadia/UnitTests_SettingsModel/SettingsModel.UnitTests.vcxproj @@ -44,6 +44,8 @@ + + Create diff --git a/src/cascadia/UnitTests_SettingsModel/TerminalSettingsTests.cpp b/src/cascadia/UnitTests_SettingsModel/TerminalSettingsTests.cpp index e50ae9c831..36dfd21eae 100644 --- a/src/cascadia/UnitTests_SettingsModel/TerminalSettingsTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/TerminalSettingsTests.cpp @@ -7,23 +7,43 @@ #include #include "../TerminalSettingsModel/CascadiaSettings.h" -#include "../TerminalSettingsModel/TerminalSettings.h" #include "TestUtils.h" using namespace Microsoft::Console; using namespace WEX::Logging; using namespace WEX::TestExecution; using namespace WEX::Common; +using namespace winrt::Microsoft::Terminal::Settings; using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::Microsoft::Terminal::Control; +// This test **does not** link the adapter library, because the adapter will +// activate things in the Terminal.Settings.Model WinRT namespace. However, +// because we are a static library consumer of the settings model (so we can +// poke at the innards), we don't actually contain any activatable classes in +// the Model namespace. +// +// Usually, we don't need to activate classes from our own library using +// RoGetActivationFactory since we set CppWinRTOptimized=true[1]. The adapter +// doesn't know this. +// +// Including the cpp file in this project pulls in the local C++/WinRT headers +// for the adapter which *do* resolve to the optimized local component +// activator, rather than the ones the adapter is linked to. +// +// [1] https://web.archive.org/web/20250823030814/https://kennykerrca.wordpress.com/2019/06/07/cppwinrt-optimizing-components/ +// +// Ideally, we would move this entire test suite to UnitTests_TerminalApp; +// however, it relies on some of CascadiaSettings, ActionMap and +// GlobalSettings' implementation details. Ugh. +#include "../TerminalSettingsAppAdapterLib/TerminalSettings.h" + namespace SettingsModelUnitTests { class TerminalSettingsTests { TEST_CLASS(TerminalSettingsTests); - TEST_METHOD(TryCreateWinRTType); TEST_METHOD(TestTerminalArgsForBinding); TEST_METHOD(CommandLineToArgvW); TEST_METHOD(NormalizeCommandLine); @@ -32,27 +52,8 @@ namespace SettingsModelUnitTests TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist); TEST_METHOD(TestLayerProfileOnColorScheme); TEST_METHOD(TestCommandlineToTitlePromotion); - - TEST_CLASS_SETUP(ClassSetup) - { - return true; - } }; - void TerminalSettingsTests::TryCreateWinRTType() - { - TerminalSettings settings; - VERIFY_IS_NOT_NULL(settings); - auto oldFontSize = settings.FontSize(); - - auto selfSettings = winrt::make_self(); - VERIFY_ARE_EQUAL(oldFontSize, selfSettings->FontSize()); - - selfSettings->FontSize(oldFontSize + 5); - auto newFontSize = selfSettings->FontSize(); - VERIFY_ARE_NOT_EQUAL(oldFontSize, newFontSize); - } - // CascadiaSettings::_normalizeCommandLine abuses some aspects from CommandLineToArgvW // to simplify the implementation. It assumes that all arguments returned by // CommandLineToArgvW are returned back to back in memory as "arg1\0arg2\0arg3\0...". @@ -330,11 +331,11 @@ namespace SettingsModelUnitTests VERIFY_IS_TRUE(terminalArgs.Profile().empty()); const auto profile{ settings->GetProfileForArgs(terminalArgs) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid0, profile.Guid()); - VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(1, termSettings.HistorySize()); + VERIFY_ARE_EQUAL(L"cmd.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(1, termSettings->HistorySize()); } { KeyChord kc{ true, false, false, false, static_cast('B'), 0 }; @@ -354,11 +355,11 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", terminalArgs.Profile()); const auto profile{ settings->GetProfileForArgs(terminalArgs) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid1, profile.Guid()); - VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(2, termSettings.HistorySize()); + VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(2, termSettings->HistorySize()); } { KeyChord kc{ true, false, false, false, static_cast('C'), 0 }; @@ -378,11 +379,11 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(L"profile1", terminalArgs.Profile()); const auto profile{ settings->GetProfileForArgs(terminalArgs) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid1, profile.Guid()); - VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(2, termSettings.HistorySize()); + VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(2, termSettings->HistorySize()); } { KeyChord kc{ true, false, false, false, static_cast('D'), 0 }; @@ -402,11 +403,11 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile()); const auto profile{ settings->GetProfileForArgs(terminalArgs) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(profile2Guid, profile.Guid()); - VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(3, termSettings.HistorySize()); + VERIFY_ARE_EQUAL(L"wsl.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(3, termSettings->HistorySize()); } { KeyChord kc{ true, false, false, false, static_cast('E'), 0 }; @@ -426,12 +427,12 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(L"foo.exe", terminalArgs.Commandline()); const auto profile{ settings->GetProfileForArgs(terminalArgs) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) }; const auto termSettings = settingsStruct.DefaultSettings(); // This action specified a command but no profile; it gets reassigned to the base profile VERIFY_ARE_EQUAL(settings->ProfileDefaults(), profile); - VERIFY_ARE_EQUAL(29, termSettings.HistorySize()); - VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline()); + VERIFY_ARE_EQUAL(29, termSettings->HistorySize()); + VERIFY_ARE_EQUAL(L"foo.exe", termSettings->Commandline()); } { KeyChord kc{ true, false, false, false, static_cast('F'), 0 }; @@ -452,11 +453,11 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(L"foo.exe", terminalArgs.Commandline()); const auto profile{ settings->GetProfileForArgs(terminalArgs) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid1, profile.Guid()); - VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(2, termSettings.HistorySize()); + VERIFY_ARE_EQUAL(L"foo.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(2, termSettings->HistorySize()); } { KeyChord kc{ true, false, false, false, static_cast('G'), 0 }; @@ -474,11 +475,11 @@ namespace SettingsModelUnitTests VERIFY_IS_TRUE(terminalArgs.Profile().empty()); const auto profile{ settings->GetProfileForArgs(terminalArgs) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid0, profile.Guid()); - VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(1, termSettings.HistorySize()); + VERIFY_ARE_EQUAL(L"cmd.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(1, termSettings->HistorySize()); } { KeyChord kc{ true, false, false, false, static_cast('H'), 0 }; @@ -497,12 +498,12 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(L"c:\\foo", terminalArgs.StartingDirectory()); const auto profile{ settings->GetProfileForArgs(terminalArgs) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid0, profile.Guid()); - VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory()); - VERIFY_ARE_EQUAL(1, termSettings.HistorySize()); + VERIFY_ARE_EQUAL(L"cmd.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(L"c:\\foo", termSettings->StartingDirectory()); + VERIFY_ARE_EQUAL(1, termSettings->HistorySize()); } { KeyChord kc{ true, false, false, false, static_cast('I'), 0 }; @@ -522,12 +523,12 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile()); const auto profile{ settings->GetProfileForArgs(terminalArgs) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(profile2Guid, profile.Guid()); - VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory()); - VERIFY_ARE_EQUAL(3, termSettings.HistorySize()); + VERIFY_ARE_EQUAL(L"wsl.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(L"c:\\foo", termSettings->StartingDirectory()); + VERIFY_ARE_EQUAL(3, termSettings->HistorySize()); } { KeyChord kc{ true, false, false, false, static_cast('J'), 0 }; @@ -546,12 +547,12 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(L"bar", terminalArgs.TabTitle()); const auto profile{ settings->GetProfileForArgs(terminalArgs) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid0, profile.Guid()); - VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle()); - VERIFY_ARE_EQUAL(1, termSettings.HistorySize()); + VERIFY_ARE_EQUAL(L"cmd.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(L"bar", termSettings->StartingTitle()); + VERIFY_ARE_EQUAL(1, termSettings->HistorySize()); } { KeyChord kc{ true, false, false, false, static_cast('K'), 0 }; @@ -571,12 +572,12 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile()); const auto profile{ settings->GetProfileForArgs(terminalArgs) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(profile2Guid, profile.Guid()); - VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle()); - VERIFY_ARE_EQUAL(3, termSettings.HistorySize()); + VERIFY_ARE_EQUAL(L"wsl.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(L"bar", termSettings->StartingTitle()); + VERIFY_ARE_EQUAL(3, termSettings->HistorySize()); } { KeyChord kc{ true, false, false, false, static_cast('L'), 0 }; @@ -598,13 +599,13 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(L"profile1", terminalArgs.Profile()); const auto profile{ settings->GetProfileForArgs(terminalArgs) }; - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) }; const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid1, profile.Guid()); - VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline()); - VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle()); - VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory()); - VERIFY_ARE_EQUAL(2, termSettings.HistorySize()); + VERIFY_ARE_EQUAL(L"foo.exe", termSettings->Commandline()); + VERIFY_ARE_EQUAL(L"bar", termSettings->StartingTitle()); + VERIFY_ARE_EQUAL(L"c:\\foo", termSettings->StartingDirectory()); + VERIFY_ARE_EQUAL(2, termSettings->HistorySize()); } } @@ -637,9 +638,8 @@ namespace SettingsModelUnitTests try { - auto terminalSettings{ TerminalSettings::CreateWithProfile(*settings, profile1, nullptr) }; - VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings); - VERIFY_ARE_EQUAL(1, terminalSettings.DefaultSettings().HistorySize()); + auto settingsStruct{ TerminalSettings::CreateWithProfile(*settings, profile1) }; + VERIFY_ARE_EQUAL(1, settingsStruct.DefaultSettings()->HistorySize()); } catch (...) { @@ -648,9 +648,8 @@ namespace SettingsModelUnitTests try { - auto terminalSettings{ TerminalSettings::CreateWithProfile(*settings, profile2, nullptr) }; - VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings); - VERIFY_ARE_EQUAL(2, terminalSettings.DefaultSettings().HistorySize()); + auto settingsStruct{ TerminalSettings::CreateWithProfile(*settings, profile2) }; + VERIFY_ARE_EQUAL(2, settingsStruct.DefaultSettings()->HistorySize()); } catch (...) { @@ -659,9 +658,8 @@ namespace SettingsModelUnitTests try { - const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(*settings, nullptr, nullptr) }; - VERIFY_ARE_NOT_EQUAL(nullptr, termSettings); - VERIFY_ARE_EQUAL(1, termSettings.DefaultSettings().HistorySize()); + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, nullptr) }; + VERIFY_ARE_EQUAL(1, settingsStruct.DefaultSettings()->HistorySize()); } catch (...) { @@ -698,9 +696,8 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(settings->GlobalSettings().DefaultProfile(), settings->ActiveProfiles().GetAt(0).Guid()); try { - const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(*settings, nullptr, nullptr) }; - VERIFY_ARE_NOT_EQUAL(nullptr, termSettings); - VERIFY_ARE_EQUAL(1, termSettings.DefaultSettings().HistorySize()); + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, nullptr) }; + VERIFY_ARE_EQUAL(1, settingsStruct.DefaultSettings()->HistorySize()); } catch (...) { @@ -792,7 +789,7 @@ namespace SettingsModelUnitTests VERIFY_ARE_EQUAL(2u, settings->GlobalSettings().ColorSchemes().Size()); auto createTerminalSettings = [&](const auto& profile, const auto& schemes, const auto& Theme) { - auto terminalSettings{ winrt::make_self() }; + auto terminalSettings{ winrt::make_self() }; terminalSettings->_ApplyProfileSettings(profile); terminalSettings->_ApplyAppearanceSettings(profile.DefaultAppearance(), schemes, Theme); return terminalSettings; @@ -839,59 +836,59 @@ namespace SettingsModelUnitTests { // just a profile (profile wins) NewTerminalArgs args{}; args.Profile(L"profile0"); - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) }; - VERIFY_ARE_EQUAL(L"profile0", settingsStruct.DefaultSettings().StartingTitle()); + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) }; + VERIFY_ARE_EQUAL(L"profile0", settingsStruct.DefaultSettings()->StartingTitle()); } { // profile and command line -> no promotion (profile wins) NewTerminalArgs args{}; args.Profile(L"profile0"); args.Commandline(L"foo.exe"); - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) }; - VERIFY_ARE_EQUAL(L"profile0", settingsStruct.DefaultSettings().StartingTitle()); + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) }; + VERIFY_ARE_EQUAL(L"profile0", settingsStruct.DefaultSettings()->StartingTitle()); } { // just a title -> it is propagated NewTerminalArgs args{}; args.TabTitle(L"Analog Kid"); - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) }; - VERIFY_ARE_EQUAL(L"Analog Kid", settingsStruct.DefaultSettings().StartingTitle()); + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) }; + VERIFY_ARE_EQUAL(L"Analog Kid", settingsStruct.DefaultSettings()->StartingTitle()); } { // title and command line -> no promotion NewTerminalArgs args{}; args.TabTitle(L"Digital Man"); args.Commandline(L"foo.exe"); - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) }; - VERIFY_ARE_EQUAL(L"Digital Man", settingsStruct.DefaultSettings().StartingTitle()); + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) }; + VERIFY_ARE_EQUAL(L"Digital Man", settingsStruct.DefaultSettings()->StartingTitle()); } { // just a commandline -> promotion NewTerminalArgs args{}; args.Commandline(L"foo.exe"); - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) }; - VERIFY_ARE_EQUAL(L"foo.exe", settingsStruct.DefaultSettings().StartingTitle()); + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) }; + VERIFY_ARE_EQUAL(L"foo.exe", settingsStruct.DefaultSettings()->StartingTitle()); } // various typesof commandline follow { NewTerminalArgs args{}; args.Commandline(L"foo.exe bar"); - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) }; - VERIFY_ARE_EQUAL(L"foo.exe", settingsStruct.DefaultSettings().StartingTitle()); + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) }; + VERIFY_ARE_EQUAL(L"foo.exe", settingsStruct.DefaultSettings()->StartingTitle()); } { NewTerminalArgs args{}; args.Commandline(L"\"foo exe.exe\" bar"); - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) }; - VERIFY_ARE_EQUAL(L"foo exe.exe", settingsStruct.DefaultSettings().StartingTitle()); + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) }; + VERIFY_ARE_EQUAL(L"foo exe.exe", settingsStruct.DefaultSettings()->StartingTitle()); } { NewTerminalArgs args{}; args.Commandline(L"\"\" grand designs"); - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) }; - VERIFY_ARE_EQUAL(L"", settingsStruct.DefaultSettings().StartingTitle()); + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) }; + VERIFY_ARE_EQUAL(L"", settingsStruct.DefaultSettings()->StartingTitle()); } { NewTerminalArgs args{}; args.Commandline(L" imagine a man"); - const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) }; - VERIFY_ARE_EQUAL(L"", settingsStruct.DefaultSettings().StartingTitle()); + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) }; + VERIFY_ARE_EQUAL(L"", settingsStruct.DefaultSettings()->StartingTitle()); } } } diff --git a/src/cascadia/UnitTests_SettingsModel/pch.h b/src/cascadia/UnitTests_SettingsModel/pch.h index 11f661ed88..4cad433c61 100644 --- a/src/cascadia/UnitTests_SettingsModel/pch.h +++ b/src/cascadia/UnitTests_SettingsModel/pch.h @@ -60,6 +60,8 @@ Author(s): #include #include +#include +#include // Manually include til after we include Windows.Foundation to give it winrt superpowers #include "til.h" diff --git a/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h b/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h index fa5a4d7078..4381800bd6 100644 --- a/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h +++ b/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h @@ -13,7 +13,7 @@ using namespace winrt::Microsoft::Terminal::Core; namespace TerminalCoreUnitTests { - class MockTermSettings : public winrt::implements + class MockTermSettings : public winrt::implements { // Color Table is special because it's an array std::array _ColorTable; @@ -32,14 +32,14 @@ namespace TerminalCoreUnitTests { } - winrt::Microsoft::Terminal::Core::Color GetColorTableEntry(int32_t index) noexcept + void SetColorTable(const std::array& colors) { - return _ColorTable.at(index); + _ColorTable = colors; } - void SetColorTableEntry(int32_t index, - winrt::Microsoft::Terminal::Core::Color color) noexcept + + void GetColorTable(winrt::com_array& table) noexcept { - _ColorTable.at(index) = color; + table = winrt::com_array(_ColorTable.begin(), _ColorTable.end()); } }; } diff --git a/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp b/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp index 1585fef905..d59ea73617 100644 --- a/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp @@ -549,6 +549,21 @@ namespace TerminalCoreUnitTests ValidateLinearSelection(term, { 0, 10 }, { term.GetViewport().RightExclusive(), 10 }); } + TEST_METHOD(TripleClick_WrappedLine) + { + Terminal term{ Terminal::TestDummyMarker{} }; + DummyRenderer renderer{ &term }; + term.Create({ 10, 5 }, 0, renderer); + term.Write(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + + // Simulate click at (x,y) = (3,1) + auto clickPos = til::point{ 3, 1 }; + term.MultiClickSelection(clickPos, Terminal::SelectionExpansion::Line); + + // Validate selection area + ValidateLinearSelection(term, { 0, 0 }, { term.GetViewport().RightExclusive(), 2 }); + } + TEST_METHOD(TripleClickDrag_Horizontal) { Terminal term{ Terminal::TestDummyMarker{} }; diff --git a/src/cascadia/WinRTUtils/LibraryResources.cpp b/src/cascadia/WinRTUtils/LibraryResources.cpp index 82c4acbc9f..66984a4d63 100644 --- a/src/cascadia/WinRTUtils/LibraryResources.cpp +++ b/src/cascadia/WinRTUtils/LibraryResources.cpp @@ -76,10 +76,10 @@ static void EnsureAllResourcesArePresent(const ScopedResourceLoader& loader) #endif -static ScopedResourceLoader GetLibraryResourceLoader() +const ScopedResourceLoader& GetLibraryResourceLoader() try { - ScopedResourceLoader loader{ g_WinRTUtilsLibraryResourceScope }; + static ScopedResourceLoader loader{ g_WinRTUtilsLibraryResourceScope }; #ifdef _DEBUG EnsureAllResourcesArePresent(loader); #endif @@ -90,15 +90,13 @@ CATCH_FAIL_FAST() winrt::hstring GetLibraryResourceString(const std::wstring_view key) try { - static auto loader{ GetLibraryResourceLoader() }; - return loader.GetLocalizedString(key); + return GetLibraryResourceLoader().GetLocalizedString(key); } CATCH_FAIL_FAST() bool HasLibraryResourceWithName(const std::wstring_view key) try { - static auto loader{ GetLibraryResourceLoader() }; - return loader.HasResourceWithName(key); + return GetLibraryResourceLoader().HasResourceWithName(key); } CATCH_FAIL_FAST() diff --git a/src/cascadia/WinRTUtils/ScopedResourceLoader.cpp b/src/cascadia/WinRTUtils/ScopedResourceLoader.cpp index e872a2d42b..4ad6482008 100644 --- a/src/cascadia/WinRTUtils/ScopedResourceLoader.cpp +++ b/src/cascadia/WinRTUtils/ScopedResourceLoader.cpp @@ -46,3 +46,14 @@ bool ScopedResourceLoader::HasResourceWithName(const std::wstring_view resourceN { return _resourceMap.HasKey(resourceName); } + +ScopedResourceLoader::ScopedResourceLoader(winrt::Windows::ApplicationModel::Resources::Core::ResourceMap map, winrt::Windows::ApplicationModel::Resources::Core::ResourceContext context) noexcept : + _resourceMap{ std::move(map) }, _resourceContext{ std::move(context) } {} + +ScopedResourceLoader ScopedResourceLoader::WithQualifier(const wil::zwstring_view qualifierName, const wil::zwstring_view qualifierValue) const +{ + auto newContext = _resourceContext.Clone(); + auto qualifierValues = newContext.QualifierValues(); + qualifierValues.Insert(qualifierName, qualifierValue); // mutable! + return ScopedResourceLoader{ _resourceMap, std::move(newContext) }; +} diff --git a/src/cascadia/WinRTUtils/inc/LibraryResources.h b/src/cascadia/WinRTUtils/inc/LibraryResources.h index 56a0b1b5e1..92d9563a8e 100644 --- a/src/cascadia/WinRTUtils/inc/LibraryResources.h +++ b/src/cascadia/WinRTUtils/inc/LibraryResources.h @@ -67,6 +67,9 @@ namespace Microsoft::Console::Utils __pragma(warning(suppress : 26485)); \ __declspec(selectany) extern const wchar_t* g_WinRTUtilsLibraryResourceScope{ (x) }; +class ScopedResourceLoader; + +const ScopedResourceLoader& GetLibraryResourceLoader(); winrt::hstring GetLibraryResourceString(const std::wstring_view key); bool HasLibraryResourceWithName(const std::wstring_view key); diff --git a/src/cascadia/WinRTUtils/inc/ScopedResourceLoader.h b/src/cascadia/WinRTUtils/inc/ScopedResourceLoader.h index f7664115e4..71a3b1cd58 100644 --- a/src/cascadia/WinRTUtils/inc/ScopedResourceLoader.h +++ b/src/cascadia/WinRTUtils/inc/ScopedResourceLoader.h @@ -11,7 +11,13 @@ public: winrt::hstring GetLocalizedString(const std::wstring_view resourceName) const; bool HasResourceWithName(const std::wstring_view resourceName) const; + ScopedResourceLoader WithQualifier(const wil::zwstring_view qualifierName, const wil::zwstring_view qualifierValue) const; + + const winrt::Windows::ApplicationModel::Resources::Core::ResourceMap& ResourceMap() const noexcept { return _resourceMap; } + const winrt::Windows::ApplicationModel::Resources::Core::ResourceContext& ResourceContext() const noexcept { return _resourceContext; } + private: + ScopedResourceLoader(winrt::Windows::ApplicationModel::Resources::Core::ResourceMap map, winrt::Windows::ApplicationModel::Resources::Core::ResourceContext context) noexcept; winrt::Windows::ApplicationModel::Resources::Core::ResourceMap _resourceMap; winrt::Windows::ApplicationModel::Resources::Core::ResourceContext _resourceContext; }; diff --git a/src/cascadia/WinRTUtils/inc/ThrottledFunc.h b/src/cascadia/WinRTUtils/inc/ThrottledFunc.h index 94c42b3362..3bcf1d87f9 100644 --- a/src/cascadia/WinRTUtils/inc/ThrottledFunc.h +++ b/src/cascadia/WinRTUtils/inc/ThrottledFunc.h @@ -3,40 +3,44 @@ #pragma once -#include "til/throttled_func.h" +#include // ThrottledFunc is a copy of til::throttled_func, // specialized for the use with a WinRT Dispatcher. -template -class ThrottledFunc : public std::enable_shared_from_this> +template +class ThrottledFunc : public std::enable_shared_from_this> { public: - using filetime_duration = std::chrono::duration>; + using filetime_duration = til::throttled_func_options::filetime_duration; using function = std::function; - // Throttles invocations to the given `func` to not occur more often than `delay`. + // Throttles invocations to the given `func` to not occur more often than specified in options. // - // If this is a: - // * ThrottledFuncLeading: `func` will be invoked immediately and - // further invocations prevented until `delay` time has passed. - // * ThrottledFuncTrailing: On the first invocation a timer of `delay` time will - // be started. After the timer has expired `func` will be invoked just once. + // Options: + // * delay: The minimum time between invocations + // * leading: If true, `func` will be invoked immediately on first call + // * trailing: If true, `func` will be invoked after the delay + // * debounce: If true, resets the timer on each call // - // After `func` was invoked the state is reset and this cycle is repeated again. - ThrottledFunc( - winrt::Windows::System::DispatcherQueue dispatcher, - filetime_duration delay, - function func) : + // At least one of leading or trailing must be true. + ThrottledFunc(winrt::Windows::System::DispatcherQueue dispatcher, til::throttled_func_options opts, function func) : _dispatcher{ std::move(dispatcher) }, _func{ std::move(func) }, - _timer{ _create_timer() } + _timer{ _create_timer() }, + _debounce{ opts.debounce }, + _leading{ opts.leading }, + _trailing{ opts.trailing } { - const auto d = -delay.count(); + if (!_leading && !_trailing) + { + throw std::invalid_argument("neither leading nor trailing"); + } + + const auto d = -opts.delay.count(); if (d >= 0) { throw std::invalid_argument("non-positive delay specified"); } - memcpy(&_delay, &d, sizeof(d)); } @@ -54,88 +58,119 @@ public: template void Run(MakeArgs&&... args) { - if (!_storage.emplace(std::forward(args)...)) - { - _leading_edge(); - } + _lead(std::make_tuple(std::forward(args)...)); } - // Modifies the pending arguments for the next function - // invocation, if there is one pending currently. - // - // `func` will be invoked as func(Args...). Make sure to bind any - // arguments in `func` by reference if you'd like to modify them. template void ModifyPending(F func) { - _storage.modify_pending(func); + const auto guard = _lock.lock_exclusive(); + if (_pendingRunArgs) + { + std::apply(func, *_pendingRunArgs); + } } private: static void __stdcall _timer_callback(PTP_CALLBACK_INSTANCE /*instance*/, PVOID context, PTP_TIMER /*timer*/) noexcept + try { - static_cast(context)->_trailing_edge(); + static_cast(context)->_trail(); } + CATCH_LOG() - void _leading_edge() - { - if constexpr (leading) - { - _dispatcher.TryEnqueue(winrt::Windows::System::DispatcherQueuePriority::Normal, [weakSelf = this->weak_from_this()]() { - if (auto self{ weakSelf.lock() }) - { - try - { - self->_func(); - } - CATCH_LOG(); - - SetThreadpoolTimerEx(self->_timer.get(), &self->_delay, 0, 0); - } - }); - } - else - { - SetThreadpoolTimerEx(_timer.get(), &_delay, 0, 0); - } - } - - void _trailing_edge() - { - if constexpr (leading) - { - _storage.reset(); - } - else - { - _dispatcher.TryEnqueue(winrt::Windows::System::DispatcherQueuePriority::Normal, [weakSelf = this->weak_from_this()]() { - if (auto self{ weakSelf.lock() }) - { - try - { - self->_storage.apply(self->_func); - } - CATCH_LOG(); - } - }); - } - } - - inline wil::unique_threadpool_timer _create_timer() + wil::unique_threadpool_timer _create_timer() { wil::unique_threadpool_timer timer{ CreateThreadpoolTimer(&_timer_callback, this, nullptr) }; THROW_LAST_ERROR_IF(!timer); return timer; } - FILETIME _delay; + void _lead(std::tuple args) + { + bool timerRunning = false; + + { + const auto guard = _lock.lock_exclusive(); + + timerRunning = _timerRunning; + _timerRunning = true; + + if (!timerRunning && _leading) + { + // Call the function immediately on the leading edge. + // See below (out of lock). + } + else if (_trailing) + { + _pendingRunArgs.emplace(std::move(args)); + } + } + + const auto execute = !timerRunning && _leading; + const auto schedule = !timerRunning || _debounce; + + if (execute) + { + _execute(std::move(args), schedule); + } + else if (schedule) + { + SetThreadpoolTimerEx(_timer.get(), &_delay, 0, 0); + } + } + + void _trail() + { + decltype(_pendingRunArgs) args; + + { + const auto guard = _lock.lock_exclusive(); + + _timerRunning = false; + args = std::exchange(_pendingRunArgs, std::nullopt); + } + + if (args) + { + _execute(std::move(*args), false); + } + } + + void _execute(std::tuple args, bool schedule) + { + _dispatcher.TryEnqueue( + winrt::Windows::System::DispatcherQueuePriority::Normal, + [weakSelf = this->weak_from_this(), args = std::move(args), schedule]() mutable { + if (auto self{ weakSelf.lock() }) + { + try + { + std::apply(self->_func, std::move(args)); + } + CATCH_LOG(); + + if (schedule) + { + SetThreadpoolTimerEx(self->_timer.get(), &self->_delay, 0, 0); + } + } + }); + } + winrt::Windows::System::DispatcherQueue _dispatcher; + + // Everything below this point is just like til::throttled_func. + function _func; - wil::unique_threadpool_timer _timer; - til::details::throttled_func_storage _storage; -}; + FILETIME _delay; -template -using ThrottledFuncTrailing = ThrottledFunc; -using ThrottledFuncLeading = ThrottledFunc; + wil::srwlock _lock; + std::optional> _pendingRunArgs; + bool _timerRunning = false; + + bool _debounce; + bool _leading; + bool _trailing; +}; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 6cdc7ff8e7..522e90c7d2 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -77,6 +77,11 @@ AppHost::AppHost(WindowEmperor* manager, const winrt::TerminalApp::AppLogic& log _windowCallbacks.ShouldExitFullscreen = _window->ShouldExitFullscreen({ &_windowLogic, &winrt::TerminalApp::TerminalWindow::RequestExitFullscreen }); _window->MakeWindow(); + + // Does window creation mean the window was activated (WM_ACTIVATE)? No. + // But it simplifies `WindowEmperor::_mostRecentWindow()`, because now the creation of a + // new window marks it as the most recent one immediately, even before it becomes active. + QueryPerformanceCounter(&_lastActivatedTime); } bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down) @@ -283,9 +288,12 @@ void AppHost::Initialize() // the PTY requesting a change to the window state and the Terminal // realizing it, but should mitigate issues where the Terminal and PTY get // de-sync'd. - _showHideWindowThrottler = std::make_shared>( + _showHideWindowThrottler = std::make_shared>( winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), - std::chrono::milliseconds(200), + til::throttled_func_options{ + .delay = std::chrono::milliseconds{ 200 }, + .trailing = true, + }, [this](const bool show) { _window->ShowWindowChanged(show); }); @@ -387,19 +395,10 @@ void AppHost::_revokeWindowCallbacks() // Method Description: // - Called every time when the active tab's title changes. We'll also fire off -// a window message so we can update the window's title on the main thread, -// though we'll only do so if the settings are configured for that. -// Arguments: -// - sender: unused -// - newTitle: the string to use as the new window title -// Return Value: -// - -void AppHost::_AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/, winrt::hstring newTitle) +// a window message so we can update the window's title on the main thread. +void AppHost::_AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { - if (_windowLogic.GetShowTitleInTitlebar()) - { - _window->UpdateTitle(newTitle); - } + _window->UpdateTitle(_windowLogic.Title()); } // The terminal page is responsible for persisting its own state, but it does @@ -746,7 +745,7 @@ void AppHost::_RaiseVisualBell(const winrt::Windows::Foundation::IInspectable&, // - delta: the wheel delta that triggered this event. // Return Value: // - -void AppHost::_WindowMouseWheeled(const winrt::Windows::Foundation::Point coord, const int32_t delta) +void AppHost::_WindowMouseWheeled(const winrt::Windows::Foundation::Point coord, const winrt::Microsoft::Terminal::Core::Point delta) { if (_windowLogic) { @@ -951,7 +950,7 @@ void AppHost::_updateTheme() _window->UseDarkTheme(_isActuallyDarkTheme(theme.RequestedTheme())); // Update the window frame. If `rainbowFrame:true` is enabled, then that - // will be used. Otherwise we'll try to use the `FrameBrush` set in the + // will be used. Otherwise, we'll try to use the `FrameBrush` set in the // terminal window, as that will have the right color for the ThemeColor for // this setting. If that value is null, then revert to the default frame // color. diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index be987ef0c7..379f876b94 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -39,7 +39,7 @@ private: std::unique_ptr _window; winrt::TerminalApp::AppLogic _appLogic{ nullptr }; winrt::TerminalApp::TerminalWindow _windowLogic{ nullptr }; - std::shared_ptr> _showHideWindowThrottler; + std::shared_ptr> _showHideWindowThrottler; SafeDispatcherTimer _frameTimer; LARGE_INTEGER _lastActivatedTime{}; winrt::guid _virtualDesktopId{}; @@ -73,7 +73,7 @@ private: void _RaiseVisualBell(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& arg); - void _WindowMouseWheeled(const winrt::Windows::Foundation::Point coord, const int32_t delta); + void _WindowMouseWheeled(const winrt::Windows::Foundation::Point coord, const winrt::Microsoft::Terminal::Core::Point delta); void _WindowActivated(bool activated); void _WindowMoved(); @@ -127,7 +127,7 @@ private: void _stopFrameTimer(); void _updateFrameColor(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&); - void _AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle); + void _AppTitleChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&); void _HandleRequestLaunchPosition(const winrt::Windows::Foundation::IInspectable& sender, winrt::TerminalApp::LaunchPositionRequest args); diff --git a/src/cascadia/WindowsTerminal/BaseWindow.h b/src/cascadia/WindowsTerminal/BaseWindow.h index 2b1f57c512..f780946282 100644 --- a/src/cascadia/WindowsTerminal/BaseWindow.h +++ b/src/cascadia/WindowsTerminal/BaseWindow.h @@ -9,7 +9,6 @@ class BaseWindow public: static constexpr UINT CM_UPDATE_TITLE = WM_USER + 0; - virtual ~BaseWindow() = 0; static T* GetThisFromHandle(HWND const window) noexcept { return reinterpret_cast(GetWindowLongPtr(window, GWLP_USERDATA)); @@ -37,7 +36,7 @@ public: return DefWindowProc(window, message, wparam, lparam); } - [[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept + [[nodiscard]] LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept { switch (message) { @@ -58,19 +57,19 @@ public: if (_minimized) { _minimized = false; - OnRestore(); + static_cast(this)->OnRestore(); } // We always need to fire the resize event, even when we're transitioning from minimized. // We might be transitioning directly from minimized to maximized, and we'll need // to trigger any size-related content changes. - OnResize(width, height); + static_cast(this)->OnResize(width, height); break; case SIZE_MINIMIZED: if (!_minimized) { _minimized = true; - OnMinimize(); + static_cast(this)->OnMinimize(); } break; default: @@ -109,10 +108,6 @@ public: return 0; } - virtual void OnResize(const UINT width, const UINT height) = 0; - virtual void OnMinimize() = 0; - virtual void OnRestore() = 0; - RECT GetWindowRect() const noexcept { RECT rc = { 0 }; @@ -204,13 +199,13 @@ protected: void _setupUserData() { - SetWindowLongPtr(_window.get(), GWLP_USERDATA, reinterpret_cast(this)); + SetWindowLongPtr(_window.get(), GWLP_USERDATA, reinterpret_cast(static_cast(this))); } // Method Description: // - This method is called when the window receives the WM_NCCREATE message. // Return Value: // - The value returned from the window proc. - [[nodiscard]] virtual LRESULT OnNcCreate(WPARAM wParam, LPARAM lParam) noexcept + [[nodiscard]] LRESULT OnNcCreate(WPARAM wParam, LPARAM lParam) noexcept { _setupUserData(); @@ -220,8 +215,3 @@ protected: return DefWindowProc(_window.get(), WM_NCCREATE, wParam, lParam); }; }; - -template -inline BaseWindow::~BaseWindow() -{ -} diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 80932f3322..55d46abd68 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -61,7 +61,10 @@ void IslandWindow::HideCursor() noexcept void IslandWindow::ShowCursorMaybe(const UINT message) noexcept { - if (_cursorHidden && (message == WM_ACTIVATE || message == WM_POINTERUPDATE)) + if (_cursorHidden && + (message == WM_ACTIVATE || + message == WM_POINTERUPDATE || + message == WM_NCPOINTERUPDATE)) { _cursorHidden = false; ShowCursor(TRUE); @@ -592,6 +595,7 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam WindowCloseButtonClicked.raise(); return 0; } + case WM_MOUSEHWHEEL: case WM_MOUSEWHEEL: try { @@ -620,7 +624,11 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam const auto scale = GetCurrentDpiScale(); const winrt::Windows::Foundation::Point real{ relative.x / scale, relative.y / scale }; - const auto wheelDelta = static_cast(HIWORD(wparam)); + winrt::Microsoft::Terminal::Core::Point wheelDelta{ 0, static_cast(HIWORD(wparam)) }; + if (message == WM_MOUSEHWHEEL) + { + std::swap(wheelDelta.X, wheelDelta.Y); + } // Raise an event, so any listeners can handle the mouse wheel event manually. MouseScrolled.raise(real, wheelDelta); @@ -1239,9 +1247,12 @@ void IslandWindow::_SetIsFullscreen(const bool fullscreenEnabled) // - void IslandWindow::SummonWindow(winrt::TerminalApp::SummonWindowBehavior args) { - auto actualDropdownDuration = args.DropdownDuration(); + const auto toggleVisibility = args ? args.ToggleVisibility() : false; + const auto toMonitor = args ? args.ToMonitor() : winrt::TerminalApp::MonitorBehavior::InPlace; + auto dropdownDuration = args ? args.DropdownDuration() : 0; + // If the user requested an animation, let's check if animations are enabled in the OS. - if (actualDropdownDuration > 0) + if (dropdownDuration > 0) { auto animationsEnabled = TRUE; SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &animationsEnabled, 0); @@ -1255,7 +1266,7 @@ void IslandWindow::SummonWindow(winrt::TerminalApp::SummonWindowBehavior args) // _globalActivateWindow/_globalDismissWindow might do if they think // there should be an animation (like making the window appear with // SetWindowPlacement rather than ShowWindow) - actualDropdownDuration = 0; + dropdownDuration = 0; } } @@ -1266,33 +1277,33 @@ void IslandWindow::SummonWindow(winrt::TerminalApp::SummonWindowBehavior args) // - activate the window // - else // - dismiss the window - if (args.ToggleVisibility() && GetForegroundWindow() == _window.get()) + if (toggleVisibility && GetForegroundWindow() == _window.get()) { auto handled = false; // They want to toggle the window when it is the FG window, and we are // the FG window. However, if we're on a different monitor than the // mouse, then we should move to that monitor instead of dismissing. - if (args.ToMonitor() == winrt::TerminalApp::MonitorBehavior::ToMouse) + if (toMonitor == winrt::TerminalApp::MonitorBehavior::ToMouse) { const til::rect cursorMonitorRect{ _getMonitorForCursor().rcMonitor }; const til::rect currentMonitorRect{ _getMonitorForWindow(GetHandle()).rcMonitor }; if (cursorMonitorRect != currentMonitorRect) { // We're not on the same monitor as the mouse. Go to that monitor. - _globalActivateWindow(actualDropdownDuration, args.ToMonitor()); + _globalActivateWindow(dropdownDuration, toMonitor); handled = true; } } if (!handled) { - _globalDismissWindow(actualDropdownDuration); + _globalDismissWindow(dropdownDuration); } } else { - _globalActivateWindow(actualDropdownDuration, args.ToMonitor()); + _globalActivateWindow(dropdownDuration, toMonitor); } } diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 97bdd0841d..83afeaf44f 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -19,7 +19,7 @@ public: static void ShowCursorMaybe(const UINT message) noexcept; IslandWindow() noexcept; - virtual ~IslandWindow() override; + ~IslandWindow(); virtual void MakeWindow() noexcept; virtual void Close(); @@ -27,13 +27,13 @@ public: virtual void OnSize(const UINT width, const UINT height); HWND GetInteropHandle() const; - [[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept override; + [[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept; - [[nodiscard]] LRESULT OnNcCreate(WPARAM wParam, LPARAM lParam) noexcept override; + [[nodiscard]] LRESULT OnNcCreate(WPARAM wParam, LPARAM lParam) noexcept; - void OnResize(const UINT width, const UINT height) override; - void OnMinimize() override; - void OnRestore() override; + void OnResize(const UINT width, const UINT height); + void OnMinimize(); + void OnRestore(); virtual void OnAppInitialized(); virtual void SetContent(winrt::Windows::UI::Xaml::UIElement content); virtual void OnApplicationThemeChanged(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme); @@ -74,7 +74,7 @@ public: til::event> DragRegionClicked; til::event> WindowCloseButtonClicked; - til::event> MouseScrolled; + til::event> MouseScrolled; til::event> WindowActivated; til::event> NotifyNotificationIconPressed; til::event> NotifyWindowHidden; diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp index 414e4043f8..95d971ecea 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp @@ -692,7 +692,7 @@ int NonClientIslandWindow::_GetResizeHandleHeight() const noexcept // If there's a taskbar on any side of the monitor, reduce our size // a little bit on that edge. // - // Note to future code archeologists: + // Note to future code archaeologists: // This doesn't seem to work for fullscreen on the primary display. // However, testing a bunch of other apps with fullscreen modes // and an auto-hiding taskbar has shown that _none_ of them diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h index bdf64f9840..6fc0761a3c 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h @@ -27,13 +27,13 @@ public: static constexpr const int topBorderVisibleHeight = 1; NonClientIslandWindow(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme) noexcept; - ~NonClientIslandWindow() override; + ~NonClientIslandWindow(); virtual void Close() override; void MakeWindow() noexcept override; virtual void OnSize(const UINT width, const UINT height) override; - [[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept override; + [[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept; virtual til::rect GetNonClientFrame(UINT dpi) const noexcept override; virtual til::size GetTotalNonClientExclusiveSize(UINT dpi) const noexcept override; diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index d0ab37175a..5b6c9fd833 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "AppHost.h" #include "resource.h" @@ -49,7 +50,7 @@ static std::vector commandlineToArgArray(const wchar_t* commandL } // Returns the length of a double-null encoded string *excluding* the trailing double-null character. -static std::wstring_view stringFromDoubleNullTerminated(const wchar_t* beg) +static wil::zwstring_view stringFromDoubleNullTerminated(const wchar_t* beg) { auto end = beg; @@ -57,7 +58,7 @@ static std::wstring_view stringFromDoubleNullTerminated(const wchar_t* beg) { } - return { beg, end }; + return { beg, gsl::narrow_cast(end - beg) }; } // Appends an uint32_t to a byte vector. @@ -78,36 +79,39 @@ static const uint8_t* deserializeUint32(const uint8_t* it, const uint8_t* end, u return it + sizeof(uint32_t); } -// Writes an uint32_t length prefix, followed by the string data, to the output vector. -static void serializeString(std::vector& out, std::wstring_view str) +// Writes a null-terminated string to `out`: A uint32_t length prefix, +// *including null byte*, followed by the string data, followed by the null-terminator. +static void serializeString(std::vector& out, wil::zwstring_view str) { const auto ptr = reinterpret_cast(str.data()); - const auto len = gsl::narrow(str.size()); - serializeUint32(out, len); + const auto len = str.size() + 1; + serializeUint32(out, gsl::narrow(len)); out.insert(out.end(), ptr, ptr + len * sizeof(wchar_t)); } -// Parses the next string from the input iterator. Performs bounds-checks. +// Counter-part to `serializeString`. Performs bounds-checks. // Returns an iterator that points past it. -static const uint8_t* deserializeString(const uint8_t* it, const uint8_t* end, std::wstring_view& str) +static const uint8_t* deserializeString(const uint8_t* it, const uint8_t* end, wil::zwstring_view& str) { uint32_t len; it = deserializeUint32(it, end, len); - if (static_cast(end - it) < len * sizeof(wchar_t)) + const auto bytes = static_cast(len) * sizeof(wchar_t); + + if (bytes == 0 || static_cast(end - it) < bytes) { throw std::out_of_range("Not enough data for string content"); } - str = { reinterpret_cast(it), len }; - return it + len * sizeof(wchar_t); + str = { reinterpret_cast(it), len - 1 }; + return it + bytes; } struct Handoff { - std::wstring_view args; - std::wstring_view env; - std::wstring_view cwd; + wil::zwstring_view args; + wil::zwstring_view env; + wil::zwstring_view cwd; uint32_t show; }; @@ -249,6 +253,29 @@ void WindowEmperor::CreateNewWindow(winrt::TerminalApp::WindowRequestedArgs args _windowCount += 1; _windows.emplace_back(std::move(host)); + + if (_windowCount == 1) + { + // The first CoreWindow is created implicitly by XAML and parented to the + // first XAML island. We parent it to our initial window for 2 reasons: + // * On Windows 10 the CoreWindow will show up as a visible window on the taskbar + // due to a WinUI bug, and this will hide it, because our initial window is hidden. + // * When we DestroyWindow() the island it will destroy the CoreWindow, + // and it's not possible to recreate it. That's also a WinUI bug. + // + // Note that this must be done after the first window (= first island) is created. + if (const auto coreWindow = winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread()) + { + if (const auto interop = coreWindow.try_as()) + { + HWND coreHandle = nullptr; + if (SUCCEEDED(interop->get_WindowHandle(&coreHandle)) && coreHandle) + { + SetParent(coreHandle, _window.get()); + } + } + } + } } AppHost* WindowEmperor::_mostRecentWindow() const noexcept @@ -272,7 +299,7 @@ AppHost* WindowEmperor::_mostRecentWindow() const noexcept void WindowEmperor::HandleCommandlineArgs(int nCmdShow) { std::wstring windowClassName; - windowClassName.reserve(47); // "Windows Terminal Preview Admin 0123456789012345" + windowClassName.reserve(64); // "Windows Terminal Preview Admin 0123456789012345 0123456789012345" #if defined(WT_BRANDING_RELEASE) windowClassName.append(L"Windows Terminal"); #elif defined(WT_BRANDING_PREVIEW) @@ -297,6 +324,18 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow) #endif } + { + wil::unique_handle processToken{ GetCurrentProcessToken() }; + const auto userTokenInfo{ wil::get_token_information(processToken.get()) }; + const auto sidLength{ GetLengthSid(userTokenInfo->User.Sid) }; + const auto hash{ til::hash(userTokenInfo->User.Sid, sidLength) }; +#ifdef _WIN64 + fmt::format_to(std::back_inserter(windowClassName), FMT_COMPILE(L" {:016x}"), hash); +#else + fmt::format_to(std::back_inserter(windowClassName), FMT_COMPILE(L" {:08x}"), hash); +#endif + } + // Windows Terminal is a single-instance application. Either acquire ownership // over the mutex, or hand off the command line to the existing instance. const auto mutex = acquireMutexOrAttemptHandoff(windowClassName.c_str(), nCmdShow); @@ -314,6 +353,7 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow) _createMessageWindow(windowClassName.c_str()); _setupGlobalHotkeys(); _checkWindowsForNotificationIcon(); + _setupSessionPersistence(_app.Logic().Settings().GlobalSettings().ShouldUsePersistedLayout()); // When the settings change, we'll want to update our global hotkeys // and our notification icon based on the new settings. @@ -323,6 +363,7 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow) _assertIsMainThread(); _setupGlobalHotkeys(); _checkWindowsForNotificationIcon(); + _setupSessionPersistence(args.NewSettings().GlobalSettings().ShouldUsePersistedLayout()); } }); @@ -343,20 +384,31 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow) for (const auto layout : layouts) { hstring args[] = { L"wt", L"-w", L"new", L"-s", winrt::to_hstring(startIdx) }; - _dispatchCommandline({ args, cwd, showCmd, env }); + _dispatchCommandlineCommon(args, cwd, env, showCmd); startIdx += 1; } } - // Create another window if needed: There aren't any yet, or we got an explicit command line. const auto args = commandlineToArgArray(GetCommandLineW()); - if (_windows.empty() || args.size() != 1) - { - _dispatchCommandline({ args, cwd, showCmd, env }); - } - // If we created no windows, e.g. because the args are "/?" we can just exit now. - _postQuitMessageIfNeeded(); + if (args.size() == 2 && args[1] == L"-Embedding") + { + // We were launched for ConPTY handoff. We have no windows and also don't want to exit. + // + // TODO: Here we could start a timer and exit after, say, 5 seconds + // if no windows are created. But that's a minor concern. + } + else + { + // Create another window if needed: There aren't any yet, OR we got an explicit command line. + if (_windows.empty() || args.size() != 1) + { + _dispatchCommandlineCommon(args, cwd, env, showCmd); + } + + // If we created no windows, e.g. because the args are "/?" we can just exit now. + _postQuitMessageIfNeeded(); + } } // ALWAYS change the _real_ CWD of the Terminal to system32, @@ -366,22 +418,17 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow) LOG_IF_WIN32_BOOL_FALSE(SetCurrentDirectoryW(system32.c_str())); } - // The first CoreWindow is created implicitly by XAML and parented to the - // first XAML island. We parent it to our initial window for 2 reasons: - // * On Windows 10 the CoreWindow will show up as a visible window on the taskbar - // due to a WinUI bug, and this will hide it, because our initial window is hidden. - // * When we DestroyWindow() the island it will destroy the CoreWindow, - // and it's not possible to recreate it. That's also a WinUI bug. - if (const auto coreWindow = winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread()) { - if (const auto interop = coreWindow.try_as()) - { - HWND coreHandle = nullptr; - if (SUCCEEDED(interop->get_WindowHandle(&coreHandle)) && coreHandle) - { - SetParent(coreHandle, _window.get()); - } - } + TerminalConnection::ConptyConnection::NewConnection([this](TerminalConnection::ConptyConnection conn) { + TerminalApp::CommandlineArgs args; + args.ShowWindowCommand(conn.ShowWindow()); + args.Connection(std::move(conn)); + _dispatchCommandline(std::move(args)); + _summonWindow(SummonWindowSelectionArgs{ + .SummonBehavior = nullptr, + }); + }); + TerminalConnection::ConptyConnection::StartInboundListener(); } // Main message loop. It pumps all windows. @@ -397,10 +444,24 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow) { if (!loggedInteraction) { +#if defined(WT_BRANDING_RELEASE) + constexpr uint8_t branding = 3; +#elif defined(WT_BRANDING_PREVIEW) + constexpr uint8_t branding = 2; +#elif defined(WT_BRANDING_CANARY) + constexpr uint8_t branding = 1; +#else + constexpr uint8_t branding = 0; +#endif + const uint8_t distribution = IsPackaged() ? 2 : + _app.Logic().Settings().IsPortableMode() ? 1 : + 0; TraceLoggingWrite( g_hWindowsTerminalProvider, "SessionBecameInteractive", TraceLoggingDescription("Event emitted when the session was interacted with"), + TraceLoggingValue(branding, "Branding"), + TraceLoggingValue(distribution, "Distribution"), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); loggedInteraction = true; @@ -488,6 +549,8 @@ void WindowEmperor::_dispatchSpecialKey(const MSG& msg) const void WindowEmperor::_dispatchCommandline(winrt::TerminalApp::CommandlineArgs args) { + _assertIsMainThread(); + const auto exitCode = args.ExitCode(); if (const auto msg = args.ExitMessage(); !msg.empty()) @@ -572,6 +635,16 @@ void WindowEmperor::_dispatchCommandline(winrt::TerminalApp::CommandlineArgs arg } } +void WindowEmperor::_dispatchCommandlineCommon(winrt::array_view args, wil::zwstring_view currentDirectory, wil::zwstring_view envString, uint32_t showWindowCommand) +{ + winrt::TerminalApp::CommandlineArgs c; + c.Commandline(args); + c.CurrentDirectory(currentDirectory); + c.CurrentEnvironment(envString); + c.ShowWindowCommand(showWindowCommand); + _dispatchCommandline(std::move(c)); +} + // This is an implementation-detail of _dispatchCommandline(). safe_void_coroutine WindowEmperor::_dispatchCommandlineCurrentDesktop(winrt::TerminalApp::CommandlineArgs args) { @@ -615,6 +688,8 @@ safe_void_coroutine WindowEmperor::_dispatchCommandlineCurrentDesktop(winrt::Ter bool WindowEmperor::_summonWindow(const SummonWindowSelectionArgs& args) const { + _assertIsMainThread(); + AppHost* window = nullptr; if (args.WindowID) @@ -655,6 +730,8 @@ bool WindowEmperor::_summonWindow(const SummonWindowSelectionArgs& args) const void WindowEmperor::_summonAllWindows() const { + _assertIsMainThread(); + TerminalApp::SummonWindowBehavior args; args.ToggleVisibility(false); @@ -792,6 +869,9 @@ LRESULT WindowEmperor::_messageHandler(HWND window, UINT const message, WPARAM c // Did the window counter get out of sync? It shouldn't. assert(_windowCount == gsl::narrow_cast(_windows.size())); + // !!! NOTE !!! + // At least theoretically the lParam pointer may be invalid. + // We should only access it if we find it in our _windows array. const auto host = reinterpret_cast(lParam); auto it = _windows.begin(); const auto end = _windows.end(); @@ -800,7 +880,15 @@ LRESULT WindowEmperor::_messageHandler(HWND window, UINT const message, WPARAM c { if (host == it->get()) { - host->Close(); + // NOTE: The AppHost destructor is highly non-trivial. + // + // It _may_ call into XAML, which _may_ pump the message loop, which would then recursively + // re-enter this function, which _may_ then handle another WM_CLOSE_TERMINAL_WINDOW, + // which would change the _windows array, and invalidate our iterator and crash. + // + // We can prevent this by deferring destruction until after the erase() call. + const auto strong = *it; + strong->Close(); _windows.erase(it); break; } @@ -879,11 +967,8 @@ LRESULT WindowEmperor::_messageHandler(HWND window, UINT const message, WPARAM c if (const auto cds = reinterpret_cast(lParam); cds->dwData == TERMINAL_HANDOFF_MAGIC) { const auto handoff = deserializeHandoffPayload(static_cast(cds->lpData), static_cast(cds->lpData) + cds->cbData); - const winrt::hstring args{ handoff.args }; - const winrt::hstring env{ handoff.env }; - const winrt::hstring cwd{ handoff.cwd }; - const auto argv = commandlineToArgArray(args.c_str()); - _dispatchCommandline({ argv, cwd, gsl::narrow_cast(handoff.show), env }); + const auto argv = commandlineToArgArray(handoff.args.c_str()); + _dispatchCommandlineCommon(argv, handoff.cwd, handoff.env, handoff.show); } return 0; case WM_HOTKEY: @@ -897,7 +982,8 @@ LRESULT WindowEmperor::_messageHandler(HWND window, UINT const message, WPARAM c RegisterApplicationRestart(nullptr, RESTART_NO_CRASH | RESTART_NO_HANG); return TRUE; case WM_ENDSESSION: - _forcePersistence = true; + _finalizeSessionPersistence(); + _skipPersistence = true; PostQuitMessage(0); return 0; default: @@ -921,21 +1007,53 @@ LRESULT WindowEmperor::_messageHandler(HWND window, UINT const message, WPARAM c return DefWindowProcW(window, message, wParam, lParam); } -void WindowEmperor::_finalizeSessionPersistence() const +void WindowEmperor::_setupSessionPersistence(bool enabled) { - const auto state = ApplicationState::SharedInstance(); + if (!enabled) + { + _persistStateTimer.Stop(); + return; + } + _persistStateTimer.Interval(std::chrono::minutes(5)); + _persistStateTimer.Tick([&](auto&&, auto&&) { + _persistState(ApplicationState::SharedInstance(), false); + }); + _persistStateTimer.Start(); +} - if (_forcePersistence || _app.Logic().Settings().GlobalSettings().ShouldUsePersistedLayout()) +void WindowEmperor::_persistState(const ApplicationState& state, bool serializeBuffer) const +{ + // Calling an `ApplicationState` setter triggers a write to state.json. + // With this if condition we avoid an unnecessary write when persistence is disabled. + if (state.PersistedWindowLayouts()) { state.PersistedWindowLayouts(nullptr); + } + + if (_app.Logic().Settings().GlobalSettings().ShouldUsePersistedLayout()) + { for (const auto& w : _windows) { - w->Logic().PersistState(); + w->Logic().PersistState(serializeBuffer); } } - // Ensure to write the state.json before we TerminateProcess() + // Ensure to write the state.json state.Flush(); +} + +void WindowEmperor::_finalizeSessionPersistence() const +{ + if (_skipPersistence) + { + // We received WM_ENDSESSION and persisted the state. + // We don't need to persist it again. + return; + } + + const auto state = ApplicationState::SharedInstance(); + + _persistState(state, true); if (_needsPersistenceCleanup) { @@ -1149,7 +1267,7 @@ void WindowEmperor::_hotkeyPressed(const long hotkeyIndex) const wil::unique_environstrings_ptr envMem{ GetEnvironmentStringsW() }; const auto env = stringFromDoubleNullTerminated(envMem.get()); const auto cwd = wil::GetCurrentDirectoryW(); - _dispatchCommandline({ argv, cwd, SW_SHOWDEFAULT, std::move(env) }); + _dispatchCommandlineCommon(argv, cwd, env, SW_SHOWDEFAULT); } void WindowEmperor::_registerHotKey(const int index, const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) noexcept diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 3024ab7a2c..c64b8c5fef 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -52,10 +52,10 @@ private: void _summonAllWindows() const; void _dispatchSpecialKey(const MSG& msg) const; void _dispatchCommandline(winrt::TerminalApp::CommandlineArgs args); + void _dispatchCommandlineCommon(winrt::array_view args, wil::zwstring_view currentDirectory, wil::zwstring_view envString, uint32_t showWindowCommand); safe_void_coroutine _dispatchCommandlineCurrentDesktop(winrt::TerminalApp::CommandlineArgs args); LRESULT _messageHandler(HWND window, UINT message, WPARAM wParam, LPARAM lParam) noexcept; void _createMessageWindow(const wchar_t* className); - bool _shouldSkipClosingWindows() const; void _postQuitMessageIfNeeded() const; safe_void_coroutine _showMessageBox(winrt::hstring message, bool error); void _notificationAreaMenuRequested(WPARAM wParam); @@ -64,6 +64,8 @@ private: void _registerHotKey(int index, const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) noexcept; void _unregisterHotKey(int index) noexcept; void _setupGlobalHotkeys(); + void _setupSessionPersistence(bool enabled); + void _persistState(const winrt::Microsoft::Terminal::Settings::Model::ApplicationState& state, bool serializeBuffer) const; void _finalizeSessionPersistence() const; void _checkWindowsForNotificationIcon(); @@ -75,20 +77,21 @@ private: UINT WM_TASKBARCREATED = 0; HMENU _currentWindowMenu = nullptr; bool _notificationIconShown = false; - bool _forcePersistence = false; + bool _skipPersistence = false; bool _needsPersistenceCleanup = false; + SafeDispatcherTimer _persistStateTimer; std::optional _currentSystemThemeIsDark; int32_t _windowCount = 0; int32_t _messageBoxCount = 0; -#ifdef NDEBUG +#if 0 // #ifdef NDEBUG static constexpr void _assertIsMainThread() noexcept { } #else void _assertIsMainThread() const noexcept { - assert(_mainThreadId == GetCurrentThreadId()); + WI_ASSERT_MSG(_mainThreadId == GetCurrentThreadId(), "This part of WindowEmperor must be accessed from the UI thread"); } DWORD _mainThreadId = GetCurrentThreadId(); #endif diff --git a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj index ef96673272..5135d643fb 100644 --- a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj +++ b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj @@ -13,6 +13,7 @@ true false Windows + Windows Terminal Host true diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index 583b67a0e4..87a0ade306 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -67,8 +67,9 @@ Abstract: #include #include -#include #include +#include +#include #include #include diff --git a/src/cascadia/WindowsTerminal_UIATests/WindowsTerminal.UIA.Tests.csproj b/src/cascadia/WindowsTerminal_UIATests/WindowsTerminal.UIA.Tests.csproj index f4cc6652f6..a67bd0a88c 100644 --- a/src/cascadia/WindowsTerminal_UIATests/WindowsTerminal.UIA.Tests.csproj +++ b/src/cascadia/WindowsTerminal_UIATests/WindowsTerminal.UIA.Tests.csproj @@ -6,7 +6,7 @@ Properties WindowsTerminal.UIA.Tests WindowsTerminal.UIA.Tests - v4.8 + v4.7.2 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10.0 diff --git a/src/cascadia/WpfTerminalControl/TerminalControl.xaml.cs b/src/cascadia/WpfTerminalControl/TerminalControl.xaml.cs index ab6a26d460..611cde52a6 100644 --- a/src/cascadia/WpfTerminalControl/TerminalControl.xaml.cs +++ b/src/cascadia/WpfTerminalControl/TerminalControl.xaml.cs @@ -103,7 +103,7 @@ namespace Microsoft.Terminal.Wpf } /// - /// Gets the selected text in the terminal, clearing the selection. Otherwise returns an empty string. + /// Gets the selected text in the terminal, clearing the selection. Otherwise, returns an empty string. /// /// Selected text, empty string if no content is selected. public string GetSelectedText() diff --git a/src/cascadia/WpfTerminalControl/WpfTerminalControl.csproj b/src/cascadia/WpfTerminalControl/WpfTerminalControl.csproj index 6581f1478b..518fe6b77b 100644 --- a/src/cascadia/WpfTerminalControl/WpfTerminalControl.csproj +++ b/src/cascadia/WpfTerminalControl/WpfTerminalControl.csproj @@ -1,8 +1,7 @@ - + - - net472;net6.0-windows + net472;net8.0-windows Microsoft.Terminal.Wpf Microsoft.Terminal.Wpf true @@ -15,6 +14,12 @@ 0.1 + + true + true + ..\..\..\build\config\272MSSharedLibSN2048.snk + + true diff --git a/src/cascadia/WpfTerminalTestNetCore/WpfTerminalTestNetCore.csproj b/src/cascadia/WpfTerminalTestNetCore/WpfTerminalTestNetCore.csproj index b3b839c485..29aa38bc09 100644 --- a/src/cascadia/WpfTerminalTestNetCore/WpfTerminalTestNetCore.csproj +++ b/src/cascadia/WpfTerminalTestNetCore/WpfTerminalTestNetCore.csproj @@ -2,8 +2,7 @@ WinExe - net6.0-windows - 6.0.9 + net8.0-windows true AnyCPU;x64;x86;ARM64 diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h index 42c3b1b9d3..ea33ad1b8e 100644 --- a/src/cascadia/inc/ControlProperties.h +++ b/src/cascadia/inc/ControlProperties.h @@ -14,7 +14,7 @@ X(til::color, SelectionBackground, DEFAULT_FOREGROUND) \ X(bool, IntenseIsBold) \ X(bool, IntenseIsBright, true) \ - X(winrt::Microsoft::Terminal::Core::AdjustTextMode, AdjustIndistinguishableColors, winrt::Microsoft::Terminal::Core::AdjustTextMode::Never) + X(winrt::Microsoft::Terminal::Core::AdjustTextMode, AdjustIndistinguishableColors, winrt::Microsoft::Terminal::Core::AdjustTextMode::Automatic) // --------------------------- Control Appearance --------------------------- // All of these settings are defined in IControlAppearance. @@ -42,6 +42,8 @@ X(winrt::hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS) \ X(bool, CopyOnSelect, false) \ X(bool, FocusFollowMouse, false) \ + X(bool, ScrollToZoom, true) \ + X(bool, ScrollToChangeOpacity, true) \ X(winrt::Windows::Foundation::IReference, TabColor, nullptr) \ X(winrt::Windows::Foundation::IReference, StartingTabColor, nullptr) \ X(bool, TrimBlockSelection, true) \ @@ -53,12 +55,11 @@ X(bool, RepositionCursorWithMouse, false) \ X(bool, RainbowSuggestions) \ X(bool, AllowVtChecksumReport) \ - X(bool, AllowVtClipboardWrite) + X(bool, AllowVtClipboardWrite, true) // --------------------------- Control Settings --------------------------- // All of these settings are defined in IControlSettings. #define CONTROL_SETTINGS(X) \ - X(winrt::hstring, ProfileName) \ X(winrt::guid, SessionId) \ X(bool, EnableUnfocusedAcrylic, false) \ X(winrt::hstring, Padding, DEFAULT_PADDING) \ @@ -71,7 +72,6 @@ X(bool, EnableColorGlyphs, true) \ X(winrt::hstring, CellWidth) \ X(winrt::hstring, CellHeight) \ - X(winrt::Microsoft::Terminal::Control::IKeyBindings, KeyBindings, nullptr) \ X(winrt::hstring, Commandline) \ X(winrt::hstring, StartingDirectory) \ X(winrt::Microsoft::Terminal::Control::ScrollbarState, ScrollState, winrt::Microsoft::Terminal::Control::ScrollbarState::Visible) \ diff --git a/src/cascadia/inc/cppwinrt_utils.h b/src/cascadia/inc/cppwinrt_utils.h index ecec48cef7..e7a8527818 100644 --- a/src/cascadia/inc/cppwinrt_utils.h +++ b/src/cascadia/inc/cppwinrt_utils.h @@ -213,6 +213,46 @@ protected: _##name = value; \ }; +// This macro defines a dependency property for a WinRT class. +// Use this in your class' header file after declaring it in the idl. +// Remember to register your dependency property in the respective cpp file. +#ifndef DEPENDENCY_PROPERTY +#define DEPENDENCY_PROPERTY(type, name) \ +public: \ + static winrt::Windows::UI::Xaml::DependencyProperty name##Property() \ + { \ + return _##name##Property; \ + } \ + type name() const \ + { \ + auto&& temp{ GetValue(_##name##Property) }; \ + if (temp) \ + { \ + return winrt::unbox_value(temp); \ + } \ + \ + if constexpr (std::is_same_v) \ + { \ + return winrt::hstring{}; \ + } \ + else if constexpr (std::is_base_of_v) \ + { \ + return { nullptr }; \ + } \ + else \ + { \ + return {}; \ + } \ + } \ + void name(const type& value) \ + { \ + SetValue(_##name##Property, winrt::box_value(value)); \ + } \ + \ +private: \ + static winrt::Windows::UI::Xaml::DependencyProperty _##name##Property; +#endif + // Use this macro for quickly defining the factory_implementation part of a // class. CppWinrt requires these for the compiler, but more often than not, // they require no customization. See diff --git a/src/cascadia/ut_app/ColorHelperTests.cpp b/src/cascadia/ut_app/ColorHelperTests.cpp deleted file mode 100644 index 03ec18d57e..0000000000 --- a/src/cascadia/ut_app/ColorHelperTests.cpp +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" - -#include "../TerminalSettingsModel/Profile.h" -#include "../TerminalApp/ColorHelper.h" - -// Import some templates to compare floats using approximate matching. -#include - -using namespace Microsoft::Console; -using namespace winrt::TerminalApp; -using namespace WEX::Logging; -using namespace WEX::TestExecution; -using namespace WEX::Common; - -namespace TerminalAppUnitTests -{ - class ColorHelperTests - { - BEGIN_TEST_CLASS(ColorHelperTests) - TEST_CLASS_PROPERTY(L"ActivationContext", L"TerminalApp.Unit.Tests.manifest") - END_TEST_CLASS() - - TEST_METHOD(ConvertRgbToHsl); - TEST_METHOD(ConvertHslToRgb); - TEST_METHOD(LuminanceTests); - }; - - void ColorHelperTests::ConvertHslToRgb() - { - auto red = winrt::Windows::UI::Colors::Red(); - auto redHsl = ColorHelper::RgbToHsl(red); - VERIFY_ARE_EQUAL(0.f, redHsl.H); - VERIFY_ARE_EQUAL(1.f, redHsl.S); - VERIFY_ARE_EQUAL(0.5f, redHsl.L); - - auto green = winrt::Windows::UI::Colors::Lime(); - auto greenHsl = ColorHelper::RgbToHsl(green); - VERIFY_ARE_EQUAL(120.f, greenHsl.H); - VERIFY_ARE_EQUAL(1.f, greenHsl.S); - VERIFY_ARE_EQUAL(0.5f, greenHsl.L); - - auto blue = winrt::Windows::UI::Colors::Blue(); - auto blueHsl = ColorHelper::RgbToHsl(blue); - VERIFY_ARE_EQUAL(240.f, blueHsl.H); - VERIFY_ARE_EQUAL(1.f, blueHsl.S); - VERIFY_ARE_EQUAL(0.5f, blueHsl.L); - - auto darkTurquoise = winrt::Windows::UI::Colors::DarkTurquoise(); - auto darkTurquoiseHsl = ColorHelper::RgbToHsl(darkTurquoise); - VERIFY_ARE_EQUAL(181.f, darkTurquoiseHsl.H); - VERIFY_ARE_EQUAL(1.f, darkTurquoiseHsl.S); - VERIFY_ARE_EQUAL(0.41f, darkTurquoiseHsl.L); - - auto darkViolet = winrt::Windows::UI::Colors::DarkViolet(); - auto darkVioletHsl = ColorHelper::RgbToHsl(darkViolet); - VERIFY_ARE_EQUAL(282.f, darkVioletHsl.H); - VERIFY_ARE_EQUAL(1.f, darkVioletHsl.S); - VERIFY_ARE_EQUAL(0.414f, darkVioletHsl.L); - - auto white = winrt::Windows::UI::Colors::White(); - auto whiteHsl = ColorHelper::RgbToHsl(white); - VERIFY_ARE_EQUAL(0.f, whiteHsl.H); - VERIFY_ARE_EQUAL(0.f, whiteHsl.S); - VERIFY_ARE_EQUAL(1.f, whiteHsl.L); - - auto black = winrt::Windows::UI::Colors::Black(); - auto blackHsl = ColorHelper::RgbToHsl(black); - VERIFY_ARE_EQUAL(0.f, blackHsl.H); - VERIFY_ARE_EQUAL(0.f, blackHsl.S); - VERIFY_ARE_EQUAL(0.f, blackHsl.L); - } - - void ColorHelperTests::ConvertRgbToHsl() - { - auto redHsl = HSL{ 0.f, 100.f, 50.f }; - auto red = ColorHelper::HslToRgb(redHsl); - VERIFY_ARE_EQUAL(255, red.R); - VERIFY_ARE_EQUAL(0, red.G); - VERIFY_ARE_EQUAL(0, red.B); - - auto greenHsl = HSL{ 120.f, 100.f, 50.f }; - auto green = ColorHelper::HslToRgb(greenHsl); - VERIFY_ARE_EQUAL(0, green.R); - VERIFY_ARE_EQUAL(255, green.G); - VERIFY_ARE_EQUAL(0, green.B); - - auto blueHsl = HSL{ 240.f, 100.f, 50.f }; - auto blue = ColorHelper::HslToRgb(blueHsl); - VERIFY_ARE_EQUAL(0, blue.R); - VERIFY_ARE_EQUAL(0, blue.G); - VERIFY_ARE_EQUAL(255, blue.B); - - auto darkTurquoiseHsl = HSL{ 181.f, 100.f, 41.f }; - auto darkTurquoise = ColorHelper::HslToRgb(darkTurquoiseHsl); - VERIFY_ARE_EQUAL(0, darkTurquoise.R); - VERIFY_ARE_EQUAL(206, darkTurquoise.G); - VERIFY_ARE_EQUAL(209, darkTurquoise.B); - - auto darkVioletHsl = HSL{ 282.f, 100.f, 41.4f }; - auto darkViolet = ColorHelper::HslToRgb(darkVioletHsl); - VERIFY_ARE_EQUAL(148, darkViolet.R); - VERIFY_ARE_EQUAL(0, darkViolet.G); - VERIFY_ARE_EQUAL(211, darkViolet.B); - - auto whiteHsl = HSL{ 360.f, 100.f, 100.f }; - auto white = ColorHelper::HslToRgb(whiteHsl); - VERIFY_ARE_EQUAL(255, white.R); - VERIFY_ARE_EQUAL(255, white.G); - VERIFY_ARE_EQUAL(255, white.B); - - auto blackHsl = HSL{ 0.f, 0.f, 0.f }; - auto black = ColorHelper::HslToRgb(blackHsl); - VERIFY_ARE_EQUAL(0, black.R); - VERIFY_ARE_EQUAL(0, black.G); - VERIFY_ARE_EQUAL(0, black.B); - } - - void ColorHelperTests::LuminanceTests() - { - auto darkTurquoiseLuminance = ColorHelper::GetLuminance(winrt::Windows::UI::Colors::DarkTurquoise()); - VERIFY_ARE_EQUAL(48.75f, darkTurquoiseLuminance * 100); - auto darkVioletLuminance = ColorHelper::GetLuminance(winrt::Windows::UI::Colors::DarkViolet()); - VERIFY_ARE_EQUAL(11.f, darkVioletLuminance * 100); - auto magentaLuminance = ColorHelper::GetLuminance(winrt::Windows::UI::Colors::Magenta()); - VERIFY_ARE_EQUAL(28.48f, magentaLuminance * 100); - } -} diff --git a/src/cascadia/ut_app/FzfTests.cpp b/src/cascadia/ut_app/FzfTests.cpp new file mode 100644 index 0000000000..e4f1fd1903 --- /dev/null +++ b/src/cascadia/ut_app/FzfTests.cpp @@ -0,0 +1,568 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" +#include "..\TerminalApp\fzf\fzf.h" + +using namespace Microsoft::Console; +using namespace WEX::Logging; +using namespace WEX::TestExecution; +using namespace WEX::Common; + +namespace TerminalAppUnitTests +{ + typedef enum + { + ScoreMatch = 16, + ScoreGapStart = -3, + ScoreGapExtension = -1, + BonusBoundary = ScoreMatch / 2, + BonusNonWord = ScoreMatch / 2, + BonusCamel123 = BonusBoundary + ScoreGapExtension, + BonusConsecutive = -(ScoreGapStart + ScoreGapExtension), + BonusFirstCharMultiplier = 2, + } score_t; + + class FzfTests + { + BEGIN_TEST_CLASS(FzfTests) + END_TEST_CLASS() + + TEST_METHOD(AllPatternCharsDoNotMatch); + TEST_METHOD(ConsecutiveChars); + TEST_METHOD(ConsecutiveChars_FirstCharBonus); + TEST_METHOD(NonWordBonusBoundary_ConsecutiveChars); + TEST_METHOD(MatchOnNonWordChars_CaseInSensitive); + TEST_METHOD(MatchOnNonWordCharsWithGap); + TEST_METHOD(BonusForCamelCaseMatch); + TEST_METHOD(BonusBoundaryAndFirstCharMultiplier); + TEST_METHOD(MatchesAreCaseInSensitive); + TEST_METHOD(MultipleTerms); + TEST_METHOD(MultipleTerms_AllCharsMatch); + TEST_METHOD(MultipleTerms_NotAllTermsMatch); + TEST_METHOD(MatchesAreCaseInSensitive_BonusBoundary); + TEST_METHOD(TraceBackWillPickTheFirstMatchIfBothHaveTheSameScore); + TEST_METHOD(TraceBackWillPickTheMatchWithTheHighestScore); + TEST_METHOD(TraceBackWillPickTheMatchWithTheHighestScore_Gaps); + TEST_METHOD(TraceBackWillPickEarlierCharsWhenNoBonus); + TEST_METHOD(MatchWithGapCanAHaveHigherScoreThanConsecutiveWhenGapMatchHasBoundaryBonus); + TEST_METHOD(ConsecutiveMatchWillScoreHigherThanMatchWithGapWhenBothHaveFirstCharBonus); + TEST_METHOD(ConsecutiveMatchWillScoreHigherThanMatchWithGapWhenBothDontHaveBonus); + TEST_METHOD(MatchWithGapCanHaveHigherScoreThanConsecutiveWhenGapHasFirstCharBonus); + TEST_METHOD(MatchWithGapThatMatchesOnTheFirstCharWillNoLongerScoreHigherThanConsecutiveCharsWhenTheGapIs3_NoConsecutiveChar_4CharPattern); + TEST_METHOD(MatchWithGapThatMatchesOnTheFirstCharWillNoLongerHigherScoreThanConsecutiveCharsWhenTheGapIs11_2CharPattern); + TEST_METHOD(MatchWithGapThatMatchesOnTheFirstCharWillNoLongerHigherScoreThanConsecutiveCharsWhenTheGapIs11_3CharPattern_1ConsecutiveChar); + TEST_METHOD(MatchWithGapThatMatchesOnTheFirstCharWillNoLongerHigherScoreThanConsecutiveCharsWhenTheGapIs5_NoConsecutiveChars_3CharPattern); + TEST_METHOD(Russian_CaseMisMatch); + TEST_METHOD(Russian_CaseMatch); + TEST_METHOD(English_CaseMatch); + TEST_METHOD(English_CaseMisMatch); + TEST_METHOD(SurrogatePair); + TEST_METHOD(French_CaseMatch); + TEST_METHOD(French_CaseMisMatch); + TEST_METHOD(German_CaseMatch); + TEST_METHOD(German_CaseMisMatch_FoldResultsInMultipleCodePoints); + TEST_METHOD(Greek_CaseMisMatch); + TEST_METHOD(Greek_CaseMatch); + TEST_METHOD(SurrogatePair_ToUtf16Pos_ConsecutiveChars); + TEST_METHOD(SurrogatePair_ToUtf16Pos_PreferConsecutiveChars); + TEST_METHOD(SurrogatePair_ToUtf16Pos_GapAndBoundary); + }; + + void AssertScoreAndRuns(std::wstring_view patternText, std::wstring_view text, int expectedScore, const std::vector& expectedRuns) + { + const auto pattern = fzf::matcher::ParsePattern(patternText); + const auto match = fzf::matcher::Match(text, pattern); + + if (expectedScore == 0 && expectedRuns.empty()) + { + VERIFY_ARE_EQUAL(std::nullopt, match); + return; + } + + VERIFY_IS_TRUE(match.has_value()); + VERIFY_ARE_EQUAL(expectedScore, match->Score); + + const auto& runs = match->Runs; + VERIFY_ARE_EQUAL(expectedRuns.size(), runs.size()); + + for (size_t i = 0; i < expectedRuns.size(); ++i) + { + VERIFY_ARE_EQUAL(expectedRuns[i].Start, runs[i].Start); + VERIFY_ARE_EQUAL(expectedRuns[i].End, runs[i].End); + } + } + + void FzfTests::AllPatternCharsDoNotMatch() + { + AssertScoreAndRuns( + L"fbb", + L"foo bar", + 0, + {}); + } + + void FzfTests::ConsecutiveChars() + { + AssertScoreAndRuns( + L"oba", + L"foobar", + ScoreMatch * 3 + BonusConsecutive * 2, + { { 2, 4 } }); + } + + void FzfTests::ConsecutiveChars_FirstCharBonus() + { + AssertScoreAndRuns( + L"foo", + L"foobar", + ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + BonusConsecutive * BonusFirstCharMultiplier * 2, + { { 0, 2 } }); + } + + void FzfTests::NonWordBonusBoundary_ConsecutiveChars() + { + AssertScoreAndRuns( + L"zshc", + L"/man1/zshcompctl.1", + ScoreMatch * 4 + BonusBoundary * BonusFirstCharMultiplier + BonusFirstCharMultiplier * BonusConsecutive * 3, + { { 6, 9 } }); + } + + void FzfTests::Russian_CaseMisMatch() + { + AssertScoreAndRuns( + L"новая", + L"Новая вкладка", + ScoreMatch * 5 + BonusBoundary * BonusFirstCharMultiplier + BonusConsecutive * BonusFirstCharMultiplier * 4, + { { 0, 4 } }); + } + + void FzfTests::Russian_CaseMatch() + { + AssertScoreAndRuns( + L"Новая", + L"Новая вкладка", + ScoreMatch * 5 + BonusBoundary * BonusFirstCharMultiplier + BonusConsecutive * BonusFirstCharMultiplier * 4, + { { 0, 4 } }); + } + + void FzfTests::German_CaseMatch() + { + AssertScoreAndRuns( + L"fuß", + L"Fußball", + ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + BonusConsecutive * BonusFirstCharMultiplier * 2, + { { 0, 2 } }); + } + + void FzfTests::German_CaseMisMatch_FoldResultsInMultipleCodePoints() + { + //This doesn't currently pass, I think ucase_toFullFolding would give the number of code points that resulted from the fold. + //I wasn't sure how to reference that + BEGIN_TEST_METHOD_PROPERTIES() + TEST_METHOD_PROPERTY(L"Ignore", L"true") + END_TEST_METHOD_PROPERTIES() + + AssertScoreAndRuns( + L"fuss", + L"Fußball", + //I think ScoreMatch * 4 is correct in this case since it matches 4 codepoints pattern??? fuss + ScoreMatch * 4 + BonusBoundary * BonusFirstCharMultiplier + BonusConsecutive * BonusFirstCharMultiplier * 3, + //Only 3 positions in the text were matched + { { 0, 2 } }); + } + + void FzfTests::French_CaseMatch() + { + AssertScoreAndRuns( + L"Éco", + L"École", + ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + BonusConsecutive * BonusFirstCharMultiplier * 2, + { { 0, 2 } }); + } + + void FzfTests::French_CaseMisMatch() + { + AssertScoreAndRuns( + L"Éco", + L"école", + ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + BonusConsecutive * BonusFirstCharMultiplier * 2, + { { 0, 2 } }); + } + + void FzfTests::Greek_CaseMatch() + { + AssertScoreAndRuns( + L"λόγος", + L"λόγος", + ScoreMatch * 5 + BonusBoundary * BonusFirstCharMultiplier + BonusConsecutive * BonusFirstCharMultiplier * 4, + { { 0, 4 } }); + } + + void FzfTests::Greek_CaseMisMatch() + { + //I think this tests validates folding (σ, ς) + AssertScoreAndRuns( + L"λόγοσ", + L"λόγος", + ScoreMatch * 5 + BonusBoundary * BonusFirstCharMultiplier + BonusConsecutive * BonusFirstCharMultiplier * 4, + { { 0, 4 } }); + } + + void FzfTests::English_CaseMatch() + { + AssertScoreAndRuns( + L"Newer", + L"Newer tab", + ScoreMatch * 5 + BonusBoundary * BonusFirstCharMultiplier + BonusConsecutive * BonusFirstCharMultiplier * 4, + { { 0, 4 } }); + } + + void FzfTests::English_CaseMisMatch() + { + AssertScoreAndRuns( + L"newer", + L"Newer tab", + ScoreMatch * 5 + BonusBoundary * BonusFirstCharMultiplier + BonusConsecutive * BonusFirstCharMultiplier * 4, + { { 0, 4 } }); + } + + void FzfTests::SurrogatePair() + { + AssertScoreAndRuns( + L"N😀ewer", + L"N😀ewer tab", + ScoreMatch * 6 + BonusBoundary * BonusFirstCharMultiplier + BonusConsecutive * BonusFirstCharMultiplier * 5, + { { 0, 6 } }); + } + + void FzfTests::SurrogatePair_ToUtf16Pos_ConsecutiveChars() + { + AssertScoreAndRuns( + L"N𠀋N😀𝄞e𐐷", + L"N𠀋N😀𝄞e𐐷 tab", + ScoreMatch * 7 + BonusBoundary * BonusFirstCharMultiplier + BonusConsecutive * BonusFirstCharMultiplier * 6, + { { 0, 10 } }); + } + + void FzfTests::SurrogatePair_ToUtf16Pos_PreferConsecutiveChars() + { + AssertScoreAndRuns( + L"𠀋😀", + L"N𠀋😀wer 😀b𐐷 ", + ScoreMatch * 2 + BonusConsecutive * 2, + { { 1, 4 } }); + } + + void FzfTests::SurrogatePair_ToUtf16Pos_GapAndBoundary() + { + AssertScoreAndRuns( + L"𠀋😀", + L"N𠀋wer 😀b𐐷 ", + ScoreMatch * 2 + ScoreGapStart + ScoreGapExtension * 3 + BonusBoundary, + { { 1, 2 }, { 7, 8 } }); + } + + void FzfTests::MatchOnNonWordChars_CaseInSensitive() + { + AssertScoreAndRuns( + L"foo-b", + L"xFoo-Bar Baz", + (ScoreMatch + BonusCamel123 * BonusFirstCharMultiplier) + + (ScoreMatch + BonusCamel123) + + (ScoreMatch + BonusCamel123) + + (ScoreMatch + BonusBoundary) + + (ScoreMatch + BonusNonWord), + { { 1, 5 } }); + } + + void FzfTests::MatchOnNonWordCharsWithGap() + { + AssertScoreAndRuns( + L"12356", + L"abc123 456", + (ScoreMatch + BonusCamel123 * BonusFirstCharMultiplier) + + (ScoreMatch + BonusCamel123) + + (ScoreMatch + BonusCamel123) + + ScoreGapStart + + ScoreGapExtension + + ScoreMatch + + ScoreMatch + BonusConsecutive, + { { 3, 5 }, { 8, 9 } }); + } + + void FzfTests::BonusForCamelCaseMatch() + { + AssertScoreAndRuns( + L"def56", + L"abcDEF 456", + (ScoreMatch + BonusCamel123 * BonusFirstCharMultiplier) + + (ScoreMatch + BonusCamel123) + + (ScoreMatch + BonusCamel123) + + ScoreGapStart + + ScoreGapExtension + + ScoreMatch + + (ScoreMatch + BonusConsecutive), + { { 3, 5 }, { 8, 9 } }); + } + + void FzfTests::BonusBoundaryAndFirstCharMultiplier() + { + AssertScoreAndRuns( + L"fbb", + L"foo bar baz", + ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + BonusBoundary * 2 + 2 * ScoreGapStart + 4 * ScoreGapExtension, + { { 0, 0 }, { 4, 4 }, { 8, 8 } }); + } + + void FzfTests::MatchesAreCaseInSensitive() + { + AssertScoreAndRuns( + L"FBB", + L"foo bar baz", + ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + BonusBoundary * 2 + 2 * ScoreGapStart + 4 * ScoreGapExtension, + { { 0, 0 }, { 4, 4 }, { 8, 8 } }); + } + + void FzfTests::MultipleTerms() + { + auto term1Score = ScoreMatch * 2 + BonusBoundary * BonusFirstCharMultiplier + (BonusFirstCharMultiplier * BonusConsecutive); + auto term2Score = ScoreMatch * 4 + BonusBoundary * BonusFirstCharMultiplier + (BonusFirstCharMultiplier * BonusConsecutive) * 3; + + AssertScoreAndRuns( + L"sp anta", + L"Split Pane, split: horizontal, profile: SSH: Antares", + term1Score + term2Score, + { { 0, 1 }, { 45, 48 } }); + } + + void FzfTests::MultipleTerms_AllCharsMatch() + { + auto term1Score = ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + (BonusFirstCharMultiplier * BonusConsecutive * 2); + auto term2Score = term1Score; + + AssertScoreAndRuns( + L"foo bar", + L"foo bar", + term1Score + term2Score, + { { 0, 2 }, { 4, 6 } }); + } + + void FzfTests::MultipleTerms_NotAllTermsMatch() + { + AssertScoreAndRuns( + L"sp anta zz", + L"Split Pane, split: horizontal, profile: SSH: Antares", + 0, + {}); + } + + void FzfTests::MatchesAreCaseInSensitive_BonusBoundary() + { + AssertScoreAndRuns( + L"fbb", + L"Foo Bar Baz", + ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + BonusBoundary * 2 + 2 * ScoreGapStart + 4 * ScoreGapExtension, + { { 0, 0 }, { 4, 4 }, { 8, 8 } }); + } + + void FzfTests::TraceBackWillPickTheFirstMatchIfBothHaveTheSameScore() + { + AssertScoreAndRuns( + L"bar", + L"Foo Bar Bar", + (ScoreMatch + BonusBoundary * BonusFirstCharMultiplier) + + (ScoreMatch + BonusBoundary) + + (ScoreMatch + BonusBoundary), + //ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier * 2, + { { 4, 6 } }); + } + + void FzfTests::TraceBackWillPickTheMatchWithTheHighestScore() + { + AssertScoreAndRuns( + L"bar", + L"Foo aBar Bar", + ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier * 2, + { { 9, 11 } }); + } + + void FzfTests::TraceBackWillPickTheMatchWithTheHighestScore_Gaps() + { + AssertScoreAndRuns( + L"bar", + L"Boo Author Raz Bar", + ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + BonusConsecutive * BonusFirstCharMultiplier * 2, + { { 15, 17 } }); + } + + void FzfTests::TraceBackWillPickEarlierCharsWhenNoBonus() + { + AssertScoreAndRuns( + L"clts", + L"close all tabs after this", + ScoreMatch * 4 + BonusBoundary * BonusFirstCharMultiplier + BonusFirstCharMultiplier * BonusConsecutive + ScoreGapStart + ScoreGapExtension * 7 + BonusBoundary + ScoreGapStart + ScoreGapExtension, + { { 0, 1 }, { 10, 10 }, { 13, 13 } }); + } + + void FzfTests::ConsecutiveMatchWillScoreHigherThanMatchWithGapWhenBothDontHaveBonus() + { + auto consecutiveScore = ScoreMatch * 3 + BonusConsecutive * 2; + auto gapScore = (ScoreMatch * 3) + ScoreGapStart + ScoreGapStart; + + AssertScoreAndRuns( + L"oob", + L"aoobar", + consecutiveScore, + { { 1, 3 } }); + + AssertScoreAndRuns( + L"oob", + L"aoaoabound", + gapScore, + { { 1, 1 }, { 3, 3 }, { 5, 5 } }); + + VERIFY_IS_GREATER_THAN(consecutiveScore, gapScore); + } + + void FzfTests::ConsecutiveMatchWillScoreHigherThanMatchWithGapWhenBothHaveFirstCharBonus() + { + auto consecutiveScore = ScoreMatch * 3 + BonusFirstCharMultiplier * BonusBoundary + BonusFirstCharMultiplier * BonusConsecutive * 2; + auto gapScore = (ScoreMatch * 3) + (BonusBoundary * BonusFirstCharMultiplier) + ScoreGapStart + ScoreGapStart; + + AssertScoreAndRuns( + L"oob", + L"oobar", + consecutiveScore, + { { 0, 2 } }); + + AssertScoreAndRuns( + L"oob", + L"oaoabound", + gapScore, + { { 0, 0 }, { 2, 2 }, { 4, 4 } }); + + VERIFY_IS_GREATER_THAN(consecutiveScore, gapScore); + } + + void FzfTests::MatchWithGapCanAHaveHigherScoreThanConsecutiveWhenGapMatchHasBoundaryBonus() + { + auto consecutiveScore = ScoreMatch * 3 + BonusConsecutive * 2; + auto gapScore = (ScoreMatch * 3) + (BonusBoundary * BonusFirstCharMultiplier) + (BonusBoundary * 2) + ScoreGapStart + (ScoreGapExtension * 2) + ScoreGapStart + ScoreGapExtension; + + AssertScoreAndRuns( + L"oob", + L"foobar", + consecutiveScore, + { { 1, 3 } }); + + AssertScoreAndRuns( + L"oob", + L"out-of-bound", + gapScore, + { { 0, 0 }, { 4, 4 }, { 7, 7 } }); + + VERIFY_IS_GREATER_THAN(gapScore, consecutiveScore); + } + + void FzfTests::MatchWithGapCanHaveHigherScoreThanConsecutiveWhenGapHasFirstCharBonus() + { + auto consecutiveScore = ScoreMatch * 2 + BonusConsecutive; + auto gapScore = ScoreMatch * 2 + BonusBoundary * BonusFirstCharMultiplier + ScoreGapStart; + + AssertScoreAndRuns( + L"ob", + L"aobar", + consecutiveScore, + { { 1, 2 } }); + + AssertScoreAndRuns( + L"ob", + L"oabar", + gapScore, + { { 0, 0 }, { 2, 2 } }); + + VERIFY_IS_GREATER_THAN(gapScore, consecutiveScore); + } + + void FzfTests::MatchWithGapThatMatchesOnTheFirstCharWillNoLongerHigherScoreThanConsecutiveCharsWhenTheGapIs11_2CharPattern() + { + auto consecutiveScore = ScoreMatch * 2 + BonusConsecutive; + auto gapScore = ScoreMatch * 2 + BonusBoundary * BonusFirstCharMultiplier + ScoreGapStart + ScoreGapExtension * 10; + + AssertScoreAndRuns( + L"ob", + L"aobar", + consecutiveScore, + { { 1, 2 } }); + + AssertScoreAndRuns( + L"ob", + L"oaaaaaaaaaaabar", + gapScore, + { { 0, 0 }, { 12, 12 } }); + + VERIFY_IS_GREATER_THAN(consecutiveScore, gapScore); + } + + void FzfTests::MatchWithGapThatMatchesOnTheFirstCharWillNoLongerHigherScoreThanConsecutiveCharsWhenTheGapIs11_3CharPattern_1ConsecutiveChar() + { + auto consecutiveScore = ScoreMatch * 3 + BonusConsecutive * 2; + auto gapScore = ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + BonusConsecutive + ScoreGapStart + ScoreGapExtension * 10; + + AssertScoreAndRuns( + L"oba", + L"aobar", + consecutiveScore, + { { 1, 3 } }); + + AssertScoreAndRuns( + L"oba", + L"oaaaaaaaaaaabar", + gapScore, + { { 0, 0 }, { 12, 13 } }); + + VERIFY_IS_GREATER_THAN(consecutiveScore, gapScore); + } + + void FzfTests::MatchWithGapThatMatchesOnTheFirstCharWillNoLongerHigherScoreThanConsecutiveCharsWhenTheGapIs5_NoConsecutiveChars_3CharPattern() + { + auto allConsecutiveScore = ScoreMatch * 3 + BonusConsecutive * 2; + auto allBoundaryWithGapScore = ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + ScoreGapStart + ScoreGapExtension + ScoreGapExtension + ScoreGapStart + ScoreGapExtension; + + AssertScoreAndRuns( + L"oba", + L"aobar", + allConsecutiveScore, + { { 1, 3 } }); + + AssertScoreAndRuns( + L"oba", + L"oaaabzzar", + allBoundaryWithGapScore, + { { 0, 0 }, { 4, 4 }, { 7, 7 } }); + + VERIFY_IS_GREATER_THAN(allConsecutiveScore, allBoundaryWithGapScore); + } + + void FzfTests::MatchWithGapThatMatchesOnTheFirstCharWillNoLongerScoreHigherThanConsecutiveCharsWhenTheGapIs3_NoConsecutiveChar_4CharPattern() + { + auto consecutiveScore = ScoreMatch * 4 + BonusConsecutive * 3; + auto gapScore = ScoreMatch * 4 + BonusBoundary * BonusFirstCharMultiplier + ScoreGapStart * 3; + + AssertScoreAndRuns( + L"obar", + L"aobar", + consecutiveScore, + { { 1, 4 } }); + + AssertScoreAndRuns( + L"obar", + L"oabzazr", + gapScore, + { { 0, 0 }, { 2, 2 }, { 4, 4 }, { 6, 6 } }); + + VERIFY_IS_GREATER_THAN(consecutiveScore, gapScore); + } +} diff --git a/src/cascadia/ut_app/TerminalApp.UnitTests.vcxproj b/src/cascadia/ut_app/TerminalApp.UnitTests.vcxproj index 705d5e3592..1eba2400bb 100644 --- a/src/cascadia/ut_app/TerminalApp.UnitTests.vcxproj +++ b/src/cascadia/ut_app/TerminalApp.UnitTests.vcxproj @@ -21,16 +21,11 @@ - - - + Create - - NotUsing - diff --git a/src/cascadia/wt/wt.vcxproj b/src/cascadia/wt/wt.vcxproj index 20e4b3f6f2..6d7edd9d1c 100644 --- a/src/cascadia/wt/wt.vcxproj +++ b/src/cascadia/wt/wt.vcxproj @@ -7,6 +7,7 @@ wt wt Application + Windows Terminal Launcher Shim diff --git a/src/common.build.pre.props b/src/common.build.pre.props index 4e2346eb05..b3f57bf230 100644 --- a/src/common.build.pre.props +++ b/src/common.build.pre.props @@ -262,7 +262,7 @@ FUZZING_BUILD;%(PreprocessorDefinitions) - libsancov.lib;clang_rt.asan-$(OCClangArchitectureName).lib;%(AdditionalDependencies) + libsancov.lib;clang_rt.asan_dynamic-$(OCClangArchitectureName).lib;%(AdditionalDependencies) @@ -279,12 +279,14 @@ arm64 --x-feature=terminal + $(VcpkgAdditionalInstallOptions) --overlay-triplets=$(SolutionDir)\dep\vcpkg-overlay-triplets\fuzzing false $(SolutionDir)\obj\$(Platform)\vcpkg + $(SolutionDir)\obj\$(Platform)\vcpkg-fuzzing $(VCPKG_ROOT) $(VsInstallRoot)\VC\vcpkg diff --git a/src/common.nugetversions.props b/src/common.nugetversions.props index 6e53f2cde2..ab545dcb3e 100644 --- a/src/common.nugetversions.props +++ b/src/common.nugetversions.props @@ -4,7 +4,7 @@ - + diff --git a/src/common.nugetversions.targets b/src/common.nugetversions.targets index 837c575fd8..186f933be3 100644 --- a/src/common.nugetversions.targets +++ b/src/common.nugetversions.targets @@ -41,13 +41,13 @@ - + - + @@ -78,14 +78,14 @@ - - + + - + diff --git a/src/cppwinrt.build.pre.props b/src/cppwinrt.build.pre.props index 7fae29094e..3a79da2dcb 100644 --- a/src/cppwinrt.build.pre.props +++ b/src/cppwinrt.build.pre.props @@ -77,7 +77,8 @@ Console - true + $(CppWinRTGenerateWindowsMetadata) + true diff --git a/src/features.xml b/src/features.xml index 569acb9d6e..d5c7942104 100644 --- a/src/features.xml +++ b/src/features.xml @@ -100,6 +100,11 @@ Enables the dynamic profile generator for OpenSSH config files 9031 AlwaysDisabled + + Dev + Canary + Preview + @@ -197,4 +202,15 @@ + + Feature_DisableWebSourceIcons + Disables icon paths that make web requests + 19075 + AlwaysDisabled + + Dev + Canary + + + diff --git a/src/host/ApiRoutines.h b/src/host/ApiRoutines.h index f6fc4ad4d8..9d6affe33b 100644 --- a/src/host/ApiRoutines.h +++ b/src/host/ApiRoutines.h @@ -56,12 +56,12 @@ public: const bool IsUnicode, const bool IsPeek, const bool IsWaitAllowed, - std::unique_ptr& waiter) noexcept override; + CONSOLE_API_MSG* pWaitReplyMessage) noexcept override; [[nodiscard]] HRESULT ReadConsoleImpl(IConsoleInputObject& context, std::span buffer, size_t& written, - std::unique_ptr& waiter, + CONSOLE_API_MSG* pWaitReplyMessage, const std::wstring_view initialData, const std::wstring_view exeName, INPUT_READ_HANDLE_DATA& readHandleState, @@ -73,12 +73,12 @@ public: [[nodiscard]] HRESULT WriteConsoleAImpl(IConsoleOutputObject& context, const std::string_view buffer, size_t& read, - std::unique_ptr& waiter) noexcept override; + CONSOLE_API_MSG* pWaitReplyMessage) noexcept override; [[nodiscard]] HRESULT WriteConsoleWImpl(IConsoleOutputObject& context, const std::wstring_view buffer, size_t& read, - std::unique_ptr& waiter) noexcept override; + CONSOLE_API_MSG* pWaitReplyMessage) noexcept override; #pragma region ThreadCreationInfo [[nodiscard]] HRESULT GetConsoleLangIdImpl(LANGID& langId) noexcept override; diff --git a/src/host/ConsoleArguments.cpp b/src/host/ConsoleArguments.cpp index d1ecdcb2b1..8241a62e0b 100644 --- a/src/host/ConsoleArguments.cpp +++ b/src/host/ConsoleArguments.cpp @@ -182,7 +182,7 @@ void ConsoleArguments::s_ConsumeArg(_Inout_ std::vector& args, _In // should be at (index+1). index will be decremented by one on success. // pSetting: receives the string at index+1 // Return Value: -// S_OK if we parsed the string successfully, otherwise E_INVALIDARG indicating +// S_OK if we parsed the string successfully; otherwise, E_INVALIDARG indicating // failure. [[nodiscard]] HRESULT ConsoleArguments::s_GetArgumentValue(_Inout_ std::vector& args, _Inout_ size_t& index, @@ -213,7 +213,7 @@ void ConsoleArguments::s_ConsumeArg(_Inout_ std::vector& args, _In // should be at (index+1). index will be decremented by one on success. // pSetting: receives the string at index+1 // Return Value: -// S_OK if we parsed the string successfully, otherwise E_INVALIDARG indicating +// S_OK if we parsed the string successfully; otherwise, E_INVALIDARG indicating // failure. [[nodiscard]] HRESULT ConsoleArguments::s_HandleFeatureValue(_Inout_ std::vector& args, _Inout_ size_t& index) { @@ -243,7 +243,7 @@ void ConsoleArguments::s_ConsumeArg(_Inout_ std::vector& args, _In // should be at (index+1). index will be decremented by one on success. // pSetting: receives the short at index+1 // Return Value: -// S_OK if we parsed the short successfully, otherwise E_INVALIDARG indicating +// S_OK if we parsed the short successfully; otherwise, E_INVALIDARG indicating // failure. This could be the case for non-numeric arguments, or for >SHORT_MAX args. [[nodiscard]] HRESULT ConsoleArguments::s_GetArgumentValue(_Inout_ std::vector& args, _Inout_ size_t& index, @@ -332,7 +332,7 @@ void ConsoleArguments::s_ConsumeArg(_Inout_ std::vector& args, _In // index: the index of the argument of which to start the commandline from. // skipFirst: if true, omit the arg at index (which should be "--") // Return Value: -// S_OK if we parsed the string successfully, otherwise E_INVALIDARG indicating +// S_OK if we parsed the string successfully; otherwise, E_INVALIDARG indicating // failure. [[nodiscard]] HRESULT ConsoleArguments::_GetClientCommandline(_Inout_ std::vector& args, const size_t index, const bool skipFirst) { @@ -369,7 +369,7 @@ void ConsoleArguments::s_ConsumeArg(_Inout_ std::vector& args, _In // Arguments: // // Return Value: -// S_OK if we parsed our _commandline successfully, otherwise E_INVALIDARG +// S_OK if we parsed our _commandline successfully; otherwise, E_INVALIDARG // indicating failure. [[nodiscard]] HRESULT ConsoleArguments::ParseCommandline() { diff --git a/src/host/PtySignalInputThread.cpp b/src/host/PtySignalInputThread.cpp index 2829ebdbcc..0b03612d9a 100644 --- a/src/host/PtySignalInputThread.cpp +++ b/src/host/PtySignalInputThread.cpp @@ -124,7 +124,13 @@ try } case PtySignal::ClearBuffer: { - _DoClearBuffer(); + ClearBufferData msg = { 0 }; + if (!_GetData(&msg, sizeof(msg))) + { + return S_OK; + } + + _DoClearBuffer(msg.keepCursorRow != 0); break; } case PtySignal::ResizeWindow: @@ -180,7 +186,7 @@ void PtySignalInputThread::_DoResizeWindow(const ResizeWindowData& data) _api.ResizeWindow(data.sx, data.sy); } -void PtySignalInputThread::_DoClearBuffer() const +void PtySignalInputThread::_DoClearBuffer(const bool keepCursorRow) const { LockConsole(); auto Unlock = wil::scope_exit([&] { UnlockConsole(); }); @@ -196,8 +202,11 @@ void PtySignalInputThread::_DoClearBuffer() const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); auto& screenInfo = gci.GetActiveOutputBuffer(); - auto& stateMachine = screenInfo.GetStateMachine(); - stateMachine.ProcessString(L"\x1b[H\x1b[2J"); + auto& tb = screenInfo.GetTextBuffer(); + const auto cursor = tb.GetCursor().GetPosition(); + + tb.ClearScrollback(cursor.y, keepCursorRow ? 1 : 0); + tb.GetCursor().SetPosition({ keepCursorRow ? cursor.x : 0, 0 }); } void PtySignalInputThread::_DoShowHide(const ShowHideData& data) diff --git a/src/host/PtySignalInputThread.hpp b/src/host/PtySignalInputThread.hpp index eead316ba9..9ff4fb0c22 100644 --- a/src/host/PtySignalInputThread.hpp +++ b/src/host/PtySignalInputThread.hpp @@ -55,6 +55,11 @@ namespace Microsoft::Console unsigned short show; // used as a bool, but passed as a ushort }; + struct ClearBufferData + { + unsigned short keepCursorRow; + }; + struct SetParentData { uint64_t handle; @@ -64,7 +69,7 @@ namespace Microsoft::Console [[nodiscard]] bool _GetData(_Out_writes_bytes_(cbBuffer) void* const pBuffer, const DWORD cbBuffer); void _DoResizeWindow(const ResizeWindowData& data); void _DoSetWindowParent(const SetParentData& data); - void _DoClearBuffer() const; + void _DoClearBuffer(bool keepCursorRow) const; void _DoShowHide(const ShowHideData& data); void _Shutdown(); diff --git a/src/host/VtIo.cpp b/src/host/VtIo.cpp index be1d8bd330..e2891ab7c6 100644 --- a/src/host/VtIo.cpp +++ b/src/host/VtIo.cpp @@ -73,7 +73,7 @@ using namespace Microsoft::Console::Interactivity; // SignalHandle: an optional file handle that will be used to send signals into the console. // This represents the ability to send signals to a *nix tty/pty. // Return Value: -// S_OK if we initialized successfully, otherwise an appropriate HRESULT +// S_OK if we initialized successfully; otherwise, an appropriate HRESULT // indicating failure. [[nodiscard]] HRESULT VtIo::_Initialize(const HANDLE InHandle, const HANDLE OutHandle, @@ -136,7 +136,7 @@ bool VtIo::IsUsingVt() const // Arguments: // // Return Value: -// S_OK if we started successfully or had nothing to start, otherwise an +// S_OK if we started successfully or had nothing to start; otherwise, an // appropriate HRESULT indicating failure. [[nodiscard]] HRESULT VtIo::StartIfNeeded() { @@ -226,6 +226,28 @@ bool VtIo::IsUsingVt() const return S_OK; } +void VtIo::Shutdown() noexcept +{ + if (_state != State::Running) + { + return; + } + + // The reverse of what we did in StartIfNeeded. + try + { + Writer writer{ this }; + + writer.WriteUTF8( + "\x1b[?1004l" // Focus Event Mode + "\x1b[?9001l" // Win32 Input Mode + ); + + writer.Submit(); + } + CATCH_LOG(); +} + void VtIo::SetDeviceAttributes(const til::enumset attributes) noexcept { _deviceAttributes = attributes; @@ -361,7 +383,7 @@ void VtIo::FormatAttributes(std::wstring& target, const TextAttribute& attribute wchar_t VtIo::SanitizeUCS2(wchar_t ch) { // If any of the values in the buffer are C0 or C1 controls, we need to - // convert them to printable codepoints, otherwise they'll end up being + // convert them to printable codepoints; otherwise, they'll end up being // evaluated as control characters by the receiving terminal. We use the // DOS 437 code page for the C0 controls and DEL, and just a `?` for the // C1 controls, since that's what you would most likely have seen in the diff --git a/src/host/VtIo.hpp b/src/host/VtIo.hpp index 6805fbc730..f2164a68c4 100644 --- a/src/host/VtIo.hpp +++ b/src/host/VtIo.hpp @@ -57,9 +57,9 @@ namespace Microsoft::Console::VirtualTerminal static wchar_t SanitizeUCS2(wchar_t ch); [[nodiscard]] HRESULT Initialize(const ConsoleArguments* const pArgs); - bool IsUsingVt() const; [[nodiscard]] HRESULT StartIfNeeded(); + void Shutdown() noexcept; void SetDeviceAttributes(til::enumset attributes) noexcept; til::enumset GetDeviceAttributes() const noexcept; diff --git a/src/host/_output.cpp b/src/host/_output.cpp index a075f5a303..fda7ae2792 100644 --- a/src/host/_output.cpp +++ b/src/host/_output.cpp @@ -110,15 +110,25 @@ static FillConsoleResult FillConsoleImpl(SCREEN_INFORMATION& screenInfo, FillCon switch (mode) { case FillConsoleMode::WriteAttribute: + { for (; columns < columnsAvailable && inputPos < lengthToWrite; ++columns, ++inputPos) { - infoBuffer[columns].Attributes = input[inputPos]; + // Overwrite all attributes except for the lead/trail byte markers. + // Those are used by WriteConsoleOutputWImplHelper to correctly serialize the input. + constexpr auto LT = COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE; + auto& attributes = infoBuffer[columns].Attributes; + attributes = (input[inputPos] & ~LT) | (attributes & LT); } break; + } case FillConsoleMode::FillAttribute: for (const auto attr = input[0]; columns < columnsAvailable && inputPos < lengthToWrite; ++columns, ++inputPos) { - infoBuffer[columns].Attributes = attr; + // Overwrite all attributes except for the lead/trail byte markers. + // Those are used by WriteConsoleOutputWImplHelper to correctly serialize the input. + constexpr auto LT = COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE; + auto& attributes = infoBuffer[columns].Attributes; + attributes = (attr & ~LT) | (attributes & LT); } break; case FillConsoleMode::WriteCharacter: diff --git a/src/host/_stream.cpp b/src/host/_stream.cpp index 85cadb1cfe..f337d6560e 100644 --- a/src/host/_stream.cpp +++ b/src/host/_stream.cpp @@ -97,17 +97,6 @@ static void AdjustCursorPosition(SCREEN_INFORMATION& screenInfo, _In_ til::point coordCursor.y = bufferSize.height - 1; } - const auto cursorMovedPastViewport = coordCursor.y > screenInfo.GetViewport().BottomInclusive(); - - // if at right or bottom edge of window, scroll right or down one char. - if (cursorMovedPastViewport) - { - til::point WindowOrigin; - WindowOrigin.x = 0; - WindowOrigin.y = coordCursor.y - screenInfo.GetViewport().BottomInclusive(); - LOG_IF_FAILED(screenInfo.SetViewportOrigin(false, WindowOrigin, true)); - } - LOG_IF_FAILED(screenInfo.SetCursorPosition(coordCursor, false)); } @@ -167,6 +156,8 @@ void WriteCharsLegacy(SCREEN_INFORMATION& screenInfo, const std::wstring_view& t auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); auto writer = gci.GetVtWriterForBuffer(&screenInfo); + const auto snap = screenInfo.SnapOnOutput(); + // If we enter this if condition, then someone wrote text in VT mode and now switched to non-VT mode. // Since the Console APIs don't support delayed EOL wrapping, we need to first put the cursor back // to a position that the Console APIs expect (= not delayed). @@ -269,17 +260,26 @@ void WriteCharsLegacy(SCREEN_INFORMATION& screenInfo, const std::wstring_view& t case UNICODE_LINEFEED: { auto pos = cursor.GetPosition(); - if (WI_IsFlagClear(screenInfo.OutputMode, DISABLE_NEWLINE_AUTO_RETURN) && pos.x != 0) + + // If DISABLE_NEWLINE_AUTO_RETURN is not set, any LF behaves like a CRLF. + if (WI_IsFlagClear(screenInfo.OutputMode, DISABLE_NEWLINE_AUTO_RETURN)) { pos.x = 0; - // This causes the current \n to be replaced with a \r\n in the ConPTY VT output. - wch = 0; - lastCharWrapped = true; + + // Setting wch=0 and lastCharWrapped=true will cause the code at the end + // of the loop to emit a CRLF. However, we only do this if the preceding + // character isn't already a CR. We don't want to emit CR CR LF after all. + if (it == beg || it[-1] != '\r') + { + wch = 0; + lastCharWrapped = true; + } } textBuffer.GetMutableRowByOffset(pos.y).SetWrapForced(false); pos.y = pos.y + 1; AdjustCursorPosition(screenInfo, pos, psScrollY); + break; } case UNICODE_CARRIAGERETURN: @@ -343,6 +343,8 @@ void WriteCharsVT(SCREEN_INFORMATION& screenInfo, const std::wstring_view& str) // may change, so get the VtIo reference now, just in case. auto writer = gci.GetVtWriterForBuffer(&screenInfo); + const auto snap = screenInfo.SnapOnOutput(); + stateMachine.ProcessString(str); if (writer) @@ -406,30 +408,9 @@ void WriteClearScreen(SCREEN_INFORMATION& screenInfo) // - pwchBuffer - wide character text to be inserted into buffer // - pcbBuffer - byte count of pwchBuffer on the way in, number of bytes consumed on the way out. // - screenInfo - Screen Information class to write the text into at the current cursor position -// - ppWaiter - If writing to the console is blocked for whatever reason, this will be filled with a pointer to context -// that can be used by the server to resume the call at a later time. -// Return Value: -// - STATUS_SUCCESS if OK. -// - CONSOLE_STATUS_WAIT if we couldn't finish now and need to be called back later (see ppWaiter). -// - Or a suitable NTSTATUS format error code for memory/string/math failures. -[[nodiscard]] NTSTATUS DoWriteConsole(_In_reads_bytes_(*pcbBuffer) PCWCHAR pwchBuffer, - _Inout_ size_t* const pcbBuffer, - SCREEN_INFORMATION& screenInfo, - std::unique_ptr& waiter) +[[nodiscard]] HRESULT DoWriteConsole(SCREEN_INFORMATION& screenInfo, std::wstring_view str) try { - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - if (WI_IsAnyFlagSet(gci.Flags, (CONSOLE_SUSPENDED | CONSOLE_SELECTING | CONSOLE_SCROLLBAR_TRACKING))) - { - waiter = std::make_unique(screenInfo, - pwchBuffer, - *pcbBuffer, - gci.OutputCP); - return CONSOLE_STATUS_WAIT; - } - - const std::wstring_view str{ pwchBuffer, *pcbBuffer / sizeof(WCHAR) }; - if (WI_IsAnyFlagClear(screenInfo.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT)) { WriteCharsLegacy(screenInfo, str, nullptr); @@ -438,55 +419,9 @@ try { WriteCharsVT(screenInfo, str); } - - return STATUS_SUCCESS; -} -NT_CATCH_RETURN() - -// Routine Description: -// - This method performs the actual work of attempting to write to the console, converting data types as necessary -// to adapt from the server types to the legacy internal host types. -// - It operates on Unicode data only. It's assumed the text is translated by this point. -// Arguments: -// - OutContext - the console output object to write the new text into -// - pwsTextBuffer - wide character text buffer provided by client application to insert -// - cchTextBufferLength - text buffer counted in characters -// - pcchTextBufferRead - character count of the number of characters we were able to insert before returning -// - ppWaiter - If we are blocked from writing now and need to wait, this is filled with contextual data for the server to restore the call later -// Return Value: -// - S_OK if successful. -// - S_OK if we need to wait (check if ppWaiter is not nullptr). -// - Or a suitable HRESULT code for math/string/memory failures. -[[nodiscard]] HRESULT WriteConsoleWImplHelper(IConsoleOutputObject& context, - const std::wstring_view buffer, - size_t& read, - std::unique_ptr& waiter) noexcept -{ - try - { - // Set out variables in case we exit early. - read = 0; - waiter.reset(); - - // Convert characters to bytes to give to DoWriteConsole. - size_t cbTextBufferLength; - RETURN_IF_FAILED(SizeTMult(buffer.size(), sizeof(wchar_t), &cbTextBufferLength)); - - auto Status = DoWriteConsole(const_cast(buffer.data()), &cbTextBufferLength, context, waiter); - - // Convert back from bytes to characters for the resulting string length written. - read = cbTextBufferLength / sizeof(wchar_t); - - if (Status == CONSOLE_STATUS_WAIT) - { - FAIL_FAST_IF_NULL(waiter.get()); - Status = STATUS_SUCCESS; - } - - RETURN_NTSTATUS(Status); - } - CATCH_RETURN(); + return S_OK; } +CATCH_RETURN() // Routine Description: // - Writes non-Unicode formatted data into the given console output object. @@ -505,13 +440,12 @@ NT_CATCH_RETURN() [[nodiscard]] HRESULT ApiRoutines::WriteConsoleAImpl(IConsoleOutputObject& context, const std::string_view buffer, size_t& read, - std::unique_ptr& waiter) noexcept + CONSOLE_API_MSG* pWaitReplyMessage) noexcept { try { // Ensure output variables are initialized. read = 0; - waiter.reset(); if (buffer.empty()) { @@ -611,67 +545,63 @@ NT_CATCH_RETURN() wstr.resize((dbcsLength + mbPtrLength) / sizeof(wchar_t)); } - // Hold the specific version of the waiter locally so we can tinker with it if we have to store additional context. - std::unique_ptr writeDataWaiter{}; - - // Make the W version of the call - size_t wcBufferWritten{}; - const auto hr{ WriteConsoleWImplHelper(screenInfo, wstr, wcBufferWritten, writeDataWaiter) }; - - // If there is no waiter, process the byte count now. - if (nullptr == writeDataWaiter.get()) + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + if (WI_IsAnyFlagSet(gci.Flags, (CONSOLE_SUSPENDED | CONSOLE_SELECTING | CONSOLE_SCROLLBAR_TRACKING))) { - // Calculate how many bytes of the original A buffer were consumed in the W version of the call to satisfy mbBufferRead. - // For UTF-8 conversions, we've already returned this information above. - if (CP_UTF8 != codepage) - { - size_t mbBufferRead{}; + const auto waiter = new WriteData(screenInfo, std::move(wstr), gci.OutputCP); - // Start by counting the number of A bytes we used in printing our W string to the screen. - try - { - mbBufferRead = GetALengthFromW(codepage, { wstr.data(), wcBufferWritten }); - } - CATCH_LOG(); - - // If we captured a byte off the string this time around up above, it means we didn't feed - // it into the WriteConsoleW above, and therefore its consumption isn't accounted for - // in the count we just made. Add +1 to compensate. - if (leadByteCaptured) - { - mbBufferRead++; - } - - // If we consumed an internally-stored lead byte this time around up above, it means that we - // fed a byte into WriteConsoleW that wasn't a part of this particular call's request. - // We need to -1 to compensate and tell the caller the right number of bytes consumed this request. - if (leadByteConsumed) - { - mbBufferRead--; - } - - read = mbBufferRead; - } - } - else - { // If there is a waiter, then we need to stow some additional information in the wait structure so // we can synthesize the correct byte count later when the wait routine is triggered. if (CP_UTF8 != codepage) { // For non-UTF8 codepages, save the lead byte captured/consumed data so we can +1 or -1 the final decoded count // in the WaitData::Notify method later. - writeDataWaiter->SetLeadByteAdjustmentStatus(leadByteCaptured, leadByteConsumed); + waiter->SetLeadByteAdjustmentStatus(leadByteCaptured, leadByteConsumed); } else { // For UTF8 codepages, just remember the consumption count from the UTF-8 parser. - writeDataWaiter->SetUtf8ConsumedCharacters(read); + waiter->SetUtf8ConsumedCharacters(read); } + + std::ignore = ConsoleWaitQueue::s_CreateWait(pWaitReplyMessage, waiter); + return CONSOLE_STATUS_WAIT; } - // Give back the waiter now that we're done with tinkering with it. - waiter.reset(writeDataWaiter.release()); + // Make the W version of the call + const auto hr = DoWriteConsole(screenInfo, wstr); + + // Calculate how many bytes of the original A buffer were consumed in the W version of the call to satisfy mbBufferRead. + // For UTF-8 conversions, we've already returned this information above. + if (CP_UTF8 != codepage) + { + size_t mbBufferRead{}; + + // Start by counting the number of A bytes we used in printing our W string to the screen. + try + { + mbBufferRead = GetALengthFromW(codepage, wstr); + } + CATCH_LOG(); + + // If we captured a byte off the string this time around up above, it means we didn't feed + // it into the WriteConsoleW above, and therefore its consumption isn't accounted for + // in the count we just made. Add +1 to compensate. + if (leadByteCaptured) + { + mbBufferRead++; + } + + // If we consumed an internally-stored lead byte this time around up above, it means that we + // fed a byte into WriteConsoleW that wasn't a part of this particular call's request. + // We need to -1 to compensate and tell the caller the right number of bytes consumed this request. + if (leadByteConsumed) + { + mbBufferRead--; + } + + read = mbBufferRead; + } return hr; } @@ -694,20 +624,24 @@ NT_CATCH_RETURN() [[nodiscard]] HRESULT ApiRoutines::WriteConsoleWImpl(IConsoleOutputObject& context, const std::wstring_view buffer, size_t& read, - std::unique_ptr& waiter) noexcept + CONSOLE_API_MSG* pWaitReplyMessage) noexcept { try { LockConsole(); auto unlock = wil::scope_exit([&] { UnlockConsole(); }); - std::unique_ptr writeDataWaiter; - RETURN_IF_FAILED(WriteConsoleWImplHelper(context.GetActiveBuffer(), buffer, read, writeDataWaiter)); + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + if (WI_IsAnyFlagSet(gci.Flags, (CONSOLE_SUSPENDED | CONSOLE_SELECTING | CONSOLE_SCROLLBAR_TRACKING))) + { + std::ignore = ConsoleWaitQueue::s_CreateWait(pWaitReplyMessage, new WriteData(context, std::wstring{ buffer }, gci.OutputCP)); + return CONSOLE_STATUS_WAIT; + } - // Transfer specific waiter pointer into the generic interface wrapper. - waiter.reset(writeDataWaiter.release()); - - return S_OK; + read = 0; + auto Status = DoWriteConsole(context, buffer); + read = buffer.size(); + return Status; } CATCH_RETURN(); } diff --git a/src/host/_stream.h b/src/host/_stream.h index 4651deb800..5953367d7a 100644 --- a/src/host/_stream.h +++ b/src/host/_stream.h @@ -25,7 +25,4 @@ void WriteClearScreen(SCREEN_INFORMATION& screenInfo); // NOTE: console lock must be held when calling this routine // String has been translated to unicode at this point. -[[nodiscard]] NTSTATUS DoWriteConsole(_In_reads_bytes_(pcbBuffer) const wchar_t* pwchBuffer, - _Inout_ size_t* const pcbBuffer, - SCREEN_INFORMATION& screenInfo, - std::unique_ptr& waiter); +[[nodiscard]] HRESULT DoWriteConsole(SCREEN_INFORMATION& screenInfo, std::wstring_view str); diff --git a/src/host/consoleInformation.cpp b/src/host/consoleInformation.cpp index d4e5926f6c..7146bee438 100644 --- a/src/host/consoleInformation.cpp +++ b/src/host/consoleInformation.cpp @@ -13,6 +13,7 @@ #include "srvinit.h" #include "../interactivity/inc/ServiceLocator.hpp" +#include "../interactivity/win32/CustomWindowMessages.h" #include "../types/inc/convert.hpp" using Microsoft::Console::Interactivity::ServiceLocator; @@ -179,6 +180,32 @@ void CONSOLE_INFORMATION::SetBracketedPasteMode(const bool enabled) noexcept _bracketedPasteMode = enabled; } +void CONSOLE_INFORMATION::CopyTextToClipboard(const std::wstring_view text) +{ + const auto window = ServiceLocator::LocateConsoleWindow(); + if (window) + { + // The clipboard can only be updated from the main GUI thread, so we + // need to post a message to trigger the actual copy operation. But if + // the pending clipboard content is already set, a message would have + // already been posted, so there's no need to post another one. + const auto clipboardMessageSent = _pendingClipboardText.has_value(); + _pendingClipboardText = text; + if (!clipboardMessageSent) + { + PostMessageW(window->GetWindowHandle(), CM_UPDATE_CLIPBOARD, 0, 0); + } + } +} + +std::optional CONSOLE_INFORMATION::UsePendingClipboardText() +{ + // Once the pending text has been used, we clear the variable to let the + // CopyTextToClipboard method know that the last CM_UPDATE_CLIPBOARD message + // has been processed, and future updates will require another message. + return std::exchange(_pendingClipboardText, {}); +} + // Method Description: // - Return the active screen buffer of the console. // Arguments: diff --git a/src/host/directio.cpp b/src/host/directio.cpp index c7e3c292da..aa55ee6c6c 100644 --- a/src/host/directio.cpp +++ b/src/host/directio.cpp @@ -58,12 +58,10 @@ using Microsoft::Console::Interactivity::ServiceLocator; const bool IsUnicode, const bool IsPeek, const bool IsWaitAllowed, - std::unique_ptr& waiter) noexcept + CONSOLE_API_MSG* pWaitReplyMessage) noexcept { try { - waiter.reset(); - if (eventReadCount == 0) { return STATUS_SUCCESS; @@ -83,9 +81,7 @@ using Microsoft::Console::Interactivity::ServiceLocator; { // If we're told to wait until later, move all of our context // to the read data object and send it back up to the server. - waiter = std::make_unique(&inputBuffer, - &readHandleState, - eventReadCount); + std::ignore = ConsoleWaitQueue::s_CreateWait(pWaitReplyMessage, new DirectReadData(&inputBuffer, &readHandleState, eventReadCount)); } return Status; } @@ -285,7 +281,7 @@ CATCH_RETURN(); // If .AsciiChar and .UnicodeChar have the same offset (since they're a union), // we can just write the latter with a byte-sized value to set the former // _and_ simultaneously clear the upper byte of .UnicodeChar to 0. Nice! - static_assert(offsetof(CHAR_INFO, Char.AsciiChar) == offsetof(CHAR_INFO, Char.UnicodeChar)); + static_assert(__builtin_offsetof(CHAR_INFO, Char.AsciiChar) == __builtin_offsetof(CHAR_INFO, Char.UnicodeChar)); // Any time we see the lead flag, we presume there will be a trailing one following it. // Giving us two bytes of space (one per cell in the ascii part of the character union) diff --git a/src/host/exe/Host.EXE.vcxproj b/src/host/exe/Host.EXE.vcxproj index f6151b95eb..3deb6f37f3 100644 --- a/src/host/exe/Host.EXE.vcxproj +++ b/src/host/exe/Host.EXE.vcxproj @@ -7,6 +7,7 @@ Host.EXE OpenConsole Application + Console Window and PTY Host (Open Source) true @@ -87,6 +88,7 @@ true winmm.lib;imm32.lib;%(AdditionalDependencies) + icu.dll;%(DelayLoadDLLs) diff --git a/src/host/exe/exemain.cpp b/src/host/exe/exemain.cpp index 2b95ba0916..054cdea490 100644 --- a/src/host/exe/exemain.cpp +++ b/src/host/exe/exemain.cpp @@ -175,7 +175,7 @@ static bool ShouldUseLegacyConhost(const ConsoleArguments& args) { // setup status error hr = HRESULT_FROM_WIN32(GetLastError()); - // fallback to V2 if conhostv1.dll cannot be loaded. + // fall back to V2 if conhostv1.dll cannot be loaded. useV2 = true; } diff --git a/src/host/exe/lto_symbols.inc b/src/host/exe/lto_symbols.inc new file mode 100644 index 0000000000..e29a4569a6 --- /dev/null +++ b/src/host/exe/lto_symbols.inc @@ -0,0 +1,3 @@ +LLVM_LTO_EXPORTED_SYMBOLS = \ + _tlgDefineProvider_annotation__Tlgg_ConhostLauncherProviderProv \ + wWinMain \ diff --git a/src/host/exe/sources b/src/host/exe/sources index c931044b13..76c7e0f101 100644 --- a/src/host/exe/sources +++ b/src/host/exe/sources @@ -1,3 +1,5 @@ +!include $(BASEDIR)\onecore\windows\core\console\console_clang_feature.inc + !include ..\sources.inc # ------------------------------------- # Windows Console @@ -20,6 +22,10 @@ UMTYPE = windows UMENTRY = wwinmain TARGET_DESTINATION = retail +!if "$(CLANG_LTO)" == "1" +!include lto_symbols.inc +!endif + # ------------------------------------- # Build System Settings # ------------------------------------- diff --git a/src/host/exe/sources.dep b/src/host/exe/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/host/exe/sources.dep +++ b/src/host/exe/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/host/ft_host/API_AliasTestsHelpers.hpp b/src/host/ft_host/API_AliasTestsHelpers.hpp index 9e2f751e7a..49dd8eb221 100644 --- a/src/host/ft_host/API_AliasTestsHelpers.hpp +++ b/src/host/ft_host/API_AliasTestsHelpers.hpp @@ -233,7 +233,7 @@ void TestGetConsoleAliasHelper(TCH* ptszSourceGiven, if (0 == dwExpectedLastError) { - // If it was successful, it should have been filled. Otherwise it will be zeroed as when it started. + // If it was successful, it should have been filled. Otherwise, it will be zeroed as when it started. StringCbCopyT(ptchExpectedTarget, cbTargetBuffer, ptszExpectedTargetGiven); } diff --git a/src/host/ft_host/sources.dep b/src/host/ft_host/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/host/ft_host/sources.dep +++ b/src/host/ft_host/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/host/ft_uia/Host.Tests.UIA.csproj b/src/host/ft_uia/Host.Tests.UIA.csproj index fcbb087bde..ce471cf7fd 100644 --- a/src/host/ft_uia/Host.Tests.UIA.csproj +++ b/src/host/ft_uia/Host.Tests.UIA.csproj @@ -6,7 +6,7 @@ Properties Conhost.UIA.Tests Conhost.UIA.Tests - v4.8 + v4.7.2 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10.0 diff --git a/src/host/ft_uia/app.config b/src/host/ft_uia/app.config index 7062d498c8..dc25723ee0 100644 --- a/src/host/ft_uia/app.config +++ b/src/host/ft_uia/app.config @@ -13,6 +13,6 @@ - + diff --git a/src/host/ft_uia/sources b/src/host/ft_uia/sources index 4cb5e3bd50..eefe4c4f3d 100644 --- a/src/host/ft_uia/sources +++ b/src/host/ft_uia/sources @@ -48,9 +48,9 @@ REFERENCES = $(CLR_REF_PATH)\System.metadata_dll; \ $(CLR_REF_PATH)\System.Core.metadata_dll; \ $(CLR_REF_PATH)\System.Data.metadata_dll; \ $(CLR_REF_PATH)\System.Drawing.metadata_dll; \ - $(ONECORESDKTOOLS_INTERNAL_REF_PATH_L)\wextest\cue\wex.common.managed.metadata_dll; \ - $(ONECORESDKTOOLS_INTERNAL_REF_PATH_L)\wextest\cue\wex.logger.interop.metadata_dll; \ - $(ONECORESDKTOOLS_INTERNAL_REF_PATH_L)\wextest\cue\te.managed.metadata_dll; \ + $(ONECORE_TAEF_REF_PATH)\net452\wex.common.managed.metadata_dll; \ + $(ONECORE_TAEF_REF_PATH)\net452\wex.logger.interop.metadata_dll; \ + $(ONECORE_TAEF_REF_PATH)\net452\te.managed.metadata_dll; \ $(ONECORESDKTOOLS_PRIVATE_REF_PATH_L)\WinAppDriver\appium-dotnet-driver.metadata_dll; \ $(ONECORESDKTOOLS_PRIVATE_REF_PATH_L)\WinAppDriver\castle.core.metadata_dll; \ $(ONECORESDKTOOLS_PRIVATE_REF_PATH_L)\WinAppDriver\newtonsoft.json.metadata_dll; \ diff --git a/src/host/input.cpp b/src/host/input.cpp index 952d3a0291..ee09771de0 100644 --- a/src/host/input.cpp +++ b/src/host/input.cpp @@ -108,7 +108,7 @@ bool ShouldTakeOverKeyboardShortcuts() void HandleGenericKeyEvent(INPUT_RECORD event, const bool generateBreak) { auto& keyEvent = event.Event.KeyEvent; - const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); auto ContinueProcessing = true; if (WI_IsAnyFlagSet(keyEvent.dwControlKeyState, CTRL_PRESSED) && @@ -167,6 +167,11 @@ void HandleGenericKeyEvent(INPUT_RECORD event, const bool generateBreak) keyEvent.bKeyDown = false; gci.pInputBuffer->Write(event); } + + if (gci.HasActiveOutputBuffer()) + { + gci.GetActiveOutputBuffer().SnapOnInput(keyEvent.wVirtualKeyCode); + } } } diff --git a/src/host/lib/sources.dep b/src/host/lib/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/host/lib/sources.dep +++ b/src/host/lib/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 3e8219a9bb..5ef852065f 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -84,7 +84,7 @@ void ConhostInternalGetSet::SetViewportPosition(const til::point position) THROW_IF_FAILED(info.SetViewportOrigin(true, position, true)); // SetViewportOrigin() only updates the virtual bottom (the bottom coordinate of the area // in the text buffer a VT client writes its output into) when it's moving downwards. - // But this function is meant to truly move the viewport no matter what. Otherwise `tput reset` breaks. + // But this function is meant to truly move the viewport no matter what. Otherwise, `tput reset` breaks. info.UpdateBottom(); } @@ -280,9 +280,9 @@ unsigned int ConhostInternalGetSet::GetInputCodePage() const // - content - the text to be copied. // Return Value: // - -void ConhostInternalGetSet::CopyToClipboard(const wil::zwstring_view /*content*/) +void ConhostInternalGetSet::CopyToClipboard(const wil::zwstring_view content) { - // TODO + ServiceLocator::LocateGlobals().getConsoleInformation().CopyTextToClipboard(content); } // Routine Description: @@ -446,6 +446,11 @@ void ConhostInternalGetSet::NotifyBufferRotation(const int delta) } } +void ConhostInternalGetSet::NotifyShellIntegrationMark() +{ + // Not implemented for conhost - shell integration marks are a Terminal app feature. +} + void ConhostInternalGetSet::InvokeCompletions(std::wstring_view /*menuJson*/, unsigned int /*replaceLength*/) { // Not implemented for conhost. diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 7b76e5f3d0..cfb1c85f16 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -67,6 +67,7 @@ public: void NotifyAccessibilityChange(const til::rect& changedRect) override; void NotifyBufferRotation(const int delta) override; + void NotifyShellIntegrationMark() override; void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override; diff --git a/src/host/readDataCooked.cpp b/src/host/readDataCooked.cpp index 8b88f2d4a3..dd3d246645 100644 --- a/src/host/readDataCooked.cpp +++ b/src/host/readDataCooked.cpp @@ -176,6 +176,11 @@ COOKED_READ_DATA::COOKED_READ_DATA(_In_ InputBuffer* const pInputBuffer, } } +const SCREEN_INFORMATION* COOKED_READ_DATA::GetScreenBuffer() const noexcept +{ + return &_screenInfo; +} + // Routine Description: // - This routine is called to complete a cooked read that blocked in ReadInputBuffer. // - The context of the read was saved in the CookedReadData structure. @@ -184,7 +189,7 @@ COOKED_READ_DATA::COOKED_READ_DATA(_In_ InputBuffer* const pInputBuffer, // - It may be called more than once. // Arguments: // - TerminationReason - if this routine is called because a ctrl-c or ctrl-break was seen, this argument -// contains CtrlC or CtrlBreak. If the owning thread is exiting, it will have ThreadDying. Otherwise 0. +// contains CtrlC or CtrlBreak. If the owning thread is exiting, it will have ThreadDying. Otherwise, 0. // - fIsUnicode - Whether to convert the final data to A (using Console Input CP) at the end or treat everything as Unicode (UCS-2) // - pReplyStatus - The status code to return to the client application that originally called the API (before it was queued to wait) // - pNumBytes - The number of bytes of data that the server/driver will need to transmit back to the client process @@ -1119,12 +1124,33 @@ void COOKED_READ_DATA::_redisplay() output.append(L"\x1b[J"); } - // Disable the cursor when opening a popup, reenable it when closing them. + // Backup the attributes (DECSC) and disable the cursor when opening a popup (DECTCEM). + // Restore the attributes (DECRC) reenable the cursor when closing them (DECTCEM). if (const auto popupOpened = !_popups.empty(); _popupOpened != popupOpened) { - wchar_t buf[] = L"\x1b[?25l"; - buf[5] = popupOpened ? 'l' : 'h'; - output.append(&buf[0], 6); + wchar_t buf[] = + // Back/restore cursor position & attributes (commonly supported) + L"\u001b7" + // Show/hide cursor (commonly supported) + "\u001b[?25l" + // The popup code uses XTPUSHSGR (CSI # {) / XTPOPSGR (CSI # }) to draw the popups in the popup-colors, + // while properly restoring the previous VT attributes. On terminals that support them, the following + // won't do anything. On other terminals however, it'll reset the attributes to default. + // This is important as the first thing the popup drawing code uses CSI K to erase the previous contents + // and CSI m to reset the attributes on terminals that don't support XTPUSHSGR/XTPOPSGR. In order for + // the first CSI K to behave as if there had a previous CSI m, we must emit an initial CSI m here. + // (rarely supported) + "\x1b[#{\x1b[m\x1b[#}"; + + buf[1] = popupOpened ? '7' : '8'; + buf[7] = popupOpened ? 'l' : 'h'; + + // When the popup closes we skip the XTPUSHSGR/XTPOPSGR sequence. This is crucial because we + // use DECRC to restore the cursor position and attributes with a widely supported sequence. + // If we emitted that XTPUSHSGR/XTPOPSGR sequence it would reset the attributes again. + const size_t len = popupOpened ? 19 : 8; + + output.append(buf, len); _popupOpened = popupOpened; } @@ -1595,10 +1621,10 @@ void COOKED_READ_DATA::_popupDrawPrompt(std::vector& lines, const til::Coo str.append(suffix); std::wstring line; - line.append(L"\x1b[K"); + line.append(L"\x1b[#{\x1b[K"); _appendPopupAttr(line); const auto res = _layoutLine(line, str, 0, 0, width); - line.append(L"\x1b[m"); + line.append(L"\x1b[m\x1b[#}"); lines.emplace_back(std::move(line), 0, 0, res.column); } @@ -1654,7 +1680,7 @@ void COOKED_READ_DATA::_popupDrawCommandList(std::vector& lines, const til const auto selected = index == cl.selected && !stackedCommandNumberPopup; std::wstring line; - line.append(L"\x1b[K"); + line.append(L"\x1b[#{\x1b[K"); _appendPopupAttr(line); wchar_t scrollbarChar = L' '; @@ -1681,7 +1707,7 @@ void COOKED_READ_DATA::_popupDrawCommandList(std::vector& lines, const til } else { - line.append(L"\x1b[m "); + line.append(L"\x1b[m\x1b[#} "); } fmt::format_to(std::back_inserter(line), FMT_COMPILE(L"{:{}}: "), index, indexWidth); @@ -1690,7 +1716,7 @@ void COOKED_READ_DATA::_popupDrawCommandList(std::vector& lines, const til if (selected) { - line.append(L"\x1b[m"); + line.append(L"\x1b[m\x1b[#}"); } line.append(L"\r\n"); diff --git a/src/host/readDataCooked.hpp b/src/host/readDataCooked.hpp index 6132ab0c37..eada0fe8e7 100644 --- a/src/host/readDataCooked.hpp +++ b/src/host/readDataCooked.hpp @@ -19,6 +19,7 @@ public: _In_ std::wstring_view initialData, _In_ ConsoleProcessHandle* pClientProcess); + const SCREEN_INFORMATION* GetScreenBuffer() const noexcept override; void MigrateUserBuffersOnTransitionToBackgroundWait(const void* oldBuffer, void* newBuffer) noexcept override; bool Notify(WaitTerminationReason TerminationReason, diff --git a/src/host/readDataDirect.cpp b/src/host/readDataDirect.cpp index 5009817763..ef36557093 100644 --- a/src/host/readDataDirect.cpp +++ b/src/host/readDataDirect.cpp @@ -36,7 +36,7 @@ DirectReadData::DirectReadData(_In_ InputBuffer* const pInputBuffer, // Arguments: // - TerminationReason - if this routine is called because a ctrl-c or // ctrl-break was seen, this argument contains CtrlC or CtrlBreak. If -// the owning thread is exiting, it will have ThreadDying. Otherwise 0. +// the owning thread is exiting, it will have ThreadDying. Otherwise, 0. // - fIsUnicode - Should we return UCS-2 unicode data, or should we // run the final data through the current Input Codepage before // returning? diff --git a/src/host/readDataRaw.cpp b/src/host/readDataRaw.cpp index 9351382c07..8c83143244 100644 --- a/src/host/readDataRaw.cpp +++ b/src/host/readDataRaw.cpp @@ -48,7 +48,7 @@ RAW_READ_DATA::~RAW_READ_DATA() = default; // Arguments: // - TerminationReason - if this routine is called because a ctrl-c or // ctrl-break was seen, this argument contains CtrlC or CtrlBreak. If -// the owning thread is exiting, it will have ThreadDying. Otherwise 0. +// the owning thread is exiting, it will have ThreadDying. Otherwise, 0. // - fIsUnicode - Whether to convert the final data to A (using // Console Input CP) at the end or treat everything as Unicode (UCS-2) // - pReplyStatus - The status code to return to the client diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index 3e1ae49d4b..136ccc4f1f 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -192,6 +192,9 @@ void SCREEN_INFORMATION::s_InsertScreenBuffer(_In_ SCREEN_INFORMATION* const pSc void SCREEN_INFORMATION::s_RemoveScreenBuffer(_In_ SCREEN_INFORMATION* const pScreenInfo) { auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + + gci.GetActiveInputBuffer()->WaitQueue.CancelWaitersForScreenBuffer(pScreenInfo); + if (pScreenInfo == gci.ScreenBuffers) { gci.ScreenBuffers = pScreenInfo->Next; @@ -1221,7 +1224,7 @@ void SCREEN_INFORMATION::_AdjustViewportSize(const til::rect* const prcClientNew const til::size* const pcoordSize) { // If the left is the only one that changed (and not the right - // also), then adjust from the left. Otherwise if the right + // also), then adjust from the left. Otherwise, if the right // changes or both changed, bias toward leaving the top-left // corner in place and resize from the bottom right. // -- @@ -1668,39 +1671,71 @@ void SCREEN_INFORMATION::SetCursorDBMode(const bool DoubleCursor) return STATUS_SUCCESS; } -void SCREEN_INFORMATION::MakeCursorVisible(const til::point CursorPosition) +static constexpr bool IsInputKey(WORD vkey) noexcept { - til::point WindowOrigin; + return vkey != VK_CONTROL && + vkey != VK_LCONTROL && + vkey != VK_RCONTROL && + vkey != VK_MENU && + vkey != VK_LMENU && + vkey != VK_RMENU && + vkey != VK_SHIFT && + vkey != VK_LSHIFT && + vkey != VK_RSHIFT && + vkey != VK_LWIN && + vkey != VK_RWIN && + vkey != VK_SNAPSHOT; +} - if (CursorPosition.x > _viewport.RightInclusive()) - { - WindowOrigin.x = CursorPosition.x - _viewport.RightInclusive(); - } - else if (CursorPosition.x < _viewport.Left()) - { - WindowOrigin.x = CursorPosition.x - _viewport.Left(); - } - else - { - WindowOrigin.x = 0; - } +void SCREEN_INFORMATION::MakeCursorVisible(til::point position) +{ + const auto viewportOrigin = _viewport.Origin(); + const auto viewportSize = _viewport.Dimensions(); + const auto bufferSize = _textBuffer->GetSize().Dimensions(); + auto origin = viewportOrigin; - if (CursorPosition.y > _viewport.BottomInclusive()) - { - WindowOrigin.y = CursorPosition.y - _viewport.BottomInclusive(); - } - else if (CursorPosition.y < _viewport.Top()) - { - WindowOrigin.y = CursorPosition.y - _viewport.Top(); - } - else - { - WindowOrigin.y = 0; - } + // Ensure the given position is in bounds. + position.x = std::clamp(position.x, 0, bufferSize.width - 1); + position.y = std::clamp(position.y, 0, bufferSize.height - 1); - if (WindowOrigin.x != 0 || WindowOrigin.y != 0) + origin.y = std::min(origin.y, position.y); // shift up if above + origin.y = std::max(origin.y, position.y - (viewportSize.height - 1)); // shift down if below + + origin.x = std::min(origin.x, position.x); // shift left if left + origin.x = std::max(origin.x, position.x - (viewportSize.width - 1)); // shift right if right + + if (origin != viewportOrigin) { - LOG_IF_FAILED(SetViewportOrigin(false, WindowOrigin, false)); + std::ignore = SetViewportOrigin(true, origin, false); + } +} + +void SCREEN_INFORMATION::SnapOnInput(const WORD vkey) +{ + if (IsInputKey(vkey)) + { + _makeCursorVisible(); + } +} + +SCREEN_INFORMATION::SnapOnScopeExit SCREEN_INFORMATION::SnapOnOutput() noexcept +{ + const auto call = + // We don't need to snap-on-output the alt buffer since it doesn't scroll anyway. + // More importantly though, in the current architecture the alt buffer gets deallocated + // the second we receive a \033[?1049l, which makes our this pointer hazardous to use. + _psiMainBuffer == nullptr && + // We only want to snap if the user didn't intentionally scroll away. + // The snapping logic could be improved, but it's alright for now. + _viewport.IsInBounds(_textBuffer->GetCursor().GetPosition()); + return SnapOnScopeExit{ call ? this : nullptr }; +} + +void SCREEN_INFORMATION::_makeCursorVisible() +{ + if (_textBuffer->GetCursor().IsVisible()) + { + MakeCursorVisible(_textBuffer->GetCursor().GetPosition()); } } @@ -2407,7 +2442,7 @@ const FontInfo& SCREEN_INFORMATION::GetCurrentFont() const noexcept // Method Description: // - Gets the desired font of the screen buffer. If we try loading this font and -// have to fallback to another, then GetCurrentFont()!=GetDesiredFont(). +// have to fall back to another, then GetCurrentFont()!=GetDesiredFont(). // We store this separately, so that if we need to reload the font, we can // try again with our preferred font info (in the desired font info) instead // of re-using the looked up value from before. diff --git a/src/host/screenInfo.hpp b/src/host/screenInfo.hpp index c7685cb1c0..d713bfed60 100644 --- a/src/host/screenInfo.hpp +++ b/src/host/screenInfo.hpp @@ -50,6 +50,25 @@ class ConversionAreaInfo; // forward decl window. circular reference class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console::IIoProvider { public: + // This little helper works like wil::scope_exit but is slimmer + // (= easier to optimize) and has a concrete type (= can declare). + struct SnapOnScopeExit + { + ~SnapOnScopeExit() + { + if (self) + { + try + { + self->_makeCursorVisible(); + } + CATCH_LOG(); + } + } + + SCREEN_INFORMATION* self; + }; + [[nodiscard]] static NTSTATUS CreateInstance(_In_ til::size coordWindowSize, const FontInfo fontInfo, _In_ til::size coordScreenBufferSize, @@ -71,6 +90,9 @@ public: void GetRequiredConsoleSizeInPixels(_Out_ til::size* const pRequiredSize) const; void MakeCurrentCursorVisible(); + void MakeCursorVisible(til::point position); + void SnapOnInput(WORD vkey); + SnapOnScopeExit SnapOnOutput() noexcept; void ClipToScreenBuffer(_Inout_ til::inclusive_rect* const psrClip) const; @@ -184,8 +206,6 @@ public: void SetCursorDBMode(const bool DoubleCursor); [[nodiscard]] NTSTATUS SetCursorPosition(const til::point Position, const bool TurnOn); - void MakeCursorVisible(const til::point CursorPosition); - [[nodiscard]] NTSTATUS UseAlternateScreenBuffer(const TextAttribute& initAttributes); void UseMainScreenBuffer(); @@ -230,6 +250,7 @@ private: void _CalculateViewportSize(const til::rect* const prcClientArea, _Out_ til::size* const pcoordSize); void _AdjustViewportSize(const til::rect* const prcClientNew, const til::rect* const prcClientOld, const til::size* const pcoordSize); void _InternalSetViewportSize(const til::size* pcoordSize, const bool fResizeFromTop, const bool fResizeFromLeft); + void _makeCursorVisible(); static void s_CalculateScrollbarVisibility(const til::rect* const prcClientArea, const til::size* const pcoordBufferSize, diff --git a/src/host/selection.cpp b/src/host/selection.cpp index d7dcf21ebe..fd815e082a 100644 --- a/src/host/selection.cpp +++ b/src/host/selection.cpp @@ -415,7 +415,7 @@ void Selection::ClearSelection(const bool fStartingNewSelection) // If we were using alternate selection, cancel it here before starting a new area. d->fUseAlternateSelection = false; - // Only unblock if we're not immediately starting a new selection. Otherwise stay blocked. + // Only unblock if we're not immediately starting a new selection. Otherwise, stay blocked. if (!fStartingNewSelection) { UnblockWriteConsole(CONSOLE_SELECTING); @@ -428,9 +428,9 @@ void Selection::ClearSelection(const bool fStartingNewSelection) // - This does not validate whether there is a valid selection right now or not. // It is assumed to already be in a proper selecting state and the given rectangle should be highlighted with the given color unconditionally. // Arguments: -// - psrRect - Rectangular area to fill with color +// - psrRect - Rectangular area to fill with color (exclusive) // - attr - The color attributes to apply -void Selection::ColorSelection(const til::inclusive_rect& srRect, const TextAttribute attr) +void Selection::ColorSelection(const til::rect& srRect, const TextAttribute attr) { auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); @@ -438,8 +438,8 @@ void Selection::ColorSelection(const til::inclusive_rect& srRect, const TextAttr auto& screenInfo = gci.GetActiveOutputBuffer(); til::point coordTargetSize; - coordTargetSize.x = CalcWindowSizeX(srRect); - coordTargetSize.y = CalcWindowSizeY(srRect); + coordTargetSize.x = srRect.width(); + coordTargetSize.y = srRect.height(); til::point coordTarget; coordTarget.x = srRect.left; @@ -475,9 +475,9 @@ void Selection::ColorSelection(const til::point coordSelectionStart, const til:: const auto& screenInfo = gci.GetActiveOutputBuffer(); const auto rectangles = screenInfo.GetTextBuffer().GetTextRects(coordSelectionStart, coordSelectionEnd, false, true); - for (const auto& rect : rectangles) + for (const auto& inclusiveRect : rectangles) { - ColorSelection(rect, attr); + ColorSelection(til::rect{ inclusiveRect }, attr); } } CATCH_LOG(); diff --git a/src/host/selection.hpp b/src/host/selection.hpp index 8be598651a..7ff876929f 100644 --- a/src/host/selection.hpp +++ b/src/host/selection.hpp @@ -58,7 +58,7 @@ public: void ClearSelection(); void ClearSelection(const bool fStartingNewSelection); - void ColorSelection(const til::inclusive_rect& srRect, const TextAttribute attr); + void ColorSelection(const til::rect& srRect, const TextAttribute attr); void ColorSelection(const til::point coordSelectionStart, const til::point coordSelectionEnd, const TextAttribute attr); // delete these or we can accidentally get copies of the singleton diff --git a/src/host/selectionInput.cpp b/src/host/selectionInput.cpp index 9a118ba08c..d7f08d28bf 100644 --- a/src/host/selectionInput.cpp +++ b/src/host/selectionInput.cpp @@ -698,7 +698,7 @@ bool Selection::_HandleColorSelection(const INPUT_KEY_INFO* const pInputKeyInfo) for (auto&& sp : selection) { sp.iterate_rows(textBuffer.GetSize().Width(), [&](til::CoordType row, til::CoordType beg, til::CoordType end) { - ColorSelection({ beg, row, end, row }, selectionAttr); + ColorSelection({ beg, row, end, row + 1 }, selectionAttr); }); } ClearSelection(); diff --git a/src/host/selectionState.cpp b/src/host/selectionState.cpp index 2504800bd9..c74e21dddd 100644 --- a/src/host/selectionState.cpp +++ b/src/host/selectionState.cpp @@ -220,6 +220,23 @@ std::pair Selection::GetSelectionAnchors() const noexcep endSelectionAnchor.x = (_d->coordSelectionAnchor.x == _d->srSelectionRect.left) ? _d->srSelectionRect.right : _d->srSelectionRect.left; endSelectionAnchor.y = (_d->coordSelectionAnchor.y == _d->srSelectionRect.top) ? _d->srSelectionRect.bottom : _d->srSelectionRect.top; + // GH #18106: Conhost and Terminal share most of the selection code. + // Both now store the selection data as a half-open range [start, end), + // where "end" is the bottom-right-most point. + // Conhost operates as an inclusive range, so we need to adjust the "end" endpoint by incrementing it by one. + const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + const auto& bufferSize = gci.GetActiveOutputBuffer().GetTextBuffer().GetSize(); + if (IsLineSelection()) + { + // General comparison for line selection. + bufferSize.IncrementInExclusiveBounds(startSelectionAnchor <= endSelectionAnchor ? endSelectionAnchor : startSelectionAnchor); + } + else + { + // Compare x-values when we're in block selection! + bufferSize.IncrementInExclusiveBounds(startSelectionAnchor.x <= endSelectionAnchor.x ? endSelectionAnchor : startSelectionAnchor); + } + if (startSelectionAnchor > endSelectionAnchor) { return { endSelectionAnchor, startSelectionAnchor }; diff --git a/src/host/server.h b/src/host/server.h index 0a0f905777..d318808a8c 100644 --- a/src/host/server.h +++ b/src/host/server.h @@ -126,6 +126,8 @@ public: bool GetBracketedPasteMode() const noexcept; void SetBracketedPasteMode(const bool enabled) noexcept; + void CopyTextToClipboard(const std::wstring_view text); + std::optional UsePendingClipboardText(); void SetTitle(const std::wstring_view newTitle); void SetTitlePrefix(const std::wstring_view newTitlePrefix); @@ -160,15 +162,16 @@ private: SCREEN_INFORMATION* pCurrentScreenBuffer = nullptr; COOKED_READ_DATA* _cookedReadData = nullptr; // non-ownership pointer bool _bracketedPasteMode = false; + std::optional _pendingClipboardText; Microsoft::Console::VirtualTerminal::VtIo _vtIo; Microsoft::Console::CursorBlinker _blinker; MidiAudio _midiAudio; }; -#define CONSOLE_STATUS_WAIT 0xC0030001 -#define CONSOLE_STATUS_READ_COMPLETE 0xC0030002 -#define CONSOLE_STATUS_WAIT_NO_BLOCK 0xC0030003 +#define CONSOLE_STATUS_WAIT ((HRESULT)0xC0030001) +#define CONSOLE_STATUS_READ_COMPLETE ((HRESULT)0xC0030002) +#define CONSOLE_STATUS_WAIT_NO_BLOCK ((HRESULT)0xC0030003) #include "../server/ObjectHandle.h" diff --git a/src/host/sources.inc b/src/host/sources.inc index e8c540fd80..2d78134309 100644 --- a/src/host/sources.inc +++ b/src/host/sources.inc @@ -89,10 +89,12 @@ SOURCES = \ # Sources, Headers, and Libraries # ------------------------------------- +!if "$(USE_CLANG)" != "1" PRECOMPILED_CXX = 1 PRECOMPILED_INCLUDE = ..\precomp.h PRECOMPILED_PCH = precomp.pch PRECOMPILED_OBJ = precomp.obj +!endif INCLUDES = \ $(INCLUDES); \ @@ -132,7 +134,7 @@ TARGETLIBS = \ $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\dxgi.lib \ $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\d3d11.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\api-ms-win-mm-playsound-l1.lib \ - $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-imm-l1-1-0.lib \ + $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-imm-l1.lib \ $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-dwmapi-ext-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-create-l1.lib \ @@ -160,12 +162,14 @@ TARGETLIBS = \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-sysparams-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-window-ext-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-winstamin-l1.lib \ + $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-syscolors-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-shell-shell32-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-uxtheme-themes-l1.lib \ $(ONECORESHELL_INTERNAL_LIB_VPATH_L)\api-ms-win-shell-dataobject-l1.lib \ $(ONECORESHELL_INTERNAL_LIB_VPATH_L)\api-ms-win-shell-namespace-l1.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-uiacore-l1.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-usp10-l1.lib \ + $(ONECORE_EXTERNAL_SDK_LIB_PATH)\ntdll.lib \ $(WINCORE_OBJ_PATH)\console\open\src\host\lib\$(O)\conhostv2.lib \ $(WINCORE_OBJ_PATH)\console\conint\$(O)\conint.lib \ $(WINCORE_OBJ_PATH)\console\open\src\buffer\out\lib\$(O)\conbufferout.lib \ @@ -177,7 +181,6 @@ TARGETLIBS = \ $(WINCORE_OBJ_PATH)\console\open\src\audio\midi\lib\$(O)\ConAudioMidi.lib \ $(WINCORE_OBJ_PATH)\console\open\src\renderer\base\lib\$(O)\ConRenderBase.lib \ $(WINCORE_OBJ_PATH)\console\open\src\renderer\gdi\lib\$(O)\ConRenderGdi.lib \ - $(WINCORE_OBJ_PATH)\console\open\src\renderer\vt\lib\$(O)\ConRenderVt.lib \ $(WINCORE_OBJ_PATH)\console\open\src\renderer\wddmcon\lib\$(O)\ConRenderWddmCon.lib \ $(WINCORE_OBJ_PATH)\console\open\src\server\lib\$(O)\ConServer.lib \ $(WINCORE_OBJ_PATH)\console\open\src\interactivity\base\lib\$(O)\ConInteractivityBaseLib.lib \ @@ -196,7 +199,7 @@ DELAYLOAD = \ api-ms-win-core-com-l1.dll; \ api-ms-win-core-registry-l2.dll; \ api-ms-win-mm-playsound-l1.dll; \ - ext-ms-win-imm-l1-1-0.lib; \ + ext-ms-win-imm-l1.dll; \ api-ms-win-shcore-obsolete-l1.dll; \ api-ms-win-shcore-scaling-l1.dll; \ api-ms-win-shell-dataobject-l1.dll; \ @@ -229,6 +232,7 @@ DELAYLOAD = \ ext-ms-win-rtcore-ntuser-sysparams-l1.dll; \ ext-ms-win-rtcore-ntuser-window-ext-l1.dll; \ ext-ms-win-rtcore-ntuser-winstamin-l1.dll; \ + ext-ms-win-rtcore-ntuser-syscolors-l1.dll; \ ext-ms-win-shell-shell32-l1.dll; \ ext-ms-win-uiacore-l1.dll; \ ext-ms-win-uxtheme-themes-l1.dll; \ diff --git a/src/host/sources.test.inc b/src/host/sources.test.inc index 7d49665c33..6fd7ff7412 100644 --- a/src/host/sources.test.inc +++ b/src/host/sources.test.inc @@ -24,10 +24,7 @@ INCLUDES = \ ..\..\inc\test; \ $(ONECORESDKTOOLS_INTERNAL_INC_PATH_L)\wextest\cue; \ -# prepend the ConRenderVt.Unittest.lib, so that it's linked before the non-ut version. - TARGETLIBS = \ - $(WINCORE_OBJ_PATH)\console\open\src\renderer\vt\ut_lib\$(O)\ConRenderVt.Unittest.lib \ $(TARGETLIBS) \ $(ONECORESDKTOOLS_INTERNAL_LIB_PATH_L)\WexTest\Cue\Wex.Common.lib \ $(ONECORESDKTOOLS_INTERNAL_LIB_PATH_L)\WexTest\Cue\Wex.Logger.lib \ diff --git a/src/host/srvinit.cpp b/src/host/srvinit.cpp index a6627bde99..49551a25bd 100644 --- a/src/host/srvinit.cpp +++ b/src/host/srvinit.cpp @@ -463,7 +463,7 @@ try TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - wil::unique_handle clientProcess{ OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | SYNCHRONIZE, TRUE, static_cast(connectMessage->Descriptor.Process)) }; + wil::unique_handle clientProcess{ OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_SET_INFORMATION | SYNCHRONIZE, TRUE, static_cast(connectMessage->Descriptor.Process)) }; RETURN_LAST_ERROR_IF_NULL(clientProcess.get()); TraceLoggingWrite(g_hConhostV2EventTraceProvider, @@ -842,16 +842,7 @@ PWSTR TranslateConsoleTitle(_In_ PCWSTR pwszConsoleTitle, const BOOL fUnexpand, { if (!gci.IsInVtIoMode()) { - auto renderThread = std::make_unique(); - // stash a local pointer to the thread here - - // We're going to give ownership of the thread to the Renderer, - // but the thread also need to be told who its renderer is, - // and we can't do that until the renderer is constructed. - auto* const localPointerToThread = renderThread.get(); - - g.pRender = new Renderer(gci.GetRenderSettings(), &gci.renderData, nullptr, 0, std::move(renderThread)); - - THROW_IF_FAILED(localPointerToThread->Initialize(g.pRender)); + g.pRender = new Renderer(gci.GetRenderSettings(), &gci.renderData); // Set up the renderer to be used to calculate the width of a glyph, // should we be unable to figure out its width another way. diff --git a/src/host/stream.cpp b/src/host/stream.cpp index 11a7f6a411..d97d629b0d 100644 --- a/src/host/stream.cpp +++ b/src/host/stream.cpp @@ -340,7 +340,7 @@ NT_CATCH_RETURN() INPUT_READ_HANDLE_DATA& readHandleState, const std::wstring_view exeName, const bool unicode, - std::unique_ptr& waiter) noexcept + CONSOLE_API_MSG* pWaitReplyMessage) noexcept { auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); RETURN_HR_IF(E_FAIL, !gci.HasActiveOutputBuffer()); @@ -364,7 +364,8 @@ NT_CATCH_RETURN() if (!cookedReadData->Read(unicode, bytesRead, controlKeyState)) { // memory will be cleaned up by wait queue - waiter.reset(cookedReadData.release()); + std::ignore = ConsoleWaitQueue::s_CreateWait(pWaitReplyMessage, cookedReadData.release()); + return CONSOLE_STATUS_WAIT; } else { @@ -468,25 +469,23 @@ NT_CATCH_RETURN() // populated. // - STATUS_SUCCESS on success // - Other NSTATUS codes as necessary -[[nodiscard]] NTSTATUS DoReadConsole(InputBuffer& inputBuffer, - const HANDLE processData, - std::span buffer, - size_t& bytesRead, - ULONG& controlKeyState, - const std::wstring_view initialData, - const DWORD ctrlWakeupMask, - INPUT_READ_HANDLE_DATA& readHandleState, - const std::wstring_view exeName, - const bool unicode, - std::unique_ptr& waiter) noexcept +[[nodiscard]] HRESULT DoReadConsole(InputBuffer& inputBuffer, + const HANDLE processData, + std::span buffer, + size_t& bytesRead, + ULONG& controlKeyState, + const std::wstring_view initialData, + const DWORD ctrlWakeupMask, + INPUT_READ_HANDLE_DATA& readHandleState, + const std::wstring_view exeName, + const bool unicode, + CONSOLE_API_MSG* pWaitReplyMessage) noexcept { try { LockConsole(); auto Unlock = wil::scope_exit([&] { UnlockConsole(); }); - waiter.reset(); - bytesRead = 0; if (buffer.size() < 1) @@ -504,17 +503,17 @@ NT_CATCH_RETURN() } else if (WI_IsFlagSet(inputBuffer.InputMode, ENABLE_LINE_INPUT)) { - return NTSTATUS_FROM_HRESULT(_ReadLineInput(inputBuffer, - processData, - buffer, - bytesRead, - controlKeyState, - initialData, - ctrlWakeupMask, - readHandleState, - exeName, - unicode, - waiter)); + return _ReadLineInput(inputBuffer, + processData, + buffer, + bytesRead, + controlKeyState, + initialData, + ctrlWakeupMask, + readHandleState, + exeName, + unicode, + pWaitReplyMessage); } else { @@ -525,7 +524,7 @@ NT_CATCH_RETURN() unicode); if (status == CONSOLE_STATUS_WAIT) { - waiter = std::make_unique(&inputBuffer, &readHandleState, gsl::narrow(buffer.size()), reinterpret_cast(buffer.data())); + std::ignore = ConsoleWaitQueue::s_CreateWait(pWaitReplyMessage, new RAW_READ_DATA(&inputBuffer, &readHandleState, gsl::narrow(buffer.size()), reinterpret_cast(buffer.data()))); } return status; } @@ -536,7 +535,7 @@ NT_CATCH_RETURN() [[nodiscard]] HRESULT ApiRoutines::ReadConsoleImpl(IConsoleInputObject& context, std::span buffer, size_t& written, - std::unique_ptr& waiter, + CONSOLE_API_MSG* pWaitReplyMessage, const std::wstring_view initialData, const std::wstring_view exeName, INPUT_READ_HANDLE_DATA& readHandleState, @@ -545,17 +544,17 @@ NT_CATCH_RETURN() const DWORD controlWakeupMask, DWORD& controlKeyState) noexcept { - return HRESULT_FROM_NT(DoReadConsole(context, - clientHandle, - buffer, - written, - controlKeyState, - initialData, - controlWakeupMask, - readHandleState, - exeName, - IsUnicode, - waiter)); + return DoReadConsole(context, + clientHandle, + buffer, + written, + controlKeyState, + initialData, + controlWakeupMask, + readHandleState, + exeName, + IsUnicode, + pWaitReplyMessage); } void UnblockWriteConsole(const DWORD dwReason) diff --git a/src/host/ut_host/ApiRoutinesTests.cpp b/src/host/ut_host/ApiRoutinesTests.cpp index 37e6ec62fd..bb2f95878b 100644 --- a/src/host/ut_host/ApiRoutinesTests.cpp +++ b/src/host/ut_host/ApiRoutinesTests.cpp @@ -372,46 +372,24 @@ class ApiRoutinesTests for (size_t i = 0; i < cchTestText; i += cchIncrement) { Log::Comment(WEX::Common::String().Format(L"Iteration %d of loop with increment %d", i, cchIncrement)); - if (fInduceWait) - { - Log::Comment(L"Blocking global output state to induce waits."); - s_AdjustOutputWait(true); - } + s_AdjustOutputWait(fInduceWait); size_t cchRead = 0; - std::unique_ptr waiter; // The increment is either the specified length or the remaining text in the string (if that is smaller). const auto cchWriteLength = std::min(cchIncrement, cchTestText - i); // Run the test method - const auto hr = _pApiRoutines->WriteConsoleAImpl(si, { pszTestText + i, cchWriteLength }, cchRead, waiter); + const auto hr = _pApiRoutines->WriteConsoleAImpl(si, { pszTestText + i, cchWriteLength }, cchRead, nullptr); - VERIFY_ARE_EQUAL(S_OK, hr, L"Successful result code from writing."); if (!fInduceWait) { - VERIFY_IS_NULL(waiter.get(), L"We should have no waiter for this case."); + VERIFY_ARE_EQUAL(S_OK, hr); VERIFY_ARE_EQUAL(cchWriteLength, cchRead, L"We should have the same character count back as 'written' that we gave in."); } else { - VERIFY_IS_NOT_NULL(waiter.get(), L"We should have a waiter for this case."); - // The cchRead is irrelevant at this point as it's not going to be returned until we're off the wait. - - Log::Comment(L"Unblocking global output state so the wait can be serviced."); - s_AdjustOutputWait(false); - Log::Comment(L"Dispatching the wait."); - auto Status = STATUS_SUCCESS; - size_t dwNumBytes = 0; - DWORD dwControlKeyState = 0; // unused but matches the pattern for read. - void* pOutputData = nullptr; // unused for writes but used for read. - const BOOL bNotifyResult = waiter->Notify(WaitTerminationReason::NoReason, FALSE, &Status, &dwNumBytes, &dwControlKeyState, &pOutputData); - - VERIFY_IS_TRUE(!!bNotifyResult, L"Wait completion on notify should be successful."); - VERIFY_ARE_EQUAL(STATUS_SUCCESS, Status, L"We should have a successful return code to pass to the caller."); - - const auto dwBytesExpected = cchWriteLength; - VERIFY_ARE_EQUAL(dwBytesExpected, dwNumBytes, L"We should have the byte length of the string we put in as the returned value."); + VERIFY_ARE_EQUAL(CONSOLE_STATUS_WAIT, hr); } } } @@ -431,43 +409,21 @@ class ApiRoutinesTests gci.LockConsole(); auto Unlock = wil::scope_exit([&] { gci.UnlockConsole(); }); - const std::wstring testText(L"Test text"); + const std::wstring_view testText(L"Test text"); - if (fInduceWait) - { - Log::Comment(L"Blocking global output state to induce waits."); - s_AdjustOutputWait(true); - } + s_AdjustOutputWait(fInduceWait); size_t cchRead = 0; - std::unique_ptr waiter; - const auto hr = _pApiRoutines->WriteConsoleWImpl(si, testText, cchRead, waiter); + const auto hr = _pApiRoutines->WriteConsoleWImpl(si, testText, cchRead, nullptr); - VERIFY_ARE_EQUAL(S_OK, hr, L"Successful result code from writing."); if (!fInduceWait) { - VERIFY_IS_NULL(waiter.get(), L"We should have no waiter for this case."); + VERIFY_ARE_EQUAL(S_OK, hr); VERIFY_ARE_EQUAL(testText.size(), cchRead, L"We should have the same character count back as 'written' that we gave in."); } else { - VERIFY_IS_NOT_NULL(waiter.get(), L"We should have a waiter for this case."); - // The cchRead is irrelevant at this point as it's not going to be returned until we're off the wait. - - Log::Comment(L"Unblocking global output state so the wait can be serviced."); - s_AdjustOutputWait(false); - Log::Comment(L"Dispatching the wait."); - auto Status = STATUS_SUCCESS; - size_t dwNumBytes = 0; - DWORD dwControlKeyState = 0; // unused but matches the pattern for read. - void* pOutputData = nullptr; // unused for writes but used for read. - const BOOL bNotifyResult = waiter->Notify(WaitTerminationReason::NoReason, TRUE, &Status, &dwNumBytes, &dwControlKeyState, &pOutputData); - - VERIFY_IS_TRUE(!!bNotifyResult, L"Wait completion on notify should be successful."); - VERIFY_ARE_EQUAL(STATUS_SUCCESS, Status, L"We should have a successful return code to pass to the caller."); - - const auto dwBytesExpected = testText.size() * sizeof(wchar_t); - VERIFY_ARE_EQUAL(dwBytesExpected, dwNumBytes, L"We should have the byte length of the string we put in as the returned value."); + VERIFY_ARE_EQUAL(CONSOLE_STATUS_WAIT, hr); } } diff --git a/src/host/ut_host/ConsoleArgumentsTests.cpp b/src/host/ut_host/ConsoleArgumentsTests.cpp index a9148dfd2e..b03b9099db 100644 --- a/src/host/ut_host/ConsoleArgumentsTests.cpp +++ b/src/host/ut_host/ConsoleArgumentsTests.cpp @@ -812,7 +812,7 @@ void ConsoleArgumentsTests::InitialSizeTests() true); // successful parse? commandline = L"conhost.exe --width foo"; - ArgTestsRunner(L"#6 look for an ivalid commandline passing a string", + ArgTestsRunner(L"#6 look for an invalid commandline passing a string", commandline, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, @@ -833,7 +833,7 @@ void ConsoleArgumentsTests::InitialSizeTests() false); // successful parse? commandline = L"conhost.exe --width 2foo"; - ArgTestsRunner(L"#7 look for an ivalid commandline passing a string with a number at the start", + ArgTestsRunner(L"#7 look for an invalid commandline passing a string with a number at the start", commandline, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, @@ -854,7 +854,7 @@ void ConsoleArgumentsTests::InitialSizeTests() false); // successful parse? commandline = L"conhost.exe --width 65535"; - ArgTestsRunner(L"#7 look for an ivalid commandline passing a value that's too big", + ArgTestsRunner(L"#7 look for an invalid commandline passing a value that's too big", commandline, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, diff --git a/src/host/ut_host/Host.UnitTests.vcxproj b/src/host/ut_host/Host.UnitTests.vcxproj index 1fbaaaff14..c914e48adf 100644 --- a/src/host/ut_host/Host.UnitTests.vcxproj +++ b/src/host/ut_host/Host.UnitTests.vcxproj @@ -78,7 +78,7 @@ - + diff --git a/src/host/ut_host/Host.UnitTests.vcxproj.filters b/src/host/ut_host/Host.UnitTests.vcxproj.filters index ebf93cbf27..dd573753e3 100644 --- a/src/host/ut_host/Host.UnitTests.vcxproj.filters +++ b/src/host/ut_host/Host.UnitTests.vcxproj.filters @@ -83,7 +83,7 @@ Header Files - + Header Files diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index a81d47ec93..943d3199db 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -44,7 +44,6 @@ class ScreenBufferTests m_state->InitEvents(); m_state->PrepareGlobalFont({ 1, 1 }); - m_state->PrepareGlobalRenderer(); m_state->PrepareGlobalInputBuffer(); m_state->PrepareGlobalScreenBuffer(); @@ -54,7 +53,6 @@ class ScreenBufferTests TEST_CLASS_CLEANUP(ClassCleanup) { m_state->CleanupGlobalScreenBuffer(); - m_state->CleanupGlobalRenderer(); m_state->CleanupGlobalInputBuffer(); delete m_state; @@ -581,8 +579,6 @@ void ScreenBufferTests::TestResetClearTabStops() // Reset the screen buffer to test the defaults. m_state->CleanupNewTextBufferInfo(); m_state->CleanupGlobalScreenBuffer(); - m_state->CleanupGlobalRenderer(); - m_state->PrepareGlobalRenderer(); m_state->PrepareGlobalScreenBuffer(); m_state->PrepareNewTextBufferInfo(); @@ -2553,11 +2549,7 @@ void ScreenBufferTests::TestAltBufferVtDispatching() // We're going to write some data to either the main buffer or the alt // buffer, as if we were using the API. - std::unique_ptr waiter; - std::wstring seq = L"\x1b[5;6H"; - auto seqCb = 2 * seq.size(); - VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, waiter)); - + VERIFY_SUCCEEDED(DoWriteConsole(mainBuffer, L"\x1b[5;6H")); VERIFY_ARE_EQUAL(til::point(0, 0), mainCursor.GetPosition()); // recall: vt coordinates are (row, column), 1-indexed VERIFY_ARE_EQUAL(til::point(5, 4), altCursor.GetPosition()); @@ -2569,17 +2561,11 @@ void ScreenBufferTests::TestAltBufferVtDispatching() VERIFY_ARE_EQUAL(expectedDefaults, mainBuffer.GetAttributes()); VERIFY_ARE_EQUAL(expectedDefaults, alternate.GetAttributes()); - seq = L"\x1b[48;2;255;0;255m"; - seqCb = 2 * seq.size(); - VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, waiter)); - + VERIFY_SUCCEEDED(DoWriteConsole(mainBuffer, L"\x1b[48;2;255;0;255m")); VERIFY_ARE_EQUAL(expectedDefaults, mainBuffer.GetAttributes()); VERIFY_ARE_EQUAL(expectedRgb, alternate.GetAttributes()); - seq = L"X"; - seqCb = 2 * seq.size(); - VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, waiter)); - + VERIFY_SUCCEEDED(DoWriteConsole(mainBuffer, L"X")); VERIFY_ARE_EQUAL(til::point(0, 0), mainCursor.GetPosition()); VERIFY_ARE_EQUAL(til::point(6, 4), altCursor.GetPosition()); @@ -5996,7 +5982,7 @@ void ScreenBufferTests::ClearAlternateBuffer() auto useMain = wil::scope_exit([&] { altBuffer.UseMainScreenBuffer(); }); - // Set the position to home, otherwise it's inherited from the main buffer. + // Set the position to home; otherwise, it's inherited from the main buffer. VERIFY_SUCCEEDED(altBuffer.SetCursorPosition({ 0, 0 }, true)); WriteText(altBuffer.GetTextBuffer()); diff --git a/src/host/ut_host/SearchTests.cpp b/src/host/ut_host/SearchTests.cpp index d37c3556d4..d7ba067e76 100644 --- a/src/host/ut_host/SearchTests.cpp +++ b/src/host/ut_host/SearchTests.cpp @@ -23,20 +23,14 @@ class SearchTests TEST_CLASS_SETUP(ClassSetup) { m_state = new CommonState(); - - m_state->PrepareGlobalRenderer(); m_state->PrepareGlobalScreenBuffer(); - return true; } TEST_CLASS_CLEANUP(ClassCleanup) { m_state->CleanupGlobalScreenBuffer(); - m_state->CleanupGlobalRenderer(); - delete m_state; - return true; } diff --git a/src/host/ut_host/TextBufferTests.cpp b/src/host/ut_host/TextBufferTests.cpp index 4252609e30..1ee380d70e 100644 --- a/src/host/ut_host/TextBufferTests.cpp +++ b/src/host/ut_host/TextBufferTests.cpp @@ -1390,22 +1390,18 @@ void TextBufferTests::TestBackspaceStringsAPI() // backspacing it with "\b \b". // Regardless of how we write those sequences of characters, the end result // should be the same. - std::unique_ptr waiter; Log::Comment(NoThrowString().Format( L"Using WriteCharsLegacy, write \\b \\b as a single string.")); - size_t aCb = 2; - size_t seqCb = 6; - VERIFY_SUCCEEDED(DoWriteConsole(L"a", &aCb, si, waiter)); - VERIFY_SUCCEEDED(DoWriteConsole(L"\b \b", &seqCb, si, waiter)); + VERIFY_SUCCEEDED(DoWriteConsole(si, L"a")); + VERIFY_SUCCEEDED(DoWriteConsole(si, L"\b \b")); VERIFY_ARE_EQUAL(cursor.GetPosition().x, x0); VERIFY_ARE_EQUAL(cursor.GetPosition().y, y0); - seqCb = 2; - VERIFY_SUCCEEDED(DoWriteConsole(L"a", &seqCb, si, waiter)); - VERIFY_SUCCEEDED(DoWriteConsole(L"\b", &seqCb, si, waiter)); - VERIFY_SUCCEEDED(DoWriteConsole(L" ", &seqCb, si, waiter)); - VERIFY_SUCCEEDED(DoWriteConsole(L"\b", &seqCb, si, waiter)); + VERIFY_SUCCEEDED(DoWriteConsole(si, L"a")); + VERIFY_SUCCEEDED(DoWriteConsole(si, L"\b")); + VERIFY_SUCCEEDED(DoWriteConsole(si, L" ")); + VERIFY_SUCCEEDED(DoWriteConsole(si, L"\b")); VERIFY_ARE_EQUAL(cursor.GetPosition().x, x0); VERIFY_ARE_EQUAL(cursor.GetPosition().y, y0); } diff --git a/src/host/ut_host/VtIoTests.cpp b/src/host/ut_host/VtIoTests.cpp index 3ad87891a2..6de23ae0fa 100644 --- a/src/host/ut_host/VtIoTests.cpp +++ b/src/host/ut_host/VtIoTests.cpp @@ -52,6 +52,16 @@ static constexpr std::wstring_view s_initialContentVT{ // clang-format on }; +static constexpr std::wstring_view s_initialContentVTWide{ + // clang-format off + L"" + sgr_red("〇") sgr_blu("一") sgr_red("二") sgr_blu("三") "\r\n" + sgr_red("四") sgr_blu("五") sgr_red("六") sgr_blu("七") "\r\n" + sgr_blu("八") sgr_red("九") sgr_blu("十") sgr_red("百") "\r\n" + sgr_blu("千") sgr_red("万") sgr_blu("億") sgr_red("兆") + // clang-format on +}; + class ::Microsoft::Console::VirtualTerminal::VtIoTests { BEGIN_TEST_CLASS(VtIoTests) @@ -71,11 +81,11 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests return { &rxBuf[0], read }; } - void setupInitialContents() const + void setupInitialContents(bool wide) const { auto& sm = screenInfo->GetStateMachine(); sm.ProcessString(L"\033c"); - sm.ProcessString(s_initialContentVT); + sm.ProcessString(wide ? s_initialContentVTWide : s_initialContentVT); sm.ProcessString(L"\x1b[H" sgr_rst()); } @@ -239,23 +249,22 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests resetContents(); size_t written; - std::unique_ptr waiter; std::string_view expected; std::string_view actual; - THROW_IF_FAILED(routines.WriteConsoleWImpl(*screenInfo, L"", written, waiter)); + THROW_IF_FAILED(routines.WriteConsoleWImpl(*screenInfo, L"", written, nullptr)); expected = ""; actual = readOutput(); VERIFY_ARE_EQUAL(expected, actual); // Force-wrap because we write up to the last column. - THROW_IF_FAILED(routines.WriteConsoleWImpl(*screenInfo, L"aaaaaaaa", written, waiter)); + THROW_IF_FAILED(routines.WriteConsoleWImpl(*screenInfo, L"aaaaaaaa", written, nullptr)); expected = "aaaaaaaa\r\n"; actual = readOutput(); VERIFY_ARE_EQUAL(expected, actual); // Force-wrap because we write up to the last column, but this time with a tab. - THROW_IF_FAILED(routines.WriteConsoleWImpl(*screenInfo, L"a\t\r\nb", written, waiter)); + THROW_IF_FAILED(routines.WriteConsoleWImpl(*screenInfo, L"a\t\r\nb", written, nullptr)); expected = "a\t\r\n\r\nb"; actual = readOutput(); VERIFY_ARE_EQUAL(expected, actual); @@ -277,7 +286,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests TEST_METHOD(WriteConsoleOutputAttribute) { - setupInitialContents(); + setupInitialContents(false); static constexpr std::array payload{ red, blu, red, blu }; static constexpr til::point target{ 6, 1 }; @@ -295,7 +304,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests TEST_METHOD(WriteConsoleOutputCharacterW) { - setupInitialContents(); + setupInitialContents(false); size_t written = 0; std::string_view expected; @@ -354,7 +363,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests actual = readOutput(); VERIFY_ARE_EQUAL(expected, actual); - setupInitialContents(); + setupInitialContents(false); // Writing at the start of a line. THROW_IF_FAILED(routines.FillConsoleOutputAttributeImpl(*screenInfo, red, 3, { 0, 0 }, cellsModified, false)); @@ -388,6 +397,25 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests VERIFY_ARE_EQUAL(expected, actual); } + TEST_METHOD(FillConsoleOutputAttributeWide) + { + setupInitialContents(true); + + size_t cellsModified = 0; + std::string_view expected; + std::string_view actual; + + // Writing nothing should produce nothing. + THROW_IF_FAILED(routines.FillConsoleOutputAttributeImpl(*screenInfo, red, 4, { 2, 1 }, cellsModified, false)); + expected = + decsc() // + cup(2, 3) sgr_red("五六") // + decrc(); + actual = readOutput(); + VERIFY_ARE_EQUAL(4u, cellsModified); + VERIFY_ARE_EQUAL(expected, actual); + } + TEST_METHOD(FillConsoleOutputCharacterW) { size_t cellsModified = 0; @@ -407,7 +435,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests actual = readOutput(); VERIFY_ARE_EQUAL(expected, actual); - setupInitialContents(); + setupInitialContents(false); // Writing at the start of a line. THROW_IF_FAILED(routines.FillConsoleOutputCharacterWImpl(*screenInfo, L'a', 3, { 0, 0 }, cellsModified, false)); @@ -453,7 +481,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests std::string_view expected; std::string_view actual; - setupInitialContents(); + setupInitialContents(false); // Scrolling from nowhere to somewhere are no-ops and should not emit anything. THROW_IF_FAILED(routines.ScrollConsoleScreenBufferWImpl(*screenInfo, { 0, 0, -1, -1 }, {}, std::nullopt, L' ', 0, false)); @@ -487,7 +515,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests // // m n M N o p O P // - setupInitialContents(); + setupInitialContents(false); // Scrolling from somewhere to somewhere. // @@ -626,7 +654,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests std::string_view expected; std::string_view actual; - setupInitialContents(); + setupInitialContents(false); // Scrolling from nowhere to somewhere are no-ops and should not emit anything. THROW_IF_FAILED(routines.ScrollConsoleScreenBufferWImpl(*screenInfo, { 0, 0, -1, -1 }, {}, std::nullopt, L' ', 0, false)); @@ -660,7 +688,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests // // m n M N o p O P // - setupInitialContents(); + setupInitialContents(false); // Scrolling from somewhere to somewhere. // @@ -797,7 +825,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests &screenInfoAlt)); routines.SetConsoleActiveScreenBufferImpl(*screenInfoAlt); - setupInitialContents(); + setupInitialContents(false); THROW_IF_FAILED(routines.SetConsoleOutputModeImpl(*screenInfoAlt, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING)); readOutput(); diff --git a/src/host/ut_host/sources b/src/host/ut_host/sources index b690477830..d8da9ec993 100644 --- a/src/host/ut_host/sources +++ b/src/host/ut_host/sources @@ -41,10 +41,7 @@ INCLUDES = \ ..\..\inc\test; \ $(ONECORESDKTOOLS_INTERNAL_INC_PATH_L)\wextest\cue; \ -# prepend the ConRenderVt.Unittest.lib, so that it's linked before the non-ut version. - TARGETLIBS = \ - $(WINCORE_OBJ_PATH)\console\open\src\renderer\vt\ut_lib\$(O)\ConRenderVt.Unittest.lib \ $(WINCORE_OBJ_PATH)\console\open\src\host\ut_lib\$(O)\ConhostV2.Unittest.lib \ $(TARGETLIBS) \ $(ONECORESDKTOOLS_INTERNAL_LIB_PATH_L)\WexTest\Cue\Wex.Common.lib \ diff --git a/src/host/ut_host/sources.dep b/src/host/ut_host/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/host/ut_host/sources.dep +++ b/src/host/ut_host/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/host/ut_lib/sources.dep b/src/host/ut_lib/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/host/ut_lib/sources.dep +++ b/src/host/ut_lib/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/host/utils.cpp b/src/host/utils.cpp index d5ac1fc90d..292450ca5b 100644 --- a/src/host/utils.cpp +++ b/src/host/utils.cpp @@ -124,7 +124,7 @@ UINT s_LoadStringEx(_In_ HINSTANCE hModule, _In_ UINT wID, _Out_writes_(cchBuffe lpsz += cch; // Step to start if next string } - // chhBufferMax == 0 means return a pointer to the read-only resource buffer. + // cchBufferMax == 0 means return a pointer to the read-only resource buffer. if (cchBufferMax == 0) { *(LPTSTR*)lpBuffer = lpsz; diff --git a/src/host/writeData.cpp b/src/host/writeData.cpp index dcf49c54c0..51d017bfc0 100644 --- a/src/host/writeData.cpp +++ b/src/host/writeData.cpp @@ -6,9 +6,10 @@ #include "_stream.h" #include "../types/inc/convert.hpp" - #include "../interactivity/inc/ServiceLocator.hpp" +using Microsoft::Console::Interactivity::ServiceLocator; + // Routine Description: // - Creates a new write data object for used in servicing write console requests // Arguments: @@ -22,31 +23,22 @@ // Return Value: // - THROW: Throws if space cannot be allocated to copy the given string WriteData::WriteData(SCREEN_INFORMATION& siContext, - _In_reads_bytes_(cbContext) PCWCHAR pwchContext, - const size_t cbContext, + std::wstring pwchContext, const UINT uiOutputCodepage) : IWaitRoutine(ReplyDataType::Write), _siContext(siContext), - _pwchContext(THROW_IF_NULL_ALLOC(reinterpret_cast(new byte[cbContext]))), - _cbContext(cbContext), + _pwchContext(std::move(pwchContext)), _uiOutputCodepage(uiOutputCodepage), _fLeadByteCaptured(false), _fLeadByteConsumed(false), _cchUtf8Consumed(0) { - memmove(_pwchContext, pwchContext, _cbContext); } // Routine Description: // - Destroys the write data object // - Frees the string copy we made on creation -WriteData::~WriteData() -{ - if (nullptr != _pwchContext) - { - delete[] _pwchContext; - } -} +WriteData::~WriteData() = default; // Routine Description: // - Stores some additional information about lead byte adjustments from the conversion @@ -87,7 +79,7 @@ void WriteData::SetUtf8ConsumedCharacters(const size_t cchUtf8Consumed) // - Called back at a later time to resume the writing operation when the output object becomes unblocked. // Arguments: // - TerminationReason - if this routine is called because a ctrl-c or ctrl-break was seen, this argument -// contains CtrlC or CtrlBreak. If the owning thread is exiting, it will have ThreadDying. Otherwise 0. +// contains CtrlC or CtrlBreak. If the owning thread is exiting, it will have ThreadDying. Otherwise, 0. // - fIsUnicode - Input data was in UCS-2 unicode or it needs to be converted with the current Output Codepage // - pReplyStatus - The status code to return to the client application that originally called the API (before it was queued to wait) // - pNumBytes - The number of bytes of data that the server/driver will need to transmit back to the client process @@ -102,7 +94,7 @@ bool WriteData::Notify(const WaitTerminationReason TerminationReason, _Out_ DWORD* const pControlKeyState, _Out_ void* const /*pOutputData*/) { - *pNumBytes = _cbContext; + *pNumBytes = 0; *pControlKeyState = 0; if (WI_IsFlagSet(TerminationReason, WaitTerminationReason::ThreadDying)) @@ -111,6 +103,12 @@ bool WriteData::Notify(const WaitTerminationReason TerminationReason, return true; } + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + if (WI_IsAnyFlagSet(gci.Flags, (CONSOLE_SUSPENDED | CONSOLE_SELECTING | CONSOLE_SCROLLBAR_TRACKING))) + { + return false; + } + // if we get to here, this routine was called by the input // thread, which grabs the current console lock. @@ -119,20 +117,16 @@ bool WriteData::Notify(const WaitTerminationReason TerminationReason, FAIL_FAST_IF(!(Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation().IsConsoleLocked())); - std::unique_ptr waiter; - auto cbContext = _cbContext; - auto Status = DoWriteConsole(_pwchContext, - &cbContext, - _siContext, - waiter); + auto Status = DoWriteConsole(_siContext, _pwchContext); if (Status == CONSOLE_STATUS_WAIT) { // an extra waiter will be created by DoWriteConsole, but we're already a waiter so discard it. - waiter.reset(); return false; } + auto cbContext = _pwchContext.size(); + // There's extra work to do to correct the byte counts if the original call was an A-version call. // We always process and hold text in the waiter as W-version text, but the A call is expecting // a byte value in its own codepage of how much we have written in that codepage. @@ -140,10 +134,6 @@ bool WriteData::Notify(const WaitTerminationReason TerminationReason, { if (CP_UTF8 != _uiOutputCodepage) { - // At this level with WriteConsole, everything is byte counts, so change back to char counts for - // GetALengthFromW to work correctly. - const auto cchContext = cbContext / sizeof(wchar_t); - // For non-UTF-8 codepages, we need to back convert the amount consumed and then // correlate that with any lead bytes we may have kept for later or reintroduced // from previous calls. @@ -152,7 +142,7 @@ bool WriteData::Notify(const WaitTerminationReason TerminationReason, // Start by counting the number of A bytes we used in printing our W string to the screen. try { - cchTextBufferRead = GetALengthFromW(_uiOutputCodepage, { _pwchContext, cchContext }); + cchTextBufferRead = GetALengthFromW(_uiOutputCodepage, _pwchContext); } CATCH_LOG(); diff --git a/src/host/writeData.hpp b/src/host/writeData.hpp index 4609b98d37..ef8e290b6a 100644 --- a/src/host/writeData.hpp +++ b/src/host/writeData.hpp @@ -25,8 +25,7 @@ class WriteData : public IWaitRoutine { public: WriteData(SCREEN_INFORMATION& siContext, - _In_reads_bytes_(cbContext) PCWCHAR pwchContext, - const size_t cbContext, + std::wstring pwchContext, const UINT uiOutputCodepage); ~WriteData(); @@ -41,12 +40,11 @@ public: _Out_ NTSTATUS* const pReplyStatus, _Out_ size_t* const pNumBytes, _Out_ DWORD* const pControlKeyState, - _Out_ void* const pOutputData); + _Out_ void* const pOutputData) override; private: SCREEN_INFORMATION& _siContext; - wchar_t* const _pwchContext; - const size_t _cbContext; + std::wstring _pwchContext; UINT const _uiOutputCodepage; bool _fLeadByteCaptured; bool _fLeadByteConsumed; diff --git a/src/inc/LibraryIncludes.h b/src/inc/LibraryIncludes.h index 6a2c1a1393..b9df47f957 100644 --- a/src/inc/LibraryIncludes.h +++ b/src/inc/LibraryIncludes.h @@ -101,6 +101,8 @@ // The compiler doesn't like that. --> Suppress the warning. #pragma warning(push) #pragma warning(disable: 4324) // structure was padded due to alignment specifier +// undefine BUILD_WINDOWS so that wrl/event.h doesn't include wrl/internalevent.h +#undef BUILD_WINDOWS #include #pragma warning(pop) diff --git a/src/inc/conpty-static.h b/src/inc/conpty-static.h index aa31c67fa3..3aca15dba7 100644 --- a/src/inc/conpty-static.h +++ b/src/inc/conpty-static.h @@ -38,7 +38,7 @@ CONPTY_EXPORT HRESULT WINAPI ConptyCreatePseudoConsole(COORD size, HANDLE hInput CONPTY_EXPORT HRESULT WINAPI ConptyCreatePseudoConsoleAsUser(HANDLE hToken, COORD size, HANDLE hInput, HANDLE hOutput, DWORD dwFlags, HPCON* phPC); CONPTY_EXPORT HRESULT WINAPI ConptyResizePseudoConsole(HPCON hPC, COORD size); -CONPTY_EXPORT HRESULT WINAPI ConptyClearPseudoConsole(HPCON hPC); +CONPTY_EXPORT HRESULT WINAPI ConptyClearPseudoConsole(HPCON hPC, BOOL keepCursorRow); CONPTY_EXPORT HRESULT WINAPI ConptyShowHidePseudoConsole(HPCON hPC, bool show); CONPTY_EXPORT HRESULT WINAPI ConptyReparentPseudoConsole(HPCON hPC, HWND newParent); CONPTY_EXPORT HRESULT WINAPI ConptyReleasePseudoConsole(HPCON hPC); diff --git a/src/inc/test/CommonState.hpp b/src/inc/test/CommonState.hpp index e79bc74d3e..82d8707653 100644 --- a/src/inc/test/CommonState.hpp +++ b/src/inc/test/CommonState.hpp @@ -66,20 +66,6 @@ public: m_pFontInfo = { L"Consolas", 0, 0, coordFontSize, 0 }; } - void PrepareGlobalRenderer() - { - Globals& g = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals(); - CONSOLE_INFORMATION& gci = g.getConsoleInformation(); - g.pRender = new Microsoft::Console::Render::Renderer(gci.GetRenderSettings(), &gci.renderData, nullptr, 0, nullptr); - } - - void CleanupGlobalRenderer() - { - Globals& g = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals(); - delete g.pRender; - g.pRender = nullptr; - } - void PrepareGlobalScreenBuffer(const til::CoordType viewWidth = s_csWindowWidth, const til::CoordType viewHeight = s_csWindowHeight, const til::CoordType bufferWidth = s_csBufferWidth, @@ -230,7 +216,43 @@ public: for (til::CoordType iRow = 0; iRow < cRowsToFill; iRow++) { ROW& row = textBuffer.GetMutableRowByOffset(iRow); - FillRow(&row, iRow & 1); + + // fill a row + // - Each row is populated with L"AB\u304bC\u304dDE " + // - 7 characters, 6 spaces. 13 total + // - The characters take up first 9 columns. (The wide glyphs take up 2 columns each) + // - か = \x304b, き = \x304d + + uint16_t column = 0; + for (const auto& ch : std::wstring_view{ L"AB\u304bC\u304dDE " }) + { + const uint16_t width = ch >= 0x80 ? 2 : 1; + row.ReplaceCharacters(column, width, { &ch, 1 }); + column += width; + } + + // A = bright red on dark gray + // This string starts at index 0 + auto Attr = TextAttribute(FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY); + row.SetAttrToEnd(0, Attr); + + // BかC = dark gold on bright blue + // This string starts at index 1 + Attr = TextAttribute(FOREGROUND_RED | FOREGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY); + row.SetAttrToEnd(1, Attr); + + // き = bright white on dark purple + // This string starts at index 5 + Attr = TextAttribute(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_BLUE); + row.SetAttrToEnd(5, Attr); + + // DE = black on dark green + // This string starts at index 7 + Attr = TextAttribute(BACKGROUND_GREEN); + row.SetAttrToEnd(7, Attr); + + // odd rows forced a wrap + row.SetWrapForced(iRow & 1); } textBuffer.GetCursor().SetYPosition(cRowsToFill); @@ -247,51 +269,4 @@ private: FontInfo m_pFontInfo; std::unique_ptr m_backupTextBufferInfo; std::unique_ptr m_readHandle; - - void FillRow(ROW* pRow, bool wrapForced) - { - // fill a row - // - Each row is populated with L"AB\u304bC\u304dDE " - // - 7 characters, 6 spaces. 13 total - // - The characters take up first 9 columns. (The wide glyphs take up 2 columns each) - // - か = \x304b, き = \x304d - - uint16_t column = 0; - for (const auto& ch : std::wstring_view{ L"AB\u304bC\u304dDE " }) - { - const uint16_t width = ch >= 0x80 ? 2 : 1; - pRow->ReplaceCharacters(column, width, { &ch, 1 }); - column += width; - } - - // A = bright red on dark gray - // This string starts at index 0 - auto Attr = TextAttribute(FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY); - pRow->SetAttrToEnd(0, Attr); - - // BかC = dark gold on bright blue - // This string starts at index 1 - Attr = TextAttribute(FOREGROUND_RED | FOREGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY); - pRow->SetAttrToEnd(1, Attr); - - // き = bright white on dark purple - // This string starts at index 5 - Attr = TextAttribute(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_BLUE); - pRow->SetAttrToEnd(5, Attr); - - // DE = black on dark green - // This string starts at index 7 - Attr = TextAttribute(BACKGROUND_GREEN); - pRow->SetAttrToEnd(7, Attr); - - // odd rows forced a wrap - if (wrapForced) - { - pRow->SetWrapForced(true); - } - else - { - pRow->SetWrapForced(false); - } - } }; diff --git a/src/inc/til.h b/src/inc/til.h index c79b0debdc..9fc1779678 100644 --- a/src/inc/til.h +++ b/src/inc/til.h @@ -55,20 +55,22 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" { template - as_view_t safe_slice_abs(const T& view, size_t beg, size_t end) + as_view_t safe_slice_abs(const T& view, size_t beg, size_t end) noexcept { - const auto len = view.size(); + const size_t len = view.size(); end = std::min(end, len); beg = std::min(beg, end); +#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1). return { view.data() + beg, end - beg }; } template - as_view_t safe_slice_len(const T& view, size_t start, size_t count) + as_view_t safe_slice_len(const T& view, size_t start, size_t count) noexcept { - const auto len = view.size(); + const size_t len = view.size(); start = std::min(start, len); count = std::min(count, len - start); +#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1). return { view.data() + start, count }; } diff --git a/src/inc/til/env.h b/src/inc/til/env.h index e41ae645dd..9e0eebf61a 100644 --- a/src/inc/til/env.h +++ b/src/inc/til/env.h @@ -366,12 +366,13 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" if (til::compare_ordinal_insensitive(var, temp) == 0 || til::compare_ordinal_insensitive(var, tmp) == 0) { - return til::details::wil_env::GetShortPathNameW(value.data()); - } - else - { - return std::wstring{ value }; + std::wstring shortPath; + if (SUCCEEDED((til::details::wil_env::GetShortPathNameW(value.data(), shortPath)))) + { + return shortPath; + } } + return std::wstring{ value }; } static bool is_path_var(std::wstring_view input) noexcept diff --git a/src/inc/til/io.h b/src/inc/til/io.h index 6f917f0d0b..bfc9c3bce3 100644 --- a/src/inc/til/io.h +++ b/src/inc/til/io.h @@ -23,7 +23,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" // Arguments: // - handle: a HANDLE to the file to check // Return Value: - // - true if it had the expected permissions. False otherwise. + // - true if it had the expected permissions; otherwise, false. _TIL_INLINEPREFIX bool isOwnedByAdministrators(const HANDLE& handle) { // If the file is owned by the administrators group, trust the @@ -256,7 +256,11 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" // renaming one is (supposed to be) atomic. // Wait... "supposed to be"!? Well it's technically not always atomic, // but it's pretty darn close to it, so... better than nothing. - std::filesystem::rename(tmpPath, resolvedPath); + std::filesystem::rename(tmpPath, resolvedPath, ec); + if (ec) + { + THROW_WIN32_MSG(ec.value(), "failed to write to file"); + } } } // io } // til diff --git a/src/inc/til/regex.h b/src/inc/til/regex.h new file mode 100644 index 0000000000..0ddc84eda6 --- /dev/null +++ b/src/inc/til/regex.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include + +namespace til::ICU // Terminal Implementation Library. Also: "Today I Learned" +{ + using unique_uregex = wistd::unique_ptr>; + + _TIL_INLINEPREFIX unique_uregex CreateRegex(const std::wstring_view& pattern, uint32_t flags, UErrorCode* status) noexcept + { +#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1). + const auto re = uregex_open(reinterpret_cast(pattern.data()), gsl::narrow_cast(pattern.size()), flags, nullptr, status); + // ICU describes the time unit as being dependent on CPU performance and "typically [in] the order of milliseconds", + // but this claim seems highly outdated already. On my CPU from 2021, a limit of 4096 equals roughly 600ms. + uregex_setTimeLimit(re, 4096, status); + uregex_setStackLimit(re, 4 * 1024 * 1024, status); + return unique_uregex{ re }; + } +} \ No newline at end of file diff --git a/src/inc/til/small_vector.h b/src/inc/til/small_vector.h index 40bc012ff1..027f0484b5 100644 --- a/src/inc/til/small_vector.h +++ b/src/inc/til/small_vector.h @@ -871,7 +871,7 @@ namespace til // An optimization for the most common vector type which is trivially and copyable and noexcept constructible. // Compared to the complex form below, we don't need the 2 moves and 1 destroy, because is_trivially_copyable_v implies - // that we can just memmove() the items in one fell swoop. We don't need a try/catch either because func() is noexcept. + // that we can just memmove() the items all at once. We don't need a try/catch either because func() is noexcept. if constexpr (noexcept(func(begin())) && std::is_trivially_copyable_v) { _size = new_size; diff --git a/src/inc/til/throttled_func.h b/src/inc/til/throttled_func.h index 7a2d544add..d1704ebd17 100644 --- a/src/inc/til/throttled_func.h +++ b/src/inc/til/throttled_func.h @@ -5,120 +5,49 @@ namespace til { - namespace details + struct throttled_func_options { - template - class throttled_func_storage - { - public: - template - bool emplace(MakeArgs&&... args) - { - std::unique_lock guard{ _lock }; - const bool hadValue = _pendingRunArgs.has_value(); - _pendingRunArgs.emplace(std::forward(args)...); - return hadValue; - } + using filetime_duration = std::chrono::duration>; - template - void modify_pending(F f) - { - std::unique_lock guard{ _lock }; - if (_pendingRunArgs) - { - std::apply(f, *_pendingRunArgs); - } - } + filetime_duration delay{}; + bool debounce = false; + bool leading = false; + bool trailing = false; + }; - void apply(const auto& func) - { - decltype(_pendingRunArgs) args; - { - std::unique_lock guard{ _lock }; - args = std::exchange(_pendingRunArgs, std::nullopt); - } - // Theoretically it should always have a value, because the throttled_func - // should not call the callback without there being a reason. - // But in practice a failure here was observed at least once. - // It's unknown to me what caused it, so the best we can do is avoid a crash. - assert(args.has_value()); - if (args) - { - std::apply(func, *args); - } - } - - explicit operator bool() const - { - std::shared_lock guard{ _lock }; - return _pendingRunArgs.has_value(); - } - - private: - // std::mutex uses imperfect Critical Sections on Windows. - // --> std::shared_mutex uses SRW locks that are small and fast. - mutable std::shared_mutex _lock; - std::optional> _pendingRunArgs; - }; - - template<> - class throttled_func_storage<> - { - public: - bool emplace() - { - return _isPending.exchange(true, std::memory_order_relaxed); - } - - void apply(const auto& func) - { - if (_isPending.exchange(false, std::memory_order_relaxed)) - { - func(); - } - } - - void reset() - { - _isPending.store(false, std::memory_order_relaxed); - } - - explicit operator bool() const - { - return _isPending.load(std::memory_order_relaxed); - } - - private: - std::atomic _isPending; - }; - } // namespace details - - template + template class throttled_func { public: - using filetime_duration = std::chrono::duration>; + using filetime_duration = throttled_func_options::filetime_duration; using function = std::function; - // Throttles invocations to the given `func` to not occur more often than `delay`. + // Throttles invocations to the given `func` to not occur more often than specified in options. // - // If this is a: - // * throttled_func_leading: `func` will be invoked immediately and - // further invocations prevented until `delay` time has passed. - // * throttled_func_trailing: On the first invocation a timer of `delay` time will - // be started. After the timer has expired `func` will be invoked just once. + // Options: + // * delay: The minimum time between invocations + // * debounce: If true, resets the timer on each call + // * leading: If true, `func` will be invoked immediately on first call + // * trailing: If true, `func` will be invoked after the delay // - // After `func` was invoked the state is reset and this cycle is repeated again. - throttled_func(filetime_duration delay, function func) : + // At least one of leading or trailing must be true. + throttled_func(throttled_func_options opts, function func) : _func{ std::move(func) }, - _timer{ _createTimer() } + _timer{ _create_timer() }, + _debounce{ opts.debounce }, + _leading{ opts.leading }, + _trailing{ opts.trailing } { - const auto d = -delay.count(); + if (!_leading && !_trailing) + { + throw std::invalid_argument("neither leading nor trailing"); + } + + const auto d = -opts.delay.count(); if (d >= 0) { throw std::invalid_argument("non-positive delay specified"); } - memcpy(&_delay, &d, sizeof(d)); } @@ -139,27 +68,7 @@ namespace til template void operator()(MakeArgs&&... args) { - const auto hadValue = _storage.emplace(std::forward(args)...); - - if constexpr (Debounce) - { - SetThreadpoolTimerEx(_timer.get(), &_delay, 0, 0); - } - else - { - if (!hadValue) - { - SetThreadpoolTimerEx(_timer.get(), &_delay, 0, 0); - } - } - - if constexpr (Leading) - { - if (!hadValue) - { - _func(); - } - } + _lead(std::make_tuple(std::forward(args)...)); } // Modifies the pending arguments for the next function @@ -170,7 +79,11 @@ namespace til template void modify_pending(F func) { - _storage.modify_pending(func); + const auto guard = _lock.lock_exclusive(); + if (_pendingRunArgs) + { + std::apply(func, *_pendingRunArgs); + } } // Makes sure that the currently pending timer is executed @@ -190,36 +103,76 @@ namespace til static void __stdcall _timer_callback(PTP_CALLBACK_INSTANCE /*instance*/, PVOID context, PTP_TIMER /*timer*/) noexcept try { - const auto self = static_cast(context); - if constexpr (Leading) - { - self->_storage.reset(); - } - else - { - self->_storage.apply(self->_func); - } + static_cast(context)->_trail(); } CATCH_LOG() - wil::unique_threadpool_timer _createTimer() + wil::unique_threadpool_timer _create_timer() { wil::unique_threadpool_timer timer{ CreateThreadpoolTimer(&_timer_callback, this, nullptr) }; THROW_LAST_ERROR_IF(!timer); return timer; } - FILETIME _delay; + void _lead(std::tuple args) + { + bool timerRunning = false; + + { + const auto guard = _lock.lock_exclusive(); + + timerRunning = _timerRunning; + _timerRunning = true; + + if (!timerRunning && _leading) + { + // Call the function immediately on the leading edge. + // See below (out of lock). + } + else if (_trailing) + { + _pendingRunArgs.emplace(std::move(args)); + } + } + + if (!timerRunning && _leading) + { + std::apply(_func, std::move(args)); + } + + if (!timerRunning || _debounce) + { + SetThreadpoolTimerEx(_timer.get(), &_delay, 0, 0); + } + } + + void _trail() + { + decltype(_pendingRunArgs) args; + + { + const auto guard = _lock.lock_exclusive(); + + _timerRunning = false; + args = std::exchange(_pendingRunArgs, std::nullopt); + } + + if (args) + { + std::apply(_func, *std::move(args)); + } + } + function _func; wil::unique_threadpool_timer _timer; - details::throttled_func_storage _storage; + FILETIME _delay; + + wil::srwlock _lock; + std::optional> _pendingRunArgs; + bool _timerRunning = false; + + bool _debounce; + bool _leading; + bool _trailing; }; - - template - using throttled_func_trailing = throttled_func; - using throttled_func_leading = throttled_func; - - template - using debounced_func_trailing = throttled_func; - using debounced_func_leading = throttled_func; } // namespace til diff --git a/src/inc/til/type_traits.h b/src/inc/til/type_traits.h index 1f426155eb..f38380ada9 100644 --- a/src/inc/til/type_traits.h +++ b/src/inc/til/type_traits.h @@ -3,6 +3,11 @@ #pragma once +namespace winrt +{ + struct hstring; +} + namespace til { namespace details @@ -50,6 +55,12 @@ namespace til { using type = std::basic_string_view; }; + + template<> + struct as_view + { + using type = std::wstring_view; + }; } template diff --git a/src/interactivity/base/InteractivityFactory.cpp b/src/interactivity/base/InteractivityFactory.cpp index 8b5647bac6..ecc5c2bc18 100644 --- a/src/interactivity/base/InteractivityFactory.cpp +++ b/src/interactivity/base/InteractivityFactory.cpp @@ -293,7 +293,7 @@ using namespace Microsoft::Console::Interactivity; // - hwnd: Receives the value of the newly created window's HWND. // - owner: the HWND that should be the initial owner of the pseudo window. // Return Value: -// - STATUS_SUCCESS on success, otherwise an appropriate error. +// - STATUS_SUCCESS on success; otherwise, an appropriate error. [[nodiscard]] NTSTATUS InteractivityFactory::CreatePseudoWindow(HWND& hwnd) { hwnd = nullptr; diff --git a/src/interactivity/base/ServiceLocator.cpp b/src/interactivity/base/ServiceLocator.cpp index 24613d852f..3e882022e7 100644 --- a/src/interactivity/base/ServiceLocator.cpp +++ b/src/interactivity/base/ServiceLocator.cpp @@ -67,13 +67,11 @@ void ServiceLocator::RundownAndExit(const HRESULT hr) Sleep(INFINITE); } - // MSFT:40226902 - HOTFIX shutdown on OneCore, by leaking the renderer, thereby - // reducing the change for existing race conditions to turn into deadlocks. -#ifndef NDEBUG // By locking the console, we ensure no background tasks are accessing the // classes we're going to destruct down below (for instance: CursorBlinker). s_globals.getConsoleInformation().LockConsole(); -#endif + + gci.GetVtIo()->Shutdown(); // A History Lesson from MSFT: 13576341: // We introduced RundownAndExit to give services that hold onto important handles @@ -92,13 +90,6 @@ void ServiceLocator::RundownAndExit(const HRESULT hr) // TODO: MSFT: 14397093 - Expand graceful rundown beyond just the Hot Bug input services case. - // MSFT:40226902 - HOTFIX shutdown on OneCore, by leaking the renderer, thereby - // reducing the change for existing race conditions to turn into deadlocks. -#ifndef NDEBUG - delete s_globals.pRender; - s_globals.pRender = nullptr; -#endif - if (s_oneCoreTeardownFunction) { s_oneCoreTeardownFunction(); @@ -107,6 +98,9 @@ void ServiceLocator::RundownAndExit(const HRESULT hr) // MSFT:40226902 - HOTFIX shutdown on OneCore, by leaking the renderer, thereby // reducing the change for existing race conditions to turn into deadlocks. #ifndef NDEBUG + delete s_globals.pRender; + s_globals.pRender = nullptr; + s_consoleWindow.reset(nullptr); #endif diff --git a/src/interactivity/base/lib/sources.dep b/src/interactivity/base/lib/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/interactivity/base/lib/sources.dep +++ b/src/interactivity/base/lib/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/interactivity/onecore/ConIoSrvComm.cpp b/src/interactivity/onecore/ConIoSrvComm.cpp index 3c543e442f..c782e25303 100644 --- a/src/interactivity/onecore/ConIoSrvComm.cpp +++ b/src/interactivity/onecore/ConIoSrvComm.cpp @@ -393,7 +393,58 @@ VOID ConIoSrvComm::HandleFocusEvent(const CIS_EVENT* const Event) { // Wait for the currently running paint operation, if any, // and prevent further attempts to render. - Renderer->WaitForPaintCompletionAndDisable(1000); + // + // When rendering takes place via DirectX, and a console application + // currently owns the screen, and a new console application is launched (or + // the user switches to another console application), the new application + // cannot take over the screen until the active one relinquishes it. This + // blocking mechanism goes as follows: + // + // 1. The console input thread of the new console application connects to + // ConIoSrv; + // 2. While servicing the new connection request, ConIoSrv sends an event to + // the active application letting it know that it has lost focus; + // 3.1 ConIoSrv waits for a reply from the client application; + // 3.2 Meanwhile, the active application receives the focus event and calls + // this method, waiting for the current paint operation to + // finish. + // + // This means that the new application is waiting on the connection request + // reply from ConIoSrv, ConIoSrv is waiting on the active application to + // acknowledge the lost focus event to reply to the new application, and the + // console input thread in the active application is waiting on the renderer + // thread to finish its current paint operation. + // + // Question: what should happen if the wait on the paint operation times + // out? + // + // There are three options: + // + // 1. On timeout, the active console application could reply with an error + // message and terminate itself, effectively relinquishing control of the + // display; + // + // 2. ConIoSrv itself could time out on waiting for a reply, and forcibly + // terminate the active console application; + // + // 3. Let the wait time out and let the user deal with it. Because the wait + // occurs on a single iteration of the renderer thread, it seemed to me that + // the likelihood of failure is extremely small, especially since the client + // console application that the active conhost instance is servicing has no + // say over what happens in the renderer thread, only by proxy. Thus, the + // chance of failure (timeout) is minimal and since the OneCoreUAP console + // is not a massively used piece of software, it didn’t seem that it would + // be a good use of time to build the requisite infrastructure to deal with + // a timeout here, at least not for now. In case of a timeout DirectX will + // catch the mistake of a new application attempting to acquire the display + // while another one still owns it and will flag it as a DWM bug. Right now, + // the active application will wait one second for the paint operation to + // finish. + // + // TODO: MSFT: 11833883 - Determine action when wait on paint operation via + // DirectX on OneCoreUAP times out while switching console + // applications. + Renderer->TriggerTeardown(); // Relinquish control of the graphics device (only one // DirectX application may control the device at any one diff --git a/src/interactivity/onecore/lib/sources.dep b/src/interactivity/onecore/lib/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/interactivity/onecore/lib/sources.dep +++ b/src/interactivity/onecore/lib/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/interactivity/win32/Clipboard.cpp b/src/interactivity/win32/Clipboard.cpp index 8c42e91af9..02db284c33 100644 --- a/src/interactivity/win32/Clipboard.cpp +++ b/src/interactivity/win32/Clipboard.cpp @@ -24,6 +24,22 @@ using namespace Microsoft::Console::Types; #pragma region Public Methods +void Clipboard::CopyText(const std::wstring& text) +{ + const auto clipboard = _openClipboard(ServiceLocator::LocateConsoleWindow()->GetWindowHandle()); + if (!clipboard) + { + LOG_LAST_ERROR(); + return; + } + + EmptyClipboard(); + // As per: https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats + // CF_UNICODETEXT: [...] A null character signals the end of the data. + // --> We add +1 to the length. This works because .c_str() is null-terminated. + _copyToClipboard(CF_UNICODETEXT, text.c_str(), (text.size() + 1) * sizeof(wchar_t)); +} + // Arguments: // - fAlsoCopyFormatting - Place colored HTML & RTF text onto the clipboard as well as the usual plain text. // Return Value: @@ -162,6 +178,11 @@ void Clipboard::StringPaste(_In_reads_(cchData) const wchar_t* const pData, const auto bracketedPasteMode = gci.GetBracketedPasteMode(); auto inEvents = TextToKeyEvents(pData, cchData, vtInputMode && bracketedPasteMode); gci.pInputBuffer->Write(inEvents); + + if (gci.HasActiveOutputBuffer()) + { + gci.GetActiveOutputBuffer().SnapOnInput(0); + } } catch (...) { diff --git a/src/interactivity/win32/ConsoleControl.hpp b/src/interactivity/win32/ConsoleControl.hpp index 1e9efe6c59..ff087bd367 100644 --- a/src/interactivity/win32/ConsoleControl.hpp +++ b/src/interactivity/win32/ConsoleControl.hpp @@ -45,9 +45,9 @@ namespace Microsoft::Console::Interactivity::Win32 }; // IConsoleControl Members - [[nodiscard]] NTSTATUS NotifyConsoleApplication(_In_ DWORD dwProcessId); - [[nodiscard]] NTSTATUS SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground); - [[nodiscard]] NTSTATUS EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags); + [[nodiscard]] NTSTATUS NotifyConsoleApplication(_In_ DWORD dwProcessId) override; + [[nodiscard]] NTSTATUS SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground) override; + [[nodiscard]] NTSTATUS EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags) override; [[nodiscard]] NTSTATUS SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId) noexcept override; // Public Members diff --git a/src/interactivity/win32/CustomWindowMessages.h b/src/interactivity/win32/CustomWindowMessages.h index bf36660249..bcc43fdb7a 100644 --- a/src/interactivity/win32/CustomWindowMessages.h +++ b/src/interactivity/win32/CustomWindowMessages.h @@ -29,4 +29,6 @@ #define CM_SET_KEYBOARD_LAYOUT (WM_USER+19) #endif +#define CM_UPDATE_CLIPBOARD (WM_USER+20) + // clang-format on diff --git a/src/interactivity/win32/WindowMetrics.cpp b/src/interactivity/win32/WindowMetrics.cpp index 637a08feba..f809ebc48d 100644 --- a/src/interactivity/win32/WindowMetrics.cpp +++ b/src/interactivity/win32/WindowMetrics.cpp @@ -75,7 +75,7 @@ til::rect WindowMetrics::GetMaxWindowRectInPixels() // - Gets the maximum possible window rectangle in pixels. Based on the monitor the window is on or the primary monitor if no window exists yet. // Arguments: // - prcSuggested - If we were given a suggested rectangle for where the window is going, we can pass it in here to find out the max size on that monitor. -// - If this value is zero and we had a valid window handle, we'll use that instead. Otherwise the value of 0 will make us use the primary monitor. +// - If this value is zero and we had a valid window handle, we'll use that instead. Otherwise, the value of 0 will make us use the primary monitor. // - pDpiSuggested - The dpi that matches the suggested rect. We will attempt to compute this during the function, but if we fail for some reason, // - the original value passed in will be left untouched. // Return Value: diff --git a/src/interactivity/win32/clipboard.hpp b/src/interactivity/win32/clipboard.hpp index 09990ab514..54851fa55b 100644 --- a/src/interactivity/win32/clipboard.hpp +++ b/src/interactivity/win32/clipboard.hpp @@ -29,6 +29,7 @@ namespace Microsoft::Console::Interactivity::Win32 public: static Clipboard& Instance(); + void CopyText(const std::wstring& text); void Copy(_In_ const bool fAlsoCopyFormatting = false); void Paste(); void PasteDrop(HDROP drop); diff --git a/src/interactivity/win32/lib/sources.dep b/src/interactivity/win32/lib/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/interactivity/win32/lib/sources.dep +++ b/src/interactivity/win32/lib/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/interactivity/win32/sources.inc b/src/interactivity/win32/sources.inc index f816c285db..cd35ffc408 100644 --- a/src/interactivity/win32/sources.inc +++ b/src/interactivity/win32/sources.inc @@ -60,4 +60,5 @@ INCLUDES = \ ..; \ TARGETLIBS = \ + $(TARGETLIBS) \ $(ONECORE_EXTERNAL_SDK_LIB_VPATH_L)\onecore.lib \ diff --git a/src/interactivity/win32/ut_interactivity_win32/sources b/src/interactivity/win32/ut_interactivity_win32/sources index 1822bf5179..55eaa847fc 100644 --- a/src/interactivity/win32/ut_interactivity_win32/sources +++ b/src/interactivity/win32/ut_interactivity_win32/sources @@ -50,7 +50,7 @@ TARGETLIBS = \ $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\dxgi.lib \ $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\propsys.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\api-ms-win-mm-playsound-l1.lib \ - $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-imm-l1-1-0.lib \ + $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-imm-l1.lib \ $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-dwmapi-ext-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-create-l1.lib \ @@ -76,12 +76,14 @@ TARGETLIBS = \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-sysparams-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-window-ext-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-winstamin-l1.lib \ + $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-syscolors-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-shell-shell32-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-uxtheme-themes-l1.lib \ $(ONECORESHELL_INTERNAL_LIB_VPATH_L)\api-ms-win-shell-dataobject-l1.lib \ $(ONECORESHELL_INTERNAL_LIB_VPATH_L)\api-ms-win-shell-namespace-l1.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-uiacore-l1.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-usp10-l1.lib \ + $(ONECORE_EXTERNAL_SDK_LIB_PATH)\ntdll.lib \ $(WINCORE_OBJ_PATH)\console\conint\$(O)\conint.lib \ $(WINCORE_OBJ_PATH)\console\open\src\buffer\out\lib\$(O)\conbufferout.lib \ $(WINCORE_OBJ_PATH)\console\open\src\host\lib\$(O)\conhostv2.lib \ @@ -93,7 +95,6 @@ TARGETLIBS = \ $(WINCORE_OBJ_PATH)\console\open\src\audio\midi\lib\$(O)\ConAudioMidi.lib \ $(WINCORE_OBJ_PATH)\console\open\src\renderer\base\lib\$(O)\ConRenderBase.lib \ $(WINCORE_OBJ_PATH)\console\open\src\renderer\gdi\lib\$(O)\ConRenderGdi.lib \ - $(WINCORE_OBJ_PATH)\console\open\src\renderer\vt\lib\$(O)\ConRenderVt.lib \ $(WINCORE_OBJ_PATH)\console\open\src\renderer\wddmcon\lib\$(O)\ConRenderWddmCon.lib \ $(WINCORE_OBJ_PATH)\console\open\src\server\lib\$(O)\ConServer.lib \ $(WINCORE_OBJ_PATH)\console\open\src\interactivity\base\lib\$(O)\ConInteractivityBaseLib.lib \ @@ -112,7 +113,7 @@ DELAYLOAD = \ OLEAUT32.dll; \ icu.dll; \ api-ms-win-mm-playsound-l1.dll; \ - ext-ms-win-imm-l1-1-0.lib; \ + ext-ms-win-imm-l1.dll; \ api-ms-win-shcore-scaling-l1.dll; \ api-ms-win-shell-dataobject-l1.dll; \ api-ms-win-shell-namespace-l1.dll; \ @@ -143,6 +144,7 @@ DELAYLOAD = \ ext-ms-win-rtcore-ntuser-sysparams-l1.dll; \ ext-ms-win-rtcore-ntuser-window-ext-l1.dll; \ ext-ms-win-rtcore-ntuser-winstamin-l1.dll; \ + ext-ms-win-rtcore-ntuser-syscolors-l1.dll; \ ext-ms-win-shell-shell32-l1.dll; \ ext-ms-win-uiacore-l1.dll; \ ext-ms-win-uxtheme-themes-l1.dll; \ diff --git a/src/interactivity/win32/ut_interactivity_win32/sources.dep b/src/interactivity/win32/ut_interactivity_win32/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/interactivity/win32/ut_interactivity_win32/sources.dep +++ b/src/interactivity/win32/ut_interactivity_win32/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/interactivity/win32/windowio.cpp b/src/interactivity/win32/windowio.cpp index 5afd66b9ce..60fbf826e0 100644 --- a/src/interactivity/win32/windowio.cpp +++ b/src/interactivity/win32/windowio.cpp @@ -567,7 +567,7 @@ BOOL HandleMouseEvent(const SCREEN_INFORMATION& ScreenInfo, if (!fShiftPressed && !pSelection->IsInSelectingState()) { short sDelta = 0; - if (Message == WM_MOUSEWHEEL) + if (Message == WM_MOUSEWHEEL || Message == WM_MOUSEHWHEEL) { sDelta = GET_WHEEL_DELTA_WPARAM(wParam); } diff --git a/src/interactivity/win32/windowproc.cpp b/src/interactivity/win32/windowproc.cpp index c3f1e68c29..3517ddd839 100644 --- a/src/interactivity/win32/windowproc.cpp +++ b/src/interactivity/win32/windowproc.cpp @@ -773,6 +773,15 @@ static constexpr TsfDataProvider s_tsfDataProvider; } #endif // DBG + case CM_UPDATE_CLIPBOARD: + { + if (const auto clipboardText = gci.UsePendingClipboardText()) + { + Clipboard::Instance().CopyText(clipboardText.value()); + } + break; + } + case EVENT_CONSOLE_CARET: case EVENT_CONSOLE_UPDATE_REGION: case EVENT_CONSOLE_UPDATE_SIMPLE: diff --git a/src/project.inc b/src/project.inc index 1d86343d9c..152aca2717 100644 --- a/src/project.inc +++ b/src/project.inc @@ -4,16 +4,16 @@ # ------------------------------------- # Pull our dependencies from vcpkg. This includes gsl - if you run into errors -# from a missing gsl, make sure you go build onecore/window/vcpkg first. +# from a missing gsl or fmt, make sure you go build .../console/vcpkg first. -!include $(PROJECT_ROOT)\vcpkg\consume.inc +!include $(PROJECT_ROOT)\core\console\vcpkg\consume.inc # ------------------------------------- # Preprocessor Settings # ------------------------------------- UNICODE = 1 -C_DEFINES = $(C_DEFINES) -DUNICODE -D_UNICODE -DFMT_HEADER_ONLY -D__INSIDE_WINDOWS -DBUILD_ONECORE_INTERACTIVITY +C_DEFINES = $(C_DEFINES) -DUNICODE -D_UNICODE -D__INSIDE_WINDOWS -DBUILD_ONECORE_INTERACTIVITY # ------------------------------------- # CRT Configuration @@ -50,7 +50,6 @@ INCLUDES= \ $(CONSOLE_SRC_PATH)\inc; \ $(CONSOLE_SRC_PATH)\..\..\inc; \ $(CONSOLE_SRC_PATH)\..\oss\chromium; \ - $(CONSOLE_SRC_PATH)\..\oss\fmt\include; \ $(CONSOLE_SRC_PATH)\..\oss\interval_tree; \ $(CONSOLE_SRC_PATH)\..\oss\pcg\include; \ $(MINWIN_INTERNAL_PRIV_SDK_INC_PATH_L); \ diff --git a/src/project.unittest.inc b/src/project.unittest.inc index 8d625b78e8..a4f5daf4bb 100644 --- a/src/project.unittest.inc +++ b/src/project.unittest.inc @@ -9,7 +9,7 @@ # Preprocessor Settings # ------------------------------------- -C_DEFINES = $(C_DEFINES) -DINLINE_TEST_METHOD_MARKUP -DUNIT_TESTING -DFMT_HEADER_ONLY +C_DEFINES = $(C_DEFINES) -DINLINE_TEST_METHOD_MARKUP -DUNIT_TESTING # ------------------------------------- # Program Information @@ -32,6 +32,7 @@ INCLUDES = \ TARGETLIBS = \ $(TARGETLIBS) \ $(ONECORE_EXTERNAL_SDK_LIB_VPATH_L)\onecore.lib \ + $(ONECORE_EXTERNAL_SDK_LIB_PATH)\ntdll.lib \ $(ONECORESDKTOOLS_INTERNAL_LIB_PATH_L)\WexTest\Cue\Wex.Common.lib \ $(ONECORESDKTOOLS_INTERNAL_LIB_PATH_L)\WexTest\Cue\Wex.Logger.lib \ $(ONECORESDKTOOLS_INTERNAL_LIB_PATH_L)\WexTest\Cue\Te.Common.lib \ diff --git a/src/propsheet/preview.cpp b/src/propsheet/preview.cpp index 64ed9fae40..9becd7b703 100644 --- a/src/propsheet/preview.cpp +++ b/src/propsheet/preview.cpp @@ -422,7 +422,7 @@ VOID PreviewPaint( * return = n1 * m / n2 * This can be used to make an aspect ration calculation where n1/n2 * is the aspect ratio and m is a known value. The return value will - * be the value that corresponds to m with the correct apsect ratio. + * be the value that corresponds to m with the correct aspect ratio. */ LONG AspectScale( diff --git a/src/propsheet/propsheet.vcxproj b/src/propsheet/propsheet.vcxproj index dd31fa42d3..77ea31ef93 100644 --- a/src/propsheet/propsheet.vcxproj +++ b/src/propsheet/propsheet.vcxproj @@ -7,6 +7,7 @@ Propsheet.DLL console DynamicLibrary + Windows Console Host Property Sheet diff --git a/src/propsheet/sources b/src/propsheet/sources index 033d3a5396..84bf81282e 100644 --- a/src/propsheet/sources +++ b/src/propsheet/sources @@ -78,6 +78,7 @@ INCLUDES = \ ..\host; \ TARGETLIBS = \ + $(TARGETLIBS) \ $(ONECORE_EXTERNAL_SDK_LIB_VPATH_L)\onecore.lib \ $(ONECORE_EXTERNAL_SDK_LIB_VPATH_L)\onecoreuap.lib \ $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\onecore_internal.lib \ diff --git a/src/propsheet/sources.dep b/src/propsheet/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/propsheet/sources.dep +++ b/src/propsheet/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/propslib/DelegationConfig.cpp b/src/propslib/DelegationConfig.cpp index 29339baa2e..2ba4d85ed5 100644 --- a/src/propslib/DelegationConfig.cpp +++ b/src/propslib/DelegationConfig.cpp @@ -293,7 +293,7 @@ try RETURN_IF_NTSTATUS_FAILED(RegistrySerialization::s_OpenConsoleKey(¤tUserKey, &consoleKey)); - // Create method for registry is a "create if not exists, otherwise open" function. + // Create method for registry is a "create if not exists; otherwise, open" function. wil::unique_hkey startupKey; RETURN_IF_NTSTATUS_FAILED(RegistrySerialization::s_CreateKey(consoleKey.get(), L"%%Startup", &startupKey)); diff --git a/src/propslib/sources.dep b/src/propslib/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/propslib/sources.dep +++ b/src/propslib/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index 90b9083b5d..af9a92f89a 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -268,7 +268,7 @@ try } // PaintCursor() is only called when the cursor is visible, but we need to invalidate the cursor area - // even if it isn't. Otherwise a transition from a visible to an invisible cursor wouldn't be rendered. + // even if it isn't. Otherwise, a transition from a visible to an invisible cursor wouldn't be rendered. if (const auto r = _api.invalidatedCursorArea; r.non_empty()) { _p.dirtyRectInPx.left = std::min(_p.dirtyRectInPx.left, r.left * _p.s->font->cellSize.x); @@ -322,7 +322,7 @@ CATCH_RETURN() auto misc = _api.s.write()->misc.write(); misc->selectionColor = newSelectionColor; // Select a black or white foreground based on the perceptual lightness of the background. - misc->selectionForeground = ColorFix::GetLuminosity(newSelectionColor) < 0.5f ? 0xffffffff : 0xff000000; + misc->selectionForeground = ColorFix::GetLightness(newSelectionColor) < 0.5f ? 0xffffffff : 0xff000000; // We copied the selection colors into _p during StartPaint, which happened just before PrepareRenderInfo // This keeps their generations in sync. @@ -500,8 +500,13 @@ try { for (const auto& cluster : clusters) { - for (const auto& ch : cluster.GetText()) + for (auto ch : cluster.GetText()) { + // Render Unicode directional isolate characters (U+2066..U+2069) as zero-width spaces. + if (ch >= L'\u2066' && ch <= L'\u2069') + { + ch = L'\u200B'; + } _api.bufferLine.emplace_back(ch); _api.bufferLineColumn.emplace_back(columnEnd); } @@ -651,7 +656,7 @@ try if (!isSettingDefaultBrushes) { auto attributes = FontRelevantAttributes::None; - WI_SetFlagIf(attributes, FontRelevantAttributes::Bold, textAttributes.IsIntense() && renderSettings.GetRenderMode(RenderSettings::Mode::IntenseIsBold)); + WI_SetFlagIf(attributes, FontRelevantAttributes::Bold, textAttributes.IsBold(renderSettings.GetRenderMode(RenderSettings::Mode::IntenseIsBold))); WI_SetFlagIf(attributes, FontRelevantAttributes::Italic, textAttributes.IsItalic()); if (_api.attributes != attributes) diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp index 2acb128591..60ee6aaf1d 100644 --- a/src/renderer/atlas/BackendD3D.cpp +++ b/src/renderer/atlas/BackendD3D.cpp @@ -2324,7 +2324,7 @@ void BackendD3D::_executeCustomShader(RenderingPayload& p) { // Before we do anything else we have to unbound _renderTargetView from being - // a render target, otherwise we can't use it as a shader resource below. + // a render target; otherwise, we can't use it as a shader resource below. p.deviceContext->OMSetRenderTargets(1, _renderTargetView.addressof(), nullptr); // IA: Input Assembler diff --git a/src/renderer/atlas/README.md b/src/renderer/atlas/README.md index c72e77f51e..a1619e9b4b 100644 --- a/src/renderer/atlas/README.md +++ b/src/renderer/atlas/README.md @@ -4,18 +4,18 @@ ```mermaid graph TD - RenderThread["RenderThread (base/thread.cpp)\ncalls Renderer::PaintFrame() x times per sec"] - Renderer["Renderer (base/renderer.cpp)\nbreaks the text buffer down into GDI-oriented graphics\nprimitives (#quot;change brush to color X#quot;, #quot;draw string Y#quot;, ...)"] - RenderEngineBase[/"RenderEngineBase\n(base/RenderEngineBase.cpp)\nabstracts 24 LOC 👻"\] + RenderThread["RenderThread (base/thread.cpp)
calls Renderer::PaintFrame() x times per sec"] + Renderer["Renderer (base/renderer.cpp)
breaks the text buffer down into GDI-oriented graphics
primitives (#quot;change brush to color X#quot;, #quot;draw string Y#quot;, ...)
"] + RenderEngineBase[/"RenderEngineBase
(base/RenderEngineBase.cpp)
abstracts 24 LOC 👻"\] GdiEngine["GdiEngine (gdi/...)"] subgraph AtlasEngine["AtlasEngine (atlas/...)"] - AtlasEngine.cpp["AtlasEngine.cpp\nImplements IRenderEngine text rendering API\nbreaks GDI graphics primitives down into DWRITE_GLYPH_RUNs"] - AtlasEngine.api.cpp["AtlasEngine.api.cpp\nImplements the parts run inside the console\nlock (many IRenderEngine setters)"] - AtlasEngine.r.cpp["AtlasEngine.r.cpp\nImplements the parts run\noutside of the console lock"] - Backend.cpp["Backend.cpp\nImplements common functionality/helpers"] - BackendD2D.cpp["BackendD2D.cpp\nPure Direct2D text renderer (for low latency\nremote desktop and older/no GPUs)"] - BackendD3D.cpp["BackendD3D.cpp\nCustom, performant text renderer\nwith our own glyph cache"] + AtlasEngine.cpp["AtlasEngine.cpp
Implements IRenderEngine text rendering API
breaks GDI graphics primitives down into DWRITE_GLYPH_RUNs
"] + AtlasEngine.api.cpp["AtlasEngine.api.cpp
Implements the parts run inside the console
lock (many IRenderEngine setters)"] + AtlasEngine.r.cpp["AtlasEngine.r.cpp
Implements the parts run
outside of the console lock"] + Backend.cpp["Backend.cpp
Implements common functionality/helpers"] + BackendD2D.cpp["BackendD2D.cpp
Pure Direct2D text renderer (for low latency
remote desktop and older/no GPUs)
"] + BackendD3D.cpp["BackendD3D.cpp
Custom, performant text renderer
with our own glyph cache
"] end RenderThread --> Renderer @@ -63,8 +63,8 @@ graph TD ```mermaid graph TD - Render --> _drawCursorPart1["_drawCursorPart1\nruns before _drawText\ndraws cursors that are behind the text"] - Render --> _drawCursorPart2["_drawCursorPart2\nruns after _drawText\ndraws inverted cursors"] + Render --> _drawCursorPart1["_drawCursorPart1
runs before _drawText
draws cursors that are behind the text"] + Render --> _drawCursorPart2["_drawCursorPart2
runs after _drawText
draws inverted cursors"] _drawCursorPart1 -.->|_cursorRects| _drawCursorPart2 ``` @@ -81,26 +81,26 @@ graph TD foreachFont --> foreachGlyph(("for each glyph")) foreachGlyph --> foreachGlyph - foreachGlyph --> _glyphAtlasMap[("font/glyph-pair lookup in\nglyph cache hashmap")] + foreachGlyph --> _glyphAtlasMap[("font/glyph-pair lookup in
glyph cache hashmap")] _glyphAtlasMap --> drawGlyph - drawGlyph --> _appendQuad["_appendQuad\nstages the glyph for later drawing"] + drawGlyph --> _appendQuad["_appendQuad
stages the glyph for later drawing"] _glyphAtlasMap --> _appendQuad subgraph drawGlyph["if glyph is missing"] - _drawGlyph["_drawGlyph\n(defers to _drawSoftFontGlyph for soft fonts)"] + _drawGlyph["_drawGlyph
(defers to _drawSoftFontGlyph for soft fonts)"] _drawGlyph -.->|if glpyh cache is full| _drawGlyphPrepareRetry - _drawGlyphPrepareRetry --> _flushQuads["_flushQuads\ndraws the current state\ninto the render target"] - _flushQuads --> _recreateInstanceBuffers["_recreateInstanceBuffers\nallocates a GPU buffer\nfor our glyph instances"] - _drawGlyphPrepareRetry --> _resetGlyphAtlas["_resetGlyphAtlas\nclears the glyph texture"] - _resetGlyphAtlas --> _resizeGlyphAtlas["_resizeGlyphAtlas\nresizes the glyph texture if it's still small"] + _drawGlyphPrepareRetry --> _flushQuads["_flushQuads
draws the current state
into the render target
"] + _flushQuads --> _recreateInstanceBuffers["_recreateInstanceBuffers
allocates a GPU buffer
for our glyph instances
"] + _drawGlyphPrepareRetry --> _resetGlyphAtlas["_resetGlyphAtlas
clears the glyph texture"] + _resetGlyphAtlas --> _resizeGlyphAtlas["_resizeGlyphAtlas
resizes the glyph texture if it's still small"] - _drawGlyph -.->|if it's a DECDHL glyph| _splitDoubleHeightGlyph["_splitDoubleHeightGlyph\nDECDHL glyphs are split up into their\ntop/bottom halves to emulate clip rects"] + _drawGlyph -.->|if it's a DECDHL glyph| _splitDoubleHeightGlyph["_splitDoubleHeightGlyph
DECDHL glyphs are split up into their
top/bottom halves to emulate clip rects
"] end - foreachGlyph -.-> _drawTextOverlapSplit["_drawTextOverlapSplit\nsplits overly wide glyphs up into smaller chunks to support\nforeground color changes within the ligature"] + foreachGlyph -.-> _drawTextOverlapSplit["_drawTextOverlapSplit
splits overly wide glyphs up into smaller chunks to support
foreground color changes within the ligature
"] - foreachRow -.->|if gridlines exist| _drawGridlineRow["_drawGridlineRow\ndraws underlines, etc."] + foreachRow -.->|if gridlines exist| _drawGridlineRow["_drawGridlineRow
draws underlines, etc."] ``` ### `_drawSelection` diff --git a/src/renderer/atlas/common.h b/src/renderer/atlas/common.h index 3f70ae6748..6668e2ce3c 100644 --- a/src/renderer/atlas/common.h +++ b/src/renderer/atlas/common.h @@ -265,7 +265,7 @@ namespace Microsoft::Console::Render::Atlas private: // These two functions don't need to use scoped objects or standard allocators, - // since this class is in fact an scoped allocator object itself. + // since this class is in fact a scoped allocator object itself. #pragma warning(push) #pragma warning(disable : 26402) // Return a scoped object instead of a heap-allocated if it has a move constructor (r.3). #pragma warning(disable : 26409) // Avoid calling new and delete explicitly, use std::make_unique instead (r.11). diff --git a/src/renderer/base/RenderSettings.cpp b/src/renderer/base/RenderSettings.cpp index 0410211143..c587c60128 100644 --- a/src/renderer/base/RenderSettings.cpp +++ b/src/renderer/base/RenderSettings.cpp @@ -46,9 +46,9 @@ void RenderSettings::RestoreDefaultSettings() noexcept { _colorTable = _defaultColorTable; _colorAliasIndices = _defaultColorAliasIndices; - // For now, DECSCNM is the only render mode we need to reset. The others are - // all user preferences that can't be changed programmatically. - _renderMode.reset(Mode::ScreenReversed); + // DECSCNM and Synchronized Output are the only render mode we need to reset. + // The others are all user preferences that can't be changed programmatically. + _renderMode.reset(Mode::ScreenReversed, Mode::SynchronizedOutput); } // Routine Description: @@ -112,6 +112,20 @@ COLORREF RenderSettings::GetColorTableEntry(const size_t tableIndex) const return _colorTable.at(tableIndex); } +// Routine Description: +// - Restores all of the xterm-addressable colors to the ones saved in SaveDefaultSettings. +void RenderSettings::RestoreDefaultIndexed256ColorTable() +{ + std::copy_n(_defaultColorTable.begin(), 256, _colorTable.begin()); +} + +// Routine Description: +// - Restores a color table entry to the value saved in SaveDefaultSettings. +void RenderSettings::RestoreDefaultColorTableEntry(const size_t tableIndex) +{ + _colorTable.at(tableIndex) = _defaultColorTable.at(tableIndex); +} + // Routine Description: // - Sets the position in the color table for the given color alias and updates the color. // Arguments: @@ -159,6 +173,11 @@ size_t RenderSettings::GetColorAliasIndex(const ColorAlias alias) const noexcept return gsl::at(_colorAliasIndices, static_cast(alias)); } +void RenderSettings::RestoreDefaultColorAliasIndex(const ColorAlias alias) noexcept +{ + gsl::at(_colorAliasIndices, static_cast(alias)) = gsl::at(_defaultColorAliasIndices, static_cast(alias)); +} + // Routine Description: // - Calculates the RGB colors of a given text attribute, using the current // color table configuration and active render settings. diff --git a/src/renderer/base/lib/sources.dep b/src/renderer/base/lib/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/renderer/base/lib/sources.dep +++ b/src/renderer/base/lib/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 31ab5a410e..9567b8cd49 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -4,8 +4,6 @@ #include "precomp.h" #include "renderer.hpp" -#pragma hdrstop - using namespace Microsoft::Console::Render; using namespace Microsoft::Console::Types; @@ -25,35 +23,12 @@ static constexpr auto renderBackoffBaseTimeMilliseconds{ 150 }; // - Creates a new renderer controller for a console. // Arguments: // - pData - The interface to console data structures required for rendering -// - pEngine - The output engine for targeting each rendering frame // Return Value: // - An instance of a Renderer. -Renderer::Renderer(const RenderSettings& renderSettings, - IRenderData* pData, - _In_reads_(cEngines) IRenderEngine** const rgpEngines, - const size_t cEngines, - std::unique_ptr thread) : +Renderer::Renderer(RenderSettings& renderSettings, IRenderData* pData) : _renderSettings(renderSettings), - _pData(pData), - _pThread{ std::move(thread) } + _pData(pData) { - for (size_t i = 0; i < cEngines; i++) - { - AddRenderEngine(rgpEngines[i]); - } -} - -// Routine Description: -// - Destroys an instance of a renderer -// Arguments: -// - -// Return Value: -// - -Renderer::~Renderer() -{ - // RenderThread blocks until it has shut down. - _destructing = true; - _pThread.reset(); } IRenderData* Renderer::GetRenderData() const noexcept @@ -72,11 +47,6 @@ IRenderData* Renderer::GetRenderData() const noexcept auto tries = maxRetriesForRenderEngine; while (tries > 0) { - if (_destructing) - { - return S_FALSE; - } - // BODGY: Optimally we would want to retry per engine, but that causes different // problems (intermittent inconsistent states between text renderer and UIA output, // not being able to lock the cursor location, etc.). @@ -91,7 +61,7 @@ IRenderData* Renderer::GetRenderData() const noexcept if (--tries == 0) { // Stop trying. - _pThread->DisablePainting(); + _thread.DisablePainting(); if (_pfnRendererEnteredErrorState) { _pfnRendererEnteredErrorState(); @@ -118,6 +88,11 @@ IRenderData* Renderer::GetRenderData() const noexcept _pData->UnlockConsole(); }); + if (_isSynchronizingOutput) + { + _synchronizeWithOutput(); + } + // Last chance check if anything scrolled without an explicit invalidate notification since the last frame. _CheckViewportAndScroll(); @@ -207,12 +182,79 @@ CATCH_RETURN() void Renderer::NotifyPaintFrame() noexcept { - // If we're running in the unittests, we might not have a render thread. - if (_pThread) + // The thread will provide throttling for us. + _thread.NotifyPaint(); +} + +// NOTE: You must be holding the console lock when calling this function. +void Renderer::SynchronizedOutputChanged() noexcept +{ + const auto so = _renderSettings.GetRenderMode(RenderSettings::Mode::SynchronizedOutput); + if (_isSynchronizingOutput == so) { - // The thread will provide throttling for us. - _pThread->NotifyPaint(); + return; } + + // If `_isSynchronizingOutput` is true, it'll kick the + // render thread into calling `_synchronizeWithOutput()`... + _isSynchronizingOutput = so; + + if (!_isSynchronizingOutput) + { + // ...otherwise, unblock `_synchronizeWithOutput()` from the `WaitOnAddress` call. + WakeByAddressSingle(&_isSynchronizingOutput); + + // It's crucial to give the render thread at least a chance to gain the lock. + // Otherwise, a VT application could continuously spam DECSET 2026 (Synchronized Output) and + // essentially drop our renderer to 10 FPS, because `_isSynchronizingOutput` is always true. + // + // Obviously calling LockConsole/UnlockConsole here is an awful, ugly hack, + // since there's no guarantee that this is the same lock as the one the VT parser uses. + // But the alternative is Denial-Of-Service of the render thread. + // + // Note that this causes raw throughput of DECSET 2026 to be comparatively low, but that's fine. + // Apps that use DECSET 2026 don't produce that sequence continuously, but rather at a fixed rate. + _pData->UnlockConsole(); + _pData->LockConsole(); + } +} + +void Renderer::_synchronizeWithOutput() noexcept +{ + constexpr DWORD timeout = 100; + + UINT64 start = 0; + DWORD elapsed = 0; + bool wrong = false; + + QueryUnbiasedInterruptTime(&start); + + // Wait for `_isSynchronizingOutput` to be set to false or for a timeout to occur. + while (true) + { + // We can't call a blocking function while holding the console lock, so release it temporarily. + _pData->UnlockConsole(); + const auto ok = WaitOnAddress(&_isSynchronizingOutput, &wrong, sizeof(_isSynchronizingOutput), timeout - elapsed); + _pData->LockConsole(); + + if (!ok || !_isSynchronizingOutput) + { + break; + } + + UINT64 now; + QueryUnbiasedInterruptTime(&now); + elapsed = static_cast((now - start) / 10000); + if (elapsed >= timeout) + { + break; + } + } + + // If a timeout occurred, `_isSynchronizingOutput` may still be true. + // Set it to false now to skip calling `_synchronizeWithOutput()` on the next frame. + _isSynchronizingOutput = false; + _renderSettings.SetRenderMode(RenderSettings::Mode::SynchronizedOutput, false); } // Routine Description: @@ -315,7 +357,7 @@ void Renderer::TriggerRedrawAll(const bool backgroundChanged, const bool frameCh void Renderer::TriggerTeardown() noexcept { // We need to shut down the paint thread on teardown. - _pThread->WaitForPaintCompletionAndDisable(INFINITE); + _thread.TriggerTeardown(); } // Routine Description: @@ -637,25 +679,7 @@ void Renderer::EnablePainting() // When the renderer is constructed, the initial viewport won't be available yet, // but once EnablePainting is called it should be safe to retrieve. _viewport = _pData->GetViewport(); - - // When running the unit tests, we may be using a render without a render thread. - if (_pThread) - { - _pThread->EnablePainting(); - } -} - -// Routine Description: -// - Waits for the current paint operation to complete, if any, up to the specified timeout. -// - Resets an event in the render thread that precludes it from advancing, thus disabling rendering. -// - If no paint operation is currently underway, returns immediately. -// Arguments: -// - dwTimeoutMs - Milliseconds to wait for the current paint operation to complete, if any (can be INFINITE). -// Return Value: -// - -void Renderer::WaitForPaintCompletionAndDisable(const DWORD dwTimeoutMs) -{ - _pThread->WaitForPaintCompletionAndDisable(dwTimeoutMs); + _thread.EnablePainting(); } // Routine Description: @@ -1092,7 +1116,7 @@ bool Renderer::_isInHoveredInterval(const til::point coordTarget) const noexcept // Arguments: // - // Return Value: -// - nullopt if the cursor is off or out-of-frame, otherwise a CursorOptions +// - nullopt if the cursor is off or out-of-frame; otherwise, a CursorOptions void Renderer::_updateCursorInfo() { // Get cursor position in buffer @@ -1437,14 +1461,6 @@ void Renderer::SetRendererEnteredErrorStateCallback(std::function pfn) _pfnRendererEnteredErrorState = std::move(pfn); } -// Method Description: -// - Attempts to restart the renderer. -void Renderer::ResetErrorStateAndResume() -{ - // because we're not stateful (we could be in the future), all we want to do is reenable painting. - EnablePainting(); -} - void Renderer::UpdateHyperlinkHoveredId(uint16_t id) noexcept { _hyperlinkHoveredId = id; diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index 8921937462..31acc72860 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -28,19 +28,14 @@ namespace Microsoft::Console::Render class Renderer { public: - Renderer(const RenderSettings& renderSettings, - IRenderData* pData, - _In_reads_(cEngines) IRenderEngine** const pEngine, - const size_t cEngines, - std::unique_ptr thread); - - ~Renderer(); + Renderer(RenderSettings& renderSettings, IRenderData* pData); IRenderData* GetRenderData() const noexcept; [[nodiscard]] HRESULT PaintFrame(); void NotifyPaintFrame() noexcept; + void SynchronizedOutputChanged() noexcept; void TriggerSystemRedraw(const til::rect* const prcDirtyClient); void TriggerRedraw(const Microsoft::Console::Types::Viewport& region); void TriggerRedraw(const til::point* const pcoord); @@ -71,7 +66,6 @@ namespace Microsoft::Console::Render bool IsGlyphWideByFont(const std::wstring_view glyph); void EnablePainting(); - void WaitForPaintCompletionAndDisable(const DWORD dwTimeoutMs); void WaitUntilCanRender(); void AddRenderEngine(_In_ IRenderEngine* const pEngine); @@ -80,7 +74,6 @@ namespace Microsoft::Console::Render void SetBackgroundColorChangedCallback(std::function pfn); void SetFrameColorChangedCallback(std::function pfn); void SetRendererEnteredErrorStateCallback(std::function pfn); - void ResetErrorStateAndResume(); void UpdateHyperlinkHoveredId(uint16_t id) noexcept; void UpdateLastHoveredInterval(const std::optional::interval>& newInterval); @@ -99,6 +92,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT _PaintFrame() noexcept; [[nodiscard]] HRESULT _PaintFrameForEngine(_In_ IRenderEngine* const pEngine) noexcept; + void _synchronizeWithOutput() noexcept; bool _CheckViewportAndScroll(); [[nodiscard]] HRESULT _PaintBackground(_In_ IRenderEngine* const pEngine); void _PaintBufferOutput(_In_ IRenderEngine* const pEngine); @@ -118,26 +112,29 @@ namespace Microsoft::Console::Render void _prepareNewComposition(); [[nodiscard]] HRESULT _PrepareRenderInfo(_In_ IRenderEngine* const pEngine); - const RenderSettings& _renderSettings; + RenderSettings& _renderSettings; std::array _engines{}; IRenderData* _pData = nullptr; // Non-ownership pointer - std::unique_ptr _pThread; static constexpr size_t _firstSoftFontChar = 0xEF20; size_t _lastSoftFontChar = 0; uint16_t _hyperlinkHoveredId = 0; std::optional::interval> _hoveredInterval; Microsoft::Console::Types::Viewport _viewport; - CursorOptions _currentCursorOptions; + CursorOptions _currentCursorOptions{}; std::optional _compositionCache; std::vector _clusterBuffer; std::function _pfnBackgroundColorChanged; std::function _pfnFrameColorChanged; std::function _pfnRendererEnteredErrorState; - bool _destructing = false; + bool _isSynchronizingOutput = false; bool _forceUpdateViewport = false; til::point_span _lastSelectionPaintSpan{}; size_t _lastSelectionPaintSize{}; std::vector _lastSelectionRectsByViewport{}; + + // Ordered last, so that it gets destroyed first. + // This ensures that the render thread stops accessing us. + RenderThread _thread{ this }; }; } diff --git a/src/renderer/base/thread.cpp b/src/renderer/base/thread.cpp index 904cf2075a..39b7ec6078 100644 --- a/src/renderer/base/thread.cpp +++ b/src/renderer/base/thread.cpp @@ -10,212 +10,40 @@ using namespace Microsoft::Console::Render; -RenderThread::RenderThread() : - _pRenderer(nullptr), - _hThread(nullptr), - _hEvent(nullptr), - _hPaintCompletedEvent(nullptr), - _fKeepRunning(true), - _hPaintEnabledEvent(nullptr), - _fNextFrameRequested(false), - _fWaiting(false) +RenderThread::RenderThread(Renderer* renderer) : + renderer(renderer) { } RenderThread::~RenderThread() { - if (_hThread) - { - _fKeepRunning = false; // stop loop after final run - EnablePainting(); // if we want to get the last frame out, we need to make sure it's enabled - SignalObjectAndWait(_hEvent, _hThread, INFINITE, FALSE); // signal final paint and wait for thread to finish. - - CloseHandle(_hThread); - _hThread = nullptr; - } - - if (_hEvent) - { - CloseHandle(_hEvent); - _hEvent = nullptr; - } - - if (_hPaintEnabledEvent) - { - CloseHandle(_hPaintEnabledEvent); - _hPaintEnabledEvent = nullptr; - } - - if (_hPaintCompletedEvent) - { - CloseHandle(_hPaintCompletedEvent); - _hPaintCompletedEvent = nullptr; - } -} - -// Method Description: -// - Create all of the Events we'll need, and the actual thread we'll be doing -// work on. -// Arguments: -// - pRendererParent: the Renderer that owns this thread, and which we should -// trigger frames for. -// Return Value: -// - S_OK if we succeeded, else an HRESULT corresponding to a failure to create -// an Event or Thread. -[[nodiscard]] HRESULT RenderThread::Initialize(Renderer* const pRendererParent) noexcept -{ - _pRenderer = pRendererParent; - - auto hr = S_OK; - // Create event before thread as thread will start immediately. - if (SUCCEEDED(hr)) - { - auto hEvent = CreateEventW(nullptr, // non-inheritable security attributes - FALSE, // auto reset event - FALSE, // initially unsignaled - nullptr // no name - ); - - if (hEvent == nullptr) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - } - else - { - _hEvent = hEvent; - } - } - - if (SUCCEEDED(hr)) - { - auto hPaintEnabledEvent = CreateEventW(nullptr, - TRUE, // manual reset event - FALSE, // initially signaled - nullptr); - - if (hPaintEnabledEvent == nullptr) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - } - else - { - _hPaintEnabledEvent = hPaintEnabledEvent; - } - } - - if (SUCCEEDED(hr)) - { - auto hPaintCompletedEvent = CreateEventW(nullptr, - TRUE, // manual reset event - TRUE, // initially signaled - nullptr); - - if (hPaintCompletedEvent == nullptr) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - } - else - { - _hPaintCompletedEvent = hPaintCompletedEvent; - } - } - - if (SUCCEEDED(hr)) - { - auto hThread = CreateThread(nullptr, // non-inheritable security attributes - 0, // use default stack size - s_ThreadProc, - this, - 0, // create immediately - nullptr // we don't need the thread ID - ); - - if (hThread == nullptr) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - } - else - { - _hThread = hThread; - - // SetThreadDescription only works on 1607 and higher. If we cannot find it, - // then it's no big deal. Just skip setting the description. - auto func = GetProcAddressByFunctionDeclaration(GetModuleHandleW(L"kernel32.dll"), SetThreadDescription); - if (func) - { - LOG_IF_FAILED(func(hThread, L"Rendering Output Thread")); - } - } - } - - return hr; + TriggerTeardown(); } DWORD WINAPI RenderThread::s_ThreadProc(_In_ LPVOID lpParameter) { const auto pContext = static_cast(lpParameter); - - if (pContext != nullptr) - { - return pContext->_ThreadProc(); - } - else - { - return (DWORD)E_INVALIDARG; - } + return pContext->_ThreadProc(); } DWORD WINAPI RenderThread::_ThreadProc() { - while (_fKeepRunning) + while (true) { + _enable.wait(); + // Between waiting on _hEvent and calling PaintFrame() there should be a minimal delay, // so that a key press progresses to a drawing operation as quickly as possible. - // As such, we wait for the renderer to complete _before_ waiting on _hEvent. - _pRenderer->WaitUntilCanRender(); + // As such, we wait for the renderer to complete _before_ waiting on `_redraw`. + renderer->WaitUntilCanRender(); - WaitForSingleObject(_hPaintEnabledEvent, INFINITE); - - if (!_fNextFrameRequested.exchange(false, std::memory_order_acq_rel)) + _redraw.wait(); + if (!_keepRunning.load(std::memory_order_relaxed)) { - // <-- - // If `NotifyPaint` is called at this point, then it will not - // set the event because `_fWaiting` is not `true` yet so we have - // to check again below. - - _fWaiting.store(true, std::memory_order_release); - - // check again now (see comment above) - if (!_fNextFrameRequested.exchange(false, std::memory_order_acq_rel)) - { - // Wait until a next frame is requested. - WaitForSingleObject(_hEvent, INFINITE); - } - - // <-- - // If `NotifyPaint` is called at this point, then it _will_ set - // the event because `_fWaiting` is `true`, but we're not waiting - // anymore! - // This can probably happen quite often: imagine a scenario where - // we are waiting, and the terminal calls `NotifyPaint` twice - // very quickly. - // In that case, both calls might end up calling `SetEvent`. The - // first one will resume this thread and the second one will - // `SetEvent` the event. So the next time we wait, the event will - // already be set and we won't actually wait. - // Because it can happen often, and because rendering is an - // expensive operation, we should reset the event to not render - // again if nothing changed. - - _fWaiting.store(false, std::memory_order_release); - - // see comment above - ResetEvent(_hEvent); + break; } - ResetEvent(_hPaintCompletedEvent); - LOG_IF_FAILED(_pRenderer->PaintFrame()); - SetEvent(_hPaintCompletedEvent); + LOG_IF_FAILED(renderer->PaintFrame()); } return S_OK; @@ -223,79 +51,56 @@ DWORD WINAPI RenderThread::_ThreadProc() void RenderThread::NotifyPaint() noexcept { - if (_fWaiting.load(std::memory_order_acquire)) - { - SetEvent(_hEvent); - } - else - { - _fNextFrameRequested.store(true, std::memory_order_release); - } + _redraw.SetEvent(); } +// Spawns a new rendering thread if none exists yet. void RenderThread::EnablePainting() noexcept { - SetEvent(_hPaintEnabledEvent); + const auto guard = _threadMutex.lock_exclusive(); + + _enable.SetEvent(); + + if (!_thread) + { + _keepRunning.store(true, std::memory_order_relaxed); + + _thread.reset(CreateThread(nullptr, 0, s_ThreadProc, this, 0, nullptr)); + THROW_LAST_ERROR_IF(!_thread); + + // SetThreadDescription only works on 1607 and higher. If we cannot find it, + // then it's no big deal. Just skip setting the description. + const auto func = GetProcAddressByFunctionDeclaration(GetModuleHandleW(L"kernel32.dll"), SetThreadDescription); + if (func) + { + LOG_IF_FAILED(func(_thread.get(), L"Rendering Output Thread")); + } + } } +// This function is meant to only be called by `Renderer`. You should use `TriggerTeardown()` instead, +// even if you plan to call `EnablePainting()` later, because that ensures proper synchronization. void RenderThread::DisablePainting() noexcept { - ResetEvent(_hPaintEnabledEvent); + _enable.ResetEvent(); } -void RenderThread::WaitForPaintCompletionAndDisable(const DWORD dwTimeoutMs) noexcept +// Stops the rendering thread, and waits for it to finish. +void RenderThread::TriggerTeardown() noexcept { - // When rendering takes place via DirectX, and a console application - // currently owns the screen, and a new console application is launched (or - // the user switches to another console application), the new application - // cannot take over the screen until the active one relinquishes it. This - // blocking mechanism goes as follows: - // - // 1. The console input thread of the new console application connects to - // ConIoSrv; - // 2. While servicing the new connection request, ConIoSrv sends an event to - // the active application letting it know that it has lost focus; - // 3.1 ConIoSrv waits for a reply from the client application; - // 3.2 Meanwhile, the active application receives the focus event and calls - // this method, waiting for the current paint operation to - // finish. - // - // This means that the new application is waiting on the connection request - // reply from ConIoSrv, ConIoSrv is waiting on the active application to - // acknowledge the lost focus event to reply to the new application, and the - // console input thread in the active application is waiting on the renderer - // thread to finish its current paint operation. - // - // Question: what should happen if the wait on the paint operation times - // out? - // - // There are three options: - // - // 1. On timeout, the active console application could reply with an error - // message and terminate itself, effectively relinquishing control of the - // display; - // - // 2. ConIoSrv itself could time out on waiting for a reply, and forcibly - // terminate the active console application; - // - // 3. Let the wait time out and let the user deal with it. Because the wait - // occurs on a single iteration of the renderer thread, it seemed to me that - // the likelihood of failure is extremely small, especially since the client - // console application that the active conhost instance is servicing has no - // say over what happens in the renderer thread, only by proxy. Thus, the - // chance of failure (timeout) is minimal and since the OneCoreUAP console - // is not a massively used piece of software, it didn’t seem that it would - // be a good use of time to build the requisite infrastructure to deal with - // a timeout here, at least not for now. In case of a timeout DirectX will - // catch the mistake of a new application attempting to acquire the display - // while another one still owns it and will flag it as a DWM bug. Right now, - // the active application will wait one second for the paint operation to - // finish. - // - // TODO: MSFT: 11833883 - Determine action when wait on paint operation via - // DirectX on OneCoreUAP times out while switching console - // applications. + const auto guard = _threadMutex.lock_exclusive(); - ResetEvent(_hPaintEnabledEvent); - WaitForSingleObject(_hPaintCompletedEvent, dwTimeoutMs); + if (_thread) + { + // The render thread first waits for the event and then checks _keepRunning. By doing it + // in reverse order here, we ensure that it's impossible for the render thread to miss this. + _keepRunning.store(false, std::memory_order_relaxed); + _redraw.SetEvent(); + _enable.SetEvent(); + + WaitForSingleObject(_thread.get(), INFINITE); + _thread.reset(); + } + + DisablePainting(); } diff --git a/src/renderer/base/thread.hpp b/src/renderer/base/thread.hpp index 982eccd99b..fbc921465d 100644 --- a/src/renderer/base/thread.hpp +++ b/src/renderer/base/thread.hpp @@ -21,30 +21,23 @@ namespace Microsoft::Console::Render class RenderThread { public: - RenderThread(); + RenderThread(Renderer* renderer); ~RenderThread(); - [[nodiscard]] HRESULT Initialize(Renderer* const pRendererParent) noexcept; - void NotifyPaint() noexcept; void EnablePainting() noexcept; void DisablePainting() noexcept; - void WaitForPaintCompletionAndDisable(const DWORD dwTimeoutMs) noexcept; + void TriggerTeardown() noexcept; private: static DWORD WINAPI s_ThreadProc(_In_ LPVOID lpParameter); DWORD WINAPI _ThreadProc(); - HANDLE _hThread; - HANDLE _hEvent; - - HANDLE _hPaintEnabledEvent; - HANDLE _hPaintCompletedEvent; - - Renderer* _pRenderer; // Non-ownership pointer - - bool _fKeepRunning; - std::atomic _fNextFrameRequested; - std::atomic _fWaiting; + Renderer* renderer; + wil::slim_event_manual_reset _enable; + wil::slim_event_auto_reset _redraw; + wil::srwlock _threadMutex; + wil::unique_handle _thread; + std::atomic _keepRunning{ false }; }; } diff --git a/src/renderer/gdi/lib/sources.dep b/src/renderer/gdi/lib/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/renderer/gdi/lib/sources.dep +++ b/src/renderer/gdi/lib/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index 86db0de940..20c27b1e61 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -574,7 +574,8 @@ try } const auto cpt = gsl::narrow_cast(points.size()); - return PolyBezier(_hdcMemoryContext, points.data(), cpt); + RETURN_HR_IF(E_FAIL, !PolyBezier(_hdcMemoryContext, points.data(), cpt)); + return S_OK; }; if (lines.test(GridLines::Left)) diff --git a/src/renderer/gdi/state.cpp b/src/renderer/gdi/state.cpp index d8f98ef50f..bf583f7850 100644 --- a/src/renderer/gdi/state.cpp +++ b/src/renderer/gdi/state.cpp @@ -560,7 +560,7 @@ GdiEngine::~GdiEngine() // Arguments: // - newTitle: the new string to use for the title of the window // Return Value: -// - S_OK if PostMessageW succeeded, otherwise E_FAIL +// - S_OK if PostMessageW succeeded; otherwise, E_FAIL [[nodiscard]] HRESULT GdiEngine::_DoUpdateTitle(_In_ const std::wstring_view /*newTitle*/) noexcept { // the CM_UPDATE_TITLE handler in windowproc will query the updated title. diff --git a/src/renderer/inc/DummyRenderer.hpp b/src/renderer/inc/DummyRenderer.hpp index b1e43b9d5f..dd03e54450 100644 --- a/src/renderer/inc/DummyRenderer.hpp +++ b/src/renderer/inc/DummyRenderer.hpp @@ -18,7 +18,7 @@ class DummyRenderer final : public Microsoft::Console::Render::Renderer { public: DummyRenderer(Microsoft::Console::Render::IRenderData* pData = nullptr) : - Microsoft::Console::Render::Renderer(_renderSettings, pData, nullptr, 0, nullptr) {} + Microsoft::Console::Render::Renderer(_renderSettings, pData) {} Microsoft::Console::Render::RenderSettings _renderSettings; }; diff --git a/src/renderer/inc/RenderSettings.hpp b/src/renderer/inc/RenderSettings.hpp index a9c370133c..8b0d64379f 100644 --- a/src/renderer/inc/RenderSettings.hpp +++ b/src/renderer/inc/RenderSettings.hpp @@ -25,7 +25,8 @@ namespace Microsoft::Console::Render AlwaysDistinguishableColors, IntenseIsBold, IntenseIsBright, - ScreenReversed + ScreenReversed, + SynchronizedOutput, }; RenderSettings() noexcept; @@ -37,10 +38,13 @@ namespace Microsoft::Console::Render void ResetColorTable() noexcept; void SetColorTableEntry(const size_t tableIndex, const COLORREF color); COLORREF GetColorTableEntry(const size_t tableIndex) const; + void RestoreDefaultIndexed256ColorTable(); + void RestoreDefaultColorTableEntry(const size_t tableIndex); void SetColorAlias(const ColorAlias alias, const size_t tableIndex, const COLORREF color); COLORREF GetColorAlias(const ColorAlias alias) const; void SetColorAliasIndex(const ColorAlias alias, const size_t tableIndex) noexcept; size_t GetColorAliasIndex(const ColorAlias alias) const noexcept; + void RestoreDefaultColorAliasIndex(const ColorAlias alias) noexcept; std::pair GetAttributeColors(const TextAttribute& attr) const noexcept; std::pair GetAttributeColorsWithAlpha(const TextAttribute& attr) const noexcept; COLORREF GetAttributeUnderlineColor(const TextAttribute& attr) const noexcept; diff --git a/src/renderer/wddmcon/lib/sources.dep b/src/renderer/wddmcon/lib/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/renderer/wddmcon/lib/sources.dep +++ b/src/renderer/wddmcon/lib/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/server/ApiDispatchers.cpp b/src/server/ApiDispatchers.cpp index 6c99344e80..be9a2fae85 100644 --- a/src/server/ApiDispatchers.cpp +++ b/src/server/ApiDispatchers.cpp @@ -173,7 +173,6 @@ constexpr T saturate(auto val) const auto pInputReadHandleData = pHandleData->GetClientInput(); - std::unique_ptr waiter; InputEventQueue outEvents; auto hr = m->_pApiRoutines->GetConsoleInputImpl( *pInputBuffer, @@ -183,7 +182,7 @@ constexpr T saturate(auto val) a->Unicode, fIsPeek, fIsWaitAllowed, - waiter); + m); // We must return the number of records in the message payload (to alert the client) // as well as in the message headers (below in SetReplyInformation) to alert the driver. @@ -192,14 +191,10 @@ constexpr T saturate(auto val) size_t cbWritten; LOG_IF_FAILED(SizeTMult(outEvents.size(), sizeof(INPUT_RECORD), &cbWritten)); - if (waiter) + if (hr == CONSOLE_STATUS_WAIT) { - hr = ConsoleWaitQueue::s_CreateWait(m, waiter.release()); - if (SUCCEEDED(hr)) - { - *pbReplyPending = TRUE; - hr = CONSOLE_STATUS_WAIT; - } + hr = S_OK; + *pbReplyPending = TRUE; } else { @@ -290,14 +285,13 @@ constexpr T saturate(auto val) // across multiple calls when we are simulating a command prompt input line for the client application. const auto pInputReadHandleData = HandleData->GetClientInput(); - std::unique_ptr waiter; size_t cbWritten; const std::span outputBuffer(reinterpret_cast(pvBuffer), cbBufferSize); auto hr = m->_pApiRoutines->ReadConsoleImpl(*pInputBuffer, outputBuffer, cbWritten, // We must set the reply length in bytes. - waiter, + m, initialData, exeView, *pInputReadHandleData, @@ -308,15 +302,10 @@ constexpr T saturate(auto val) LOG_IF_FAILED(SizeTToULong(cbWritten, &a->NumBytes)); - if (nullptr != waiter.get()) + if (hr == CONSOLE_STATUS_WAIT) { - // If we received a waiter, we need to queue the wait and not reply. - hr = ConsoleWaitQueue::s_CreateWait(m, waiter.release()); - - if (SUCCEEDED(hr)) - { - *pbReplyPending = TRUE; - } + hr = S_OK; + *pbReplyPending = TRUE; } else { @@ -355,7 +344,6 @@ constexpr T saturate(auto val) ULONG cbBufferSize; RETURN_IF_FAILED(m->GetInputBuffer(&pvBuffer, &cbBufferSize)); - std::unique_ptr waiter; size_t cbRead; // We have to hold onto the HR from the call and return it. @@ -373,7 +361,7 @@ constexpr T saturate(auto val) TraceLoggingUInt32(a->NumBytes, "NumBytes"), TraceLoggingCountedWideString(buffer.data(), static_cast(buffer.size()), "Buffer")); - hr = m->_pApiRoutines->WriteConsoleWImpl(*pScreenInfo, buffer, cchInputRead, waiter); + hr = m->_pApiRoutines->WriteConsoleWImpl(*pScreenInfo, buffer, cchInputRead, m); // We must set the reply length in bytes. Convert back from characters. LOG_IF_FAILED(SizeTMult(cchInputRead, sizeof(wchar_t), &cbRead)); @@ -388,7 +376,7 @@ constexpr T saturate(auto val) TraceLoggingUInt32(a->NumBytes, "NumBytes"), TraceLoggingCountedString(buffer.data(), static_cast(buffer.size()), "Buffer")); - hr = m->_pApiRoutines->WriteConsoleAImpl(*pScreenInfo, buffer, cchInputRead, waiter); + hr = m->_pApiRoutines->WriteConsoleAImpl(*pScreenInfo, buffer, cchInputRead, m); // Reply length is already in bytes (chars), don't need to convert. cbRead = cchInputRead; @@ -397,14 +385,10 @@ constexpr T saturate(auto val) // We must return the byte length of the read data in the message. LOG_IF_FAILED(SizeTToULong(cbRead, &a->NumBytes)); - if (nullptr != waiter.get()) + if (hr == CONSOLE_STATUS_WAIT) { - // If we received a waiter, we need to queue the wait and not reply. - hr = ConsoleWaitQueue::s_CreateWait(m, waiter.release()); - if (SUCCEEDED(hr)) - { - *pbReplyPending = TRUE; - } + hr = S_OK; + *pbReplyPending = TRUE; } else { diff --git a/src/server/IApiRoutines.h b/src/server/IApiRoutines.h index 568b150968..eb53f347a3 100644 --- a/src/server/IApiRoutines.h +++ b/src/server/IApiRoutines.h @@ -27,6 +27,8 @@ typedef InputBuffer IConsoleInputObject; class INPUT_READ_HANDLE_DATA; +typedef struct _CONSOLE_API_MSG CONSOLE_API_MSG; + #include "IWaitRoutine.h" #include "../types/inc/IInputEvent.hpp" #include "../types/inc/viewport.hpp" @@ -64,12 +66,12 @@ public: const bool IsUnicode, const bool IsPeek, const bool IsWaitAllowed, - std::unique_ptr& waiter) noexcept = 0; + CONSOLE_API_MSG* pWaitReplyMessage) noexcept = 0; [[nodiscard]] virtual HRESULT ReadConsoleImpl(IConsoleInputObject& context, std::span buffer, size_t& written, - std::unique_ptr& waiter, + CONSOLE_API_MSG* pWaitReplyMessage, const std::wstring_view initialData, const std::wstring_view exeName, INPUT_READ_HANDLE_DATA& readHandleState, @@ -81,12 +83,12 @@ public: [[nodiscard]] virtual HRESULT WriteConsoleAImpl(IConsoleOutputObject& context, const std::string_view buffer, size_t& read, - std::unique_ptr& waiter) noexcept = 0; + CONSOLE_API_MSG* pWaitReplyMessage) noexcept = 0; [[nodiscard]] virtual HRESULT WriteConsoleWImpl(IConsoleOutputObject& context, const std::wstring_view buffer, size_t& read, - std::unique_ptr& waiter) noexcept = 0; + CONSOLE_API_MSG* pWaitReplyMessage) noexcept = 0; #pragma region Thread Creation Info [[nodiscard]] virtual HRESULT GetConsoleLangIdImpl(LANGID& langId) noexcept = 0; diff --git a/src/server/IWaitRoutine.h b/src/server/IWaitRoutine.h index 59ffa905bd..b213c26fc4 100644 --- a/src/server/IWaitRoutine.h +++ b/src/server/IWaitRoutine.h @@ -42,6 +42,10 @@ public: IWaitRoutine& operator=(const IWaitRoutine&) & = delete; IWaitRoutine& operator=(IWaitRoutine&&) & = delete; + virtual const SCREEN_INFORMATION* GetScreenBuffer() const noexcept + { + return nullptr; + } virtual void MigrateUserBuffersOnTransitionToBackgroundWait(const void* oldBuffer, void* newBuffer) = 0; virtual bool Notify(const WaitTerminationReason TerminationReason, diff --git a/src/server/IoDispatchers.cpp b/src/server/IoDispatchers.cpp index da08215756..0e5cf60da8 100644 --- a/src/server/IoDispatchers.cpp +++ b/src/server/IoDispatchers.cpp @@ -486,7 +486,7 @@ PCONSOLE_API_MSG IoDispatchers::ConsoleHandleConnectionRequest(_In_ PCONSOLE_API return pReceiveMsg; } - // For future code archeologists: GH#2988 + // For future code archaeologists: GH#2988 // // Here, the console calls ConsoleControl(ConsoleSetForeground,...) with a // flag depending on if the console is focused or not. This is surprisingly diff --git a/src/server/WaitBlock.cpp b/src/server/WaitBlock.cpp index b26dffcb58..d1485f7068 100644 --- a/src/server/WaitBlock.cpp +++ b/src/server/WaitBlock.cpp @@ -75,6 +75,11 @@ ConsoleWaitBlock::~ConsoleWaitBlock() delete _pWaiter; } +const SCREEN_INFORMATION* ConsoleWaitBlock::GetScreenBuffer() const noexcept +{ + return _pWaiter->GetScreenBuffer(); +} + // Routine Description: // - Creates and enqueues a new wait for later callback when a routine cannot be serviced at this time. // - Will extract the process ID and the target object, enqueuing in both to know when to callback @@ -86,6 +91,15 @@ ConsoleWaitBlock::~ConsoleWaitBlock() [[nodiscard]] HRESULT ConsoleWaitBlock::s_CreateWait(_Inout_ CONSOLE_API_MSG* const pWaitReplyMessage, _In_ IWaitRoutine* const pWaiter) { + if (!pWaitReplyMessage || !pWaiter) + { + if (pWaiter) + { + delete pWaiter; + } + return E_INVALIDARG; + } + const auto ProcessData = pWaitReplyMessage->GetProcessHandle(); FAIL_FAST_IF_NULL(ProcessData); diff --git a/src/server/WaitBlock.h b/src/server/WaitBlock.h index 560eaee91f..38e8f51ebd 100644 --- a/src/server/WaitBlock.h +++ b/src/server/WaitBlock.h @@ -30,6 +30,7 @@ class ConsoleWaitBlock public: ~ConsoleWaitBlock(); + const SCREEN_INFORMATION* GetScreenBuffer() const noexcept; bool Notify(const WaitTerminationReason TerminationReason); [[nodiscard]] static HRESULT s_CreateWait(_Inout_ CONSOLE_API_MSG* const pWaitReplymessage, diff --git a/src/server/WaitQueue.cpp b/src/server/WaitQueue.cpp index b6231ea46f..d714c65011 100644 --- a/src/server/WaitQueue.cpp +++ b/src/server/WaitQueue.cpp @@ -103,6 +103,22 @@ bool ConsoleWaitQueue::NotifyWaiters(const bool fNotifyAll, return fResult; } +void ConsoleWaitQueue::CancelWaitersForScreenBuffer(const SCREEN_INFORMATION* pScreenInfo) +{ + for (auto it = _blocks.begin(); it != _blocks.end();) + { + const auto nextIt = std::next(it); // we have to capture next before it is potentially erased + auto* pWaitBlock = *it; + + if (pWaitBlock->GetScreenBuffer() == pScreenInfo) + { + _NotifyBlock(pWaitBlock, WaitTerminationReason::HandleClosing); + } + + it = nextIt; + } +} + // Routine Description: // - A helper to delete successfully notified callbacks // Arguments: diff --git a/src/server/WaitQueue.h b/src/server/WaitQueue.h index 929f364446..a23c7059c9 100644 --- a/src/server/WaitQueue.h +++ b/src/server/WaitQueue.h @@ -37,10 +37,11 @@ public: bool NotifyWaiters(const bool fNotifyAll, const WaitTerminationReason TerminationReason); + void CancelWaitersForScreenBuffer(const SCREEN_INFORMATION* pScreenInfo); + [[nodiscard]] static HRESULT s_CreateWait(_Inout_ CONSOLE_API_MSG* const pWaitReplyMessage, _In_ IWaitRoutine* const pWaiter); -private: bool _NotifyBlock(_In_ ConsoleWaitBlock* pWaitBlock, const WaitTerminationReason TerminationReason); diff --git a/src/server/lib/sources.dep b/src/server/lib/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/server/lib/sources.dep +++ b/src/server/lib/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/terminal/adapter/DispatchTypes.hpp b/src/terminal/adapter/DispatchTypes.hpp index f1adb1457c..b99dd997dd 100644 --- a/src/terminal/adapter/DispatchTypes.hpp +++ b/src/terminal/adapter/DispatchTypes.hpp @@ -544,6 +544,7 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes ALTERNATE_SCROLL = DECPrivateMode(1007), ASB_AlternateScreenBuffer = DECPrivateMode(1049), XTERM_BracketedPasteMode = DECPrivateMode(2004), + SO_SynchronizedOutput = DECPrivateMode(2026), GCM_GraphemeClusterMode = DECPrivateMode(2027), W32IM_Win32InputMode = DECPrivateMode(9001), }; diff --git a/src/terminal/adapter/FontBuffer.cpp b/src/terminal/adapter/FontBuffer.cpp index dabee138f7..bd0a2e2c94 100644 --- a/src/terminal/adapter/FontBuffer.cpp +++ b/src/terminal/adapter/FontBuffer.cpp @@ -259,7 +259,7 @@ void FontBuffer::_prepareCharacterBuffer() { // If any of the attributes have changed since the last time characters // were downloaded, the font dimensions will need to be recalculated, and - // the buffer will need to be cleared. Otherwise we'll just be adding to + // the buffer will need to be cleared. Otherwise, we'll just be adding to // the existing font, assuming the current dimensions. if (_cellMatrix != _pendingCellMatrix || _cellHeight != _pendingCellHeight || @@ -316,7 +316,7 @@ void FontBuffer::_addSixelValue(const VTInt value) noexcept { if (_currentChar < MAX_CHARS && _sixelColumn < _textWidth) { - // Each sixel updates six pixels of a single column, so we setup a bit + // Each sixel updates six pixels of a single column, so we set up a bit // mask for the column we want to update, and then set that bit in each // row for which there is a corresponding "on" bit in the input value. const auto outputColumnBit = (0x8000 >> (_sixelColumn + _textOffset)); @@ -397,7 +397,7 @@ std::tuple FontBuffer::_calculateDimensions() const } // Now we're going to test whether the dimensions are in range for a number - // of known terminals. We use the declared dimensions if given, otherwise + // of known terminals. We use the declared dimensions if given; otherwise, // estimate the size from the used sixel values. If comparing a sixel-based // height, though, we need to round up the target cell height to account for // the fact that our used height will always be a multiple of six. diff --git a/src/terminal/adapter/ITermDispatch.hpp b/src/terminal/adapter/ITermDispatch.hpp index 1576571aa9..5ebb2ff31c 100644 --- a/src/terminal/adapter/ITermDispatch.hpp +++ b/src/terminal/adapter/ITermDispatch.hpp @@ -25,6 +25,12 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch public: using StringHandler = std::function; + enum class OptionalFeature + { + ChecksumReport, + ClipboardWrite, + }; + #pragma warning(push) #pragma warning(disable : 26432) // suppress rule of 5 violation on interface because tampering with this is fraught with peril virtual ~ITermDispatch() = 0; @@ -78,8 +84,11 @@ public: virtual void TabSet(const VTParameter setType) = 0; // DECST8C virtual void SetColorTableEntry(const size_t tableIndex, const DWORD color) = 0; // OSCSetColorTable virtual void RequestColorTableEntry(const size_t tableIndex) = 0; // OSCGetColorTable - virtual void SetXtermColorResource(const size_t resource, const DWORD color) = 0; // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor, OSCResetCursorColor + virtual void ResetColorTable() = 0; // OSCResetColorTable + virtual void ResetColorTableEntry(const size_t tableIndex) = 0; // OSCResetColorTable + virtual void SetXtermColorResource(const size_t resource, const DWORD color) = 0; // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor virtual void RequestXtermColorResource(const size_t resource) = 0; // OSCGetDefaultForeground, OSCGetDefaultBackground, OSCGetCursorColor + virtual void ResetXtermColorResource(const size_t resource) = 0; // OSCResetForegroundColor, OSCResetBackgroundColor, OSCResetCursorColor, OSCResetHighlightColor virtual void AssignColor(const DispatchTypes::ColorItem item, const VTInt fgIndex, const VTInt bgIndex) = 0; // DECAC virtual void EraseInDisplay(const DispatchTypes::EraseType eraseType) = 0; // ED @@ -130,7 +139,6 @@ public: virtual void ScreenAlignmentPattern() = 0; // DECALN virtual void SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) = 0; // DECSCUSR - virtual void SetVtChecksumReportSupport(const bool enabled) = 0; virtual void SetClipboard(wil::zwstring_view content) = 0; // OSCSetClipboard @@ -182,6 +190,8 @@ public: virtual StringHandler RestorePresentationState(const DispatchTypes::PresentationReportFormat format) = 0; // DECRSPS virtual void PlaySounds(const VTParameters parameters) = 0; // DECPS + + virtual void SetOptionalFeatures(const til::enumset features) = 0; }; inline Microsoft::Console::VirtualTerminal::ITermDispatch::~ITermDispatch() = default; #pragma warning(pop) diff --git a/src/terminal/adapter/ITerminalApi.hpp b/src/terminal/adapter/ITerminalApi.hpp index 532133f9a8..3ce0e5c83d 100644 --- a/src/terminal/adapter/ITerminalApi.hpp +++ b/src/terminal/adapter/ITerminalApi.hpp @@ -86,6 +86,7 @@ namespace Microsoft::Console::VirtualTerminal virtual void NotifyAccessibilityChange(const til::rect& changedRect) = 0; virtual void NotifyBufferRotation(const int delta) = 0; + virtual void NotifyShellIntegrationMark() = 0; virtual void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) = 0; diff --git a/src/terminal/adapter/InteractDispatch.cpp b/src/terminal/adapter/InteractDispatch.cpp index 09cc2c970c..8ed76b9d7c 100644 --- a/src/terminal/adapter/InteractDispatch.cpp +++ b/src/terminal/adapter/InteractDispatch.cpp @@ -62,15 +62,38 @@ void InteractDispatch::WriteString(const std::wstring_view string) { if (!string.empty()) { - const auto codepage = _api.GetOutputCodePage(); - InputEventQueue keyEvents; + const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); +#pragma warning(suppress : 26429) // Symbol 'inputBuffer' is never tested for nullness, it can be marked as not_null (f.23). + const auto inputBuffer = gci.GetActiveInputBuffer(); - for (const auto& wch : string) + // The input *may* be keyboard input in which case we must call CharToKeyEvents. + // + // However, it could also be legitimate VT sequences (e.g. a bracketed paste sequence). + // If we called `InputBuffer::Write` with those, we would end up indirectly + // calling `TerminalInput::HandleKey` and "double encode" the sequence. + // The effect of this is noticeable with the German keyboard layout, for instance, + // where the [ key maps to AltGr+8, and we fail to map it back to [ later. + // + // It's worth noting that all of this is bad design in either case. + // The way it should work is that we write INPUT_RECORDs and Strings as-is into the + // InputBuffer, and only during retrieval they're converted into one or the other. + // This prevents any kinds of double-encoding issues. + if (inputBuffer->IsInVirtualTerminalInputMode()) { - CharToKeyEvents(wch, codepage, keyEvents); + inputBuffer->WriteString(string); } + else + { + const auto codepage = _api.GetOutputCodePage(); + InputEventQueue keyEvents; - WriteInput(keyEvents); + for (const auto& wch : string) + { + CharToKeyEvents(wch, codepage, keyEvents); + } + + inputBuffer->Write(keyEvents); + } } } diff --git a/src/terminal/adapter/MacroBuffer.hpp b/src/terminal/adapter/MacroBuffer.hpp index 92c1c07c73..86e5dfed88 100644 --- a/src/terminal/adapter/MacroBuffer.hpp +++ b/src/terminal/adapter/MacroBuffer.hpp @@ -30,7 +30,7 @@ namespace Microsoft::Console::VirtualTerminal public: // The original DEC terminals only supported 6K of memory, which is // probably a bit low for modern usage. But we also don't want to make - // this value too large, otherwise it could be used in a denial-of- + // this value too large; otherwise, it could be used in a denial-of- // service attack. So for now this is probably a sufficient limit, but // we may need to increase it in the future if we intend to support // macros containing sixel sequences. diff --git a/src/terminal/adapter/SixelParser.cpp b/src/terminal/adapter/SixelParser.cpp index 979dd42371..15b9499d2a 100644 --- a/src/terminal/adapter/SixelParser.cpp +++ b/src/terminal/adapter/SixelParser.cpp @@ -237,6 +237,7 @@ void SixelParser::_executeNextLine() _imageCursor.y += _sixelHeight; _availablePixelHeight -= _sixelHeight; _resizeImageBuffer(_sixelHeight); + _fillImageBackgroundWhenScrolled(); } void SixelParser::_executeMoveToHome() @@ -341,6 +342,12 @@ void SixelParser::_initRasterAttributes(const VTInt macroParameter, const Dispat // By default, the filled area will cover the maximum extent allowed. _backgroundSize = { til::CoordTypeMax, til::CoordTypeMax }; + + // If the image ends up extending beyond the bottom of the page, we may need + // to perform additional background filling as the screen is scrolled, which + // requires us to track the area filled so far. This will be initialized, if + // necessary, in the _fillImageBackground method below. + _filledBackgroundHeight = std::nullopt; } void SixelParser::_updateRasterAttributes(const VTParameters& rasterAttributes) @@ -373,6 +380,15 @@ void SixelParser::_updateRasterAttributes(const VTParameters& rasterAttributes) // back to the dimensions from an earlier raster attributes command. _backgroundSize.width = width > 0 ? width : _backgroundSize.width; _backgroundSize.height = height > 0 ? height : _backgroundSize.height; + + // If the aspect ratio has changed, the image height may increase, and that + // could potentially trigger a scroll requiring the background to be filled. + _fillImageBackgroundWhenScrolled(); + + // And while not documented, we know from testing on a VT330 that the raster + // attributes command should also trigger a carriage return. This applies + // regardless of whether the requested aspect ratio is valid or not. + _executeCarriageReturn(); } void SixelParser::_scrollTextBuffer(Page& page, const int scrollAmount) @@ -610,7 +626,7 @@ void SixelParser::_updateTextColors() // the text output as well. if (_conformanceLevel <= 3 && _maxColors > 2 && _colorTableChanged) [[unlikely]] { - for (IndexType tableIndex = 0; tableIndex < _maxColors; tableIndex++) + for (IndexType tableIndex = 0; _maxColors <= 16 && tableIndex < _maxColors; tableIndex++) { _dispatcher.SetColorTableEntry(tableIndex, _colorFromIndex(tableIndex)); } @@ -656,24 +672,60 @@ void SixelParser::_fillImageBackground() { _backgroundFillRequired = false; - const auto backgroundHeight = std::min(_backgroundSize.height, _availablePixelHeight); - const auto backgroundWidth = std::min(_backgroundSize.width, _availablePixelWidth); - _resizeImageBuffer(backgroundHeight); - // When a background fill is requested, we prefill the buffer with the 0 // color index, up to the boundaries set by the raster attributes (or if // none were given, up to the page boundaries). The actual image output // isn't limited by the background dimensions though. - static constexpr auto backgroundPixel = IndexedPixel{}; - const auto backgroundOffset = _imageCursor.y * _imageMaxWidth; - auto dst = std::next(_imageBuffer.begin(), backgroundOffset); - for (auto i = 0; i < backgroundHeight; i++) - { - std::fill_n(dst, backgroundWidth, backgroundPixel); - std::advance(dst, _imageMaxWidth); - } + const auto backgroundHeight = std::min(_backgroundSize.height, _availablePixelHeight); + _resizeImageBuffer(backgroundHeight); + _fillImageBackground(backgroundHeight); + // When the image extends beyond the page boundaries, and the screen is + // scrolled, we also need to fill the newly exposed lines, so we keep a + // record of the area filled so far. Initially this is considered to be + // the available height, even if it wasn't all filled to start with. + _filledBackgroundHeight = _imageCursor.y + _availablePixelHeight; + _fillImageBackgroundWhenScrolled(); + } +} - _imageWidth = std::max(_imageWidth, backgroundWidth); +void SixelParser::_fillImageBackground(const int backgroundHeight) +{ + static constexpr auto backgroundPixel = IndexedPixel{}; + const auto backgroundWidth = std::min(_backgroundSize.width, _availablePixelWidth); + const auto backgroundOffset = _imageCursor.y * _imageMaxWidth; + auto dst = std::next(_imageBuffer.begin(), backgroundOffset); + for (auto i = 0; i < backgroundHeight; i++) + { + std::fill_n(dst, backgroundWidth, backgroundPixel); + std::advance(dst, _imageMaxWidth); + } + _imageWidth = std::max(_imageWidth, backgroundWidth); +} + +void SixelParser::_fillImageBackgroundWhenScrolled() +{ + // If _filledBackgroundHeight is set, that means a background fill has been + // requested, and we need to extend that area whenever the image is about to + // overrun it. The newly filled area is a multiple of the cell height (this + // is to match the behavior of the original hardware terminals). + const auto imageHeight = _imageCursor.y + _sixelHeight; + if (_filledBackgroundHeight && imageHeight > _filledBackgroundHeight) [[unlikely]] + { + _filledBackgroundHeight = (imageHeight + _cellSize.height - 1) / _cellSize.height * _cellSize.height; + const auto additionalFillHeight = *_filledBackgroundHeight - _imageCursor.y; + _resizeImageBuffer(additionalFillHeight); + _fillImageBackground(additionalFillHeight); + } +} + +void SixelParser::_decreaseFilledBackgroundHeight(const int decreasedHeight) noexcept +{ + // Sometimes the top of the image buffer may be clipped (e.g. when the image + // scrolls off the top of a margin area). When that occurs, our record of + // the filled height will need to be decreased to account for the new start. + if (_filledBackgroundHeight) [[unlikely]] + { + _filledBackgroundHeight = *_filledBackgroundHeight - decreasedHeight; } } @@ -717,11 +769,13 @@ void SixelParser::_eraseImageBufferRows(const int rowCount, const til::CoordType const auto bufferOffsetEnd = bufferOffset + pixelCount * _imageMaxWidth; if (static_cast(bufferOffsetEnd) >= _imageBuffer.size()) [[unlikely]] { + _decreaseFilledBackgroundHeight(_imageCursor.y); _imageBuffer.clear(); _imageCursor.y = 0; } else { + _decreaseFilledBackgroundHeight(pixelCount); _imageBuffer.erase(_imageBuffer.begin() + bufferOffset, _imageBuffer.begin() + bufferOffsetEnd); _imageCursor.y -= pixelCount; } diff --git a/src/terminal/adapter/SixelParser.hpp b/src/terminal/adapter/SixelParser.hpp index ea97783fec..62a7f7eae7 100644 --- a/src/terminal/adapter/SixelParser.hpp +++ b/src/terminal/adapter/SixelParser.hpp @@ -89,6 +89,7 @@ namespace Microsoft::Console::VirtualTerminal til::CoordType _pendingTextScrollCount = 0; til::size _backgroundSize; bool _backgroundFillRequired = false; + std::optional _filledBackgroundHeight; void _initColorMap(const VTParameter backgroundColor); void _defineColor(const VTParameters& colorParameters); @@ -109,6 +110,9 @@ namespace Microsoft::Console::VirtualTerminal void _initImageBuffer(); void _resizeImageBuffer(const til::CoordType requiredHeight); void _fillImageBackground(); + void _fillImageBackground(const int backgroundHeight); + void _fillImageBackgroundWhenScrolled(); + void _decreaseFilledBackgroundHeight(const int decreasedHeight) noexcept; void _writeToImageBuffer(const int sixelValue, const int repeatCount); void _eraseImageBufferRows(const int rowCount, const til::CoordType startRow = 0) noexcept; void _maybeFlushImageBuffer(const bool endOfSequence = false); diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index fe1b3c9a8a..5de6cdd602 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -160,13 +160,6 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string) } const auto textPositionAfter = state.text.data(); - // TODO: A row should not be marked as wrapped just because we wrote the last column. - // It should be marked whenever we write _past_ it (above, _DoLineFeed call). See GH#15602. - if (wrapAtEOL && state.columnEnd >= state.columnLimit) - { - textBuffer.SetWrapForced(cursorPosition.y, true); - } - if (state.columnBeginDirty != state.columnEndDirty) { const til::rect changedRect{ state.columnBeginDirty, cursorPosition.y, state.columnEndDirty, cursorPosition.y + 1 }; @@ -1016,7 +1009,7 @@ void AdaptDispatch::_ChangeRectOrStreamAttributes(const til::rect& changeArea, c // top line is altered from the left offset up to the end of the line. The // bottom line is altered from the start up to the right offset. All the // lines in-between have their entire length altered. The right coordinate - // must be greater than the left, otherwise the operation is ignored. + // must be greater than the left; otherwise, the operation is ignored. else if (lineCount > 1 && changeRect.right > changeRect.left) { const auto pageWidth = page.Width(); @@ -1304,11 +1297,6 @@ void AdaptDispatch::SelectAttributeChangeExtent(const DispatchTypes::ChangeExten } } -void AdaptDispatch::SetVtChecksumReportSupport(const bool enabled) noexcept -{ - _vtChecksumReportEnabled = enabled; -} - // Routine Description: // - DECRQCRA - Computes and reports a checksum of the specified area of // the buffer memory. @@ -1325,7 +1313,7 @@ void AdaptDispatch::RequestChecksumRectangularArea(const VTInt id, const VTInt p // If this feature is not enabled, we'll just report a zero checksum. if constexpr (Feature_VtChecksumReport::IsEnabled()) { - if (_vtChecksumReportEnabled) + if (_optionalFeatures.test(OptionalFeature::ChecksumReport)) { // If the page number is 0, then we're meant to return a checksum of all // of the pages, but we have no need for that, so we'll just return 0. @@ -1334,7 +1322,7 @@ void AdaptDispatch::RequestChecksumRectangularArea(const VTInt id, const VTInt p // As part of the checksum, we need to include the color indices of each // cell, and in the case of default colors, those indices come from the // color alias table. But if they're not in the bottom 16 range, we just - // fallback to using white on black (7 and 0). + // fall back to using white on black (7 and 0). auto defaultFgIndex = _renderSettings.GetColorAliasIndex(ColorAlias::DefaultForeground); auto defaultBgIndex = _renderSettings.GetColorAliasIndex(ColorAlias::DefaultBackground); defaultFgIndex = defaultFgIndex < 16 ? defaultFgIndex : 7; @@ -1490,8 +1478,16 @@ void AdaptDispatch::DeviceAttributes() // 28 = Rectangular area operations // 32 = Text macros // 42 = ISO Latin-2 character set + // 52 = Clipboard access - _ReturnCsiResponse(L"?61;4;6;7;14;21;22;23;24;28;32;42c"); + if (_optionalFeatures.test(OptionalFeature::ClipboardWrite)) + { + _ReturnCsiResponse(L"?61;4;6;7;14;21;22;23;24;28;32;42;52c"); + } + else + { + _ReturnCsiResponse(L"?61;4;6;7;14;21;22;23;24;28;32;42c"); + } } // Routine Description: @@ -1914,6 +1910,13 @@ void AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con case DispatchTypes::ModeParams::XTERM_BracketedPasteMode: _api.SetSystemMode(ITerminalApi::Mode::BracketedPaste, enable); break; + case DispatchTypes::ModeParams::SO_SynchronizedOutput: + _renderSettings.SetRenderMode(RenderSettings::Mode::SynchronizedOutput, enable); + if (_renderer) + { + _renderer->SynchronizedOutputChanged(); + } + break; case DispatchTypes::ModeParams::GCM_GraphemeClusterMode: break; case DispatchTypes::ModeParams::W32IM_Win32InputMode: @@ -2052,6 +2055,9 @@ void AdaptDispatch::RequestMode(const DispatchTypes::ModeParams param) case DispatchTypes::ModeParams::XTERM_BracketedPasteMode: state = mapTemp(_api.GetSystemMode(ITerminalApi::Mode::BracketedPaste)); break; + case DispatchTypes::ModeParams::SO_SynchronizedOutput: + state = mapTemp(_renderSettings.GetRenderMode(RenderSettings::Mode::SynchronizedOutput)); + break; case DispatchTypes::ModeParams::GCM_GraphemeClusterMode: state = mapPerm(CodepointWidthDetector::Singleton().GetMode() == TextMeasurementMode::Graphemes); break; @@ -2386,7 +2392,9 @@ void AdaptDispatch::CarriageReturn() // Arguments: // - page - Target page on which the line feed is executed. // - withReturn - Set to true if a carriage return should be performed as well. -// - wrapForced - Set to true is the line feed was the result of the line wrapping. if the viewport panned down. False if not. +// - wrapForced - Set to true if the line feed was the result of the line wrapping. +// Return Value: +// - true if the viewport panned down; otherwise, false. bool AdaptDispatch::_DoLineFeed(const Page& page, const bool withReturn, const bool wrapForced) { auto& textBuffer = page.Buffer(); @@ -2462,7 +2470,7 @@ bool AdaptDispatch::_DoLineFeed(const Page& page, const bool withReturn, const b _api.NotifyBufferRotation(1); // We trigger a scroll rather than a redraw, since that's more efficient, - // but we need to turn the cursor off before doing so, otherwise a ghost + // but we need to turn the cursor off before doing so; otherwise, a ghost // cursor can be left behind in the previous position. cursor.SetIsOn(false); textBuffer.TriggerScroll({ 0, -1 }); @@ -2876,7 +2884,7 @@ void AdaptDispatch::AcceptC1Controls(const bool enabled) void AdaptDispatch::SendC1Controls(const bool enabled) { // If this is an attempt to enable C1 controls, the input code page must be - // one of the DOCS choices (UTF-8 or ISO-8859-1), otherwise there's a risk + // one of the DOCS choices (UTF-8 or ISO-8859-1); otherwise, there's a risk // that those controls won't have a valid encoding. const auto codepage = _api.GetInputCodePage(); if (enabled == false || codepage == CP_UTF8 || codepage == 28591) @@ -3037,6 +3045,7 @@ void AdaptDispatch::HardReset() if (_renderer) { _renderer->TriggerRedrawAll(true, true); + _renderer->SynchronizedOutputChanged(); } // Cursor to 1,1 - the Soft Reset guarantees this is absolute @@ -3292,6 +3301,40 @@ void AdaptDispatch::RequestColorTableEntry(const size_t tableIndex) } } +void AdaptDispatch::ResetColorTable() +{ + _renderSettings.RestoreDefaultIndexed256ColorTable(); + if (_renderer) + { + // This is pessimistic because it's unlikely that the frame or background changed, + // but let's tell the renderer that both changed anyway. + _renderer->TriggerRedrawAll(true, true); + } +} + +// Method Description: +// - Restores a single color table entry to its default user-specified value +// Arguments: +// - tableIndex: The VT color table index +void AdaptDispatch::ResetColorTableEntry(const size_t tableIndex) +{ + _renderSettings.RestoreDefaultColorTableEntry(tableIndex); + + if (_renderer) + { + // If we're updating the background color, we need to let the renderer + // know, since it may want to repaint the window background to match. + const auto backgroundIndex = _renderSettings.GetColorAliasIndex(ColorAlias::DefaultBackground); + const auto backgroundChanged = (tableIndex == backgroundIndex); + + // Similarly for the frame color, the tab may need to be repainted. + const auto frameIndex = _renderSettings.GetColorAliasIndex(ColorAlias::FrameBackground); + const auto frameChanged = (tableIndex == frameIndex); + + _renderer->TriggerRedrawAll(backgroundChanged, frameChanged); + } +} + // Method Description: // - Sets one Xterm Color Resource such as Default Foreground, Background, Cursor void AdaptDispatch::SetXtermColorResource(const size_t resource, const DWORD color) @@ -3313,7 +3356,7 @@ void AdaptDispatch::SetXtermColorResource(const size_t resource, const DWORD col // Method Description: // - Reports the value of one Xterm Color Resource, if it is set. // Return Value: -// True if handled successfully. False otherwise. +// - true if handled successfully; otherwise, false. void AdaptDispatch::RequestXtermColorResource(const size_t resource) { assert(resource >= 10); @@ -3338,6 +3381,25 @@ void AdaptDispatch::RequestXtermColorResource(const size_t resource) } } +// Method Description: +// - Restores to the original user-provided value one Xterm Color Resource such as Default Foreground, Background, Cursor +void AdaptDispatch::ResetXtermColorResource(const size_t resource) +{ + assert(resource >= 10); + const auto mappingIndex = resource - 10; + const auto& oscMapping = XtermResourceColorTableMappings.at(mappingIndex); + if (oscMapping.ColorTableIndex > 0) + { + if (oscMapping.AliasIndex >= 0) + { + // If this color reset applies to an aliased color, point the alias back at the original color + _renderSettings.RestoreDefaultColorAliasIndex(static_cast(oscMapping.AliasIndex)); + } + + ResetColorTableEntry(oscMapping.ColorTableIndex); + } +} + // Method Description: // DECAC - Assigns the foreground and background color indexes that should be // used for a given aspect of the user interface. @@ -3538,6 +3600,7 @@ void AdaptDispatch::DoConEmuAction(const std::wstring_view string) else if (subParam == 12) { _pages.ActivePage().Buffer().StartCommand(); + _api.NotifyShellIntegrationMark(); } } @@ -3568,6 +3631,7 @@ void AdaptDispatch::DoITerm2Action(const std::wstring_view string) if (action == L"SetMark") { _pages.ActivePage().Buffer().StartPrompt(); + _api.NotifyShellIntegrationMark(); } } @@ -3601,16 +3665,19 @@ void AdaptDispatch::DoFinalTermAction(const std::wstring_view string) case L'A': // FTCS_PROMPT { _pages.ActivePage().Buffer().StartPrompt(); + _api.NotifyShellIntegrationMark(); break; } case L'B': // FTCS_COMMAND_START { _pages.ActivePage().Buffer().StartCommand(); + _api.NotifyShellIntegrationMark(); break; } case L'C': // FTCS_COMMAND_EXECUTED { _pages.ActivePage().Buffer().StartOutput(); + _api.NotifyShellIntegrationMark(); break; } case L'D': // FTCS_COMMAND_FINISHED @@ -3631,6 +3698,7 @@ void AdaptDispatch::DoFinalTermAction(const std::wstring_view string) } _pages.ActivePage().Buffer().EndCurrentCommand(error); + _api.NotifyShellIntegrationMark(); break; } @@ -4127,7 +4195,7 @@ ITermDispatch::StringHandler AdaptDispatch::RequestSetting() { // Although we don't yet support any operations with parameter // prefixes, it's important that we still parse the prefix and - // include it in the ID. Otherwise we'll mistakenly respond to + // include it in the ID. Otherwise, we'll mistakenly respond to // prefixed queries that we don't actually recognise. const auto isParameterPrefix = ch >= L'<' && ch <= L'?'; const auto isParameter = ch >= L'0' && ch < L'9'; @@ -4733,3 +4801,8 @@ void AdaptDispatch::PlaySounds(const VTParameters parameters) _api.PlayMidiNote(noteNumber, noteNumber == 71 ? 0 : velocity, duration); }); } + +void AdaptDispatch::SetOptionalFeatures(const til::enumset features) noexcept +{ + _optionalFeatures = features; +} diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 2f63e782ca..af726c0e01 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -133,8 +133,11 @@ namespace Microsoft::Console::VirtualTerminal void SetColorTableEntry(const size_t tableIndex, const DWORD color) override; // OSCSetColorTable void RequestColorTableEntry(const size_t tableIndex) override; // OSCGetColorTable - void SetXtermColorResource(const size_t resource, const DWORD color) override; // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor, OSCResetCursorColor + void ResetColorTable() override; // OSCResetColorTable + void ResetColorTableEntry(const size_t tableIndex) override; // OSCResetColorTable + void SetXtermColorResource(const size_t resource, const DWORD color) override; // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor void RequestXtermColorResource(const size_t resource) override; // OSCGetDefaultForeground, OSCGetDefaultBackground, OSCGetCursorColor + void ResetXtermColorResource(const size_t resource) override; // OSCResetForegroundColor, OSCResetBackgroundColor, OSCResetCursorColor, OSCResetHighlightColor void AssignColor(const DispatchTypes::ColorItem item, const VTInt fgIndex, const VTInt bgIndex) override; // DECAC void WindowManipulation(const DispatchTypes::WindowManipulationType function, @@ -185,7 +188,7 @@ namespace Microsoft::Console::VirtualTerminal void PlaySounds(const VTParameters parameters) override; // DECPS - void SetVtChecksumReportSupport(const bool enabled) noexcept override; + void SetOptionalFeatures(const til::enumset features) noexcept override; private: enum class Mode @@ -310,7 +313,7 @@ namespace Microsoft::Console::VirtualTerminal std::unique_ptr _fontBuffer; std::shared_ptr _macroBuffer; std::optional _initialCodePage; - bool _vtChecksumReportEnabled = false; + til::enumset _optionalFeatures = { OptionalFeature::ClipboardWrite }; // We have two instances of the saved cursor state, because we need // one for the main buffer (at index 0), and another for the alt buffer diff --git a/src/terminal/adapter/charsets.hpp b/src/terminal/adapter/charsets.hpp index bc81e31e72..72b441facc 100644 --- a/src/terminal/adapter/charsets.hpp +++ b/src/terminal/adapter/charsets.hpp @@ -19,7 +19,7 @@ namespace Microsoft::Console::VirtualTerminal public: constexpr CharSet(const std::initializer_list> replacements) { - for (auto i = L'\0'; i < _translationTable.size(); i++) + for (auto i = L'\0'; i < gsl::narrow_cast(_translationTable.size()); i++) _translationTable.at(i) = BaseChar + i; for (auto replacement : replacements) _translationTable.at(replacement.first - BaseChar) = replacement.second; diff --git a/src/terminal/adapter/lib/sources.dep b/src/terminal/adapter/lib/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/terminal/adapter/lib/sources.dep +++ b/src/terminal/adapter/lib/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/terminal/adapter/termDispatch.hpp b/src/terminal/adapter/termDispatch.hpp index f6dab368f8..99c9033fee 100644 --- a/src/terminal/adapter/termDispatch.hpp +++ b/src/terminal/adapter/termDispatch.hpp @@ -71,8 +71,11 @@ public: void TabSet(const VTParameter /*setType*/) override {} // DECST8C void SetColorTableEntry(const size_t /*tableIndex*/, const DWORD /*color*/) override {} // OSCSetColorTable void RequestColorTableEntry(const size_t /*tableIndex*/) override {} // OSCGetColorTable - void SetXtermColorResource(const size_t /*resource*/, const DWORD /*color*/) override {} // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor, OSCResetCursorColor + void ResetColorTable() override {} // OSCResetColorTable + void ResetColorTableEntry(const size_t /*tableIndex*/) override {} // OSCResetColorTable + void SetXtermColorResource(const size_t /*resource*/, const DWORD /*color*/) override {} // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor void RequestXtermColorResource(const size_t /*resource*/) override {} // OSCGetDefaultForeground, OSCGetDefaultBackground, OSCGetCursorColor + void ResetXtermColorResource(const size_t /*resource*/) override {} // OSCResetForegroundColor, OSCResetBackgroundColor, OSCResetCursorColor, OSCResetHighlightColor void AssignColor(const DispatchTypes::ColorItem /*item*/, const VTInt /*fgIndex*/, const VTInt /*bgIndex*/) override {} // DECAC void EraseInDisplay(const DispatchTypes::EraseType /* eraseType*/) override {} // ED @@ -175,7 +178,7 @@ public: void PlaySounds(const VTParameters /*parameters*/) override{}; // DECPS - void SetVtChecksumReportSupport(const bool /*enabled*/) override{}; + void SetOptionalFeatures(const til::enumset /*features*/) override{}; }; #pragma warning(default : 26440) // Restore "can be declared noexcept" warning diff --git a/src/terminal/adapter/ut_adapter/MouseInputTest.cpp b/src/terminal/adapter/ut_adapter/MouseInputTest.cpp index f3df50a0a6..bcfb6fe023 100644 --- a/src/terminal/adapter/ut_adapter/MouseInputTest.cpp +++ b/src/terminal/adapter/ut_adapter/MouseInputTest.cpp @@ -99,7 +99,7 @@ public: TerminalInput::StringType str; str.append(std::wstring_view{ buffer }); - str[str.size() - 1] = IsButtonDown(uiButton) ? L'M' : L'm'; + str[str.size() - 1] = IsButtonUp(uiButton) ? L'm' : L'M'; return str; } @@ -127,10 +127,13 @@ public: wch = L'!'; break; case WM_MOUSEWHEEL: - case WM_MOUSEHWHEEL: Log::Comment(NoThrowString().Format(L"MOUSEWHEEL")); wch = L'`' + (sScrollDelta > 0 ? 0 : 1); break; + case WM_MOUSEHWHEEL: + Log::Comment(NoThrowString().Format(L"MOUSEHWHEEL")); + wch = L'b' + (sScrollDelta > 0 ? 1 : 0); + break; case WM_MOUSEMOVE: default: Log::Comment(NoThrowString().Format(L"DEFAULT")); @@ -168,9 +171,11 @@ public: result = 3 + 0x20; // we add 0x20 to hover events, which are all encoded as WM_MOUSEMOVE events break; case WM_MOUSEWHEEL: - case WM_MOUSEHWHEEL: result = (sScrollDelta > 0 ? 64 : 65); break; + case WM_MOUSEHWHEEL: + result = (sScrollDelta > 0 ? 67 : 66); + break; default: result = 0; break; @@ -182,23 +187,18 @@ public: return result; } - bool IsButtonDown(unsigned int uiButton) + bool IsButtonUp(unsigned int uiButton) { - auto fIsDown = false; switch (uiButton) { - case WM_LBUTTONDBLCLK: - case WM_LBUTTONDOWN: - case WM_RBUTTONDOWN: - case WM_RBUTTONDBLCLK: - case WM_MBUTTONDOWN: - case WM_MBUTTONDBLCLK: - case WM_MOUSEWHEEL: - case WM_MOUSEHWHEEL: - fIsDown = true; - break; + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + case WM_XBUTTONUP: + return true; + default: + return false; } - return fIsDown; } /* From winuser.h - Needed to manually specify the test properties @@ -587,6 +587,12 @@ public: Log::Comment(L"Test mouse wheel scrolling down"); VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1B[B"), mouseInput.HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, -WHEEL_DELTA, {})); + Log::Comment(L"Test mouse wheel scrolling right"); + VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1B[C"), mouseInput.HandleMouse({ 0, 0 }, WM_MOUSEHWHEEL, noModifierKeys, WHEEL_DELTA, {})); + + Log::Comment(L"Test mouse wheel scrolling left"); + VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1B[D"), mouseInput.HandleMouse({ 0, 0 }, WM_MOUSEHWHEEL, noModifierKeys, -WHEEL_DELTA, {})); + Log::Comment(L"Enable cursor keys mode"); mouseInput.SetInputMode(TerminalInput::Mode::CursorKey, true); @@ -596,6 +602,12 @@ public: Log::Comment(L"Test mouse wheel scrolling down"); VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1BOB"), mouseInput.HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, -WHEEL_DELTA, {})); + Log::Comment(L"Test mouse wheel scrolling right"); + VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1BOC"), mouseInput.HandleMouse({ 0, 0 }, WM_MOUSEHWHEEL, noModifierKeys, WHEEL_DELTA, {})); + + Log::Comment(L"Test mouse wheel scrolling left"); + VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1BOD"), mouseInput.HandleMouse({ 0, 0 }, WM_MOUSEHWHEEL, noModifierKeys, -WHEEL_DELTA, {})); + Log::Comment(L"Confirm no effect when scroll mode is disabled"); mouseInput.UseAlternateScreenBuffer(); mouseInput.SetInputMode(TerminalInput::Mode::AlternateScroll, false); diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index b288561359..5821bb28ae 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -212,6 +212,11 @@ public: Log::Comment(L"NotifyBufferRotation MOCK called..."); } + void NotifyShellIntegrationMark() override + { + Log::Comment(L"NotifyShellIntegrationMark MOCK called..."); + } + void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override { Log::Comment(L"InvokeCompletions MOCK called..."); @@ -415,7 +420,6 @@ public: auto& renderer = _testGetSet->_renderer; auto& renderSettings = renderer._renderSettings; auto adapter = std::make_unique(*_testGetSet, &renderer, renderSettings, _terminalInput); - adapter->SetVtChecksumReportSupport(true); fSuccess = adapter.get() != nullptr; if (fSuccess) @@ -1737,11 +1741,15 @@ public: Log::Comment(L"Test 1: Verify normal response."); _testGetSet->PrepData(); _pDispatch->DeviceAttributes(); + _testGetSet->ValidateInputEvent(L"\x1b[?61;4;6;7;14;21;22;23;24;28;32;42;52c"); - auto pwszExpectedResponse = L"\x1b[?61;4;6;7;14;21;22;23;24;28;32;42c"; - _testGetSet->ValidateInputEvent(pwszExpectedResponse); + Log::Comment(L"Test 2: Verify response with clipboard disabled."); + _testGetSet->PrepData(); + _pDispatch->SetOptionalFeatures({}); + _pDispatch->DeviceAttributes(); + _testGetSet->ValidateInputEvent(L"\x1b[?61;4;6;7;14;21;22;23;24;28;32;42c"); - Log::Comment(L"Test 2: Verify failure when ReturnResponse doesn't work."); + Log::Comment(L"Test 3: Verify failure when ReturnResponse doesn't work."); _testGetSet->PrepData(); _testGetSet->_returnResponseResult = FALSE; @@ -2187,6 +2195,8 @@ public: using namespace std::string_view_literals; + _pDispatch->SetOptionalFeatures(ITermDispatch::OptionalFeature::ChecksumReport); + Log::Comment(L"Test 1: ASCII characters"); outputText(L"A"sv); verifyChecksumReport(L"FF4F"); diff --git a/src/terminal/adapter/ut_adapter/inputTest.cpp b/src/terminal/adapter/ut_adapter/inputTest.cpp index ab893be772..d672004cd1 100644 --- a/src/terminal/adapter/ut_adapter/inputTest.cpp +++ b/src/terminal/adapter/ut_adapter/inputTest.cpp @@ -78,7 +78,7 @@ public: irTest.Event.KeyEvent.bKeyDown = TRUE; // If we want to test a key with the Right Alt modifier, we must generate - // an event for the Alt key first, otherwise the modifier will be dropped. + // an event for the Alt key first; otherwise, the modifier will be dropped. if (WI_IsFlagSet(uiKeystate, RIGHT_ALT_PRESSED)) { irTest.Event.KeyEvent.wVirtualKeyCode = VK_MENU; diff --git a/src/terminal/adapter/ut_adapter/sources b/src/terminal/adapter/ut_adapter/sources index 6f4eb322a2..2fe3c59699 100644 --- a/src/terminal/adapter/ut_adapter/sources +++ b/src/terminal/adapter/ut_adapter/sources @@ -48,7 +48,7 @@ TARGETLIBS = \ $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\d3d11.lib \ $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\d3dcompiler.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\api-ms-win-mm-playsound-l1.lib \ - $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-imm-l1-1-0.lib \ + $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-imm-l1.lib \ $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-dwmapi-ext-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-create-l1.lib \ @@ -73,12 +73,14 @@ TARGETLIBS = \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-sysparams-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-window-ext-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-winstamin-l1.lib \ + $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-syscolors-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-shell-shell32-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-uxtheme-themes-l1.lib \ $(ONECORESHELL_INTERNAL_LIB_VPATH_L)\api-ms-win-shell-dataobject-l1.lib \ $(ONECORESHELL_INTERNAL_LIB_VPATH_L)\api-ms-win-shell-namespace-l1.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-uiacore-l1.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-usp10-l1.lib \ + $(ONECORE_EXTERNAL_SDK_LIB_PATH)\ntdll.lib \ $(WINCORE_OBJ_PATH)\console\conint\$(O)\conint.lib \ $(WINCORE_OBJ_PATH)\console\open\src\buffer\out\lib\$(O)\conbufferout.lib \ $(WINCORE_OBJ_PATH)\console\open\src\host\lib\$(O)\conhostv2.lib \ @@ -91,7 +93,6 @@ TARGETLIBS = \ $(WINCORE_OBJ_PATH)\console\open\src\renderer\base\lib\$(O)\ConRenderBase.lib \ $(WINCORE_OBJ_PATH)\console\open\src\renderer\gdi\lib\$(O)\ConRenderGdi.lib \ $(WINCORE_OBJ_PATH)\console\open\src\renderer\wddmcon\lib\$(O)\ConRenderWddmCon.lib \ - $(WINCORE_OBJ_PATH)\console\open\src\renderer\vt\lib\$(O)\ConRenderVt.lib \ $(WINCORE_OBJ_PATH)\console\open\src\server\lib\$(O)\ConServer.lib \ $(WINCORE_OBJ_PATH)\console\open\src\interactivity\base\lib\$(O)\ConInteractivityBaseLib.lib \ $(WINCORE_OBJ_PATH)\console\open\src\interactivity\win32\lib\$(O)\ConInteractivityWin32Lib.lib \ @@ -107,7 +108,7 @@ DELAYLOAD = \ OLEAUT32.dll; \ icu.dll; \ api-ms-win-mm-playsound-l1.dll; \ - ext-ms-win-imm-l1-1-0.lib; \ + ext-ms-win-imm-l1.dll; \ api-ms-win-shcore-scaling-l1.dll; \ api-ms-win-shell-dataobject-l1.dll; \ api-ms-win-shell-namespace-l1.dll; \ @@ -137,6 +138,7 @@ DELAYLOAD = \ ext-ms-win-rtcore-ntuser-sysparams-l1.dll; \ ext-ms-win-rtcore-ntuser-window-ext-l1.dll; \ ext-ms-win-rtcore-ntuser-winstamin-l1.dll; \ + ext-ms-win-rtcore-ntuser-syscolors-l1.dll; \ ext-ms-win-shell-shell32-l1.dll; \ ext-ms-win-uiacore-l1.dll; \ ext-ms-win-uxtheme-themes-l1.dll; \ diff --git a/src/terminal/adapter/ut_adapter/sources.dep b/src/terminal/adapter/ut_adapter/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/terminal/adapter/ut_adapter/sources.dep +++ b/src/terminal/adapter/ut_adapter/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/terminal/input/lib/sources.dep b/src/terminal/input/lib/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/terminal/input/lib/sources.dep +++ b/src/terminal/input/lib/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/terminal/input/mouseInput.cpp b/src/terminal/input/mouseInput.cpp index 596df0360e..19052ad22d 100644 --- a/src/terminal/input/mouseInput.cpp +++ b/src/terminal/input/mouseInput.cpp @@ -63,24 +63,19 @@ static constexpr bool _isWheelMsg(const unsigned int buttonCode) noexcept } // Routine Description: -// - Determines if the input windows message code describes a button press -// (either down or doubleclick) +// - Determines if the input windows message code describes a button release // Parameters: // - button - the message to decode. // Return value: -// - true if button is a button down event -static constexpr bool _isButtonDown(const unsigned int button) noexcept +// - true if button is a button up event +static constexpr bool _isButtonUp(const unsigned int button) noexcept { switch (button) { - case WM_LBUTTONDBLCLK: - case WM_LBUTTONDOWN: - case WM_RBUTTONDOWN: - case WM_RBUTTONDBLCLK: - case WM_MBUTTONDOWN: - case WM_MBUTTONDBLCLK: - case WM_MOUSEWHEEL: - case WM_MOUSEHWHEEL: + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + case WM_XBUTTONUP: return true; default: return false; @@ -168,9 +163,11 @@ static constexpr wchar_t _windowsButtonToXEncoding(const unsigned int button, xvalue = 1; break; case WM_MOUSEWHEEL: - case WM_MOUSEHWHEEL: xvalue = delta > 0 ? 0x40 : 0x41; break; + case WM_MOUSEHWHEEL: + xvalue = delta > 0 ? 0x43 : 0x42; + break; default: xvalue = 0; break; @@ -226,9 +223,11 @@ static constexpr int _windowsButtonToSGREncoding(const unsigned int button, xvalue = 3; break; case WM_MOUSEWHEEL: - case WM_MOUSEHWHEEL: xvalue = delta > 0 ? 0x40 : 0x41; break; + case WM_MOUSEHWHEEL: + xvalue = delta > 0 ? 0x43 : 0x42; + break; default: xvalue = 0; break; @@ -274,7 +273,7 @@ static constexpr wchar_t _encodeDefaultCoordinate(const til::CoordType sCoordina // Parameters: // - // Return value: -// - true, if we are tracking mouse input. False, otherwise +// - true, if we are tracking mouse input; otherwise, false. bool TerminalInput::IsTrackingMouseInput() const noexcept { return _inputMode.any(Mode::DefaultMouseTracking, Mode::ButtonEventMouseTracking, Mode::AnyEventMouseTracking); @@ -372,7 +371,7 @@ TerminalInput::OutputType TerminalInput::HandleMouse(const til::point position, // then we want to handle hovers with WM_MOUSEMOVE. // However, if we're dragging (WM_MOUSEMOVE with a button pressed), // then use that pressed button instead. - return _GenerateSGRSequence(position, physicalButtonPressed ? realButton : button, _isButtonDown(realButton), isHover, modifierKeyState, delta); + return _GenerateSGRSequence(position, physicalButtonPressed ? realButton : button, _isButtonUp(button), isHover, modifierKeyState, delta); } else { @@ -383,7 +382,7 @@ TerminalInput::OutputType TerminalInput::HandleMouse(const til::point position, if (ShouldSendAlternateScroll(button, delta)) { - return _makeAlternateScrollOutput(delta); + return _makeAlternateScrollOutput(button, delta); } return {}; @@ -463,18 +462,18 @@ TerminalInput::OutputType TerminalInput::_GenerateUtf8Sequence(const til::point // Parameters: // - position - The windows coordinates (top,left = 0,0) of the mouse event // - button - the message to decode. WM_MOUSEMOVE is used for mouse hovers with no buttons pressed. -// - isDown - true if a mouse button was pressed. +// - isRelease - true if a mouse button was released. // - isHover - true if the sequence is generated in response to a mouse hover // - modifierKeyState - the modifier keys pressed with this button // - delta - the amount that the scroll wheel changed (should be 0 unless button is a WM_MOUSE*WHEEL) // - ppwchSequence - On success, where to put the pointer to the generated sequence // - pcchLength - On success, where to put the length of the generated sequence -TerminalInput::OutputType TerminalInput::_GenerateSGRSequence(const til::point position, const unsigned int button, const bool isDown, const bool isHover, const short modifierKeyState, const short delta) +TerminalInput::OutputType TerminalInput::_GenerateSGRSequence(const til::point position, const unsigned int button, const bool isRelease, const bool isHover, const short modifierKeyState, const short delta) { // Format for SGR events is: - // "\x1b[<%d;%d;%d;%c", xButton, x+1, y+1, fButtonDown? 'M' : 'm' + // "\x1b[<%d;%d;%d;%c", xButton, x+1, y+1, isRelease? 'm' : 'M' const auto xbutton = _windowsButtonToSGREncoding(button, isHover, modifierKeyState, delta); - return fmt::format(FMT_COMPILE(L"{}<{};{};{}{}"), _csi, xbutton, position.x + 1, position.y + 1, isDown ? L'M' : L'm'); + return fmt::format(FMT_COMPILE(L"{}<{};{};{}{}"), _csi, xbutton, position.x + 1, position.y + 1, isRelease ? L'm' : L'M'); } // Routine Description: @@ -498,14 +497,32 @@ bool TerminalInput::ShouldSendAlternateScroll(const unsigned int button, const s // - Sends a sequence to the input corresponding to cursor up / down depending on the sScrollDelta. // Parameters: // - delta: The scroll wheel delta of the input event -TerminalInput::OutputType TerminalInput::_makeAlternateScrollOutput(const short delta) const +TerminalInput::OutputType TerminalInput::_makeAlternateScrollOutput(const unsigned int button, const short delta) const { - if (delta > 0) + if (button == WM_MOUSEWHEEL) { - return MakeOutput(_keyMap.at(VK_UP)); + if (delta > 0) + { + return MakeOutput(_keyMap.at(VK_UP)); + } + else + { + return MakeOutput(_keyMap.at(VK_DOWN)); + } + } + else if (button == WM_MOUSEHWHEEL) + { + if (delta > 0) + { + return MakeOutput(_keyMap.at(VK_RIGHT)); + } + else + { + return MakeOutput(_keyMap.at(VK_LEFT)); + } } else { - return MakeOutput(_keyMap.at(VK_DOWN)); + return {}; } } diff --git a/src/terminal/input/terminalInput.cpp b/src/terminal/input/terminalInput.cpp index ac0795bdc4..65dfa6f33c 100644 --- a/src/terminal/input/terminalInput.cpp +++ b/src/terminal/input/terminalInput.cpp @@ -171,7 +171,7 @@ TerminalInput::OutputType TerminalInput::HandleKey(const INPUT_RECORD& event) if (matchingLastKeyPress && !_inputMode.test(Mode::AutoRepeat)) { // Note that we must return an empty string here to imply that we've handled - // the event, otherwise the key press can still end up being submitted. + // the event; otherwise, the key press can still end up being submitted. return _makeNoOutput(); } _lastVirtualKeyCode = virtualKeyCode; diff --git a/src/terminal/input/terminalInput.hpp b/src/terminal/input/terminalInput.hpp index 4048826956..9d52ac03ec 100644 --- a/src/terminal/input/terminalInput.hpp +++ b/src/terminal/input/terminalInput.hpp @@ -109,9 +109,9 @@ namespace Microsoft::Console::VirtualTerminal #pragma region MouseInput [[nodiscard]] OutputType _GenerateDefaultSequence(til::point position, unsigned int button, bool isHover, short modifierKeyState, short delta); [[nodiscard]] OutputType _GenerateUtf8Sequence(til::point position, unsigned int button, bool isHover, short modifierKeyState, short delta); - [[nodiscard]] OutputType _GenerateSGRSequence(til::point position, unsigned int button, bool isDown, bool isHover, short modifierKeyState, short delta); + [[nodiscard]] OutputType _GenerateSGRSequence(til::point position, unsigned int button, bool isRelease, bool isHover, short modifierKeyState, short delta); - [[nodiscard]] OutputType _makeAlternateScrollOutput(short delta) const; + [[nodiscard]] OutputType _makeAlternateScrollOutput(unsigned int button, short delta) const; static constexpr unsigned int s_GetPressedButton(const MouseButtonState state) noexcept; #pragma endregion diff --git a/src/terminal/parser/InputStateMachineEngine.cpp b/src/terminal/parser/InputStateMachineEngine.cpp index af781a9c8d..c2a2079b70 100644 --- a/src/terminal/parser/InputStateMachineEngine.cpp +++ b/src/terminal/parser/InputStateMachineEngine.cpp @@ -471,15 +471,22 @@ bool InputStateMachineEngine::ActionCsiDispatch(const VTID id, const VTParameter // We catch it here and store the information for later retrieval. if (_deviceAttributes.load(std::memory_order_relaxed) == 0) { - til::enumset attributes; + til::enumset attributes{ DeviceAttribute::__some__ }; // The first parameter denotes the conformance level. - if (parameters.at(0).value() >= 61) + const auto len = parameters.size(); + if (len >= 2 && parameters.at(0).value() >= 61) { - parameters.subspan(1).for_each([&](auto p) { - attributes.set(static_cast(p)); - return true; - }); + // NOTE: VTParameters::for_each will replace empty spans with a single default value. + // This means we could not distinguish between no parameters and a single default parameter. + for (size_t i = 1; i < len; i++) + { + const auto value = parameters.at(i).value(); + if (value > 0 && value < 64) + { + attributes.set(static_cast(value)); + } + } } _deviceAttributes.fetch_or(attributes.bits(), std::memory_order_relaxed); @@ -890,6 +897,22 @@ bool InputStateMachineEngine::_UpdateSGRMouseButtonState(const VTID id, eventFlags |= MOUSE_WHEELED; break; } + case CsiMouseButtonCodes::ScrollLeft: + { + // set high word to proper scroll direction + // scroll intensity is assumed to be constant value + buttonState |= SCROLL_DELTA_BACKWARD; + eventFlags |= MOUSE_HWHEELED; + break; + } + case CsiMouseButtonCodes::ScrollRight: + { + // set high word to proper scroll direction + // scroll intensity is assumed to be constant value + buttonState |= SCROLL_DELTA_FORWARD; + eventFlags |= MOUSE_HWHEELED; + break; + } case CsiMouseButtonCodes::Released: // hover event, we still want to send these but we don't // need to do anything special here, so just break diff --git a/src/terminal/parser/InputStateMachineEngine.hpp b/src/terminal/parser/InputStateMachineEngine.hpp index 7787fe5c0a..1b81f17f92 100644 --- a/src/terminal/parser/InputStateMachineEngine.hpp +++ b/src/terminal/parser/InputStateMachineEngine.hpp @@ -51,6 +51,10 @@ namespace Microsoft::Console::VirtualTerminal enum class DeviceAttribute : uint64_t { + // Special value to indicate that InputStateMachineEngine::_deviceAttributes has been set. + // 0 in this case means 1<<0 == 1, which in turn means that _deviceAttributes is non-zero. + __some__ = 0, + Columns132 = 1, PrinterPort = 2, Sixel = 4, @@ -107,6 +111,8 @@ namespace Microsoft::Console::VirtualTerminal Released = 3, ScrollForward = 4, ScrollBack = 5, + ScrollLeft = 6, + ScrollRight = 7, }; constexpr unsigned short CsiMouseModifierCode_Drag = 32; diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index c8a1367a26..4febc78ee8 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -810,10 +810,40 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s } break; } - case OscActionCodes::ResetCursorColor: + case OscActionCodes::ResetColor: { - // The reset codes for xterm dynamic resources are the set codes + 100 - _dispatch->SetXtermColorResource(parameter - 100u, INVALID_COLOR); + if (string.empty()) + { + _dispatch->ResetColorTable(); + } + else + { + for (auto&& c : til::split_iterator{ string, L';' }) + { + if (const auto index{ til::parse_unsigned(c, 10) }; index) + { + _dispatch->ResetColorTableEntry(*index); + } + else + { + // NOTE: xterm stops at the first unparseable index whereas VTE keeps going. + break; + } + } + } + break; + } + case OscActionCodes::ResetForegroundColor: + case OscActionCodes::ResetBackgroundColor: + case OscActionCodes::ResetCursorColor: + case OscActionCodes::ResetHighlightColor: + { + // NOTE: xterm ignores the request if there's any parameters whereas VTE resets the provided index and ignores the rest + if (string.empty()) + { + // The reset codes for xterm dynamic resources are the set codes + 100 + _dispatch->ResetXtermColorResource(parameter - 100u); + } break; } case OscActionCodes::Hyperlink: diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp index ab41e066cf..d36789e108 100644 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -214,9 +214,11 @@ namespace Microsoft::Console::VirtualTerminal SetHighlightColor = 17, DECSWT_SetWindowTitle = 21, SetClipboard = 52, - ResetForegroundColor = 110, // Not implemented - ResetBackgroundColor = 111, // Not implemented + ResetColor = 104, + ResetForegroundColor = 110, + ResetBackgroundColor = 111, ResetCursorColor = 112, + ResetHighlightColor = 117, FinalTermAction = 133, VsCodeAction = 633, ITerm2Action = 1337, diff --git a/src/terminal/parser/ft_fuzzer/sources b/src/terminal/parser/ft_fuzzer/sources index a8c508a25d..c221ac9c0c 100644 --- a/src/terminal/parser/ft_fuzzer/sources +++ b/src/terminal/parser/ft_fuzzer/sources @@ -3,7 +3,7 @@ # - Console Virtual Terminal Parser Fuzzer # ------------------------------------- -!include $(PROJECT_ROOT)\vcpkg\consume.inc +!include ..\..\..\..\..\vcpkg\consume.inc # This program will generate fuzz input for the parsing engine # and is to be used in conjunction with the fuzz wrapper tool. @@ -27,6 +27,8 @@ TEST_CODE = 1 USE_UNICRT = 1 USE_MSVCRT = 1 +NO_WCHAR_T = 1 # use native wchar_t +USE_CXX17_STD_BYTE = 1 # Windows disables std::byte by default... USE_STL = 1 STL_VER = STL_VER_CURRENT @@ -47,6 +49,7 @@ C_DEFINES = $(C_DEFINES) -DUNICODE -D_UNICODE USE_STD_CPP20 = 1 MSC_WARNING_LEVEL = /W4 /WX +USER_C_FLAGS = $(USER_C_FLAGS) /Zc:preprocessor /fp:contract /utf-8 # ------------------------------------- # Build System Settings @@ -68,7 +71,6 @@ SOURCES = \ INCLUDES = \ ..\..\..\inc; \ $(CONSOLE_SRC_PATH)\..\oss\chromium; \ - $(CONSOLE_SRC_PATH)\..\oss\fmt\include; \ $(CONSOLE_SRC_PATH)\..\oss\interval_tree; \ $(INCLUDES) \ diff --git a/src/terminal/parser/ft_fuzzer/sources.dep b/src/terminal/parser/ft_fuzzer/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/terminal/parser/ft_fuzzer/sources.dep +++ b/src/terminal/parser/ft_fuzzer/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/terminal/parser/ft_fuzzwrapper/sources b/src/terminal/parser/ft_fuzzwrapper/sources index 1753a400e3..73df7091e4 100644 --- a/src/terminal/parser/ft_fuzzwrapper/sources +++ b/src/terminal/parser/ft_fuzzwrapper/sources @@ -60,6 +60,7 @@ INCLUDES = \ TARGETLIBS = \ $(TARGETLIBS) \ + $(ONECORE_EXTERNAL_SDK_LIB_PATH)\ntdll.lib \ $(ONECORE_EXTERNAL_SDK_LIB_VPATH_L)\onecore.lib \ $(OBJ_PATH)\..\lib\$(O)\ConTermParser.lib \ $(OBJ_PATH)\..\..\..\types\lib\$(O)\ConTypes.lib \ diff --git a/src/terminal/parser/ft_fuzzwrapper/sources.dep b/src/terminal/parser/ft_fuzzwrapper/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/terminal/parser/ft_fuzzwrapper/sources.dep +++ b/src/terminal/parser/ft_fuzzwrapper/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/terminal/parser/lib/sources.dep b/src/terminal/parser/lib/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/terminal/parser/lib/sources.dep +++ b/src/terminal/parser/lib/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/terminal/parser/stateMachine.cpp b/src/terminal/parser/stateMachine.cpp index 12954512bd..c45e2fc2f4 100644 --- a/src/terminal/parser/stateMachine.cpp +++ b/src/terminal/parser/stateMachine.cpp @@ -1511,7 +1511,7 @@ void StateMachine::_EventOscString(const wchar_t wch) // - Handle the two-character termination of a OSC sequence. // Events in this state will: // 1. Trigger the OSC action associated with the param on an OscTerminator -// 2. Otherwise treat this as a normal escape character event. +// 2. Otherwise, treat this as a normal escape character event. // Arguments: // - wch - Character that triggered the event // Return Value: diff --git a/src/terminal/parser/ut_parser/InputEngineTest.cpp b/src/terminal/parser/ut_parser/InputEngineTest.cpp index cdfaf1ac1e..4cee7091d4 100644 --- a/src/terminal/parser/ut_parser/InputEngineTest.cpp +++ b/src/terminal/parser/ut_parser/InputEngineTest.cpp @@ -1194,8 +1194,10 @@ void InputEngineTest::SGRMouseTest_Scroll() // NOTE: scrolling events do NOT send a mouse up event const std::vector> testData = { // TEST INPUT EXPECTED OUTPUT - { { CsiMouseButtonCodes::ScrollForward, 0, { 1, 1 }, CsiActionCodes::MouseDown }, { SCROLL_DELTA_FORWARD, 0, { 0, 0 }, MOUSE_WHEELED } }, - { { CsiMouseButtonCodes::ScrollBack, 0, { 1, 1 }, CsiActionCodes::MouseDown }, { SCROLL_DELTA_BACKWARD, 0, { 0, 0 }, MOUSE_WHEELED } }, + { { CsiMouseButtonCodes::ScrollForward, 0, { 1, 1 }, CsiActionCodes::MouseDown }, { SCROLL_DELTA_FORWARD, 0, { 0, 0 }, MOUSE_WHEELED } }, + { { CsiMouseButtonCodes::ScrollBack, 0, { 1, 1 }, CsiActionCodes::MouseDown }, { SCROLL_DELTA_BACKWARD, 0, { 0, 0 }, MOUSE_WHEELED } }, + { { CsiMouseButtonCodes::ScrollLeft, 0, { 1, 1 }, CsiActionCodes::MouseDown }, { SCROLL_DELTA_BACKWARD, 0, { 0, 0 }, MOUSE_HWHEELED } }, + { { CsiMouseButtonCodes::ScrollRight, 0, { 1, 1 }, CsiActionCodes::MouseDown }, { SCROLL_DELTA_FORWARD, 0, { 0, 0 }, MOUSE_HWHEELED } }, }; // clang-format on VerifySGRMouseData(testData); diff --git a/src/terminal/parser/ut_parser/OutputEngineTest.cpp b/src/terminal/parser/ut_parser/OutputEngineTest.cpp index 416ed6681d..f12dac966c 100644 --- a/src/terminal/parser/ut_parser/OutputEngineTest.cpp +++ b/src/terminal/parser/ut_parser/OutputEngineTest.cpp @@ -1163,7 +1163,8 @@ public: _hyperlinkMode{ false }, _options{ s_cMaxOptions, static_cast(s_uiGraphicsCleared) }, // fill with cleared option _colorTable{}, - _setColorTableEntry{ false } + _setColorTableEntry{ false }, + _resetAllColors{ false } { } @@ -1390,6 +1391,16 @@ public: _colorTableEntriesRequested.push_back(tableIndex); } + void ResetColorTable() noexcept override + { + _resetAllColors = true; + } + + void ResetColorTableEntry(const size_t tableIndex) noexcept override + { + _colorTableEntriesReset.push_back(tableIndex); + } + void SetXtermColorResource(const size_t resource, const DWORD color) override { _xtermResourcesChanged.push_back(resource); @@ -1401,6 +1412,11 @@ public: _xtermResourcesRequested.push_back(resource); } + void ResetXtermColorResource(const size_t resource) override + { + _xtermResourcesReset.push_back(resource); + } + void SetClipboard(wil::zwstring_view content) noexcept override { _copyContent = content; @@ -1476,8 +1492,11 @@ public: std::vector _xtermResourcesChanged; std::vector _xtermResourceValues; std::vector _xtermResourcesRequested; + std::vector _xtermResourcesReset; bool _setColorTableEntry; std::vector _colorTableEntriesRequested; + bool _resetAllColors; + std::vector _colorTableEntriesReset; bool _hyperlinkMode; std::wstring _copyContent; std::wstring _uri; @@ -3221,6 +3240,79 @@ class StateMachineExternalTest final pDispatch->ClearState(); } + TEST_METHOD(TestOscXtermResourceReset) + { + auto dispatch = std::make_unique(); + auto pDispatch = dispatch.get(); + auto engine = std::make_unique(std::move(dispatch)); + StateMachine mach(std::move(engine)); + + mach.ProcessString(L"\033]110\033\\"); + VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesChanged.size()); + VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesRequested.size()); + VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesReset.size()); + VERIFY_ARE_EQUAL(10u, pDispatch->_xtermResourcesReset[0]); + pDispatch->ClearState(); + + mach.ProcessString(L"\033]111;\033\\"); // dangling ; + VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesChanged.size()); + VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesRequested.size()); + VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesReset.size()); + VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesReset[0]); + pDispatch->ClearState(); + + mach.ProcessString(L"\033]111;110\033\\"); + // NOTE: this is xterm behavior - ignore the entire sequence if any params exist + VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesChanged.size()); + VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesRequested.size()); + VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesReset.size()); + pDispatch->ClearState(); + } + + TEST_METHOD(TestOscColorTableReset) + { + auto dispatch = std::make_unique(); + auto pDispatch = dispatch.get(); + auto engine = std::make_unique(std::move(dispatch)); + StateMachine mach(std::move(engine)); + + mach.ProcessString(L"\033]104\033\\"); + VERIFY_IS_TRUE(pDispatch->_resetAllColors); + VERIFY_ARE_EQUAL(0u, pDispatch->_colorTableEntriesReset.size()); + VERIFY_ARE_EQUAL(0u, pDispatch->_colorTableEntriesRequested.size()); + VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesReset.size()); + pDispatch->ClearState(); + + mach.ProcessString(L"\033]104;1;3;5;7;9\033\\"); + VERIFY_IS_FALSE(pDispatch->_resetAllColors); + VERIFY_ARE_EQUAL(5u, pDispatch->_colorTableEntriesReset.size()); + VERIFY_ARE_EQUAL(0u, pDispatch->_colorTableEntriesRequested.size()); + VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesReset.size()); + VERIFY_ARE_EQUAL(1u, pDispatch->_colorTableEntriesReset[0]); + VERIFY_ARE_EQUAL(3u, pDispatch->_colorTableEntriesReset[1]); + VERIFY_ARE_EQUAL(5u, pDispatch->_colorTableEntriesReset[2]); + VERIFY_ARE_EQUAL(7u, pDispatch->_colorTableEntriesReset[3]); + VERIFY_ARE_EQUAL(9u, pDispatch->_colorTableEntriesReset[4]); + pDispatch->ClearState(); + + // NOTE: xterm behavior - stop after first failed parse + mach.ProcessString(L"\033]104;1;a;3\033\\"); + VERIFY_IS_FALSE(pDispatch->_resetAllColors); + VERIFY_IS_FALSE(pDispatch->_setColorTableEntry); + VERIFY_ARE_EQUAL(1u, pDispatch->_colorTableEntriesReset.size()); + VERIFY_ARE_EQUAL(0u, pDispatch->_colorTableEntriesRequested.size()); + VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesReset.size()); + VERIFY_ARE_EQUAL(1u, pDispatch->_colorTableEntriesReset[0]); + pDispatch->ClearState(); + + mach.ProcessString(L"\033]104;;;\033\\"); + VERIFY_IS_FALSE(pDispatch->_setColorTableEntry); + VERIFY_ARE_EQUAL(0u, pDispatch->_colorTableEntriesReset.size()); + VERIFY_ARE_EQUAL(0u, pDispatch->_colorTableEntriesRequested.size()); + VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesReset.size()); + pDispatch->ClearState(); + } + TEST_METHOD(TestOscSetWindowTitle) { BEGIN_TEST_METHOD_PROPERTIES() diff --git a/src/terminal/parser/ut_parser/sources b/src/terminal/parser/ut_parser/sources index fcc2178953..fd4e09dc3b 100644 --- a/src/terminal/parser/ut_parser/sources +++ b/src/terminal/parser/ut_parser/sources @@ -38,7 +38,7 @@ TARGETLIBS = \ $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\d3d11.lib \ $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\d3dcompiler.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\api-ms-win-mm-playsound-l1.lib \ - $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-imm-l1-1-0.lib \ + $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-imm-l1.lib \ $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-dwmapi-ext-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-create-l1.lib \ @@ -63,12 +63,14 @@ TARGETLIBS = \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-sysparams-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-window-ext-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-winstamin-l1.lib \ + $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-syscolors-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-shell-shell32-l1.lib \ $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-uxtheme-themes-l1.lib \ $(ONECORESHELL_INTERNAL_LIB_VPATH_L)\api-ms-win-shell-dataobject-l1.lib \ $(ONECORESHELL_INTERNAL_LIB_VPATH_L)\api-ms-win-shell-namespace-l1.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-uiacore-l1.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-usp10-l1.lib \ + $(ONECORE_EXTERNAL_SDK_LIB_PATH)\ntdll.lib \ $(WINCORE_OBJ_PATH)\console\conint\$(O)\conint.lib \ $(CONSOLE_OBJ_PATH)\buffer\out\lib\$(O)\conbufferout.lib \ $(CONSOLE_OBJ_PATH)\host\lib\$(O)\conhostv2.lib \ @@ -80,7 +82,6 @@ TARGETLIBS = \ $(CONSOLE_OBJ_PATH)\renderer\base\lib\$(O)\ConRenderBase.lib \ $(CONSOLE_OBJ_PATH)\renderer\gdi\lib\$(O)\ConRenderGdi.lib \ $(CONSOLE_OBJ_PATH)\renderer\wddmcon\lib\$(O)\ConRenderWddmCon.lib \ - $(CONSOLE_OBJ_PATH)\renderer\vt\lib\$(O)\ConRenderVt.lib \ $(CONSOLE_OBJ_PATH)\audio\midi\lib\$(O)\ConAudioMidi.lib \ $(CONSOLE_OBJ_PATH)\server\lib\$(O)\ConServer.lib \ $(CONSOLE_OBJ_PATH)\interactivity\base\lib\$(O)\ConInteractivityBaseLib.lib \ @@ -98,7 +99,7 @@ DELAYLOAD = \ OLEAUT32.dll; \ icu.dll; \ api-ms-win-mm-playsound-l1.dll; \ - ext-ms-win-imm-l1-1-0.lib; \ + ext-ms-win-imm-l1.dll; \ api-ms-win-shcore-scaling-l1.dll; \ api-ms-win-shell-dataobject-l1.dll; \ api-ms-win-shell-namespace-l1.dll; \ @@ -129,6 +130,7 @@ DELAYLOAD = \ ext-ms-win-rtcore-ntuser-sysparams-l1.dll; \ ext-ms-win-rtcore-ntuser-window-ext-l1.dll; \ ext-ms-win-rtcore-ntuser-winstamin-l1.dll; \ + ext-ms-win-rtcore-ntuser-syscolors-l1.dll; \ ext-ms-win-shell-shell32-l1.dll; \ ext-ms-win-uiacore-l1.dll; \ ext-ms-win-usp10-l1.dll; \ diff --git a/src/terminal/parser/ut_parser/sources.dep b/src/terminal/parser/ut_parser/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/terminal/parser/ut_parser/sources.dep +++ b/src/terminal/parser/ut_parser/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/til/ut_til/sources.dep b/src/til/ut_til/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/til/ut_til/sources.dep +++ b/src/til/ut_til/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/til/ut_til/throttled_func.cpp b/src/til/ut_til/throttled_func.cpp index 3cb85025f5..dce22d5080 100644 --- a/src/til/ut_til/throttled_func.cpp +++ b/src/til/ut_til/throttled_func.cpp @@ -19,21 +19,25 @@ class ThrottledFuncTests TEST_METHOD(Basic) { using namespace std::chrono_literals; - using throttled_func = til::throttled_func_trailing; + using throttled_func = til::throttled_func; til::latch latch{ 2 }; std::unique_ptr tf; - tf = std::make_unique(10ms, [&](bool reschedule) { - latch.count_down(); + tf = std::make_unique( + til::throttled_func_options{ + .delay = 10ms, + .trailing = true }, + [&](bool reschedule) { + latch.count_down(); - // This will ensure that the callback is called even if we - // invoke the throttled_func from inside the callback itself. - if (reschedule) - { - tf->operator()(false); - } - }); + // This will ensure that the callback is called even if we + // invoke the throttled_func from inside the callback itself. + if (reschedule) + { + tf->operator()(false); + } + }); // This will ensure that the throttled_func invokes the callback in general. tf->operator()(true); diff --git a/src/tools/TerminalStress/TerminalStress.csproj b/src/tools/TerminalStress/TerminalStress.csproj index 208c8da8e4..b1eedf24b1 100644 --- a/src/tools/TerminalStress/TerminalStress.csproj +++ b/src/tools/TerminalStress/TerminalStress.csproj @@ -2,8 +2,7 @@ Exe - net6.0 - 6.0.9 + net8.0-windows diff --git a/src/tools/U8U16Test/U8U16Test.cpp b/src/tools/U8U16Test/U8U16Test.cpp index 89ed982453..5c4d6a2f10 100644 --- a/src/tools/U8U16Test/U8U16Test.cpp +++ b/src/tools/U8U16Test/U8U16Test.cpp @@ -57,7 +57,7 @@ u8state::u8state() noexcept : { // If the Lead Byte indicates that the last bytes in the string is a partial UTF-8 code point then cache them: // Use the bitmask at index `sequenceLen`. Compare the result with the operand having the same index. If they - // are not equal then the sequence has to be cached because it is a partial code point. Otherwise the + // are not equal then the sequence has to be cached because it is a partial code point. Otherwise, the // sequence is a complete UTF-8 code point and the whole string is ready for the conversion to hstring. if ((*backIter & _cmpMasks.at(sequenceLen)) != _cmpOperands.at(sequenceLen)) { diff --git a/src/tools/vtapp/VTApp.csproj b/src/tools/vtapp/VTApp.csproj index e42b141f0a..c14d36f427 100644 --- a/src/tools/vtapp/VTApp.csproj +++ b/src/tools/vtapp/VTApp.csproj @@ -7,7 +7,7 @@ Properties VTApp VTApp - v4.8 + v4.7.2 512 true diff --git a/src/tools/vtpipeterm/main.cpp b/src/tools/vtpipeterm/main.cpp index fa79b0b9cf..0a87724727 100644 --- a/src/tools/vtpipeterm/main.cpp +++ b/src/tools/vtpipeterm/main.cpp @@ -1,12 +1,20 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN +#endif #define NOMINMAX #include +#ifndef __INSIDE_WINDOWS #define CONPTY_IMPEXP #include +#else // Building inside Windows, just use the kernel32 ones. +#define ConptyCreatePseudoConsole CreatePseudoConsole +#define ConptyReleasePseudoConsole ReleasePseudoConsole +#define ConptyResizePseudoConsole ResizePseudoConsole +#endif #include @@ -81,7 +89,7 @@ static int run(int argc, const wchar_t* argv[]) auto viewportSize = getViewportSize(); HPCON hPC = nullptr; - THROW_IF_FAILED(ConptyCreatePseudoConsole(viewportSize, pipe.client.get(), pipe.client.get(), 0, &hPC)); + THROW_IF_FAILED(ConptyCreatePseudoConsole(viewportSize, pipe.client.get(), pipe.client.get(), PSEUDOCONSOLE_INHERIT_CURSOR, &hPC)); pipe.client.reset(); PROCESS_INFORMATION pi; diff --git a/src/tools/vtpipeterm/sources b/src/tools/vtpipeterm/sources index f19c0fe1ca..5fec40ebba 100644 --- a/src/tools/vtpipeterm/sources +++ b/src/tools/vtpipeterm/sources @@ -1,3 +1,6 @@ +!include $(PROJECT_ROOT)\core\console\vcpkg\consume.inc + +USE_STD_CPP20 = 1 MSC_WARNING_LEVEL=/W4 /WX @@ -18,8 +21,10 @@ USE_NATIVE_EH = 1 C_DEFINES=-DUNICODE -D__INSIDE_WINDOWS TARGETLIBS=\ + $(TARGETLIBS) \ $(MINWIN_EXTERNAL_SDK_LIB_PATH_L)\ntdll.lib \ - $(ONECORE_EXTERNAL_SDK_LIB_VPATH_L)\onecore.lib + $(ONECORE_EXTERNAL_SDK_LIB_VPATH_L)\onecore.lib \ + $(WINCORE_OBJ_PATH)\console\open\src\types\lib\$(O)\ConTypes.lib \ SOURCES=main.cpp \ diff --git a/src/tools/vttests/common.py b/src/tools/vttests/common.py index 2ecd854145..b08d23618b 100644 --- a/src/tools/vttests/common.py +++ b/src/tools/vttests/common.py @@ -59,7 +59,18 @@ def clear_all(): def sgr(code=0): csi('{}m'.format(code)) -def sgr_n(seq=[]): +def sgr_n(seq=None): + """Apply multiple SGR (Select Graphic Rendition) parameters. + + Parameters + ---------- + seq : Iterable of int | None + Sequence of numeric SGR codes to emit. If ``None`` (default) an empty + sequence is assumed. A mutable default argument must be avoided to + prevent unwanted state sharing between calls. + """ + if seq is None: + seq = [] csi('{}m'.format(';'.join(str(code) for code in seq))) def tbc(): diff --git a/src/tsf/Implementation.cpp b/src/tsf/Implementation.cpp index f2cd958933..7d0e2dfd34 100644 --- a/src/tsf/Implementation.cpp +++ b/src/tsf/Implementation.cpp @@ -711,13 +711,23 @@ TextAttribute Implementation::_textAttributeFromAtom(TfGuidAtom atom) const TF_DISPLAYATTRIBUTE da; THROW_IF_FAILED(dai->GetAttributeInfo(&da)); - if (da.crText.type != TF_CT_NONE) + // The Tencent QQPinyin IME creates TF_CT_COLORREF attributes with a color of 0x000000 (black). + // We respect their wish, which results in the preview text being invisible. + // (Note that sending this COLORREF is incorrect, and not a bug in our handling.) + // + // After some discussion, we realized that an IME which sets only one color but not + // the others is likely not properly tested anyway, so we reject those cases. + // After all, what behavior do we expect, if the IME sends e.g. foreground=blue, + // without knowing whether our terminal theme already uses a blue background? + if (da.crText.type != TF_CT_NONE && da.crText.type == da.crBk.type) { attr.SetForeground(_colorFromDisplayAttribute(da.crText)); - } - if (da.crBk.type != TF_CT_NONE) - { attr.SetBackground(_colorFromDisplayAttribute(da.crBk)); + // I'm not sure what the best way to handle this is. + if (da.crText.type == da.crLine.type) + { + attr.SetUnderlineColor(_colorFromDisplayAttribute(da.crLine)); + } } if (da.lsStyle >= TF_LS_NONE && da.lsStyle <= TF_LS_SQUIGGLE) { @@ -737,10 +747,6 @@ TextAttribute Implementation::_textAttributeFromAtom(TfGuidAtom atom) const { attr.SetUnderlineStyle(UnderlineStyle::DoublyUnderlined); } - if (da.crLine.type != TF_CT_NONE) - { - attr.SetUnderlineColor(_colorFromDisplayAttribute(da.crLine)); - } return attr; } diff --git a/src/tsf/sources.dep b/src/tsf/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/tsf/sources.dep +++ b/src/tsf/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/types/ColorFix.cpp b/src/types/ColorFix.cpp index 7648de2262..d5a5fd1cd5 100644 --- a/src/types/ColorFix.cpp +++ b/src/types/ColorFix.cpp @@ -209,7 +209,14 @@ COLORREF ColorFix::GetPerceivableColor(COLORREF color, COLORREF reference, float return linearToColorref(oklab::oklab_to_linear_srgb(colorOklab)) | (color & 0xff000000); } -float ColorFix::GetLuminosity(COLORREF color) noexcept +COLORREF ColorFix::AdjustLightness(COLORREF color, float delta) noexcept +{ + auto lab = oklab::linear_srgb_to_oklab(colorrefToLinear(color)); + lab.l = saturate(lab.l + delta); + return linearToColorref(oklab::oklab_to_linear_srgb(lab)) | (color & 0xff000000); +} + +float ColorFix::GetLightness(COLORREF color) noexcept { return oklab::linear_srgb_to_oklab(colorrefToLinear(color)).l; } diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index 9b3d7040a5..d91c57a08d 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -568,7 +568,7 @@ try // - the anchors have been populated // This means that we've found a contiguous range where the text attribute was found. // No point in searching through the rest of the search space. - // TLDR: keep updating the second anchor and make the range wider until the attribute changes. + // TL;DR: keep updating the second anchor and make the range wider until the attribute changes. break; } } @@ -663,7 +663,7 @@ CATCH_RETURN(); // - pRetVal - the attributeId's sub-type for the first cell in the range (i.e. foreground color) // - attr - the text attribute we're checking // Return Value: -// - true, if the attributeId is supported. false, otherwise. +// - true, if the attributeId is supported. Otherwise, false. // - pRetVal is populated with the appropriate response relevant to the returned bool. bool UiaTextRangeBase::_initializeAttrQuery(TEXTATTRIBUTEID attributeId, VARIANT* pRetVal, const TextAttribute& attr) const { @@ -1310,7 +1310,7 @@ til::CoordType UiaTextRangeBase::_getViewportHeight(const til::inclusive_rect& v { assert(viewport.bottom >= viewport.top); // + 1 because til::inclusive_rect is inclusive on both sides so subtracting top - // and bottom gets rid of 1 more then it should. + // and bottom gets rid of 1 more, then it should. return viewport.bottom - viewport.top + 1; } diff --git a/src/types/inc/ColorFix.hpp b/src/types/inc/ColorFix.hpp index 24c4cf79f3..60a7338731 100644 --- a/src/types/inc/ColorFix.hpp +++ b/src/types/inc/ColorFix.hpp @@ -10,5 +10,6 @@ namespace ColorFix { COLORREF GetPerceivableColor(COLORREF color, COLORREF reference, float minSquaredDistance) noexcept; - float GetLuminosity(COLORREF color) noexcept; + COLORREF AdjustLightness(COLORREF color, float delta) noexcept; + float GetLightness(COLORREF color) noexcept; } diff --git a/src/types/inc/utils.hpp b/src/types/inc/utils.hpp index af730836e6..b06f171206 100644 --- a/src/types/inc/utils.hpp +++ b/src/types/inc/utils.hpp @@ -133,4 +133,5 @@ namespace Microsoft::Console::Utils bool IsWindows11() noexcept; + bool IsLikelyToBeEmojiOrSymbolIcon(std::wstring_view text) noexcept; } diff --git a/src/types/lib/sources.dep b/src/types/lib/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/types/lib/sources.dep +++ b/src/types/lib/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/types/ut_types/sources b/src/types/ut_types/sources index f5741649fe..e2fe087a64 100644 --- a/src/types/ut_types/sources +++ b/src/types/ut_types/sources @@ -25,6 +25,7 @@ INCLUDES = \ TARGETLIBS = \ $(WINCORE_OBJ_PATH)\console\open\src\types\lib\$(O)\ConTypes.lib \ + $(ONECORE_EXTERNAL_SDK_LIB_PATH)\ntdll.lib \ $(TARGETLIBS) \ # ------------------------------------- diff --git a/src/types/ut_types/sources.dep b/src/types/ut_types/sources.dep index bc61c95c6b..fce1139651 100644 --- a/src/types/ut_types/sources.dep +++ b/src/types/ut_types/sources.dep @@ -1,3 +1,3 @@ BUILD_PASS1_CONSUMES= \ - onecore\windows\vcpkg|PASS1 \ + onecore\windows\core\console\vcpkg|PASS1 \ diff --git a/src/types/utils.cpp b/src/types/utils.cpp index 4ca6a46556..1044b17abd 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -9,6 +9,8 @@ #include "inc/colorTable.hpp" +#include + using namespace Microsoft::Console; // Routine Description: @@ -1279,3 +1281,37 @@ bool Utils::IsWindows11() noexcept }(); return isWindows11; } + +bool Utils::IsLikelyToBeEmojiOrSymbolIcon(std::wstring_view text) noexcept +{ + if (text.size() == 1 && !IS_HIGH_SURROGATE(til::at(text, 0))) + { + // If it's a single code unit, it's definitely either zero or one grapheme clusters. + // If it turns out to be illegal Unicode, we don't really care. + return true; + } + + if (text.size() >= 2 && til::at(text, 0) <= 0x7F && til::at(text, 1) <= 0x7F) + { + // Two adjacent ASCII characters (as seen in most file paths) aren't a single + // grapheme cluster. + return false; + } + + // Use ICU to determine whether text is composed of a single grapheme cluster. + int32_t off{ 0 }; + UErrorCode status{ U_ZERO_ERROR }; + +#pragma warning(disable : 26490) // Don't use reinterpret_cast (type.1). + const auto b{ ubrk_open(UBRK_CHARACTER, + nullptr, + reinterpret_cast(text.data()), + gsl::narrow_cast(text.size()), + &status) }; + if (status <= U_ZERO_ERROR) + { + off = ubrk_next(b); + ubrk_close(b); + } + return off == gsl::narrow_cast(text.size()); +} diff --git a/src/winconpty/dll/winconptydll.vcxproj b/src/winconpty/dll/winconptydll.vcxproj index ef86147e67..80ed63fa23 100644 --- a/src/winconpty/dll/winconptydll.vcxproj +++ b/src/winconpty/dll/winconptydll.vcxproj @@ -7,6 +7,7 @@ winconpty.DLL conpty DynamicLibrary + ConPTY Interface Library diff --git a/src/winconpty/winconpty.cpp b/src/winconpty/winconpty.cpp index 79c97ddaa2..969a8fbdfe 100644 --- a/src/winconpty/winconpty.cpp +++ b/src/winconpty/winconpty.cpp @@ -278,15 +278,16 @@ HRESULT _ResizePseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const CO // Return Value: // - S_OK if the call succeeded, else an appropriate HRESULT for failing to // write the clear message to the pty. -HRESULT _ClearPseudoConsole(_In_ const PseudoConsole* const pPty) +static HRESULT _ClearPseudoConsole(_In_ const PseudoConsole* const pPty, BOOL keepCursorRow) noexcept { if (pPty == nullptr) { return E_INVALIDARG; } - unsigned short signalPacket[1]; + unsigned short signalPacket[2]; signalPacket[0] = PTY_SIGNAL_CLEAR_WINDOW; + signalPacket[1] = keepCursorRow ? 1 : 0; const auto fSuccess = WriteFile(pPty->hSignal, signalPacket, sizeof(signalPacket), nullptr, nullptr); return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError()); @@ -417,7 +418,7 @@ static void _ClosePseudoConsole(_In_ PseudoConsole* pPty) noexcept // INHERIT_CURSOR: This will cause the created conpty to attempt to inherit the // cursor position of the parent terminal application. This can be useful // for applications like `ssh`, where ssh (currently running in a terminal) -// might want to create a pseudoterminal session for an child application +// might want to create a pseudoterminal session for a child application // and the child inherit the cursor position of ssh. // The created conpty will immediately emit a "Device Status Request" VT // sequence to hOutput, that should be replied to on hInput in the format @@ -492,13 +493,13 @@ extern "C" HRESULT WINAPI ConptyResizePseudoConsole(_In_ HPCON hPC, _In_ COORD s // - This is used exclusively by ConPTY to support GH#1193, GH#1882. This allows // a terminal to clear the contents of the ConPTY buffer, which is important // if the user would like to be able to clear the terminal-side buffer. -extern "C" HRESULT WINAPI ConptyClearPseudoConsole(_In_ HPCON hPC) +extern "C" HRESULT WINAPI ConptyClearPseudoConsole(_In_ HPCON hPC, BOOL keepCursorRow) { const PseudoConsole* const pPty = (PseudoConsole*)hPC; auto hr = pPty == nullptr ? E_INVALIDARG : S_OK; if (SUCCEEDED(hr)) { - hr = _ClearPseudoConsole(pPty); + hr = _ClearPseudoConsole(pPty, keepCursorRow); } return hr; } diff --git a/src/winconpty/winconpty.h b/src/winconpty/winconpty.h index 4bb43ddf8f..8a245226ca 100644 --- a/src/winconpty/winconpty.h +++ b/src/winconpty/winconpty.h @@ -68,7 +68,6 @@ HRESULT _CreatePseudoConsole(const HANDLE hToken, _Inout_ PseudoConsole* pPty); HRESULT _ResizePseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const COORD size); -HRESULT _ClearPseudoConsole(_In_ const PseudoConsole* const pPty); HRESULT _ShowHidePseudoConsole(_In_ const PseudoConsole* const pPty, const bool show); HRESULT _ReparentPseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const HWND newParent); void _ClosePseudoConsoleMembers(_In_ PseudoConsole* pPty); diff --git a/tools/GenerateAppxFromManifest.ps1 b/tools/GenerateAppxFromManifest.ps1 index 3bf0bba826..a52c1cfc15 100644 --- a/tools/GenerateAppxFromManifest.ps1 +++ b/tools/GenerateAppxFromManifest.ps1 @@ -21,7 +21,7 @@ param ( [xml]$appxPrototypeData = Get-Content $AppxManifestPrototype # You need to make sure each element we add is part of the same namespace as the -# Package, otherwise powershell will append a bunch of `xmlns=""` properties +# Package; otherwise, powershell will append a bunch of `xmlns=""` properties # that will make the appx deployment reject the manifest. $rootNS = $appxPrototypeData.Package.NamespaceURI @@ -38,7 +38,7 @@ $files | ForEach-Object { $InProcessServer = $appxPrototypeData.CreateNode("element", "InProcessServer", $rootNS) $Path = $appxPrototypeData.CreateNode("element", "Path", $rootNS) - # You need to stash the result here, otherwise a blank line will be echoed to + # You need to stash the result here; otherwise, a blank line will be echoed to # the console. $placeholder = $Path.InnerText = $_.name diff --git a/tools/OpenConsole.psm1 b/tools/OpenConsole.psm1 index 05fc5af4c2..da577f6618 100644 --- a/tools/OpenConsole.psm1 +++ b/tools/OpenConsole.psm1 @@ -83,6 +83,7 @@ function Set-MsbuildDevEnvironment switch ($env:PROCESSOR_ARCHITECTURE) { "amd64" { $arch = "x64" } "x86" { $arch = "x86" } + "arm64" { $arch = "arm64" } default { throw "Unknown architecture: $switch" } } @@ -263,13 +264,13 @@ function Invoke-OpenConsoleTests() #.SYNOPSIS -# Builds OpenConsole.sln using msbuild. Any arguments get passed on to msbuild. +# Builds OpenConsole.slnx using msbuild. Any arguments get passed on to msbuild. function Invoke-OpenConsoleBuild() { $root = Find-OpenConsoleRoot - & "$root\dep\nuget\nuget.exe" restore "$root\OpenConsole.sln" + & "$root\dep\nuget\nuget.exe" restore "$root\OpenConsole.slnx" & "$root\dep\nuget\nuget.exe" restore "$root\dep\nuget\packages.config" - msbuild.exe "$root\OpenConsole.sln" @args + msbuild.exe "$root\OpenConsole.slnx" @args } #.SYNOPSIS diff --git a/tools/README.md b/tools/README.md index 94e841a0b1..f69efb3a21 100644 --- a/tools/README.md +++ b/tools/README.md @@ -30,7 +30,7 @@ the `%DEFAULT_CONFIGURATION%` configuration, which is `Debug` if you use `razzle ## opencon (and openbash, openps) `opencon` can be used to launch the **last built** OpenConsole binary. If given an -argument, it will try and run that program in the launched window. Otherwise it +argument, it will try and run that program in the launched window. Otherwise, it will default to cmd.exe. `openbash` is similar, it immediately launches bash.exe (the Windows Subsystem diff --git a/tools/ReleaseEngineering/Draft-TerminalReleases.ps1 b/tools/ReleaseEngineering/Draft-TerminalReleases.ps1 index 5d3775892e..3da91ff51a 100644 --- a/tools/ReleaseEngineering/Draft-TerminalReleases.ps1 +++ b/tools/ReleaseEngineering/Draft-TerminalReleases.ps1 @@ -30,6 +30,7 @@ Enum AssetType { Unknown ApplicationBundle PreinstallKit + GroupPolicy Zip } @@ -83,6 +84,9 @@ Class Asset { $local:bundlePath = Join-Path $local:directory $local:bundleName $this.Type = [AssetType]::PreinstallKit $this.Architecture = "all" + } ElseIf (".zip" -eq $local:ext -and $local:filename -like 'GroupPolicy*') { + $this.Type = [AssetType]::GroupPolicy + $this.Architecture = "all" } ElseIf (".zip" -eq $local:ext) { $this.Type = [AssetType]::Zip } ElseIf (".msixbundle" -eq $local:ext) { @@ -90,7 +94,7 @@ Class Asset { $this.Architecture = "all" } - If ($this.Type -Ne [AssetType]::Zip) { + If ($this.Type -In ([AssetType]::ApplicationBundle, [AssetType]::PreinstallKit)) { Write-Verbose "Cracking bundle $($local:bundlePath)" $local:firstMsixName = & $script:tar -t -f $local:bundlePath | Select-String 'Cascadia.*\.msix' | @@ -105,8 +109,10 @@ Class Asset { $local:Manifest = [xml](Get-Content (Join-Path $local:directory AppxManifest.xml)) $this.ParseManifest($local:Manifest) } Else { - & $script:tar -x -f $this.Path -C $local:directory --strip-components=1 '*/wt.exe' - $this.ExpandedVersion = (Get-Item (Join-Path $local:directory wt.exe)).VersionInfo.ProductVersion + If ($this.Type -Ne [AssetType]::GroupPolicy) { + & $script:tar -x -f $this.Path -C $local:directory --strip-components=1 '*/wt.exe' + $this.ExpandedVersion = (Get-Item (Join-Path $local:directory wt.exe)).VersionInfo.ProductVersion + } # Zip files just encode everything in their filename. Not great, but workable. $this.ParseFilename($local:filename) @@ -133,7 +139,9 @@ Class Asset { $parts = [IO.Path]::GetFileNameWithoutExtension($filename).Split("_") $this.Name = $parts[0] $this.Version = $parts[1] - $this.Architecture = $parts[2] + If ($parts.Length -Ge 3) { + $this.Architecture = $parts[2] + } } [string]IdealFilename() { @@ -149,6 +157,9 @@ Class Asset { Zip { "{0}_{1}_{2}.zip" -f ($this.Name, $this.Version, $this.Architecture) } + GroupPolicy { + "{0}_{1}.zip" -f ($this.Name, $this.Version) + } Default { Throw "Unknown type $($_.Type)" } @@ -174,7 +185,7 @@ class Release { Release([Asset[]]$a) { $this.Assets = $a - $this.Branding = $a[0].Branding + $this.Branding = $a | Where-Object Branding -Ne ([Branding]::Unknown) | Select -Unique -First 1 -Expand Branding $this.Name = Switch($this.Branding) { Release { "Windows Terminal" } Preview { "Windows Terminal Preview" } diff --git a/tools/ReleaseEngineering/ServicingPipeline.ps1 b/tools/ReleaseEngineering/ServicingPipeline.ps1 index c562ae0195..957168dbe7 100644 --- a/tools/ReleaseEngineering/ServicingPipeline.ps1 +++ b/tools/ReleaseEngineering/ServicingPipeline.ps1 @@ -126,6 +126,10 @@ Function Get-GraphQlProjectWithNodes($Organization, $Number) { pageInfo { hasNextPage endCursor } nodes { id + type + title: fieldValueByName(name: "Title") { + ... on ProjectV2ItemFieldTextValue { text } + } status: fieldValueByName(name: "Status") { ... on ProjectV2ItemFieldSingleSelectValue { name } } @@ -161,6 +165,96 @@ Function Get-GraphQlProjectWithNodes($Organization, $Number) { $Project } +Enum ServicingStatus { + Unknown + ToConsider + ToCherryPick + CherryPicked + Validated + Shipped + Rejected +} + +Enum ServicingItemType { + Unknown + DraftIssue + Issue + PullRequest + Redacted +} + +Class ServicingCard { + [String]$Id + [Int]$Number + [String]$Title + [String]$Commit + [ServicingStatus]$Status + [ServicingItemType]$Type + static [ServicingItemType]TypeFromString($name) { + $v = Switch -Exact ($name) { + "DRAFT_ISSUE" { [ServicingItemType]::DraftIssue } + "ISSUE" { [ServicingItemType]::Issue } + "PULL_REQUEST" { [ServicingItemType]::PullRequest } + "REDACTED" { [ServicingItemType]::Redacted } + Default { [ServicingItemType]::Unknown } + } + Return $v + } + + static [ServicingStatus]StatusFromString($name) { + $v = Switch -Exact ($name) { + "To Consider" { [ServicingStatus]::ToConsider } + "To Cherry Pick" { [ServicingStatus]::ToCherryPick } + "Cherry Picked" { [ServicingStatus]::CherryPicked } + "Validated" { [ServicingStatus]::Validated } + "Shipped" { [ServicingStatus]::Shipped } + "Rejected" { [ServicingStatus]::Rejected } + Default { [ServicingStatus]::Unknown } + } + Return $v + } + + ServicingCard([object]$node) { + $this.Id = $node.id + $this.Title = $node.title.text + If (-Not [String]::IsNullOrEmpty($node.content.mergeCommit.oid)) { + $this.Commit = $node.content.mergeCommit.oid + } ElseIf (-Not [String]::IsNullOrEmpty($node.content.closedByPullRequestsReferences.nodes.mergeCommit.oid)) { + $this.Commit = $node.content.closedByPullRequestsReferences.nodes.mergeCommit.oid + } + If (-Not [String]::IsNullOrEmpty($node.content.number)) { + $this.Number = [Int]$node.content.number + } + $this.Status = [ServicingCard]::StatusFromString($node.status.name) + $this.Type = [ServicingCard]::TypeFromString($node.type) + } + + [string]Format() { + $color = Switch -Exact ($this.Status) { + ([ServicingStatus]::ToConsider) { "`e[38:5:166m" } + ([ServicingStatus]::ToCherryPick) { "`e[38:5:28m" } + ([ServicingStatus]::CherryPicked) { "`e[38:5:20m" } + ([ServicingStatus]::Validated) { "`e[38:5:126m" } + ([ServicingStatus]::Shipped) { "`e[38:5:92m" } + ([ServicingStatus]::Rejected) { "`e[38:5:160m" } + Default { "`e[m" } + } + $symbol = Switch -Exact ($this.Type) { + ([ServicingItemType]::DraftIssue) { "`u{25cb}" } # white circle + ([ServicingItemType]::Issue) { "`u{2b24}" } # black circle + ([ServicingItemType]::PullRequest) { "`u{25c0}" } # black left triangle + ([ServicingItemType]::Redacted) { "`u{25ec}" } # triangle with dot + Default { "`u{2b24}" } + } + $localTitle = $this.Title + If ($this.Number -Gt 0) { + $localTitle = "[`e]8;;https://github.com/microsoft/terminal/issues/{1}`e\Link`e]8;;`e\] {0} (#{1})" -f ($this.Title, $this.Number) + } + Return "{0}{1}`e[m ({2}) {3}" -f ($color, $symbol, $this.Status, $localTitle) + } +} + + If ([String]::IsNullOrEmpty($Version)) { $BranchVersionRegex = [Regex]"^release-(\d+(\.\d+)+)$" $Branch = & git rev-parse --abbrev-ref HEAD @@ -209,20 +303,50 @@ $StatusFieldId = $StatusField.id $StatusRejectOptionId = $StatusField.options | Where-Object name -eq $script:RejectStatusName | Select-Object -Expand id $StatusDoneOptionId = $StatusField.options | Where-Object name -eq $script:DoneStatusName | Select-Object -Expand id -$ToPickList = $Project.organization.projectV2.items.nodes | Where-Object { $_.status.name -eq $TodoStatusName } +# Create ServicingCards out of each node, but filter out the ones with no commits. +$cards = $Project.organization.projectV2.items.nodes | ForEach-Object { [ServicingCard]::new($_) } -$commits = New-Object System.Collections.ArrayList -$cards = [System.Collections.Generic.Dictionary[String, String[]]]::new() -$ToPickList | ForEach-Object { - If (-Not [String]::IsNullOrEmpty($_.content.mergeCommit.oid)) { - $co = $_.content.mergeCommit.oid - } ElseIf (-Not [String]::IsNullOrEmpty($_.content.closedByPullRequestsReferences.nodes.mergeCommit.oid)) { - $co = $_.content.closedByPullRequestsReferences.nodes.mergeCommit.oid - } Else { - Return +$incompleteCards = $cards | Where-Object { [String]::IsNullOrEmpty($_.Commit) -And $_.Status -Eq ([ServicingStatus]::ToCherryPick) } +If ($incompleteCards.Length -Gt 0) { + Write-Host "Cards to cherry pick are not associated with commits:" + $incompleteCards | ForEach-Object { + Write-Host " - $($_.Format())" } - $null = $commits.Add($co) - $cards[$co] += $_.id + Write-Host "" +} + +$considerCards = $cards | Where-Object Status -Eq ([ServicingStatus]::ToConsider) +If ($considerCards.Length -Gt 0) { + Write-Host "`e[7m CONSIDERATION QUEUE `e[27m" + $considerCards | ForEach-Object { + Write-Host " - $($_.Format())" + } + Write-Host "" +} + +$commitGroups = $cards | Where-Object { -Not [String]::IsNullOrEmpty($_.Commit) } | Group-Object Commit +$commits = New-Object System.Collections.ArrayList +$finalCardsForCommit = [System.Collections.Generic.Dictionary[String, ServicingCard[]]]::new() + +$commitGroups | ForEach-Object { + $statuses = $_.Group | Select-Object -Unique Status + If ($statuses.Length -Gt 1) { + Write-Host "Commit $($_.Name) is present in more than one column:" + $_.Group | ForEach-Object { + Write-Host " - $($_.Format())" + } + Write-Host "`e[1mIt will be ignored.`e[m`n" + } Else { + If ($statuses[0].Status -eq ([ServicingStatus]::ToCherryPick)) { + $null = $commits.Add($_.Name) + $finalCardsForCommit[$_.Name] = $_.Group + } + } +} + +If ($commits.Count -Eq 0) { + Write-Host "Nothing to do." + Exit } $sortedAllCommits = & git rev-list --no-walk=sorted $commits @@ -235,6 +359,9 @@ If ($GpgSign) { $sortedAllCommits | ForEach-Object { Write-Host "`e[96m`e[1;7mPICK`e[22;27m`e[m $(& git show -q --pretty=oneline $_)" + ForEach($card In $finalCardsForCommit[$_]) { + Write-Host $card.Format() + } $null = & git cherry-pick -x $_ 2>&1 $Err = "" While ($True) { @@ -249,8 +376,8 @@ $sortedAllCommits | ForEach-Object { } If ($Global:Reject) { - ForEach($card In $cards[$_]) { - Set-GraphQlProjectEntryStatus -Project $Project.organization.projectV2.id -Item $card -Field $StatusFieldId -Value $StatusRejectOptionId + ForEach($card In $finalCardsForCommit[$_]) { + Set-GraphQlProjectEntryStatus -Project $Project.organization.projectV2.id -Item $card.Id -Field $StatusFieldId -Value $StatusRejectOptionId } # Fall through to Skip } @@ -262,10 +389,10 @@ $sortedAllCommits | ForEach-Object { $Err = & git cherry-pick --continue --no-edit } Else { - & git commit @PickArgs --amend --no-edit --trailer "Service-Card-Id:$($cards[$_])" --trailer "Service-Version:$Version" | Out-Null + & git commit @PickArgs --amend --no-edit --trailer "Service-Card-Id:$($finalCardsForCommit[$_].Id)" --trailer "Service-Version:$Version" | Out-Null Write-Host "`e[92;1;7m OK `e[m" - ForEach($card In $cards[$_]) { - Set-GraphQlProjectEntryStatus -Project $Project.organization.projectV2.id -Item $card -Field $StatusFieldId -Value $StatusDoneOptionId + ForEach($card In $finalCardsForCommit[$_]) { + Set-GraphQlProjectEntryStatus -Project $Project.organization.projectV2.id -Item $card.Id -Field $StatusFieldId -Value $StatusDoneOptionId } Break } diff --git a/tools/bcz.cmd b/tools/bcz.cmd index 72071fa7cc..72e15498e1 100644 --- a/tools/bcz.cmd +++ b/tools/bcz.cmd @@ -73,11 +73,11 @@ if "%_SKIP_NUGET_RESTORE%" == "1" ( echo Skipped nuget restore ) else ( echo Performing nuget restore... - nuget.exe restore %OPENCON%\OpenConsole.sln + nuget.exe restore %OPENCON%\OpenConsole.slnx ) @rem /p:GenerateAppxPackageOnBuild=false will prevent us from building the whole .msix package when building the wapproj project. -set _BUILD_CMDLINE="%MSBUILD%" %OPENCON%\OpenConsole.sln /t:"%_MSBUILD_TARGET%" /m /p:Configuration=%_LAST_BUILD_CONF% /p:GenerateAppxPackageOnBuild=false /p:Platform=%ARCH% %_APPX_ARGS% +set _BUILD_CMDLINE="%MSBUILD%" %OPENCON%\OpenConsole.slnx /t:"%_MSBUILD_TARGET%" /m /p:Configuration=%_LAST_BUILD_CONF% /p:GenerateAppxPackageOnBuild=false /p:Platform=%ARCH% %_APPX_ARGS% echo %_BUILD_CMDLINE% echo Starting build... @@ -135,7 +135,7 @@ setlocal enabledelayedexpansion rem TODO:GH#2172 Find a way to only rebuild the metaproj if the sln changed rem First generate the metaproj file set MSBuildEmitSolution=1 -"%msbuild%" %OPENCON%\OpenConsole.sln /t:ValidateSolutionConfiguration /m > NUL +"%msbuild%" %OPENCON%\OpenConsole.slnx /t:ValidateSolutionConfiguration /m > NUL set MSBuildEmitSolution= rem Use bx.ps1 to figure out which target we're looking at diff --git a/tools/bx.ps1 b/tools/bx.ps1 index 38edab55ac..5127929d09 100644 --- a/tools/bx.ps1 +++ b/tools/bx.ps1 @@ -11,11 +11,11 @@ $projectPath = $projects.FullName # Parse the solution's metaproj file. -[xml]$Metaproj = Get-Content "$env:OPENCON\OpenConsole.sln.metaproj" +[xml]$Metaproj = Get-Content "$env:OPENCON\OpenConsole.slnx.metaproj" $targets = $Metaproj.Project.Target -# Most projects are in OpenConsole.sln.metaproj as ".*proj.metaproj". +# Most projects are in OpenConsole.slnx.metaproj as ".*proj.metaproj". # We'll filter to search for these first and foremost. $msBuildCondition = "'%(ProjectReference.Identity)' == '$projectPath.metaproj'" @@ -25,7 +25,7 @@ $msBuildCondition = "'%(ProjectReference.Identity)' == '$projectPath.metaproj'" $matchingTargets = $targets | Where-Object { $_.MSBuild.Condition -eq $msBuildCondition } # If we didn't find a target, it's possible that the project didn't have a -# .metaproj in OpenConsole.sln.metaproj. Try filtering again, but leave off the +# .metaproj in OpenConsole.slnx.metaproj. Try filtering again, but leave off the # .metaproj extension. if ($matchingTargets.length -eq 0) { diff --git a/tools/razzle.cmd b/tools/razzle.cmd index b118620f5b..18618d4b6e 100644 --- a/tools/razzle.cmd +++ b/tools/razzle.cmd @@ -21,7 +21,7 @@ rem Add nuget to PATH set PATH=%OPENCON%\dep\nuget;%PATH% rem Run nuget restore so you can use vswhere -nuget restore %OPENCON%\OpenConsole.sln -Verbosity quiet +nuget restore %OPENCON%\OpenConsole.slnx -Verbosity quiet nuget restore %OPENCON%\dep\nuget\packages.config -Verbosity quiet :FIND_MSBUILD diff --git a/vcpkg.json b/vcpkg.json index 3536fad210..28b1d753e4 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -17,7 +17,7 @@ "overrides": [ { "name": "fmt", - "version": "11.0.2" + "version": "11.1.4" }, { "name": "ms-gsl", @@ -36,5 +36,8 @@ "version": "0.30.3" } ], - "builtin-baseline": "fe1cde61e971d53c9687cf9a46308f8f55da19fa" + "builtin-baseline": "fe1cde61e971d53c9687cf9a46308f8f55da19fa", + "vcpkg-configuration": { + "overlay-ports": [ "./dep/vcpkg-overlay-ports" ] + } }