Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ tracy-client = { version = "0.18.4", default-features = false }

[workspace.dependencies.smithay]
# version = "0.4.1"
git = "https://github.com/Smithay/smithay.git"
rev = "ff5fa7df392cecfba049ffed55cdaa4e98a8e7ef"
git = "https://github.com/MithicSpirit/smithay.git"
rev = "3aa06c55bc0f3ec97b441d41add221b2aac86109"
default-features = false

[workspace.dependencies.smithay-drm-extras]
Expand Down
3 changes: 3 additions & 0 deletions niri-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1737,6 +1737,7 @@ mod tests {
is_window_cast_target: None,
is_urgent: None,
at_startup: None,
xdg_tag: None,
},
],
excludes: [
Expand All @@ -1756,6 +1757,7 @@ mod tests {
is_window_cast_target: None,
is_urgent: None,
at_startup: None,
xdg_tag: None,
},
Match {
app_id: None,
Expand All @@ -1771,6 +1773,7 @@ mod tests {
is_window_cast_target: None,
is_urgent: None,
at_startup: None,
xdg_tag: None,
},
],
default_column_width: None,
Expand Down
2 changes: 2 additions & 0 deletions niri-config/src/window_rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ pub struct Match {
pub is_urgent: Option<bool>,
#[knuffel(property)]
pub at_startup: Option<bool>,
#[knuffel(property, str)]
pub xdg_tag: Option<RegexEq>,
}

#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
Expand Down
2 changes: 2 additions & 0 deletions niri-ipc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1368,6 +1368,8 @@ pub struct Window {
///
/// The timestamp comes from the monotonic clock.
pub focus_timestamp: Option<Timestamp>,
/// xdg-toplevel-tag tag and description, if set.
pub xdg_tag: (Option<String>, Option<String>),
}

/// A moment in time.
Expand Down
5 changes: 5 additions & 0 deletions src/handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ use smithay::wayland::tablet_manager::TabletSeatHandler;
use smithay::wayland::xdg_activation::{
XdgActivationHandler, XdgActivationState, XdgActivationToken, XdgActivationTokenData,
};
use smithay::wayland::xdg_toplevel_tag::XdgToplevelTagHandler;
use smithay::{
delegate_cursor_shape, delegate_data_control, delegate_data_device, delegate_dmabuf,
delegate_drm_lease, delegate_ext_data_control, delegate_fractional_scale,
Expand All @@ -70,6 +71,7 @@ use smithay::{
delegate_relative_pointer, delegate_seat, delegate_security_context, delegate_session_lock,
delegate_single_pixel_buffer, delegate_tablet_manager, delegate_text_input_manager,
delegate_viewporter, delegate_virtual_keyboard_manager, delegate_xdg_activation,
delegate_xdg_toplevel_tag,
};

pub use crate::handlers::xdg_shell::KdeDecorationsModeState;
Expand Down Expand Up @@ -861,3 +863,6 @@ impl MutterX11InteropHandler for State {}
delegate_mutter_x11_interop!(State);

delegate_single_pixel_buffer!(State);

impl XdgToplevelTagHandler for State {}
delegate_xdg_toplevel_tag!(State);
7 changes: 7 additions & 0 deletions src/ipc/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,13 @@ fn print_window(window: &Window) {
println!(" App ID: (unset)");
}

match &window.xdg_tag {
(Some(tag), Some(description)) => println!(" XDG Tag: {description} [{tag}]"),
(None, Some(description)) => println!(" XDG Tag: {description}"),
(Some(tag), None) => println!(" XDG Tag: [{tag}]"),
(None, None) => println!(" XDG Tag: (unset)"),
}

println!(
" Is floating: {}",
if window.is_floating { "yes" } else { "no" }
Expand Down
11 changes: 9 additions & 2 deletions src/ipc/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use crate::backend::IpcOutputMap;
use crate::input::pick_window_grab::PickWindowGrab;
use crate::layout::workspace::WorkspaceId;
use crate::niri::State;
use crate::utils::{version, with_toplevel_role};
use crate::utils::{version, with_toplevel_role, with_toplevel_role_and_tag};
use crate::window::Mapped;

// If an event stream client fails to read events fast enough that we accumulate more than this
Expand Down Expand Up @@ -517,10 +517,17 @@ fn make_ipc_window(
workspace_id: Option<WorkspaceId>,
layout: WindowLayout,
) -> niri_ipc::Window {
with_toplevel_role(mapped.toplevel(), |role| niri_ipc::Window {
with_toplevel_role_and_tag(mapped.toplevel(), |role, tag| niri_ipc::Window {
id: mapped.id().get(),
title: role.title.clone(),
app_id: role.app_id.clone(),
xdg_tag: match tag {
Some(tag) => (
tag.tag().map(|x| x.as_ref().to_owned()),
tag.description().map(|x| x.as_ref().to_owned()),
),
None => (None, None),
},
pid: mapped.credentials().map(|c| c.pid),
workspace_id: workspace_id.map(|id| id.get()),
is_focused: mapped.is_focused(),
Expand Down
4 changes: 4 additions & 0 deletions src/niri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ use smithay::wayland::viewporter::ViewporterState;
use smithay::wayland::virtual_keyboard::VirtualKeyboardManagerState;
use smithay::wayland::xdg_activation::XdgActivationState;
use smithay::wayland::xdg_foreign::XdgForeignState;
use smithay::wayland::xdg_toplevel_tag::XdgToplevelTagManager;
use wayland_server::protocol::wl_output::WlOutput;

#[cfg(feature = "dbus")]
Expand Down Expand Up @@ -312,6 +313,7 @@ pub struct Niri {
pub gamma_control_manager_state: GammaControlManagerState,
pub activation_state: XdgActivationState,
pub mutter_x11_interop_state: MutterX11InteropManagerState,
pub xdg_toplevel_tag_manager: XdgToplevelTagManager,

// This will not work as is outside of tests, so it is gated with #[cfg(test)] for now. In
// particular, shaders will need to learn about the single pixel buffer. Also, it must be
Expand Down Expand Up @@ -2367,6 +2369,7 @@ impl Niri {

let mutter_x11_interop_state =
MutterX11InteropManagerState::new::<State, _>(&display_handle, move |_| true);
let xdg_toplevel_tag_manager = XdgToplevelTagManager::new::<State>(&display_handle);

#[cfg(test)]
let single_pixel_buffer_state = SinglePixelBufferState::new::<State>(&display_handle);
Expand Down Expand Up @@ -2561,6 +2564,7 @@ impl Niri {
gamma_control_manager_state,
activation_state,
mutter_x11_interop_state,
xdg_toplevel_tag_manager,
#[cfg(test)]
single_pixel_buffer_state,

Expand Down
19 changes: 19 additions & 0 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use smithay::wayland::shell::xdg::{
ToplevelCachedState, ToplevelConfigure, ToplevelState, ToplevelSurface, XdgToplevelSurfaceData,
XdgToplevelSurfaceRoleAttributes,
};
use smithay::wayland::xdg_toplevel_tag::XdgToplevelTagSurfaceData;
use wayland_backend::server::Credentials;

use crate::handlers::KdeDecorationsModeState;
Expand Down Expand Up @@ -356,6 +357,24 @@ pub fn with_toplevel_role<T>(
})
}

pub fn with_toplevel_role_and_tag<T>(
toplevel: &ToplevelSurface,
f: impl FnOnce(&mut XdgToplevelSurfaceRoleAttributes, Option<&XdgToplevelTagSurfaceData>) -> T,
) -> T {
with_states(toplevel.wl_surface(), |states| {
let mut role = states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.lock()
.unwrap();

let tag = states.data_map.get::<XdgToplevelTagSurfaceData>();

f(&mut role, tag)
})
}

pub fn with_toplevel_role_and_current<T>(
toplevel: &ToplevelSurface,
f: impl FnOnce(&mut XdgToplevelSurfaceRoleAttributes, Option<&ToplevelState>) -> T,
Expand Down
23 changes: 19 additions & 4 deletions src/window/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ use smithay::wayland::compositor::with_states;
use smithay::wayland::shell::xdg::{
SurfaceCachedState, ToplevelSurface, XdgToplevelSurfaceRoleAttributes,
};
use smithay::wayland::xdg_toplevel_tag::XdgToplevelTagSurfaceData;

use crate::utils::with_toplevel_role;
use crate::utils::with_toplevel_role_and_tag;

pub mod mapped;
pub use mapped::Mapped;
Expand Down Expand Up @@ -185,7 +186,7 @@ impl ResolvedWindowRules {

let mut resolved = ResolvedWindowRules::default();

with_toplevel_role(window.toplevel(), |role| {
with_toplevel_role_and_tag(window.toplevel(), |role, tag| {
// Ensure server_pending like in Smithay's with_pending_state().
if role.server_pending.is_none() {
role.server_pending = Some(role.current_server_state().clone());
Expand All @@ -202,7 +203,7 @@ impl ResolvedWindowRules {
}
}

window_matches(window, role, m)
window_matches(window, role, tag, m)
};

if !(rule.matches.is_empty() || rule.matches.iter().any(matches)) {
Expand Down Expand Up @@ -383,7 +384,12 @@ impl ResolvedWindowRules {
}
}

fn window_matches(window: WindowRef, role: &XdgToplevelSurfaceRoleAttributes, m: &Match) -> bool {
fn window_matches(
window: WindowRef,
role: &XdgToplevelSurfaceRoleAttributes,
tag_data: Option<&XdgToplevelTagSurfaceData>,
m: &Match,
) -> bool {
// Must be ensured by the caller.
let server_pending = role.server_pending.as_ref().unwrap();

Expand Down Expand Up @@ -427,6 +433,15 @@ fn window_matches(window: WindowRef, role: &XdgToplevelSurfaceRoleAttributes, m:
}
}

if let Some(tag_re) = &m.xdg_tag {
let Some(tag) = &tag_data.and_then(|x| x.tag()) else {
return false;
};
if !tag_re.0.is_match(tag.as_ref()) {
return false;
}
}

if let Some(is_active_in_column) = m.is_active_in_column {
if window.is_active_in_column() != is_active_in_column {
return false;
Expand Down