Merge branch 'dev/migrie/f/just-elevated-state-2' into dev/migrie/f/non-terminal-content-elevation-warning
This commit is contained in:
commit
b2796019f8
|
@ -909,6 +909,7 @@ namespace winrt::TerminalApp::implementation
|
|||
// actual file you wrote. So listen for that too.
|
||||
wil::FolderChangeEvents::FileName | wil::FolderChangeEvents::LastWriteTime,
|
||||
[this, settingsPath](wil::FolderChangeEvent, PCWSTR fileModified) {
|
||||
// TODO!
|
||||
static const std::filesystem::path statePath{ std::wstring_view{ ApplicationState::SharedInstance().FilePath() } };
|
||||
|
||||
const auto modifiedBasename = std::filesystem::path{ fileModified }.filename();
|
||||
|
|
|
@ -63,8 +63,10 @@ using namespace ::Microsoft::Terminal::Settings::Model;
|
|||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{
|
||||
ApplicationState::ApplicationState(std::filesystem::path path) noexcept :
|
||||
_path{ std::move(path) },
|
||||
ApplicationState::ApplicationState(std::filesystem::path sharedPath,
|
||||
std::filesystem::path elevatedPath) noexcept :
|
||||
_sharedPath{ std::move(sharedPath) },
|
||||
_elevatedPath{ std::move(elevatedPath) },
|
||||
_throttler{ std::chrono::seconds(1), [this]() { _write(); } }
|
||||
{
|
||||
_read();
|
||||
|
@ -87,33 +89,43 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
// Returns the state.json path on the disk.
|
||||
winrt::hstring ApplicationState::FilePath() const noexcept
|
||||
{
|
||||
return winrt::hstring{ _path.wstring() };
|
||||
return winrt::hstring{ _sharedPath.wstring() };
|
||||
}
|
||||
|
||||
// TODO!
|
||||
// Deserializes the state.json at _path into this ApplicationState.
|
||||
// * ANY errors during app state will result in the creation of a new empty state.
|
||||
// * ANY errors during runtime will result in changes being partially ignored.
|
||||
void ApplicationState::_read() const noexcept
|
||||
try
|
||||
{
|
||||
// Use the derived class's implementation of _readFileContents to get the
|
||||
// actual contents of the file.
|
||||
const auto data = _readFileContents().value_or(std::string{});
|
||||
if (data.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::string errs;
|
||||
std::unique_ptr<Json::CharReader> reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() };
|
||||
|
||||
Json::Value root;
|
||||
if (!reader->parse(data.data(), data.data() + data.size(), &root, &errs))
|
||||
const auto sharedData = _readSharedContents().value_or(std::string{});
|
||||
if (!sharedData.empty())
|
||||
{
|
||||
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs));
|
||||
}
|
||||
Json::Value root;
|
||||
if (!reader->parse(sharedData.data(), sharedData.data() + sharedData.size(), &root, &errs))
|
||||
{
|
||||
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs));
|
||||
}
|
||||
|
||||
FromJson(root);
|
||||
FromJson(root, FileSource::Shared);
|
||||
}
|
||||
if (::Microsoft::Console::Utils::IsElevated())
|
||||
{
|
||||
if (const auto elevatedData{ _readElevatedContents().value_or(std::string{}) }; !elevatedData.empty())
|
||||
{
|
||||
Json::Value root;
|
||||
if (!reader->parse(elevatedData.data(), elevatedData.data() + elevatedData.size(), &root, &errs))
|
||||
{
|
||||
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs));
|
||||
}
|
||||
|
||||
FromJson(root, FileSource::Local);
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_LOG()
|
||||
|
||||
|
@ -124,25 +136,22 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
void ApplicationState::_write() const noexcept
|
||||
try
|
||||
{
|
||||
Json::Value root{ this->ToJson() };
|
||||
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
const auto content = Json::writeString(wbuilder, root);
|
||||
|
||||
// Use the derived class's implementation of _writeFileContents to write the
|
||||
// file to disk.
|
||||
_writeFileContents(content);
|
||||
_writeSharedContents(Json::writeString(wbuilder, ToJson(FileSource::Shared)));
|
||||
_writeElevatedContents(Json::writeString(wbuilder, ToJson(FileSource::Local)));
|
||||
}
|
||||
CATCH_LOG()
|
||||
|
||||
// Returns the application-global ApplicationState object.
|
||||
Microsoft::Terminal::Settings::Model::ApplicationState ApplicationState::SharedInstance()
|
||||
{
|
||||
static auto state = winrt::make_self<ApplicationState>(GetBaseSettingsPath() / (::Microsoft::Console::Utils::IsElevated() ? elevatedStateFileName : stateFileName));
|
||||
static auto state = winrt::make_self<ApplicationState>(GetBaseSettingsPath() / stateFileName,
|
||||
GetBaseSettingsPath() / elevatedStateFileName);
|
||||
return *state;
|
||||
}
|
||||
|
||||
void ApplicationState::FromJson(const Json::Value& root) const noexcept
|
||||
void ApplicationState::FromJson(const Json::Value& root, FileSource parseSource) const noexcept
|
||||
{
|
||||
auto state = _state.lock();
|
||||
// GetValueForKey() comes in two variants:
|
||||
|
@ -150,17 +159,24 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
// * return std::optional<T> by value
|
||||
// At the time of writing the former version skips missing fields in the json,
|
||||
// but we want to explicitly clear state fields that were removed from state.json.
|
||||
#define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) state->name = JsonUtils::GetValueForKey<std::optional<type>>(root, key);
|
||||
#define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \
|
||||
if (parseSource == source) \
|
||||
state->name = JsonUtils::GetValueForKey<std::optional<type>>(root, key);
|
||||
|
||||
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
|
||||
#undef MTSM_APPLICATION_STATE_GEN
|
||||
}
|
||||
Json::Value ApplicationState::ToJson() const noexcept
|
||||
|
||||
Json::Value ApplicationState::ToJson(FileSource parseSource) const noexcept
|
||||
{
|
||||
Json::Value root{ Json::objectValue };
|
||||
|
||||
{
|
||||
auto state = _state.lock_shared();
|
||||
#define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) JsonUtils::SetValueForKey(root, key, state->name);
|
||||
#define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \
|
||||
if (parseSource == source) \
|
||||
JsonUtils::SetValueForKey(root, key, state->name);
|
||||
|
||||
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
|
||||
#undef MTSM_APPLICATION_STATE_GEN
|
||||
}
|
||||
|
@ -168,27 +184,42 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
}
|
||||
|
||||
// Generate all getter/setters
|
||||
#define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) \
|
||||
type ApplicationState::name() const noexcept \
|
||||
{ \
|
||||
const auto state = _state.lock_shared(); \
|
||||
const auto& value = state->name; \
|
||||
return value ? *value : type{ __VA_ARGS__ }; \
|
||||
} \
|
||||
\
|
||||
void ApplicationState::name(const type& value) noexcept \
|
||||
{ \
|
||||
{ \
|
||||
auto state = _state.lock(); \
|
||||
state->name.emplace(value); \
|
||||
} \
|
||||
\
|
||||
_throttler(); \
|
||||
#define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \
|
||||
type ApplicationState::name() const noexcept \
|
||||
{ \
|
||||
const auto state = _state.lock_shared(); \
|
||||
const auto& value = state->name; \
|
||||
return value ? *value : type{ __VA_ARGS__ }; \
|
||||
} \
|
||||
\
|
||||
void ApplicationState::name(const type& value) noexcept \
|
||||
{ \
|
||||
{ \
|
||||
auto state = _state.lock(); \
|
||||
state->name.emplace(value); \
|
||||
} \
|
||||
\
|
||||
_throttler(); \
|
||||
}
|
||||
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
|
||||
#undef MTSM_APPLICATION_STATE_GEN
|
||||
|
||||
void ApplicationState::_writeFileContents(const std::string_view content) const
|
||||
std::optional<std::string> ApplicationState::_readSharedContents() const
|
||||
{
|
||||
return ReadUTF8FileIfExists(_sharedPath);
|
||||
}
|
||||
|
||||
std::optional<std::string> ApplicationState::_readElevatedContents() const
|
||||
{
|
||||
return ::Microsoft::Console::Utils::IsElevated() ? ReadUTF8FileIfExists(_elevatedPath, true) : std::nullopt;
|
||||
}
|
||||
|
||||
void ApplicationState::_writeSharedContents(const std::string_view content) const
|
||||
{
|
||||
WriteUTF8FileAtomic(_sharedPath, content);
|
||||
}
|
||||
|
||||
void ApplicationState::_writeElevatedContents(const std::string_view content) const
|
||||
{
|
||||
if (::Microsoft::Console::Utils::IsElevated())
|
||||
{
|
||||
|
@ -201,16 +232,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
// We're not worried about someone else doing that though, if they do
|
||||
// that with the wrong permissions, then we'll just ignore the file and
|
||||
// start over.
|
||||
WriteUTF8File(_path, content, true);
|
||||
WriteUTF8File(_elevatedPath, content, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteUTF8FileAtomic(_path, content);
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> ApplicationState::_readFileContents() const
|
||||
{
|
||||
return ReadUTF8FileIfExists(_path, ::Microsoft::Console::Utils::IsElevated());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,12 +23,19 @@ Abstract:
|
|||
// (type, function name, JSON key, ...variadic construction arguments)
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{
|
||||
#define MTSM_APPLICATION_STATE_FIELDS(X) \
|
||||
X(std::unordered_set<winrt::guid>, GeneratedProfiles, "generatedProfiles") \
|
||||
X(Windows::Foundation::Collections::IVector<Model::WindowLayout>, PersistedWindowLayouts, "persistedWindowLayouts") \
|
||||
X(Windows::Foundation::Collections::IVector<hstring>, RecentCommands, "recentCommands") \
|
||||
X(Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage>, DismissedMessages, "dismissedMessages") \
|
||||
X(Windows::Foundation::Collections::IVector<hstring>, AllowedCommandlines, "allowedCommandlines")
|
||||
enum FileSource
|
||||
{
|
||||
Shared,
|
||||
Local,
|
||||
// ElevatedOnly
|
||||
};
|
||||
|
||||
#define MTSM_APPLICATION_STATE_FIELDS(X) \
|
||||
X(FileSource::Shared, std::unordered_set<winrt::guid>, GeneratedProfiles, "generatedProfiles") \
|
||||
X(FileSource::Local, Windows::Foundation::Collections::IVector<Model::WindowLayout>, PersistedWindowLayouts, "persistedWindowLayouts") \
|
||||
X(FileSource::Shared, Windows::Foundation::Collections::IVector<hstring>, RecentCommands, "recentCommands") \
|
||||
X(FileSource::Shared, Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage>, DismissedMessages, "dismissedMessages") \
|
||||
X(FileSource::Local, Windows::Foundation::Collections::IVector<hstring>, AllowedCommandlines, "allowedCommandlines")
|
||||
|
||||
struct WindowLayout : WindowLayoutT<WindowLayout>
|
||||
{
|
||||
|
@ -43,20 +50,21 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
{
|
||||
static Microsoft::Terminal::Settings::Model::ApplicationState SharedInstance();
|
||||
|
||||
ApplicationState(std::filesystem::path path) noexcept;
|
||||
ApplicationState(std::filesystem::path sharedPath,
|
||||
std::filesystem::path elevatedPath) noexcept;
|
||||
~ApplicationState();
|
||||
|
||||
// Methods
|
||||
void Reload() const noexcept;
|
||||
void FromJson(const Json::Value& root) const noexcept;
|
||||
Json::Value ToJson() const noexcept;
|
||||
void FromJson(const Json::Value& root, FileSource parseSource) const noexcept;
|
||||
Json::Value ToJson(FileSource parseSource) const noexcept;
|
||||
|
||||
// General getters/setters
|
||||
winrt::hstring FilePath() const noexcept;
|
||||
|
||||
// State getters/setters
|
||||
#define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) \
|
||||
type name() const noexcept; \
|
||||
#define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \
|
||||
type name() const noexcept; \
|
||||
void name(const type& value) noexcept;
|
||||
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
|
||||
#undef MTSM_APPLICATION_STATE_GEN
|
||||
|
@ -64,19 +72,22 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
private:
|
||||
struct state_t
|
||||
{
|
||||
#define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) std::optional<type> name{ __VA_ARGS__ };
|
||||
#define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) std::optional<type> name{ __VA_ARGS__ };
|
||||
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
|
||||
#undef MTSM_APPLICATION_STATE_GEN
|
||||
};
|
||||
til::shared_mutex<state_t> _state;
|
||||
std::filesystem::path _path;
|
||||
std::filesystem::path _sharedPath;
|
||||
std::filesystem::path _elevatedPath;
|
||||
til::throttled_func_trailing<> _throttler;
|
||||
|
||||
void _write() const noexcept;
|
||||
void _read() const noexcept;
|
||||
|
||||
std::optional<std::string> _readFileContents() const;
|
||||
void _writeFileContents(const std::string_view content) const;
|
||||
std::optional<std::string> _readSharedContents() const;
|
||||
void _writeSharedContents(const std::string_view content) const;
|
||||
std::optional<std::string> _readElevatedContents() const;
|
||||
void _writeElevatedContents(const std::string_view content) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue