Skip to content

Commit

Permalink
wayland: implement ext-idle-notifier
Browse files Browse the repository at this point in the history
  • Loading branch information
mahkoh committed Feb 15, 2024
1 parent 01e3930 commit 9a024fe
Show file tree
Hide file tree
Showing 9 changed files with 287 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use {
client::Client,
ifs::{
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1Global,
ext_idle_notifier_v1::ExtIdleNotifierV1Global,
ext_session_lock_manager_v1::ExtSessionLockManagerV1Global,
ipc::{
wl_data_device_manager::WlDataDeviceManagerGlobal,
Expand Down Expand Up @@ -167,6 +168,7 @@ impl Globals {
add_singleton!(XdgActivationV1Global);
add_singleton!(ExtForeignToplevelListV1Global);
add_singleton!(ZwpIdleInhibitManagerV1Global);
add_singleton!(ExtIdleNotifierV1Global);
}

pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
Expand Down
2 changes: 2 additions & 0 deletions src/ifs.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pub mod ext_foreign_toplevel_handle_v1;
pub mod ext_foreign_toplevel_list_v1;
pub mod ext_idle_notification_v1;
pub mod ext_idle_notifier_v1;
pub mod ext_session_lock_manager_v1;
pub mod ext_session_lock_v1;
pub mod ipc;
Expand Down
72 changes: 72 additions & 0 deletions src/ifs/ext_idle_notification_v1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use {
crate::{
async_engine::SpawnedFuture,
client::{Client, ClientError},
ifs::wl_seat::WlSeatGlobal,
leaks::Tracker,
object::Object,
utils::{
asyncevent::AsyncEvent,
buffd::{MsgParser, MsgParserError},
},
wire::{ext_idle_notification_v1::*, ExtIdleNotificationV1Id},
},
std::{cell::Cell, rc::Rc},
thiserror::Error,
};

pub struct ExtIdleNotificationV1 {
pub id: ExtIdleNotificationV1Id,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
pub resume: AsyncEvent,
pub task: Cell<Option<SpawnedFuture<()>>>,
pub seat: Rc<WlSeatGlobal>,
pub duration_usec: u64,
}

impl ExtIdleNotificationV1 {
fn detach(&self) {
self.seat.remove_idle_notification(self);
self.task.take();
}

fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), ExtIdleNotificationV1Error> {
let _req: Destroy = self.client.parse(self, msg)?;
self.detach();
self.client.remove_obj(self)?;
Ok(())
}

pub fn send_idled(&self) {
self.client.event(Idled { self_id: self.id });
}

pub fn send_resumed(&self) {
self.client.event(Resumed { self_id: self.id });
}
}

object_base! {
self = ExtIdleNotificationV1;

DESTROY => destroy,
}

impl Object for ExtIdleNotificationV1 {
fn break_loops(&self) {
self.detach();
}
}

simple_add_obj!(ExtIdleNotificationV1);

#[derive(Debug, Error)]
pub enum ExtIdleNotificationV1Error {
#[error("Parsing failed")]
MsgParserError(#[source] Box<MsgParserError>),
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(ExtIdleNotificationV1Error, MsgParserError);
efrom!(ExtIdleNotificationV1Error, ClientError);
142 changes: 142 additions & 0 deletions src/ifs/ext_idle_notifier_v1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use {
crate::{
client::{Client, ClientError},
globals::{Global, GlobalName},
ifs::ext_idle_notification_v1::ExtIdleNotificationV1,
leaks::Tracker,
object::Object,
time::now_usec,
utils::{
buffd::{MsgParser, MsgParserError},
errorfmt::ErrorFmt,
},
wire::{ext_idle_notifier_v1::*, ExtIdleNotifierV1Id},
},
std::{cell::Cell, rc::Rc},
thiserror::Error,
};

pub struct ExtIdleNotifierV1Global {
pub name: GlobalName,
}

impl ExtIdleNotifierV1Global {
pub fn new(name: GlobalName) -> Self {
Self { name }
}

fn bind_(
self: Rc<Self>,
id: ExtIdleNotifierV1Id,
client: &Rc<Client>,
_version: u32,
) -> Result<(), ExtIdleNotifierV1Error> {
let obj = Rc::new(ExtIdleNotifierV1 {
id,
client: client.clone(),
tracker: Default::default(),
});
track!(client, obj);
client.add_client_obj(&obj)?;
Ok(())
}
}

pub struct ExtIdleNotifierV1 {
pub id: ExtIdleNotifierV1Id,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
}

impl ExtIdleNotifierV1 {
fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), ExtIdleNotifierV1Error> {
let _req: Destroy = self.client.parse(self, msg)?;
self.client.remove_obj(self)?;
Ok(())
}

fn get_idle_notification(&self, msg: MsgParser<'_, '_>) -> Result<(), ExtIdleNotifierV1Error> {
let req: GetIdleNotification = self.client.parse(self, msg)?;
let seat = self.client.lookup(req.seat)?;
let notification = Rc::new(ExtIdleNotificationV1 {
id: req.id,
client: self.client.clone(),
tracker: Default::default(),
resume: Default::default(),
task: Cell::new(None),
seat: seat.global.clone(),
duration_usec: (req.timeout as u64).max(1000).saturating_mul(1000),
});
self.client.add_client_obj(&notification)?;
let future = self.client.state.eng.spawn(run(notification.clone()));
notification.task.set(Some(future));
Ok(())
}
}

async fn run(n: Rc<ExtIdleNotificationV1>) {
loop {
let now = now_usec();
let elapsed = now.saturating_sub(n.seat.last_input());
if elapsed < n.duration_usec {
let res = n
.client
.state
.wheel
.timeout((n.duration_usec - elapsed + 999) / 1000)
.await;
if let Err(e) = res {
log::error!("Could not wait for idle timeout to elapse: {}", ErrorFmt(e));
return;
}
} else {
n.send_idled();
n.seat.add_idle_notification(&n);
n.resume.triggered().await;
n.send_resumed();
}
}
}

global_base!(
ExtIdleNotifierV1Global,
ExtIdleNotifierV1,
ExtIdleNotifierV1Error
);

impl Global for ExtIdleNotifierV1Global {
fn singleton(&self) -> bool {
true
}

fn version(&self) -> u32 {
1
}

fn secure(&self) -> bool {
true
}
}

simple_add_global!(ExtIdleNotifierV1Global);

object_base! {
self = ExtIdleNotifierV1;

DESTROY => destroy,
GET_IDLE_NOTIFICATION => get_idle_notification,
}

impl Object for ExtIdleNotifierV1 {}

simple_add_obj!(ExtIdleNotifierV1);

#[derive(Debug, Error)]
pub enum ExtIdleNotifierV1Error {
#[error("Parsing failed")]
MsgParserError(#[source] Box<MsgParserError>),
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(ExtIdleNotifierV1Error, MsgParserError);
efrom!(ExtIdleNotifierV1Error, ClientError);
28 changes: 25 additions & 3 deletions src/ifs/wl_seat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ use {
fixed::Fixed,
globals::{Global, GlobalName},
ifs::{
ipc,
ext_idle_notification_v1::ExtIdleNotificationV1,
ipc::{
self,
wl_data_device::{ClipboardIpc, WlDataDevice},
wl_data_source::WlDataSource,
zwp_primary_selection_device_v1::{
Expand All @@ -42,6 +43,7 @@ use {
object::Object,
rect::Rect,
state::State,
time::now_usec,
tree::{
generic_node_visitor, ContainerNode, ContainerSplit, Direction, FloatNode, FoundNode,
Node, OutputNode, WorkspaceNode,
Expand All @@ -57,8 +59,8 @@ use {
rc_eq::rc_eq,
},
wire::{
wl_seat::*, WlDataDeviceId, WlKeyboardId, WlPointerId, WlSeatId,
ZwpPrimarySelectionDeviceV1Id, ZwpRelativePointerV1Id,
wl_seat::*, ExtIdleNotificationV1Id, WlDataDeviceId, WlKeyboardId, WlPointerId,
WlSeatId, ZwpPrimarySelectionDeviceV1Id, ZwpRelativePointerV1Id,
},
xkbcommon::{XkbKeymap, XkbState},
},
Expand Down Expand Up @@ -155,6 +157,8 @@ pub struct WlSeatGlobal {
cursor_size: Cell<u32>,
hardware_cursor: Cell<bool>,
constraint: CloneCell<Option<Rc<SeatConstraint>>>,
idle_notifications: CopyHashMap<(ClientId, ExtIdleNotificationV1Id), Rc<ExtIdleNotificationV1>>,
last_input_usec: Cell<u64>,
}

const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
Expand Down Expand Up @@ -209,6 +213,8 @@ impl WlSeatGlobal {
cursor_size: Cell::new(DEFAULT_CURSOR_SIZE),
hardware_cursor: Cell::new(state.globals.seats.len() == 0),
constraint: Default::default(),
idle_notifications: Default::default(),
last_input_usec: Cell::new(now_usec()),
});
state.add_cursor_size(DEFAULT_CURSOR_SIZE);
let seat = slf.clone();
Expand Down Expand Up @@ -922,6 +928,22 @@ impl WlSeatGlobal {
}
Ok(())
}

pub fn add_idle_notification(&self, notification: &Rc<ExtIdleNotificationV1>) {
self.idle_notifications.set(
(notification.client.id, notification.id),
notification.clone(),
);
}

pub fn remove_idle_notification(&self, notification: &ExtIdleNotificationV1) {
self.idle_notifications
.remove(&(notification.client.id, notification.id));
}

pub fn last_input(&self) -> u64 {
self.last_input_usec.get()
}
}

global_base!(WlSeatGlobal, WlSeat, WlSeatError);
Expand Down
18 changes: 18 additions & 0 deletions src/ifs/wl_seat/event_handling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,24 @@ impl NodeSeatState {

impl WlSeatGlobal {
pub fn event(self: &Rc<Self>, dev: &DeviceHandlerData, event: InputEvent) {
match event {
InputEvent::Key { time_usec, .. }
| InputEvent::ConnectorPosition { time_usec, .. }
| InputEvent::Motion { time_usec, .. }
| InputEvent::Button { time_usec, .. }
| InputEvent::AxisFrame { time_usec, .. } => {
self.last_input_usec.set(time_usec);
if self.idle_notifications.is_not_empty() {
for (_, notification) in self.idle_notifications.lock().drain() {
notification.resume.trigger();
}
}
}
InputEvent::AxisPx { .. }
| InputEvent::AxisSource { .. }
| InputEvent::AxisStop { .. }
| InputEvent::Axis120 { .. } => {}
}
match event {
InputEvent::Key {
time_usec,
Expand Down
4 changes: 4 additions & 0 deletions src/utils/copyhashmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ impl<K: Eq + Hash, V> CopyHashMap<K, V> {
unsafe { self.map.get().deref().is_empty() }
}

pub fn is_not_empty(&self) -> bool {
!self.is_empty()
}

pub fn len(&self) -> usize {
unsafe { self.map.get().deref().len() }
}
Expand Down
12 changes: 12 additions & 0 deletions wire/ext_idle_notification_v1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# requests

msg destroy = 0 {
}

# events

msg idled = 0 {
}

msg resumed = 1 {
}
10 changes: 10 additions & 0 deletions wire/ext_idle_notifier_v1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# requests

msg destroy = 0 {
}

msg get_idle_notification = 1 {
id: id(ext_idle_notification_v1),
timeout: u32,
seat: id(wl_seat),
}

0 comments on commit 9a024fe

Please sign in to comment.