Merge pull request #4565 from guttermonk/idle_inhibitor-activity-detection
Added Idle Inhibitor Features: Signal Inputs & Activity Detection
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
struct zwp_idle_inhibitor_v1;
|
struct zwp_idle_inhibitor_v1;
|
||||||
struct zwp_idle_inhibit_manager_v1;
|
struct zwp_idle_inhibit_manager_v1;
|
||||||
|
struct ext_idle_notifier_v1;
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
|
|
||||||
@@ -27,6 +28,7 @@ class Client {
|
|||||||
struct wl_registry* registry = nullptr;
|
struct wl_registry* registry = nullptr;
|
||||||
struct zxdg_output_manager_v1* xdg_output_manager = nullptr;
|
struct zxdg_output_manager_v1* xdg_output_manager = nullptr;
|
||||||
struct zwp_idle_inhibit_manager_v1* idle_inhibit_manager = nullptr;
|
struct zwp_idle_inhibit_manager_v1* idle_inhibit_manager = nullptr;
|
||||||
|
struct ext_idle_notifier_v1* idle_notifier = nullptr;
|
||||||
std::vector<std::unique_ptr<Bar>> bars;
|
std::vector<std::unique_ptr<Bar>> bars;
|
||||||
Config config;
|
Config config;
|
||||||
std::string bar_id;
|
std::string bar_id;
|
||||||
|
|||||||
@@ -6,25 +6,35 @@
|
|||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
|
|
||||||
|
struct ext_idle_notification_v1;
|
||||||
|
|
||||||
namespace waybar::modules {
|
namespace waybar::modules {
|
||||||
|
|
||||||
class IdleInhibitor : public ALabel {
|
class IdleInhibitor : public ALabel {
|
||||||
sigc::connection timeout_;
|
sigc::connection timeout_;
|
||||||
|
ext_idle_notification_v1* idle_notification_;
|
||||||
|
uint32_t idle_timeout_ms_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&);
|
IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||||
virtual ~IdleInhibitor();
|
virtual ~IdleInhibitor();
|
||||||
auto update() -> void override;
|
auto update() -> void override;
|
||||||
|
auto refresh(int) -> void override;
|
||||||
static std::list<waybar::AModule*> modules;
|
static std::list<waybar::AModule*> modules;
|
||||||
static bool status;
|
static bool status;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool handleToggle(GdkEventButton* const& e) override;
|
bool handleToggle(GdkEventButton* const& e) override;
|
||||||
void toggleStatus();
|
void toggleStatus();
|
||||||
|
void setupIdleNotification();
|
||||||
|
void teardownIdleNotification();
|
||||||
|
static void handleIdled(void* data, ext_idle_notification_v1* notification);
|
||||||
|
static void handleResumed(void* data, ext_idle_notification_v1* notification);
|
||||||
|
|
||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
struct zwp_idle_inhibitor_v1* idle_inhibitor_;
|
struct zwp_idle_inhibitor_v1* idle_inhibitor_;
|
||||||
int pid_;
|
int pid_;
|
||||||
|
bool wait_for_activity_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
|
|||||||
@@ -76,6 +76,17 @@ screensaver, also known as "presentation mode".
|
|||||||
typeof: double ++
|
typeof: double ++
|
||||||
The number of minutes the inhibition should last.
|
The number of minutes the inhibition should last.
|
||||||
|
|
||||||
|
*wait-for-activity*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: *false* ++
|
||||||
|
When enabled, the idle inhibitor remains active as long as there is keyboard or mouse activity on the bar. If there is no activity for the duration specified in *timeout*, the inhibitor will automatically toggle off. This option requires *timeout* to be set.
|
||||||
|
|
||||||
|
*signal*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The signal number used to toggle the idle inhibitor externally. ++
|
||||||
|
The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*. ++
|
||||||
|
Use `pkill -SIGRTMIN+N waybar` to toggle the idle inhibitor from scripts or keybindings.
|
||||||
|
|
||||||
*tooltip*: ++
|
*tooltip*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: true ++
|
default: true ++
|
||||||
@@ -115,17 +126,46 @@ screensaver, also known as "presentation mode".
|
|||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
|
Basic usage with timeout:
|
||||||
|
|
||||||
```
|
```
|
||||||
"idle_inhibitor": {
|
"idle_inhibitor": {
|
||||||
"format": "{icon}",
|
"format": "{icon}",
|
||||||
"format-icons": {
|
"format-icons": {
|
||||||
"activated": "",
|
"activated": "",
|
||||||
"deactivated": ""
|
"deactivated": ""
|
||||||
},
|
},
|
||||||
"timeout": 30.5
|
"timeout": 30.5
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
With external control via signals (can be toggled with `pkill -SIGRTMIN+8 waybar`):
|
||||||
|
|
||||||
|
```
|
||||||
|
"idle_inhibitor": {
|
||||||
|
"format": "{icon}",
|
||||||
|
"format-icons": {
|
||||||
|
"activated": "",
|
||||||
|
"deactivated": ""
|
||||||
|
},
|
||||||
|
"signal": 8
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
With wait-for-activity feature:
|
||||||
|
|
||||||
|
```
|
||||||
|
"idle_inhibitor": {
|
||||||
|
"format": "{icon}",
|
||||||
|
"format-icons": {
|
||||||
|
"activated": "",
|
||||||
|
"deactivated": ""
|
||||||
|
},
|
||||||
|
"timeout": 5.0,
|
||||||
|
"wait-for-activity": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# STYLE
|
# STYLE
|
||||||
|
|
||||||
- *#idle_inhibitor*
|
- *#idle_inhibitor*
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="ext_idle_notify_v1">
|
||||||
|
<copyright>
|
||||||
|
Copyright © 2015 Martin Gräßlin
|
||||||
|
Copyright © 2022 Simon Ser
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice (including the next
|
||||||
|
paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<interface name="ext_idle_notifier_v1" version="2">
|
||||||
|
<description summary="idle notification manager">
|
||||||
|
This interface allows clients to monitor user idle status.
|
||||||
|
|
||||||
|
After binding to this global, clients can create ext_idle_notification_v1
|
||||||
|
objects to get notified when the user is idle for a given amount of time.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the manager">
|
||||||
|
Destroy the manager object. All objects created via this interface
|
||||||
|
remain valid.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_idle_notification">
|
||||||
|
<description summary="create a notification object">
|
||||||
|
Create a new idle notification object.
|
||||||
|
|
||||||
|
The notification object has a minimum timeout duration and is tied to a
|
||||||
|
seat. The client will be notified if the seat is inactive for at least
|
||||||
|
the provided timeout. See ext_idle_notification_v1 for more details.
|
||||||
|
|
||||||
|
A zero timeout is valid and means the client wants to be notified as
|
||||||
|
soon as possible when the seat is inactive.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="ext_idle_notification_v1"/>
|
||||||
|
<arg name="timeout" type="uint" summary="minimum idle timeout in msec"/>
|
||||||
|
<arg name="seat" type="object" interface="wl_seat"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<!-- Version 2 additions -->
|
||||||
|
|
||||||
|
<request name="get_input_idle_notification" since="2">
|
||||||
|
<description summary="create a notification object">
|
||||||
|
Create a new idle notification object to track input from the
|
||||||
|
user, such as keyboard and mouse movement. Because this object is
|
||||||
|
meant to track user input alone, it ignores idle inhibitors.
|
||||||
|
|
||||||
|
The notification object has a minimum timeout duration and is tied to a
|
||||||
|
seat. The client will be notified if the seat is inactive for at least
|
||||||
|
the provided timeout. See ext_idle_notification_v1 for more details.
|
||||||
|
|
||||||
|
A zero timeout is valid and means the client wants to be notified as
|
||||||
|
soon as possible when the seat is inactive.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="ext_idle_notification_v1"/>
|
||||||
|
<arg name="timeout" type="uint" summary="minimum idle timeout in msec"/>
|
||||||
|
<arg name="seat" type="object" interface="wl_seat"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="ext_idle_notification_v1" version="2">
|
||||||
|
<description summary="idle notification">
|
||||||
|
This interface is used by the compositor to send idle notification events
|
||||||
|
to clients.
|
||||||
|
|
||||||
|
Initially the notification object is not idle. The notification object
|
||||||
|
becomes idle when no user activity has happened for at least the timeout
|
||||||
|
duration, starting from the creation of the notification object. User
|
||||||
|
activity may include input events or a presence sensor, but is
|
||||||
|
compositor-specific.
|
||||||
|
|
||||||
|
How this notification responds to idle inhibitors depends on how
|
||||||
|
it was constructed. If constructed from the
|
||||||
|
get_idle_notification request, then if an idle inhibitor is
|
||||||
|
active (e.g. another client has created a zwp_idle_inhibitor_v1
|
||||||
|
on a visible surface), the compositor must not make the
|
||||||
|
notification object idle. However, if constructed from the
|
||||||
|
get_input_idle_notification request, then idle inhibitors are
|
||||||
|
ignored, and only input from the user, e.g. from a keyboard or
|
||||||
|
mouse, counts as activity.
|
||||||
|
|
||||||
|
When the notification object becomes idle, an idled event is sent. When
|
||||||
|
user activity starts again, the notification object stops being idle,
|
||||||
|
a resumed event is sent and the timeout is restarted.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the notification object">
|
||||||
|
Destroy the notification object.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="idled">
|
||||||
|
<description summary="notification object is idle">
|
||||||
|
This event is sent when the notification object becomes idle.
|
||||||
|
|
||||||
|
It's a compositor protocol error to send this event twice without a
|
||||||
|
resumed event in-between.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="resumed">
|
||||||
|
<description summary="notification object is no longer idle">
|
||||||
|
This event is sent when the notification object stops being idle.
|
||||||
|
|
||||||
|
It's a compositor protocol error to send this event twice without an
|
||||||
|
idled event in-between. It's a compositor protocol error to send this
|
||||||
|
event prior to any idled event.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
||||||
@@ -29,6 +29,7 @@ client_protocols = [
|
|||||||
['river-status-unstable-v1.xml'],
|
['river-status-unstable-v1.xml'],
|
||||||
['river-control-unstable-v1.xml'],
|
['river-control-unstable-v1.xml'],
|
||||||
['dwl-ipc-unstable-v2.xml'],
|
['dwl-ipc-unstable-v2.xml'],
|
||||||
|
['ext-idle-notify-v1.xml'],
|
||||||
]
|
]
|
||||||
|
|
||||||
if wayland_protos.version().version_compare('>=1.39')
|
if wayland_protos.version().version_compare('>=1.39')
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
#include <gtk-layer-shell.h>
|
#include <gtk-layer-shell.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "gtkmm/icontheme.h"
|
#include "gtkmm/icontheme.h"
|
||||||
|
#include "ext-idle-notify-v1-client-protocol.h"
|
||||||
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
||||||
#include "util/clara.hpp"
|
#include "util/clara.hpp"
|
||||||
#include "util/format.hpp"
|
#include "util/format.hpp"
|
||||||
@@ -39,6 +41,12 @@ void waybar::Client::handleGlobal(void* data, struct wl_registry* registry, uint
|
|||||||
|
|
||||||
client->idle_inhibit_manager = static_cast<struct zwp_idle_inhibit_manager_v1*>(
|
client->idle_inhibit_manager = static_cast<struct zwp_idle_inhibit_manager_v1*>(
|
||||||
wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface, 1));
|
wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface, 1));
|
||||||
|
} else if (strcmp(interface, ext_idle_notifier_v1_interface.name) == 0) {
|
||||||
|
// Bind version 2 if available (for get_input_idle_notification), otherwise version 1
|
||||||
|
auto bind_version = std::min(version, 2u);
|
||||||
|
client->idle_notifier = static_cast<struct ext_idle_notifier_v1 *>(
|
||||||
|
wl_registry_bind(registry, name, &ext_idle_notifier_v1_interface, bind_version));
|
||||||
|
spdlog::debug("Bound ext-idle-notifier-v1 at version {}", bind_version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "modules/idle_inhibitor.hpp"
|
#include "modules/idle_inhibitor.hpp"
|
||||||
|
|
||||||
|
#include "ext-idle-notify-v1-client-protocol.h"
|
||||||
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
||||||
#include "util/command.hpp"
|
#include "util/command.hpp"
|
||||||
|
|
||||||
@@ -11,11 +12,24 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar&
|
|||||||
: ALabel(config, "idle_inhibitor", id, "{status}", 0, false, true),
|
: ALabel(config, "idle_inhibitor", id, "{status}", 0, false, true),
|
||||||
bar_(bar),
|
bar_(bar),
|
||||||
idle_inhibitor_(nullptr),
|
idle_inhibitor_(nullptr),
|
||||||
pid_(-1) {
|
idle_notification_(nullptr),
|
||||||
|
idle_timeout_ms_(0),
|
||||||
|
pid_(-1),
|
||||||
|
wait_for_activity_(false) {
|
||||||
if (waybar::Client::inst()->idle_inhibit_manager == nullptr) {
|
if (waybar::Client::inst()->idle_inhibit_manager == nullptr) {
|
||||||
throw std::runtime_error("idle-inhibit not available");
|
throw std::runtime_error("idle-inhibit not available");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read the wait-for-activity config option
|
||||||
|
if (config_["wait-for-activity"].isBool()) {
|
||||||
|
wait_for_activity_ = config_["wait-for-activity"].asBool();
|
||||||
|
|
||||||
|
// Check if ext-idle-notify protocol is available when wait-for-activity is enabled
|
||||||
|
if (wait_for_activity_ && waybar::Client::inst()->idle_notifier == nullptr) {
|
||||||
|
throw std::runtime_error("wait-for-activity requires ext-idle-notify-v1 protocol support");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (waybar::modules::IdleInhibitor::modules.empty() && config_["start-activated"].isBool() &&
|
if (waybar::modules::IdleInhibitor::modules.empty() && config_["start-activated"].isBool() &&
|
||||||
config_["start-activated"].asBool() != status) {
|
config_["start-activated"].asBool() != status) {
|
||||||
toggleStatus();
|
toggleStatus();
|
||||||
@@ -32,6 +46,8 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar&
|
|||||||
}
|
}
|
||||||
|
|
||||||
waybar::modules::IdleInhibitor::~IdleInhibitor() {
|
waybar::modules::IdleInhibitor::~IdleInhibitor() {
|
||||||
|
teardownIdleNotification();
|
||||||
|
|
||||||
if (idle_inhibitor_ != nullptr) {
|
if (idle_inhibitor_ != nullptr) {
|
||||||
zwp_idle_inhibitor_v1_destroy(idle_inhibitor_);
|
zwp_idle_inhibitor_v1_destroy(idle_inhibitor_);
|
||||||
idle_inhibitor_ = nullptr;
|
idle_inhibitor_ = nullptr;
|
||||||
@@ -77,6 +93,17 @@ auto waybar::modules::IdleInhibitor::update() -> void {
|
|||||||
ALabel::update();
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::IdleInhibitor::refresh(int sig) -> void {
|
||||||
|
if (config_["signal"].isInt() && sig == SIGRTMIN + config_["signal"].asInt()) {
|
||||||
|
toggleStatus();
|
||||||
|
|
||||||
|
// Make all other idle inhibitor modules update
|
||||||
|
for (auto const& module : waybar::modules::IdleInhibitor::modules) {
|
||||||
|
module->update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void waybar::modules::IdleInhibitor::toggleStatus() {
|
void waybar::modules::IdleInhibitor::toggleStatus() {
|
||||||
status = !status;
|
status = !status;
|
||||||
|
|
||||||
@@ -88,7 +115,16 @@ void waybar::modules::IdleInhibitor::toggleStatus() {
|
|||||||
if (status && config_["timeout"].isNumeric()) {
|
if (status && config_["timeout"].isNumeric()) {
|
||||||
auto timeoutMins = config_["timeout"].asDouble();
|
auto timeoutMins = config_["timeout"].asDouble();
|
||||||
int timeoutSecs = timeoutMins * 60;
|
int timeoutSecs = timeoutMins * 60;
|
||||||
|
idle_timeout_ms_ = timeoutSecs * 1000;
|
||||||
|
|
||||||
|
// If wait-for-activity is enabled, set up idle notification
|
||||||
|
if (wait_for_activity_) {
|
||||||
|
spdlog::debug("idle_inhibitor: wait-for-activity enabled, timeout: {} ms", idle_timeout_ms_);
|
||||||
|
// Tear down any existing notification first to ensure fresh setup
|
||||||
|
teardownIdleNotification();
|
||||||
|
setupIdleNotification();
|
||||||
|
} else {
|
||||||
|
// Original behavior: simple timeout
|
||||||
timeout_ = Glib::signal_timeout().connect_seconds(
|
timeout_ = Glib::signal_timeout().connect_seconds(
|
||||||
[]() {
|
[]() {
|
||||||
/* intentionally not tied to a module instance lifetime
|
/* intentionally not tied to a module instance lifetime
|
||||||
@@ -104,6 +140,10 @@ void waybar::modules::IdleInhibitor::toggleStatus() {
|
|||||||
},
|
},
|
||||||
timeoutSecs);
|
timeoutSecs);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// When deactivated, tear down idle notification
|
||||||
|
teardownIdleNotification();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) {
|
bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) {
|
||||||
@@ -121,3 +161,96 @@ bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) {
|
|||||||
ALabel::handleToggle(e);
|
ALabel::handleToggle(e);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void waybar::modules::IdleInhibitor::handleIdled(void* data,
|
||||||
|
ext_idle_notification_v1* /*notification*/) {
|
||||||
|
spdlog::info("deactivating idle_inhibitor due to user inactivity");
|
||||||
|
status = false;
|
||||||
|
|
||||||
|
// Clean up the notification since we're deactivating
|
||||||
|
auto* self = static_cast<IdleInhibitor*>(data);
|
||||||
|
if (self != nullptr) {
|
||||||
|
self->teardownIdleNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& module : waybar::modules::IdleInhibitor::modules) {
|
||||||
|
module->update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void waybar::modules::IdleInhibitor::handleResumed(void* data,
|
||||||
|
ext_idle_notification_v1* /*notification*/) {
|
||||||
|
// User became active again - notification will continue monitoring
|
||||||
|
spdlog::debug("user activity detected, idle_inhibitor still active");
|
||||||
|
}
|
||||||
|
|
||||||
|
void waybar::modules::IdleInhibitor::setupIdleNotification() {
|
||||||
|
spdlog::debug("idle_inhibitor: setting up idle notification");
|
||||||
|
|
||||||
|
// Clean up any existing notification first
|
||||||
|
if (idle_notification_ != nullptr) {
|
||||||
|
spdlog::debug("idle_inhibitor: cleaning up existing notification before setup");
|
||||||
|
teardownIdleNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* client = waybar::Client::inst();
|
||||||
|
if (client->idle_notifier == nullptr) {
|
||||||
|
spdlog::error("ext-idle-notify protocol not available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the wayland seat from the display
|
||||||
|
auto* gdk_seat = gdk_display_get_default_seat(client->gdk_display->gobj());
|
||||||
|
if (gdk_seat == nullptr) {
|
||||||
|
spdlog::error("failed to get default seat");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto* wl_seat = gdk_wayland_seat_get_wl_seat(gdk_seat);
|
||||||
|
|
||||||
|
// Check protocol version to determine which function to use
|
||||||
|
uint32_t version =
|
||||||
|
wl_proxy_get_version(reinterpret_cast<struct wl_proxy*>(client->idle_notifier));
|
||||||
|
|
||||||
|
spdlog::debug("idle_inhibitor: creating notification with timeout {} ms (protocol version {})",
|
||||||
|
idle_timeout_ms_, version);
|
||||||
|
|
||||||
|
if (version >= 2) {
|
||||||
|
// Version 2+: Use get_input_idle_notification which ignores idle inhibitors
|
||||||
|
// This allows us to detect actual user inactivity even while the inhibitor is active
|
||||||
|
spdlog::debug("idle_inhibitor: using get_input_idle_notification (ignores inhibitors)");
|
||||||
|
idle_notification_ = ext_idle_notifier_v1_get_input_idle_notification(
|
||||||
|
client->idle_notifier, idle_timeout_ms_, wl_seat);
|
||||||
|
} else {
|
||||||
|
// Version 1: Fall back to get_idle_notification
|
||||||
|
// WARNING: This respects idle inhibitors, so it won't fire while inhibitor is active
|
||||||
|
spdlog::warn(
|
||||||
|
"idle_inhibitor: ext-idle-notifier-v1 version {} doesn't support "
|
||||||
|
"get_input_idle_notification, "
|
||||||
|
"wait-for-activity may not work correctly",
|
||||||
|
version);
|
||||||
|
idle_notification_ = ext_idle_notifier_v1_get_idle_notification(client->idle_notifier,
|
||||||
|
idle_timeout_ms_, wl_seat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idle_notification_ == nullptr) {
|
||||||
|
spdlog::error("idle_inhibitor: failed to create idle notification");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ext_idle_notification_v1_listener idle_notification_listener = {
|
||||||
|
.idled = &IdleInhibitor::handleIdled,
|
||||||
|
.resumed = &IdleInhibitor::handleResumed,
|
||||||
|
};
|
||||||
|
|
||||||
|
ext_idle_notification_v1_add_listener(idle_notification_, &idle_notification_listener, this);
|
||||||
|
wl_display_roundtrip(client->wl_display);
|
||||||
|
spdlog::debug("idle_inhibitor: idle notification setup complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
void waybar::modules::IdleInhibitor::teardownIdleNotification() {
|
||||||
|
if (idle_notification_ != nullptr) {
|
||||||
|
spdlog::debug("idle_inhibitor: tearing down idle notification");
|
||||||
|
ext_idle_notification_v1_destroy(idle_notification_);
|
||||||
|
idle_notification_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user