Files
PowerToys/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.cpp
Jaylyn Barbee 29688cea0e [Light Switch] Enter latitude and longitude manually in Sunrise to sunset mode (#43276)
<!-- 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 PR introduces new UI to allow the users to manually enter their
lat/long.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #42429
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Localization:** All end-user-facing strings can be localized
- [x] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here:
#[5979](https://github.com/MicrosoftDocs/windows-dev-docs-pr/pull/5979)

---------

Co-authored-by: Niels Laute <niels.laute@live.nl>
2025-11-11 16:18:18 -05:00

250 lines
6.9 KiB
C++

#include "LightSwitchSettings.h"
#include <common/utils/json.h>
#include <common/SettingsAPI/settings_helpers.h>
#include "SettingsObserver.h"
#include <filesystem>
#include <fstream>
#include <logger.h>
using namespace std;
LightSwitchSettings& LightSwitchSettings::instance()
{
static LightSwitchSettings inst;
return inst;
}
LightSwitchSettings::LightSwitchSettings()
{
LoadSettings();
}
std::wstring LightSwitchSettings::GetSettingsFileName()
{
return PTSettingsHelper::get_module_save_file_location(L"LightSwitch");
}
void LightSwitchSettings::InitFileWatcher()
{
if (!m_settingsChangedEvent)
{
m_settingsChangedEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
}
if (!m_settingsFileWatcher)
{
m_settingsFileWatcher = std::make_unique<FileWatcher>(
GetSettingsFileName(),
[this]() {
using namespace std::chrono;
{
std::lock_guard<std::mutex> lock(m_debounceMutex);
m_lastChangeTime = steady_clock::now();
if (m_debouncePending)
return;
m_debouncePending = true;
}
m_debounceThread = std::jthread([this](std::stop_token stop) {
using namespace std::chrono;
while (!stop.stop_requested())
{
std::this_thread::sleep_for(seconds(3));
auto elapsed = steady_clock::now() - m_lastChangeTime;
if (elapsed >= seconds(1))
break;
}
{
std::lock_guard<std::mutex> lock(m_debounceMutex);
m_debouncePending = false;
}
Logger::info(L"[LightSwitchSettings] Settings file stabilized, reloading.");
try
{
LoadSettings();
SetEvent(m_settingsChangedEvent);
}
catch (const std::exception& e)
{
std::wstring wmsg;
wmsg.assign(e.what(), e.what() + strlen(e.what()));
Logger::error(L"[LightSwitchSettings] Exception during debounced reload: {}", wmsg);
}
});
});
}
}
LightSwitchSettings::~LightSwitchSettings()
{
Logger::info(L"[LightSwitchSettings] Cleaning up settings resources...");
// Stop and join the debounce thread (std::jthread auto-joins, but we can signal stop too)
if (m_debounceThread.joinable())
{
m_debounceThread.request_stop();
}
// Release the file watcher so it closes file handles and background threads
if (m_settingsFileWatcher)
{
m_settingsFileWatcher.reset();
Logger::info(L"[LightSwitchSettings] File watcher stopped.");
}
// Close the Windows event handle
if (m_settingsChangedEvent)
{
CloseHandle(m_settingsChangedEvent);
m_settingsChangedEvent = nullptr;
Logger::info(L"[LightSwitchSettings] Settings changed event closed.");
}
Logger::info(L"[LightSwitchSettings] Cleanup complete.");
}
void LightSwitchSettings::AddObserver(SettingsObserver& observer)
{
m_observers.insert(&observer);
}
void LightSwitchSettings::RemoveObserver(SettingsObserver& observer)
{
m_observers.erase(&observer);
}
void LightSwitchSettings::NotifyObservers(SettingId id) const
{
for (auto observer : m_observers)
{
if (observer->WantsToBeNotified(id))
{
observer->SettingsUpdate(id);
}
}
}
HANDLE LightSwitchSettings::GetSettingsChangedEvent() const
{
return m_settingsChangedEvent;
}
void LightSwitchSettings::LoadSettings()
{
std::lock_guard<std::mutex> guard(m_settingsMutex);
try
{
PowerToysSettings::PowerToyValues values =
PowerToysSettings::PowerToyValues::load_from_settings_file(L"LightSwitch");
if (const auto jsonVal = values.get_string_value(L"scheduleMode"))
{
auto val = *jsonVal;
auto newMode = FromString(val);
if (m_settings.scheduleMode != newMode)
{
m_settings.scheduleMode = newMode;
NotifyObservers(SettingId::ScheduleMode);
}
}
// Latitude
if (const auto jsonVal = values.get_string_value(L"latitude"))
{
auto val = *jsonVal;
if (m_settings.latitude != val)
{
m_settings.latitude = val;
NotifyObservers(SettingId::Latitude);
}
}
// Longitude
if (const auto jsonVal = values.get_string_value(L"longitude"))
{
auto val = *jsonVal;
if (m_settings.longitude != val)
{
m_settings.longitude = val;
NotifyObservers(SettingId::Longitude);
}
}
// LightTime
if (const auto jsonVal = values.get_int_value(L"lightTime"))
{
auto val = *jsonVal;
if (m_settings.lightTime != val)
{
m_settings.lightTime = val;
NotifyObservers(SettingId::LightTime);
}
}
// DarkTime
if (const auto jsonVal = values.get_int_value(L"darkTime"))
{
auto val = *jsonVal;
if (m_settings.darkTime != val)
{
m_settings.darkTime = val;
NotifyObservers(SettingId::DarkTime);
}
}
// Offset
if (const auto jsonVal = values.get_int_value(L"sunrise_offset"))
{
auto val = *jsonVal;
if (m_settings.sunrise_offset != val)
{
m_settings.sunrise_offset = val;
NotifyObservers(SettingId::Sunrise_Offset);
}
}
if (const auto jsonVal = values.get_int_value(L"sunset_offset"))
{
auto val = *jsonVal;
if (m_settings.sunset_offset != val)
{
m_settings.sunset_offset = val;
NotifyObservers(SettingId::Sunset_Offset);
}
}
// ChangeSystem
if (const auto jsonVal = values.get_bool_value(L"changeSystem"))
{
auto val = *jsonVal;
if (m_settings.changeSystem != val)
{
m_settings.changeSystem = val;
NotifyObservers(SettingId::ChangeSystem);
}
}
// ChangeApps
if (const auto jsonVal = values.get_bool_value(L"changeApps"))
{
auto val = *jsonVal;
if (m_settings.changeApps != val)
{
m_settings.changeApps = val;
NotifyObservers(SettingId::ChangeApps);
}
}
}
catch (...)
{
// Keeps defaults if load fails
}
}