Skip to content

Commit

Permalink
Add idle notify/inhibit doc
Browse files Browse the repository at this point in the history
  • Loading branch information
ehopperdietzel committed Aug 6, 2024
1 parent dbbb916 commit 77a0452
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 26 deletions.
53 changes: 51 additions & 2 deletions src/lib/core/LIdleListener.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,63 @@

#include <LObject.h>

// TODO: add doc
/**
* @brief Idle state listener
*
* Clients using the [Idle Notify](https://wayland.app/protocols/ext-idle-notify-v1) protocol create an
* instance of this class to be notified when the user has been idle for a set amount of time. Clients
* like [swayidle](https://man.archlinux.org/man/swayidle) use this information to trigger screen lockers,
* screen savers, etc.
*
* @note All idle listeners can be accessed from LSeat::idleListeners().
*
* Each listener has its own timer with a fixed timeout() in milliseconds, defined by the client().
* The timer should be reset with resetTimer() whenever a user event occurs (see LSeat::onEvent()).
*
* If there is no user activity and the timeout() is reached, LSeat::onIdleListenerTimeout() is triggered.
*
* - If resetTimer() is not called within that event, the client will be notified that the user is idle until
* resetTimer() is called again.
* - If a client requests to inhibit the idle state (see section below), resetTimer() should always be called.
*
* @note Resetting all timers each time an event occurs isn't very CPU-friendly. Consider using LSeat::setIsUserIdleHint() instead
* like the default LSeat::onEvent() implementation does.
*
* @section idle_inhibitors Idle Inhibitors
*
* Clients using the [Idle Inhibit](https://wayland.app/protocols/idle-inhibit-unstable-v1) protocol can request
* to prevent the idle state if one of their surfaces is mapped and visible.
*
* These surfaces can be accessed from LSeat::idleInhibitorSurfaces().
*
* The auxiliary virtual method LSeat::isIdleStateInhibited() is used by the default implementation of LSeat::onIdleListenerTimeout()
* to check if the timer should be reset.
*/
class Louvre::LIdleListener final : public LObject
{
public:

/**
* @brief Resets the internal timer.
*
* This method should be called each time an event indicating user activity occurs (see LSeat::onEvent()).
* When the timeout() is reached, LSeat::onIdleListenerTimeout() is triggered.
*/
void resetTimer() const noexcept;
LClient *client() const noexcept;

/**
* @brief The timeout in milliseconds.
*/
UInt32 timeout() const noexcept;

/**
* @brief Client owner of the listener.
*/
LClient *client() const noexcept;

/**
* @brief Associated Wayland resource.
*/
const Protocols::IdleNotify::RIdleNotification &resource() const noexcept
{
return m_resource;
Expand Down
109 changes: 88 additions & 21 deletions src/lib/core/LSeat.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,44 @@ class Louvre::LSeat : public LFactoryObject
const std::vector<LInputDevice *> &inputDevices() const noexcept;

/**
* @brief Idle inhibitor surfaces
* @brief Surfaces that inhibit the idle state.
*
* Surfaces requesting to inhibit the compositor idle state while they are mapped and visible.
* @see LIdleListener for more details.
*/
const std::vector<LSurface *> &idleInhibitorSurfaces() const noexcept;

/**
* @brief List of idle listeners.
*
* Provides access to idle listeners used by clients to detect when the user has been idle for a specified amount of time.
*
* @see LIdleListener for more details.
*/
const std::vector<const LIdleListener*> &idleListeners() const noexcept;

/**
* @brief Sets a hint about the user's idle state.
*
* Resetting all idle listener timers manually with `LIdleListeners::resetTimer()` each time an event occurs isn't very CPU-friendly,
* as multiple events can be triggered in a single main loop iteration.
*
* Instead, by using this method (which only updates a boolean variable), we can ask Louvre to update all timers only once at the end of an iteration.
*
* @note The value is automatically set to `true` at the start of each iteration.
*
* @see LIdleListener for more details.
*/
void setIsUserIdleHint(bool isIdle) noexcept;

/**
* @brief Hint about the user's idle state set with `setIsUserIdleHint()`.
*
* @see LIdleListener for more details.
*
* @return A boolean indicating the user's idle state. `true` if the user is idle, `false` otherwise.
*/
bool isUserIdleHint() const noexcept;

/**
* @brief The seat name
*
Expand All @@ -91,7 +123,6 @@ class Louvre::LSeat : public LFactoryObject
*/
const std::vector<LToplevelResizeSession*> &toplevelResizeSessions() const noexcept;


/**
* @brief Active LToplevelRole move sessions.
*
Expand Down Expand Up @@ -220,19 +251,6 @@ class Louvre::LSeat : public LFactoryObject
/// @name Virtual Methods
/// @{

/**
* @brief Native input backend events
*
* Override this virtual method if you want to access all events generated by the input backend.
*
* @param event Opaque handle to the native backend event.
* When using the Libinput backend it corresponds to a [libinput_event](https://wayland.freedesktop.org/libinput/doc/latest/api/structlibinput__event.html) struct
*
* #### Default implementation
* @snippet LSeatDefault.cpp nativeInputEvent
*/
virtual void nativeInputEvent(void *event);

/**
* @brief Notify a change in the enabled() property
*
Expand Down Expand Up @@ -289,15 +307,64 @@ class Louvre::LSeat : public LFactoryObject
*/
virtual void inputDeviceUnplugged(LInputDevice *device);

// TODO: Add idle inhibit add/remove events and doc

const std::vector<const LIdleListener*> &idleListeners() const noexcept;
/**
* @brief Checks if the idle state is inhibited.
*
* The default implementation returns `true` if at least one of the surfaces
* listed in `idleInhibitorSurfaces()` is mapped and visible on at least one output.
*
* @note Since the method of displaying surfaces is specific to each compositor,
* no additional checks are performed to determine if the surface is obscured
* by opaque areas. You may override this method to include such checks if needed.
*
* @see LIdleListener for more details.
*
* @return Returns `true` if the idle state is inhibited, `false` otherwise.
*
* #### Default Implementation
* @snippet LSeatDefault.cpp isIdleStateInhibited
*/
virtual bool isIdleStateInhibited() const;

/**
* @brief Notifies the timeout of an idle listener
*
* If LIdleListener::resetTimer() is not called within this event, the LIdleListener::client()
* will be notified that the user is idle until LIdleListener::resetTimer() is called again.
*
* If the idle state is being inhibited (see isIdleStateInhibited()) LIdleListener::resetTimer()
* should always be called.
*
* @see LIdleListener for more details.
* *
* #### Default Implementation
* @snippet LSeatDefault.cpp onIdleListenerTimeout
*/
virtual void onIdleListenerTimeout(const LIdleListener &listener);

/**
* @brief Notifies of any input event.
*
* This event is similar to `nativeInputEvent()`, but is only invoked
* for input event types currently supported by Louvre.
*
* #### Default Implementation
* @snippet LSeatDefault.cpp onEvent
*/
virtual void onEvent(const LEvent &event);

void setIsUserIdleHint(bool isIdle) noexcept;
bool isUserIdleHint() const noexcept;
/**
* @brief Native input backend events
*
* Override this virtual method if you want to access all events generated by the input backend.
*
* @param event Opaque handle to the native backend event.
* When using the Libinput backend it corresponds to a [libinput_event](https://wayland.freedesktop.org/libinput/doc/latest/api/structlibinput__event.html) struct
*
* #### Default implementation
* @snippet LSeatDefault.cpp nativeInputEvent
*/
virtual void nativeInputEvent(void *event);

/// @}

Expand Down
10 changes: 7 additions & 3 deletions src/lib/core/default/LSeatDefault.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ void LSeat::inputDeviceUnplugged(LInputDevice *device)
}
//! [inputDeviceUnplugged]

//! [isIdleStateInhibited]
bool LSeat::isIdleStateInhibited() const
{
for (LSurface *surface : idleInhibitorSurfaces())
Expand All @@ -83,17 +84,19 @@ bool LSeat::isIdleStateInhibited() const

return false;
}
//! [isIdleStateInhibited]

//! [onIdleListenerTimeout]
void LSeat::onIdleListenerTimeout(const LIdleListener &listener)
{
if (isIdleStateInhibited())
listener.resetTimer();

/*
* If the timer is not reset, the client will assume the user is idle.
*/
/* If the timer is not reset, the client will assume the user is idle. */
}
//! [onIdleListenerTimeout]

//! [onEvent]
void LSeat::onEvent(const LEvent &event)
{
L_UNUSED(event)
Expand All @@ -114,3 +117,4 @@ void LSeat::onEvent(const LEvent &event)
*/
setIsUserIdleHint(false);
}
//! [onEvent]

0 comments on commit 77a0452

Please sign in to comment.