1
0
Fork 0
forked from wry/wry

config: add initial-tile-state window rule

This commit is contained in:
Julian Orth 2025-05-07 15:59:42 +02:00
parent b1ca98b488
commit 5e3465d861
16 changed files with 258 additions and 26 deletions

View file

@ -23,7 +23,7 @@ use {
input::{InputDevice, Seat, SwitchEvent},
keyboard::{mods::Modifiers, syms::KeySym},
video::{Connector, DrmDevice},
window,
window::{self, TileState},
},
libloading::Library,
std::{cell::Cell, io, mem, ptr, rc::Rc},
@ -169,6 +169,10 @@ impl ConfigProxy {
};
handler.auto_focus(data)
}
pub fn initial_tile_state(&self, data: &ToplevelData) -> Option<TileState> {
self.handler.get()?.initial_tile_state(data)
}
}
impl Drop for ConfigProxy {
@ -233,6 +237,7 @@ impl ConfigProxy {
window_matcher_leafs: Default::default(),
window_matcher_std_kinds: state.tl_matcher_manager.kind(window::CLIENT_WINDOW),
window_matcher_no_auto_focus: Default::default(),
window_matcher_initial_tile_state: Default::default(),
});
let init_msg = bincode_ops()
.serialize(&InitMessage::V1(V1InitMessage {}))

View file

@ -66,7 +66,7 @@ use {
TearingMode as ConfigTearingMode, TransferFunction as ConfigTransferFunction,
Transform, VrrMode as ConfigVrrMode,
},
window::{Window, WindowMatcher},
window::{TileState, Window, WindowMatcher},
xwayland::XScalingMode,
},
libloading::Library,
@ -126,6 +126,13 @@ pub(super) struct ConfigProxyHandler {
pub window_matcher_std_kinds: Rc<TlmUpstreamNode>,
pub window_matcher_no_auto_focus:
CopyHashMap<WindowMatcher, Rc<CachedCriterion<WindowCriterionIpc, ToplevelData>>>,
pub window_matcher_initial_tile_state: CopyHashMap<
WindowMatcher,
(
Rc<CachedCriterion<WindowCriterionIpc, ToplevelData>>,
TileState,
),
>,
}
pub struct Pollable {
@ -2030,6 +2037,7 @@ impl ConfigProxyHandler {
self.window_matchers.remove(&matcher);
self.window_matcher_leafs.remove(&matcher);
self.window_matcher_no_auto_focus.remove(&matcher);
self.window_matcher_initial_tile_state.remove(&matcher);
}
fn handle_enable_window_matcher_events(
@ -2073,6 +2081,17 @@ impl ConfigProxyHandler {
Ok(())
}
fn handle_set_window_matcher_initial_tile_state(
&self,
matcher: WindowMatcher,
tile_state: TileState,
) -> Result<(), CphError> {
let m = self.get_window_matcher(matcher)?;
self.window_matcher_initial_tile_state
.set(matcher, (m, tile_state));
Ok(())
}
fn spaces_change(&self) {
struct V;
impl NodeVisitorBase for V {
@ -2884,6 +2903,12 @@ impl ConfigProxyHandler {
} => self
.handle_set_window_matcher_auto_focus(matcher, auto_focus)
.wrn("set_window_matcher_auto_focus")?,
ClientMessage::SetWindowMatcherInitialTileState {
matcher,
tile_state,
} => self
.handle_set_window_matcher_initial_tile_state(matcher, tile_state)
.wrn("set_window_matcher_initial_tile_state")?,
}
Ok(())
}
@ -2896,6 +2921,15 @@ impl ConfigProxyHandler {
}
true
}
pub fn initial_tile_state(&self, data: &ToplevelData) -> Option<TileState> {
for (matcher, state) in self.window_matcher_initial_tile_state.lock().values() {
if matcher.node.pull(data) {
return Some(*state);
}
}
None
}
}
#[derive(Debug, Error)]

View file

@ -21,6 +21,7 @@ use {
xwayland::XWaylandEvent,
},
bstr::BString,
jay_config::window::TileState,
std::{
cell::{Cell, RefCell},
ops::{Deref, Not},
@ -266,6 +267,14 @@ impl Xwindow {
pub fn map_status_changed(self: &Rc<Self>) {
let map_change = self.map_change();
let override_redirect = self.data.info.override_redirect.get();
let map_floating = match self
.toplevel_data
.state
.initial_tile_state(&self.toplevel_data)
{
None => self.data.info.wants_floating.get(),
Some(m) => m == TileState::Floating,
};
match map_change {
Change::None => return,
Change::Unmap => {
@ -282,7 +291,7 @@ impl Xwindow {
Some(self.data.state.root.stacked.add_last(self.clone()));
self.data.state.tree_changed();
}
Change::Map if self.data.info.wants_floating.get() => {
Change::Map if map_floating => {
let ws = self.data.state.float_map_ws();
let ext = self.data.info.pending_extents.get();
self.data

View file

@ -34,6 +34,7 @@ use {
wire::{XdgToplevelId, xdg_toplevel::*},
},
ahash::{AHashMap, AHashSet},
jay_config::window::TileState,
num_derive::FromPrimitive,
std::{
cell::{Cell, RefCell},
@ -381,6 +382,31 @@ impl XdgToplevelRequestHandler for XdgToplevel {
}
impl XdgToplevel {
fn map(
self: &Rc<Self>,
parent: Option<&XdgToplevel>,
pos: Option<(&Rc<OutputNode>, i32, i32)>,
) {
if let Some(state) = self.state.initial_tile_state(&self.toplevel_data) {
match state {
TileState::Floating => {
let mut ws = None;
if let Some(parent) = parent {
ws = parent.xdg.workspace.get();
}
let ws = ws.unwrap_or_else(|| self.state.ensure_map_workspace(None));
self.map_floating(&ws, pos.map(|p| (p.1, p.2)));
}
_ => self.map_tiled(),
}
return;
}
match parent {
None => self.map_tiled(),
Some(p) => self.map_child(p, pos),
}
}
fn map_floating(self: &Rc<Self>, workspace: &Rc<WorkspaceNode>, abs_pos: Option<(i32, i32)>) {
let (width, height) = self.toplevel_data.float_size(workspace);
self.state
@ -474,11 +500,7 @@ impl XdgToplevel {
}
self.state.tree_changed();
} else {
if let Some(parent) = self.parent.get() {
self.map_child(&parent, pos);
} else {
self.map_tiled();
}
self.map(self.parent.get().as_deref(), pos);
self.extents_changed();
if let Some(workspace) = self.xdg.workspace.get() {
let output = workspace.output.get();

View file

@ -82,8 +82,8 @@ use {
time::Time,
tree::{
ContainerNode, ContainerSplit, Direction, DisplayNode, FloatNode, LatchListener, Node,
NodeIds, NodeVisitorBase, OutputNode, PlaceholderNode, TearingMode, ToplevelNode,
ToplevelNodeBase, VrrMode, WorkspaceNode, generic_node_visitor,
NodeIds, NodeVisitorBase, OutputNode, PlaceholderNode, TearingMode, ToplevelData,
ToplevelNode, ToplevelNodeBase, VrrMode, WorkspaceNode, generic_node_visitor,
},
utils::{
activation_token::ActivationToken, asyncevent::AsyncEvent, bindings::Bindings,
@ -112,6 +112,7 @@ use {
jay_config::{
PciId,
video::{GfxApi, Transform},
window::TileState,
},
std::{
cell::{Cell, RefCell},
@ -662,6 +663,16 @@ impl State {
}
}
pub fn ensure_map_workspace(&self, seat: Option<&Rc<WlSeatGlobal>>) -> Rc<WorkspaceNode> {
seat.cloned()
.or_else(|| self.seat_queue.last().map(|s| s.deref().clone()))
.map(|s| s.get_output())
.or_else(|| self.root.outputs.lock().values().next().cloned())
.or_else(|| self.dummy_output.get())
.unwrap()
.ensure_workspace()
}
pub fn map_tiled(self: &Rc<Self>, node: Rc<dyn ToplevelNode>) {
let seat = self.seat_queue.last();
self.do_map_tiled(seat.as_deref(), node.clone());
@ -669,12 +680,7 @@ impl State {
}
fn do_map_tiled(self: &Rc<Self>, seat: Option<&Rc<WlSeatGlobal>>, node: Rc<dyn ToplevelNode>) {
let output = seat
.map(|s| s.get_output())
.or_else(|| self.root.outputs.lock().values().next().cloned())
.or_else(|| self.dummy_output.get())
.unwrap();
let ws = output.ensure_workspace();
let ws = self.ensure_map_workspace(seat);
self.map_tiled_on(node, &ws);
}
@ -1384,6 +1390,10 @@ impl State {
};
ctx.supports_color_management()
}
pub fn initial_tile_state(&self, data: &ToplevelData) -> Option<TileState> {
self.config.get()?.initial_tile_state(data)
}
}
#[derive(Debug, Error)]