Compare commits

..

16 Commits

Author SHA1 Message Date
Mike Griese
2da5b0b146 Add support for multiple panes in the same window (#825)
* Start working on adding support for panes

  See #1000 for the panes megathread on remaining work.

  The functionality will be there, but the keybinding won't be there, so people have to
  opt-in to it.
2019-06-07 16:56:44 -05:00
Jeremy Banks
31b614d5b2 Code to add WSLProfiles. (#1050)
* Code to add WSLProfiles.

* Updates recomended by miniksa

* Corrections from Mauve

* More updates from miniska (clarified WaitForSingleObject errors, and moved the try block to the calling function)

* Added THROW_LAST_ERROR for WAIT_FAILED instead of passing an unhandled exception.

* Migrate STL dependancies to LibraryIncludes.h

* Renamed function to provide more clarity

* Set WSL starting directory.

* Default Linux icon and brackets on new lines.

* Added system path so we don't rely on execution from the PATH environment variable.  Removed incorrect error useage.  Removed variable that was not required.

* Remove default directory setting.
2019-06-07 16:12:32 -05:00
Oisin Grehan
6b51d783c2 added UTF8 torture test text file and a solution dependency graph diagram. (#1166) 2019-06-07 15:54:04 -05:00
nicole mazzuca
c73761db96 Fix OpenConsole.psm1 to use vswhere (#1113)
* Fix OpenConsole.psm1 to use vswhere

I'm not sure this is good, since I don't write a lot of powershell, and
I don't know the project very well, but hopefully it's good!

* Do as @DHowett-MSFT says and use VSSetup

whee!

* try to do what @heaths is recommending

* fix `Import-LocalModule`

* fix openconsole.psm1 for hopefully the last time
2019-06-07 20:20:26 +00:00
Rich Turner
b9d8bf55c4 Updated & linked-to Contributor's Guide 2019-06-06 14:08:15 -07:00
Rich Turner
16c32a622e Docs: Contributor's guide and spec-template (#967)
Submitting first draft of spec template, and `contributing.md` outlining our guidance on how to engage with us, file issues, suggest features, submit changes, etc.
2019-06-05 13:54:21 -07:00
David Teresi
7ede3785ee Fix crash when window width and height are too high (#1134)
## Summary of the Pull Request

Currently, the program crashes with a window width or height greater than 32767 (accounting for window decorations). This can be caused when the `initialRows` and `initialColumns` settings are set too high (also depends on the font width and height). This fixes the issue by not allowing the window to expand beyond 32767x32767.

## References
#843 - relocated the ClampToShortMax helper for reuse elsewhere
2019-06-04 16:31:36 -07:00
Michael Niksa
30a579e18b [inbox] Fix build warnings and namespace issues introduced by GitHub merge (#1144)
These were introduced by:

build warning with using wrong type in wil macro: #1105
namespace issues: #955
These showed up in the official Windows build. I fixed them on that side to restore the build and now I'm bringing them back out.
2019-06-04 16:30:37 -07:00
Michael Niksa
6aac2c06e3 Change ParseNext function in UTF16 parser to never yield invalid data… (#1129)
…. It will return a replacement character at that point if it was given bad data. #788

<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request

This modifies the parser used while inserting text into the underlying data buffer to never return an empty sequence. The empty sequence is invalid as you can't insert a "nothing" into the buffer. The buffer asserted this with a fail fast crash. Now we will instead insert U+FFFD (the Unicode replacement character) � to symbolize that something was invalid and has been replaced.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes #788 and internal MSFT: 20990158
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [x] Tests added/passed
* [x] Requires documentation to be updated
* [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #788

<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

The solution here isn't perfect and isn't going to solve all of our problems. I was basically trying to stop the crash while not getting in the way of the other things coming down the pipe for the input channels.

I considered the following:
1. Remove the fail fast assertion from the buffer
  - I didn't want to do this because it really is invalid to get all the way to placing the text down into the buffer and then request a string of 0 length get inserted. I feel the fail fast is a good indication that something is terribly wrong elsewhere that should be corrected.
2. Update the UTF16 parser in order to stop returning empty strings
  - This is what I ultimately did. If it would ever return just a lead, it returns �. If it would ever return just a trail, it returns �. Otherwise it will return them as a pair if they're both there, or it will return a single valid codepoint. I am now assuming that if the parse function is being called in an Output Iterator and doesn't contain a string with all pieces of the data that are needed, that someone at a higher level messed up the data, it is in valid, and it should be repaired into replacements.
  - This then will move the philosophy up out of the buffer layer to make folks inserting into the buffer identify half a sequence (if they're sitting on a stream where this circumstance could happen... one `wchar_t` at a time) and hold onto it until the next bit arrives. This is because there can be many different routes into the buffer from many different streams/channels. So buffering it low, right near the insertion point, is bad as it might pair loose `wchar_t` across stream entrypoints.
3. Update the iterator, on creating views, to disallow/transform empty strings. 
  - I considered this solution as well, but it would have required, under some circumstances, a second parsing of the string to identify lead/trail status from outside the `Utf16Parser` class to realize when to use the � character. So I avoided the double-parse.
4. Change the cooked read classes to identify that they pulled the lead `wchar_t` from a sequence then try to pull another one.
   - I was going to attempt this, but @adiviness said that he tried it and it made all sorts of other weirdness happen with the edit line.
   - Additionally, @adiviness has an outstanding series of effort to make cooked read significantly less horrible and disgusting. I didn't want to get in the way here.
5. Change the `GetChar` method off of the input buffer queue to return a `char32_t`, a `wstring_view`, transform a standalone lead/trail, etc.
    - The `GetChar` method is used by several different accessors and API calls to retrieve information off of the input queue, transforming the Key events into straight up characters. To change this at that level would change them all.  Long-term, it is probably warranted to do so as all of those consumers likely need to become aware of handling UTF-16 surrogates before we can declare victory. But two problems.
          1. This gets in the way of @adiviness work on cooked read data
          2. This goes WAY beyond the scope of what I want to accomplish here as the immediate goal is to stop the crash, not fix the world.


I've validated this by:
1. Writing some additional tests against the Utf16Parser to simulate some of the theoretical sequences that could arrive and need to be corrected into replacement characters per a verbal discussion and whiteboarding with @adiviness.
2. Manually triggered the emoji panel and inserted a bunch of emoji. Then seeked around left and right, deleted assorted points with the backspace key, pressed enter to commit, and used the up-arrow history to recommit them to see what happened. There were no crashes. The behavior is still weird and not great... but outside the scope of no crashy crashy.
2019-06-04 15:22:18 -07:00
Michael Niksa
107ea3c2e4 Flush input queue before running test. #1137 (#1139)
Flushes the input queue on RawReadUnpacksCoalescedInputRecords test to ensure that other tests cannot cause failure by leaving extraneous input records behind after they run.

This only failed in the core operating system gate tests. This is because those tests run a subset of the complete test suite (subtracting the ones that do not make sense in a core environment). Apparently one of the tests that was skipped that normally runs prior to the UnpacksCoalesced test ensured that the input queue was clean enough for this test to succeed. But in the core environment, the test that ran prior left stuff behind.

To resolve this, I'm making the Coalesced test more resilient by cleaning out the queue prior to performing its operations.

(Also, bonus, I'm fixing the typo in the name Coalesced.)

This is less complicated/expensive than tracking down the tests that are leaving garbage behind, should prevent issues in the future related to ordering (since the tests run alphabetically, by default), and isn't as expensive as running the test in isolation (with its own conhost stood up for just the one test.)

Validated by running te.exe Microsoft.Console.Host.FeatureTests.dll /name:*InputTests* against a core operating system variant. Prior to change, this test failed. After the change, this test succeeded.

This will be automatically double-checked by the gates run after check-in.
2019-06-04 15:16:09 -07:00
Mike Griese
8a69be0cc7 Switch to jsoncpp as our json library (#1005)
Switch to using jsoncpp as our json library. This lets us pretty-print the json file by default, and lets users place comments in the json file.

We will now only re-write the file when the actual logical structure of the json object changes, not only when the serialization changes.

Unfortunately, this will remove any existing ordering of profiles, and make the order random. We don't terribly care though, because when #754 lands, this will be less painful.

It also introduces a top-level globals object to hold all the global properties, including keybindings. Existing profiles should gracefully upgrade.
2019-06-04 16:55:27 -05:00
Michael Niksa
69e88cd921 Add explicit "validation steps" to PR template (#1140) 2019-06-04 21:19:32 +00:00
d-bingham
5d96ebc225 Fix acrylic brush flash/fade on new tab creation. (#1092)
Fixes #1082 -- #853's fix of the acrylic background's flash/fade on any settings change managed to cause a flash/fade on new tab creation. This change removed both flash/fades. #853 split background brush initialization from background color changes; due to the brush being constructed with a default color and then the color being initialized later, new tabs were getting the flash/fade that accompanies a re-focused fluent-style acrylic background. This PR initializes the acrylic color at brush initialization to avoid the problem.
2019-06-04 13:25:24 -07:00
Michael Ratanapintha
e6e316977d Clean up some misuses of INVALID_HANDLE_VALUE (fixes #427) (#1105)
Almost all functions in the Windows API that open or create objects and return HANDLEs to them return null on failure; only a few (mostly to do with the file system) return INVALID_HANDLE_VALUE on failure. This PR scrubs the repo of a few, but not necessarily all, cases where INVALID_HANDLE_VALUE was mistakenly used or tested against instead of null. In particular, it fixes 2 cases reported in issue #427 where the return value of CreateThread() was compared against INVALID_HANDLE_VALUE against null, causing the error handling code to run at the wrong time.

There are a lot of other uses of INVALID_HANDLE_VALUE I found that looked questionable, but which I left alone. Most of these were used to initialize HANDLE-typed variables and as a sentinel to see if those variables remained unset to a "real" value.

Fixes #427
2019-06-04 13:23:42 -07:00
Summon528
d51ce7021c Provide workaround for _GetFocusedTabIndex (#1117)
Use tabview.SelectedIndex for setting focus tab

References
  Closes #1100, Closes #1039, Closes #1074
2019-06-04 13:18:23 -07:00
Dustin L. Howett (MSFT)
880272c748 inbox: Synchronize the font between the WDDMCon Renderer and the SCREEN_INFORMATION (#1089)
Synchronize the font between the WDDMCon Renderer and the SCREEN_INFORMATION when the OneCore Interactivity library starts up. #21717424

Retrieved from rs_onecore_dep_acioss ccca0315e7db34c09f5fcd9dfabae666ede1687b

Fixes #958.
2019-05-31 17:59:07 -07:00
76 changed files with 10970 additions and 1011 deletions

View File

@@ -14,3 +14,6 @@
<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

4
.gitignore vendored
View File

@@ -263,6 +263,9 @@ build*.metadata
# .razzlerc.cmd file - used by dev environment
tools/.razzlerc.*
# .PowershellModules - if one needs a powershell module dependency, one
# can save it here. used by tools/OpenConsole.psm1
.PowershellModules
# message compiler output
MSG*.bin
/*.exe
@@ -275,3 +278,4 @@ MSG*.bin
**/Unmerged/*
profiles.json
*.metaproj
*.swp

49
NOTICE.md Normal file
View File

@@ -0,0 +1,49 @@
# NOTICES AND INFORMATION
Do Not Translate or Localize
This software incorporates material from third parties. Microsoft makes certain
open source code available at http://3rdpartysource.microsoft.com, or you may
send a check or money order for US $5.00, including the product name, the open
source component name, and version number, to:
```
Source Code Compliance Team
Microsoft Corporation
One Microsoft Way
Redmond, WA 98052
USA
```
Notwithstanding any other terms, you may reverse engineer this software to the
extent required to debug changes to any libraries licensed under the GNU Lesser
General Public License.
## jsoncpp
**Source**: https://github.com/open-source-parsers/jsoncpp
### License
```
Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
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.
```

View File

@@ -100,7 +100,7 @@ Secondly, try pressing <kbd>Ctrl</kbd> + <kbd>T</kbd>. The tabs are hidden when
We are excited to work alongside you, our amazing community, to build and enhance Windows Terminal\!
We ask that **before you start work on a feature that you would like to contribute, <span class="underline">please file an issue</span> describing your proposed change**: We will be happy to work with you to figure out the best approach, provide guidance and mentorship throughout feature development, and help avoid any wasted or duplicate effort.
We ask that **before you start work on a feature that you would like to contribute**, please read our [Contributor's Guide](https://github.com/microsoft/terminal/blob/master/doc/contributing.md). We will be happy to work with you to figure out the best approach, provide guidance and mentorship throughout feature development, and help avoid any wasted or duplicate effort.
> 👉 **Remember\!** Your contributions may be incorporated into future versions of Windows\! Because of this, all pull requests will be subject to the same level of scrutiny for quality, coding standards, performance, globalization, accessibility, and compatibility as those of our internal contributors.

View File

@@ -586,8 +586,6 @@ typedef struct _CONSOLE_STATE_INFO {
COLORREF DefaultForeground;
COLORREF DefaultBackground;
BOOL TerminalScrolling;
LPWSTR VersionString;
/* END V2 CONSOLE_STATE_INFO */
} CONSOLE_STATE_INFO, *PCONSOLE_STATE_INFO;

7
dep/jsoncpp/README.md Normal file
View File

@@ -0,0 +1,7 @@
# jsoncpp
[Amalgamated](https://github.com/open-source-parsers/jsoncpp/wiki/Amalgamated)
from source commit
[ddabf50](https://github.com/open-source-parsers/jsoncpp/commit/ddabf50f72cf369bf652a95c4d9fe31a1865a781),
release 1.8.4.

View File

@@ -0,0 +1,333 @@
/// Json-cpp amalgamated forward header (http://jsoncpp.sourceforge.net/).
/// It is intended to be used with #include "json/json-forwards.h"
/// This header provides forward declaration for all JsonCpp types.
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
/*
The JsonCpp library's source code, including accompanying documentation,
tests and demonstration applications, are licensed under the following
conditions...
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
jurisdictions which recognize such a disclaimer. In such jurisdictions,
this software is released into the Public Domain.
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
The JsonCpp Authors, and is released under the terms of the MIT License (see below).
In jurisdictions which recognize Public Domain property, the user of this
software may choose to accept it either as 1) Public Domain, 2) under the
conditions of the MIT License (see below), or 3) under the terms of dual
Public Domain/MIT License conditions described here, as they choose.
The MIT License is about as close to Public Domain as a license can get, and is
described in clear, concise terms at:
http://en.wikipedia.org/wiki/MIT_License
The full text of the MIT License follows:
========================================================================
Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
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.
========================================================================
(END LICENSE TEXT)
The MIT license is compatible with both the GPL and commercial
software, affording one all of the rights of Public Domain with the
minor nuisance of being required to keep the above copyright notice
and license text in the source code. Note also that by accepting the
Public Domain "license" you can re-license your copy using whatever
license you like.
*/
// //////////////////////////////////////////////////////////////////////
// End of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
#ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED
# define JSON_FORWARD_AMALGAMATED_H_INCLUDED
/// If defined, indicates that the source file is amalgamated
/// to prevent private header inclusion.
#define JSON_IS_AMALGAMATION
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/config.h
// //////////////////////////////////////////////////////////////////////
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_CONFIG_H_INCLUDED
#define JSON_CONFIG_H_INCLUDED
#include <stddef.h>
#include <string> //typedef String
#include <stdint.h> //typedef int64_t, uint64_t
/// If defined, indicates that json library is embedded in CppTL library.
//# define JSON_IN_CPPTL 1
/// If defined, indicates that json may leverage CppTL library
//# define JSON_USE_CPPTL 1
/// If defined, indicates that cpptl vector based map should be used instead of
/// std::map
/// as Value container.
//# define JSON_USE_CPPTL_SMALLMAP 1
// If non-zero, the library uses exceptions to report bad input instead of C
// assertion macros. The default is to use exceptions.
#ifndef JSON_USE_EXCEPTION
#define JSON_USE_EXCEPTION 1
#endif
/// If defined, indicates that the source file is amalgamated
/// to prevent private header inclusion.
/// Remarks: it is automatically defined in the generated amalgamated header.
// #define JSON_IS_AMALGAMATION
#ifdef JSON_IN_CPPTL
#include <cpptl/config.h>
#ifndef JSON_USE_CPPTL
#define JSON_USE_CPPTL 1
#endif
#endif
#ifdef JSON_IN_CPPTL
#define JSON_API CPPTL_API
#elif defined(JSON_DLL_BUILD)
#if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllexport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#elif defined(JSON_DLL)
#if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllimport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#endif // ifdef JSON_IN_CPPTL
#if !defined(JSON_API)
#define JSON_API
#endif
// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
// integer
// Storages, and 64 bits integer support is disabled.
// #define JSON_NO_INT64 1
#if defined(_MSC_VER) // MSVC
# if _MSC_VER <= 1200 // MSVC 6
// Microsoft Visual Studio 6 only support conversion from __int64 to double
// (no conversion from unsigned __int64).
# define JSON_USE_INT64_DOUBLE_CONVERSION 1
// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
// characters in the debug information)
// All projects I've ever seen with VS6 were using this globally (not bothering
// with pragma push/pop).
# pragma warning(disable : 4786)
# endif // MSVC 6
# if _MSC_VER >= 1500 // MSVC 2008
/// Indicates that the following function is deprecated.
# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
# endif
#endif // defined(_MSC_VER)
// In c++11 the override keyword allows you to explicitly define that a function
// is intended to override the base-class version. This makes the code more
// managable and fixes a set of common hard-to-find bugs.
#if __cplusplus >= 201103L
# define JSONCPP_OVERRIDE override
# define JSONCPP_NOEXCEPT noexcept
#elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900
# define JSONCPP_OVERRIDE override
# define JSONCPP_NOEXCEPT throw()
#elif defined(_MSC_VER) && _MSC_VER >= 1900
# define JSONCPP_OVERRIDE override
# define JSONCPP_NOEXCEPT noexcept
#else
# define JSONCPP_OVERRIDE
# define JSONCPP_NOEXCEPT throw()
#endif
#ifndef JSON_HAS_RVALUE_REFERENCES
#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010
#define JSON_HAS_RVALUE_REFERENCES 1
#endif // MSVC >= 2010
#ifdef __clang__
#if __has_feature(cxx_rvalue_references)
#define JSON_HAS_RVALUE_REFERENCES 1
#endif // has_feature
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
#define JSON_HAS_RVALUE_REFERENCES 1
#endif // GXX_EXPERIMENTAL
#endif // __clang__ || __GNUC__
#endif // not defined JSON_HAS_RVALUE_REFERENCES
#ifndef JSON_HAS_RVALUE_REFERENCES
#define JSON_HAS_RVALUE_REFERENCES 0
#endif
#ifdef __clang__
# if __has_extension(attribute_deprecated_with_message)
# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
# endif
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
# endif // GNUC version
#endif // __clang__ || __GNUC__
#if !defined(JSONCPP_DEPRECATED)
#define JSONCPP_DEPRECATED(message)
#endif // if !defined(JSONCPP_DEPRECATED)
#if __GNUC__ >= 6
# define JSON_USE_INT64_DOUBLE_CONVERSION 1
#endif
#if !defined(JSON_IS_AMALGAMATION)
# include "version.h"
# if JSONCPP_USING_SECURE_MEMORY
# include "allocator.h" //typedef Allocator
# endif
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
typedef int Int;
typedef unsigned int UInt;
#if defined(JSON_NO_INT64)
typedef int LargestInt;
typedef unsigned int LargestUInt;
#undef JSON_HAS_INT64
#else // if defined(JSON_NO_INT64)
// For Microsoft Visual use specific types as long long is not supported
#if defined(_MSC_VER) // Microsoft Visual Studio
typedef __int64 Int64;
typedef unsigned __int64 UInt64;
#else // if defined(_MSC_VER) // Other platforms, use long long
typedef int64_t Int64;
typedef uint64_t UInt64;
#endif // if defined(_MSC_VER)
typedef Int64 LargestInt;
typedef UInt64 LargestUInt;
#define JSON_HAS_INT64
#endif // if defined(JSON_NO_INT64)
#if JSONCPP_USING_SECURE_MEMORY
#define JSONCPP_STRING std::basic_string<char, std::char_traits<char>, Json::SecureAllocator<char> >
#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream<char, std::char_traits<char>, Json::SecureAllocator<char> >
#define JSONCPP_OSTREAM std::basic_ostream<char, std::char_traits<char>>
#define JSONCPP_ISTRINGSTREAM std::basic_istringstream<char, std::char_traits<char>, Json::SecureAllocator<char> >
#define JSONCPP_ISTREAM std::istream
#else
#define JSONCPP_STRING std::string
#define JSONCPP_OSTRINGSTREAM std::ostringstream
#define JSONCPP_OSTREAM std::ostream
#define JSONCPP_ISTRINGSTREAM std::istringstream
#define JSONCPP_ISTREAM std::istream
#endif // if JSONCPP_USING_SECURE_MEMORY
} // end namespace Json
#endif // JSON_CONFIG_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/config.h
// //////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/forwards.h
// //////////////////////////////////////////////////////////////////////
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_FORWARDS_H_INCLUDED
#define JSON_FORWARDS_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
// writer.h
class FastWriter;
class StyledWriter;
// reader.h
class Reader;
// features.h
class Features;
// value.h
typedef unsigned int ArrayIndex;
class StaticString;
class Path;
class PathArgument;
class Value;
class ValueIteratorBase;
class ValueIterator;
class ValueConstIterator;
} // namespace Json
#endif // JSON_FORWARDS_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/forwards.h
// //////////////////////////////////////////////////////////////////////
#endif //ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED

2207
dep/jsoncpp/json/json.h Normal file

File diff suppressed because it is too large Load Diff

5386
dep/jsoncpp/jsoncpp.cpp Normal file

File diff suppressed because it is too large Load Diff

232
doc/cascadia/Panes.md Normal file
View File

@@ -0,0 +1,232 @@
---
author: "Mike Griese @zadjii-msft"
created on: 2019-May-16
---
# Panes in the Windows Terminal
## Abstract
Panes are an abstraction by which the terminal can display multiple terminal
instances simultaneously in a single terminal window. While tabs allow for a
single terminal window to have many terminal sessions running simultaneously
within a single window, only one tab can be visible at a time. Panes, on the
other hand, allow a user to have many different terminal sessions visible to the
user within the context of a single window at the same time. This can enable
greater productivity from the user, as they can see the output of one terminal
window while working in another.
This spec will help outline the design of the implementation of panes in the
Windows Terminal.
## Inspirations
Panes within the context of a single terminal window are not a new idea. The
design of the panes for the Windows Terminal was heavily inspired by the
application `tmux`, which is a commandline application which acts as a "terminal
multiplexer", allowing for the easy managment of many terminal sessions from a
single application.
Other applications that include pane-like functionality include (but are not
limited to):
* screen
* terminator
* emacs & vim
* Iterm2
## Design
The architecture of the Windows Terminal can be broken into two main pieces:
Tabs and Panes. The Windows Terminal supports _top-level_ tabs, with nested
panes inside the tabs. This means that there's a single strip of tabs along the
application, and each tab has a set of panes that are visible within the context
of that tab.
Panes are implemented as a binary tree of panes. A Pane can either be a leaf
pane, with it's own terminal control that it displays, or it could be a parent
pane, where it has two children, each with their own terminal control.
When a pane is a parent, its two children are either split vertically or
horizontally. Parent nodes don't have a terminal of their own, they merely
display the terminals of their children.
* If a Pane is split vertically, the two panes are seperated by a vertical
split, as to appear side-by-side. Think `[|]`
* If a Pane is split horizontally, the two panes are split by a horizontal
separator, and appear above/below one another. Think `[-]`.
As additional panes are created, panes will continue to subdivide the space of
their parent. It's up to the parent pane to control the sizing and display of
it's children.
### Example
We'll start by taking the terminal and creating a single vertical split. There
are now two panes in the terminal, side by side. The original terminal is `A`,
and the newly created one is `B`. The terminal now looks like this:
```
+---------------+
| | | 1: parent [|]
| | | ├── 2: A
| | | └── 3: B
| A | B |
| | |
| | |
| | |
+---------------+
```
Here, there are actually 3 nodes: 1 is the parent of both 2 and 3. 2 is the node
containing the `A` terminal, and 3 is the node with the `B` terminal.
We could now split `B` in two horizontally, creating a third terminal pane `C`.
```
+---------------+
| | | 1: parent [|]
| | B | ├── 2: A
| | | └── 3: parent [-]
| A +-------+ ├── 4: B
| | | └── 5: C
| | C |
| | |
+---------------+
```
Node 3 is now a parent node, and the terminal `B` has moved into a new node as a
sibling of the new terminal `C`.
We could also split `A` in horizontally, creating a fourth terminal pane `D`.
```
+---------------+
| | | 1: parent [|]
| A | B | ├── 2: parent [-]
| | | | ├── 4: A
+-------+-------+ | └── 5: D
| | | └── 3: parent [-]
| D | C | ├── 4: B
| | | └── 5: C
+---------------+
```
While it may appear that there's a single horizonal separator and a single
vertical separator here, that's not actually the case. Due to the tree-like
structure of the pane splitting, the horizontal splits exist only between the
two panes they're splitting. So, the user could move each of the horizontal
splits independently, without affecting the other set of panes. As an example:
```
+---------------+
| | |
| A | |
+-------+ B |
| | |
| D | |
| +-------+
| | C |
+---------------+
```
### Creating a pane
In the basic use case, the user will decide to split the currently focused pane.
The currently focused pane is always a leaf, because as parent's can't be
focused (they don't have their own terminal). When a user decides to add a new
pane, the child will:
1. Convert into a parent
2. Move its terminal into its first child
3. Split its UI in half, and display each child in one half.
It's up to the app hosting the panes to tell the pane what kind of terminal in
wants created in the new pane. By default, the new pane will be created with the
default settings profile.
### While panes are open
When a tab has multiple panes open, only one is the "active" pane. This is the
pane that was last focused in the tab. If the tab is the currently open tab,
then this is the pane with the currently focused terminal control. When the user
brings the tab into focus, the last focused pane is the pane that should become
focused again.
The tab's state will be updated to reflect the state of it's focused pane. The
title text and icon of the tab will reflect that of the focused pane. Should the
focus switch from one pane to another, the tab's text and icon should update to
reflect the newly focused control. Any additional state that the tab would
display for a single pane should also be reflected in the tab for a tab with
multiple panes.
While panes are open, the user should be able to move any split between panes.
In moving the split, the sizes of the terminal controls should be resized to
match.
### Closing a pane
A pane can either be closed by the user manually, or when the terminal it's
attached to raises its ConnectionClosed event. When this happens, we should
remove this pane from the tree. The parent of the closing pane will have to
remove the pane as one of it's children. If the sibling of the closing pane is a
leaf, then the parent should just take all of the state from the remaining pane.
This will cause the remaining pane's content to expand to take the entire
boundaries of the parent's pane. If the remaining child was a parent itself,
then the parent will take both the children of the remaining pane, and make them
the parent's children, as if the parent node was taken from the tree and
replaced by the remaining child.
## Future considerations
The Pane implementation isn't complete in it's current form. There are many
additional things that could be done to improve the user experience. This is by
no means a comprehensive list.
* [ ] Panes should be resizable with the mouse. The user should be able to drag
the separator for a pair of panes, and have the content between them resize as
the separator moves.
* [ ] There's no keyboard shortcut for "ClosePane"
* [ ] The user should be able to configure what profile is used for splitting a
pane. Currently, the default profile is used, but it's possible a user might
want to create a new pane with the parent pane's profile.
* [ ] There should be some sort of UI to indicate that a particular pane is
focused, more than just the blinking cursor. `tmux` accomplishes this by
colorizing the separators adjacent to the active pane. Another idea is
displaying a small outline around the focused pane (like when tabbing through
controls on a webpage).
* [ ] The user should be able to navigate the focus of panes with the keyboard,
instead of requiring the mouse.
* [ ] The user should be able to zoom a pane, to make the pane take the entire
size of the terminal window temporarily.
* [ ] A pane doesn't necessarily need to host a terminal. It could potentially
host another UIElement. One could imagine enabling a user to quickly open up a
Browser pane to search for a particular string without needing to leave the
terminal.
## Footnotes
### Why not top-level panes, and nested tabs?
If each pane were to have it's own set of tabs, then each pane would need to
reserve screen real estate for a row of tabs. As a user continued to split the
window, more and more of the screen would be dedicated to just displaying a row
of tabs, which isn't really the important part of the application, the terminal
is.
Additionally, if there were top-level panes, once the root was split, it would
not be possible to move a single pane to be the full size of the window. The
user would need to somehow close the other panes, to be able to make the split
the size of the dull window.
One con of this design is that if a control is hosted in a pane, the current
design makes it hard to move out of a pane into it's own tab, or into another
pane. This could be solved a number of ways. There could be keyboard shortcuts
for swapping the positions of tabs, or a shortcut for both "zooming" a tab
(temporarily making it the full size) or even popping a pane out to it's own
tab. Additionally, a right-click menu option could be added to do the
aformentioned actions. Discoverability of these two actions is not as high as
just dragging a tab from one pane to another; however, it's believed that panes
are more of a power-user scenario, and power users will not neccessarily be
turned off by the feature's discoverability.

155
doc/contributing.md Normal file
View File

@@ -0,0 +1,155 @@
# Terminal Contributor's Guide
Below is our guidance for how to report issues, propose new features, and submit contributions via Pull Requests (PRs).
## Open Development Workflow
The Windows Terminal team is VERY active in this GitHub Repo. In fact, we live in it all day long and carry out all our development in the open!
When the team finds issues we file them in the repo. When we propose new ideas or think-up new features, we file new feature requests. When we work on fixes or features, we create branches and work on those improvements. And when PRs are reviewed, we review in public - including all the good, the bad, and the ugly parts.
The point of doing all this work in public is to ensure that we are holding ourselves to a high degree of transparency, and so that the community sees that we apply the same processes and hold ourselves to the same quality-bar as we do to community-submitted issues and PRs. We also want to make sure that we expose our team culture and "tribal knowledge" that is inherent in any closely-knit team, which often contains considerable value to those new to the project who are trying to figure out "why the heck does this thing look/work like this???"
### Repo Bot
The team triages new issues several times a week. During triage, the team uses labels to categorize, manage, and drive the project workflow.
We employ [a bot engine](https://github.com/microsoft/terminal/blob/master/doc/bot.md) to help us automate common processes within our workflow.
We drive the bot by tagging issues with specific labels which cause the bot engine to close issues, merge branches, etc. This bot engine helps us keep the repo clean by automating the process of notifying appropriate parties if/when information/follow-up is needed, and closing stale issues/PRs after reminders have remained unanswered for several days.
Therefore, if you do file issues, or create PRs, please keep an eye on your GitHub notifications. If you do not respond to requests for information, your issues/PRs may be closed automatically.
---
## Before you start, file an issue
Please follow this simple rule to help us eliminate any unnecessary wasted effort & frustration, and ensure an efficient and effective use of everyone's time - yours, ours, and other community members':
> 👉 If you have a question, think you've discovered an issue, would like to propose a new feature, etc., then find/file an issue **BEFORE** starting work to fix/implement it.
### Search existing issues first
Before filing a new issue, search existing open and closed issues first: This project is moving fast! It is likely someone else has found the problem you're seeing, and someone may be working on or have already contributed a fix!
If no existing item describes your issue/feature, great - please file a new issue:
### File a new Issue
* Don't know whether you're reporting an issue or requesting a feature? File an issue
* Have a question that you don't see answered in docs, videos, etc.? File an issue
* Want to know if we're planning on building a particular feature? File an issue
* Got a great idea for a new feature? File an issue/request/idea
* Don't understand how to do something? File an issue/Community Guidance Request
* Found an existing issue that describes yours? Great - upvote and add additional commentary / info / repro-steps / etc.
When you hit "New Issue", select the type of issue closest to what you want to report/ask/request:
![New issue types](/doc/images/new-issue-template.png)
### Complete the template
**Complete the information requested in the issue template, providing as much information as possible**. The more information you provide, the more likely your issue/ask will be understood and implemented. Helpful information includes:
* What device you're running (inc. CPU type, memory, disk, etc.)
* What build of Windows your device is running
👉 Tip: Run the following in PowerShell Core
```powershell
C:\> $PSVersionTable.OS
Microsoft Windows 10.0.18909
```
... or in Windows PowerShell
```powershell
C:\> $PSVersionTable.BuildVersion
Major Minor Build Revision
----- ----- ----- --------
10 0 18912 1001
```
... or Cmd:
```cmd
C:\> ver
Microsoft Windows [Version 10.0.18900.1001]
```
* What tools and apps you're using (e.g. VS 2019, VSCode, etc.)
* Don't assume we're experts in setting up YOUR environment and don't assume we are experts in `<your distro/tool of choice>`. Teach us to help you!
* **We LOVE detailed repro steps!** What steps do we need to take to reproduce the issue? Assume we love to read repro steps. As much detail as you can stand is probably _barely_ enough detail for us!
* If you're reporting a particular character/glyph not rendering correctly, the specific Unicode codepoint would be MOST welcome (e.g. U+1F4AF, U+4382)
* Prefer error message text where possible or screenshots of errors if text cannot be captured
* We MUCH prefer text command-line script than screenshots of command-line script.
* **If you intend to implement the fix/feature yourself then say so!** If you do not indicate otherwise we will assume that the issue is our to solve, or may label the issue as `Help-Wanted`.
### DO NOT post "+1" comments
> ⚠ DO NOT post "+1", "me too", or similar comments - they just add noise to an issue.
If you don't have any additional info/context to add but would like to indicate that you're affected by the issue, upvote the original issue by clicking its [+😊] button and hitting 👍 (+1) icon. This way we can actually measure how impactful an issue is.
---
## Contributing fixes / features
For those able & willing to help fix issues and/or implement features ...
### To Spec or not to Spec
Some issues/features may be quick and simple to describe and understand. For such scenarios, once a team member has agreed with your approach, skip ahead to the section headed "Fork, Branch, and Create your PR", below.
Small issues that do not require a spec will be labelled Issue-Bug or Issue-Task.
However, some issues/features will require careful thought & formal design before implementation. For these scenarios, we'll request that a spec is written and the associated issue will be labeled Issue-Feature.
Specs help collaborators discuss different approaches to solve a problem, describe how the feature will behave, how the feature will impact the user, what happens if something goes wrong, etc. Driving towards agreement in a spec, before any code is written, often results in simpler code, and less wasted effort in the long run.
Specs will be managed in a very similar manner as code contributions so please follow the "Fork, Branch and Create your PR" below.
### Writing / Contributing-to a Spec
To write/contribute to a spec: fork, branch and commit via PRs, as you would with any code changes.
Specs are written in markdown, stored under the `\doc\spec` folder and named `[issue id] - [spec description].md`.
👉 **It is important to follow the spec templates and complete the requested information**. The available spec templates will help ensure that specs contain the minimum information & decisions necessary to permit development to begin. In particular, specs require you to confirm that you've already discussed the issue/idea with the team in an issue and that you provide the issue ID for reference.
Team members will be happy to help review specs and guide them to completion.
### Help Wanted
Once the team have approved an issue/spec, development can proceed. If no developers are immediately available, the spec can be parked ready for a developer to get started. Parked specs' issues will be labeled "Help Wanted". To find a list of development opportunities waiting for developer involvement, visit the Issues and filter on [the Help-Wanted label](https://github.com/microsoft/terminal/labels/Help-Wanted).
---
## Development
### Fork, Clone, Branch and Create your PR
Once you've discussed your proposed feature/fix/etc. with a team member, and you've agreed an approach or a spec has been written and approved, it's time to start development:
1. Fork the repo if you haven't already
1. Clone your fork locally
1. Create & push a feature branch
1. Create a [Draft Pull Request (PR)](https://github.blog/2019-02-14-introducing-draft-pull-requests/)
1. Work on your changes
### Code Review
When you'd like the team to take a look, (even if the work is not yet fully-complete), mark the PR as 'Ready For Review' so that the team can review your work and provide comments, suggestions, and request changes. It may take several cycles, but the end result will be solid, testable, conformant code that is safe for us to merge.
> ⚠ Remember: **changes you make may affect both Windows Terminal and Windows Console and may end up being re-incorporated into Windows itself!** Because of this, we will treat community PR's with the same level of scrutiny and rigor as commits submitted to the official Windows source by team members and partners.
### Merge
Once your code has been reviewed and approved by the requisite number of team members, it will be merged into the master branch. Once merged, your PR will be automatically closed.
---
## Thank you
Thank you in advance for your contribution! Now, [what's next on the list](https://github.com/microsoft/terminal/labels/Help-Wanted)? 😜

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View File

@@ -0,0 +1,212 @@
UTF-8 encoded sample plain-text file
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
Markus Kuhn [ˈmaʳkʊs kuːn] <http://www.cl.cam.ac.uk/~mgk25/> — 2002-07-25
The ASCII compatible UTF-8 encoding used in this plain-text file
is defined in Unicode, ISO 10646-1, and RFC 2279.
Using Unicode/UTF-8, you can write in emails and source code things such as
Mathematics and sciences:
∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ⎧⎡⎛┌─────┐⎞⎤⎫
⎪⎢⎜│a²+b³ ⎟⎥⎪
∀x∈: ⌈x⌉ = x⌋, α ∧ ¬β = ¬(¬α β), ⎪⎢⎜│───── ⎟⎥⎪
⎪⎢⎜⎷ c₈ ⎟⎥⎪
⊆ ℕ₀ ⊂ , ⎨⎢⎜ ⎟⎥⎬
⎪⎢⎜ ∞ ⎟⎥⎪
⊥ < a ≠ b ≡ c ≤ d ≪ ⇒ (⟦A⟧ ⇔ ⟪B⟫), ⎪⎢⎜ ⎲ ⎟⎥⎪
⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪
2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm ⎩⎣⎝i=1 ⎠⎦⎭
Linguistics and dictionaries:
ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn
Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ]
APL:
((VV)=V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈
Nicer typography in plain text files:
╔══════════════════════════════════════════╗
║ ║
║ • single and “double” quotes ║
║ ║
║ • Curly apostrophes: “Weve been here” ║
║ ║
║ • Latin-1 apostrophe and accents: '´` ║
║ ║
║ • deutsche „Anführungszeichen“ ║
║ ║
║ • †, ‡, ‰, •, 34, —, 5/+5, ™, … ║
║ ║
║ • ASCII safety test: 1lI|, 0OD, 8B ║
║ ╭─────────╮ ║
║ • the euro symbol: │ 14.95 € │ ║
║ ╰─────────╯ ║
╚══════════════════════════════════════════╝
Combining characters:
STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑
Greek (in Polytonic):
The Greek anthem:
Σὲ γνωρίζω ἀπὸ τὴν κόψη
τοῦ σπαθιοῦ τὴν τρομερή,
σὲ γνωρίζω ἀπὸ τὴν ὄψη
ποὺ μὲ βία μετράει τὴ γῆ.
᾿Απ᾿ τὰ κόκκαλα βγαλμένη
τῶν ῾Ελλήνων τὰ ἱερά
καὶ σὰν πρῶτα ἀνδρειωμένη
χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά!
From a speech of Demosthenes in the 4th century BC:
Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι,
ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς
λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ
τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿
εἰς τοῦτο προήκοντα, ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ
πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν
οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι,
οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν
ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον
τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι
γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν
προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους
σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ
τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ
τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς
τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον.
Δημοσθένους, Γ´ ᾿Ολυνθιακὸς
Georgian:
From a Unicode conference invitation:
გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო
კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს,
ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს
ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი,
ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება
ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში,
ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში.
Russian:
From a Unicode conference invitation:
Зарегистрируйтесь сейчас на Десятую Международную Конференцию по
Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.
Конференция соберет широкий круг экспертов по вопросам глобального
Интернета и Unicode, локализации и интернационализации, воплощению и
применению Unicode в различных операционных системах и программных
приложениях, шрифтах, верстке и многоязычных компьютерных системах.
Thai (UCS Level 2):
Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese
classic 'San Gua'):
[----------------------------|------------------------]
๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่
สิบสองกษัตริย์ก่อนหน้าแลถัดไป สององค์ไซร้โง่เขลาเบาปัญญา
ทรงนับถือขันทีเป็นที่พึ่ง บ้านเมืองจึงวิปริตเป็นนักหนา
โฮจิ๋นเรียกทัพทั่วหัวเมืองมา หมายจะฆ่ามดชั่วตัวสำคัญ
เหมือนขับไสไล่เสือจากเคหา รับหมาป่าเข้ามาเลยอาสัญ
ฝ่ายอ้องอุ้นยุแยกให้แตกกัน ใช้สาวนั้นเป็นชนวนชื่นชวนใจ
พลันลิฉุยกุยกีกลับก่อเหตุ ช่างอาเพศจริงหนาฟ้าร้องไห้
ต้องรบราฆ่าฟันจนบรรลัย ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ
(The above is a two-column text. If combining characters are handled
correctly, the lines of the second column should be aligned with the
| character above.)
Ethiopian:
Proverbs in the Amharic language:
ሰማይ አይታረስ ንጉሥ አይከሰስ።
ብላ ካለኝ እንደአባቴ በቆመጠኝ።
ጌጥ ያለቤቱ ቁምጥና ነው።
ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው።
የአፍ ወለምታ በቅቤ አይታሽም።
አይጥ በበላ ዳዋ ተመታ።
ሲተረጉሙ ይደረግሙ።
ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል።
ድር ቢያብር አንበሳ ያስር።
ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም።
እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም።
የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ።
ሥራ ከመፍታት ልጄን ላፋታት።
ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል።
የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ።
ተንጋሎ ቢተፉ ተመልሶ ባፉ።
ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው።
እግርህን በፍራሽህ ልክ ዘርጋ።
Runes:
ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ
(Old English, which transcribed into Latin reads 'He cwaeth that he
bude thaem lande northweardum with tha Westsae.' and means 'He said
that he lived in the northern land near the Western Sea.')
Braille:
⡌⠁⠧⠑ ⠼⠁⠒ ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌
⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞
⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎
⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂
⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙
⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑
⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲
⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹
⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞
⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕
⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹
⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎
⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎
⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳
⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞
⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
(The first couple of paragraphs of "A Christmas Carol" by Dickens)
Compact font selection example text:
ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789
abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ
–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд
∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi<>⑀₂ἠḂӥẄɐː⍎אԱა
Greetings in various languages:
Hello world, Καλημέρα κόσμε, コンニチハ
Box drawing alignment tests: █
╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳
║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳
║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳
╠╡ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎
║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏
╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ ▗▄▖▛▀▜ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█
▝▀▘▙▄▟

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

View File

@@ -0,0 +1,58 @@
---
author: <first-name> <last-name> <github-id>/<email>
created on: <yyyy-mm-dd>
last updated: <yyyy-mm-dd>
issue id: <github issue id>
---
# Spec Title
## Abstract
[comment]: # Outline what this spec describes
## Inspiration
[comment]: # What were the drivers/inspiration behind the creation of this spec.
## Solution Design
[comment]: # Outline the design of the solution. Feel free to include ASCII-art diagrams, etc.
## UI/UX Design
[comment]: # What will this fix/feature look like? How will it affect the end user?
## Capabilities
[comment]: # Discuss how the proposed fixes/features impact the following key considerations:
### Accessibility
[comment]: # How will the proposed change impact accessibility for users of screen readers, assistive input devices, etc.
### Security
[comment]: # How will the proposed change impact security?
### Reliability
[comment]: # Will the proposed change improve reliabilty? If not, why make the change?
### Compatibility
[comment]: # Will the proposed change break existing code/behaviors? If so, how, and is the breaking change "worth it"?
### Performance, Power, and Efficiency
## Potential Issues
[comment]: # What are some of the things that might cause problems with the fixes/features proposed? Consider how the user might be negatively impacted.
## Future considerations
[comment]: # What are some of the things that the fixes/features might unlock in the future? Does the implementation of this spec enable scenarios?
## Resources
[comment]: # Be sure to add links to references, resources, footnotes, etc.

View File

@@ -413,7 +413,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view,
{
const auto glyph = Utf16Parser::ParseNext(view);
DbcsAttribute dbcsAttr;
if (!glyph.empty() && IsGlyphFullWidth(glyph))
if (IsGlyphFullWidth(glyph))
{
dbcsAttr.SetLeading();
}

View File

@@ -424,6 +424,8 @@ namespace winrt::TerminalApp::implementation
bindings.ScrollDown([this]() { _Scroll(1); });
bindings.NextTab([this]() { _SelectNextTab(true); });
bindings.PrevTab([this]() { _SelectNextTab(false); });
bindings.SplitVertical([this]() { _SplitVertical(std::nullopt); });
bindings.SplitHorizontal([this]() { _SplitHorizontal(std::nullopt); });
bindings.ScrollUpPage([this]() { _ScrollPage(-1); });
bindings.ScrollDownPage([this]() { _ScrollPage(1); });
bindings.SwitchToTab([this](const auto index) { _SelectTab({ index }); });
@@ -579,23 +581,16 @@ namespace winrt::TerminalApp::implementation
for (auto &tab : _tabs)
{
const auto term = tab->GetTerminalControl();
const GUID tabProfile = tab->GetProfile();
if (profileGuid == tabProfile)
{
term.UpdateSettings(settings);
// Update the icons of the tabs with this profile open.
auto tabViewItem = tab->GetTabViewItem();
tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [profile, tabViewItem]() {
// _GetIconFromProfile has to run on the main thread
tabViewItem.Icon(App::_GetIconFromProfile(profile));
});
}
// Attempt to reload the settings of any panes with this profile
tab->UpdateSettings(settings, profileGuid);
}
}
// Update the icon of the tab for the currently focused profile in that tab.
for (auto& tab : _tabs)
{
_UpdateTabIcon(tab);
}
_root.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() {
// Refresh the UI theme
@@ -608,6 +603,50 @@ namespace winrt::TerminalApp::implementation
}
// Method Description:
// - Get the icon of the currently focused terminal control, and set its
// tab's icon to that icon.
// Arguments:
// - tab: the Tab to update the title for.
void App::_UpdateTabIcon(std::shared_ptr<Tab> tab)
{
const auto lastFocusedProfileOpt = tab->GetFocusedProfile();
if (lastFocusedProfileOpt.has_value())
{
const auto lastFocusedProfile = lastFocusedProfileOpt.value();
auto tabViewItem = tab->GetTabViewItem();
tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this, lastFocusedProfile, tabViewItem]() {
// _GetIconFromProfile has to run on the main thread
const auto* const matchingProfile = _settings->FindProfile(lastFocusedProfile);
if (matchingProfile)
{
tabViewItem.Icon(App::_GetIconFromProfile(*matchingProfile));
}
});
}
}
// Method Description:
// - Get the title of the currently focused terminal control, and set it's
// tab's text to that text. If this tab is the focused tab, then also
// bubble this title to any listeners of our TitleChanged event.
// Arguments:
// - tab: the Tab to update the title for.
void App::_UpdateTitle(std::shared_ptr<Tab> tab)
{
auto newTabTitle = tab->GetFocusedTitle();
// TODO #608: If the settings don't want the terminal's text in the
// tab, then display something else.
tab->SetTabText(newTabTitle);
if (_settings->GlobalSettings().GetShowTitleInTitlebar() &&
tab->IsFocused())
{
_titleChangeHandlers(newTabTitle);
}
}
// Method Description:
// - Update the current theme of the application. This will manually update
// all of the elements in our UI to match the given theme.
@@ -631,6 +670,8 @@ namespace winrt::TerminalApp::implementation
void App::_SetFocusedTabIndex(int tabIndex)
{
// GH#1117: This is a workaround because _tabView.SelectedIndex(tabIndex)
// sometimes set focus to an incorrect tab after removing some tabs
auto tab = _tabs.at(tabIndex);
_tabView.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [tab, this](){
auto tabViewItem = tab->GetTabViewItem();
@@ -725,6 +766,60 @@ namespace winrt::TerminalApp::implementation
eventArgs.HandleClipboardData(text);
}
// Method Description:
// - Connects event handlers to the TermControl for events that we want to
// handle. This includes:
// * the Copy and Paste events, for setting and retrieving clipboard data
// on the right thread
// * the TitleChanged event, for changing the text of the tab
// * the GotFocus event, for changing the title/icon in the tab when a new
// control is focused
// Arguments:
// - term: The newly created TermControl to connect the events for
// - hostingTab: The Tab that's hosting this TermControl instance
void App::_RegisterTerminalEvents(TermControl term, std::shared_ptr<Tab> hostingTab)
{
// Add an event handler when the terminal's selection wants to be copied.
// When the text buffer data is retrieved, we'll copy the data into the Clipboard
term.CopyToClipboard({ this, &App::_CopyToClipboardHandler });
// Add an event handler when the terminal wants to paste data from the Clipboard.
term.PasteFromClipboard({ this, &App::_PasteFromClipboardHandler });
// Don't capture a strong ref to the tab. If the tab is removed as this
// is called, we don't really care anymore about handling the event.
std::weak_ptr<Tab> weakTabPtr = hostingTab;
term.TitleChanged([this, weakTabPtr](auto newTitle){
auto tab = weakTabPtr.lock();
if (!tab)
{
return;
}
// The title of the control changed, but not necessarily the title
// of the tab. Get the title of the focused pane of the tab, and set
// the tab's text to the focused panes' text.
_UpdateTitle(tab);
});
term.GetControl().GotFocus([this, weakTabPtr](auto&&, auto&&)
{
auto tab = weakTabPtr.lock();
if (!tab)
{
return;
}
// Update the focus of the tab's panes
tab->UpdateFocus();
// Possibly update the title of the tab, window to match the newly
// focused pane.
_UpdateTitle(tab);
// Possibly update the icon of the tab.
_UpdateTabIcon(tab);
});
}
// Method Description:
// - Creates a new tab with the given settings. If the tab bar is not being
// currently displayed, it will be shown.
@@ -735,41 +830,11 @@ namespace winrt::TerminalApp::implementation
// Initialize the new tab
TermControl term{ settings };
// Add an event handler when the terminal's selection wants to be copied.
// When the text buffer data is retrieved, we'll copy the data into the Clipboard
term.CopyToClipboard([=](auto copiedData) {
_root.Dispatcher().RunAsync(CoreDispatcherPriority::High, [copiedData]() {
DataPackage dataPack = DataPackage();
dataPack.RequestedOperation(DataPackageOperation::Copy);
dataPack.SetText(copiedData);
Clipboard::SetContent(dataPack);
// TODO: MSFT 20642290 and 20642291
// rtf copy and html copy
});
});
// Add an event handler when the terminal wants to paste data from the Clipboard.
term.PasteFromClipboard([=](auto /*sender*/, auto eventArgs) {
_root.Dispatcher().RunAsync(CoreDispatcherPriority::High, [eventArgs]() {
PasteFromClipboard(eventArgs);
});
});
// Add the new tab to the list of our tabs.
auto newTab = _tabs.emplace_back(std::make_shared<Tab>(profileGuid, term));
// Add an event handler when the terminal's title changes. When the
// title changes, we'll bubble it up to listeners of our own title
// changed event, so they can handle it.
newTab->GetTerminalControl().TitleChanged([=](auto newTitle){
// Only bubble the change if this tab is the focused tab.
if (_settings->GlobalSettings().GetShowTitleInTitlebar() &&
newTab->IsFocused())
{
_titleChangeHandlers(newTitle);
}
});
// Hookup our event handlers to the new terminal
_RegisterTerminalEvents(term, newTab);
auto tabViewItem = newTab->GetTabViewItem();
_tabView.Items().Append(tabViewItem);
@@ -782,24 +847,15 @@ namespace winrt::TerminalApp::implementation
tabViewItem.Icon(_GetIconFromProfile(*profile));
}
// Add an event handler when the terminal's connection is closed.
newTab->GetTerminalControl().ConnectionClosed([=]() {
_tabView.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [newTab, tabViewItem, this]() {
const GUID tabProfile = newTab->GetProfile();
// Don't just capture this pointer, because the profile might
// get destroyed before this is called (case in point -
// reloading settings)
const auto* const p = _settings->FindProfile(tabProfile);
tabViewItem.PointerPressed({ this, &App::_OnTabClick });
if (p != nullptr && p->GetCloseOnExit())
{
_RemoveTabViewItem(tabViewItem);
}
// When the tab is closed, remove it from our list of tabs.
newTab->Closed([tabViewItem, this](){
_tabView.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [tabViewItem, this]() {
_RemoveTabViewItem(tabViewItem);
});
});
tabViewItem.PointerPressed({ this, &App::_OnTabClick });
// This is one way to set the tab's selected background color.
// tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), a Brush?);
@@ -815,7 +871,14 @@ namespace winrt::TerminalApp::implementation
// - the index of the currently focused tab if there is one, else -1
int App::_GetFocusedTabIndex() const
{
return _tabView.SelectedIndex();
// GH#1117: This is a workaround because _tabView.SelectedIndex()
// sometimes return incorrect result after removing some tabs
uint32_t focusedIndex;
if (_tabView.Items().IndexOf(_tabView.SelectedItem(), focusedIndex))
{
return focusedIndex;
}
return -1;
}
void App::_OpenSettings()
@@ -856,7 +919,7 @@ namespace winrt::TerminalApp::implementation
{
delta = std::clamp(delta, -1, 1);
const auto focusedTabIndex = _GetFocusedTabIndex();
const auto control = _tabs[focusedTabIndex]->GetTerminalControl();
const auto control = _GetFocusedControl();
const auto termHeight = control.GetViewHeight();
_tabs[focusedTabIndex]->Scroll(termHeight * delta);
}
@@ -868,10 +931,7 @@ namespace winrt::TerminalApp::implementation
// and get text to appear on separate lines.
void App::_CopyText(const bool trimTrailingWhitespace)
{
const int focusedTabIndex = _GetFocusedTabIndex();
std::shared_ptr<Tab> focusedTab{ _tabs[focusedTabIndex] };
const auto control = focusedTab->GetTerminalControl();
const auto control = _GetFocusedControl();
control.CopySelectionToClipboard(trimTrailingWhitespace);
}
@@ -921,10 +981,9 @@ namespace winrt::TerminalApp::implementation
try
{
auto tab = _tabs.at(selectedIndex);
auto control = tab->GetTerminalControl().GetControl();
_tabContent.Children().Clear();
_tabContent.Children().Append(control);
_tabContent.Children().Append(tab->GetRootElement());
tab->SetFocused(true);
_titleChangeHandlers(GetTitle());
@@ -977,8 +1036,7 @@ namespace winrt::TerminalApp::implementation
{
try
{
auto tab = _tabs.at(selectedIndex);
return tab->GetTerminalControl().Title();
return _GetFocusedControl().Title();
}
CATCH_LOG();
}
@@ -1067,6 +1125,98 @@ namespace winrt::TerminalApp::implementation
}
}
winrt::Microsoft::Terminal::TerminalControl::TermControl App::_GetFocusedControl()
{
int focusedTabIndex = _GetFocusedTabIndex();
auto focusedTab = _tabs[focusedTabIndex];
return focusedTab->GetFocusedTerminalControl();
}
// Method Description:
// - Vertically split the focused pane, and place the given TermControl into
// the newly created pane.
// Arguments:
// - profile: The profile GUID to associate with the newly created pane. If
// this is nullopt, use the default profile.
void App::_SplitVertical(const std::optional<GUID>& profileGuid)
{
_SplitPane(Pane::SplitState::Vertical, profileGuid);
}
// Method Description:
// - Horizontally split the focused pane and place the given TermControl
// into the newly created pane.
// Arguments:
// - profile: The profile GUID to associate with the newly created pane. If
// this is nullopt, use the default profile.
void App::_SplitHorizontal(const std::optional<GUID>& profileGuid)
{
_SplitPane(Pane::SplitState::Horizontal, profileGuid);
}
// Method Description:
// - Split the focused pane either horizontally or vertically, and place the
// given TermControl into the newly created pane.
// - If splitType == SplitState::None, this method does nothing.
// Arguments:
// - splitType: one value from the Pane::SplitState enum, indicating how the
// new pane should be split from its parent.
// - profile: The profile GUID to associate with the newly created pane. If
// this is nullopt, use the default profile.
void App::_SplitPane(const Pane::SplitState splitType, const std::optional<GUID>& profileGuid)
{
// Do nothing if we're requesting no split.
if (splitType == Pane::SplitState::None)
{
return;
}
const auto realGuid = profileGuid ? profileGuid.value() :
_settings->GlobalSettings().GetDefaultProfile();
const auto controlSettings = _settings->MakeSettings(realGuid);
TermControl newControl{ controlSettings };
const int focusedTabIndex = _GetFocusedTabIndex();
auto focusedTab = _tabs[focusedTabIndex];
// Hookup our event handlers to the new terminal
_RegisterTerminalEvents(newControl, focusedTab);
return splitType == Pane::SplitState::Horizontal ? focusedTab->AddHorizontalSplit(realGuid, newControl) :
focusedTab->AddVerticalSplit(realGuid, newControl);
}
// Method Description:
// - Place `copiedData` into the clipboard as text. Triggered when a
// terminal control raises it's CopyToClipboard event.
// Arguments:
// - copiedData: the new string content to place on the clipboard.
void App::_CopyToClipboardHandler(const winrt::hstring& copiedData)
{
_root.Dispatcher().RunAsync(CoreDispatcherPriority::High, [copiedData]() {
DataPackage dataPack = DataPackage();
dataPack.RequestedOperation(DataPackageOperation::Copy);
dataPack.SetText(copiedData);
Clipboard::SetContent(dataPack);
// TODO: MSFT 20642290 and 20642291
// rtf copy and html copy
});
}
// Method Description:
// - Fires an async event to get data from the clipboard, and paste it to
// the terminal. Triggered when the Terminal Control requests clipboard
// data with it's PasteFromClipboard event.
// Arguments:
// - eventArgs: the PasteFromClipboard event sent from the TermControl
void App::_PasteFromClipboardHandler(const IInspectable& /*sender*/,
const PasteFromClipboardEventArgs& eventArgs)
{
_root.Dispatcher().RunAsync(CoreDispatcherPriority::High, [eventArgs]() {
PasteFromClipboard(eventArgs);
});
}
// Method Description:
// - Takes a MenuFlyoutItem and a corresponding KeyChord value and creates the accelerator for UI display.

View File

@@ -62,7 +62,6 @@ namespace winrt::TerminalApp::implementation
std::vector<std::shared_ptr<Tab>> _tabs;
std::unique_ptr<::TerminalApp::CascadiaSettings> _settings;
std::unique_ptr<TerminalApp::AppKeyBindings> _keyBindings;
HRESULT _settingsLoadedResult;
@@ -90,6 +89,11 @@ namespace winrt::TerminalApp::implementation
void _FeedbackButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _UpdateTabView();
void _UpdateTabIcon(std::shared_ptr<Tab> tab);
void _UpdateTitle(std::shared_ptr<Tab> tab);
void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, std::shared_ptr<Tab> hostingTab);
void _CreateNewTabFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings);
@@ -103,6 +107,9 @@ namespace winrt::TerminalApp::implementation
void _Scroll(int delta);
void _CopyText(const bool trimTrailingWhitespace);
void _SplitVertical(const std::optional<GUID>& profileGuid);
void _SplitHorizontal(const std::optional<GUID>& profileGuid);
void _SplitPane(const Pane::SplitState splitType, const std::optional<GUID>& profileGuid);
// Todo: add more event implementations here
// MSFT:20641986: Add keybindings for New Window
void _ScrollPage(int delta);
@@ -118,6 +125,12 @@ namespace winrt::TerminalApp::implementation
void _ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme);
static Windows::UI::Xaml::Controls::IconElement _GetIconFromProfile(const ::TerminalApp::Profile& profile);
winrt::Microsoft::Terminal::TerminalControl::TermControl _GetFocusedControl();
void _CopyToClipboardHandler(const winrt::hstring& copiedData);
void _PasteFromClipboardHandler(const IInspectable& sender, const Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs& eventArgs);
static void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord);
};
}

View File

@@ -11,82 +11,6 @@ using namespace winrt::Microsoft::Terminal;
using namespace winrt::TerminalApp;
using namespace winrt::Windows::Data::Json;
static constexpr std::wstring_view KEYS_KEY{ L"keys" };
static constexpr std::wstring_view COMMAND_KEY{ L"command" };
static constexpr std::wstring_view COPYTEXT_KEY{ L"copy" };
static constexpr std::wstring_view PASTETEXT_KEY{ L"paste" };
static constexpr std::wstring_view NEWTAB_KEY{ L"newTab" };
static constexpr std::wstring_view NEWTABWITHPROFILE0_KEY{ L"newTabProfile0" };
static constexpr std::wstring_view NEWTABWITHPROFILE1_KEY{ L"newTabProfile1" };
static constexpr std::wstring_view NEWTABWITHPROFILE2_KEY{ L"newTabProfile2" };
static constexpr std::wstring_view NEWTABWITHPROFILE3_KEY{ L"newTabProfile3" };
static constexpr std::wstring_view NEWTABWITHPROFILE4_KEY{ L"newTabProfile4" };
static constexpr std::wstring_view NEWTABWITHPROFILE5_KEY{ L"newTabProfile5" };
static constexpr std::wstring_view NEWTABWITHPROFILE6_KEY{ L"newTabProfile6" };
static constexpr std::wstring_view NEWTABWITHPROFILE7_KEY{ L"newTabProfile7" };
static constexpr std::wstring_view NEWTABWITHPROFILE8_KEY{ L"newTabProfile8" };
static constexpr std::wstring_view NEWWINDOW_KEY{ L"newWindow" };
static constexpr std::wstring_view CLOSEWINDOW_KEY{ L"closeWindow" };
static constexpr std::wstring_view CLOSETAB_KEY{ L"closeTab" };
static constexpr std::wstring_view SWITCHTOTAB_KEY{ L"switchToTab" };
static constexpr std::wstring_view NEXTTAB_KEY{ L"nextTab" };
static constexpr std::wstring_view PREVTAB_KEY{ L"prevTab" };
static constexpr std::wstring_view INCREASEFONTSIZE_KEY{ L"increaseFontSize" };
static constexpr std::wstring_view DECREASEFONTSIZE_KEY{ L"decreaseFontSize" };
static constexpr std::wstring_view SCROLLUP_KEY{ L"scrollUp" };
static constexpr std::wstring_view SCROLLDOWN_KEY{ L"scrollDown" };
static constexpr std::wstring_view SCROLLUPPAGE_KEY{ L"scrollUpPage" };
static constexpr std::wstring_view SCROLLDOWNPAGE_KEY{ L"scrollDownPage" };
static constexpr std::wstring_view SWITCHTOTAB0_KEY{ L"switchToTab0" };
static constexpr std::wstring_view SWITCHTOTAB1_KEY{ L"switchToTab1" };
static constexpr std::wstring_view SWITCHTOTAB2_KEY{ L"switchToTab2" };
static constexpr std::wstring_view SWITCHTOTAB3_KEY{ L"switchToTab3" };
static constexpr std::wstring_view SWITCHTOTAB4_KEY{ L"switchToTab4" };
static constexpr std::wstring_view SWITCHTOTAB5_KEY{ L"switchToTab5" };
static constexpr std::wstring_view SWITCHTOTAB6_KEY{ L"switchToTab6" };
static constexpr std::wstring_view SWITCHTOTAB7_KEY{ L"switchToTab7" };
static constexpr std::wstring_view SWITCHTOTAB8_KEY{ L"switchToTab8" };
static constexpr std::wstring_view OPENSETTINGS_KEY{ L"openSettings" };
// Specifically use a map here over an unordered_map. We want to be able to
// iterate over these entries in-order when we're serializing the keybindings.
static const std::map<std::wstring_view, ShortcutAction> commandNames {
{ COPYTEXT_KEY, ShortcutAction::CopyText },
{ PASTETEXT_KEY, ShortcutAction::PasteText },
{ NEWTAB_KEY, ShortcutAction::NewTab },
{ NEWTABWITHPROFILE0_KEY, ShortcutAction::NewTabProfile0 },
{ NEWTABWITHPROFILE1_KEY, ShortcutAction::NewTabProfile1 },
{ NEWTABWITHPROFILE2_KEY, ShortcutAction::NewTabProfile2 },
{ NEWTABWITHPROFILE3_KEY, ShortcutAction::NewTabProfile3 },
{ NEWTABWITHPROFILE4_KEY, ShortcutAction::NewTabProfile4 },
{ NEWTABWITHPROFILE5_KEY, ShortcutAction::NewTabProfile5 },
{ NEWTABWITHPROFILE6_KEY, ShortcutAction::NewTabProfile6 },
{ NEWTABWITHPROFILE7_KEY, ShortcutAction::NewTabProfile7 },
{ NEWTABWITHPROFILE8_KEY, ShortcutAction::NewTabProfile8 },
{ NEWWINDOW_KEY, ShortcutAction::NewWindow },
{ CLOSEWINDOW_KEY, ShortcutAction::CloseWindow },
{ CLOSETAB_KEY, ShortcutAction::CloseTab },
{ NEXTTAB_KEY, ShortcutAction::NextTab },
{ PREVTAB_KEY, ShortcutAction::PrevTab },
{ INCREASEFONTSIZE_KEY, ShortcutAction::IncreaseFontSize },
{ DECREASEFONTSIZE_KEY, ShortcutAction::DecreaseFontSize },
{ SCROLLUP_KEY, ShortcutAction::ScrollUp },
{ SCROLLDOWN_KEY, ShortcutAction::ScrollDown },
{ SCROLLUPPAGE_KEY, ShortcutAction::ScrollUpPage },
{ SCROLLDOWNPAGE_KEY, ShortcutAction::ScrollDownPage },
{ SWITCHTOTAB0_KEY, ShortcutAction::SwitchToTab0 },
{ SWITCHTOTAB1_KEY, ShortcutAction::SwitchToTab1 },
{ SWITCHTOTAB2_KEY, ShortcutAction::SwitchToTab2 },
{ SWITCHTOTAB3_KEY, ShortcutAction::SwitchToTab3 },
{ SWITCHTOTAB4_KEY, ShortcutAction::SwitchToTab4 },
{ SWITCHTOTAB5_KEY, ShortcutAction::SwitchToTab5 },
{ SWITCHTOTAB6_KEY, ShortcutAction::SwitchToTab6 },
{ SWITCHTOTAB7_KEY, ShortcutAction::SwitchToTab7 },
{ SWITCHTOTAB8_KEY, ShortcutAction::SwitchToTab8 },
{ OPENSETTINGS_KEY, ShortcutAction::OpenSettings },
};
namespace winrt::TerminalApp::implementation
{
void AppKeyBindings::SetKeyBinding(const TerminalApp::ShortcutAction& action,
@@ -190,6 +114,13 @@ namespace winrt::TerminalApp::implementation
_PrevTabHandlers();
return true;
case ShortcutAction::SplitVertical:
_SplitVerticalHandlers();
return true;
case ShortcutAction::SplitHorizontal:
_SplitHorizontalHandlers();
return true;
case ShortcutAction::SwitchToTab0:
_SwitchToTabHandlers(0);
return true;
@@ -224,139 +155,6 @@ namespace winrt::TerminalApp::implementation
return false;
}
// -------------------------------- Events ---------------------------------
DEFINE_EVENT(AppKeyBindings, CopyText, _CopyTextHandlers, TerminalApp::CopyTextEventArgs);
DEFINE_EVENT(AppKeyBindings, PasteText, _PasteTextHandlers, TerminalApp::PasteTextEventArgs);
DEFINE_EVENT(AppKeyBindings, NewTab, _NewTabHandlers, TerminalApp::NewTabEventArgs);
DEFINE_EVENT(AppKeyBindings, NewTabWithProfile, _NewTabWithProfileHandlers, TerminalApp::NewTabWithProfileEventArgs);
DEFINE_EVENT(AppKeyBindings, NewWindow, _NewWindowHandlers, TerminalApp::NewWindowEventArgs);
DEFINE_EVENT(AppKeyBindings, CloseWindow, _CloseWindowHandlers, TerminalApp::CloseWindowEventArgs);
DEFINE_EVENT(AppKeyBindings, CloseTab, _CloseTabHandlers, TerminalApp::CloseTabEventArgs);
DEFINE_EVENT(AppKeyBindings, SwitchToTab, _SwitchToTabHandlers, TerminalApp::SwitchToTabEventArgs);
DEFINE_EVENT(AppKeyBindings, NextTab, _NextTabHandlers, TerminalApp::NextTabEventArgs);
DEFINE_EVENT(AppKeyBindings, PrevTab, _PrevTabHandlers, TerminalApp::PrevTabEventArgs);
DEFINE_EVENT(AppKeyBindings, IncreaseFontSize, _IncreaseFontSizeHandlers, TerminalApp::IncreaseFontSizeEventArgs);
DEFINE_EVENT(AppKeyBindings, DecreaseFontSize, _DecreaseFontSizeHandlers, TerminalApp::DecreaseFontSizeEventArgs);
DEFINE_EVENT(AppKeyBindings, ScrollUp, _ScrollUpHandlers, TerminalApp::ScrollUpEventArgs);
DEFINE_EVENT(AppKeyBindings, ScrollDown, _ScrollDownHandlers, TerminalApp::ScrollDownEventArgs);
DEFINE_EVENT(AppKeyBindings, ScrollUpPage, _ScrollUpPageHandlers, TerminalApp::ScrollUpPageEventArgs);
DEFINE_EVENT(AppKeyBindings, ScrollDownPage, _ScrollDownPageHandlers, TerminalApp::ScrollDownPageEventArgs);
DEFINE_EVENT(AppKeyBindings, OpenSettings, _OpenSettingsHandlers, TerminalApp::OpenSettingsEventArgs);
// Method Description:
// - Deserialize an AppKeyBindings from the key mappings that are in the
// array `json`. The json array should contain an array of objects with
// both a `command` string and a `keys` array, where `command` is one of
// the names listed in `commandNames`, and `keys` is an array of
// keypresses. Currently, the array should contain a single string, which
// can be deserialized into a KeyChord.
// Arguments:
// - json: and array of JsonObject's to deserialize into our _keyShortcuts mapping.
// Return Value:
// - the newly constructed AppKeyBindings object.
TerminalApp::AppKeyBindings AppKeyBindings::FromJson(const JsonArray& json)
{
TerminalApp::AppKeyBindings newBindings{};
for (const auto& value : json)
{
if (value.ValueType() == JsonValueType::Object)
{
JsonObject obj = value.GetObjectW();
if (obj.HasKey(COMMAND_KEY) && obj.HasKey(KEYS_KEY))
{
const auto commandString = obj.GetNamedString(COMMAND_KEY);
const auto keys = obj.GetNamedArray(KEYS_KEY);
if (keys.Size() != 1)
{
continue;
}
const auto keyChordString = keys.GetAt(0).GetString();
ShortcutAction action;
// Try matching the command to one we have
auto found = commandNames.find(commandString);
if (found != commandNames.end())
{
action = found->second;
}
else
{
continue;
}
// Try parsing the chord
try
{
auto chord = KeyChordSerialization::FromString(keyChordString);
newBindings.SetKeyBinding(action, chord);
}
catch (...)
{
continue;
}
}
}
}
return newBindings;
}
// Function Description:
// - Small helper to insert a given KeyChord, ShortcutAction pair into the
// given json array
// Arguments:
// - bindingsArray: The JsonArray to insert the object into.
// - chord: The KeyChord to serailize and place in the json array
// - actionName: the name of the ShortcutAction to use with this KeyChord
static void _AddShortcutToJsonArray(const JsonArray& bindingsArray,
const Settings::KeyChord& chord,
const std::wstring_view& actionName)
{
const auto keyString = KeyChordSerialization::ToString(chord);
if (keyString == L"")
{
return;
}
winrt::Windows::Data::Json::JsonObject jsonObject;
winrt::Windows::Data::Json::JsonArray keysArray;
keysArray.Append(JsonValue::CreateStringValue(keyString));
jsonObject.Insert(KEYS_KEY, keysArray);
jsonObject.Insert(COMMAND_KEY, JsonValue::CreateStringValue(actionName));
bindingsArray.Append(jsonObject);
}
// Method Description:
// - Serialize this AppKeyBindings to a json array of objects. Each object
// in the array represents a single keybinding, mapping a KeyChord to a
// ShortcutAction.
// Return Value:
// - a JsonArray which is an equivalent serialization of this object.
Windows::Data::Json::JsonArray AppKeyBindings::ToJson()
{
winrt::Windows::Data::Json::JsonArray bindingsArray;
// Iterate over all the possible actions in the names list, and see if
// it has a binding.
for (auto& actionName : commandNames)
{
const auto searchedForName = actionName.first;
const auto searchedForAction = actionName.second;
for (const auto& kv : _keyShortcuts)
{
const auto chord = kv.first;
const auto command = kv.second;
if (command == searchedForAction)
{
_AddShortcutToJsonArray(bindingsArray, chord, searchedForName);
}
}
}
return bindingsArray;
}
// Method Description:
// - Takes the KeyModifier flags from Terminal and maps them to the WinRT types which are used by XAML
// Return Value:
@@ -364,7 +162,7 @@ namespace winrt::TerminalApp::implementation
Windows::System::VirtualKeyModifiers AppKeyBindings::ConvertVKModifiers(Settings::KeyModifiers modifiers)
{
Windows::System::VirtualKeyModifiers keyModifiers = Windows::System::VirtualKeyModifiers::None;
if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Ctrl))
{
keyModifiers |= Windows::System::VirtualKeyModifiers::Control;
@@ -408,4 +206,25 @@ namespace winrt::TerminalApp::implementation
return winrt::hstring{ buffer };
}
// -------------------------------- Events ---------------------------------
DEFINE_EVENT(AppKeyBindings, CopyText, _CopyTextHandlers, TerminalApp::CopyTextEventArgs);
DEFINE_EVENT(AppKeyBindings, PasteText, _PasteTextHandlers, TerminalApp::PasteTextEventArgs);
DEFINE_EVENT(AppKeyBindings, NewTab, _NewTabHandlers, TerminalApp::NewTabEventArgs);
DEFINE_EVENT(AppKeyBindings, NewTabWithProfile, _NewTabWithProfileHandlers, TerminalApp::NewTabWithProfileEventArgs);
DEFINE_EVENT(AppKeyBindings, NewWindow, _NewWindowHandlers, TerminalApp::NewWindowEventArgs);
DEFINE_EVENT(AppKeyBindings, CloseWindow, _CloseWindowHandlers, TerminalApp::CloseWindowEventArgs);
DEFINE_EVENT(AppKeyBindings, CloseTab, _CloseTabHandlers, TerminalApp::CloseTabEventArgs);
DEFINE_EVENT(AppKeyBindings, SwitchToTab, _SwitchToTabHandlers, TerminalApp::SwitchToTabEventArgs);
DEFINE_EVENT(AppKeyBindings, NextTab, _NextTabHandlers, TerminalApp::NextTabEventArgs);
DEFINE_EVENT(AppKeyBindings, PrevTab, _PrevTabHandlers, TerminalApp::PrevTabEventArgs);
DEFINE_EVENT(AppKeyBindings, SplitVertical, _SplitVerticalHandlers, TerminalApp::SplitVerticalEventArgs);
DEFINE_EVENT(AppKeyBindings, SplitHorizontal, _SplitHorizontalHandlers, TerminalApp::SplitHorizontalEventArgs);
DEFINE_EVENT(AppKeyBindings, IncreaseFontSize, _IncreaseFontSizeHandlers, TerminalApp::IncreaseFontSizeEventArgs);
DEFINE_EVENT(AppKeyBindings, DecreaseFontSize, _DecreaseFontSizeHandlers, TerminalApp::DecreaseFontSizeEventArgs);
DEFINE_EVENT(AppKeyBindings, ScrollUp, _ScrollUpHandlers, TerminalApp::ScrollUpEventArgs);
DEFINE_EVENT(AppKeyBindings, ScrollDown, _ScrollDownHandlers, TerminalApp::ScrollDownEventArgs);
DEFINE_EVENT(AppKeyBindings, ScrollUpPage, _ScrollUpPageHandlers, TerminalApp::ScrollUpPageEventArgs);
DEFINE_EVENT(AppKeyBindings, ScrollDownPage, _ScrollDownPageHandlers, TerminalApp::ScrollDownPageEventArgs);
DEFINE_EVENT(AppKeyBindings, OpenSettings, _OpenSettingsHandlers, TerminalApp::OpenSettingsEventArgs);
}

View File

@@ -32,15 +32,13 @@ namespace winrt::TerminalApp::implementation
{
AppKeyBindings() = default;
static TerminalApp::AppKeyBindings FromJson(Windows::Data::Json::JsonArray const& json);
Windows::Data::Json::JsonArray ToJson();
static Windows::System::VirtualKeyModifiers ConvertVKModifiers(winrt::Microsoft::Terminal::Settings::KeyModifiers modifiers);
static winrt::hstring FormatOverrideShortcutText(winrt::Microsoft::Terminal::Settings::KeyModifiers modifiers);
bool TryKeyChord(winrt::Microsoft::Terminal::Settings::KeyChord const& kc);
void SetKeyBinding(TerminalApp::ShortcutAction const& action, winrt::Microsoft::Terminal::Settings::KeyChord const& chord);
Microsoft::Terminal::Settings::KeyChord GetKeyBinding(TerminalApp::ShortcutAction const& action);
static Windows::System::VirtualKeyModifiers ConvertVKModifiers(winrt::Microsoft::Terminal::Settings::KeyModifiers modifiers);
static winrt::hstring FormatOverrideShortcutText(winrt::Microsoft::Terminal::Settings::KeyModifiers modifiers);
DECLARE_EVENT(CopyText, _CopyTextHandlers, TerminalApp::CopyTextEventArgs);
DECLARE_EVENT(PasteText, _PasteTextHandlers, TerminalApp::PasteTextEventArgs);
DECLARE_EVENT(NewTab, _NewTabHandlers, TerminalApp::NewTabEventArgs);
@@ -51,6 +49,8 @@ namespace winrt::TerminalApp::implementation
DECLARE_EVENT(SwitchToTab, _SwitchToTabHandlers, TerminalApp::SwitchToTabEventArgs);
DECLARE_EVENT(NextTab, _NextTabHandlers, TerminalApp::NextTabEventArgs);
DECLARE_EVENT(PrevTab, _PrevTabHandlers, TerminalApp::PrevTabEventArgs);
DECLARE_EVENT(SplitVertical, _SplitVerticalHandlers, TerminalApp::SplitVerticalEventArgs);
DECLARE_EVENT(SplitHorizontal, _SplitHorizontalHandlers, TerminalApp::SplitHorizontalEventArgs);
DECLARE_EVENT(IncreaseFontSize, _IncreaseFontSizeHandlers, TerminalApp::IncreaseFontSizeEventArgs);
DECLARE_EVENT(DecreaseFontSize, _DecreaseFontSizeHandlers, TerminalApp::DecreaseFontSizeEventArgs);
DECLARE_EVENT(ScrollUp, _ScrollUpHandlers, TerminalApp::ScrollUpEventArgs);

View File

@@ -22,6 +22,8 @@ namespace TerminalApp
CloseTab,
NextTab,
PrevTab,
SplitVertical,
SplitHorizontal,
SwitchToTab0,
SwitchToTab1,
SwitchToTab2,
@@ -49,6 +51,8 @@ namespace TerminalApp
delegate void CloseTabEventArgs();
delegate void NextTabEventArgs();
delegate void PrevTabEventArgs();
delegate void SplitVerticalEventArgs();
delegate void SplitHorizontalEventArgs();
delegate void SwitchToTabEventArgs(Int32 profileIndex);
delegate void IncreaseFontSizeEventArgs();
delegate void DecreaseFontSizeEventArgs();
@@ -63,9 +67,6 @@ namespace TerminalApp
{
AppKeyBindings();
Windows.Data.Json.JsonArray ToJson();
static AppKeyBindings FromJson(Windows.Data.Json.JsonArray json);
void SetKeyBinding(ShortcutAction action, Microsoft.Terminal.Settings.KeyChord chord);
Microsoft.Terminal.Settings.KeyChord GetKeyBinding(ShortcutAction action);
@@ -79,6 +80,8 @@ namespace TerminalApp
event SwitchToTabEventArgs SwitchToTab;
event NextTabEventArgs NextTab;
event PrevTabEventArgs PrevTab;
event SplitVerticalEventArgs SplitVertical;
event SplitHorizontalEventArgs SplitHorizontal;
event IncreaseFontSizeEventArgs IncreaseFontSize;
event DecreaseFontSizeEventArgs DecreaseFontSize;
event ScrollUpEventArgs ScrollUp;

View File

@@ -0,0 +1,211 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "AppKeyBindingsSerialization.h"
#include "KeyChordSerialization.h"
#include "Utils.h"
#include <winrt/Microsoft.Terminal.Settings.h>
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::TerminalApp;
static constexpr std::string_view KeysKey{ "keys" };
static constexpr std::string_view CommandKey{ "command" };
static constexpr std::string_view CopyTextKey{ "copy" };
static constexpr std::string_view PasteTextKey{ "paste" };
static constexpr std::string_view NewTabKey{ "newTab" };
static constexpr std::string_view NewTabWithProfile0Key{ "newTabProfile0" };
static constexpr std::string_view NewTabWithProfile1Key{ "newTabProfile1" };
static constexpr std::string_view NewTabWithProfile2Key{ "newTabProfile2" };
static constexpr std::string_view NewTabWithProfile3Key{ "newTabProfile3" };
static constexpr std::string_view NewTabWithProfile4Key{ "newTabProfile4" };
static constexpr std::string_view NewTabWithProfile5Key{ "newTabProfile5" };
static constexpr std::string_view NewTabWithProfile6Key{ "newTabProfile6" };
static constexpr std::string_view NewTabWithProfile7Key{ "newTabProfile7" };
static constexpr std::string_view NewTabWithProfile8Key{ "newTabProfile8" };
static constexpr std::string_view NewWindowKey{ "newWindow" };
static constexpr std::string_view CloseWindowKey{ "closeWindow" };
static constexpr std::string_view CloseTabKey{ "closeTab" };
static constexpr std::string_view SwitchtoTabKey{ "switchToTab" };
static constexpr std::string_view NextTabKey{ "nextTab" };
static constexpr std::string_view PrevTabKey{ "prevTab" };
static constexpr std::string_view IncreaseFontSizeKey{ "increaseFontSize" };
static constexpr std::string_view DecreaseFontSizeKey{ "decreaseFontSize" };
static constexpr std::string_view ScrollupKey{ "scrollUp" };
static constexpr std::string_view ScrolldownKey{ "scrollDown" };
static constexpr std::string_view ScrolluppageKey{ "scrollUpPage" };
static constexpr std::string_view ScrolldownpageKey{ "scrollDownPage" };
static constexpr std::string_view SwitchToTab0Key{ "switchToTab0" };
static constexpr std::string_view SwitchToTab1Key{ "switchToTab1" };
static constexpr std::string_view SwitchToTab2Key{ "switchToTab2" };
static constexpr std::string_view SwitchToTab3Key{ "switchToTab3" };
static constexpr std::string_view SwitchToTab4Key{ "switchToTab4" };
static constexpr std::string_view SwitchToTab5Key{ "switchToTab5" };
static constexpr std::string_view SwitchToTab6Key{ "switchToTab6" };
static constexpr std::string_view SwitchToTab7Key{ "switchToTab7" };
static constexpr std::string_view SwitchToTab8Key{ "switchToTab8" };
static constexpr std::string_view OpenSettingsKey{ "openSettings" };
// Specifically use a map here over an unordered_map. We want to be able to
// iterate over these entries in-order when we're serializing the keybindings.
// HERE BE DRAGONS:
// These are string_views that are being used as keys. These string_views are
// just pointers to other strings. This could be dangerous, if the map outlived
// the actual strings being pointed to. However, since both these strings and
// the map are all const for the lifetime of the app, we have nothing to worry
// about here.
static const std::map<std::string_view, ShortcutAction, std::less<>> commandNames {
{ CopyTextKey, ShortcutAction::CopyText },
{ PasteTextKey, ShortcutAction::PasteText },
{ NewTabKey, ShortcutAction::NewTab },
{ NewTabWithProfile0Key, ShortcutAction::NewTabProfile0 },
{ NewTabWithProfile1Key, ShortcutAction::NewTabProfile1 },
{ NewTabWithProfile2Key, ShortcutAction::NewTabProfile2 },
{ NewTabWithProfile3Key, ShortcutAction::NewTabProfile3 },
{ NewTabWithProfile4Key, ShortcutAction::NewTabProfile4 },
{ NewTabWithProfile5Key, ShortcutAction::NewTabProfile5 },
{ NewTabWithProfile6Key, ShortcutAction::NewTabProfile6 },
{ NewTabWithProfile7Key, ShortcutAction::NewTabProfile7 },
{ NewTabWithProfile8Key, ShortcutAction::NewTabProfile8 },
{ NewWindowKey, ShortcutAction::NewWindow },
{ CloseWindowKey, ShortcutAction::CloseWindow },
{ CloseTabKey, ShortcutAction::CloseTab },
{ NextTabKey, ShortcutAction::NextTab },
{ PrevTabKey, ShortcutAction::PrevTab },
{ IncreaseFontSizeKey, ShortcutAction::IncreaseFontSize },
{ DecreaseFontSizeKey, ShortcutAction::DecreaseFontSize },
{ ScrollupKey, ShortcutAction::ScrollUp },
{ ScrolldownKey, ShortcutAction::ScrollDown },
{ ScrolluppageKey, ShortcutAction::ScrollUpPage },
{ ScrolldownpageKey, ShortcutAction::ScrollDownPage },
{ SwitchToTab0Key, ShortcutAction::SwitchToTab0 },
{ SwitchToTab1Key, ShortcutAction::SwitchToTab1 },
{ SwitchToTab2Key, ShortcutAction::SwitchToTab2 },
{ SwitchToTab3Key, ShortcutAction::SwitchToTab3 },
{ SwitchToTab4Key, ShortcutAction::SwitchToTab4 },
{ SwitchToTab5Key, ShortcutAction::SwitchToTab5 },
{ SwitchToTab6Key, ShortcutAction::SwitchToTab6 },
{ SwitchToTab7Key, ShortcutAction::SwitchToTab7 },
{ SwitchToTab8Key, ShortcutAction::SwitchToTab8 },
};
// Function Description:
// - Small helper to create a json value serialization of a single
// KeyBinding->Action maping. The created object is of schema:
// {
// keys:[String],
// command:String
// }
// Arguments:
// - chord: The KeyChord to serialize
// - actionName: the name of the ShortcutAction to use with this KeyChord
// Return Value:
// - a Json::Value which is an equivalent serialization of this object.
static Json::Value _ShortcutAsJsonObject(const KeyChord& chord,
const std::string_view actionName)
{
const auto keyString = KeyChordSerialization::ToString(chord);
if (keyString == L"")
{
return nullptr;
}
Json::Value jsonObject;
Json::Value keysArray;
keysArray.append(winrt::to_string(keyString));
jsonObject[JsonKey(KeysKey)] = keysArray;
jsonObject[JsonKey(CommandKey)] = actionName.data();
return jsonObject;
}
// Method Description:
// - Serialize this AppKeyBindings to a json array of objects. Each object in
// the array represents a single keybinding, mapping a KeyChord to a
// ShortcutAction.
// Return Value:
// - a Json::Value which is an equivalent serialization of this object.
Json::Value AppKeyBindingsSerialization::ToJson(const winrt::TerminalApp::AppKeyBindings& bindings)
{
Json::Value bindingsArray;
// Iterate over all the possible actions in the names list, and see if
// it has a binding.
for (auto& actionName : commandNames)
{
const auto searchedForName = actionName.first;
const auto searchedForAction = actionName.second;
if (const auto chord{ bindings.GetKeyBinding(searchedForAction) })
{
if (const auto serialization{ _ShortcutAsJsonObject(chord, searchedForName) })
{
bindingsArray.append(serialization);
}
}
}
return bindingsArray;
}
// Method Description:
// - Deserialize an AppKeyBindings from the key mappings that are in the array
// `json`. The json array should contain an array of objects with both a
// `command` string and a `keys` array, where `command` is one of the names
// listed in `commandNames`, and `keys` is an array of keypresses. Currently,
// the array should contain a single string, which can be deserialized into a
// KeyChord.
// Arguments:
// - json: and array of JsonObject's to deserialize into our _keyShortcuts mapping.
// Return Value:
// - the newly constructed AppKeyBindings object.
winrt::TerminalApp::AppKeyBindings AppKeyBindingsSerialization::FromJson(const Json::Value& json)
{
winrt::TerminalApp::AppKeyBindings newBindings{};
for (const auto& value : json)
{
if (value.isObject())
{
const auto commandString = value[JsonKey(CommandKey)];
const auto keys = value[JsonKey(KeysKey)];
if (commandString && keys)
{
if (!keys.isArray() || keys.size() != 1)
{
continue;
}
const auto keyChordString = winrt::to_hstring(keys[0].asString());
ShortcutAction action;
// Try matching the command to one we have
const auto found = commandNames.find(commandString.asString());
if (found != commandNames.end())
{
action = found->second;
}
else
{
continue;
}
// Try parsing the chord
try
{
const auto chord = KeyChordSerialization::FromString(keyChordString);
newBindings.SetKeyBinding(action, chord);
}
catch (...)
{
continue;
}
}
}
}
return newBindings;
}

View File

@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
// Module Name:
// - Profile.hpp
//
// Abstract:
// - A couple helper functions for serializing/deserializing an AppKeyBindings
// to/from json. We need this to exist as external helper functions, rather
// than defining these as methods on the AppKeyBindings class, because
// AppKeyBindings is a winrt type. When we're working with a AppKeyBindings
// object, we only have access to methods defined on the winrt interface (in
// the idl). We don't have access to methods we define on the
// implementation. Since JsonValue is not a winrt type, we can't define any
// methods that operate on it in the idl.
//
// Author(s):
// - Mike Griese - May 2019
#pragma once
#include "AppKeyBindings.h"
class AppKeyBindingsSerialization final
{
public:
static winrt::TerminalApp::AppKeyBindings FromJson(const Json::Value& json);
static Json::Value ToJson(const winrt::TerminalApp::AppKeyBindings& bindings);
};

View File

@@ -4,6 +4,8 @@
#include "pch.h"
#include <argb.h>
#include <conattrs.hpp>
#include <io.h>
#include <fcntl.h>
#include "CascadiaSettings.h"
#include "../../types/inc/utils.hpp"
#include "../../inc/DefaultSettings.h"
@@ -21,6 +23,7 @@ static constexpr GUID TERMINAL_PROFILE_NAMESPACE_GUID =
static constexpr std::wstring_view PACKAGED_PROFILE_ICON_PATH{ L"ms-appx:///ProfileIcons/" };
static constexpr std::wstring_view PACKAGED_PROFILE_ICON_EXTENSION{ L".png" };
static constexpr std::wstring_view DEFAULT_LINUX_ICON_GUID{ L"{9acb9455-ca41-5af7-950f-6bca1bc9722f}" };
CascadiaSettings::CascadiaSettings() :
_globals{},
@@ -227,6 +230,11 @@ void CascadiaSettings::_CreateDefaultProfiles()
_profiles.emplace_back(powershellProfile);
_profiles.emplace_back(cmdProfile);
try
{
_AppendWslProfiles(_profiles);
}
CATCH_LOG()
}
// Method Description:
@@ -467,6 +475,78 @@ bool CascadiaSettings::_isPowerShellCoreInstalledInPath(const std::wstring_view
return false;
}
// Function Description:
// - Adds all of the WSL profiles to the provided container.
// Arguments:
// - A ref to the profiles container where the WSL profiles are to be added
// Return Value:
// - <none>
void CascadiaSettings::_AppendWslProfiles(std::vector<TerminalApp::Profile>& profileStorage)
{
wil::unique_handle readPipe;
wil::unique_handle writePipe;
SECURITY_ATTRIBUTES sa{ sizeof(sa), nullptr, true };
THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&readPipe, &writePipe, &sa, 0));
STARTUPINFO si{};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdOutput = writePipe.get();
si.hStdError = writePipe.get();
wil::unique_process_information pi{};
wil::unique_cotaskmem_string systemPath;
THROW_IF_FAILED(wil::GetSystemDirectoryW(systemPath));
std::wstring command(systemPath.get());
command += L"\\wsl.exe --list";
THROW_IF_WIN32_BOOL_FALSE(CreateProcessW(nullptr, const_cast<LPWSTR>(command.c_str()), nullptr, nullptr,
TRUE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi));
switch (WaitForSingleObject(pi.hProcess, INFINITE))
{
case WAIT_OBJECT_0:
break;
case WAIT_ABANDONED:
case WAIT_TIMEOUT:
THROW_HR(ERROR_CHILD_NOT_COMPLETE);
case WAIT_FAILED:
THROW_LAST_ERROR();
default:
THROW_HR(ERROR_UNHANDLED_EXCEPTION);
}
DWORD exitCode;
if (GetExitCodeProcess(pi.hProcess, &exitCode) == false)
{
THROW_HR(E_INVALIDARG);
}
else if (exitCode != 0)
{
return;
}
DWORD bytesAvailable;
THROW_IF_WIN32_BOOL_FALSE(PeekNamedPipe(readPipe.get(), nullptr, NULL, nullptr, &bytesAvailable, nullptr));
std::wfstream pipe{ _wfdopen(_open_osfhandle((intptr_t)readPipe.get(), _O_WTEXT | _O_RDONLY), L"r") };
//don't worry about the handle returned from wfdOpen, readPipe handle is already managed by wil and closing the file handle will cause an error.
std::wstring wline;
std::getline(pipe, wline); //remove the header from the output.
while (pipe.tellp() < bytesAvailable)
{
std::getline(pipe, wline);
std::wstringstream wlinestream(wline);
if (wlinestream)
{
std::wstring distName;
std::getline(wlinestream, distName, L' ');
auto WSLDistro{ _CreateDefaultProfile(distName) };
WSLDistro.SetCommandline(L"wsl.exe -d " + distName);
WSLDistro.SetColorScheme({ L"Campbell" });
std::wstring iconPath{ PACKAGED_PROFILE_ICON_PATH };
iconPath.append(DEFAULT_LINUX_ICON_GUID);
iconPath.append(PACKAGED_PROFILE_ICON_EXTENSION);
WSLDistro.SetIconPath(iconPath);
profileStorage.emplace_back(WSLDistro);
}
}
}
// Function Description:
// - Get a environment variable string.
// Arguments:

View File

@@ -44,8 +44,8 @@ public:
winrt::TerminalApp::AppKeyBindings GetKeybindings() const noexcept;
winrt::Windows::Data::Json::JsonObject ToJson() const;
static std::unique_ptr<CascadiaSettings> FromJson(winrt::Windows::Data::Json::JsonObject json);
Json::Value ToJson() const;
static std::unique_ptr<CascadiaSettings> FromJson(const Json::Value& json);
static winrt::hstring GetSettingsPath();
@@ -62,14 +62,15 @@ private:
void _CreateDefaultProfiles();
static bool _IsPackaged();
static void _SaveAsPackagedApp(const winrt::hstring content);
static void _SaveAsUnpackagedApp(const winrt::hstring content);
static void _SaveAsPackagedApp(const std::string& content);
static void _SaveAsUnpackagedApp(const std::string& content);
static std::wstring _GetFullPathToUnpackagedSettingsFile();
static winrt::hstring _GetPackagedSettingsPath();
static std::optional<winrt::hstring> _LoadAsPackagedApp();
static std::optional<winrt::hstring> _LoadAsUnpackagedApp();
static std::optional<std::string> _LoadAsPackagedApp();
static std::optional<std::string> _LoadAsUnpackagedApp();
static bool _isPowerShellCoreInstalledInPath(const std::wstring_view programFileEnv, std::filesystem::path& cmdline);
static bool _isPowerShellCoreInstalled(std::filesystem::path& cmdline);
static void _AppendWslProfiles(std::vector<TerminalApp::Profile>& profileStorage);
static std::wstring ExpandEnvironmentVariableString(std::wstring_view source);
static Profile _CreateDefaultProfile(const std::wstring_view name);
};

View File

@@ -4,6 +4,7 @@
#include "pch.h"
#include <argb.h>
#include "CascadiaSettings.h"
#include "AppKeyBindingsSerialization.h"
#include "../../types/inc/utils.hpp"
#include <appmodel.h>
#include <shlobj.h>
@@ -19,9 +20,10 @@ using namespace ::Microsoft::Console;
static constexpr std::wstring_view FILENAME { L"profiles.json" };
static constexpr std::wstring_view SETTINGS_FOLDER_NAME{ L"\\Microsoft\\Windows Terminal\\" };
static constexpr std::wstring_view PROFILES_KEY{ L"profiles" };
static constexpr std::wstring_view KEYBINDINGS_KEY{ L"keybindings" };
static constexpr std::wstring_view SCHEMES_KEY{ L"schemes" };
static constexpr std::string_view ProfilesKey{ "profiles" };
static constexpr std::string_view KeybindingsKey{ "keybindings" };
static constexpr std::string_view GlobalsKey{ "globals" };
static constexpr std::string_view SchemesKey{ "schemes" };
// Method Description:
// - Creates a CascadiaSettings from whatever's saved on disk, or instantiates
@@ -37,26 +39,34 @@ static constexpr std::wstring_view SCHEMES_KEY{ L"schemes" };
std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadAll(const bool saveOnLoad)
{
std::unique_ptr<CascadiaSettings> resultPtr;
std::optional<winrt::hstring> fileData = _IsPackaged() ?
_LoadAsPackagedApp() : _LoadAsUnpackagedApp();
std::optional<std::string> fileData = _IsPackaged() ?
_LoadAsPackagedApp() : _LoadAsUnpackagedApp();
const bool foundFile = fileData.has_value();
if (foundFile)
{
const auto actualData = fileData.value();
// If Parse fails, it'll throw a hresult_error
JsonObject root = JsonObject::Parse(actualData);
// Parse the json data.
Json::Value root;
std::unique_ptr<Json::CharReader> reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() };
std::string errs; // This string will recieve any error text from failing to parse.
// `parse` will return false if it fails.
if (!reader->parse(actualData.c_str(), actualData.c_str() + actualData.size(), &root, &errs))
{
// TODO:GH#990 display this exception text to the user, in a
// copy-pasteable way.
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs));
}
resultPtr = FromJson(root);
// Update profile only if it has changed.
if (saveOnLoad)
{
const JsonObject json = resultPtr->ToJson();
auto serializedSettings = json.Stringify();
if (actualData != serializedSettings)
// Logically compare the json we've parsed from the file to what
// we'd serialize at runtime. If the values are different, then
// write the updated schema back out.
const Json::Value reserialized = resultPtr->ToJson();
if (reserialized != root)
{
resultPtr->SaveAll();
}
@@ -84,16 +94,19 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadAll(const bool saveOnLoa
// - <none>
void CascadiaSettings::SaveAll() const
{
const JsonObject json = ToJson();
auto serializedSettings = json.Stringify();
const auto json = ToJson();
Json::StreamWriterBuilder wbuilder;
// Use 4 spaces to indent instead of \t
wbuilder.settings_["indentation"] = " ";
const auto serializedString = Json::writeString(wbuilder, json);
if (_IsPackaged())
{
_SaveAsPackagedApp(serializedSettings);
_SaveAsPackagedApp(serializedString);
}
else
{
_SaveAsUnpackagedApp(serializedSettings);
_SaveAsUnpackagedApp(serializedString);
}
}
@@ -103,32 +116,28 @@ void CascadiaSettings::SaveAll() const
// - <none>
// Return Value:
// - a JsonObject which is an equivalent serialization of this object.
JsonObject CascadiaSettings::ToJson() const
Json::Value CascadiaSettings::ToJson() const
{
// _globals.ToJson will initialize the settings object will all the global
// settings in the root of the object.
winrt::Windows::Data::Json::JsonObject jsonObject = _globals.ToJson();
Json::Value root;
JsonArray schemesArray{};
Json::Value profilesArray;
for (const auto& profile : _profiles)
{
profilesArray.append(profile.ToJson());
}
Json::Value schemesArray;
const auto& colorSchemes = _globals.GetColorSchemes();
for (auto& scheme : colorSchemes)
{
schemesArray.Append(scheme.ToJson());
schemesArray.append(scheme.ToJson());
}
JsonArray profilesArray{};
for (auto& profile : _profiles)
{
profilesArray.Append(profile.ToJson());
}
root[GlobalsKey.data()] = _globals.ToJson();
root[ProfilesKey.data()] = profilesArray;
root[SchemesKey.data()] = schemesArray;
jsonObject.Insert(PROFILES_KEY, profilesArray);
jsonObject.Insert(SCHEMES_KEY, schemesArray);
jsonObject.Insert(KEYBINDINGS_KEY,
_globals.GetKeybindings().ToJson());
return jsonObject;
return root;
}
// Method Description:
@@ -137,11 +146,33 @@ JsonObject CascadiaSettings::ToJson() const
// - json: an object which should be a serialization of a CascadiaSettings object.
// Return Value:
// - a new CascadiaSettings instance created from the values in `json`
std::unique_ptr<CascadiaSettings> CascadiaSettings::FromJson(JsonObject json)
std::unique_ptr<CascadiaSettings> CascadiaSettings::FromJson(const Json::Value& json)
{
std::unique_ptr<CascadiaSettings> resultPtr = std::make_unique<CascadiaSettings>();
resultPtr->_globals = GlobalAppSettings::FromJson(json);
if (auto globals{ json[GlobalsKey.data()] })
{
if (globals.isObject())
{
resultPtr->_globals = GlobalAppSettings::FromJson(globals);
}
}
else
{
// If there's no globals key in the root object, then try looking at the
// root object for those properties instead, to gracefully upgrade.
// This will attempt to do the legacy keybindings loading too
resultPtr->_globals = GlobalAppSettings::FromJson(json);
// If we didn't find keybindings in the legacy path, then they probably
// don't exist in the file. Create the default keybindings if we
// couldn't find any keybindings.
auto keybindings{ json[KeybindingsKey.data()] };
if (!keybindings)
{
resultPtr->_CreateDefaultKeybindings();
}
}
// TODO:MSFT:20737698 - Display an error if we failed to parse settings
// What should we do here if these keys aren't found?For default profile,
@@ -153,47 +184,30 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::FromJson(JsonObject json)
// Or should we just recreate the default profiles?
auto& resultSchemes = resultPtr->_globals.GetColorSchemes();
if (json.HasKey(SCHEMES_KEY))
if (auto schemes{ json[SchemesKey.data()] })
{
auto schemes = json.GetNamedArray(SCHEMES_KEY);
for (auto schemeJson : schemes)
{
if (schemeJson.ValueType() == JsonValueType::Object)
if (schemeJson.isObject())
{
auto schemeObj = schemeJson.GetObjectW();
auto scheme = ColorScheme::FromJson(schemeObj);
auto scheme = ColorScheme::FromJson(schemeJson);
resultSchemes.emplace_back(std::move(scheme));
}
}
}
if (json.HasKey(PROFILES_KEY))
if (auto profiles{ json[ProfilesKey.data()] })
{
auto profiles = json.GetNamedArray(PROFILES_KEY);
for (auto profileJson : profiles)
{
if (profileJson.ValueType() == JsonValueType::Object)
if (profileJson.isObject())
{
auto profileObj = profileJson.GetObjectW();
auto profile = Profile::FromJson(profileObj);
resultPtr->_profiles.emplace_back(std::move(profile));
auto profile = Profile::FromJson(profileJson);
resultPtr->_profiles.emplace_back(profile);
}
}
}
// Load the keybindings from the file as well
if (json.HasKey(KEYBINDINGS_KEY))
{
const auto keybindingsObj = json.GetNamedArray(KEYBINDINGS_KEY);
auto loadedBindings = AppKeyBindings::FromJson(keybindingsObj);
resultPtr->_globals.SetKeybindings(loadedBindings);
}
else
{
// Create the default keybindings if we couldn't find any keybindings.
resultPtr->_CreateDefaultKeybindings();
}
return resultPtr;
}
@@ -221,7 +235,7 @@ bool CascadiaSettings::_IsPackaged()
// - content: the given string of content to write to the file.
// Return Value:
// - <none>
void CascadiaSettings::_SaveAsPackagedApp(const winrt::hstring content)
void CascadiaSettings::_SaveAsPackagedApp(const std::string& content)
{
auto curr = ApplicationData::Current();
auto folder = curr.RoamingFolder();
@@ -232,11 +246,13 @@ void CascadiaSettings::_SaveAsPackagedApp(const winrt::hstring content)
auto file = file_async.get();
DataWriter dw = DataWriter();
const char* firstChar = content.c_str();
const char* lastChar = firstChar + content.size();
// DataWriter will convert UTF-16 string to UTF-8 (expected settings file encoding)
dw.UnicodeEncoding(UnicodeEncoding::Utf8);
dw.WriteString(content);
const uint8_t* firstByte = reinterpret_cast<const uint8_t*>(firstChar);
const uint8_t* lastByte = reinterpret_cast<const uint8_t*>(lastChar);
winrt::array_view<const uint8_t> bytes{ firstByte, lastByte };
dw.WriteBytes(bytes);
FileIO::WriteBufferAsync(file, dw.DetachBuffer()).get();
}
@@ -250,11 +266,8 @@ void CascadiaSettings::_SaveAsPackagedApp(const winrt::hstring content)
// - <none>
// This can throw an exception if we fail to open the file for writing, or we
// fail to write the file
void CascadiaSettings::_SaveAsUnpackagedApp(const winrt::hstring content)
void CascadiaSettings::_SaveAsUnpackagedApp(const std::string& content)
{
// convert UTF-16 to UTF-8
auto contentString = winrt::to_string(content);
// Get path to output file
// In this scenario, the settings file will end up under e.g. C:\Users\admin\AppData\Roaming\Microsoft\Windows Terminal\profiles.json
std::wstring pathToSettingsFile = CascadiaSettings::_GetFullPathToUnpackagedSettingsFile();
@@ -264,7 +277,7 @@ void CascadiaSettings::_SaveAsUnpackagedApp(const winrt::hstring content)
{
THROW_LAST_ERROR();
}
THROW_LAST_ERROR_IF(!WriteFile(hOut, contentString.c_str(), gsl::narrow<DWORD>(contentString.length()), 0, 0));
THROW_LAST_ERROR_IF(!WriteFile(hOut, content.c_str(), gsl::narrow<DWORD>(content.length()), 0, 0));
CloseHandle(hOut);
}
@@ -306,7 +319,7 @@ std::wstring CascadiaSettings::_GetFullPathToUnpackagedSettingsFile()
// Return Value:
// - an optional with the content of the file if we were able to open it,
// otherwise the optional will be empty
std::optional<winrt::hstring> CascadiaSettings::_LoadAsPackagedApp()
std::optional<std::string> CascadiaSettings::_LoadAsPackagedApp()
{
auto curr = ApplicationData::Current();
auto folder = curr.RoamingFolder();
@@ -320,7 +333,11 @@ std::optional<winrt::hstring> CascadiaSettings::_LoadAsPackagedApp()
const auto storageFile = file.as<StorageFile>();
// settings file is UTF-8 without BOM
return { FileIO::ReadTextAsync(storageFile, UnicodeEncoding::Utf8).get() };
auto buffer = FileIO::ReadBufferAsync(storageFile).get();
auto bufferData = buffer.data();
std::vector<uint8_t> bytes{ bufferData, bufferData + buffer.Length() };
std::string resultString{ bytes.begin(), bytes.end() };
return { resultString };
}
@@ -333,7 +350,7 @@ std::optional<winrt::hstring> CascadiaSettings::_LoadAsPackagedApp()
// otherwise the optional will be empty.
// If the file exists, but we fail to read it, this can throw an exception
// from reading the file
std::optional<winrt::hstring> CascadiaSettings::_LoadAsUnpackagedApp()
std::optional<std::string> CascadiaSettings::_LoadAsUnpackagedApp()
{
std::wstring pathToSettingsFile = CascadiaSettings::_GetFullPathToUnpackagedSettingsFile();
const auto hFile = CreateFileW(pathToSettingsFile.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
@@ -358,10 +375,7 @@ std::optional<winrt::hstring> CascadiaSettings::_LoadAsUnpackagedApp()
// convert buffer to UTF-8 string
std::string utf8string(utf8buffer.get(), fileSize);
// UTF-8 to UTF-16
const winrt::hstring fileData = winrt::to_hstring(utf8string);
return { fileData };
return { utf8string };
}
// function Description:

View File

@@ -4,6 +4,7 @@
#include "pch.h"
#include "ColorScheme.h"
#include "../../types/inc/Utils.hpp"
#include "Utils.h"
using namespace TerminalApp;
using namespace ::Microsoft::Console;
@@ -12,28 +13,28 @@ using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace winrt::TerminalApp;
using namespace winrt::Windows::Data::Json;
static constexpr std::wstring_view NAME_KEY{ L"name" };
static constexpr std::wstring_view TABLE_KEY{ L"colors" };
static constexpr std::wstring_view FOREGROUND_KEY{ L"foreground" };
static constexpr std::wstring_view BACKGROUND_KEY{ L"background" };
static const std::array<std::wstring, 16> TABLE_COLORS =
static constexpr std::string_view NameKey{ "name" };
static constexpr std::string_view TableKey{ "colors" };
static constexpr std::string_view ForegroundKey{ "foreground" };
static constexpr std::string_view BackgroundKey{ "background" };
static constexpr std::array<std::string_view, 16> TableColors =
{
L"black",
L"red",
L"green",
L"yellow",
L"blue",
L"purple",
L"cyan",
L"white",
L"brightBlack",
L"brightRed",
L"brightGreen",
L"brightYellow",
L"brightBlue",
L"brightPurple",
L"brightCyan",
L"brightWhite"
"black",
"red",
"green",
"yellow",
"blue",
"purple",
"cyan",
"white",
"brightBlack",
"brightRed",
"brightGreen",
"brightYellow",
"brightBlue",
"brightPurple",
"brightCyan",
"brightWhite"
};
ColorScheme::ColorScheme() :
@@ -83,29 +84,22 @@ void ColorScheme::ApplyScheme(TerminalSettings terminalSettings) const
// - <none>
// Return Value:
// - a JsonObject which is an equivalent serialization of this object.
JsonObject ColorScheme::ToJson() const
Json::Value ColorScheme::ToJson() const
{
winrt::Windows::Data::Json::JsonObject jsonObject;
Json::Value root;
root[JsonKey(NameKey)] = winrt::to_string(_schemeName);
root[JsonKey(ForegroundKey)] = Utils::ColorToHexString(_defaultForeground);
root[JsonKey(BackgroundKey)] = Utils::ColorToHexString(_defaultBackground);
auto fg = JsonValue::CreateStringValue(Utils::ColorToHexString(_defaultForeground));
auto bg = JsonValue::CreateStringValue(Utils::ColorToHexString(_defaultBackground));
auto name = JsonValue::CreateStringValue(_schemeName);
jsonObject.Insert(NAME_KEY, name);
jsonObject.Insert(FOREGROUND_KEY, fg);
jsonObject.Insert(BACKGROUND_KEY, bg);
int i = 0;
for (const auto& current : TABLE_COLORS)
for (const auto& colorName : TableColors)
{
auto& color = _table.at(i);
auto s = JsonValue::CreateStringValue(Utils::ColorToHexString(color));
jsonObject.Insert(current, s);
auto& colorValue = _table.at(i);
root[JsonKey(colorName)] = Utils::ColorToHexString(colorValue);
i++;
}
return jsonObject;
return root;
}
// Method Description:
@@ -114,39 +108,35 @@ JsonObject ColorScheme::ToJson() const
// - json: an object which should be a serialization of a ColorScheme object.
// Return Value:
// - a new ColorScheme instance created from the values in `json`
ColorScheme ColorScheme::FromJson(winrt::Windows::Data::Json::JsonObject json)
ColorScheme ColorScheme::FromJson(const Json::Value& json)
{
ColorScheme result{};
if (json.HasKey(NAME_KEY))
if (auto name{ json[JsonKey(NameKey)] })
{
result._schemeName = json.GetNamedString(NAME_KEY);
result._schemeName = winrt::to_hstring(name.asString());
}
if (json.HasKey(FOREGROUND_KEY))
if (auto fgString{ json[JsonKey(ForegroundKey)] })
{
const auto fgString = json.GetNamedString(FOREGROUND_KEY);
const auto color = Utils::ColorFromHexString(fgString.c_str());
const auto color = Utils::ColorFromHexString(fgString.asString());
result._defaultForeground = color;
}
if (json.HasKey(BACKGROUND_KEY))
if (auto bgString{ json[JsonKey(BackgroundKey)] })
{
const auto bgString = json.GetNamedString(BACKGROUND_KEY);
const auto color = Utils::ColorFromHexString(bgString.c_str());
const auto color = Utils::ColorFromHexString(bgString.asString());
result._defaultBackground = color;
}
// Legacy Deserialization. Leave in place to allow forward compatibility
if (json.HasKey(TABLE_KEY))
if (auto table{ json[JsonKey(TableKey)] })
{
const auto table = json.GetNamedArray(TABLE_KEY);
int i = 0;
for (auto v : table)
for (const auto& tableEntry : table)
{
if (v.ValueType() == JsonValueType::String)
if (tableEntry.isString())
{
auto str = v.GetString();
auto color = Utils::ColorFromHexString(str.c_str());
auto color = Utils::ColorFromHexString(tableEntry.asString());
result._table.at(i) = color;
}
i++;
@@ -154,12 +144,11 @@ ColorScheme ColorScheme::FromJson(winrt::Windows::Data::Json::JsonObject json)
}
int i = 0;
for (const auto& current : TABLE_COLORS)
for (const auto& current : TableColors)
{
if (json.HasKey(current))
if (auto str{ json[JsonKey(current)] })
{
const auto str = json.GetNamedString(current);
const auto color = Utils::ColorFromHexString(str.c_str());
const auto color = Utils::ColorFromHexString(str.asString());
result._table.at(i) = color;
}
i++;
@@ -168,6 +157,7 @@ ColorScheme ColorScheme::FromJson(winrt::Windows::Data::Json::JsonObject json)
return result;
}
std::wstring_view ColorScheme::GetName() const noexcept
{
return { _schemeName };

View File

@@ -19,7 +19,6 @@ Author(s):
#include <winrt/Microsoft.Terminal.TerminalControl.h>
#include <winrt/TerminalApp.h>
#include "../../inc/conattrs.hpp"
#include <conattrs.hpp>
namespace TerminalApp
{
@@ -36,8 +35,8 @@ public:
void ApplyScheme(winrt::Microsoft::Terminal::Settings::TerminalSettings terminalSettings) const;
winrt::Windows::Data::Json::JsonObject ToJson() const;
static ColorScheme FromJson(winrt::Windows::Data::Json::JsonObject json);
Json::Value ToJson() const;
static ColorScheme FromJson(const Json::Value& json);
std::wstring_view GetName() const noexcept;
std::array<COLORREF, COLOR_TABLE_SIZE>& GetTable() noexcept;

View File

@@ -5,6 +5,8 @@
#include "GlobalAppSettings.h"
#include "../../types/inc/Utils.hpp"
#include "../../inc/DefaultSettings.h"
#include "AppKeyBindingsSerialization.h"
#include "Utils.h"
using namespace TerminalApp;
using namespace winrt::Microsoft::Terminal::Settings;
@@ -13,17 +15,18 @@ using namespace winrt::Windows::Data::Json;
using namespace winrt::Windows::UI::Xaml;
using namespace ::Microsoft::Console;
static constexpr std::wstring_view DEFAULTPROFILE_KEY{ L"defaultProfile" };
static constexpr std::wstring_view ALWAYS_SHOW_TABS_KEY{ L"alwaysShowTabs" };
static constexpr std::wstring_view INITIALROWS_KEY{ L"initialRows" };
static constexpr std::wstring_view INITIALCOLS_KEY{ L"initialCols" };
static constexpr std::wstring_view SHOW_TITLE_IN_TITLEBAR_KEY{ L"showTerminalTitleInTitlebar" };
static constexpr std::wstring_view REQUESTED_THEME_KEY{ L"requestedTheme" };
static constexpr std::wstring_view SHOW_TABS_IN_TITLEBAR_KEY{ L"showTabsInTitlebar" };
static constexpr std::string_view KeybindingsKey{ "keybindings" };
static constexpr std::string_view DefaultProfileKey{ "defaultProfile" };
static constexpr std::string_view AlwaysShowTabsKey{ "alwaysShowTabs" };
static constexpr std::string_view InitialRowsKey{ "initialRows" };
static constexpr std::string_view InitialColsKey{ "initialCols" };
static constexpr std::string_view ShowTitleInTitlebarKey{ "showTerminalTitleInTitlebar" };
static constexpr std::string_view RequestedThemeKey{ "requestedTheme" };
static constexpr std::string_view ShowTabsInTitlebarKey{ "showTabsInTitlebar" };
static constexpr std::wstring_view LIGHT_THEME_VALUE{ L"light" };
static constexpr std::wstring_view DARK_THEME_VALUE{ L"dark" };
static constexpr std::wstring_view SYSTEM_THEME_VALUE{ L"system" };
static constexpr std::wstring_view LightThemeValue{ L"light" };
static constexpr std::wstring_view DarkThemeValue{ L"dark" };
static constexpr std::wstring_view SystemThemeValue{ L"system" };
GlobalAppSettings::GlobalAppSettings() :
_keybindings{},
@@ -136,30 +139,18 @@ void GlobalAppSettings::ApplyToSettings(TerminalSettings& settings) const noexce
// - <none>
// Return Value:
// - a JsonObject which is an equivalent serialization of this object.
JsonObject GlobalAppSettings::ToJson() const
Json::Value GlobalAppSettings::ToJson() const
{
winrt::Windows::Data::Json::JsonObject jsonObject;
Json::Value jsonObject;
const auto guidStr = Utils::GuidToString(_defaultProfile);
const auto defaultProfile = JsonValue::CreateStringValue(guidStr);
const auto initialRows = JsonValue::CreateNumberValue(_initialRows);
const auto initialCols = JsonValue::CreateNumberValue(_initialCols);
jsonObject.Insert(DEFAULTPROFILE_KEY, defaultProfile);
jsonObject.Insert(INITIALROWS_KEY, initialRows);
jsonObject.Insert(INITIALCOLS_KEY, initialCols);
jsonObject.Insert(ALWAYS_SHOW_TABS_KEY,
JsonValue::CreateBooleanValue(_alwaysShowTabs));
jsonObject.Insert(SHOW_TITLE_IN_TITLEBAR_KEY,
JsonValue::CreateBooleanValue(_showTitleInTitlebar));
jsonObject.Insert(SHOW_TABS_IN_TITLEBAR_KEY,
JsonValue::CreateBooleanValue(_showTabsInTitlebar));
jsonObject.Insert(REQUESTED_THEME_KEY,
JsonValue::CreateStringValue(_SerializeTheme(_requestedTheme)));
// We'll add the keybindings later in CascadiaSettings, because if we do it
// here, they'll appear before the profiles.
jsonObject[JsonKey(DefaultProfileKey)] = winrt::to_string(Utils::GuidToString(_defaultProfile));
jsonObject[JsonKey(InitialRowsKey)] = _initialRows;
jsonObject[JsonKey(InitialColsKey)] = _initialCols;
jsonObject[JsonKey(AlwaysShowTabsKey)] = _alwaysShowTabs;
jsonObject[JsonKey(ShowTitleInTitlebarKey)] = _showTitleInTitlebar;
jsonObject[JsonKey(ShowTabsInTitlebarKey)] = _showTabsInTitlebar;
jsonObject[JsonKey(RequestedThemeKey)] = winrt::to_string(_SerializeTheme(_requestedTheme));
jsonObject[JsonKey(KeybindingsKey)] = AppKeyBindingsSerialization::ToJson(_keybindings);
return jsonObject;
}
@@ -170,49 +161,53 @@ JsonObject GlobalAppSettings::ToJson() const
// - json: an object which should be a serialization of a GlobalAppSettings object.
// Return Value:
// - a new GlobalAppSettings instance created from the values in `json`
GlobalAppSettings GlobalAppSettings::FromJson(winrt::Windows::Data::Json::JsonObject json)
GlobalAppSettings GlobalAppSettings::FromJson(const Json::Value& json)
{
GlobalAppSettings result{};
if (json.HasKey(DEFAULTPROFILE_KEY))
if (auto defaultProfile{ json[JsonKey(DefaultProfileKey)] })
{
auto guidString = json.GetNamedString(DEFAULTPROFILE_KEY);
auto guid = Utils::GuidFromString(guidString.c_str());
auto guid = Utils::GuidFromString(GetWstringFromJson(defaultProfile));
result._defaultProfile = guid;
}
if (json.HasKey(ALWAYS_SHOW_TABS_KEY))
if (auto alwaysShowTabs{ json[JsonKey(AlwaysShowTabsKey)] })
{
result._alwaysShowTabs = json.GetNamedBoolean(ALWAYS_SHOW_TABS_KEY);
result._alwaysShowTabs = alwaysShowTabs.asBool();
}
if (json.HasKey(INITIALROWS_KEY))
if (auto initialRows{ json[JsonKey(InitialRowsKey)] })
{
result._initialRows = static_cast<int32_t>(json.GetNamedNumber(INITIALROWS_KEY));
result._initialRows = initialRows.asInt();
}
if (json.HasKey(INITIALCOLS_KEY))
if (auto initialCols{ json[JsonKey(InitialColsKey)] })
{
result._initialCols = static_cast<int32_t>(json.GetNamedNumber(INITIALCOLS_KEY));
result._initialCols = initialCols.asInt();
}
if (json.HasKey(SHOW_TITLE_IN_TITLEBAR_KEY))
if (auto showTitleInTitlebar{ json[JsonKey(ShowTitleInTitlebarKey)] })
{
result._showTitleInTitlebar = json.GetNamedBoolean(SHOW_TITLE_IN_TITLEBAR_KEY);
result._showTitleInTitlebar = showTitleInTitlebar.asBool();
}
if (json.HasKey(SHOW_TABS_IN_TITLEBAR_KEY))
if (auto showTabsInTitlebar{ json[JsonKey(ShowTabsInTitlebarKey)] })
{
result._showTabsInTitlebar = json.GetNamedBoolean(SHOW_TABS_IN_TITLEBAR_KEY);
result._showTabsInTitlebar = showTabsInTitlebar.asBool();
}
if (json.HasKey(REQUESTED_THEME_KEY))
if (auto requestedTheme{ json[JsonKey(RequestedThemeKey)] })
{
const auto themeStr = json.GetNamedString(REQUESTED_THEME_KEY);
result._requestedTheme = _ParseTheme(themeStr.c_str());
result._requestedTheme = _ParseTheme(GetWstringFromJson(requestedTheme));
}
if (auto keybindings{ json[JsonKey(KeybindingsKey)] })
{
result._keybindings = AppKeyBindingsSerialization::FromJson(keybindings);
}
return result;
}
// Method Description:
// - Helper function for converting a user-specified cursor style corresponding
// CursorStyle enum value
@@ -222,15 +217,15 @@ GlobalAppSettings GlobalAppSettings::FromJson(winrt::Windows::Data::Json::JsonOb
// - The corresponding enum value which maps to the string provided by the user
ElementTheme GlobalAppSettings::_ParseTheme(const std::wstring& themeString) noexcept
{
if (themeString == LIGHT_THEME_VALUE)
if (themeString == LightThemeValue)
{
return ElementTheme::Light;
}
else if (themeString == DARK_THEME_VALUE)
else if (themeString == DarkThemeValue)
{
return ElementTheme::Dark;
}
// default behavior for invalid data or SYSTEM_THEME_VALUE
// default behavior for invalid data or SystemThemeValue
return ElementTheme::Default;
}
@@ -246,10 +241,10 @@ std::wstring_view GlobalAppSettings::_SerializeTheme(const ElementTheme theme) n
switch (theme)
{
case ElementTheme::Light:
return LIGHT_THEME_VALUE;
return LightThemeValue;
case ElementTheme::Dark:
return DARK_THEME_VALUE;
return DarkThemeValue;
default:
return SYSTEM_THEME_VALUE;
return SystemThemeValue;
}
}

View File

@@ -50,8 +50,8 @@ public:
winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme() const noexcept;
winrt::Windows::Data::Json::JsonObject ToJson() const;
static GlobalAppSettings FromJson(winrt::Windows::Data::Json::JsonObject json);
Json::Value ToJson() const;
static GlobalAppSettings FromJson(const Json::Value& json);
void ApplyToSettings(winrt::Microsoft::Terminal::Settings::TerminalSettings& settings) const noexcept;

View File

@@ -0,0 +1,586 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "Pane.h"
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::TerminalControl;
static const int PaneSeparatorSize = 4;
Pane::Pane(const GUID& profile, const TermControl& control, const bool lastFocused) :
_control{ control },
_lastFocused{ lastFocused },
_profile{ profile }
{
_root.Children().Append(_control.GetControl());
_connectionClosedToken = _control.ConnectionClosed({ this, &Pane::_ControlClosedHandler });
// Set the background of the pane to match that of the theme's default grid
// background. This way, we'll match the small underline under the tabs, and
// the UI will be consistent on bot light and dark modes.
const auto res = Application::Current().Resources();
const auto key = winrt::box_value(L"BackgroundGridThemeStyle");
if (res.HasKey(key))
{
const auto g = res.Lookup(key);
const auto style = g.try_as<winrt::Windows::UI::Xaml::Style>();
// try_as fails by returning nullptr
if (style)
{
_root.Style(style);
}
}
}
// Method Description:
// - Called when our attached control is closed. Triggers listeners to our close
// event, if we're a leaf pane.
// - If this was called, and we became a parent pane (due to work on another
// thread), this function will do nothing (allowing the control's new parent
// to handle the event instead).
// Arguments:
// - <none>
// Return Value:
// - <none>
void Pane::_ControlClosedHandler()
{
std::unique_lock lock{ _createCloseLock };
// It's possible that this event handler started being executed, then before
// we got the lock, another thread created another child. So our control is
// actually no longer _our_ control, and instead could be a descendant.
//
// When the control's new Pane takes ownership of the control, the new
// parent will register it's own event handler. That event handler will get
// fired after this handler returns, and will properly cleanup state.
if (!_IsLeaf())
{
return;
}
if (_control.ShouldCloseOnExit())
{
// Fire our Closed event to tell our parent that we should be removed.
_closedHandlers();
}
}
// Method Description:
// - Get the root UIElement of this pane. There may be a single TermControl as a
// child, or an entire tree of grids and panes as children of this element.
// Arguments:
// - <none>
// Return Value:
// - the Grid acting as the root of this pane.
Controls::Grid Pane::GetRootElement()
{
return _root;
}
// Method Description:
// - If this is the last focused pane, returns itself. Returns nullptr if this
// is a leaf and it's not focused. If it's a parent, it returns nullptr if no
// children of this pane were the last pane to be focused, or the Pane that
// _was_ the last pane to be focused (if there was one).
// - This Pane's control might not currently be focused, if the tab itself is
// not currently focused.
// Return Value:
// - nullptr if we're a leaf and unfocused, or no children were marked
// `_lastFocused`, else returns this
std::shared_ptr<Pane> Pane::GetFocusedPane()
{
if (_IsLeaf())
{
return _lastFocused ? shared_from_this() : nullptr;
}
else
{
auto firstFocused = _firstChild->GetFocusedPane();
if (firstFocused != nullptr)
{
return firstFocused;
}
return _secondChild->GetFocusedPane();
}
}
// Method Description:
// - Returns nullptr if no children of this pane were the last control to be
// focused, or the TermControl that _was_ the last control to be focused (if
// there was one).
// - This control might not currently be focused, if the tab itself is not
// currently focused.
// Arguments:
// - <none>
// Return Value:
// - nullptr if no children were marked `_lastFocused`, else the TermControl
// that was last focused.
TermControl Pane::GetFocusedTerminalControl()
{
auto lastFocused = GetFocusedPane();
return lastFocused ? lastFocused->_control : nullptr;
}
// Method Description:
// - Returns nullopt if no children of this pane were the last control to be
// focused, or the GUID of the profile of the last control to be focused (if
// there was one).
// Arguments:
// - <none>
// Return Value:
// - nullopt if no children of this pane were the last control to be
// focused, else the GUID of the profile of the last control to be focused
std::optional<GUID> Pane::GetFocusedProfile()
{
auto lastFocused = GetFocusedPane();
return lastFocused ? lastFocused->_profile : std::nullopt;
}
// Method Description:
// - Returns true if this pane was the last pane to be focused in a tree of panes.
// Arguments:
// - <none>
// Return Value:
// - true iff we were the last pane focused in this tree of panes.
bool Pane::WasLastFocused() const noexcept
{
return _lastFocused;
}
// Method Description:
// - Returns true iff this pane has no child panes.
// Arguments:
// - <none>
// Return Value:
// - true iff this pane has no child panes.
bool Pane::_IsLeaf() const noexcept
{
return _splitState == SplitState::None;
}
// Method Description:
// - Returns true if this pane is currently focused, or there is a pane which is
// a child of this pane that is actively focused
// Arguments:
// - <none>
// Return Value:
// - true if the currently focused pane is either this pane, or one of this
// pane's descendants
bool Pane::_HasFocusedChild() const noexcept
{
// We're intentionally making this one giant expression, so the compiler
// will skip the following lookups if one of the lookups before it returns
// true
return (_control && _control.GetControl().FocusState() != FocusState::Unfocused) ||
(_firstChild && _firstChild->_HasFocusedChild()) ||
(_secondChild && _secondChild->_HasFocusedChild());
}
// Method Description:
// - Update the focus state of this pane, and all its descendants.
// * If this is a leaf node, and our control is actively focused, we'll mark
// ourselves as the _lastFocused.
// * If we're not a leaf, we'll recurse on our children to check them.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Pane::UpdateFocus()
{
if (_IsLeaf())
{
const auto controlFocused = _control &&
_control.GetControl().FocusState() != FocusState::Unfocused;
_lastFocused = controlFocused;
}
else
{
_lastFocused = false;
_firstChild->UpdateFocus();
_secondChild->UpdateFocus();
}
}
// Method Description:
// - Focuses this control if we're a leaf, or attempts to focus the first leaf
// of our first child, recursively.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Pane::_FocusFirstChild()
{
if (_IsLeaf())
{
_control.GetControl().Focus(FocusState::Programmatic);
}
else
{
_firstChild->_FocusFirstChild();
}
}
// Method Description:
// - Attempts to update the settings of this pane or any children of this pane.
// * If this pane is a leaf, and our profile guid matches the parameter, then
// we'll apply the new settings to our control.
// * If we're not a leaf, we'll recurse on our children.
// Arguments:
// - settings: The new TerminalSettings to apply to any matching controls
// - profile: The GUID of the profile these settings should apply to.
// Return Value:
// - <none>
void Pane::UpdateSettings(const TerminalSettings& settings, const GUID& profile)
{
if (!_IsLeaf())
{
_firstChild->UpdateSettings(settings, profile);
_secondChild->UpdateSettings(settings, profile);
}
else
{
if (profile == _profile)
{
_control.UpdateSettings(settings);
}
}
}
// Method Description:
// - Closes one of our children. In doing so, takes the control from the other
// child, and makes this pane a leaf node again.
// Arguments:
// - closeFirst: if true, the first child should be closed, and the second
// should be preserved, and vice-versa for false.
// Return Value:
// - <none>
void Pane::_CloseChild(const bool closeFirst)
{
// Lock the create/close lock so that another operation won't concurrently
// modify our tree
std::unique_lock lock{ _createCloseLock };
// If we're a leaf, then chances are both our children closed in close
// succession. We waited on the lock while the other child was closed, so
// now we don't have a child to close anymore. Return here. When we moved
// the non-closed child into us, we also set up event handlers that will be
// triggered when we return from this.
if (_IsLeaf())
{
return;
}
auto closedChild = closeFirst ? _firstChild : _secondChild;
auto remainingChild = closeFirst ? _secondChild : _firstChild;
// If the only child left is a leaf, that means we're a leaf now.
if (remainingChild->_IsLeaf())
{
// take the control and profile of the pane that _wasn't_ closed.
_control = remainingChild->_control;
_profile = remainingChild->_profile;
// Add our new event handler before revoking the old one.
_connectionClosedToken = _control.ConnectionClosed({ this, &Pane::_ControlClosedHandler });
// Revoke the old event handlers. Remove both the handlers for the panes
// themselves closing, and remove their handlers for their controls
// closing. At this point, if the remaining child's control is closed,
// they'll trigger only our event handler for the control's close.
_firstChild->Closed(_firstClosedToken);
_secondChild->Closed(_secondClosedToken);
closedChild->_control.ConnectionClosed(closedChild->_connectionClosedToken);
remainingChild->_control.ConnectionClosed(remainingChild->_connectionClosedToken);
// If either of our children was focused, we want to take that focus from
// them.
_lastFocused = _firstChild->_lastFocused || _secondChild->_lastFocused;
// Remove all the ui elements of our children. This'll make sure we can
// re-attach the TermControl to our Grid.
_firstChild->_root.Children().Clear();
_secondChild->_root.Children().Clear();
// Reset our UI:
_root.Children().Clear();
_root.ColumnDefinitions().Clear();
_root.RowDefinitions().Clear();
_separatorRoot = { nullptr };
// Reattach the TermControl to our grid.
_root.Children().Append(_control.GetControl());
if (_lastFocused)
{
_control.GetControl().Focus(FocusState::Programmatic);
}
_splitState = SplitState::None;
// Release our children.
_firstChild = nullptr;
_secondChild = nullptr;
}
else
{
// First stash away references to the old panes and their tokens
const auto oldFirstToken = _firstClosedToken;
const auto oldSecondToken = _secondClosedToken;
const auto oldFirst = _firstChild;
const auto oldSecond = _secondClosedToken;
// Steal all the state from our child
_splitState = remainingChild->_splitState;
_separatorRoot = remainingChild->_separatorRoot;
_firstChild = remainingChild->_firstChild;
_secondChild = remainingChild->_secondChild;
// Set up new close handlers on the children
_SetupChildCloseHandlers();
// Revoke the old event handlers.
_firstChild->Closed(_firstClosedToken);
_secondChild->Closed(_secondClosedToken);
// Reset our UI:
_root.Children().Clear();
_root.ColumnDefinitions().Clear();
_root.RowDefinitions().Clear();
// Copy the old UI over to our grid.
// Start by copying the row/column definitions. Iterate over the
// rows/cols, and remove each one from the old grid, and attach it to
// our grid instead.
while (remainingChild->_root.ColumnDefinitions().Size() > 0)
{
auto col = remainingChild->_root.ColumnDefinitions().GetAt(0);
remainingChild->_root.ColumnDefinitions().RemoveAt(0);
_root.ColumnDefinitions().Append(col);
}
while (remainingChild->_root.RowDefinitions().Size() > 0)
{
auto row = remainingChild->_root.RowDefinitions().GetAt(0);
remainingChild->_root.RowDefinitions().RemoveAt(0);
_root.RowDefinitions().Append(row);
}
// Remove the child's UI elements from the child's grid, so we can
// attach them to us instead.
remainingChild->_root.Children().Clear();
_root.Children().Append(_firstChild->GetRootElement());
_root.Children().Append(_separatorRoot);
_root.Children().Append(_secondChild->GetRootElement());
// If the closed child was focused, transfer the focus to it's first sibling.
if (closedChild->_lastFocused)
{
_FocusFirstChild();
}
// Release the pointers that the child was holding.
remainingChild->_firstChild = nullptr;
remainingChild->_secondChild = nullptr;
remainingChild->_separatorRoot = { nullptr };
}
}
// Method Description:
// - Adds event handlers to our children to handle their close events.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Pane::_SetupChildCloseHandlers()
{
_firstClosedToken = _firstChild->Closed([this](){
_root.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [=](){
_CloseChild(true);
});
});
_secondClosedToken = _secondChild->Closed([this](){
_root.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [=](){
_CloseChild(false);
});
});
}
// Method Description:
// - Initializes our UI for a new split in this pane. Sets up row/column
// definitions, and initializes the separator grid. Does nothing if our split
// state is currently set to SplitState::None
// Arguments:
// - <none>
// Return Value:
// - <none>
void Pane::_CreateSplitContent()
{
if (_splitState == SplitState::Vertical)
{
// Create three columns in this grid: one for each pane, and one for the separator.
auto separatorColDef = Controls::ColumnDefinition();
separatorColDef.Width(GridLengthHelper::Auto());
_root.ColumnDefinitions().Append(Controls::ColumnDefinition{});
_root.ColumnDefinitions().Append(separatorColDef);
_root.ColumnDefinitions().Append(Controls::ColumnDefinition{});
// Create the pane separator
_separatorRoot = Controls::Grid{};
_separatorRoot.Width(PaneSeparatorSize);
// NaN is the special value XAML uses for "Auto" sizing.
_separatorRoot.Height(NAN);
}
else if (_splitState == SplitState::Horizontal)
{
// Create three rows in this grid: one for each pane, and one for the separator.
auto separatorRowDef = Controls::RowDefinition();
separatorRowDef.Height(GridLengthHelper::Auto());
_root.RowDefinitions().Append(Controls::RowDefinition{});
_root.RowDefinitions().Append(separatorRowDef);
_root.RowDefinitions().Append(Controls::RowDefinition{});
// Create the pane separator
_separatorRoot = Controls::Grid{};
_separatorRoot.Height(PaneSeparatorSize);
// NaN is the special value XAML uses for "Auto" sizing.
_separatorRoot.Width(NAN);
}
}
// Method Description:
// - Sets the row/column of our child UI elements, to match our current split type.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Pane::_ApplySplitDefinitions()
{
if (_splitState == SplitState::Vertical)
{
Controls::Grid::SetColumn(_firstChild->GetRootElement(), 0);
Controls::Grid::SetColumn(_separatorRoot, 1);
Controls::Grid::SetColumn(_secondChild->GetRootElement(), 2);
}
else if (_splitState == SplitState::Horizontal)
{
Controls::Grid::SetRow(_firstChild->GetRootElement(), 0);
Controls::Grid::SetRow(_separatorRoot, 1);
Controls::Grid::SetRow(_secondChild->GetRootElement(), 2);
}
}
// Method Description:
// - Vertically split the focused pane in our tree of panes, and place the given
// TermControl into the newly created pane. If we're the focused pane, then
// we'll create two new children, and place them side-by-side in our Grid.
// Arguments:
// - profile: The profile GUID to associate with the newly created pane.
// - control: A TermControl to use in the new pane.
// Return Value:
// - <none>
void Pane::SplitVertical(const GUID& profile, const TermControl& control)
{
// If we're not the leaf, recurse into our children to split them.
if (!_IsLeaf())
{
if (_firstChild->_HasFocusedChild())
{
_firstChild->SplitVertical(profile, control);
}
else if (_secondChild->_HasFocusedChild())
{
_secondChild->SplitVertical(profile, control);
}
return;
}
_DoSplit(SplitState::Vertical, profile, control);
}
// Method Description:
// - Horizontally split the focused pane in our tree of panes, and place the given
// TermControl into the newly created pane. If we're the focused pane, then
// we'll create two new children, and place them side-by-side in our Grid.
// Arguments:
// - profile: The profile GUID to associate with the newly created pane.
// - control: A TermControl to use in the new pane.
// Return Value:
// - <none>
void Pane::SplitHorizontal(const GUID& profile, const TermControl& control)
{
if (!_IsLeaf())
{
if (_firstChild->_HasFocusedChild())
{
_firstChild->SplitHorizontal(profile, control);
}
else if (_secondChild->_HasFocusedChild())
{
_secondChild->SplitHorizontal(profile, control);
}
return;
}
_DoSplit(SplitState::Horizontal, profile, control);
}
// Method Description:
// - Does the bulk of the work of creating a new split. Initializes our UI,
// creates a new Pane to host the control, registers event handlers.
// Arguments:
// - splitType: what type of split we should create.
// - profile: The profile GUID to associate with the newly created pane.
// - control: A TermControl to use in the new pane.
// Return Value:
// - <none>
void Pane::_DoSplit(SplitState splitType, const GUID& profile, const TermControl& control)
{
// Lock the create/close lock so that another operation won't concurrently
// modify our tree
std::unique_lock lock{ _createCloseLock };
// revoke our handler - the child will take care of the control now.
_control.ConnectionClosed(_connectionClosedToken);
_connectionClosedToken.value = 0;
_splitState = splitType;
_CreateSplitContent();
// Remove any children we currently have. We can't add the existing
// TermControl to a new grid until we do this.
_root.Children().Clear();
// Create two new Panes
// Move our control, guid into the first one.
// Move the new guid, control into the second.
_firstChild = std::make_shared<Pane>(_profile.value(), _control);
_profile = std::nullopt;
_control = { nullptr };
_secondChild = std::make_shared<Pane>(profile, control);
_root.Children().Append(_firstChild->GetRootElement());
_root.Children().Append(_separatorRoot);
_root.Children().Append(_secondChild->GetRootElement());
_ApplySplitDefinitions();
// Register event handlers on our children to handle their Close events
_SetupChildCloseHandlers();
_lastFocused = false;
}
DEFINE_EVENT(Pane, Closed, _closedHandlers, ConnectionClosedEventArgs);

View File

@@ -0,0 +1,85 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
// Module Name:
// - Pane.h
//
// Abstract:
// - Panes are an abstraction by which the terminal can dislay multiple terminal
// instances simultaneously in a single terminal window. While tabs allow for
// a single terminal window to have many terminal sessions running
// simultaneously within a single window, only one tab can be visible at a
// time. Panes, on the other hand, allow a user to have many different
// terminal sessions visible to the user within the context of a single window
// at the same time. This can enable greater productivity from the user, as
// they can see the output of one terminal window while working in another.
// - See doc/cascadia/Panes.md for a detailed description.
//
// Author:
// - Mike Griese (zadjii-msft) 16-May-2019
#pragma once
#include <winrt/Microsoft.Terminal.TerminalControl.h>
#include "../../cascadia/inc/cppwinrt_utils.h"
class Pane : public std::enable_shared_from_this<Pane>
{
public:
enum class SplitState : int
{
None = 0,
Vertical = 1,
Horizontal = 2
};
Pane(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control, const bool lastFocused = false);
std::shared_ptr<Pane> GetFocusedPane();
winrt::Microsoft::Terminal::TerminalControl::TermControl GetFocusedTerminalControl();
std::optional<GUID> GetFocusedProfile();
winrt::Windows::UI::Xaml::Controls::Grid GetRootElement();
bool WasLastFocused() const noexcept;
void UpdateFocus();
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::TerminalSettings& settings, const GUID& profile);
void SplitHorizontal(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
void SplitVertical(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
DECLARE_EVENT(Closed, _closedHandlers, winrt::Microsoft::Terminal::TerminalControl::ConnectionClosedEventArgs);
private:
winrt::Windows::UI::Xaml::Controls::Grid _root{};
winrt::Windows::UI::Xaml::Controls::Grid _separatorRoot{ nullptr };
winrt::Microsoft::Terminal::TerminalControl::TermControl _control{ nullptr };
std::shared_ptr<Pane> _firstChild{ nullptr };
std::shared_ptr<Pane> _secondChild{ nullptr };
SplitState _splitState{ SplitState::None };
bool _lastFocused{ false };
std::optional<GUID> _profile{ std::nullopt };
winrt::event_token _connectionClosedToken{ 0 };
winrt::event_token _firstClosedToken{ 0 };
winrt::event_token _secondClosedToken{ 0 };
std::shared_mutex _createCloseLock{};
bool _IsLeaf() const noexcept;
bool _HasFocusedChild() const noexcept;
void _SetupChildCloseHandlers();
void _DoSplit(SplitState splitType, const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
void _CreateSplitContent();
void _ApplySplitDefinitions();
void _CloseChild(const bool closeFirst);
void _FocusFirstChild();
void _ControlClosedHandler();
};

View File

@@ -3,6 +3,7 @@
#include "pch.h"
#include "Profile.h"
#include "Utils.h"
#include "../../types/inc/Utils.hpp"
#include <DefaultSettings.h>
@@ -12,50 +13,50 @@ using namespace winrt::TerminalApp;
using namespace winrt::Windows::Data::Json;
using namespace ::Microsoft::Console;
static constexpr std::wstring_view NAME_KEY{ L"name" };
static constexpr std::wstring_view GUID_KEY{ L"guid" };
static constexpr std::wstring_view COLORSCHEME_KEY{ L"colorScheme" };
static constexpr std::wstring_view COLORSCHEME_KEY_OLD{ L"colorscheme" };
static constexpr std::string_view NameKey{ "name" };
static constexpr std::string_view GuidKey{ "guid" };
static constexpr std::string_view ColorSchemeKey{ "colorScheme" };
static constexpr std::string_view ColorSchemeKeyOld{ "colorscheme" };
static constexpr std::wstring_view FOREGROUND_KEY{ L"foreground" };
static constexpr std::wstring_view BACKGROUND_KEY{ L"background" };
static constexpr std::wstring_view COLORTABLE_KEY{ L"colorTable" };
static constexpr std::wstring_view HISTORYSIZE_KEY{ L"historySize" };
static constexpr std::wstring_view SNAPONINPUT_KEY{ L"snapOnInput" };
static constexpr std::wstring_view CURSORCOLOR_KEY{ L"cursorColor" };
static constexpr std::wstring_view CURSORSHAPE_KEY{ L"cursorShape" };
static constexpr std::wstring_view CURSORHEIGHT_KEY{ L"cursorHeight" };
static constexpr std::string_view ForegroundKey{ "foreground" };
static constexpr std::string_view BackgroundKey{ "background" };
static constexpr std::string_view ColorTableKey{ "colorTable" };
static constexpr std::string_view HistorySizeKey{ "historySize" };
static constexpr std::string_view SnapOnInputKey{ "snapOnInput" };
static constexpr std::string_view CursorColorKey{ "cursorColor" };
static constexpr std::string_view CursorShapeKey{ "cursorShape" };
static constexpr std::string_view CursorHeightKey{ "cursorHeight" };
static constexpr std::wstring_view COMMANDLINE_KEY{ L"commandline" };
static constexpr std::wstring_view FONTFACE_KEY{ L"fontFace" };
static constexpr std::wstring_view FONTSIZE_KEY{ L"fontSize" };
static constexpr std::wstring_view ACRYLICTRANSPARENCY_KEY{ L"acrylicOpacity" };
static constexpr std::wstring_view USEACRYLIC_KEY{ L"useAcrylic" };
static constexpr std::wstring_view SCROLLBARSTATE_KEY{ L"scrollbarState" };
static constexpr std::wstring_view CLOSEONEXIT_KEY{ L"closeOnExit" };
static constexpr std::wstring_view PADDING_KEY{ L"padding" };
static constexpr std::wstring_view STARTINGDIRECTORY_KEY{ L"startingDirectory" };
static constexpr std::wstring_view ICON_KEY{ L"icon" };
static constexpr std::wstring_view BACKGROUNDIMAGE_KEY{ L"backgroundImage" };
static constexpr std::wstring_view BACKGROUNDIMAGEOPACITY_KEY{ L"backgroundImageOpacity" };
static constexpr std::wstring_view BACKGROUNDIMAGESTRETCHMODE_KEY{ L"backgroundImageStretchMode" };
static constexpr std::string_view CommandlineKey{ "commandline" };
static constexpr std::string_view FontFaceKey{ "fontFace" };
static constexpr std::string_view FontSizeKey{ "fontSize" };
static constexpr std::string_view AcrylicTransparencyKey{ "acrylicOpacity" };
static constexpr std::string_view UseAcrylicKey{ "useAcrylic" };
static constexpr std::string_view ScrollbarStateKey{ "scrollbarState" };
static constexpr std::string_view CloseOnExitKey{ "closeOnExit" };
static constexpr std::string_view PaddingKey{ "padding" };
static constexpr std::string_view StartingDirectoryKey{ "startingDirectory" };
static constexpr std::string_view IconKey{ "icon" };
static constexpr std::string_view BackgroundImageKey{ "backgroundImage" };
static constexpr std::string_view BackgroundImageOpacityKey{ "backgroundImageOpacity" };
static constexpr std::string_view BackgroundimageStretchModeKey{ "backgroundImageStretchMode" };
// Possible values for Scrollbar state
static constexpr std::wstring_view ALWAYS_VISIBLE{ L"visible" };
static constexpr std::wstring_view ALWAYS_HIDE{ L"hidden" };
static constexpr std::wstring_view AlwaysVisible{ L"visible" };
static constexpr std::wstring_view AlwaysHide{ L"hidden" };
// Possible values for Cursor Shape
static constexpr std::wstring_view CURSORSHAPE_VINTAGE{ L"vintage" };
static constexpr std::wstring_view CURSORSHAPE_BAR{ L"bar" };
static constexpr std::wstring_view CURSORSHAPE_UNDERSCORE{ L"underscore" };
static constexpr std::wstring_view CURSORSHAPE_FILLEDBOX{ L"filledBox" };
static constexpr std::wstring_view CURSORSHAPE_EMPTYBOX{ L"emptyBox" };
static constexpr std::wstring_view CursorShapeVintage{ L"vintage" };
static constexpr std::wstring_view CursorShapeBar{ L"bar" };
static constexpr std::wstring_view CursorShapeUnderscore{ L"underscore" };
static constexpr std::wstring_view CursorShapeFilledbox{ L"filledBox" };
static constexpr std::wstring_view CursorShapeEmptybox{ L"emptyBox" };
// Possible values for Image Stretch Mode
static constexpr std::wstring_view IMAGESTRETCHMODE_NONE{ L"none" };
static constexpr std::wstring_view IMAGESTRETCHMODE_FILL{ L"fill" };
static constexpr std::wstring_view IMAGESTRETCHMODE_UNIFORM{ L"uniform" };
static constexpr std::wstring_view IMAGESTRETCHMODE_UNIFORMTOFILL{ L"uniformToFill" };
static constexpr std::string_view ImageStretchModeNone{ "none" };
static constexpr std::string_view ImageStretchModeFill{ "fill" };
static constexpr std::string_view ImageStretchModeUniform{ "uniform" };
static constexpr std::string_view ImageStretchModeUniformTofill{ "uniformToFill" };
Profile::Profile() :
Profile(Utils::CreateGuid())
@@ -209,118 +210,89 @@ TerminalSettings Profile::CreateTerminalSettings(const std::vector<ColorScheme>&
// - <none>
// Return Value:
// - a JsonObject which is an equivalent serialization of this object.
JsonObject Profile::ToJson() const
Json::Value Profile::ToJson() const
{
winrt::Windows::Data::Json::JsonObject jsonObject;
Json::Value root;
// Profile-specific settings
const auto guidStr = Utils::GuidToString(_guid);
const auto guid = JsonValue::CreateStringValue(guidStr);
const auto name = JsonValue::CreateStringValue(_name);
///// Profile-specific settings /////
root[JsonKey(GuidKey)] = winrt::to_string(Utils::GuidToString(_guid));
root[JsonKey(NameKey)] = winrt::to_string(_name);
// Core Settings
const auto historySize = JsonValue::CreateNumberValue(_historySize);
const auto snapOnInput = JsonValue::CreateBooleanValue(_snapOnInput);
const auto cursorColor = JsonValue::CreateStringValue(Utils::ColorToHexString(_cursorColor));
// Control Settings
const auto cmdline = JsonValue::CreateStringValue(_commandline);
const auto fontFace = JsonValue::CreateStringValue(_fontFace);
const auto fontSize = JsonValue::CreateNumberValue(_fontSize);
const auto acrylicTransparency = JsonValue::CreateNumberValue(_acrylicTransparency);
const auto useAcrylic = JsonValue::CreateBooleanValue(_useAcrylic);
const auto closeOnExit = JsonValue::CreateBooleanValue(_closeOnExit);
const auto padding = JsonValue::CreateStringValue(_padding);
if (_startingDirectory)
{
const auto startingDirectory = JsonValue::CreateStringValue(_startingDirectory.value());
jsonObject.Insert(STARTINGDIRECTORY_KEY, startingDirectory);
}
jsonObject.Insert(GUID_KEY, guid);
jsonObject.Insert(NAME_KEY, name);
// Core Settings
///// Core Settings /////
if (_defaultForeground)
{
const auto defaultForeground = JsonValue::CreateStringValue(Utils::ColorToHexString(_defaultForeground.value()));
jsonObject.Insert(FOREGROUND_KEY, defaultForeground);
root[JsonKey(ForegroundKey)] = Utils::ColorToHexString(_defaultForeground.value());
}
if (_defaultBackground)
{
const auto defaultBackground = JsonValue::CreateStringValue(Utils::ColorToHexString(_defaultBackground.value()));
jsonObject.Insert(BACKGROUND_KEY, defaultBackground);
root[JsonKey(BackgroundKey)] = Utils::ColorToHexString(_defaultBackground.value());
}
if (_schemeName)
{
const auto scheme = JsonValue::CreateStringValue(_schemeName.value());
jsonObject.Insert(COLORSCHEME_KEY, scheme);
const auto scheme = winrt::to_string(_schemeName.value());
root[JsonKey(ColorSchemeKey)] = scheme;
}
else
{
JsonArray tableArray{};
Json::Value tableArray{};
for (auto& color : _colorTable)
{
auto s = Utils::ColorToHexString(color);
tableArray.Append(JsonValue::CreateStringValue(s));
tableArray.append(Utils::ColorToHexString(color));
}
jsonObject.Insert(COLORTABLE_KEY, tableArray);
root[JsonKey(ColorTableKey)] = tableArray;
}
jsonObject.Insert(HISTORYSIZE_KEY, historySize);
jsonObject.Insert(SNAPONINPUT_KEY, snapOnInput);
jsonObject.Insert(CURSORCOLOR_KEY, cursorColor);
root[JsonKey(HistorySizeKey)] = _historySize;
root[JsonKey(SnapOnInputKey)] = _snapOnInput;
root[JsonKey(CursorColorKey)] = Utils::ColorToHexString(_cursorColor);
// Only add the cursor height property if we're a legacy-style cursor.
if (_cursorShape == CursorStyle::Vintage)
{
jsonObject.Insert(CURSORHEIGHT_KEY, JsonValue::CreateNumberValue(_cursorHeight));
root[JsonKey(CursorHeightKey)] = _cursorHeight;
}
jsonObject.Insert(CURSORSHAPE_KEY, JsonValue::CreateStringValue(_SerializeCursorStyle(_cursorShape)));
root[JsonKey(CursorShapeKey)] = winrt::to_string(_SerializeCursorStyle(_cursorShape));
// Control Settings
jsonObject.Insert(COMMANDLINE_KEY, cmdline);
jsonObject.Insert(FONTFACE_KEY, fontFace);
jsonObject.Insert(FONTSIZE_KEY, fontSize);
jsonObject.Insert(ACRYLICTRANSPARENCY_KEY, acrylicTransparency);
jsonObject.Insert(USEACRYLIC_KEY, useAcrylic);
jsonObject.Insert(CLOSEONEXIT_KEY, closeOnExit);
jsonObject.Insert(PADDING_KEY, padding);
///// Control Settings /////
root[JsonKey(CommandlineKey)] = winrt::to_string(_commandline);
root[JsonKey(FontFaceKey)] = winrt::to_string(_fontFace);
root[JsonKey(FontSizeKey)] = _fontSize;
root[JsonKey(AcrylicTransparencyKey)] = _acrylicTransparency;
root[JsonKey(UseAcrylicKey)] = _useAcrylic;
root[JsonKey(CloseOnExitKey)] = _closeOnExit;
root[JsonKey(PaddingKey)] = winrt::to_string(_padding);
if (_scrollbarState)
{
const auto scrollbarState = JsonValue::CreateStringValue(_scrollbarState.value());
jsonObject.Insert(SCROLLBARSTATE_KEY, scrollbarState);
const auto scrollbarState = winrt::to_string(_scrollbarState.value());
root[JsonKey(ScrollbarStateKey)] = scrollbarState;
}
if (_icon)
{
const auto icon = JsonValue::CreateStringValue(_icon.value());
jsonObject.Insert(ICON_KEY, icon);
const auto icon = winrt::to_string(_icon.value());
root[JsonKey(IconKey)] = icon;
}
if (_startingDirectory)
{
root[JsonKey(StartingDirectoryKey)] = winrt::to_string(_startingDirectory.value());
}
if (_backgroundImage)
{
const auto backgroundImage = JsonValue::CreateStringValue(_backgroundImage.value());
jsonObject.Insert(BACKGROUNDIMAGE_KEY, backgroundImage);
root[JsonKey(BackgroundImageKey)] = winrt::to_string(_backgroundImage.value());
}
if (_backgroundImageOpacity)
{
const auto opacity = JsonValue::CreateNumberValue(_backgroundImageOpacity.value());
jsonObject.Insert(BACKGROUNDIMAGEOPACITY_KEY, opacity);
root[JsonKey(BackgroundImageOpacityKey)] = _backgroundImageOpacity.value();
}
if (_backgroundImageStretchMode)
{
const auto imageStretchMode = JsonValue::CreateStringValue(
SerializeImageStretchMode(_backgroundImageStretchMode.value()));
jsonObject.Insert(BACKGROUNDIMAGESTRETCHMODE_KEY, imageStretchMode);
root[JsonKey(BackgroundimageStretchModeKey)] = SerializeImageStretchMode(_backgroundImageStretchMode.value()).data();
}
return jsonObject;
return root;
}
// Method Description:
@@ -329,149 +301,133 @@ JsonObject Profile::ToJson() const
// - json: an object which should be a serialization of a Profile object.
// Return Value:
// - a new Profile instance created from the values in `json`
Profile Profile::FromJson(winrt::Windows::Data::Json::JsonObject json)
Profile Profile::FromJson(const Json::Value& json)
{
Profile result{};
// Profile-specific Settings
if (json.HasKey(NAME_KEY))
if (auto name{ json[JsonKey(NameKey)] })
{
result._name = json.GetNamedString(NAME_KEY);
result._name = GetWstringFromJson(name);
}
if (json.HasKey(GUID_KEY))
if (auto guid{ json[JsonKey(GuidKey)] })
{
const auto guidString = json.GetNamedString(GUID_KEY);
// TODO: MSFT:20737698 - if this fails, display an approriate error
const auto guid = Utils::GuidFromString(guidString.c_str());
result._guid = guid;
result._guid = Utils::GuidFromString(GetWstringFromJson(guid));
}
// Core Settings
if (json.HasKey(FOREGROUND_KEY))
if (auto foreground{ json[JsonKey(ForegroundKey)] })
{
const auto fgString = json.GetNamedString(FOREGROUND_KEY);
// TODO: MSFT:20737698 - if this fails, display an approriate error
const auto color = Utils::ColorFromHexString(fgString.c_str());
const auto color = Utils::ColorFromHexString(foreground.asString());
result._defaultForeground = color;
}
if (json.HasKey(BACKGROUND_KEY))
if (auto background{ json[JsonKey(BackgroundKey)] })
{
const auto bgString = json.GetNamedString(BACKGROUND_KEY);
// TODO: MSFT:20737698 - if this fails, display an approriate error
const auto color = Utils::ColorFromHexString(bgString.c_str());
const auto color = Utils::ColorFromHexString(background.asString());
result._defaultBackground = color;
}
if (json.HasKey(COLORSCHEME_KEY))
if (auto colorScheme{ json[JsonKey(ColorSchemeKey)] })
{
result._schemeName = json.GetNamedString(COLORSCHEME_KEY);
result._schemeName = GetWstringFromJson(colorScheme);
}
else if (json.HasKey(COLORSCHEME_KEY_OLD))
else if (auto colorScheme{ json[JsonKey(ColorSchemeKeyOld)] })
{
// TODO: deprecate old settings key
result._schemeName = json.GetNamedString(COLORSCHEME_KEY_OLD);
// TODO:GH#1069 deprecate old settings key
result._schemeName = GetWstringFromJson(colorScheme);
}
else if (json.HasKey(COLORTABLE_KEY))
else if (auto colortable{ json[JsonKey(ColorTableKey)] })
{
const auto table = json.GetNamedArray(COLORTABLE_KEY);
int i = 0;
for (auto v : table)
for (const auto& tableEntry : colortable)
{
if (v.ValueType() == JsonValueType::String)
if (tableEntry.isString())
{
const auto str = v.GetString();
// TODO: MSFT:20737698 - if this fails, display an approriate error
const auto color = Utils::ColorFromHexString(str.c_str());
const auto color = Utils::ColorFromHexString(tableEntry.asString());
result._colorTable[i] = color;
}
i++;
}
}
if (json.HasKey(HISTORYSIZE_KEY))
if (auto historySize{ json[JsonKey(HistorySizeKey)] })
{
// TODO:MSFT:20642297 - Use a sentinel value (-1) for "Infinite scrollback"
result._historySize = static_cast<int32_t>(json.GetNamedNumber(HISTORYSIZE_KEY));
result._historySize = historySize.asInt();
}
if (json.HasKey(SNAPONINPUT_KEY))
if (auto snapOnInput{ json[JsonKey(SnapOnInputKey)] })
{
result._snapOnInput = json.GetNamedBoolean(SNAPONINPUT_KEY);
result._snapOnInput = snapOnInput.asBool();
}
if (json.HasKey(CURSORCOLOR_KEY))
if (auto cursorColor{ json[JsonKey(CursorColorKey)] })
{
const auto cursorString = json.GetNamedString(CURSORCOLOR_KEY);
// TODO: MSFT:20737698 - if this fails, display an approriate error
const auto color = Utils::ColorFromHexString(cursorString.c_str());
const auto color = Utils::ColorFromHexString(cursorColor.asString());
result._cursorColor = color;
}
if (json.HasKey(CURSORHEIGHT_KEY))
if (auto cursorHeight{ json[JsonKey(CursorHeightKey)] })
{
result._cursorHeight = static_cast<uint32_t>(json.GetNamedNumber(CURSORHEIGHT_KEY));
result._cursorHeight = cursorHeight.asUInt();
}
if (json.HasKey(CURSORSHAPE_KEY))
if (auto cursorShape{ json[JsonKey(CursorShapeKey)] })
{
const auto shapeString = json.GetNamedString(CURSORSHAPE_KEY);
result._cursorShape = _ParseCursorShape(shapeString.c_str());
result._cursorShape = _ParseCursorShape(GetWstringFromJson(cursorShape));
}
// Control Settings
if (json.HasKey(COMMANDLINE_KEY))
if (auto commandline{ json[JsonKey(CommandlineKey)] })
{
result._commandline = json.GetNamedString(COMMANDLINE_KEY);
result._commandline = GetWstringFromJson(commandline);
}
if (json.HasKey(FONTFACE_KEY))
if (auto fontFace{ json[JsonKey(FontFaceKey)] })
{
result._fontFace = json.GetNamedString(FONTFACE_KEY);
result._fontFace = GetWstringFromJson(fontFace);
}
if (json.HasKey(FONTSIZE_KEY))
if (auto fontSize{ json[JsonKey(FontSizeKey)] })
{
result._fontSize = static_cast<int32_t>(json.GetNamedNumber(FONTSIZE_KEY));
result._fontSize = fontSize.asInt();
}
if (json.HasKey(ACRYLICTRANSPARENCY_KEY))
if (auto acrylicTransparency{ json[JsonKey(AcrylicTransparencyKey)] })
{
result._acrylicTransparency = json.GetNamedNumber(ACRYLICTRANSPARENCY_KEY);
result._acrylicTransparency = acrylicTransparency.asFloat();
}
if (json.HasKey(USEACRYLIC_KEY))
if (auto useAcrylic{ json[JsonKey(UseAcrylicKey)] })
{
result._useAcrylic = json.GetNamedBoolean(USEACRYLIC_KEY);
result._useAcrylic = useAcrylic.asBool();
}
if (json.HasKey(CLOSEONEXIT_KEY))
if (auto closeOnExit{ json[JsonKey(CloseOnExitKey)] })
{
result._closeOnExit = json.GetNamedBoolean(CLOSEONEXIT_KEY);
result._closeOnExit = closeOnExit.asBool();
}
if (json.HasKey(PADDING_KEY))
if (auto padding{ json[JsonKey(PaddingKey)] })
{
result._padding = json.GetNamedString(PADDING_KEY);
result._padding = GetWstringFromJson(padding);
}
if (json.HasKey(SCROLLBARSTATE_KEY))
if (auto scrollbarState{ json[JsonKey(ScrollbarStateKey)] })
{
result._scrollbarState = json.GetNamedString(SCROLLBARSTATE_KEY);
result._scrollbarState = GetWstringFromJson(scrollbarState);
}
if (json.HasKey(STARTINGDIRECTORY_KEY))
if (auto startingDirectory{ json[JsonKey(StartingDirectoryKey)] })
{
result._startingDirectory = json.GetNamedString(STARTINGDIRECTORY_KEY);
result._startingDirectory = GetWstringFromJson(startingDirectory);
}
if (json.HasKey(ICON_KEY))
if (auto icon{ json[JsonKey(IconKey)] })
{
result._icon = json.GetNamedString(ICON_KEY);
result._icon = GetWstringFromJson(icon);
}
if (json.HasKey(BACKGROUNDIMAGE_KEY))
if (auto backgroundImage{ json[JsonKey(BackgroundImageKey)] })
{
result._backgroundImage = json.GetNamedString(BACKGROUNDIMAGE_KEY);
result._backgroundImage = GetWstringFromJson(backgroundImage);
}
if (json.HasKey(BACKGROUNDIMAGEOPACITY_KEY))
if (auto backgroundImageOpacity{ json[JsonKey(BackgroundImageOpacityKey)] })
{
result._backgroundImageOpacity = json.GetNamedNumber(BACKGROUNDIMAGEOPACITY_KEY);
result._backgroundImageOpacity = backgroundImageOpacity.asFloat();
}
if (json.HasKey(BACKGROUNDIMAGESTRETCHMODE_KEY))
if (auto backgroundImageStretchMode{ json[JsonKey(BackgroundimageStretchModeKey)] })
{
const auto stretchMode = json.GetNamedString(BACKGROUNDIMAGESTRETCHMODE_KEY);
result._backgroundImageStretchMode = ParseImageStretchMode(stretchMode.c_str());
result._backgroundImageStretchMode = ParseImageStretchMode(backgroundImageStretchMode.asString());
}
return result;
}
void Profile::SetFontFace(std::wstring fontFace) noexcept
{
_fontFace = fontFace;
@@ -597,17 +553,16 @@ std::wstring Profile::EvaluateStartingDirectory(const std::wstring& directory)
// - The corresponding enum value which maps to the string provided by the user
ScrollbarState Profile::ParseScrollbarState(const std::wstring& scrollbarState)
{
if (scrollbarState == ALWAYS_VISIBLE)
if (scrollbarState == AlwaysVisible)
{
return ScrollbarState::Visible;
}
else if (scrollbarState == ALWAYS_HIDE)
else if (scrollbarState == AlwaysHide)
{
return ScrollbarState::Hidden;
}
else
{
// default behavior for invalid data
return ScrollbarState::Visible;
}
}
@@ -619,17 +574,17 @@ ScrollbarState Profile::ParseScrollbarState(const std::wstring& scrollbarState)
// - The value from the profiles.json file
// Return Value:
// - The corresponding enum value which maps to the string provided by the user
winrt::Windows::UI::Xaml::Media::Stretch Profile::ParseImageStretchMode(const std::wstring& imageStretchMode)
winrt::Windows::UI::Xaml::Media::Stretch Profile::ParseImageStretchMode(const std::string_view imageStretchMode)
{
if (imageStretchMode == IMAGESTRETCHMODE_NONE)
if (imageStretchMode == ImageStretchModeNone)
{
return winrt::Windows::UI::Xaml::Media::Stretch::None;
}
else if (imageStretchMode == IMAGESTRETCHMODE_FILL)
else if (imageStretchMode == ImageStretchModeFill)
{
return winrt::Windows::UI::Xaml::Media::Stretch::Fill;
}
else if (imageStretchMode == IMAGESTRETCHMODE_UNIFORM)
else if (imageStretchMode == ImageStretchModeUniform)
{
return winrt::Windows::UI::Xaml::Media::Stretch::Uniform;
}
@@ -640,30 +595,28 @@ winrt::Windows::UI::Xaml::Media::Stretch Profile::ParseImageStretchMode(const st
}
// Method Description:
// - Helper function for converting an ImageStretchMode to the
// - Helper function for converting an ImageStretchMode to the
// correct string value.
// Arguments:
// - imageStretchMode: The enum value to convert to a string.
// Return Value:
// - The string value for the given ImageStretchMode
std::wstring_view Profile::SerializeImageStretchMode(const winrt::Windows::UI::Xaml::Media::Stretch imageStretchMode)
std::string_view Profile::SerializeImageStretchMode(const winrt::Windows::UI::Xaml::Media::Stretch imageStretchMode)
{
switch (imageStretchMode)
{
case winrt::Windows::UI::Xaml::Media::Stretch::None:
return IMAGESTRETCHMODE_NONE;
return ImageStretchModeNone;
case winrt::Windows::UI::Xaml::Media::Stretch::Fill:
return IMAGESTRETCHMODE_FILL;
return ImageStretchModeFill;
case winrt::Windows::UI::Xaml::Media::Stretch::Uniform:
return IMAGESTRETCHMODE_UNIFORM;
return ImageStretchModeUniform;
default:
case winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill:
return IMAGESTRETCHMODE_UNIFORMTOFILL;
return ImageStretchModeUniformTofill;
}
}
// Method Description:
// - Helper function for converting a user-specified cursor style corresponding
// CursorStyle enum value
@@ -673,23 +626,23 @@ std::wstring_view Profile::SerializeImageStretchMode(const winrt::Windows::UI::X
// - The corresponding enum value which maps to the string provided by the user
CursorStyle Profile::_ParseCursorShape(const std::wstring& cursorShapeString)
{
if (cursorShapeString == CURSORSHAPE_VINTAGE)
if (cursorShapeString == CursorShapeVintage)
{
return CursorStyle::Vintage;
}
else if (cursorShapeString == CURSORSHAPE_BAR)
else if (cursorShapeString == CursorShapeBar)
{
return CursorStyle::Bar;
}
else if (cursorShapeString == CURSORSHAPE_UNDERSCORE)
else if (cursorShapeString == CursorShapeUnderscore)
{
return CursorStyle::Underscore;
}
else if (cursorShapeString == CURSORSHAPE_FILLEDBOX)
else if (cursorShapeString == CursorShapeFilledbox)
{
return CursorStyle::FilledBox;
}
else if (cursorShapeString == CURSORSHAPE_EMPTYBOX)
else if (cursorShapeString == CursorShapeEmptybox)
{
return CursorStyle::EmptyBox;
}
@@ -709,15 +662,15 @@ std::wstring_view Profile::_SerializeCursorStyle(const CursorStyle cursorShape)
switch (cursorShape)
{
case CursorStyle::Underscore:
return CURSORSHAPE_UNDERSCORE;
return CursorShapeUnderscore;
case CursorStyle::FilledBox:
return CURSORSHAPE_FILLEDBOX;
return CursorShapeFilledbox;
case CursorStyle::EmptyBox:
return CURSORSHAPE_EMPTYBOX;
return CursorShapeEmptybox;
case CursorStyle::Vintage:
return CURSORSHAPE_VINTAGE;
return CursorShapeVintage;
default:
case CursorStyle::Bar:
return CURSORSHAPE_BAR;
return CursorShapeBar;
}
}

View File

@@ -32,8 +32,8 @@ public:
winrt::Microsoft::Terminal::Settings::TerminalSettings CreateTerminalSettings(const std::vector<::TerminalApp::ColorScheme>& schemes) const;
winrt::Windows::Data::Json::JsonObject ToJson() const;
static Profile FromJson(winrt::Windows::Data::Json::JsonObject json);
Json::Value ToJson() const;
static Profile FromJson(const Json::Value& json);
GUID GetGuid() const noexcept;
std::wstring_view GetName() const noexcept;
@@ -59,8 +59,8 @@ private:
static std::wstring EvaluateStartingDirectory(const std::wstring& directory);
static winrt::Microsoft::Terminal::Settings::ScrollbarState ParseScrollbarState(const std::wstring& scrollbarState);
static winrt::Windows::UI::Xaml::Media::Stretch ParseImageStretchMode(const std::wstring& imageStretchMode);
static std::wstring_view SerializeImageStretchMode(const winrt::Windows::UI::Xaml::Media::Stretch imageStretchMode);
static winrt::Windows::UI::Xaml::Media::Stretch ParseImageStretchMode(const std::string_view imageStretchMode);
static std::string_view SerializeImageStretchMode(const winrt::Windows::UI::Xaml::Media::Stretch imageStretchMode);
static winrt::Microsoft::Terminal::Settings::CursorStyle _ParseCursorShape(const std::wstring& cursorShapeString);
static std::wstring_view _SerializeCursorStyle(const winrt::Microsoft::Terminal::Settings::CursorStyle cursorShape);

View File

@@ -6,41 +6,47 @@
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::TerminalControl;
Tab::Tab(GUID profile, winrt::Microsoft::Terminal::TerminalControl::TermControl control) :
_control{ control },
_focused{ false },
_profile{ profile },
_tabViewItem{ nullptr }
static const int TabViewFontSize = 12;
Tab::Tab(const GUID& profile, const TermControl& control)
{
_rootPane = std::make_shared<Pane>(profile, control, true);
_rootPane->Closed([=]() {
_closedHandlers();
});
_MakeTabViewItem();
}
Tab::~Tab()
{
// When we're destructed, winrt will automatically decrement the refcount
// of our terminalcontrol.
// Assuming that refcount hits 0, it'll destruct it on its own, including
// calling Close on the terminal and connection.
}
void Tab::_MakeTabViewItem()
{
_tabViewItem = ::winrt::Microsoft::UI::Xaml::Controls::TabViewItem{};
const auto title = _control.Title();
_tabViewItem.Header(title);
_control.TitleChanged([=](auto newTitle){
_tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [=](){
_tabViewItem.Header(newTitle);
});
});
_tabViewItem.FontSize(TabViewFontSize);
}
winrt::Microsoft::Terminal::TerminalControl::TermControl Tab::GetTerminalControl()
UIElement Tab::GetRootElement()
{
return _control;
return _rootPane->GetRootElement();
}
// Method Description:
// - Returns nullptr if no children of this tab were the last control to be
// focused, or the TermControl that _was_ the last control to be focused (if
// there was one).
// - This control might not currently be focused, if the tab itself is not
// currently focused.
// Arguments:
// - <none>
// Return Value:
// - nullptr if no children were marked `_lastFocused`, else the TermControl
// that was last focused.
TermControl Tab::GetFocusedTerminalControl()
{
return _rootPane->GetFocusedTerminalControl();
}
winrt::Microsoft::UI::Xaml::Controls::TabViewItem Tab::GetTabViewItem()
@@ -48,13 +54,28 @@ winrt::Microsoft::UI::Xaml::Controls::TabViewItem Tab::GetTabViewItem()
return _tabViewItem;
}
bool Tab::IsFocused()
// Method Description:
// - Returns true if this is the currently focused tab. For any set of tabs,
// there should only be one tab that is marked as focused, though each tab has
// no control over the other tabs in the set.
// Arguments:
// - <none>
// Return Value:
// - true iff this tab is focused.
bool Tab::IsFocused() const noexcept
{
return _focused;
}
void Tab::SetFocused(bool focused)
// Method Description:
// - Updates our focus state. If we're gaining focus, make sure to transfer
// focus to the last focused terminal control in our tree of controls.
// Arguments:
// - focused: our new focus state. If true, we should be focused. If false, we
// should be unfocused.
// Return Value:
// - <none>
void Tab::SetFocused(const bool focused)
{
_focused = focused;
@@ -64,15 +85,89 @@ void Tab::SetFocused(bool focused)
}
}
GUID Tab::GetProfile() const noexcept
// Method Description:
// - Returns nullopt if no children of this tab were the last control to be
// focused, or the GUID of the profile of the last control to be focused (if
// there was one).
// Arguments:
// - <none>
// Return Value:
// - nullopt if no children of this tab were the last control to be
// focused, else the GUID of the profile of the last control to be focused
std::optional<GUID> Tab::GetFocusedProfile() const noexcept
{
return _profile;
return _rootPane->GetFocusedProfile();
}
// Method Description:
// - Attempts to update the settings of this tab's tree of panes.
// Arguments:
// - settings: The new TerminalSettings to apply to any matching controls
// - profile: The GUID of the profile these settings should apply to.
// Return Value:
// - <none>
void Tab::UpdateSettings(const TerminalSettings& settings, const GUID& profile)
{
_rootPane->UpdateSettings(settings, profile);
}
// Method Description:
// - Focus the last focused control in our tree of panes.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Tab::_Focus()
{
_focused = true;
_control.GetControl().Focus(FocusState::Programmatic);
auto lastFocusedControl = _rootPane->GetFocusedTerminalControl();
if (lastFocusedControl)
{
lastFocusedControl.GetControl().Focus(FocusState::Programmatic);
}
}
// Method Description:
// - Update the focus state of this tab's tree of panes. If one of the controls
// under this tab is focused, then it will be marked as the last focused. If
// there are no focused panes, then there will not be a last focused control
// when this returns.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Tab::UpdateFocus()
{
_rootPane->UpdateFocus();
}
// Method Description:
// - Gets the title string of the last focused terminal control in our tree.
// Returns the empty string if there is no such control.
// Arguments:
// - <none>
// Return Value:
// - the title string of the last focused terminal control in our tree.
winrt::hstring Tab::GetFocusedTitle() const
{
const auto lastFocusedControl = _rootPane->GetFocusedTerminalControl();
return lastFocusedControl ? lastFocusedControl.Title() : L"";
}
// Method Description:
// - Set the text on the TabViewItem for this tab.
// Arguments:
// - text: The new text string to use as the Header for our TabViewItem
// Return Value:
// - <none>
void Tab::SetTabText(const winrt::hstring& text)
{
// Copy the hstring, so we don't capture a dead reference
winrt::hstring textCopy{ text };
_tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [text = std::move(textCopy), this](){
_tabViewItem.Header(text);
});
}
// Method Description:
@@ -83,10 +178,39 @@ void Tab::_Focus()
// - delta: a number of lines to move the viewport relative to the current viewport.
// Return Value:
// - <none>
void Tab::Scroll(int delta)
void Tab::Scroll(const int delta)
{
_control.GetControl().Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [=](){
const auto currentOffset = _control.GetScrollOffset();
_control.KeyboardScrollViewport(currentOffset + delta);
auto control = GetFocusedTerminalControl();
control.GetControl().Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [control, delta](){
const auto currentOffset = control.GetScrollOffset();
control.KeyboardScrollViewport(currentOffset + delta);
});
}
// Method Description:
// - Vertically split the focused pane in our tree of panes, and place the
// given TermControl into the newly created pane.
// Arguments:
// - profile: The profile GUID to associate with the newly created pane.
// - control: A TermControl to use in the new pane.
// Return Value:
// - <none>
void Tab::AddVerticalSplit(const GUID& profile, TermControl& control)
{
_rootPane->SplitVertical(profile, control);
}
// Method Description:
// - Horizontally split the focused pane in our tree of panes, and place the
// given TermControl into the newly created pane.
// Arguments:
// - profile: The profile GUID to associate with the newly created pane.
// - control: A TermControl to use in the new pane.
// Return Value:
// - <none>
void Tab::AddHorizontalSplit(const GUID& profile, TermControl& control)
{
_rootPane->SplitHorizontal(profile, control);
}
DEFINE_EVENT(Tab, Closed, _closedHandlers, ConnectionClosedEventArgs);

View File

@@ -3,30 +3,40 @@
#pragma once
#include <winrt/Microsoft.UI.Xaml.Controls.h>
#include <winrt/Microsoft.Terminal.TerminalControl.h>
#include "Pane.h"
class Tab
{
public:
Tab(GUID profile, winrt::Microsoft::Terminal::TerminalControl::TermControl control);
~Tab();
Tab(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
winrt::Microsoft::UI::Xaml::Controls::TabViewItem GetTabViewItem();
winrt::Microsoft::Terminal::TerminalControl::TermControl GetTerminalControl();
winrt::Windows::UI::Xaml::UIElement GetRootElement();
winrt::Microsoft::Terminal::TerminalControl::TermControl GetFocusedTerminalControl();
std::optional<GUID> GetFocusedProfile() const noexcept;
bool IsFocused();
void SetFocused(bool focused);
bool IsFocused() const noexcept;
void SetFocused(const bool focused);
GUID GetProfile() const noexcept;
void Scroll(const int delta);
void AddVerticalSplit(const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
void AddHorizontalSplit(const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
void Scroll(int delta);
void UpdateFocus();
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::TerminalSettings& settings, const GUID& profile);
winrt::hstring GetFocusedTitle() const;
void SetTabText(const winrt::hstring& text);
DECLARE_EVENT(Closed, _closedHandlers, winrt::Microsoft::Terminal::TerminalControl::ConnectionClosedEventArgs);
private:
winrt::Microsoft::Terminal::TerminalControl::TermControl _control;
bool _focused;
GUID _profile;
winrt::Microsoft::UI::Xaml::Controls::TabViewItem _tabViewItem;
std::shared_ptr<Pane> _rootPane{ nullptr };
bool _focused{ false };
winrt::Microsoft::UI::Xaml::Controls::TabViewItem _tabViewItem{ nullptr };
void _MakeTabViewItem();
void _Focus();

View File

@@ -37,11 +37,14 @@
<!-- ========================= Headers ======================== -->
<ItemGroup>
<ClInclude Include="Tab.h" />
<ClInclude Include="Pane.h" />
<ClInclude Include="ColorScheme.h" />
<ClInclude Include="GlobalAppSettings.h" />
<ClInclude Include="Profile.h" />
<ClInclude Include="CascadiaSettings.h" />
<ClInclude Include="AppKeyBindingsSerialization.h" />
<ClInclude Include="KeyChordSerialization.h" />
<ClInclude Include="Utils.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="AppKeyBindings.h">
<DependentUpon>AppKeyBindings.idl</DependentUpon>
@@ -54,12 +57,15 @@
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>
<ClCompile Include="Tab.cpp" />
<ClCompile Include="Pane.cpp" />
<ClCompile Include="ColorScheme.cpp" />
<ClCompile Include="GlobalAppSettings.cpp" />
<ClCompile Include="Profile.cpp" />
<ClCompile Include="CascadiaSettings.cpp" />
<ClCompile Include="CascadiaSettingsSerialization.cpp" />
<ClCompile Include="AppKeyBindingsSerialization.cpp" />
<ClCompile Include="KeyChordSerialization.cpp" />
<ClCompile Include="Utils.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
@@ -70,6 +76,13 @@
<DependentUpon>App.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
<!-- You _NEED_ to include this file and the jsoncpp IncludePath (below) if
you want to use jsoncpp -->
<ClCompile Include="$(OpenConsoleDir)\dep\jsoncpp\jsoncpp.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<!-- ========================= idl Files ======================== -->
@@ -117,6 +130,10 @@
<NoOutputRedirection>true</NoOutputRedirection>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>WindowsApp.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>

View File

@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "Utils.h"
// Method Description:
// - Contstructs a wstring from a given Json::Value object. Reads the object as
// a std::string using asString, then builds an hstring from that std::string,
// then converts that hstring into a std::wstring.
// Arguments:
// - json: the Json::Value to parse as a string
// Return Value:
// - the wstring equivalent of the value in json
std::wstring GetWstringFromJson(const Json::Value& json)
{
return winrt::to_hstring(json.asString()).c_str();
}

View File

@@ -0,0 +1,30 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- Utils.h
Abstract:
- Helpers for the TerminalApp project
Author(s):
- Mike Griese - May 2019
--*/
#pragma once
std::wstring GetWstringFromJson(const Json::Value& json);
// Method Description:
// - Create a std::string from a string_view. We do this because we can't look
// up a key in a Json::Value with a string_view directly, so instead we'll use
// this helper. Should a string_view lookup ever be added to jsoncpp, we can
// remove this entirely.
// Arguments:
// - key: the string_view to build a string from
// Return Value:
// - a std::string to use for looking up a value from a Json::Value
inline std::string JsonKey(const std::string_view key)
{
return static_cast<std::string>(key);
}

View File

@@ -49,3 +49,6 @@
TRACELOGGING_DECLARE_PROVIDER(g_hTerminalWin32Provider);
#include <telemetry\ProjectTelemetry.h>
#include <TraceLoggingActivity.h>
// JsonCpp
#include <json.h>

View File

@@ -36,7 +36,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
HANDLE _outPipe{ INVALID_HANDLE_VALUE }; // The pipe for reading output from
HANDLE _signalPipe{ INVALID_HANDLE_VALUE };
DWORD _outputThreadId{};
HANDLE _hOutputThread{ INVALID_HANDLE_VALUE };
HANDLE _hOutputThread{ nullptr };
PROCESS_INFORMATION _piConhost{};
guid _guid{}; // A "unique" session identifier for connected client
bool _closing{};

View File

@@ -202,8 +202,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// enabled and no background image is present.
// - Avoids image flickering and acrylic brush redraw if settings are changed
// but the appropriate brush is still in place.
// - Does not apply background color; _BackgroundColorChanged must be called
// to do so.
// - Does not apply background color outside of acrylic mode;
// _BackgroundColorChanged must be called to do so.
// Arguments:
// - <none>
// Return Value:
@@ -223,6 +223,18 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
acrylic.BackgroundSource(Media::AcrylicBackgroundSource::HostBackdrop);
}
// see GH#1082: Initialize background color so we don't get a
// fade/flash when _BackgroundColorChanged is called
uint32_t color = _settings.DefaultBackground();
winrt::Windows::UI::Color bgColor{};
bgColor.R = GetRValue(color);
bgColor.G = GetGValue(color);
bgColor.B = GetBValue(color);
bgColor.A = 255;
acrylic.FallbackColor(bgColor);
acrylic.TintColor(bgColor);
// Apply brush settings
acrylic.TintOpacity(_settings.TintOpacity());
@@ -254,7 +266,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|| imageSource.UriSource().RawUri() != imageUri.RawUri())
{
// Note that BitmapImage handles the image load asynchronously,
// which is especially important since the image
// which is especially important since the image
// may well be both large and somewhere out on the
// internet.
Media::Imaging::BitmapImage image(imageUri);
@@ -347,12 +359,22 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Don't let anyone else do something to the buffer.
auto lock = _terminal->LockForWriting();
if (_connection != nullptr)
// Clear out the cursor timer, so it doesn't trigger again on us once we're destructed.
if (_cursorTimer)
{
_cursorTimer.value().Stop();
_cursorTimer = std::nullopt;
}
if (_connection)
{
_connection.Close();
}
_renderer->TriggerTeardown();
if (_renderer)
{
_renderer->TriggerTeardown();
}
_swapChainPanel = nullptr;
_root = nullptr;
@@ -682,9 +704,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse)
{
// Ignore mouse events while the terminal does not have focus.
// This prevents the user from selecting and copying text if they
// click inside the current tab to refocus the terminal window.
// Ignore mouse events while the terminal does not have focus.
// This prevents the user from selecting and copying text if they
// click inside the current tab to refocus the terminal window.
if (!_focused)
{
args.Handled(true);
@@ -958,6 +980,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void TermControl::_GotFocusHandler(Windows::Foundation::IInspectable const& /* sender */,
RoutedEventArgs const& /* args */)
{
if (_closing)
{
return;
}
_focused = true;
if (_cursorTimer.has_value())
@@ -972,6 +998,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void TermControl::_LostFocusHandler(Windows::Foundation::IInspectable const& /* sender */,
RoutedEventArgs const& /* args */)
{
if (_closing)
{
return;
}
_focused = false;
if (_cursorTimer.has_value())
@@ -1043,7 +1073,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void TermControl::_BlinkCursor(Windows::Foundation::IInspectable const& /* sender */,
Windows::Foundation::IInspectable const& /* e */)
{
if (!_terminal->IsCursorBlinkingAllowed() && _terminal->IsCursorVisible())
if ((_closing) || (!_terminal->IsCursorBlinkingAllowed() && _terminal->IsCursorVisible()))
{
return;
}
@@ -1179,7 +1209,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - Scrolls the viewport of the terminal and updates the scroll bar accordingly
// Arguments:
// - viewTop: the viewTop to scroll to
// The difference between this function and ScrollViewport is that this one also
// The difference between this function and ScrollViewport is that this one also
// updates the _scrollBar after the viewport scroll. The reason _scrollBar is not updated in
// ScrollViewport is because ScrollViewport is being called by _ScrollbarChangeHandler
void TermControl::KeyboardScrollViewport(int viewTop)
@@ -1334,7 +1364,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// don't necessarily include that state.
// Return Value:
// - a KeyModifiers value with flags set for each key that's pressed.
Settings::KeyModifiers TermControl::_GetPressedModifierKeys() const{
Settings::KeyModifiers TermControl::_GetPressedModifierKeys() const
{
CoreWindow window = CoreWindow::GetForCurrentThread();
// DONT USE
// != CoreVirtualKeyStates::None
@@ -1356,6 +1387,17 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
(shift ? Settings::KeyModifiers::Shift : Settings::KeyModifiers::None) };
}
// Method Description:
// - Returns true if this control should close when its connection is closed.
// Arguments:
// - <none>
// Return Value:
// - true iff the control should close when the connection is closed.
bool TermControl::ShouldCloseOnExit() const noexcept
{
return _settings.CloseOnExit();
}
// Method Description:
// - Gets the corresponding viewport terminal position for the cursor
// by excluding the padding and normalizing with the font size.
@@ -1373,11 +1415,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
static_cast<SHORT>(cursorPosition.X - _root.Padding().Left),
static_cast<SHORT>(cursorPosition.Y - _root.Padding().Top)
};
const auto fontSize = _actualFont.GetSize();
FAIL_FAST_IF(fontSize.X == 0);
FAIL_FAST_IF(fontSize.Y == 0);
// Normalize to terminal coordinates by using font size
terminalPosition.X /= fontSize.X;
terminalPosition.Y /= fontSize.Y;

View File

@@ -41,6 +41,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
hstring Title();
void CopySelectionToClipboard(bool trimTrailingWhitespace);
void Close();
bool ShouldCloseOnExit() const noexcept;
void ScrollViewport(int viewTop);
void KeyboardScrollViewport(int viewTop);

View File

@@ -33,6 +33,7 @@ namespace Microsoft.Terminal.TerminalControl
String Title { get; };
void CopySelectionToClipboard(Boolean trimTrailingWhitespace);
void Close();
Boolean ShouldCloseOnExit { get; };
void ScrollViewport(Int32 viewTop);
void KeyboardScrollViewport(Int32 viewTop);

View File

@@ -14,15 +14,11 @@
using namespace winrt::Microsoft::Terminal::Settings;
using namespace Microsoft::Terminal::Core;
using namespace Microsoft::Console;
using namespace Microsoft::Console::Render;
using namespace Microsoft::Console::Types;
using namespace Microsoft::Console::VirtualTerminal;
static constexpr short _ClampToShortMax(int value, short min)
{
return static_cast<short>(std::clamp(value, static_cast<int>(min), SHRT_MAX));
}
static std::wstring _KeyEventsToText(std::deque<std::unique_ptr<IInputEvent>>& inEventsToWrite)
{
std::wstring wstr = L"";
@@ -70,7 +66,8 @@ void Terminal::Create(COORD viewportSize, SHORT scrollbackLines, IRenderTarget&
{
_mutableViewport = Viewport::FromDimensions({ 0,0 }, viewportSize);
_scrollbackLines = scrollbackLines;
const COORD bufferSize { viewportSize.X, _ClampToShortMax(viewportSize.Y + scrollbackLines, 1) };
const COORD bufferSize { viewportSize.X,
Utils::ClampToShortMax(viewportSize.Y + scrollbackLines, 1) };
const TextAttribute attr{};
const UINT cursorSize = 12;
_buffer = std::make_unique<TextBuffer>(bufferSize, attr, cursorSize, renderTarget);
@@ -84,9 +81,10 @@ void Terminal::Create(COORD viewportSize, SHORT scrollbackLines, IRenderTarget&
void Terminal::CreateFromSettings(winrt::Microsoft::Terminal::Settings::ICoreSettings settings,
Microsoft::Console::Render::IRenderTarget& renderTarget)
{
const COORD viewportSize{ _ClampToShortMax(settings.InitialCols(), 1), _ClampToShortMax(settings.InitialRows(), 1) };
const COORD viewportSize{ Utils::ClampToShortMax(settings.InitialCols(), 1),
Utils::ClampToShortMax(settings.InitialRows(), 1) };
// TODO:MSFT:20642297 - Support infinite scrollback here, if HistorySize is -1
Create(viewportSize, _ClampToShortMax(settings.HistorySize(), 0), renderTarget);
Create(viewportSize, Utils::ClampToShortMax(settings.HistorySize(), 0), renderTarget);
UpdateSettings(settings);
}
@@ -499,11 +497,11 @@ void Terminal::_InitializeColorTable()
{
gsl::span<COLORREF> tableView = { &_colorTable[0], gsl::narrow<ptrdiff_t>(_colorTable.size()) };
// First set up the basic 256 colors
::Microsoft::Console::Utils::Initialize256ColorTable(tableView);
Utils::Initialize256ColorTable(tableView);
// Then use fill the first 16 values with the Campbell scheme
::Microsoft::Console::Utils::InitializeCampbellColorTable(tableView);
Utils::InitializeCampbellColorTable(tableView);
// Then make sure all the values have an alpha of 255
::Microsoft::Console::Utils::SetColorTableAlpha(tableView, 0xff);
Utils::SetColorTableAlpha(tableView, 0xff);
}
// Method Description:

View File

@@ -4,12 +4,14 @@
#include "pch.h"
#include "AppHost.h"
#include "../types/inc/Viewport.hpp"
#include "../types/inc/Utils.hpp"
using namespace winrt::Windows::UI;
using namespace winrt::Windows::UI::Composition;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Hosting;
using namespace winrt::Windows::Foundation::Numerics;
using namespace ::Microsoft::Console;
using namespace ::Microsoft::Console::Types;
// The tabs are 34.8px tall. This is their default height - we're not
@@ -131,8 +133,10 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, const RECT proposedRect)
auto initialSize = _app.GetLaunchDimensions(dpix);
const short _currentWidth = gsl::narrow<short>(ceil(initialSize.X));
const short _currentHeight = gsl::narrow<short>(ceil(initialSize.Y));
const short _currentWidth = Utils::ClampToShortMax(
static_cast<long>(ceil(initialSize.X)), 1);
const short _currentHeight = Utils::ClampToShortMax(
static_cast<long>(ceil(initialSize.Y)), 1);
// Create a RECT from our requested client size
auto nonClient = Viewport::FromDimensions({ _currentWidth,
@@ -172,8 +176,8 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, const RECT proposedRect)
const COORD origin{ gsl::narrow<short>(proposedRect.left),
gsl::narrow<short>(proposedRect.top) };
const COORD dimensions{ gsl::narrow<short>(adjustedWidth),
gsl::narrow<short>(adjustedHeight) };
const COORD dimensions{ Utils::ClampToShortMax(adjustedWidth, 1),
Utils::ClampToShortMax(adjustedHeight, 1) };
const auto newPos = Viewport::FromDimensions(origin, dimensions);

View File

@@ -6,7 +6,7 @@
</ClCompile>
<Link>
<ProgramDatabaseFile>$(OutDir)$(TargetName)FullPDB.pdb</ProgramDatabaseFile>
<AdditionalDependencies>onecore_apiset.lib;version.lib;dwrite.lib;dxgi.lib;d2d1.lib;d3d11.lib;shcore.lib;uxtheme.lib;dwmapi.lib;winmm.lib;pathcch.lib;propsys.lib;uiautomationcore.lib;Shlwapi.lib;ntdll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>onecore_apiset.lib;dwrite.lib;dxgi.lib;d2d1.lib;d3d11.lib;shcore.lib;uxtheme.lib;dwmapi.lib;winmm.lib;pathcch.lib;propsys.lib;uiautomationcore.lib;Shlwapi.lib;ntdll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<!--
There's a property that dictates which libraries are linked by default: MinimalCoreWin.
When it's enabled, only a sparing few libraries are injected into Link.AdditionalDependencies.

View File

@@ -181,7 +181,7 @@ HRESULT PtySignalInputThread::Start() noexcept
0,
&dwThreadId);
RETURN_LAST_ERROR_IF(hThread == INVALID_HANDLE_VALUE);
RETURN_LAST_ERROR_IF_NULL(hThread);
_hThread.reset(hThread);
_dwThreadId = dwThreadId;

View File

@@ -173,7 +173,7 @@ HRESULT VtInputThread::Start()
0,
&dwThreadId);
RETURN_LAST_ERROR_IF(hThread == INVALID_HANDLE_VALUE);
RETURN_LAST_ERROR_IF_NULL(hThread);
_hThread.reset(hThread);
_dwThreadId = dwThreadId;

View File

@@ -80,7 +80,7 @@
</ClCompile>
<Link>
<AllowIsolation>true</AllowIsolation>
<!--AdditionalManifestDependencies>type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'</AdditionalManifestDependencies-->
<AdditionalManifestDependencies>type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'</AdditionalManifestDependencies>
</Link>
</ItemDefinitionGroup>
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->

View File

@@ -55,7 +55,7 @@ class InputTests
TEST_METHOD(TestMouseHorizWheelReadConsoleNoMouseInput);
TEST_METHOD(TestMouseWheelReadConsoleInputQuickEdit);
TEST_METHOD(TestMouseHorizWheelReadConsoleInputQuickEdit);
TEST_METHOD(RawReadUnpacksCoalsescedInputRecords);
TEST_METHOD(RawReadUnpacksCoalescedInputRecords);
BEGIN_TEST_METHOD(TestVtInputGeneration)
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
@@ -671,7 +671,7 @@ void InputTests::TestVtInputGeneration()
VERIFY_ARE_EQUAL(rgInputRecords[2].Event.KeyEvent.uChar.UnicodeChar, L'A');
}
void InputTests::RawReadUnpacksCoalsescedInputRecords()
void InputTests::RawReadUnpacksCoalescedInputRecords()
{
DWORD mode = 0;
HANDLE hIn = GetStdInputHandle();
@@ -683,6 +683,10 @@ void InputTests::RawReadUnpacksCoalsescedInputRecords()
WI_ClearFlag(mode, ENABLE_LINE_INPUT);
SetConsoleMode(hIn, mode);
// flush input queue before attempting to add new events and check
// in case any are leftover from previous tests
VERIFY_WIN32_BOOL_SUCCEEDED(FlushConsoleInputBuffer(hIn));
INPUT_RECORD record;
record.EventType = KEY_EVENT;
record.Event.KeyEvent.bKeyDown = TRUE;

View File

@@ -278,8 +278,6 @@ class DbcsTests
END_TEST_METHOD()
};
HANDLE hScreen = INVALID_HANDLE_VALUE;
bool DbcsTests::DbcsTestSetup()
{
return true;

View File

@@ -12,47 +12,10 @@
#include <windows.h>
#include "resource.h"
#include "conhost.rcv"
#ifndef EXTERNAL_BUILD
#include "conhost.rcv"
#include <ntverp.h>
#include <common.ver>
#else
//////////
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1, 0, 0, 2019
PRODUCTVERSION 1, 0, 0, 2019
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEOS VOS__WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "FileDescription", VER_FILEDESCRIPTION_STR
VALUE "FileVersion", "1.0.0-20190111-APPX dev/main-07d84ed8+dirty\0"
VALUE "InternalName", VER_INTERNALNAME_STR
VALUE "OriginalFilename", "OpenConsole.exe"
VALUE "ProductName", "Confans"
VALUE "ProductVersion", "1.0.0-20190111-APPX\0"
END
END
BLOCK "VarFileInfo"
BEGIN
/* The following line should only be modified for localized versions. */
/* It consists of any number of WORD,WORD pairs, with each pair */
/* describing a language,codepage combination supported by the file. */
/* */
/* For example, a file might have values "0x409,1252" indicating that it */
/* supports English language (0x409) in the Windows ANSI codepage (1252). */
VALUE "Translation", 0x409, 1252
END
END
//////////
#endif
IDI_APPICON ICON "..\\..\\..\\res\\console.ico"

View File

@@ -86,4 +86,127 @@ class Utf16ParserTests
VERIFY_ARE_EQUAL(result.at(0).at(i), SunglassesEmoji.at(i));
}
}
const std::wstring_view Replacement{ &UNICODE_REPLACEMENT, 1 };
TEST_METHOD(ParseNextLeadOnly)
{
std::wstring wstr{ SunglassesEmoji.at(0) };
const auto expected = Replacement;
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextTrailOnly)
{
std::wstring wstr{ SunglassesEmoji.at(1) };
const auto expected = Replacement;
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextSingleOnly)
{
std::wstring wstr{ CyrillicChar.at(0) };
const auto expected = std::wstring_view{ CyrillicChar.data(), CyrillicChar.size() };
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextLeadLead)
{
std::wstring wstr{ SunglassesEmoji.at(0) };
wstr += SunglassesEmoji.at(0);
const auto expected = Replacement;
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextLeadTrail)
{
std::wstring wstr{ SunglassesEmoji.at(0) };
wstr += SunglassesEmoji.at(1);
const auto expected = std::wstring_view{ SunglassesEmoji.data(), SunglassesEmoji.size() };
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextTrailTrail)
{
std::wstring wstr{ SunglassesEmoji.at(1) };
wstr += SunglassesEmoji.at(1);
const auto expected = Replacement;
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextLeadSingle)
{
std::wstring wstr{ SunglassesEmoji.at(0) };
wstr += LatinChar.at(0);
const auto expected = std::wstring_view{ LatinChar.data(), LatinChar.size() };
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextTrailSingle)
{
std::wstring wstr{ SunglassesEmoji.at(1) };
wstr += LatinChar.at(0);
const auto expected = std::wstring_view{ LatinChar.data(), LatinChar.size() };
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextLeadLeadTrail)
{
std::wstring wstr{ SunglassesEmoji.at(0) };
wstr += SunglassesEmoji.at(0);
wstr += SunglassesEmoji.at(1);
const auto expected = std::wstring_view{ SunglassesEmoji.data(), SunglassesEmoji.size() };
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextTrailLeadTrail)
{
std::wstring wstr{ SunglassesEmoji.at(1) };
wstr += SunglassesEmoji.at(0);
wstr += SunglassesEmoji.at(1);
const auto expected = std::wstring_view{ SunglassesEmoji.data(), SunglassesEmoji.size() };
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextSingleLeadTrail)
{
std::wstring wstr{ GaelicChar.at(0) };
wstr += SunglassesEmoji.at(0);
wstr += SunglassesEmoji.at(1);
const auto expected = std::wstring_view{ GaelicChar.data(), GaelicChar.size() };
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
};

View File

@@ -37,6 +37,7 @@
#include <iterator>
#include <math.h>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <filesystem>
#include <functional>

View File

@@ -543,4 +543,38 @@ namespace WEX::TestExecution
}
};
template<>
class VerifyOutputTraits < std::wstring_view >
{
public:
static WEX::Common::NoThrowString ToString(const std::wstring_view& view)
{
return WEX::Common::NoThrowString(view.data(), gsl::narrow<int>(view.size()));
}
};
template<>
class VerifyCompareTraits < std::wstring_view, std::wstring_view >
{
public:
static bool AreEqual(const std::wstring_view& expected, const std::wstring_view& actual)
{
return expected == actual;
}
static bool AreSame(const std::wstring_view& expected, const std::wstring_view& actual)
{
return expected.data() == actual.data();
}
static bool IsLessThan(const std::wstring_view&, const std::wstring_view&) = delete;
static bool IsGreaterThan(const std::wstring_view&, const std::wstring_view&) = delete;
static bool IsNull(const std::wstring_view& object)
{
return object.size() == 0;
}
};
}

View File

@@ -9,6 +9,8 @@
#pragma hdrstop
using namespace Microsoft::Console::Interactivity;
UINT VTRedirMapVirtualKeyW(_In_ UINT uCode, _In_ UINT uMapType)
{
return ServiceLocator::LocateInputServices()->MapVirtualKeyW(uCode, uMapType);

View File

@@ -17,6 +17,7 @@
#define DEFAULT_COLOR_ATTRIBUTE (0xC)
using namespace Microsoft::Console::Render;
using namespace Microsoft::Console::Interactivity;
using namespace Microsoft::Console::Interactivity::OneCore;
BgfxEngine::BgfxEngine(PVOID SharedViewBase, LONG DisplayHeight, LONG DisplayWidth, LONG FontWidth, LONG FontHeight)

View File

@@ -21,10 +21,11 @@
extern void LockConsole();
extern void UnlockConsole();
using namespace Microsoft::Console::Render;
using namespace Microsoft::Console::Interactivity::OneCore;
ConIoSrvComm::ConIoSrvComm() :
_inputPipeThreadHandle(INVALID_HANDLE_VALUE),
_inputPipeThreadHandle(nullptr),
_pipeReadHandle(INVALID_HANDLE_VALUE),
_pipeWriteHandle(INVALID_HANDLE_VALUE),
_alpcClientCommunicationPort(INVALID_HANDLE_VALUE),
@@ -40,11 +41,11 @@ ConIoSrvComm::ConIoSrvComm() :
ConIoSrvComm::~ConIoSrvComm()
{
// Cancel pending IOs on the input thread that might get us stuck.
if (INVALID_HANDLE_VALUE != _inputPipeThreadHandle)
if (_inputPipeThreadHandle)
{
LOG_IF_WIN32_BOOL_FALSE(CancelSynchronousIo(_inputPipeThreadHandle));
CloseHandle(_inputPipeThreadHandle);
_inputPipeThreadHandle = INVALID_HANDLE_VALUE;
_inputPipeThreadHandle = nullptr;
}
// Free any handles we might have open.
@@ -242,7 +243,7 @@ NTSTATUS ConIoSrvComm::EnsureConnection()
VOID ConIoSrvComm::ServiceInputPipe()
{
// Save off a handle to the thread that is coming in here in case it gets blocked and we need to tear down.
THROW_HR_IF(E_NOT_VALID_STATE, INVALID_HANDLE_VALUE != _inputPipeThreadHandle); // We can't store two of them, so it's invalid if there are two.
THROW_HR_IF(E_NOT_VALID_STATE, !!_inputPipeThreadHandle); // We can't store two of them, so it's invalid if there are two.
THROW_IF_WIN32_BOOL_FALSE(DuplicateHandle(GetCurrentProcess(),
GetCurrentThread(),
GetCurrentProcess(),

View File

@@ -13,6 +13,7 @@
#include "..\inc\ServiceLocator.hpp"
using namespace Microsoft::Console::Interactivity;
using namespace Microsoft::Console::Interactivity::OneCore;
@@ -53,6 +54,8 @@ DWORD WINAPI ConsoleInputThreadProcOneCore(LPVOID /*lpParam*/)
if (NT_SUCCESS(Status))
{
globals.getConsoleInformation().GetActiveOutputBuffer().RefreshFontWithRenderer();
globals.ntstatusConsoleInputInitStatus = Status;
globals.hConsoleInputInitEvent.SetEvent();

View File

@@ -299,10 +299,6 @@ void Menu::s_ShowPropertiesDialog(HWND const hwnd, BOOL const Defaults)
{
delete[] allocatedLinkTitle;
}
if (StateInfo.VersionString != nullptr)
{
delete[] StateInfo.VersionString;
}
}
[[nodiscard]]
@@ -388,47 +384,6 @@ HRESULT Menu::s_GetConsoleState(CONSOLE_STATE_INFO * const pStateInfo)
pStateInfo->DefaultBackground = gci.GetDefaultBackgroundColor();
pStateInfo->TerminalScrolling = gci.IsTerminalScrolling();
{
HMODULE hSelf = GetModuleHandle(nullptr);
WCHAR moduleFilename[MAX_PATH];
GetModuleFileNameW(hSelf, &moduleFilename[0], MAX_PATH);
DWORD versionLength = GetFileVersionInfoSizeExW(0, moduleFilename, 0);
VS_FIXEDFILEINFO* vinfo{nullptr};
LPWSTR vi{nullptr};
if (versionLength > 0)
{
void* versionData = new uint8_t[versionLength];
if (GetFileVersionInfoExW(0, moduleFilename, 0, versionLength, versionData))
{
UINT vsize;
VerQueryValueW(versionData, L"\\", (LPVOID*)&vinfo, &vsize);
VerQueryValueW(versionData, L"\\StringFileInfo\\040904e4\\FileVersion", (LPVOID*)&vi, &vsize);
//wcscpy_s(pStateInfo->VersionString, 1024, L"1.0.0-get-rekt-son");
}
}
pStateInfo->VersionString = new(std::nothrow) wchar_t[1024]{UNICODE_NULL};
if (nullptr != vinfo)
{
WORD maj, min, rev, bui;
maj = (vinfo->dwFileVersionMS & 0xFFFF0000) >> 16;
min = (vinfo->dwFileVersionMS & 0x0000FFFF);
rev = (vinfo->dwFileVersionLS & 0xFFFF0000) >> 16;
bui = (vinfo->dwFileVersionLS & 0x0000FFFF);
StringCchPrintfW(pStateInfo->VersionString, 1024, L"Version %s (%d.%d.%d.%d)",
vi ? vi : L"UNKNOWN", maj, min, rev, bui);
}
else
{
StringCchPrintfW(pStateInfo->VersionString, 1024, L"Unknown Version (get rekt scrub)!");
}
}
// end console v2 properties
return S_OK;
}

View File

@@ -475,45 +475,6 @@ UINT CALLBACK PropSheetPageProc(_In_ HWND hWnd, _In_ UINT uMsg, _Inout_ LPPROPSH
return 1;
}
INT_PTR WINAPI VersionDlgProc(const HWND hDlg, const UINT wMsg, const WPARAM /*wParam*/, const LPARAM lParam)
{
static bool fHaveInitialized = false;
switch (wMsg) {
case WM_INITDIALOG:
SetDlgItemTextW(hDlg, IDD_VERSION_STRING, gpStateInfo->VersionString);
fHaveInitialized = true;
return TRUE;
case WM_NOTIFY:
{
const PSHNOTIFY * const pshn = (LPPSHNOTIFY)lParam;
switch (pshn->hdr.code) {
case PSN_APPLY:
EndDlgPage(hDlg, !pshn->lParam);
return TRUE;
case PSN_KILLACTIVE:
{
// Fake the dialog proc into thinking the edit control just
// lost focus so it'll update properly
int item = GetDlgCtrlID(GetFocus());
if (item)
{
SendMessage(hDlg, WM_COMMAND, MAKELONG(item, EN_KILLFOCUS), 0);
}
return TRUE;
}
}
}
case WM_VSCROLL:
// Fake the dialog proc into thinking the edit control just
// lost focus so it'll update properly
SendMessage(hDlg, WM_COMMAND, MAKELONG((GetDlgCtrlID((HWND)lParam) - 1), EN_KILLFOCUS), 0);
return TRUE;
}
return false;
}
BOOL PopulatePropSheetPageArray(_Out_writes_(cPsps) PROPSHEETPAGE *pPsp, const size_t cPsps, const BOOL fRegisterCallbacks)
{
BOOL fRet = (cPsps == NUMBER_OF_PAGES);
@@ -527,9 +488,6 @@ BOOL PopulatePropSheetPageArray(_Out_writes_(cPsps) PROPSHEETPAGE *pPsp, const s
PROPSHEETPAGE* const pLayoutPage = &(pPsp[LAYOUT_PAGE_INDEX]);
PROPSHEETPAGE* const pColorsPage = &(pPsp[COLORS_PAGE_INDEX]);
PROPSHEETPAGE* const pTerminalPage = &(pPsp[TERMINAL_PAGE_INDEX]);
#ifdef EXTERNAL_BUILD
PROPSHEETPAGE* const pVersionPage = &(pPsp[VERSION_PAGE_INDEX]);
#endif
pOptionsPage->dwSize = sizeof(PROPSHEETPAGE);
pOptionsPage->hInstance = ghInstance;
@@ -575,15 +533,6 @@ BOOL PopulatePropSheetPageArray(_Out_writes_(cPsps) PROPSHEETPAGE *pPsp, const s
pTerminalPage->dwFlags = PSP_DEFAULT;
}
#ifdef EXTERNAL_BUILD
pVersionPage->dwSize = sizeof(PROPSHEETPAGE);
pVersionPage->hInstance = ghInstance;
pVersionPage->pszTemplate = MAKEINTRESOURCE(DID_VERSION);
pVersionPage->pfnDlgProc = VersionDlgProc;
pVersionPage->lParam = VERSION_PAGE_INDEX;
pVersionPage->dwFlags = PSP_DEFAULT;
#endif
// Register callbacks if requested (used for file property sheet purposes)
if (fRegisterCallbacks)
{

View File

@@ -239,12 +239,6 @@ const unsigned int FONT_PAGE_INDEX = 1;
const unsigned int LAYOUT_PAGE_INDEX = 2;
const unsigned int COLORS_PAGE_INDEX = 3;
const unsigned int TERMINAL_PAGE_INDEX = 4;
const unsigned int VERSION_PAGE_INDEX = 5;
// number of property sheet pages
static const int V1_NUMBER_OF_PAGES = 4;
#ifndef EXTERNAL_BUILD
static const int NUMBER_OF_PAGES = 5;
#else
static const int NUMBER_OF_PAGES = 6;
#endif

View File

@@ -620,22 +620,6 @@ BEGIN
IDD_HELP_TERMINAL, "SysLink", WS_TABSTOP, 10, 225, 200, 10
END
#ifdef EXTERNAL_BUILD
// This is the template for the version dialog
DID_VERSION DIALOG 0, 0, 245, 226
CAPTION " Version "
STYLE WS_VISIBLE | WS_CAPTION | WS_CHILD | DS_MODALFRAME
FONT 8,"MS Shell Dlg"
BEGIN
// GROUPBOX text, id, x, y, width, height [, style [, extended-style]]
// CONTROL text, id, class, style, x, y, width, height [, extended-style]
LTEXT "You will, I am certain, be wondering what this tab does.\nI wonder the same!",
IDD_VERSION_STRING, 10, 10, 225, 206, WS_TABSTOP
END
#endif
//
// Strings

View File

@@ -157,11 +157,6 @@ Revision History:
#define IDD_TERMINAL_CURSOR_BLUE 631
#define IDD_HELP_TERMINAL 632
#ifdef EXTERNAL_BUILD
#define DID_VERSION 700
#define IDD_VERSION_STRING 701
#endif
#define BM_TRUETYPE_ICON 1000

View File

@@ -22,7 +22,7 @@ using namespace Microsoft::Console::Render;
HRESULT GdiEngine::StartPaint() noexcept
{
// If we have no handle, we don't need to paint. Return quickly.
RETURN_HR_IF(S_FALSE, INVALID_HANDLE_VALUE == _hwndTargetWindow);
RETURN_HR_IF(S_FALSE, !_IsWindowValid());
// If we're already painting, we don't need to paint. Return quickly.
RETURN_HR_IF(S_FALSE, _fPaintStarted);

View File

@@ -339,19 +339,9 @@ HRESULT WddmConEngine::UpdateDrawingBrushes(COLORREF const /*colorForeground*/,
}
[[nodiscard]]
HRESULT WddmConEngine::UpdateFont(const FontInfoDesired& /*pfiFontInfoDesired*/, FontInfo& fiFontInfo) noexcept
HRESULT WddmConEngine::UpdateFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo) noexcept
{
COORD coordSize = {0};
LOG_IF_FAILED(GetFontSize(&coordSize));
fiFontInfo.SetFromEngine(fiFontInfo.GetFaceName(),
fiFontInfo.GetFamily(),
fiFontInfo.GetWeight(),
fiFontInfo.IsTrueTypeFont(),
coordSize,
coordSize);
return S_OK;
return GetProposedFont(fiFontInfoDesired, fiFontInfo, USER_DEFAULT_SCREEN_DPI);
}
[[nodiscard]]
@@ -374,10 +364,20 @@ HRESULT WddmConEngine::UpdateViewport(const SMALL_RECT /*srNewViewport*/) noexce
}
[[nodiscard]]
HRESULT WddmConEngine::GetProposedFont(const FontInfoDesired& /*pfiFontInfoDesired*/,
FontInfo& /*pfiFontInfo*/,
HRESULT WddmConEngine::GetProposedFont(const FontInfoDesired& /*fiFontInfoDesired*/,
FontInfo& fiFontInfo,
int const /*iDpi*/) noexcept
{
COORD coordSize = { 0 };
LOG_IF_FAILED(GetFontSize(&coordSize));
fiFontInfo.SetFromEngine(fiFontInfo.GetFaceName(),
fiFontInfo.GetFamily(),
fiFontInfo.GetWeight(),
fiFontInfo.IsTrueTypeFont(),
coordSize,
coordSize);
return S_OK;
}

View File

@@ -20,16 +20,12 @@ VtConsole::VtConsole(PipeReadCallback const pfnReadCallback,
bool const fHeadless,
bool const fUseConpty,
COORD const initialSize) :
_signalPipe(INVALID_HANDLE_VALUE),
_outPipe(INVALID_HANDLE_VALUE),
_inPipe(INVALID_HANDLE_VALUE),
_dwOutputThreadId(0)
_pfnReadCallback(pfnReadCallback),
_fHeadless(fHeadless),
_fUseConPty(fUseConpty),
_lastDimensions(initialSize)
{
_pfnReadCallback = pfnReadCallback;
_fHeadless = fHeadless;
_fUseConPty = fUseConpty;
_lastDimensions = initialSize;
THROW_IF_NULL_ALLOC(pfnReadCallback);
}
void VtConsole::spawn()

View File

@@ -57,9 +57,9 @@ private:
PROCESS_INFORMATION _piPty;
PROCESS_INFORMATION _piClient;
HANDLE _outPipe;
HANDLE _inPipe;
HANDLE _signalPipe;
HANDLE _outPipe = INVALID_HANDLE_VALUE;
HANDLE _inPipe = INVALID_HANDLE_VALUE;
HANDLE _signalPipe = INVALID_HANDLE_VALUE;
HPCON _hPC;
@@ -70,8 +70,8 @@ private:
PipeReadCallback _pfnReadCallback;
DWORD _dwOutputThreadId;
HANDLE _hOutputThread = INVALID_HANDLE_VALUE;
DWORD _dwOutputThreadId = 0;
HANDLE _hOutputThread = nullptr;
void _createPseudoConsole(const std::wstring& command);
void _createConptyManually(const std::wstring& command);

View File

@@ -4,6 +4,7 @@
#include "precomp.h"
#include "inc/Utf16Parser.hpp"
#include "unicode.hpp"
// Routine Description:
// - Finds the next single collection for the codepoint out of the given UTF-16 string information.
@@ -15,35 +16,41 @@
// - A view into the string given of just the next codepoint unit.
std::wstring_view Utf16Parser::ParseNext(std::wstring_view wstr)
{
size_t pos = 0;
size_t length = 0;
for (auto wch : wstr)
for (size_t pos = 0; pos < wstr.size(); ++pos)
{
const auto wch = wstr.at(pos);
// If it's a lead and followed directly by a trail, then return the pair.
// If it's not followed directly by the trail, go around again and seek forward.
if (IsLeadingSurrogate(wch))
{
length++;
// Try to find the next item... if it isn't there, we'll go around again.
const auto posNext = pos + 1;
if (posNext < wstr.size())
{
// If we found it and it's trailing, return the pair.
const auto wchNext = wstr.at(posNext);
if (IsTrailingSurrogate(wchNext))
{
return wstr.substr(pos, 2);
}
}
// If we missed either if in any way, we'll fall through and go around again searching for more.
}
// If it's just a trail at this point, go around again and seek forward.
else if (IsTrailingSurrogate(wch))
{
if (length != 0)
{
length++;
break;
}
else
{
pos++;
}
continue;
}
// If it's neither lead nor trail, then it's < U+10000 and it can be returned as a single wchar_t point.
else
{
length++;
break;
return wstr.substr(pos, 1);
}
}
return wstr.substr(pos, length);
// If we get all the way through and there's nothing valid, then this is just a replacement character as it was broken/garbage.
return std::wstring_view{ &UNICODE_REPLACEMENT, 1 };
}
// Routine Description:

View File

@@ -15,12 +15,14 @@ namespace Microsoft::Console::Utils
{
bool IsValidHandle(const HANDLE handle) noexcept;
short ClampToShortMax(const long value, const short min);
std::wstring GuidToString(const GUID guid);
GUID GuidFromString(const std::wstring wstr);
GUID CreateGuid();
std::wstring ColorToHexString(const COLORREF color);
COLORREF ColorFromHexString(const std::wstring wstr);
std::string ColorToHexString(const COLORREF color);
COLORREF ColorFromHexString(const std::string wstr);
void InitializeCampbellColorTable(gsl::span<COLORREF>& table);
void Initialize256ColorTable(gsl::span<COLORREF>& table);

View File

@@ -2,6 +2,7 @@
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<ItemGroup>
<ClCompile Include="UtilsTests.cpp" />
<ClCompile Include="UuidTests.cpp" />
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>

View File

@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "WexTestClass.h"
#include "..\..\inc\consoletaeftemplates.hpp"
#include "..\inc\utils.hpp"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
using namespace Microsoft::Console::Utils;
class UtilsTests
{
TEST_CLASS(UtilsTests);
TEST_METHOD(TestClampToShortMax)
{
const short min = 1;
// Test outside the lower end of the range
const short minExpected = min;
auto minActual = ClampToShortMax(0, min);
VERIFY_ARE_EQUAL(minExpected, minActual);
// Test negative numbers
const short negativeExpected = min;
auto negativeActual = ClampToShortMax(-1, min);
VERIFY_ARE_EQUAL(negativeExpected, negativeActual);
// Test outside the upper end of the range
const short maxExpected = SHRT_MAX;
auto maxActual = ClampToShortMax(50000, min);
VERIFY_ARE_EQUAL(maxExpected, maxActual);
// Test within the range
const short withinRangeExpected = 100;
auto withinRangeActual = ClampToShortMax(withinRangeExpected, min);
VERIFY_ARE_EQUAL(withinRangeExpected, withinRangeActual);
}
};

View File

@@ -15,6 +15,7 @@ DLLDEF =
SOURCES = \
$(SOURCES) \
UuidTests.cpp \
UtilsTests.cpp \
DefaultResource.rc \
INCLUDES = \

View File

@@ -6,6 +6,20 @@
using namespace Microsoft::Console;
// Function Description:
// - Clamps a long in between `min` and `SHRT_MAX`
// Arguments:
// - value: the value to clamp
// - min: the minimum value to clamp to
// Return Value:
// - The clamped value as a short.
short Utils::ClampToShortMax(const long value, const short min)
{
return static_cast<short>(std::clamp(value,
static_cast<long>(min),
static_cast<long>(SHRT_MAX)));
}
// Function Description:
// - Creates a String representation of a guid, in the format
// "{12345678-ABCD-EF12-3456-7890ABCDEF12}"
@@ -60,31 +74,33 @@ GUID Utils::CreateGuid()
// - color: the COLORREF to create the string for
// Return Value:
// - a string representation of the color
std::wstring Utils::ColorToHexString(const COLORREF color)
std::string Utils::ColorToHexString(const COLORREF color)
{
std::wstringstream ss;
ss << L"#" << std::uppercase << std::setfill(L'0') << std::hex;
ss << std::setw(2) << GetRValue(color);
ss << std::setw(2) << GetGValue(color);
ss << std::setw(2) << GetBValue(color);
std::stringstream ss;
ss << "#" << std::uppercase << std::setfill('0') << std::hex;
// Force the compiler to promote from byte to int. Without it, the
// stringstream will try to write the components as chars
ss << std::setw(2) << static_cast<int>(GetRValue(color));
ss << std::setw(2) << static_cast<int>(GetGValue(color));
ss << std::setw(2) << static_cast<int>(GetBValue(color));
return ss.str();
}
// Function Description:
// - Parses a color from a string. The string should be in the format "#RRGGBB"
// Arguments:
// - wstr: a string representation of the COLORREF to parse
// - str: a string representation of the COLORREF to parse
// Return Value:
// - A COLORREF if the string could successfully be parsed. If the string is not
// the correct format, throws E_INVALIDARG
COLORREF Utils::ColorFromHexString(const std::wstring wstr)
COLORREF Utils::ColorFromHexString(const std::string str)
{
THROW_HR_IF(E_INVALIDARG, wstr.size() < 7 || wstr.size() >= 8);
THROW_HR_IF(E_INVALIDARG, wstr[0] != L'#');
THROW_HR_IF(E_INVALIDARG, str.size() < 7 || str.size() >= 8);
THROW_HR_IF(E_INVALIDARG, str[0] != '#');
std::wstring rStr{ &wstr[1], 2 };
std::wstring gStr{ &wstr[3], 2 };
std::wstring bStr{ &wstr[5], 2 };
std::string rStr{ &str[1], 2 };
std::string gStr{ &str[3], 2 };
std::string bStr{ &str[5], 2 };
BYTE r = static_cast<BYTE>(std::stoul(rStr, nullptr, 16));
BYTE g = static_cast<BYTE>(std::stoul(gStr, nullptr, 16));

View File

@@ -2,21 +2,87 @@
# The project's root directory.
Set-Item -force -path "env:OpenConsoleRoot" -value "$PSScriptRoot\.."
#.SYNOPSIS
# Finds and imports a module that should be local to the project
#.PARAMETER ModuleName
# The name of the module to import
function Import-LocalModule
{
[CmdletBinding()]
param(
[parameter(Mandatory=$true, Position=0)]
[string]$Name
)
$ErrorActionPreference = 'Stop'
$modules_root = "$env:OpenConsoleRoot\.PowershellModules"
$local = $null -eq (Get-Module -Name $Name)
if (-not $local)
{
return
}
if (-not (Test-Path $modules_root)) {
New-Item $modules_root -ItemType 'directory' | Out-Null
}
if (-not (Test-Path "$modules_root\$Name")) {
Write-Verbose "$Name not downloaded -- downloading now"
$module = Find-Module "$Name"
$version = $module.Version
Write-Verbose "Saving $Name to $modules_root"
Save-Module -InputObject $module -Path $modules_root
Import-Module "$modules_root\$Name\$version\$Name.psd1"
} else {
Write-Verbose "$Name already downloaded"
$versions = Get-ChildItem "$modules_root\$Name" | Sort-Object
Get-ChildItem -Path $versions[0] "$Name.psd1" | Import-Module
}
}
#.SYNOPSIS
# Grabs all environment variable set after vcvarsall.bat is called and pulls
# them into the Powershell environment.
function Set-MsbuildDevEnvironment()
function Set-MsbuildDevEnvironment
{
$path = "$env:VS140COMNTOOLS\..\.."
pushd $path
cmd /c "vcvarsall.bat&set" | foreach {
if ($_ -match "=")
[CmdletBinding()]
param()
$ErrorActionPreference = 'Stop'
Import-LocalModule -Name 'VSSetup'
Write-Verbose 'Searching for VC++ instances'
$vsinfo = `
Get-VSSetupInstance -All `
| Select-VSSetupInstance `
-Latest -Product * `
-Require 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64'
$vspath = $vsinfo.InstallationPath
switch ($env:PROCESSOR_ARCHITECTURE) {
"amd64" { $arch = "x64" }
"x86" { $arch = "x86" }
default { throw "Unknown architecture: $switch" }
}
$vcvarsall = "$vspath\VC\Auxiliary\Build\vcvarsall.bat"
Write-Verbose 'Setting up environment variables'
cmd /c ("`"$vcvarsall`" $arch & set") | ForEach-Object {
if ($_ -match '=')
{
$s = $_.Split("=");
Set-Item -force -path "env:\$($s[0])" -value "$($s[1])"
}
}
popd
Write-Host "Dev environment variables set" -ForegroundColor Green
}