mirror of
https://github.com/2e3s/awatcher.git
synced 2025-06-07 12:05:49 +00:00
Add ext-idle-notify-v1 support
This commit is contained in:
parent
70001c3523
commit
06b30f4fc0
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -447,7 +447,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "awatcher"
|
name = "awatcher"
|
||||||
version = "0.2.2"
|
version = "0.2.4-alpha1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"aw-datastore",
|
"aw-datastore",
|
||||||
@ -3821,7 +3821,7 @@ checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "watchers"
|
name = "watchers"
|
||||||
version = "0.2.2"
|
version = "0.2.4-alpha1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
@ -18,7 +18,7 @@ image = { version = "0.24.6" }
|
|||||||
members = ["watchers"]
|
members = ["watchers"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.2.2"
|
version = "0.2.4-alpha1"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
|
@ -7,6 +7,7 @@ mod idle;
|
|||||||
mod kwin_window;
|
mod kwin_window;
|
||||||
mod wl_bindings;
|
mod wl_bindings;
|
||||||
mod wl_connection;
|
mod wl_connection;
|
||||||
|
mod wl_ext_idle_notify;
|
||||||
mod wl_foreign_toplevel;
|
mod wl_foreign_toplevel;
|
||||||
mod wl_kwin_idle;
|
mod wl_kwin_idle;
|
||||||
mod x11_connection;
|
mod x11_connection;
|
||||||
@ -74,6 +75,7 @@ async fn filter_first_supported(
|
|||||||
) -> Option<Box<dyn Watcher>> {
|
) -> Option<Box<dyn Watcher>> {
|
||||||
match watcher_type {
|
match watcher_type {
|
||||||
WatcherType::Idle => {
|
WatcherType::Idle => {
|
||||||
|
watch!(client, watcher_type, wl_ext_idle_notify::IdleWatcher);
|
||||||
watch!(client, watcher_type, wl_kwin_idle::IdleWatcher);
|
watch!(client, watcher_type, wl_kwin_idle::IdleWatcher);
|
||||||
watch!(client, watcher_type, x11_screensaver_idle::IdleWatcher);
|
watch!(client, watcher_type, x11_screensaver_idle::IdleWatcher);
|
||||||
#[cfg(feature = "gnome")]
|
#[cfg(feature = "gnome")]
|
||||||
|
102
watchers/src/watchers/wl-protocols/ext-idle-notify-v1.xml
Normal file
102
watchers/src/watchers/wl-protocols/ext-idle-notify-v1.xml
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<?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="1">
|
||||||
|
<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>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="ext_idle_notification_v1" version="1">
|
||||||
|
<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. 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.
|
||||||
|
|
||||||
|
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>
|
@ -21,6 +21,25 @@ pub mod idle {
|
|||||||
wayland_scanner::generate_client_code!("src/watchers/wl-protocols/idle.xml");
|
wayland_scanner::generate_client_code!("src/watchers/wl-protocols/idle.xml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod ext_idle {
|
||||||
|
#![allow(dead_code,non_camel_case_types,unused_unsafe,unused_variables)]
|
||||||
|
#![allow(non_upper_case_globals,non_snake_case,unused_imports)]
|
||||||
|
#![allow(missing_docs, clippy::all)]
|
||||||
|
#![allow(clippy::wildcard_imports)]
|
||||||
|
|
||||||
|
//! Client-side API of this protocol
|
||||||
|
use wayland_client;
|
||||||
|
use wayland_client::protocol::*;
|
||||||
|
|
||||||
|
pub mod __interfaces {
|
||||||
|
use wayland_client::protocol::__interfaces::*;
|
||||||
|
wayland_scanner::generate_interfaces!("src/watchers/wl-protocols/ext-idle-notify-v1.xml");
|
||||||
|
}
|
||||||
|
use self::__interfaces::*;
|
||||||
|
|
||||||
|
wayland_scanner::generate_client_code!("src/watchers/wl-protocols/ext-idle-notify-v1.xml");
|
||||||
|
}
|
||||||
|
|
||||||
pub mod wlr_foreign_toplevel {
|
pub mod wlr_foreign_toplevel {
|
||||||
#![allow(dead_code,non_camel_case_types,unused_unsafe,unused_variables)]
|
#![allow(dead_code,non_camel_case_types,unused_unsafe,unused_variables)]
|
||||||
#![allow(non_upper_case_globals,non_snake_case,unused_imports)]
|
#![allow(non_upper_case_globals,non_snake_case,unused_imports)]
|
||||||
|
@ -5,6 +5,9 @@ use wayland_client::{
|
|||||||
protocol::{wl_registry, wl_seat::WlSeat},
|
protocol::{wl_registry, wl_seat::WlSeat},
|
||||||
Connection, Dispatch, EventQueue, Proxy, QueueHandle,
|
Connection, Dispatch, EventQueue, Proxy, QueueHandle,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use wl_bindings::ext_idle::ext_idle_notification_v1::ExtIdleNotificationV1;
|
||||||
|
use wl_bindings::ext_idle::ext_idle_notifier_v1::ExtIdleNotifierV1;
|
||||||
use wl_bindings::idle::org_kde_kwin_idle::OrgKdeKwinIdle;
|
use wl_bindings::idle::org_kde_kwin_idle::OrgKdeKwinIdle;
|
||||||
use wl_bindings::idle::org_kde_kwin_idle_timeout::OrgKdeKwinIdleTimeout;
|
use wl_bindings::idle::org_kde_kwin_idle_timeout::OrgKdeKwinIdleTimeout;
|
||||||
use wl_bindings::wlr_foreign_toplevel::zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1;
|
use wl_bindings::wlr_foreign_toplevel::zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1;
|
||||||
@ -81,6 +84,33 @@ where
|
|||||||
.map_err(std::convert::Into::into)
|
.map_err(std::convert::Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_ext_idle(&self) -> anyhow::Result<ExtIdleNotifierV1>
|
||||||
|
where
|
||||||
|
T: Dispatch<ExtIdleNotifierV1, ()>,
|
||||||
|
{
|
||||||
|
self.globals
|
||||||
|
.bind::<ExtIdleNotifierV1, T, ()>(
|
||||||
|
&self.queue_handle,
|
||||||
|
1..=ExtIdleNotifierV1::interface().version,
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
.map_err(std::convert::Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_ext_idle_notification(&self, timeout: u32) -> anyhow::Result<ExtIdleNotificationV1>
|
||||||
|
where
|
||||||
|
T: Dispatch<ExtIdleNotifierV1, ()>
|
||||||
|
+ Dispatch<WlSeat, ()>
|
||||||
|
+ Dispatch<ExtIdleNotificationV1, ()>,
|
||||||
|
{
|
||||||
|
let seat: WlSeat =
|
||||||
|
self.globals
|
||||||
|
.bind(&self.queue_handle, 1..=WlSeat::interface().version, ())?;
|
||||||
|
|
||||||
|
let idle = self.get_ext_idle()?;
|
||||||
|
Ok(idle.get_idle_notification(timeout, &seat, &self.queue_handle, ()))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_kwin_idle_timeout(&self, timeout: u32) -> anyhow::Result<OrgKdeKwinIdleTimeout>
|
pub fn get_kwin_idle_timeout(&self, timeout: u32) -> anyhow::Result<OrgKdeKwinIdleTimeout>
|
||||||
where
|
where
|
||||||
T: Dispatch<OrgKdeKwinIdle, ()>
|
T: Dispatch<OrgKdeKwinIdle, ()>
|
||||||
|
159
watchers/src/watchers/wl_ext_idle_notify.rs
Normal file
159
watchers/src/watchers/wl_ext_idle_notify.rs
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
use super::wl_bindings;
|
||||||
|
use super::wl_connection::{subscribe_state, WlEventConnection};
|
||||||
|
use super::Watcher;
|
||||||
|
use crate::report_client::ReportClient;
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use chrono::{DateTime, Duration, Utc};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use wayland_client::{
|
||||||
|
globals::GlobalListContents,
|
||||||
|
protocol::{wl_registry, wl_seat::WlSeat},
|
||||||
|
Connection, Dispatch, Proxy, QueueHandle,
|
||||||
|
};
|
||||||
|
use wl_bindings::ext_idle::ext_idle_notification_v1::Event as ExtIdleNotificationV1Event;
|
||||||
|
use wl_bindings::ext_idle::ext_idle_notification_v1::ExtIdleNotificationV1;
|
||||||
|
use wl_bindings::ext_idle::ext_idle_notifier_v1::ExtIdleNotifierV1;
|
||||||
|
|
||||||
|
struct IdleState {
|
||||||
|
idle_notification: ExtIdleNotificationV1,
|
||||||
|
last_input_time: DateTime<Utc>,
|
||||||
|
is_idle: bool,
|
||||||
|
is_changed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for IdleState {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
info!("Releasing idle notification");
|
||||||
|
self.idle_notification.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdleState {
|
||||||
|
fn new(idle_notification: ExtIdleNotificationV1) -> Self {
|
||||||
|
Self {
|
||||||
|
idle_notification,
|
||||||
|
last_input_time: Utc::now(),
|
||||||
|
is_idle: false,
|
||||||
|
is_changed: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn idle(&mut self) {
|
||||||
|
self.is_idle = true;
|
||||||
|
self.is_changed = true;
|
||||||
|
debug!("Idle");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resume(&mut self) {
|
||||||
|
self.is_idle = false;
|
||||||
|
self.last_input_time = Utc::now();
|
||||||
|
self.is_changed = true;
|
||||||
|
debug!("Resumed");
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_ping(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()> {
|
||||||
|
let now = Utc::now();
|
||||||
|
if !self.is_idle {
|
||||||
|
self.last_input_time = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.is_changed {
|
||||||
|
let result = if self.is_idle {
|
||||||
|
debug!("Reporting as changed to idle");
|
||||||
|
client
|
||||||
|
.ping(false, self.last_input_time, Duration::zero())
|
||||||
|
.await?;
|
||||||
|
client
|
||||||
|
.ping(
|
||||||
|
true,
|
||||||
|
self.last_input_time + Duration::milliseconds(1),
|
||||||
|
Duration::zero(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
} else {
|
||||||
|
debug!("Reporting as no longer idle");
|
||||||
|
|
||||||
|
client
|
||||||
|
.ping(true, self.last_input_time, Duration::zero())
|
||||||
|
.await?;
|
||||||
|
client
|
||||||
|
.ping(
|
||||||
|
false,
|
||||||
|
self.last_input_time + Duration::milliseconds(1),
|
||||||
|
Duration::zero(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
};
|
||||||
|
self.is_changed = false;
|
||||||
|
result
|
||||||
|
} else if self.is_idle {
|
||||||
|
trace!("Reporting as idle");
|
||||||
|
client
|
||||||
|
.ping(true, self.last_input_time, now - self.last_input_time)
|
||||||
|
.await
|
||||||
|
} else {
|
||||||
|
trace!("Reporting as not idle");
|
||||||
|
client
|
||||||
|
.ping(false, self.last_input_time, Duration::zero())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe_state!(wl_registry::WlRegistry, GlobalListContents, IdleState);
|
||||||
|
subscribe_state!(wl_registry::WlRegistry, (), IdleState);
|
||||||
|
subscribe_state!(WlSeat, (), IdleState);
|
||||||
|
subscribe_state!(ExtIdleNotifierV1, (), IdleState);
|
||||||
|
|
||||||
|
impl Dispatch<ExtIdleNotificationV1, ()> for IdleState {
|
||||||
|
fn event(
|
||||||
|
state: &mut Self,
|
||||||
|
_: &ExtIdleNotificationV1,
|
||||||
|
event: <ExtIdleNotificationV1 as Proxy>::Event,
|
||||||
|
_: &(),
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
if let ExtIdleNotificationV1Event::Idled = event {
|
||||||
|
state.idle();
|
||||||
|
} else if let ExtIdleNotificationV1Event::Resumed = event {
|
||||||
|
state.resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IdleWatcher {
|
||||||
|
connection: WlEventConnection<IdleState>,
|
||||||
|
idle_state: IdleState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Watcher for IdleWatcher {
|
||||||
|
async fn new(client: &Arc<ReportClient>) -> anyhow::Result<Self> {
|
||||||
|
let mut connection: WlEventConnection<IdleState> = WlEventConnection::connect()?;
|
||||||
|
connection.get_ext_idle()?;
|
||||||
|
|
||||||
|
let timeout = u32::try_from(client.config.idle_timeout.as_secs() * 1000);
|
||||||
|
let mut idle_state = IdleState::new(
|
||||||
|
connection
|
||||||
|
.get_ext_idle_notification(timeout.unwrap())
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
connection.event_queue.roundtrip(&mut idle_state).unwrap();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
connection,
|
||||||
|
idle_state,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run_iteration(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()> {
|
||||||
|
self.connection
|
||||||
|
.event_queue
|
||||||
|
.roundtrip(&mut self.idle_state)
|
||||||
|
.map_err(|e| anyhow!("Event queue is not processed: {e}"))?;
|
||||||
|
|
||||||
|
self.idle_state.send_ping(client).await
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user