Skip to content

Commit

Permalink
Add --idle-timeout-when-locked for idle timeout when session is loc…
Browse files Browse the repository at this point in the history
…ked (#3546)

Fixes #3306.

This PR adds a new configuration option, `--idle-timeout-when-locked`,
that allows the compositor to apply a separate idle timeout when the
session is locked. The default value is 0, meaning the display will stay
on indefinitely when the session is locked.

Examples showing its interaction with the existing `--idle-timeout`
option:
- `--idle-timeout=60 --idle-timeout-when-locked=10`: The display turns
off after 60s of idle time when the session is unlocked, and after 10s
of idle time when the session is locked.
- `--idle-timeout=0 --idle-timeout-when-locked=10`: The display never
turns off when idle, but it turns off after 10s of idle time when the
session is locked.
- `--idle-timeout=60 --idle-timeout-when-locked=0`: The display turns
off after 60s of idle time but never when the session is locked.
- `--idle-timeout=0 --idle-timeout-on-lock=0`: This is the default
behavior. The display never turns off, wether the session is locked or
unlocked.

### Implementation details:

`BasicIdleHandler` now registers a session lock listener via the
`SessionLockListener` class, which takes `on_lock` and `on_unlock`
callbacks to track the state of the session lock. When the idle handlers
(the dimmer and the power setter) are registered, they will use the
timeout value from `--idle-timeout-when-locked` when the session is
locked, and from `--idle-timeout` when the session is not locked.
  • Loading branch information
hbatagelo authored Oct 17, 2024
2 parents 9dabd2e + 004fcd3 commit f66bcae
Show file tree
Hide file tree
Showing 8 changed files with 278 additions and 20 deletions.
1 change: 1 addition & 0 deletions include/platform/mir/options/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ extern char const* const wayland_extensions_opt;
extern char const* const add_wayland_extensions_opt;
extern char const* const drop_wayland_extensions_opt;
extern char const* const idle_timeout_opt;
extern char const* const idle_timeout_when_locked_opt;

extern char const* const enable_key_repeat_opt;

Expand Down
5 changes: 5 additions & 0 deletions src/include/server/mir/shell/idle_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ class IdleHandler : public ObserverRegistrar<IdleHandlerObserver>
/// is sent the display is never turned off or dimmed, which is the default.
virtual void set_display_off_timeout(std::optional<time::Duration> timeout) = 0;

/// Duration Mir will remain idle before the display is turned off when the session is locked. The display may dim
/// some time before this. If nullopt is sent, the display is never turned off or dimmed during session lock, which
/// is the default.
virtual void set_display_off_timeout_when_locked(std::optional<time::Duration> timeout) = 0;

private:
IdleHandler(IdleHandler const&) = delete;
IdleHandler& operator=(IdleHandler const&) = delete;
Expand Down
8 changes: 6 additions & 2 deletions src/platform/options/default_configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ char const* const mo::wayland_extensions_opt = "wayland-extensions";
char const* const mo::add_wayland_extensions_opt = "add-wayland-extensions";
char const* const mo::drop_wayland_extensions_opt = "drop-wayland-extensions";
char const* const mo::idle_timeout_opt = "idle-timeout";
char const* const mo::idle_timeout_when_locked_opt = "idle-timeout-when-locked";

char const* const mo::off_opt_value = "off";
char const* const mo::log_opt_value = "log";
Expand Down Expand Up @@ -180,8 +181,11 @@ mo::DefaultConfiguration::DefaultConfiguration(
(enable_key_repeat_opt, po::value<bool>()->default_value(true),
"Enable server generated key repeat")
(idle_timeout_opt, po::value<int>()->default_value(0),
"Time (in seconds) Mir will remain idle before turning off the display, "
"or 0 to keep display on forever.")
"Time (in seconds) Mir will remain idle before turning off the display "
"when the session is not locked, or 0 to keep display on forever.")
(idle_timeout_when_locked_opt, po::value<int>()->default_value(0),
"Time (in seconds) Mir will remain idle before turning off the display "
"when the session is locked, or 0 to keep the display on forever.")
(fatal_except_opt, "On \"fatal error\" conditions [e.g. drivers behaving "
"in unexpected ways] throw an exception (instead of a core dump)")
(debug_opt, "Enable extra development debugging. "
Expand Down
8 changes: 7 additions & 1 deletion src/platform/symbols.map
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,12 @@ MIR_PLATFORM_2.18 {
vtable?for?mir::options::Option;
vtable?for?mir::options::ProgramOption;
};
local: *;
};

MIR_PLATFORM_2.19 {
global:
extern "C++" {
mir::options::idle_timeout_when_locked_opt;
};
local: *;
} MIR_PLATFORM_2.18;
95 changes: 82 additions & 13 deletions src/server/shell/basic_idle_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "basic_idle_handler.h"
#include "mir/fatal.h"
#include "mir/scene/idle_hub.h"
#include "mir/scene/session_lock.h"
#include "mir/graphics/renderable.h"
#include "mir/renderer/sw/pixel_source.h"
#include "mir/input/scene.h"
Expand Down Expand Up @@ -170,51 +171,119 @@ struct PowerModeSetter : ms::IdleStateObserver
};
}

class msh::BasicIdleHandler::SessionLockListener : public ms::SessionLockObserver
{
public:
explicit SessionLockListener(msh::BasicIdleHandler* idle_handler) : idle_handler{idle_handler}
{
}

void on_lock() override
{
idle_handler->on_session_lock();
}

void on_unlock() override
{
idle_handler->on_session_unlock();
}

private:
msh::BasicIdleHandler* const idle_handler;
};

msh::BasicIdleHandler::BasicIdleHandler(
std::shared_ptr<ms::IdleHub> const& idle_hub,
std::shared_ptr<input::Scene> const& input_scene,
std::shared_ptr<graphics::GraphicBufferAllocator> const& allocator,
std::shared_ptr<msh::DisplayConfigurationController> const& display_config_controller)
std::shared_ptr<msh::DisplayConfigurationController> const& display_config_controller,
std::shared_ptr<ms::SessionLock> const& session_lock)
: idle_hub{idle_hub},
input_scene{input_scene},
allocator{allocator},
display_config_controller{display_config_controller}
display_config_controller{display_config_controller},
session_lock{session_lock},
session_lock_monitor{std::make_shared<SessionLockListener>(this)}
{
session_lock->register_interest(session_lock_monitor);
}

msh::BasicIdleHandler::~BasicIdleHandler()
{
session_lock->unregister_interest(*session_lock_monitor);
std::lock_guard lock{mutex};
clear_observers(lock);
}

void msh::BasicIdleHandler::set_display_off_timeout(std::optional<time::Duration> timeout)
{
std::lock_guard lock{mutex};
if (timeout == current_off_timeout)
if (timeout != current_off_timeout)
{
return;
current_off_timeout = timeout;
if (!session_locked)
{
clear_observers(lock);
register_observers(lock);
}
}
current_off_timeout = timeout;
clear_observers(lock);
if (timeout)
}

void msh::BasicIdleHandler::set_display_off_timeout_when_locked(std::optional<time::Duration> timeout)
{
std::lock_guard lock{mutex};
if (timeout != current_off_timeout_when_locked)
{
current_off_timeout_when_locked = timeout;
if (session_locked)
{
clear_observers(lock);
register_observers(lock);
}
}
}

void msh::BasicIdleHandler::on_session_lock()
{
std::lock_guard lock{mutex};
session_locked = true;
if (current_off_timeout_when_locked != current_off_timeout)
{
clear_observers(lock);
register_observers(lock);
}
}

void msh::BasicIdleHandler::on_session_unlock()
{
std::lock_guard lock{mutex};
session_locked = false;
if (current_off_timeout_when_locked != current_off_timeout)
{
clear_observers(lock);
register_observers(lock);
}
}

void msh::BasicIdleHandler::register_observers(ProofOfMutexLock const&)
{
if (auto const off_timeout{session_locked ? current_off_timeout_when_locked : current_off_timeout})
{
auto const off_timeout = timeout.value();
if (off_timeout <= time::Duration{})
if (*off_timeout <= time::Duration{0})
{
fatal_error("BasicIdleHandler given invalid timeout %d, should be >0", off_timeout.count());
fatal_error("BasicIdleHandler given invalid timeout %d, should be >0", off_timeout->count());
}
if (off_timeout >= dim_time_before_off * 2)
if (*off_timeout >= dim_time_before_off * 2)
{
auto const dim_timeout = off_timeout - dim_time_before_off;
auto const dim_timeout = *off_timeout - dim_time_before_off;
auto const dimmer = std::make_shared<Dimmer>(input_scene, allocator, multiplexer);
observers.push_back(dimmer);
idle_hub->register_interest(dimmer, dim_timeout);
}
auto const power_setter = std::make_shared<PowerModeSetter>(
display_config_controller, mir_power_mode_off, multiplexer);
observers.push_back(power_setter);
idle_hub->register_interest(power_setter, off_timeout);
idle_hub->register_interest(power_setter, *off_timeout);
}
}

Expand Down
16 changes: 15 additions & 1 deletion src/server/shell/basic_idle_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ namespace scene
{
class IdleHub;
class IdleStateObserver;
class SessionLock;
}
namespace shell
{
Expand All @@ -51,12 +52,15 @@ class BasicIdleHandler : public IdleHandler
std::shared_ptr<scene::IdleHub> const& idle_hub,
std::shared_ptr<input::Scene> const& input_scene,
std::shared_ptr<graphics::GraphicBufferAllocator> const& allocator,
std::shared_ptr<shell::DisplayConfigurationController> const& display_config_controller);
std::shared_ptr<shell::DisplayConfigurationController> const& display_config_controller,
std::shared_ptr<scene::SessionLock> const& session_lock);

~BasicIdleHandler();

void set_display_off_timeout(std::optional<time::Duration> timeout) override;

void set_display_off_timeout_when_locked(std::optional<time::Duration> timeout) override;

void register_interest(std::weak_ptr<IdleHandlerObserver> const&) override;

void register_interest(
Expand All @@ -70,15 +74,25 @@ class BasicIdleHandler : public IdleHandler
void unregister_interest(IdleHandlerObserver const&) override;

private:
class SessionLockListener;

void on_session_lock();
void on_session_unlock();

void register_observers(ProofOfMutexLock const&);
void clear_observers(ProofOfMutexLock const&);

std::shared_ptr<scene::IdleHub> const idle_hub;
std::shared_ptr<input::Scene> const input_scene;
std::shared_ptr<graphics::GraphicBufferAllocator> const allocator;
std::shared_ptr<shell::DisplayConfigurationController> const display_config_controller;
std::shared_ptr<scene::SessionLock> const session_lock;
std::shared_ptr<SessionLockListener> const session_lock_monitor;

std::mutex mutex;
std::optional<time::Duration> current_off_timeout;
std::optional<time::Duration> current_off_timeout_when_locked;
bool session_locked{false};
std::vector<std::shared_ptr<scene::IdleStateObserver>> observers;

class BasicIdleHandlerObserverMultiplexer: public ObserverMultiplexer<IdleHandlerObserver>
Expand Down
22 changes: 21 additions & 1 deletion src/server/shell/default_configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ auto mir::DefaultServerConfiguration::the_idle_handler() -> std::shared_ptr<msh:
the_idle_hub(),
the_input_scene(),
the_buffer_allocator(),
the_display_configuration_controller());
the_display_configuration_controller(),
the_session_lock());

auto options = the_options();
int const idle_timeout_seconds = options->get<int>(options::idle_timeout_opt);
Expand All @@ -122,6 +123,25 @@ auto mir::DefaultServerConfiguration::the_idle_handler() -> std::shared_ptr<msh:
idle_handler->set_display_off_timeout(std::chrono::seconds{idle_timeout_seconds});
}

int const idle_timeout_when_locked = options->get<int>(options::idle_timeout_when_locked_opt);
if (idle_timeout_when_locked < 0)
{
throw mir::AbnormalExit(
"Invalid " +
std::string{options::idle_timeout_when_locked_opt} +
" value " +
std::to_string(idle_timeout_when_locked) +
", must be > 0");
}
if (idle_timeout_when_locked == 0)
{
idle_handler->set_display_off_timeout_when_locked(std::nullopt);
}
else
{
idle_handler->set_display_off_timeout_when_locked(std::chrono::seconds{idle_timeout_when_locked});
}

return idle_handler;
});
}
Expand Down
Loading

0 comments on commit f66bcae

Please sign in to comment.