aboutsummaryrefslogtreecommitdiff
path: root/src/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'src/ipc')
-rw-r--r--src/ipc/client.rs66
-rw-r--r--src/ipc/server.rs33
2 files changed, 93 insertions, 6 deletions
diff --git a/src/ipc/client.rs b/src/ipc/client.rs
index 5ddefd3b..42fbbf75 100644
--- a/src/ipc/client.rs
+++ b/src/ipc/client.rs
@@ -7,7 +7,7 @@ use niri_config::OutputName;
use niri_ipc::socket::Socket;
use niri_ipc::{
Event, KeyboardLayouts, LogicalOutput, Mode, Output, OutputConfigChanged, Overview, Request,
- Response, Transform, Window,
+ Response, Transform, Window, WindowLayout,
};
use serde_json::json;
@@ -447,6 +447,9 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
Event::WindowUrgencyChanged { id, urgent } => {
println!("Window {id}: urgency changed to {urgent}");
}
+ Event::WindowLayoutsChanged { changes } => {
+ println!("Window layouts changed: {changes:?}");
+ }
Event::KeyboardLayoutsChanged { keyboard_layouts } => {
println!("Keyboard layouts changed: {keyboard_layouts:?}");
}
@@ -612,4 +615,65 @@ fn print_window(window: &Window) {
} else {
println!(" Workspace ID: (none)");
}
+
+ let WindowLayout {
+ pos_in_scrolling_layout,
+ tile_size,
+ window_size,
+ tile_pos_in_workspace_view,
+ window_offset_in_tile,
+ } = window.layout;
+
+ println!(" Layout:");
+ println!(
+ " Tile size: {} x {}",
+ fmt_rounded(tile_size.0),
+ fmt_rounded(tile_size.1)
+ );
+
+ if let Some(pos) = pos_in_scrolling_layout {
+ println!(" Scrolling position: column {}, tile {}", pos.0, pos.1);
+ }
+
+ if let Some(pos) = tile_pos_in_workspace_view {
+ println!(
+ " Workspace-view position: {}, {}",
+ fmt_rounded(pos.0),
+ fmt_rounded(pos.1)
+ );
+ }
+
+ println!(" Window size: {} x {}", window_size.0, window_size.1);
+ println!(
+ " Window offset in tile: {} x {}",
+ fmt_rounded(window_offset_in_tile.0),
+ fmt_rounded(window_offset_in_tile.1)
+ );
+}
+
+fn fmt_rounded(x: f64) -> String {
+ let r = x.round();
+ if (r - x).abs() <= 0.005 {
+ format!("{r}")
+ } else {
+ format!("{x:.2}")
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use insta::assert_snapshot;
+
+ use super::*;
+
+ #[test]
+ fn test_fmt_rounded() {
+ assert_snapshot!(fmt_rounded(1.9), @"1.90");
+ assert_snapshot!(fmt_rounded(1.994), @"1.99");
+ assert_snapshot!(fmt_rounded(1.996), @"2");
+ assert_snapshot!(fmt_rounded(2.0), @"2");
+ assert_snapshot!(fmt_rounded(2.004), @"2");
+ assert_snapshot!(fmt_rounded(2.006), @"2.01");
+ assert_snapshot!(fmt_rounded(2.1), @"2.10");
+ }
}
diff --git a/src/ipc/server.rs b/src/ipc/server.rs
index cbcf3f67..464a2a13 100644
--- a/src/ipc/server.rs
+++ b/src/ipc/server.rs
@@ -17,7 +17,8 @@ use futures_util::{select_biased, AsyncBufReadExt, AsyncWrite, AsyncWriteExt, Fu
use niri_config::OutputName;
use niri_ipc::state::{EventStreamState, EventStreamStatePart as _};
use niri_ipc::{
- Event, KeyboardLayouts, OutputConfigChanged, Overview, Reply, Request, Response, Workspace,
+ Event, KeyboardLayouts, OutputConfigChanged, Overview, Reply, Request, Response, WindowLayout,
+ Workspace,
};
use smithay::desktop::layer_map_for_output;
use smithay::input::pointer::{
@@ -477,7 +478,11 @@ async fn handle_event_stream_client(client: EventStreamClient) -> anyhow::Result
Ok(())
}
-fn make_ipc_window(mapped: &Mapped, workspace_id: Option<WorkspaceId>) -> niri_ipc::Window {
+fn make_ipc_window(
+ mapped: &Mapped,
+ workspace_id: Option<WorkspaceId>,
+ layout: WindowLayout,
+) -> niri_ipc::Window {
with_toplevel_role(mapped.toplevel(), |role| niri_ipc::Window {
id: mapped.id().get(),
title: role.title.clone(),
@@ -487,6 +492,7 @@ fn make_ipc_window(mapped: &Mapped, workspace_id: Option<WorkspaceId>) -> niri_i
is_focused: mapped.is_focused(),
is_floating: mapped.is_floating(),
is_urgent: mapped.is_urgent(),
+ layout,
})
}
@@ -657,10 +663,12 @@ impl State {
let mut events = Vec::new();
let layout = &self.niri.layout;
+ let mut batch_change_layouts: Vec<(u64, WindowLayout)> = Vec::new();
+
// Check for window changes.
let mut seen = HashSet::new();
let mut focused_id = None;
- layout.with_windows(|mapped, _, ws_id| {
+ layout.with_windows(|mapped, _, ws_id, window_layout| {
let id = mapped.id().get();
seen.insert(id);
@@ -669,7 +677,7 @@ impl State {
}
let Some(ipc_win) = state.windows.get(&id) else {
- let window = make_ipc_window(mapped, ws_id);
+ let window = make_ipc_window(mapped, ws_id, window_layout);
events.push(Event::WindowOpenedOrChanged { window });
return;
};
@@ -683,11 +691,15 @@ impl State {
});
if changed {
- let window = make_ipc_window(mapped, ws_id);
+ let window = make_ipc_window(mapped, ws_id, window_layout);
events.push(Event::WindowOpenedOrChanged { window });
return;
}
+ if ipc_win.layout != window_layout {
+ batch_change_layouts.push((id, window_layout));
+ }
+
if mapped.is_focused() && !ipc_win.is_focused {
events.push(Event::WindowFocusChanged { id: Some(id) });
}
@@ -698,6 +710,17 @@ impl State {
}
});
+ // It might make sense to push layout changes after closed windows (since windows about to
+ // be closed will occupy the same column/tile positions as the window that moved into this
+ // vacated space), but also we are already pushing some layout changes in
+ // WindowOpenedOrChanged above, meaning that the receiving end has to handle this case
+ // anyway.
+ if !batch_change_layouts.is_empty() {
+ events.push(Event::WindowLayoutsChanged {
+ changes: batch_change_layouts,
+ });
+ }
+
// Check for closed windows.
let mut ipc_focused_id = None;
for (id, ipc_win) in &state.windows {