Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/app/application.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ class Application {
bool m_releaseSleepDelayWhenLocked = false;
// Set before Noctalia-initiated suspend so PrepareForSleep skips lock-before-sleep.
bool m_skipLockOnNextSleep = false;
// Prevents the unlock hook from resuming tracking during suspend.
bool m_preparingForSleep = false;
std::unique_ptr<AccountsService> m_accountsService;
std::unique_ptr<ScreenSaverService> m_screenSaverService;
std::unique_ptr<ScreenSaverPollSource> m_screenSaverPollSource;
Expand Down
9 changes: 9 additions & 0 deletions src/app/application_services.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,9 @@ void Application::initSystemBusServices() {
// fade-complete cleanup races with process freeze.
m_idleGraceOverlay.hide();
if (sleeping) {
// Screen time must not accumulate across suspend even when lock-before-suspend is off.
m_preparingForSleep = true;
m_screenTimeService.setTrackingPaused(true);
// Delay inhibit (when lock_before_suspend is on) holds sleep until we lock.
// Do not use runAfterSessionLocked here — that slot belongs to lock-and-suspend.
if (m_skipLockOnNextSleep) {
Expand Down Expand Up @@ -1011,6 +1014,12 @@ void Application::initSystemBusServices() {
}
m_skipLockOnNextSleep = false;
m_releaseSleepDelayWhenLocked = false;
m_preparingForSleep = false;
if (!m_lockScreen.isSessionLocked()) {
// After wake, resume tracking if the session is already unlocked.
// Otherwise, keep it paused until the session unlocks.
m_screenTimeService.setTrackingPaused(false);
}
if (m_configService.shouldLockBeforeSuspend() && m_logindService != nullptr) {
(void)m_logindService->acquireSleepDelayInhibit();
}
Expand Down
4 changes: 4 additions & 0 deletions src/app/application_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,13 +327,17 @@ void Application::initLockScreenAndSession() {
m_idleGraceOverlay.hide();
m_lockscreenWidgetsController.onLockStateChanged();
m_idleManager.setSessionLocked(true);
m_screenTimeService.setTrackingPaused(true);
m_hookManager.fire(HookKind::SessionLocked);
releaseSleepDelayInhibitIfPending();
},
[this]() {
m_idleGraceOverlay.hide();
m_lockscreenWidgetsController.onLockStateChanged();
m_idleManager.setSessionLocked(false);
if (!m_preparingForSleep) {
m_screenTimeService.setTrackingPaused(false);
}
m_hookManager.fire(HookKind::SessionUnlocked);
// Lock aborted before engage (e.g. compositor finished the lock object) — still release
// so PrepareForSleep is not stuck on the delay inhibit.
Expand Down
39 changes: 34 additions & 5 deletions src/system/screen_time_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,18 +313,47 @@ void ScreenTimeService::setEnabled(bool enabled) {
}
m_tickTimer.stop();
} else {
onFocusChange();
if (!m_tickTimer.active()) {
m_tickTimer.startRepeating(kTickInterval, [this]() { tick(); });
}
restartTicking();
}
if (m_changeCallback) {
m_changeCallback();
}
}

void ScreenTimeService::restartTicking() {
if (m_trackingPaused) {
return;
}
onFocusChange();
if (!m_tickTimer.active()) {
m_tickTimer.startRepeating(kTickInterval, [this]() { tick(); });
}
}

void ScreenTimeService::setTrackingPaused(bool paused) {
if (m_trackingPaused == paused) {
return;
}
m_trackingPaused = paused;
if (paused) {
// Pause counts (session lock or suspend) and stop counting.
flushActiveSession(std::chrono::steady_clock::now());
m_activeAppKey.clear();
m_activeSince = {};
if (m_dirty) {
save();
}
m_tickTimer.stop();
} else {
// Resume on unlock/login, resume the tick timer and credit the current app.
if (m_enabled) {
restartTicking();
}
}
}

void ScreenTimeService::onFocusChange() {
if (!m_enabled) {
if (!m_enabled || m_trackingPaused) {
return;
}
const std::string candidate = appKeyForActive();
Expand Down
3 changes: 3 additions & 0 deletions src/system/screen_time_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class ScreenTimeService {
void onFocusChange();
void setChangeCallback(std::function<void()> callback);
void setEnabled(bool enabled);
void setTrackingPaused(bool paused);
[[nodiscard]] bool enabled() const noexcept { return m_enabled; }

[[nodiscard]] ScreenTimeSnapshot snapshot(int rangeDays = 1);
Expand All @@ -65,6 +66,7 @@ class ScreenTimeService {
};

void tick();
void restartTicking();
void flushActiveSession(std::chrono::steady_clock::time_point now);
void ensureCurrentDayLocked(std::chrono::system_clock::time_point now);
void load();
Expand Down Expand Up @@ -96,4 +98,5 @@ class ScreenTimeService {
std::chrono::steady_clock::time_point m_activeSince;
bool m_dirty = false;
bool m_enabled = false;
bool m_trackingPaused = false;
};