fix using the ext-idle-notify-v1 protocol
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
|
||||
struct zwp_idle_inhibitor_v1;
|
||||
struct zwp_idle_inhibit_manager_v1;
|
||||
struct ext_idle_notifier_v1;
|
||||
|
||||
namespace waybar {
|
||||
|
||||
@@ -27,6 +28,7 @@ class Client {
|
||||
struct wl_registry *registry = nullptr;
|
||||
struct zxdg_output_manager_v1 *xdg_output_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;
|
||||
Config config;
|
||||
std::string bar_id;
|
||||
|
||||
@@ -10,9 +10,8 @@ namespace waybar::modules {
|
||||
|
||||
class IdleInhibitor : public ALabel {
|
||||
sigc::connection timeout_;
|
||||
sigc::connection activity_timeout_;
|
||||
sigc::connection motion_connection_;
|
||||
sigc::connection key_connection_;
|
||||
struct ext_idle_notification_v1* idle_notification_;
|
||||
uint32_t idle_timeout_ms_;
|
||||
|
||||
public:
|
||||
IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||
@@ -24,12 +23,11 @@ class IdleInhibitor : public ALabel {
|
||||
|
||||
private:
|
||||
bool handleToggle(GdkEventButton* const& e) override;
|
||||
bool handleMotion(GdkEventMotion* const& e);
|
||||
bool handleKey(GdkEventKey* const& e);
|
||||
void toggleStatus();
|
||||
void resetActivityTimeout();
|
||||
void setupActivityMonitoring();
|
||||
void teardownActivityMonitoring();
|
||||
void setupIdleNotification();
|
||||
void teardownIdleNotification();
|
||||
static void handleIdled(void* data, struct ext_idle_notification_v1* notification);
|
||||
static void handleResumed(void* data, struct ext_idle_notification_v1* notification);
|
||||
|
||||
const Bar& bar_;
|
||||
struct zwp_idle_inhibitor_v1* 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-control-unstable-v1.xml'],
|
||||
['dwl-ipc-unstable-v2.xml'],
|
||||
['ext-idle-notify-v1.xml'],
|
||||
]
|
||||
|
||||
if wayland_protos.version().version_compare('>=1.39')
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "gtkmm/icontheme.h"
|
||||
#include "ext-idle-notify-v1-client-protocol.h"
|
||||
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
||||
#include "util/clara.hpp"
|
||||
#include "util/format.hpp"
|
||||
@@ -26,6 +27,9 @@ void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint
|
||||
} else if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == 0) {
|
||||
client->idle_inhibit_manager = static_cast<struct zwp_idle_inhibit_manager_v1 *>(
|
||||
wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface, 1));
|
||||
} else if (strcmp(interface, ext_idle_notifier_v1_interface.name) == 0) {
|
||||
client->idle_notifier = static_cast<struct ext_idle_notifier_v1 *>(
|
||||
wl_registry_bind(registry, name, &ext_idle_notifier_v1_interface, 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "modules/idle_inhibitor.hpp"
|
||||
|
||||
#include "ext-idle-notify-v1-client-protocol.h"
|
||||
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
||||
#include "util/command.hpp"
|
||||
|
||||
@@ -11,6 +12,8 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar&
|
||||
: ALabel(config, "idle_inhibitor", id, "{status}", 0, false, true),
|
||||
bar_(bar),
|
||||
idle_inhibitor_(nullptr),
|
||||
idle_notification_(nullptr),
|
||||
idle_timeout_ms_(0),
|
||||
pid_(-1),
|
||||
wait_for_activity_(false) {
|
||||
if (waybar::Client::inst()->idle_inhibit_manager == nullptr) {
|
||||
@@ -20,6 +23,11 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar&
|
||||
// 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() &&
|
||||
@@ -38,7 +46,7 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar&
|
||||
}
|
||||
|
||||
waybar::modules::IdleInhibitor::~IdleInhibitor() {
|
||||
teardownActivityMonitoring();
|
||||
teardownIdleNotification();
|
||||
|
||||
if (idle_inhibitor_ != nullptr) {
|
||||
zwp_idle_inhibitor_v1_destroy(idle_inhibitor_);
|
||||
@@ -107,11 +115,11 @@ void waybar::modules::IdleInhibitor::toggleStatus() {
|
||||
if (status && config_["timeout"].isNumeric()) {
|
||||
auto timeoutMins = config_["timeout"].asDouble();
|
||||
int timeoutSecs = timeoutMins * 60;
|
||||
idle_timeout_ms_ = timeoutSecs * 1000;
|
||||
|
||||
// If wait-for-activity is enabled, set up activity monitoring
|
||||
// If wait-for-activity is enabled, set up idle notification
|
||||
if (wait_for_activity_) {
|
||||
setupActivityMonitoring();
|
||||
resetActivityTimeout();
|
||||
setupIdleNotification();
|
||||
} else {
|
||||
// Original behavior: simple timeout
|
||||
timeout_ = Glib::signal_timeout().connect_seconds(
|
||||
@@ -130,8 +138,8 @@ void waybar::modules::IdleInhibitor::toggleStatus() {
|
||||
timeoutSecs);
|
||||
}
|
||||
} else {
|
||||
// When deactivated, tear down activity monitoring
|
||||
teardownActivityMonitoring();
|
||||
// When deactivated, tear down idle notification
|
||||
teardownIdleNotification();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,77 +159,59 @@ bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool waybar::modules::IdleInhibitor::handleMotion(GdkEventMotion* const& e) {
|
||||
if (wait_for_activity_ && status) {
|
||||
resetActivityTimeout();
|
||||
void waybar::modules::IdleInhibitor::handleIdled(void* data,
|
||||
struct ext_idle_notification_v1* /*notification*/) {
|
||||
spdlog::info("deactivating idle_inhibitor due to user inactivity");
|
||||
status = false;
|
||||
for (auto const& module : waybar::modules::IdleInhibitor::modules) {
|
||||
module->update();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool waybar::modules::IdleInhibitor::handleKey(GdkEventKey* const& e) {
|
||||
if (wait_for_activity_ && status) {
|
||||
resetActivityTimeout();
|
||||
}
|
||||
return false;
|
||||
void waybar::modules::IdleInhibitor::handleResumed(void* data,
|
||||
struct 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::resetActivityTimeout() {
|
||||
if (!config_["timeout"].isNumeric()) {
|
||||
void waybar::modules::IdleInhibitor::setupIdleNotification() {
|
||||
// Don't set up if already exists
|
||||
if (idle_notification_ != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (activity_timeout_.connected()) {
|
||||
activity_timeout_.disconnect();
|
||||
}
|
||||
|
||||
auto timeoutMins = config_["timeout"].asDouble();
|
||||
int timeoutSecs = timeoutMins * 60;
|
||||
|
||||
activity_timeout_ = Glib::signal_timeout().connect_seconds(
|
||||
[]() {
|
||||
spdlog::info("deactivating idle_inhibitor due to inactivity");
|
||||
status = false;
|
||||
for (auto const& module : waybar::modules::IdleInhibitor::modules) {
|
||||
module->update();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
timeoutSecs);
|
||||
}
|
||||
|
||||
void waybar::modules::IdleInhibitor::setupActivityMonitoring() {
|
||||
// Don't set up if already connected
|
||||
if (motion_connection_.connected() || key_connection_.connected()) {
|
||||
auto* client = waybar::Client::inst();
|
||||
if (client->idle_notifier == nullptr) {
|
||||
spdlog::error("ext-idle-notify protocol not available");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get non-const reference to the window to set up event monitoring
|
||||
// This is safe because we're only setting up signal handlers, not modifying the Bar itself
|
||||
auto& window = const_cast<Gtk::Window&>(bar_.window);
|
||||
|
||||
// Enable motion and key event monitoring on the bar window
|
||||
auto gdk_window = window.get_window();
|
||||
if (gdk_window) {
|
||||
gdk_window->set_events(gdk_window->get_events() | Gdk::POINTER_MOTION_MASK | Gdk::KEY_PRESS_MASK);
|
||||
// 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);
|
||||
|
||||
// Connect to the bar window's event signals
|
||||
motion_connection_ = window.signal_motion_notify_event().connect(
|
||||
sigc::mem_fun(*this, &IdleInhibitor::handleMotion));
|
||||
key_connection_ = window.signal_key_press_event().connect(
|
||||
sigc::mem_fun(*this, &IdleInhibitor::handleKey));
|
||||
// Create idle notification that monitors all input (not just when inhibitor is active)
|
||||
// We use get_idle_notification instead of get_input_idle_notification to respect
|
||||
// idle inhibitors from other applications
|
||||
idle_notification_ = ext_idle_notifier_v1_get_idle_notification(
|
||||
client->idle_notifier, idle_timeout_ms_, wl_seat);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void waybar::modules::IdleInhibitor::teardownActivityMonitoring() {
|
||||
if (activity_timeout_.connected()) {
|
||||
activity_timeout_.disconnect();
|
||||
}
|
||||
|
||||
if (motion_connection_.connected()) {
|
||||
motion_connection_.disconnect();
|
||||
}
|
||||
|
||||
if (key_connection_.connected()) {
|
||||
key_connection_.disconnect();
|
||||
void waybar::modules::IdleInhibitor::teardownIdleNotification() {
|
||||
if (idle_notification_ != nullptr) {
|
||||
ext_idle_notification_v1_destroy(idle_notification_);
|
||||
idle_notification_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user