Skip to content

Commit 10bd62d

Browse files
committed
refactor: gate Wayland idle tracker implementation behind Linux target configuration
1 parent f9d9777 commit 10bd62d

2 files changed

Lines changed: 153 additions & 140 deletions

File tree

Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,10 @@ discord-rich-presence = "0.2.5"
1717
clap = { version = "4.5.40", features = ["derive"] }
1818
colored = "3.0.0"
1919
user-idle = { version = "0.6.0", default-features = false, features = ["dbus"] }
20+
[target.'cfg(target_os = "linux")'.dependencies]
21+
dbus = "0.9"
2022
parking_lot = "0.12"
2123
wayland-client = "0.31"
2224
wayland-protocols = { version = "0.32", features = ["client", "staging"] }
23-
24-
[target.'cfg(target_os = "linux")'.dependencies]
25-
dbus = "0.9"
2625
[dev-dependencies]
2726
serde_json = "1"

src/idle.rs

Lines changed: 151 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
use std::sync::Arc;
2-
use std::sync::atomic::{AtomicBool, Ordering};
3-
use std::time::{Duration, Instant};
1+
use std::time::Duration;
42

53
use user_idle::UserIdle;
64

@@ -13,21 +11,28 @@ pub(crate) enum IdleState {
1311
/// Provider that monitors user idle state across Wayland (ext-idle-notify-v1),
1412
/// DBus (GNOME / KDE), X11, and OS-native APIs.
1513
pub(crate) struct IdleMonitor {
16-
wayland: Option<WaylandIdleTracker>,
14+
#[cfg(target_os = "linux")]
15+
wayland: Option<wayland::WaylandIdleTracker>,
1716
}
1817

1918
impl IdleMonitor {
20-
pub(crate) fn new(threshold: Duration) -> Self {
21-
let wayland = WaylandIdleTracker::start(threshold);
22-
Self { wayland }
19+
pub(crate) fn new(_threshold: Duration) -> Self {
20+
#[cfg(target_os = "linux")]
21+
let wayland = wayland::WaylandIdleTracker::start(_threshold);
22+
Self {
23+
#[cfg(target_os = "linux")]
24+
wayland,
25+
}
2326
}
2427

2528
/// Checks the current idle state. Returns `IdleState::Idle(elapsed)` if the user
2629
/// has been idle for at least the threshold, or `IdleState::Active` otherwise.
30+
#[allow(clippy::needless_pass_by_ref_mut)]
2731
pub(crate) fn check_idle(&mut self, threshold: Duration) -> IdleState {
2832
if threshold == Duration::ZERO {
2933
return IdleState::Idle(Duration::ZERO);
3034
}
35+
#[cfg(target_os = "linux")]
3136
if let Some(wayland) = &mut self.wayland {
3237
if let Some(state) = wayland.get_state(threshold) {
3338
return state;
@@ -78,161 +83,170 @@ fn get_gnome_mutter_idle() -> Result<Duration, ()> {
7883
// Wayland ext-idle-notify-v1 implementation
7984
// ---------------------------------------------------------------------------
8085

81-
use wayland_client::protocol::wl_registry;
82-
use wayland_client::protocol::wl_seat::WlSeat;
83-
use wayland_client::{Connection, Dispatch, EventQueue, QueueHandle};
84-
use wayland_protocols::ext::idle_notify::v1::client::ext_idle_notification_v1::{
85-
self, ExtIdleNotificationV1,
86-
};
87-
use wayland_protocols::ext::idle_notify::v1::client::ext_idle_notifier_v1::ExtIdleNotifierV1;
88-
89-
struct WaylandState {
90-
seat: Option<WlSeat>,
91-
notifier: Option<ExtIdleNotifierV1>,
92-
notification: Option<ExtIdleNotificationV1>,
93-
is_idle: Arc<AtomicBool>,
94-
idle_since: Arc<parking_lot::Mutex<Option<Instant>>>,
95-
}
86+
#[cfg(target_os = "linux")]
87+
mod wayland {
88+
use std::sync::Arc;
89+
use std::sync::atomic::{AtomicBool, Ordering};
90+
use std::time::{Duration, Instant};
91+
92+
use wayland_client::protocol::wl_registry;
93+
use wayland_client::protocol::wl_seat::WlSeat;
94+
use wayland_client::{Connection, Dispatch, EventQueue, QueueHandle};
95+
use wayland_protocols::ext::idle_notify::v1::client::ext_idle_notification_v1::{
96+
self, ExtIdleNotificationV1,
97+
};
98+
use wayland_protocols::ext::idle_notify::v1::client::ext_idle_notifier_v1::ExtIdleNotifierV1;
99+
100+
use super::IdleState;
101+
102+
struct WaylandState {
103+
seat: Option<WlSeat>,
104+
notifier: Option<ExtIdleNotifierV1>,
105+
notification: Option<ExtIdleNotificationV1>,
106+
is_idle: Arc<AtomicBool>,
107+
idle_since: Arc<parking_lot::Mutex<Option<Instant>>>,
108+
}
96109

97-
struct WaylandIdleTracker {
98-
conn: Connection,
99-
event_queue: EventQueue<WaylandState>,
100-
state: WaylandState,
101-
}
110+
pub(super) struct WaylandIdleTracker {
111+
conn: Connection,
112+
event_queue: EventQueue<WaylandState>,
113+
state: WaylandState,
114+
}
102115

103-
impl WaylandIdleTracker {
104-
fn start(threshold: Duration) -> Option<Self> {
105-
if std::env::var_os("WAYLAND_DISPLAY").is_none()
106-
&& std::env::var_os("WAYLAND_SOCKET").is_none()
107-
{
108-
return None;
109-
}
116+
impl WaylandIdleTracker {
117+
pub(super) fn start(threshold: Duration) -> Option<Self> {
118+
if std::env::var_os("WAYLAND_DISPLAY").is_none()
119+
&& std::env::var_os("WAYLAND_SOCKET").is_none()
120+
{
121+
return None;
122+
}
110123

111-
let conn = Connection::connect_to_env().ok()?;
112-
let display = conn.display();
113-
let mut event_queue = conn.new_event_queue();
114-
let qh = event_queue.handle();
124+
let conn = Connection::connect_to_env().ok()?;
125+
let display = conn.display();
126+
let mut event_queue = conn.new_event_queue();
127+
let qh = event_queue.handle();
115128

116-
let is_idle = Arc::new(AtomicBool::new(false));
117-
let idle_since = Arc::new(parking_lot::Mutex::new(None));
129+
let is_idle = Arc::new(AtomicBool::new(false));
130+
let idle_since = Arc::new(parking_lot::Mutex::new(None));
118131

119-
let mut state = WaylandState {
120-
seat: None,
121-
notifier: None,
122-
notification: None,
123-
is_idle: is_idle.clone(),
124-
idle_since: idle_since.clone(),
125-
};
132+
let mut state = WaylandState {
133+
seat: None,
134+
notifier: None,
135+
notification: None,
136+
is_idle: is_idle.clone(),
137+
idle_since: idle_since.clone(),
138+
};
126139

127-
let _registry = display.get_registry(&qh, ());
140+
let _registry = display.get_registry(&qh, ());
128141

129-
// Initial roundtrip to get registry globals
130-
event_queue.roundtrip(&mut state).ok()?;
142+
// Initial roundtrip to get registry globals
143+
event_queue.roundtrip(&mut state).ok()?;
131144

132-
let seat = state.seat.as_ref()?;
133-
let notifier = state.notifier.as_ref()?;
145+
let seat = state.seat.as_ref()?;
146+
let notifier = state.notifier.as_ref()?;
134147

135-
let timeout_ms = threshold.as_millis().max(1).min(u32::MAX as u128) as u32;
136-
let notification = notifier.get_idle_notification(timeout_ms, seat, &qh, ());
137-
state.notification = Some(notification);
148+
let timeout_ms = threshold.as_millis().max(1).min(u32::MAX as u128) as u32;
149+
let notification = notifier.get_idle_notification(timeout_ms, seat, &qh, ());
150+
state.notification = Some(notification);
138151

139-
let _ = conn.flush();
152+
let _ = conn.flush();
140153

141-
Some(Self {
142-
conn,
143-
event_queue,
144-
state,
145-
})
146-
}
154+
Some(Self {
155+
conn,
156+
event_queue,
157+
state,
158+
})
159+
}
147160

148-
fn get_state(&mut self, threshold: Duration) -> Option<IdleState> {
149-
// Dispatch pending wayland events non-blockingly
150-
let _ = self.event_queue.dispatch_pending(&mut self.state);
151-
let _ = self.conn.flush();
152-
153-
let is_idle = self.state.is_idle.load(Ordering::Acquire);
154-
if is_idle {
155-
let mut guard = self.state.idle_since.lock();
156-
let start = *guard.get_or_insert_with(Instant::now);
157-
let elapsed = threshold + start.elapsed();
158-
Some(IdleState::Idle(elapsed))
159-
} else {
160-
*self.state.idle_since.lock() = None;
161-
Some(IdleState::Active)
161+
pub(super) fn get_state(&mut self, threshold: Duration) -> Option<IdleState> {
162+
// Dispatch pending wayland events non-blockingly
163+
let _ = self.event_queue.dispatch_pending(&mut self.state);
164+
let _ = self.conn.flush();
165+
166+
let is_idle = self.state.is_idle.load(Ordering::Acquire);
167+
if is_idle {
168+
let mut guard = self.state.idle_since.lock();
169+
let start = *guard.get_or_insert_with(Instant::now);
170+
let elapsed = threshold + start.elapsed();
171+
Some(IdleState::Idle(elapsed))
172+
} else {
173+
*self.state.idle_since.lock() = None;
174+
Some(IdleState::Active)
175+
}
162176
}
163177
}
164-
}
165178

166-
impl Dispatch<wl_registry::WlRegistry, ()> for WaylandState {
167-
fn event(
168-
state: &mut Self,
169-
registry: &wl_registry::WlRegistry,
170-
event: wl_registry::Event,
171-
_: &(),
172-
_: &Connection,
173-
qh: &QueueHandle<Self>,
174-
) {
175-
if let wl_registry::Event::Global {
176-
name,
177-
interface,
178-
version,
179-
} = event
180-
{
181-
if interface == "wl_seat" && state.seat.is_none() {
182-
state.seat = Some(registry.bind(name, version.min(1), qh, ()));
183-
} else if interface == "ext_idle_notifier_v1" && state.notifier.is_none() {
184-
state.notifier = Some(registry.bind(name, version.min(1), qh, ()));
179+
impl Dispatch<wl_registry::WlRegistry, ()> for WaylandState {
180+
fn event(
181+
state: &mut Self,
182+
registry: &wl_registry::WlRegistry,
183+
event: wl_registry::Event,
184+
_: &(),
185+
_: &Connection,
186+
qh: &QueueHandle<Self>,
187+
) {
188+
if let wl_registry::Event::Global {
189+
name,
190+
interface,
191+
version,
192+
} = event
193+
{
194+
if interface == "wl_seat" && state.seat.is_none() {
195+
state.seat = Some(registry.bind(name, version.min(1), qh, ()));
196+
} else if interface == "ext_idle_notifier_v1" && state.notifier.is_none() {
197+
state.notifier = Some(registry.bind(name, version.min(1), qh, ()));
198+
}
185199
}
186200
}
187201
}
188-
}
189202

190-
impl Dispatch<WlSeat, ()> for WaylandState {
191-
fn event(
192-
_: &mut Self,
193-
_: &WlSeat,
194-
_: wayland_client::protocol::wl_seat::Event,
195-
_: &(),
196-
_: &Connection,
197-
_: &QueueHandle<Self>,
198-
) {
203+
impl Dispatch<WlSeat, ()> for WaylandState {
204+
fn event(
205+
_: &mut Self,
206+
_: &WlSeat,
207+
_: wayland_client::protocol::wl_seat::Event,
208+
_: &(),
209+
_: &Connection,
210+
_: &QueueHandle<Self>,
211+
) {
212+
}
199213
}
200-
}
201214

202-
impl Dispatch<ExtIdleNotifierV1, ()> for WaylandState {
203-
fn event(
204-
_: &mut Self,
205-
_: &ExtIdleNotifierV1,
206-
_: wayland_protocols::ext::idle_notify::v1::client::ext_idle_notifier_v1::Event,
207-
_: &(),
208-
_: &Connection,
209-
_: &QueueHandle<Self>,
210-
) {
215+
impl Dispatch<ExtIdleNotifierV1, ()> for WaylandState {
216+
fn event(
217+
_: &mut Self,
218+
_: &ExtIdleNotifierV1,
219+
_: wayland_protocols::ext::idle_notify::v1::client::ext_idle_notifier_v1::Event,
220+
_: &(),
221+
_: &Connection,
222+
_: &QueueHandle<Self>,
223+
) {
224+
}
211225
}
212-
}
213226

214-
impl Dispatch<ExtIdleNotificationV1, ()> for WaylandState {
215-
fn event(
216-
state: &mut Self,
217-
_: &ExtIdleNotificationV1,
218-
event: ext_idle_notification_v1::Event,
219-
_: &(),
220-
_: &Connection,
221-
_: &QueueHandle<Self>,
222-
) {
223-
match event {
224-
ext_idle_notification_v1::Event::Idled => {
225-
state.is_idle.store(true, Ordering::Release);
226-
let mut guard = state.idle_since.lock();
227-
if guard.is_none() {
228-
*guard = Some(Instant::now());
227+
impl Dispatch<ExtIdleNotificationV1, ()> for WaylandState {
228+
fn event(
229+
state: &mut Self,
230+
_: &ExtIdleNotificationV1,
231+
event: ext_idle_notification_v1::Event,
232+
_: &(),
233+
_: &Connection,
234+
_: &QueueHandle<Self>,
235+
) {
236+
match event {
237+
ext_idle_notification_v1::Event::Idled => {
238+
state.is_idle.store(true, Ordering::Release);
239+
let mut guard = state.idle_since.lock();
240+
if guard.is_none() {
241+
*guard = Some(Instant::now());
242+
}
229243
}
244+
ext_idle_notification_v1::Event::Resumed => {
245+
state.is_idle.store(false, Ordering::Release);
246+
*state.idle_since.lock() = None;
247+
}
248+
_ => {}
230249
}
231-
ext_idle_notification_v1::Event::Resumed => {
232-
state.is_idle.store(false, Ordering::Release);
233-
*state.idle_since.lock() = None;
234-
}
235-
_ => {}
236250
}
237251
}
238252
}

0 commit comments

Comments
 (0)