Commit a76842e12aca
tarball16 files changed+276-212
@@ -52,17 +52,17 @@ impl XdgActivationHandler for State {
5252
return;5353
};5454
let ctx = data.user_data.get::<Context>();55
- if self.awaiting.contains(&id)55
+ if self.wm.awaiting.contains(&id)5656
&& let Some(ctx) = ctx57
- && !self.placed_by_rule.contains(&id)57
+ && !self.wm.placed_by_rule.contains(&id)5858
{59
- self.layout.relocate(id, ctx.output, ctx.tag);59
+ self.wm.layout.relocate(id, ctx.output, ctx.tag);6060
}6161
let may_focus = ctx.is_some_and(|c| c.may_focus) && data.timestamp.elapsed() < FRESH;62
- if may_focus && self.layout.visible(id) {63
- self.layout.focus(id);62
+ if may_focus && self.wm.layout.visible(id) {63
+ self.wm.layout.focus(id);6464
} else {65
- self.layout.mark_urgent(id);65
+ self.wm.layout.mark_urgent(id);6666
}6767
self.apply_layout();6868
}@@ -71,8 +71,8 @@ impl XdgActivationHandler for State {
7171
impl State {7272
/// Where a launch starting now should put its window.7373
pub fn launch_context(&self) -> (Option<OutputId>, TagId) {74
- let output = self.layout.active_output();75
- let tag = output.map_or(self.layout.homeless(), |o| self.layout.launch_tag(o));74
+ let output = self.wm.layout.active_output();75
+ let tag = output.map_or(self.wm.layout.homeless(), |o| self.wm.layout.launch_tag(o));7676
(output, tag)7777
}7878
@@ -87,7 +87,7 @@ impl ToplevelCaptureSourceHandler for State {
8787
source: ImageCaptureSource,8888
toplevel: ForeignToplevelHandle,8989
) {90
- if let Some(id) = self.list_handles.window(&toplevel) {90
+ if let Some(id) = self.wm.list_handles.window(&toplevel) {9191
source.user_data().insert_if_missing(|| id);9292
}9393
}@@ -151,13 +151,16 @@ impl State {
151151
return Some(Target::Output(output.upgrade()?.name()));152152
}153153
let id = *data.get::<WindowId>()?;154
- self.windows.contains_key(&id).then_some(Target::Window(id))154
+ self.wm155
+ .windows156
+ .contains_key(&id)157
+ .then_some(Target::Window(id))155158
}156159
157160
fn target_size(&self, target: &Target) -> Option<(i32, i32)> {158161
let size: (i32, i32) = match target {159162
Target::Output(name) => self.output_named(name)?.1.current_mode()?.size.into(),160
- Target::Window(id) => self.windows.get(id)?.geometry().size.into(),163
+ Target::Window(id) => self.wm.windows.get(id)?.geometry().size.into(),161164
};162165
(size.0 > 0 && size.1 > 0).then_some(size)163166
}@@ -202,7 +205,7 @@ impl State {
202205
};203206
self.output_elements(o, &output, session.draw_cursor())204207
}205
- Target::Window(id) => window_elements(&mut self.renderer, &self.windows[id]),208
+ Target::Window(id) => window_elements(&mut self.renderer, &self.wm.windows[id]),206209
};207210
let buffer = frame.buffer();208211
let result = match get_dmabuf(&buffer) {@@ -268,7 +271,7 @@ impl State {
268271
pub fn output_elements(&mut self, o: OutputId, output: &Output, cursor: bool) -> Vec<Draw> {269272
let now = now_ns();270273
let mut elements = Vec::new();271
- if cursor && let Some(area) = self.layout.output(o).map(|out| out.area) {274
+ if cursor && let Some(area) = self.wm.layout.output(o).map(|out| out.area) {272275
let local = self.pointer_pos - Point::from((area.x, area.y));273276
if local.x >= 0.0 && local.y >= 0.0 && local.x < area.w && local.y < area.h {274277
elements.extend(self.cursor.elements(@@ -281,14 +284,14 @@ impl State {
281284
match crate::lock::elements(&self.services.lock, &mut self.renderer, output) {282285
Some(locked) => elements.extend(locked),283286
None => elements.extend(crate::scene::build(284
- &self.layout,287
+ &self.wm.layout,285288
o,286
- &self.windows,287
- &self.anim,289
+ &self.wm.windows,290
+ &self.wm.anim,288291
&mut self.renderer,289292
now,290293
&self.settings.config.decoration,291
- &mut self.borders,294
+ &mut self.wm.borders,292295
Some(output),293296
self.services294297
.xwayland@@ -300,7 +303,8 @@ impl State {
300303
}301304
302305
fn output_named(&self, name: &str) -> Option<(OutputId, Output)> {303
- self.layout306
+ self.wm307
+ .layout304308
.outputs()305309
.filter_map(|(o, _)| Some((o, self.backend.output(o)?)))306310
.find(|(_, out)| out.name() == name)@@ -50,19 +50,19 @@ fn dir(d: monolith_ipc::Dir) -> Dir {
5050
5151
impl State {5252
pub fn run(&mut self, cmd: Command) -> Result<(), String> {53
- let output = self.layout.active_output();54
- let focused = self.layout.focused();53
+ let output = self.wm.layout.active_output();54
+ let focused = self.wm.layout.focused();5555
let inverted = std::mem::take(&mut self.move_inverted);5656
let merge = (self.settings.config.general.move_mode == MoveMode::Merge) != inverted;5757
let on_focused = |state: &mut Self, f: fn(&mut monolith_layout::Layout, WindowId)| {5858
if let Some(w) = focused {59
- f(&mut state.layout, w);59
+ f(&mut state.wm.layout, w);6060
}6161
};6262
match cmd {6363
Command::Spawn { argv } => self.spawn(&argv)?,6464
Command::Close => {65
- if let Some(w) = focused.and_then(|w| self.windows.get(&w)) {65
+ if let Some(w) = focused.and_then(|w| self.wm.windows.get(&w)) {6666
match (w.toplevel(), w.x11_surface()) {6767
(Some(t), _) => t.send_close(),6868
(_, Some(x)) => {@@ -75,19 +75,19 @@ impl State {
7575
Command::Quit => self.core.signal.stop(),7676
Command::Reload => self.reload()?,7777
Command::Focus { dir: d } => {78
- self.layout.focus_dir(dir(d));78
+ self.wm.layout.focus_dir(dir(d));7979
}8080
Command::Move { dir: d } if merge => {81
- self.layout.merge_window(dir(d));81
+ self.wm.layout.merge_window(dir(d));8282
}8383
Command::Move { dir: d } => {84
- self.layout.move_window(dir(d));84
+ self.wm.layout.move_window(dir(d));8585
}8686
Command::MoveContainer { dir: d } => {87
- self.layout.move_container(dir(d));87
+ self.wm.layout.move_container(dir(d));8888
}8989
Command::Maximize => {90
- self.layout.expand(ExpandScope::All);90
+ self.wm.layout.expand(ExpandScope::All);9191
}9292
Command::InvertMove => self.move_inverted = !inverted,9393
Command::Resize { axis, delta } => {@@ -95,73 +95,74 @@ impl State {
9595
monolith_ipc::Axis::Horizontal => Axis::Horizontal,9696
monolith_ipc::Axis::Vertical => Axis::Vertical,9797
};98
- self.layout.resize(axis, delta);98
+ self.wm.layout.resize(axis, delta);9999
}100100
Command::Expand { scope } => {101
- self.layout.expand(match scope {101
+ self.wm.layout.expand(match scope {102102
monolith_ipc::ExpandScope::Window => ExpandScope::Window,103103
monolith_ipc::ExpandScope::Container => ExpandScope::Container,104104
});105105
}106
- Command::Flip => output.into_iter().for_each(|o| self.layout.flip(o)),106
+ Command::Flip => output.into_iter().for_each(|o| self.wm.layout.flip(o)),107107
Command::Float => on_focused(self, |l, w| l.toggle_floating(w)),108108
Command::Fullscreen => on_focused(self, |l, w| l.toggle_fullscreen(w)),109109
Command::Sticky => on_focused(self, |l, w| l.toggle_sticky(w)),110110
Command::View { tag } => {111
- let t = self.layout.tag(&tag);112
- output.into_iter().for_each(|o| self.layout.view(o, t));111
+ let t = self.wm.layout.tag(&tag);112
+ output.into_iter().for_each(|o| self.wm.layout.view(o, t));113113
}114114
Command::ToggleView { tag } => {115
- let t = self.layout.tag(&tag);115
+ let t = self.wm.layout.tag(&tag);116116
output117117
.into_iter()118
- .for_each(|o| self.layout.toggle_view(o, t));118
+ .for_each(|o| self.wm.layout.toggle_view(o, t));119119
}120120
Command::ViewEverywhere { tag } => {121
- let t = self.layout.tag(&tag);122
- self.layout.view_everywhere(t);121
+ let t = self.wm.layout.tag(&tag);122
+ self.wm.layout.view_everywhere(t);123123
}124124
Command::Tag { tag } => {125
- let t = self.layout.tag(&tag);125
+ let t = self.wm.layout.tag(&tag);126126
if let Some(w) = focused {127
- self.layout.set_tag(w, t);127
+ self.wm.layout.set_tag(w, t);128128
}129129
}130130
Command::ReorderTag { tag, index } => {131131
let t = self132
+ .wm132133
.layout133134
.tag_id(&tag)134135
.ok_or_else(|| format!("no tag {tag}"))?;135
- self.layout.reorder_tag(t, index);136
+ self.wm.layout.reorder_tag(t, index);136137
}137138
Command::FocusOutput { dir: d } => {138
- if let Some(o) = output.and_then(|o| self.layout.output_towards(o, dir(d))) {139
- self.layout.set_active_output(o);140
- if let Some(w) = self.layout.focused_on(o) {141
- self.layout.focus(w);139
+ if let Some(o) = output.and_then(|o| self.wm.layout.output_towards(o, dir(d))) {140
+ self.wm.layout.set_active_output(o);141
+ if let Some(w) = self.wm.layout.focused_on(o) {142
+ self.wm.layout.focus(w);142143
}143144
}144145
}145146
Command::SendToOutput { dir: d } => {146147
if let (Some(w), Some(o)) = (147148
focused,148
- output.and_then(|o| self.layout.output_towards(o, dir(d))),149
+ output.and_then(|o| self.wm.layout.output_towards(o, dir(d))),149150
) {150
- self.layout.send_to_output(w, o);151
+ self.wm.layout.send_to_output(w, o);151152
}152153
}153154
Command::Scratchpad { name } => {154155
if let Some(w) = focused {155
- self.layout.scratchpad_add(w, &name);156
+ self.wm.layout.scratchpad_add(w, &name);156157
}157158
}158159
Command::Summon { name } => {159
- if !self.layout.scratchpads().iter().any(|s| s.name == name) {160
+ if !self.wm.layout.scratchpads().iter().any(|s| s.name == name) {160161
return Err(format!("no scratchpad {name}"));161162
}162
- self.layout.summon(&name);163
+ self.wm.layout.summon(&name);163164
}164
- Command::Dismiss { name } => self.layout.dismiss(name.as_deref()),165
+ Command::Dismiss { name } => self.wm.layout.dismiss(name.as_deref()),165166
}166167
self.apply_layout();167168
Ok(())@@ -200,8 +201,8 @@ impl State {
200201
let config = Config::load(self.settings.path.as_deref()).map_err(|e| e.to_string())?;201202
let keyboard_changed = config.keyboard != self.settings.config.keyboard;202203
self.settings.binds = Binds::new(&config);203
- self.layout.gaps = crate::state::gaps(&config);204
- self.anim.settings = config.animation.clone();204
+ self.wm.layout.gaps = crate::state::gaps(&config);205
+ self.wm.anim.settings = config.animation.clone();205206
self.settings.config = config;206207
if keyboard_changed {207208
self.reconfigure_keyboard();@@ -6,6 +6,7 @@ use crate::backend::Backend;
66
use crate::scene::Draw;77
use crate::state::Services;88
use crate::state::{State, now_ns};9
+use crate::wm::Wm;910
use monolith_config::Vrr;1011
use monolith_layout::{OutputId, Rect};1112
use smithay::backend::allocator::Fourcc;@@ -342,8 +343,7 @@ fn connect(state: &mut State, connector: connector::Info, crtc: crtc::Handle) {
342343
let State {343344
backend: Backend::Drm(drm),344345
renderer,345
- layout,346
- space,346
+ wm: Wm { layout, space, .. },347347
core,348348
..349349
} = state@@ -446,8 +446,8 @@ fn disconnect(state: &mut State, crtc: crtc::Handle) {
446446
drm.handle.remove(token);447447
}448448
tracing::info!(name = surface.output.name(), "output disconnected");449
- state.space.unmap_output(&surface.output);450
- state.layout.remove_output(surface.id);449
+ state.wm.space.unmap_output(&surface.output);450
+ state.wm.layout.remove_output(surface.id);451451
}452452
453453
impl Drm {@@ -608,12 +608,16 @@ fn render_surface(state: &mut State, crtc: crtc::Handle, target: u64) -> bool {
608608
let State {609609
backend: Backend::Drm(drm),610610
renderer,611
- layout,612
- windows,613
- anim,614
- borders,611
+ wm:612
+ Wm {613
+ layout,614
+ windows,615
+ anim,616
+ borders,617
+ space,618
+ ..619
+ },615620
settings,616
- space,617621
core,618622
cursor,619623
cursor_status,@@ -769,7 +773,7 @@ fn on_input(state: &mut State, event: InputEvent<LibinputInputBackend>) {
769773
state.cursor_moved = true;770774
}771775
InputEvent::PointerMotionAbsolute { event } => {772
- let Some(area) = state.layout.outputs().next().map(|(_, o)| o.area) else {776
+ let Some(area) = state.wm.layout.outputs().next().map(|(_, o)| o.area) else {773777
return;774778
};775779
let p = event.position_transformed((area.w as i32, area.h as i32).into());@@ -814,7 +818,7 @@ fn clamp(
814818
let inside = |a: &Rect, p: Point<f64, smithay::utils::Logical>| {815819
p.x >= a.x && p.y >= a.y && p.x < a.right() && p.y < a.bottom()816820
};817
- let areas: Vec<Rect> = state.layout.outputs().map(|(_, o)| o.area).collect();821
+ let areas: Vec<Rect> = state.wm.layout.outputs().map(|(_, o)| o.area).collect();818822
if areas.iter().any(|a| inside(a, pos)) {819823
return pos;820824
}@@ -73,9 +73,10 @@ impl CompositorHandler for State {
7373
root = parent;7474
}7575
if let Some(window) = self76
+ .wm7677
.windows7778
.values()78
- .chain(&self.unmapped)79
+ .chain(&self.wm.unmapped)7980
.find(|w| crate::state::root(w).as_ref() == Some(&root))8081
{8182
window.on_commit();@@ -87,11 +88,12 @@ impl CompositorHandler for State {
8788
}8889
}8990
if let Some(i) = self91
+ .wm9092
.unmapped9193
.iter()9294
.position(|w| w.toplevel().is_some_and(|t| t.wl_surface() == surface))9395
{94
- let window = self.unmapped.remove(i);96
+ let window = self.wm.unmapped.remove(i);9597
let toplevel = window.toplevel().cloned();9698
self.map_window(window, None);9799
if let Some(t) = toplevel {@@ -99,8 +101,8 @@ impl CompositorHandler for State {
99101
}100102
}101103
self.on_buffer(surface);102
- self.popups.commit(surface);103
- if let Some(PopupKind::Xdg(popup)) = self.popups.find_popup(surface)104
+ self.wm.popups.commit(surface);105
+ if let Some(PopupKind::Xdg(popup)) = self.wm.popups.find_popup(surface)104106
&& !popup.is_initial_configure_sent()105107
{106108
let _ = popup.send_configure();@@ -141,7 +143,7 @@ impl XdgShellHandler for State {
141143
}142144
143145
fn new_toplevel(&mut self, surface: ToplevelSurface) {144
- self.unmapped.push(Window::new_wayland_window(surface));146
+ self.wm.unmapped.push(Window::new_wayland_window(surface));145147
}146148
147149
fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {@@ -158,7 +160,7 @@ impl XdgShellHandler for State {
158160
159161
fn new_popup(&mut self, surface: PopupSurface, positioner: PositionerState) {160162
surface.with_pending_state(|s| s.geometry = positioner.get_geometry());161
- let _ = self.popups.track_popup(PopupKind::Xdg(surface));163
+ let _ = self.wm.popups.track_popup(PopupKind::Xdg(surface));162164
}163165
164166
fn reposition_request(@@ -361,9 +363,13 @@ fn server_side(toplevel: &ToplevelSurface) {
361363
impl State {362364
fn set_fullscreen(&mut self, surface: &WlSurface, on: bool) {363365
if let Some(id) = self.window_id(surface)364
- && self.layout.window(id).is_some_and(|w| w.fullscreen != on)366
+ && self367
+ .wm368
+ .layout369
+ .window(id)370
+ .is_some_and(|w| w.fullscreen != on)365371
{366
- self.layout.toggle_fullscreen(id);372
+ self.wm.layout.toggle_fullscreen(id);367373
self.apply_layout();368374
}369375
}@@ -383,6 +389,7 @@ impl WorkspaceHandler for State {
383389
Action::Create { group, name } => (group, name),384390
};385391
let Some(o) = self392
+ .wm386393
.layout387394
.outputs()388395
.find(|(_, out)| out.name == *group)@@ -390,12 +397,12 @@ impl WorkspaceHandler for State {
390397
else {391398
continue;392399
};393
- let t = self.layout.tag(tag);400
+ let t = self.wm.layout.tag(tag);394401
match action {395
- Action::Activate { .. } | Action::Create { .. } => self.layout.view(o, t),402
+ Action::Activate { .. } | Action::Create { .. } => self.wm.layout.view(o, t),396403
Action::Deactivate { .. } => {397
- if self.layout.is_viewed(o, t) {398
- self.layout.toggle_view(o, t);404
+ if self.wm.layout.is_viewed(o, t) {405
+ self.wm.layout.toggle_view(o, t);399406
}400407
}401408
}@@ -59,7 +59,8 @@ impl State {
5959
while let Some(parent) = get_parent(&root) {6060
root = parent;6161
}62
- self.space62
+ self.wm63
+ .space6364
.elements()6465
.any(|w| crate::state::root(w).as_ref() == Some(&root))6566
|| self.layer_maps_contain(&root)@@ -115,7 +115,7 @@ impl State {
115115
);116116
match action {117117
Some(Some(Action::Run(cmd))) => {118
- self.layer_focus = None;118
+ self.wm.layer_focus = None;119119
if let Err(e) = self.run(cmd) {120120
tracing::warn!("bind failed: {e}");121121
}@@ -143,22 +143,23 @@ fn focus_under_pointer(state: &mut State) {
143143
if let Some((layer, surface, _)) = state.layer_under(pos, &layers)144144
&& layer.can_receive_keyboard_focus()145145
{146
- state.layer_focus = Some(surface);146
+ state.wm.layer_focus = Some(surface);147147
state.sync_focus();148148
return;149149
}150
- if state.layer_focus.take().is_some() {150
+ if state.wm.layer_focus.take().is_some() {151151
state.sync_focus();152152
}153153
let id = state154
+ .wm154155
.space155156
.element_under(pos)156157
.and_then(|(w, _)| crate::state::root(w))157158
.and_then(|s| state.window_id(&s));158159
if let Some(id) = id159
- && state.layout.focused() != Some(id)160
+ && state.wm.layout.focused() != Some(id)160161
{161
- state.layout.focus(id);162
+ state.wm.layout.focus(id);162163
state.apply_layout();163164
}164165
}@@ -188,10 +189,11 @@ fn start_drag(state: &mut State, button: u32) -> bool {
188189
Modifier::Ctrl => m.ctrl,189190
Modifier::Shift => m.shift,190191
});191
- let Some(id) = state.layout.focused() else {192
+ let Some(id) = state.wm.layout.focused() else {192193
return false;193194
};194195
let Some(rect) = state196
+ .wm195197
.layout196198
.window(id)197199
.filter(|w| w.floating && !w.fullscreen)@@ -223,7 +225,7 @@ fn drag_to(state: &mut State, pos: Point<f64, Logical>) -> bool {
223225
Rect::new(r.x + dx, r.y + dy, r.w, r.h)224226
};225227
let id = d.window;226
- state.layout.set_float_rect(id, rect);228
+ state.wm.layout.set_float_rect(id, rect);227229
state.apply_layout();228230
true229231
}@@ -147,7 +147,7 @@ pub fn broadcast(state: &mut State) {
147147
}148148
149149
fn snapshot(state: &State) -> monolith_ipc::State {150
- let l = &state.layout;150
+ let l = &state.wm.layout;151151
let focused = l.focused();152152
let tags = l153153
.tags()@@ -173,7 +173,7 @@ fn snapshot(state: &State) -> monolith_ipc::State {
173173
let mut windows: Vec<monolith_ipc::Window> = l174174
.windows()175175
.map(|(id, w)| {176
- let (app_id, title) = state.windows.get(&id).map(identity).unwrap_or_default();176
+ let (app_id, title) = state.wm.windows.get(&id).map(identity).unwrap_or_default();177177
monolith_ipc::Window {178178
id: slotmap::Key::data(&id).as_ffi(),179179
app_id,@@ -35,8 +35,8 @@ impl WlrLayerShellHandler for State {
3535
let output = output3636
.as_ref()3737
.and_then(Output::from_resource)38
- .or_else(|| self.space.output_under(self.pointer_pos).next().cloned())39
- .or_else(|| self.space.outputs().next().cloned());38
+ .or_else(|| self.wm.space.output_under(self.pointer_pos).next().cloned())39
+ .or_else(|| self.wm.space.outputs().next().cloned());4040
let Some(output) = output else {4141
surface.send_close();4242
return;@@ -49,11 +49,11 @@ impl WlrLayerShellHandler for State {
4949
}5050
5151
fn new_popup(&mut self, _parent: WlrLayerSurface, popup: PopupSurface) {52
- let _ = self.popups.track_popup(PopupKind::Xdg(popup));52
+ let _ = self.wm.popups.track_popup(PopupKind::Xdg(popup));5353
}5454
5555
fn layer_destroyed(&mut self, surface: WlrLayerSurface) {56
- let outputs: Vec<Output> = self.space.outputs().cloned().collect();56
+ let outputs: Vec<Output> = self.wm.space.outputs().cloned().collect();5757
for output in outputs {5858
let mut map = layer_map_for_output(&output);5959
let gone = map@@ -64,8 +64,8 @@ impl WlrLayerShellHandler for State {
6464
map.unmap_layer(&layer);6565
}6666
}67
- if self.layer_focus.as_ref() == Some(surface.wl_surface()) {68
- self.layer_focus = None;67
+ if self.wm.layer_focus.as_ref() == Some(surface.wl_surface()) {68
+ self.wm.layer_focus = None;6969
}7070
self.reserve_layers();7171
}@@ -77,6 +77,7 @@ impl State {
7777
/// Returns false for surfaces that are not layer surfaces.7878
pub fn layer_commit(&mut self, surface: &WlSurface) -> bool {7979
let Some(output) = self80
+ .wm8081
.space8182
.outputs()8283
.find(|o| {@@ -109,13 +110,13 @@ impl State {
109110
110111
/// Keeps tiled windows out of the space bars reserve.111112
pub fn reserve_layers(&mut self) {112
- let outputs: Vec<OutputId> = self.layout.outputs().map(|(o, _)| o).collect();113
+ let outputs: Vec<OutputId> = self.wm.layout.outputs().map(|(o, _)| o).collect();113114
let mut changed = false;114115
for o in outputs {115116
let Some(output) = self.backend.output(o) else {116117
continue;117118
};118
- let Some(area) = self.layout.output(o).map(|out| out.area) else {119
+ let Some(area) = self.wm.layout.output(o).map(|out| out.area) else {119120
continue;120121
};121122
let mut map = layer_map_for_output(&output);@@ -128,7 +129,7 @@ impl State {
128129
right: (area.w - f64::from(zone.loc.x + zone.size.w)).max(0.0),129130
};130131
drop(map);131
- if self.layout.set_reserved(o, reserved) {132
+ if self.wm.layout.set_reserved(o, reserved) {132133
tracing::debug!(133134
output = output.name(),134135
?zone,@@ -148,7 +149,7 @@ impl State {
148149
/// The layer surface that should hold the keyboard, if any. One asking149150
/// for exclusive focus above the windows wins over everything.150151
pub fn layer_keyboard_focus(&self) -> Option<WlSurface> {151
- for output in self.space.outputs() {152
+ for output in self.wm.space.outputs() {152153
let map = layer_map_for_output(output);153154
let exclusive = map154155
.layers_on(Layer::Overlay)@@ -160,11 +161,11 @@ impl State {
160161
return Some(l.wl_surface().clone());161162
}162163
}163
- self.layer_focus.clone().filter(|s| s.is_alive())164
+ self.wm.layer_focus.clone().filter(|s| s.is_alive())164165
}165166
166167
pub fn layer_maps_contain(&self, surface: &WlSurface) -> bool {167
- self.space.outputs().any(|o| {168
+ self.wm.space.outputs().any(|o| {168169
layer_map_for_output(o)169170
.layer_for_surface(surface, WindowSurfaceType::TOPLEVEL)170171
.is_some()@@ -177,8 +178,8 @@ impl State {
177178
pos: Point<f64, Logical>,178179
layers: &[Layer],179180
) -> Option<(LayerSurface, WlSurface, Point<f64, Logical>)> {180
- let output = self.space.output_under(pos).next()?;181
- let origin = self.space.output_geometry(output)?.loc;181
+ let output = self.wm.space.output_under(pos).next()?;182
+ let origin = self.wm.space.output_geometry(output)?.loc;182183
let local = pos - origin.to_f64();183184
let map = layer_map_for_output(output);184185
layers.iter().find_map(|&layer| {@@ -102,6 +102,7 @@ impl State {
102102
return;103103
}104104
let all = self105
+ .wm105106
.space106107
.outputs()107108
.all(|o| self.services.lock.blanked.contains(&o.name()));@@ -120,7 +121,7 @@ impl State {
120121
}121122
122123
pub fn lock_keyboard_focus(&self) -> Option<WlSurface> {123
- let under = self.space.output_under(self.pointer_pos).next();124
+ let under = self.wm.space.output_under(self.pointer_pos).next();124125
under125126
.and_then(|o| self.services.lock.surface_on(o))126127
.or_else(|| {@@ -138,8 +139,8 @@ impl State {
138139
&self,139140
pos: Point<f64, Logical>,140141
) -> Option<(WlSurface, Point<f64, Logical>)> {141
- let output = self.space.output_under(pos).next()?;142
- let origin = self.space.output_geometry(output)?.loc;142
+ let output = self.wm.space.output_under(pos).next()?;143
+ let origin = self.wm.space.output_geometry(output)?.loc;143144
let surface = self.services.lock.surface_on(output)?;144145
under_from_surface_tree(145146
surface.wl_surface(),@@ -19,6 +19,7 @@ mod nested;
1919
mod scene;2020
mod state;2121
mod toplevels;22
+mod wm;2223
mod xwayland;2324
2425
#[cfg(feature = "spans")]@@ -8,6 +8,7 @@ mod input;
88
use crate::backend::Backend;99
use crate::state::Services;1010
use crate::state::State;11
+use crate::wm::Wm;1112
use calloop_wayland_source::WaylandSource;1213
use monolith_layout::{OutputId, Rect};1314
use monolith_render_vk::VulkanAllocator;@@ -132,14 +133,14 @@ pub fn init(
132133
Some((0, 0).into()),133134
);134135
output.set_preferred(mode);135
- state.space.map_output(&output, (0, 0));136
+ state.wm.space.map_output(&output, (0, 0));136137
let area = Rect::new(137138
0.0,138139
0.0,139140
f64::from(DEFAULT_SIZE.0),140141
f64::from(DEFAULT_SIZE.1),141142
);142
- let output_id = state.layout.add_output("nested", area);143
+ let output_id = state.wm.layout.add_output("nested", area);143144
144145
state.backend = Backend::Nested(Box::new(Host {145146
output_id,@@ -174,13 +175,17 @@ pub fn render(state: &mut State) {
174175
let State {175176
backend,176177
renderer,177
- space,178
+ wm:179
+ Wm {180
+ layout,181
+ windows,182
+ anim,183
+ borders,184
+ space,185
+ ..186
+ },178187
needs_redraw,179188
core,180
- layout,181
- windows,182
- anim,183
- borders,184189
settings,185190
services:186191
Services {@@ -2,6 +2,7 @@ use crate::anim::{Animator, Ghost};
22
use crate::commands::Binds;33
use crate::focus::Focus;44
use crate::globals::Protocols;5
+use crate::wm::Wm;56
use monolith_config::Config;67
use monolith_layout::{Layout, OutputId, Rect, WindowId};78
use monolith_protocols::workspace::{Group, Snapshot, Workspace};@@ -39,8 +40,7 @@ pub struct State {
3940
pub settings: Settings,4041
pub protocols: Protocols,4142
pub renderer: VulkanRenderer,42
- pub space: Space<Window>,43
- pub popups: PopupManager,43
+ pub wm: Wm,4444
pub backend: crate::backend::Backend,4545
pub cursor: crate::cursor::Cursor,4646
pub cursor_status: CursorImageStatus,@@ -48,28 +48,12 @@ pub struct State {
4848
/// The pointer moved, which on its own only redraws the cursor.4949
pub cursor_moved: bool,5050
51
- pub layout: Layout,52
- pub windows: HashMap<WindowId, Window>,53
- /// Toplevels that have not committed yet. Rules need their app_id and54
- /// title, which clients set after creating the toplevel.55
- pub unmapped: Vec<Window>,5651
pub suppressed: Vec<Keycode>,5752
pub move_inverted: bool,5853
pub drag: Option<crate::input::Drag>,5954
/// Global logical position, shared by every backend.6055
pub pointer_pos: Point<f64, Logical>,6156
pub services: Services,62
- pub list_handles: crate::toplevels::ListHandles,63
- pub anim: Animator,64
- pub borders: crate::scene::Borders,65
- /// How each shown window was placed by the last layout pass, to spot66
- /// switches between tiled, floating and fullscreen.67
- pub last_kind: HashMap<WindowId, (Kind, Rect, OutputId)>,68
- pub awaiting: HashSet<WindowId>,69
- /// Windows a rule sent somewhere, which activation tokens leave alone.70
- pub placed_by_rule: HashSet<WindowId>,71
- /// A layer surface given the keyboard by a click, like a launcher.72
- pub layer_focus: Option<WlSurface>,7357
pub seat_state: SeatState<State>,7458
pub seat: Seat<State>,7559
}@@ -165,16 +149,25 @@ impl State {
165149
path: config_path,166150
},167151
renderer,168
- space: Space::default(),169
- popups: PopupManager::default(),152
+ wm: Wm {153
+ layout,154
+ windows: HashMap::new(),155
+ unmapped: Vec::new(),156
+ space: Space::default(),157
+ popups: PopupManager::default(),158
+ anim: Animator::new(animation),159
+ borders: Default::default(),160
+ last_kind: HashMap::new(),161
+ awaiting: HashSet::new(),162
+ placed_by_rule: HashSet::new(),163
+ list_handles: Default::default(),164
+ layer_focus: None,165
+ },170166
backend: crate::backend::Backend::Starting,171167
cursor: crate::cursor::Cursor::load(),172168
cursor_status: CursorImageStatus::default_named(),173169
needs_redraw: true,174170
cursor_moved: false,175
- layout,176
- windows: HashMap::new(),177
- unmapped: Vec::new(),178171
suppressed: Vec::new(),179172
move_inverted: false,180173
drag: None,@@ -188,13 +181,6 @@ impl State {
188181
ipc: Default::default(),189182
idle,190183
},191
- list_handles: Default::default(),192
- anim: Animator::new(animation),193
- borders: Default::default(),194
- last_kind: HashMap::new(),195
- awaiting: HashSet::new(),196
- placed_by_rule: HashSet::new(),197
- layer_focus: None,198184
seat_state,199185
seat,200186
})@@ -202,8 +188,8 @@ impl State {
202188
203189
/// Runs after every batch of events, so many client commits produce one frame.204190
pub fn after_dispatch(&mut self) {205
- self.space.refresh();206
- self.popups.cleanup();191
+ self.wm.space.refresh();192
+ self.wm.popups.cleanup();207193
match self.backend {208194
crate::backend::Backend::Drm(_) => crate::drm::render(self),209195
crate::backend::Backend::Nested(_) => crate::nested::render(self),@@ -227,7 +213,7 @@ impl State {
227213
};228214
host.output229215
.change_current_state(Some(mode), None, None, None);230
- self.layout.set_output_area(o, area);216
+ self.wm.layout.set_output_area(o, area);231217
self.reserve_layers();232218
self.reconfigure_lock();233219
self.apply_layout();@@ -252,14 +238,15 @@ impl State {
252238
.output253239
.as_deref()254240
.and_then(|name| {255
- self.layout241
+ self.wm242
+ .layout256243
.outputs()257244
.find(|(_, o)| o.name == name)258245
.map(|(id, _)| id)259246
})260247
.or(launch.and_then(|l| l.0));261248
let tag = match rule.tag.as_deref() {262
- Some(t) => Some(self.layout.tag(t)),249
+ Some(t) => Some(self.wm.layout.tag(t)),263250
None => launch.map(|l| l.1),264251
};265252
let float = float.map(|r| {@@ -270,23 +257,23 @@ impl State {
270257
f64::from(r.size.h),271258
)272259
});273
- let id = self.layout.add_window(output, tag, float);260
+ let id = self.wm.layout.add_window(output, tag, float);274261
if rule.tag.is_some() || rule.output.is_some() {275
- self.placed_by_rule.insert(id);262
+ self.wm.placed_by_rule.insert(id);276263
}277264
if rule.floating == Some(true) {278
- self.layout.toggle_floating(id);265
+ self.wm.layout.toggle_floating(id);279266
}280267
if rule.sticky == Some(true) {281
- self.layout.toggle_sticky(id);268
+ self.wm.layout.toggle_sticky(id);282269
}283270
if let Some(name) = &rule.scratchpad {284
- self.layout.scratchpad_add(id, name);271
+ self.wm.layout.scratchpad_add(id, name);285272
}286
- self.windows.insert(id, window);273
+ self.wm.windows.insert(id, window);287274
self.await_first_buffer(id);288
- if self.layout.visible(id) {289
- self.layout.focus(id);275
+ if self.wm.layout.visible(id) {276
+ self.wm.layout.focus(id);290277
}291278
self.apply_layout();292279
}@@ -295,7 +282,7 @@ impl State {
295282
/// spawn animation never plays over an empty slot. Clients that are slow296283
/// to draw get it anyway after a while.297284
fn await_first_buffer(&mut self, id: WindowId) {298
- self.awaiting.insert(id);285
+ self.wm.awaiting.insert(id);299286
let timeout = Timer::from_duration(Duration::from_millis(300));300287
let _ = self.core.handle.insert_source(timeout, move |_, _, state| {301288
state.first_buffer(id);@@ -305,7 +292,7 @@ impl State {
305292
306293
/// Called on commits, starts the spawn of a window that now has a buffer.307294
pub fn on_buffer(&mut self, surface: &WlSurface) {308
- if self.awaiting.is_empty() {295
+ if self.wm.awaiting.is_empty() {309296
return;310297
}311298
let has_buffer =@@ -316,44 +303,48 @@ impl State {
316303
}317304
318305
fn first_buffer(&mut self, id: WindowId) {319
- self.placed_by_rule.remove(&id);320
- if self.awaiting.remove(&id) {321
- self.anim.spawned(id, now_ns());306
+ self.wm.placed_by_rule.remove(&id);307
+ if self.wm.awaiting.remove(&id) {308
+ self.wm.anim.spawned(id, now_ns());322309
self.apply_layout();323310
}324311
}325312
326313
pub fn unmap_window(&mut self, surface: &WlSurface) {327
- self.unmapped.retain(|w| root(w).as_ref() != Some(surface));314
+ self.wm315
+ .unmapped316
+ .retain(|w| root(w).as_ref() != Some(surface));328317
if let Some(id) = self.window_id(surface) {329318
self.remove_window(id);330319
}331320
}332321
333322
pub fn remove_window(&mut self, id: WindowId) {334
- if let Some(window) = self.windows.remove(&id) {335
- self.space.unmap_elem(&window);323
+ if let Some(window) = self.wm.windows.remove(&id) {324
+ self.wm.space.unmap_elem(&window);336325
self.leave_ghost(id, &window);337326
}338
- self.layout.remove_window(id);339
- self.placed_by_rule.remove(&id);327
+ self.wm.layout.remove_window(id);328
+ self.wm.placed_by_rule.remove(&id);340329
self.apply_layout();341330
}342331
343332
/// Keeps a visible tiled window's last frame shrinking to its seed while344333
/// its neighbours close over it.345334
fn leave_ghost(&mut self, id: WindowId, window: &Window) {346
- let Some(o) = self.layout.window(id).and_then(|w| w.output) else {335
+ let Some(o) = self.wm.layout.window(id).and_then(|w| w.output) else {347336
return;348337
};349338
let tiled = self339
+ .wm350340
.layout351341
.output(o)352342
.is_some_and(|out| out.tree.contains(id));353
- if !tiled || !self.layout.visible(id) {343
+ if !tiled || !self.wm.layout.visible(id) {354344
return;355345
}356346
let Some(seed) = self347
+ .wm357348
.layout358349
.arrange_hiding(o, &[id])359350
.tiled@@ -374,7 +365,7 @@ impl State {
374365
})375366
.flatten();376367
if let Some((texture, scale, transform)) = snapshot {377
- self.anim.add_ghost(Ghost {368
+ self.wm.anim.add_ghost(Ghost {378369
id,379370
output: o,380371
seed: seed.rect,@@ -388,7 +379,8 @@ impl State {
388379
}389380
390381
pub fn window_id(&self, surface: &WlSurface) -> Option<WindowId> {391
- self.windows382
+ self.wm383
+ .windows392384
.iter()393385
.find(|(_, w)| root(w).as_ref() == Some(surface))394386
.map(|(id, _)| *id)@@ -399,10 +391,10 @@ impl State {
399391
let now = now_ns();400392
let mut shown = Vec::new();401393
let mut tracks = Vec::new();402
- let outputs: Vec<OutputId> = self.layout.outputs().map(|(o, _)| o).collect();394
+ let outputs: Vec<OutputId> = self.wm.layout.outputs().map(|(o, _)| o).collect();403395
for o in outputs {404
- let arr = self.layout.arrange(o);405
- let area = self.layout.output(o).map(|o| o.area).unwrap_or_default();396
+ let arr = self.wm.layout.arrange(o);397
+ let area = self.wm.layout.output(o).map(|o| o.area).unwrap_or_default();406398
for p in &arr.tiled {407399
if p.visible && arr.fullscreen != Some(p.window) {408400
shown.push((p.window, p.rect, Kind::Tiled, o));@@ -420,12 +412,12 @@ impl State {
420412
.tiled421413
.iter()422414
.map(|p| p.window)423
- .filter(|w| self.awaiting.contains(w))415
+ .filter(|w| self.wm.awaiting.contains(w))424416
.collect();425417
let planned = if held.is_empty() {426418
arr.tiled.clone()427419
} else {428
- self.layout.arrange_hiding(o, &held).tiled420
+ self.wm.layout.arrange_hiding(o, &held).tiled429421
};430422
let targets: Vec<(WindowId, Rect)> =431423
planned.iter().map(|p| (p.window, p.rect)).collect();@@ -434,71 +426,74 @@ impl State {
434426
435427
let mut glides = Vec::new();436428
for &(id, rect, kind, _) in &shown {437
- match self.last_kind.get(&id) {429
+ match self.wm.last_kind.get(&id) {438430
Some(&(was, from, o)) if was != kind => {439431
let from = self432
+ .wm440433
.anim441434
.gliding(id, now)442435
.or_else(|| {443436
(was == Kind::Tiled)444
- .then(|| self.anim.tiled(o, id, now))437
+ .then(|| self.wm.anim.tiled(o, id, now))445438
.flatten()446439
})447440
.unwrap_or(from);448441
glides.push((id, from, rect));449442
}450
- Some(&(Kind::Floating, from, _)) if from != rect => self.anim.stop_glide(id),443
+ Some(&(Kind::Floating, from, _)) if from != rect => self.wm.anim.stop_glide(id),451444
_ => {}452445
}453446
}454
- let gap = self.layout.gaps.inner;447
+ let gap = self.wm.layout.gaps.inner;455448
for (o, targets) in tracks {456449
let new: Vec<WindowId> = targets457450
.iter()458451
.map(|t| t.0)459
- .filter(|&id| self.anim.tiled(o, id, now).is_none())452
+ .filter(|&id| self.wm.anim.tiled(o, id, now).is_none())460453
.collect();461454
let seeds: HashMap<WindowId, Rect> = if new.is_empty() {462455
HashMap::new()463456
} else {464
- self.layout457
+ self.wm458
+ .layout465459
.arrange_hiding(o, &new)466460
.tiled467461
.into_iter()468462
.map(|p| (p.window, p.rect))469463
.collect()470464
};471
- self.anim.retarget(o, now, &targets, gap, |id| {465
+ self.wm.anim.retarget(o, now, &targets, gap, |id| {472466
seeds.get(&id).copied().unwrap_or_default()473467
});474468
}475469
for (id, from, to) in glides {476
- self.anim.glide(id, from, to, now);470
+ self.wm.anim.glide(id, from, to, now);477471
}478
- self.last_kind = shown.iter().map(|&(id, r, k, o)| (id, (k, r, o))).collect();472
+ self.wm.last_kind = shown.iter().map(|&(id, r, k, o)| (id, (k, r, o))).collect();479473
480474
let mut mapped = Vec::new();481475
for (id, rect, kind, _) in shown {482
- if let Some(window) = self.windows.get(&id)483
- && configure(window, rect, kind, self.layout.maximized(id))476
+ if let Some(window) = self.wm.windows.get(&id)477
+ && configure(window, rect, kind, self.wm.layout.maximized(id))484478
{485479
let loc = (rect.x.round() as i32, rect.y.round() as i32);486480
let loc = Point::from(loc) - window.geometry().loc;487
- self.space.map_element(window.clone(), loc, false);481
+ self.wm.space.map_element(window.clone(), loc, false);488482
mapped.push(id);489483
}490484
}491485
let hidden: Vec<Window> = self486
+ .wm492487
.windows493488
.iter()494489
.filter(|(id, _)| !mapped.contains(id))495490
.map(|(_, w)| w.clone())496491
.collect();497492
for window in hidden {498
- self.space.unmap_elem(&window);493
+ self.wm.space.unmap_elem(&window);499494
}500495
for id in &mapped {501
- self.space.raise_element(&self.windows[id], false);496
+ self.wm.space.raise_element(&self.wm.windows[id], false);502497
}503498
self.sync_focus();504499
self.update_inhibit();@@ -506,8 +501,8 @@ impl State {
506501
}507502
508503
pub fn sync_focus(&mut self) {509
- let focused = self.layout.focused();510
- for (id, window) in &self.windows {504
+ let focused = self.wm.layout.focused();505
+ for (id, window) in &self.wm.windows {511506
if window.set_activated(Some(*id) == focused)512507
&& let Some(t) = window.toplevel()513508
&& t.is_initial_configure_sent()@@ -519,7 +514,7 @@ impl State {
519514
self.lock_keyboard_focus().map(Focus::from)520515
} else {521516
self.layer_keyboard_focus().map(Focus::from).or_else(|| {522
- let w = focused.and_then(|id| self.windows.get(&id))?;517
+ let w = focused.and_then(|id| self.wm.windows.get(&id))?;523518
match w.x11_surface() {524519
Some(x) => Some(Focus::X11(Box::new(x.clone()))),525520
None => w.toplevel().map(|t| Focus::from(t.wl_surface().clone())),@@ -537,7 +532,7 @@ impl State {
537532
if !self.protocols.workspaces.has_clients() {538533
return;539534
}540
- let l = &self.layout;535
+ let l = &self.wm.layout;541536
let occupied: HashSet<(OutputId, monolith_layout::TagId)> = l542537
.windows()543538
.filter(|(_, w)| w.scratchpad.is_none())@@ -609,11 +604,11 @@ impl State {
609604
return Some((s, (p + geo.loc).to_f64()));610605
}611606
}612
- let fullscreen = self.layout.outputs().any(|(o, out)| {607
+ let fullscreen = self.wm.layout.outputs().any(|(o, out)| {613608
let a = out.area;614609
(a.x..a.right()).contains(&pos.x)615610
&& (a.y..a.bottom()).contains(&pos.y)616
- && self.layout.arrange(o).fullscreen.is_some()611
+ && self.wm.layout.arrange(o).fullscreen.is_some()617612
});618613
let above: &[Layer] = if fullscreen {619614
&[Layer::Overlay]@@ -623,7 +618,8 @@ impl State {
623618
let layer = |layers| self.layer_under(pos, layers).map(|(_, s, p)| (s, p));624619
layer(above)625620
.or_else(|| {626
- self.space621
+ self.wm622
+ .space627623
.element_under(pos)628624
.and_then(|(window, location)| {629625
window@@ -39,22 +39,22 @@ impl ForeignToplevelHandler for State {
3939
}4040
4141
fn foreign_toplevel_action(&mut self, action: Action) {42
- let id_of = |k: u64| self.windows.keys().copied().find(|&id| key(id) == k);42
+ let id_of = |k: u64| self.wm.windows.keys().copied().find(|&id| key(id) == k);4343
match action {4444
Action::Activate(k) => {4545
let Some(id) = id_of(k) else { return };46
- if !self.layout.visible(id)47
- && let Some(w) = self.layout.window(id)46
+ if !self.wm.layout.visible(id)47
+ && let Some(w) = self.wm.layout.window(id)4848
&& let Some(o) = w.output4949
&& w.scratchpad.is_none()5050
{5151
let tag = w.tag;52
- self.layout.toggle_view(o, tag);52
+ self.wm.layout.toggle_view(o, tag);5353
}54
- self.layout.focus(id);54
+ self.wm.layout.focus(id);5555
}5656
Action::Close(k) => {57
- let Some(window) = id_of(k).and_then(|id| self.windows.get(&id)) else {57
+ let Some(window) = id_of(k).and_then(|id| self.wm.windows.get(&id)) else {5858
return;5959
};6060
match (window.toplevel(), window.x11_surface()) {@@ -68,15 +68,20 @@ impl ForeignToplevelHandler for State {
6868
}6969
Action::Maximize(k, on) => {7070
let Some(id) = id_of(k) else { return };71
- if self.layout.maximized(id) != on {72
- self.layout.focus(id);73
- self.layout.expand(monolith_layout::ExpandScope::All);71
+ if self.wm.layout.maximized(id) != on {72
+ self.wm.layout.focus(id);73
+ self.wm.layout.expand(monolith_layout::ExpandScope::All);7474
}7575
}7676
Action::Fullscreen(k, on) => {7777
let Some(id) = id_of(k) else { return };78
- if self.layout.window(id).is_some_and(|w| w.fullscreen != on) {79
- self.layout.toggle_fullscreen(id);78
+ if self79
+ .wm80
+ .layout81
+ .window(id)82
+ .is_some_and(|w| w.fullscreen != on)83
+ {84
+ self.wm.layout.toggle_fullscreen(id);8085
}8186
}8287
}@@ -87,15 +92,15 @@ impl ForeignToplevelHandler for State {
8792
impl State {8893
/// Brings both window lists in line with the windows.8994
pub fn publish_toplevels(&mut self) {90
- let mut handles = std::mem::take(&mut self.list_handles.0);95
+ let mut handles = std::mem::take(&mut self.wm.list_handles.0);9196
handles.retain(|id, h| {92
- let keep = self.windows.contains_key(id);97
+ let keep = self.wm.windows.contains_key(id);9398
if !keep {9499
self.protocols.toplevel_list.remove_toplevel(h);95100
}96101
keep97102
});98
- for (id, window) in &self.windows {103
+ for (id, window) in &self.wm.windows {99104
let (app_id, title) = identity(window);100105
match handles.get(id) {101106
Some(h) => {@@ -114,18 +119,19 @@ impl State {
114119
}115120
}116121
}117
- self.list_handles.0 = handles;122
+ self.wm.list_handles.0 = handles;118123
119124
if !self.protocols.foreign_toplevels.has_clients() {120125
return;121126
}122
- let focused = self.layout.focused();127
+ let focused = self.wm.layout.focused();123128
let toplevels = self129
+ .wm124130
.windows125131
.iter()126132
.map(|(&id, window)| {127133
let (app_id, title) = identity(window);128
- let w = self.layout.window(id);134
+ let w = self.wm.layout.window(id);129135
Toplevel {130136
key: key(id),131137
title,@@ -136,7 +142,7 @@ impl State {
136142
.into_iter()137143
.collect(),138144
activated: focused == Some(id),139
- maximized: self.layout.maximized(id),145
+ maximized: self.wm.layout.maximized(id),140146
fullscreen: w.is_some_and(|w| w.fullscreen),141147
}142148
})@@ -0,0 +1,29 @@
1
+//! The window model: the layout and what monolith keeps per window beside it.2
+3
+use crate::anim::Animator;4
+use crate::state::Kind;5
+use monolith_layout::{Layout, OutputId, Rect, WindowId};6
+use smithay::desktop::{PopupManager, Space, Window};7
+use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;8
+use std::collections::{HashMap, HashSet};9
+10
+pub struct Wm {11
+ pub layout: Layout,12
+ pub windows: HashMap<WindowId, Window>,13
+ /// Toplevels that have not committed yet. Rules need their app_id and14
+ /// title, which clients set after creating the toplevel.15
+ pub unmapped: Vec<Window>,16
+ pub space: Space<Window>,17
+ pub popups: PopupManager,18
+ pub anim: Animator,19
+ pub borders: crate::scene::Borders,20
+ /// How each shown window was placed by the last layout pass, to spot21
+ /// switches between tiled, floating and fullscreen.22
+ pub last_kind: HashMap<WindowId, (Kind, Rect, OutputId)>,23
+ pub awaiting: HashSet<WindowId>,24
+ /// Windows a rule sent somewhere, which activation tokens leave alone.25
+ pub placed_by_rule: HashSet<WindowId>,26
+ pub list_handles: crate::toplevels::ListHandles,27
+ /// A layer surface given the keyboard by a click, like a launcher.28
+ pub layer_focus: Option<WlSurface>,29
+}@@ -320,6 +320,7 @@ impl XwmHandler for State {
320320
fn disconnected(&mut self, _xwm: XwmId) {321321
tracing::info!("Xwayland exited");322322
let gone: Vec<_> = self323
+ .wm323324
.windows324325
.iter()325326
.filter(|(_, w)| w.x11_surface().is_some())@@ -357,7 +358,8 @@ fn floats(window: &X11Surface) -> bool {
357358
358359
impl State {359360
pub fn x11_window_id(&self, window: &X11Surface) -> Option<monolith_layout::WindowId> {360
- self.windows361
+ self.wm362
+ .windows361363
.iter()362364
.find(|(_, w)| w.x11_surface() == Some(window))363365
.map(|(id, _)| *id)@@ -375,9 +377,13 @@ impl State {
375377
376378
fn x11_fullscreen(&mut self, window: &X11Surface, on: bool) {377379
if let Some(id) = self.x11_window_id(window)378
- && self.layout.window(id).is_some_and(|w| w.fullscreen != on)380
+ && self381
+ .wm382
+ .layout383
+ .window(id)384
+ .is_some_and(|w| w.fullscreen != on)379385
{380
- self.layout.toggle_fullscreen(id);386
+ self.wm.layout.toggle_fullscreen(id);381387
self.apply_layout();382388
}383389
}