diff --git a/cli/cli.cpp b/cli/cli.cpp index c91284e..fa16d2f 100644 --- a/cli/cli.cpp +++ b/cli/cli.cpp @@ -173,7 +173,8 @@ int main(int argc, char **argv) { .wakeupActions = wakeupActionsSet, .suspendProcesses = suspendProcesses, .wakeLog = wakeLog, - .disableDevices = disableDevices}; + .disableDevices = disableDevices, + .sleepAfterLidCloseSeconds = 40}; } else { goodnight::Logger::info("Using default config..."); config = {.keepSleep = true, @@ -184,7 +185,8 @@ int main(int argc, char **argv) { }, .suspendProcesses = false, .wakeLog = true, - .disableDevices = false}; + .disableDevices = false, + .sleepAfterLidCloseSeconds = 40}; } // Output the config as a human readable string diff --git a/gui/gui.cpp b/gui/gui.cpp index d9c2ffb..d7c8d78 100644 --- a/gui/gui.cpp +++ b/gui/gui.cpp @@ -181,6 +181,7 @@ struct SavedConfig { goodnight::Daemon::Config::WakeupActions::Other); } } + config.sleepAfterLidCloseSeconds = 40; return config; } @@ -209,9 +210,8 @@ struct SavedConfig { struct UIData { int daemonEnabled; // 0 = disabled, 1 = enabled - goodnight::Daemon::Config config{ - .wakeLog = true, - }; + goodnight::Daemon::Config config{.wakeLog = true, + .sleepAfterLidCloseSeconds = 40}; std::vector wakeupActions; bool english = false; bool runOnStartup = false; diff --git a/include/goodnight/goodnight.h b/include/goodnight/goodnight.h index a7781ed..79fa1a3 100644 --- a/include/goodnight/goodnight.h +++ b/include/goodnight/goodnight.h @@ -81,9 +81,9 @@ struct PowerListener { void addListener(std::function listener); ~PowerListener(); + void emitEvent(Events event); private: - void emitEvent(Events event); bool started = false; std::vector> listeners; @@ -173,6 +173,7 @@ struct Daemon { bool wakeLog = false; bool disableDevices = false; + int sleepAfterLidCloseSeconds = 0; }; using expected = std::expected; expected updateConfig(const Config &config); @@ -192,6 +193,9 @@ struct Daemon { std::unique_ptr powerListenerDisableDevices = nullptr; std::unique_ptr deviceManager = nullptr; + std::unique_ptr powerListenerSleepAfterLidCloseSeconds = + nullptr; + Config::WakeupActions ToWakeupAction(PowerListener::ExitModernStandbyEvent::Reason reason) { if (reason == PowerListener::ExitModernStandbyEvent::Reason::Mouse) { @@ -224,5 +228,6 @@ struct Daemon { expected updateSuspendProcesses(const Config &new_config); expected updateWakeLog(const Config &new_config); expected updateDisableDevices(const Config &new_config); + expected updateSleepAfterLidCloseSeconds(const Config &new_config); }; } // namespace goodnight \ No newline at end of file diff --git a/src/goodnight.cpp b/src/goodnight.cpp index 6a74a0f..92e2c0f 100644 --- a/src/goodnight.cpp +++ b/src/goodnight.cpp @@ -4,11 +4,13 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -145,21 +147,23 @@ std::expected PowerListener::start() { auto eventRecordId = std::stoul(extractStr("", "")); auto eventId = std::stoul(extractStr("", "")); - auto eventTime = extractStr(""); - - // parse the event time - std::chrono::system_clock::time_point timePoint; - { - std::tm tm = {}; - std::istringstream ss(eventTime); - ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S.%f"); - timePoint = - std::chrono::system_clock::from_time_t(std::mktime(&tm)); - } - - // time till now - auto timeTillNow = std::chrono::system_clock::now() - timePoint; - if (timeTillNow > std::chrono::seconds(3)) { + auto eventTime = extractStr("> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S.%f"); + time = std::mktime(&tm); + auto nowTime = std::chrono::system_clock::now(); + auto timezone = std::chrono::current_zone(); + + auto localTime = timezone->to_local(nowTime); + auto timeTillNow = + localTime.time_since_epoch().count() / 1000 / 10000 - + std::chrono::seconds(time).count() - + timezone->get_info(nowTime).offset.count() * 2; + + if (timeTillNow > 1000) { Logger::info("Event {}(EventId-{}) is too old, skip", eventRecordId, eventId); continue; @@ -255,6 +259,9 @@ std::expected Daemon::updateConfig(const Config &config) { if (auto res = updateDisableDevices(config); !res) { return res; } + if (auto res = updateSleepAfterLidCloseSeconds(config); !res) { + return res; + } this->config = config; return {}; } @@ -907,6 +914,61 @@ bool isAdministrator() { CloseHandle(hToken); return elevation.TokenIsElevated; } +Daemon::expected +Daemon::updateSleepAfterLidCloseSeconds(const Config &new_config) { + if (new_config.sleepAfterLidCloseSeconds) { + if (!config.sleepAfterLidCloseSeconds) { + powerListenerSleepAfterLidCloseSeconds = + std::make_unique(); + auto lastLidCloseTime = std::make_shared(0); + powerListenerSleepAfterLidCloseSeconds->addListener([=, + this](auto event) { + if (auto *lidEvent = std::get_if(&event)) { + if (lidEvent->closed) { + auto now = + std::chrono::system_clock::now().time_since_epoch().count(); + *lastLidCloseTime = now; + Logger::info("Lid closed, sleep after {} seconds", + config.sleepAfterLidCloseSeconds); + std::this_thread::sleep_for( + std::chrono::seconds(config.sleepAfterLidCloseSeconds)); + if (*lastLidCloseTime != now) { + Logger::info("Lid closed, but opened before sleep, skip sleep"); + } + StandbyManager::displayOff(); + + // emit sleep event + PowerListener::EnterModernStandbyEvent enterEvent; + enterEvent.reason = 15; + enterEvent.BatteryRemainingCapacityOnEnter = 0; + enterEvent.BatteryFullChargeCapacityOnEnter = 0; + + auto emitFor = [&](auto &listener) { + if (listener) + listener->emitEvent(enterEvent); + }; + + emitFor(powerListenerKeepSleep); + emitFor(powerListenerSuspendProcesses); + emitFor(powerListenerDisableDevices); + std::this_thread::sleep_for(std::chrono::seconds(20)); + + StandbyManager::sleep(); + } else { + *lastLidCloseTime = 0; + } + } + }); + + if (auto res = powerListenerSleepAfterLidCloseSeconds->start(); !res) { + return res; + } + } + } else { + powerListenerSleepAfterLidCloseSeconds = nullptr; + } + return {}; +} } // namespace goodnight bool goodnight::startMessageLoop() {