diff options
| -rw-r--r-- | niri-ipc/src/lib.rs | 45 | ||||
| -rw-r--r-- | niri-ipc/src/state.rs | 11 | ||||
| -rw-r--r-- | src/ipc/client.rs | 6 | ||||
| -rw-r--r-- | src/ipc/server.rs | 11 |
4 files changed, 72 insertions, 1 deletions
diff --git a/niri-ipc/src/lib.rs b/niri-ipc/src/lib.rs index 4a2e8996..c20dffbd 100644 --- a/niri-ipc/src/lib.rs +++ b/niri-ipc/src/lib.rs @@ -54,6 +54,7 @@ use std::collections::HashMap; use std::str::FromStr; +use std::time::Duration; use serde::{Deserialize, Serialize}; @@ -1298,6 +1299,24 @@ pub struct Window { pub is_urgent: bool, /// Position- and size-related properties of the window. pub layout: WindowLayout, + /// Timestamp when the window was most recently focused. + /// + /// This timestamp is intended for most-recently-used window switchers, i.e. Alt-Tab. It only + /// updates after some debounce time so that quick window switching doesn't mark intermediate + /// windows as recently focused. + /// + /// The timestamp comes from the monotonic clock. + pub focus_timestamp: Option<Timestamp>, +} + +/// A moment in time. +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] +pub struct Timestamp { + /// Number of whole seconds. + pub secs: u64, + /// Fractional part of the timestamp in nanoseconds (10<sup>-9</sup> seconds). + pub nanos: u32, } /// Position- and size-related properties of a [`Window`]. @@ -1513,6 +1532,17 @@ pub enum Event { /// Id of the newly focused window, or `None` if no window is now focused. id: Option<u64>, }, + /// Window focus timestamp changed. + /// + /// This event is separate from [`Event::WindowFocusChanged`] because the focus timestamp only + /// updates after some debounce time so that quick window switching doesn't mark intermediate + /// windows as recently focused. + WindowFocusTimestampChanged { + /// Id of the window. + id: u64, + /// The new focus timestamp. + focus_timestamp: Option<Timestamp>, + }, /// Window urgency changed. WindowUrgencyChanged { /// Id of the window. @@ -1560,6 +1590,21 @@ pub enum Event { }, } +impl From<Duration> for Timestamp { + fn from(value: Duration) -> Self { + Timestamp { + secs: value.as_secs(), + nanos: value.subsec_nanos(), + } + } +} + +impl From<Timestamp> for Duration { + fn from(value: Timestamp) -> Self { + Duration::new(value.secs, value.nanos) + } +} + impl FromStr for WorkspaceReferenceArg { type Err = &'static str; diff --git a/niri-ipc/src/state.rs b/niri-ipc/src/state.rs index 3ba63a52..a83b94ce 100644 --- a/niri-ipc/src/state.rs +++ b/niri-ipc/src/state.rs @@ -193,6 +193,17 @@ impl EventStreamStatePart for WindowsState { win.is_focused = Some(win.id) == id; } } + Event::WindowFocusTimestampChanged { + id, + focus_timestamp, + } => { + for win in self.windows.values_mut() { + if win.id == id { + win.focus_timestamp = focus_timestamp; + break; + } + } + } Event::WindowUrgencyChanged { id, urgent } => { for win in self.windows.values_mut() { if win.id == id { diff --git a/src/ipc/client.rs b/src/ipc/client.rs index 8fea1d2b..ef4bdf51 100644 --- a/src/ipc/client.rs +++ b/src/ipc/client.rs @@ -458,6 +458,12 @@ pub fn handle_msg(mut msg: Msg, json: bool) -> anyhow::Result<()> { Event::WindowFocusChanged { id } => { println!("Window focus changed: {id:?}"); } + Event::WindowFocusTimestampChanged { + id, + focus_timestamp, + } => { + println!("Window {id}: focus timestamp changed to {focus_timestamp:?}"); + } Event::WindowUrgencyChanged { id, urgent } => { println!("Window {id}: urgency changed to {urgent}"); } diff --git a/src/ipc/server.rs b/src/ipc/server.rs index ade57de1..95176ce3 100644 --- a/src/ipc/server.rs +++ b/src/ipc/server.rs @@ -18,7 +18,7 @@ use niri_config::OutputName; use niri_ipc::state::{EventStreamState, EventStreamStatePart as _}; use niri_ipc::{ Action, Event, KeyboardLayouts, OutputConfigChanged, Overview, Reply, Request, Response, - WindowLayout, Workspace, + Timestamp, WindowLayout, Workspace, }; use smithay::desktop::layer_map_for_output; use smithay::input::pointer::{ @@ -514,6 +514,7 @@ fn make_ipc_window( is_floating: mapped.is_floating(), is_urgent: mapped.is_urgent(), layout, + focus_timestamp: mapped.get_focus_timestamp().map(Timestamp::from), }) } @@ -725,6 +726,14 @@ impl State { events.push(Event::WindowFocusChanged { id: Some(id) }); } + let focus_timestamp = mapped.get_focus_timestamp().map(Timestamp::from); + if focus_timestamp != ipc_win.focus_timestamp { + events.push(Event::WindowFocusTimestampChanged { + id, + focus_timestamp, + }); + } + let urgent = mapped.is_urgent(); if urgent != ipc_win.is_urgent { events.push(Event::WindowUrgencyChanged { id, urgent }) |
