Commit 60956e3999de
tarball13 files changed+232-198
@@ -52,9 +52,10 @@ impl XdgActivationHandler for State {
5252
return;5353
};5454
let ctx = data.user_data.get::<Context>();55
- if self.wm.awaiting.contains(&id)55
+ if let Some(w) = self.wm.get(id)56
+ && w.awaiting_buffer57
+ && !w.placed_by_rule5658
&& let Some(ctx) = ctx57
- && !self.wm.placed_by_rule.contains(&id)5859
{5960
self.wm.layout.relocate(id, ctx.output, ctx.tag);6061
}@@ -237,6 +237,11 @@ impl Animator {
237237
|| !self.spawns.is_empty()238238
}239239
240
+ pub fn forget(&mut self, id: WindowId) {241
+ self.glides.remove(&id);242
+ self.spawns.remove(&id);243
+ }244
+240245
pub fn prune(&mut self, now: u64) {241246
self.glides.retain(|_, g| g.timeline.end_ns() > now);242247
let phase = self.phase_ns();@@ -87,7 +87,11 @@ impl ToplevelCaptureSourceHandler for State {
8787
source: ImageCaptureSource,8888
toplevel: ForeignToplevelHandle,8989
) {90
- if let Some(id) = self.wm.list_handles.window(&toplevel) {90
+ let id = self.wm.iter().find_map(|(id, w)| {91
+ let h = w.list_handle.as_ref()?;92
+ (h.identifier() == toplevel.identifier()).then_some(id)93
+ });94
+ if let Some(id) = id {9195
source.user_data().insert_if_missing(|| id);9296
}9397
}@@ -151,16 +155,13 @@ impl State {
151155
return Some(Target::Output(output.upgrade()?.name()));152156
}153157
let id = *data.get::<WindowId>()?;154
- self.wm155
- .windows156
- .contains_key(&id)157
- .then_some(Target::Window(id))158
+ self.wm.get(id).map(|_| Target::Window(id))158159
}159160
160161
fn target_size(&self, target: &Target) -> Option<(i32, i32)> {161162
let size: (i32, i32) = match target {162163
Target::Output(name) => self.output_named(name)?.1.current_mode()?.size.into(),163
- Target::Window(id) => self.wm.windows.get(id)?.geometry().size.into(),164
+ Target::Window(id) => self.wm.window(*id)?.geometry().size.into(),164165
};165166
(size.0 > 0 && size.1 > 0).then_some(size)166167
}@@ -205,7 +206,13 @@ impl State {
205206
};206207
self.output_elements(o, &output, session.draw_cursor())207208
}208
- Target::Window(id) => window_elements(&mut self.renderer, &self.wm.windows[id]),209
+ Target::Window(id) => match self.wm.window(*id) {210
+ Some(window) => window_elements(&mut self.renderer, window),211
+ None => {212
+ frame.fail(CaptureFailureReason::Stopped);213
+ return;214
+ }215
+ },209216
};210217
let buffer = frame.buffer();211218
let result = match get_dmabuf(&buffer) {@@ -284,14 +291,11 @@ impl State {
284291
match crate::lock::elements(&self.services.lock, &mut self.renderer, output) {285292
Some(locked) => elements.extend(locked),286293
None => elements.extend(crate::scene::build(287
- &self.wm.layout,294
+ &mut self.wm,288295
o,289
- &self.wm.windows,290
- &self.wm.anim,291296
&mut self.renderer,292297
now,293298
&self.settings.config.decoration,294
- &mut self.wm.borders,295299
Some(output),296300
self.services297301
.xwayland@@ -62,14 +62,8 @@ impl State {
6262
match cmd {6363
Command::Spawn { argv } => self.spawn(&argv)?,6464
Command::Close => {65
- if let Some(w) = focused.and_then(|w| self.wm.windows.get(&w)) {66
- match (w.toplevel(), w.x11_surface()) {67
- (Some(t), _) => t.send_close(),68
- (_, Some(x)) => {69
- let _ = x.close();70
- }71
- _ => {}72
- }65
+ if let Some(w) = focused.and_then(|w| self.wm.window(w)) {66
+ crate::wm::close(w);7367
}7468
}7569
Command::Quit => self.core.signal.stop(),@@ -608,15 +608,7 @@ fn render_surface(state: &mut State, crtc: crtc::Handle, target: u64) -> bool {
608608
let State {609609
backend: Backend::Drm(drm),610610
renderer,611
- wm:612
- Wm {613
- layout,614
- windows,615
- anim,616
- borders,617
- space,618
- ..619
- },611
+ wm,620612
settings,621613
core,622614
cursor,@@ -648,7 +640,7 @@ fn render_surface(state: &mut State, crtc: crtc::Handle, target: u64) -> bool {
648640
&& match settings.config.output.vrr {649641
Vrr::Off => false,650642
Vrr::On => true,651
- Vrr::Fullscreen => layout.arrange(surface.id).fullscreen.is_some(),643
+ Vrr::Fullscreen => wm.layout.arrange(surface.id).fullscreen.is_some(),652644
};653645
if want_vrr != surface.vrr_on {654646
match surface.drm.with_compositor(|c| c.use_vrr(want_vrr)) {@@ -668,7 +660,7 @@ fn render_surface(state: &mut State, crtc: crtc::Handle, target: u64) -> bool {
668660
}669661
let began = std::time::Instant::now();670662
let now = target;671
- let Some(area) = layout.output(surface.id).map(|o| o.area) else {663
+ let Some(area) = wm.layout.output(surface.id).map(|o| o.area) else {672664
return false;673665
};674666
let mut elements = Vec::new();@@ -680,21 +672,18 @@ fn render_surface(state: &mut State, crtc: crtc::Handle, target: u64) -> bool {
680672
match crate::lock::elements(lock, renderer, &surface.output) {681673
Some(locked) => elements.extend(locked),682674
None => elements.extend(crate::scene::build(683
- layout,675
+ wm,684676
surface.id,685
- windows,686
- anim,687677
renderer,688678
now,689679
&settings.config.decoration,690
- borders,691680
Some(&surface.output),692681
xwayland.as_ref().map_or(&[], |x| &x.unmanaged),693682
)),694683
}695684
let scene_ns = scene_began.elapsed().as_nanos() as u64;696
- let animating = anim.active(now);697
- anim.prune(now);685
+ let animating = wm.anim.active(now);686
+ wm.anim.prune(now);698687
let render_began = std::time::Instant::now();699688
let cpu_began = thread_cpu_ns();700689
let mut queue_ns = 0;@@ -742,7 +731,7 @@ fn render_surface(state: &mut State, crtc: crtc::Handle, target: u64) -> bool {
742731
crate::lock::send_frames(lock, &output, elapsed);743732
return animating && !surface.queued;744733
}745
- for window in space.elements_for_output(&output) {734
+ for window in wm.space.elements_for_output(&output) {746735
window.send_frame(&output, elapsed, Some(Duration::ZERO), |_, _| {747736
Some(output.clone())748737
});@@ -72,13 +72,14 @@ impl CompositorHandler for State {
7272
while let Some(parent) = get_parent(&root) {7373
root = parent;7474
}75
- if let Some(window) = self76
- .wm77
- .windows78
- .values()79
- .chain(&self.wm.unmapped)80
- .find(|w| crate::state::root(w).as_ref() == Some(&root))81
- {75
+ let mapped = self.wm.id_of(&root).and_then(|id| self.wm.window(id));76
+ let window = mapped.or_else(|| {77
+ self.wm78
+ .unmapped79
+ .iter()80
+ .find(|w| crate::state::root(w).as_ref() == Some(&root))81
+ });82
+ if let Some(window) = window {8283
window.on_commit();8384
}8485
if self.services.captures.wants_damage()@@ -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.wm.windows.get(&id).map(identity).unwrap_or_default();176
+ let (app_id, title) = state.wm.window(id).map(identity).unwrap_or_default();177177
monolith_ipc::Window {178178
id: slotmap::Key::data(&id).as_ffi(),179179
app_id,@@ -8,7 +8,6 @@ mod input;
88
use crate::backend::Backend;99
use crate::state::Services;1010
use crate::state::State;11
-use crate::wm::Wm;1211
use calloop_wayland_source::WaylandSource;1312
use monolith_layout::{OutputId, Rect};1413
use monolith_render_vk::VulkanAllocator;@@ -175,15 +174,7 @@ pub fn render(state: &mut State) {
175174
let State {176175
backend,177176
renderer,178
- wm:179
- Wm {180
- layout,181
- windows,182
- anim,183
- borders,184
- space,185
- ..186
- },177
+ wm,187178
needs_redraw,188179
core,189180
settings,@@ -242,14 +233,11 @@ pub fn render(state: &mut State) {
242233
let o = host.output_id;243234
let elements = crate::lock::elements(lock, renderer, &host.output).unwrap_or_else(|| {244235
crate::scene::build(245
- layout,236
+ wm,246237
o,247
- windows,248
- anim,249238
renderer,250239
now,251240
&settings.config.decoration,252
- borders,253241
Some(&host.output),254242
xwayland.as_ref().map_or(&[], |x| &x.unmanaged),255243
)@@ -270,12 +258,12 @@ pub fn render(state: &mut State) {
270258
}271259
}272260
};273
- *needs_redraw = anim.active(now);274
- anim.prune(now);261
+ *needs_redraw = wm.anim.active(now);262
+ wm.anim.prune(now);275263
lock.shown(&host.output);276264
let elapsed = core.start.elapsed();277265
crate::lock::send_frames(lock, &host.output, elapsed);278
- for window in space.elements().filter(|_| !lock.active()) {266
+ for window in wm.space.elements().filter(|_| !lock.active()) {279267
window.send_frame(&host.output, elapsed, Some(Duration::ZERO), |_, _| {280268
Some(host.output.clone())281269
});@@ -1,9 +1,9 @@
11
//! What a frame draws. The space holds final geometry for input, while the22
//! scene draws windows where the animator says they are right now.33
4
-use crate::anim::Animator;4
+use crate::wm::Wm;55
use monolith_config::Decoration;6
-use monolith_layout::{Layout, OutputId, Rect, WindowId};6
+use monolith_layout::{OutputId, Rect};77
use monolith_render_vk::{Effect, VulkanFrame, VulkanRenderer, VulkanTexture};88
use smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement;99
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};@@ -21,7 +21,6 @@ use smithay::utils::user_data::UserDataMap;
2121
use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform};2222
use smithay::wayland::shell::wlr_layer::Layer;2323
use smithay::xwayland::X11Surface;24
-use std::collections::HashMap;2524
2625
smithay::backend::renderer::element::render_elements! {2726
pub Draw<=VulkanRenderer>;@@ -34,26 +33,18 @@ smithay::backend::renderer::element::render_elements! {
3433
Rescaled=RescaleRenderElement<WaylandSurfaceRenderElement<VulkanRenderer>>,3534
}3635
37
-/// Border strips per window, kept between frames so unchanged borders cause38
-/// no damage.39
-#[derive(Default)]40
-pub struct Borders(HashMap<WindowId, [SolidColorBuffer; 4]>);41
-4236
/// Elements for output `o` in its own coordinates, front to back.43
-#[expect(clippy::too_many_arguments)]4437
#[profiling::function]4538
pub fn build(46
- layout: &Layout,39
+ wm: &mut Wm,4740
o: OutputId,48
- windows: &HashMap<WindowId, Window>,49
- anim: &Animator,5041
renderer: &mut VulkanRenderer,5142
now: u64,5243
deco: &Decoration,53
- borders: &mut Borders,5444
output: Option<&Output>,5545
unmanaged: &[X11Surface],5646
) -> Vec<Draw> {47
+ let (layout, anim, windows) = wm.parts();5748
let Some(area) = layout.output(o).map(|out| out.area) else {5849
return Vec::new();5950
};@@ -90,7 +81,6 @@ pub fn build(
9081
let glide = anim.gliding(w, now);9182
above.push((w, local(glide.unwrap_or(area)), false, glide.is_some()));9283
}93
- borders.0.retain(|id, _| windows.contains_key(id));9484
let focused = layout.focused();9585
let mut out = Vec::new();9686
for x in unmanaged.iter().rev() {@@ -112,11 +102,11 @@ pub fn build(
112102
}113103
}114104
for (id, r, framed, gliding) in tiled.into_iter().chain(above).rev() {115
- let Some(window) = windows.get(&id) else {105
+ let Some(state) = windows.get_mut(id) else {116106
continue;117107
};118108
let effect = anim.effect(id, now);119
- window_elements(&mut out, renderer, window, r, effect, gliding);109
+ window_elements(&mut out, renderer, &state.window, r, effect, gliding);120110
if framed && deco.border > 0 {121111
let color = if focused == Some(id) {122112
deco.focused.0@@ -124,7 +114,8 @@ pub fn build(
124114
deco.unfocused.0125115
};126116
let alpha = effect.map_or(1.0, |e| e.progress);127
- border_elements(&mut out, borders, id, r, deco.border as i32, color, alpha);117
+ let borders = &mut state.borders;118
+ border_elements(&mut out, borders, r, deco.border as i32, color, alpha);128119
}129120
}130121
for (g, r) in ghosts.into_iter().rev() {@@ -242,8 +233,7 @@ fn window_elements(
242233
/// Four strips just outside `r`, in the gap, so the client keeps its size.243234
fn border_elements(244235
out: &mut Vec<Draw>,245
- borders: &mut Borders,246
- id: WindowId,236
+ borders: &mut Option<[SolidColorBuffer; 4]>,247237
r: Rect,248238
b: i32,249239
color: [f32; 4],@@ -259,9 +249,7 @@ fn border_elements(
259249
((x + w, y), (b, h)),260250
];261251
let buffers = borders262
- .0263
- .entry(id)264
- .or_insert_with(|| std::array::from_fn(|_| SolidColorBuffer::new((0, 0), color)));252
+ .get_or_insert_with(|| std::array::from_fn(|_| SolidColorBuffer::new((0, 0), color)));265253
for (buffer, (at, size)) in buffers.iter_mut().zip(strips) {266254
buffer.update(size, color);267255
out.push(Draw::Border(SolidColorRenderElement::from_buffer(@@ -11,7 +11,7 @@ use monolith_render_vk::VulkanTexture;
1111
use smithay::backend::renderer::Renderer;1212
use smithay::backend::renderer::utils::{import_surface_tree, with_renderer_surface_state};1313
use smithay::desktop::utils::under_from_surface_tree;14
-use smithay::desktop::{PopupManager, Space, Window, WindowSurfaceType};14
+use smithay::desktop::{Window, WindowSurfaceType};1515
use smithay::input::keyboard::{Keycode, XkbConfig};1616
use smithay::input::pointer::CursorImageStatus;1717
use smithay::input::{Seat, SeatState};@@ -149,20 +149,7 @@ impl State {
149149
path: config_path,150150
},151151
renderer,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
- },152
+ wm: Wm::new(layout, Animator::new(animation)),166153
backend: crate::backend::Backend::Starting,167154
cursor: crate::cursor::Cursor::load(),168155
cursor_status: CursorImageStatus::default_named(),@@ -258,9 +245,6 @@ impl State {
258245
)259246
});260247
let id = self.wm.layout.add_window(output, tag, float);261
- if rule.tag.is_some() || rule.output.is_some() {262
- self.wm.placed_by_rule.insert(id);263
- }264248
if rule.floating == Some(true) {265249
self.wm.layout.toggle_floating(id);266250
}@@ -270,7 +254,8 @@ impl State {
270254
if let Some(name) = &rule.scratchpad {271255
self.wm.layout.scratchpad_add(id, name);272256
}273
- self.wm.windows.insert(id, window);257
+ self.wm258
+ .insert(id, window, rule.tag.is_some() || rule.output.is_some());274259
self.await_first_buffer(id);275260
if self.wm.layout.visible(id) {276261
self.wm.layout.focus(id);@@ -282,7 +267,6 @@ impl State {
282267
/// spawn animation never plays over an empty slot. Clients that are slow283268
/// to draw get it anyway after a while.284269
fn await_first_buffer(&mut self, id: WindowId) {285
- self.wm.awaiting.insert(id);286270
let timeout = Timer::from_duration(Duration::from_millis(300));287271
let _ = self.core.handle.insert_source(timeout, move |_, _, state| {288272
state.first_buffer(id);@@ -292,7 +276,7 @@ impl State {
292276
293277
/// Called on commits, starts the spawn of a window that now has a buffer.294278
pub fn on_buffer(&mut self, surface: &WlSurface) {295
- if self.wm.awaiting.is_empty() {279
+ if !self.wm.any_awaiting() {296280
return;297281
}298282
let has_buffer =@@ -303,8 +287,11 @@ impl State {
303287
}304288
305289
fn first_buffer(&mut self, id: WindowId) {306
- self.wm.placed_by_rule.remove(&id);307
- if self.wm.awaiting.remove(&id) {290
+ let Some(w) = self.wm.get_mut(id) else {291
+ return;292
+ };293
+ w.placed_by_rule = false;294
+ if std::mem::take(&mut w.awaiting_buffer) {308295
self.wm.anim.spawned(id, now_ns());309296
self.apply_layout();310297
}@@ -320,12 +307,11 @@ impl State {
320307
}321308
322309
pub fn remove_window(&mut self, id: WindowId) {323
- if let Some(window) = self.wm.windows.remove(&id) {324
- self.wm.space.unmap_elem(&window);325
- self.leave_ghost(id, &window);310
+ if let Some(state) = self.wm.remove(id) {311
+ self.wm.space.unmap_elem(&state.window);312
+ self.leave_ghost(id, &state.window);326313
}327314
self.wm.layout.remove_window(id);328
- self.wm.placed_by_rule.remove(&id);329315
self.apply_layout();330316
}331317
@@ -379,11 +365,7 @@ impl State {
379365
}380366
381367
pub fn window_id(&self, surface: &WlSurface) -> Option<WindowId> {382
- self.wm383
- .windows384
- .iter()385
- .find(|(_, w)| root(w).as_ref() == Some(surface))386
- .map(|(id, _)| *id)368
+ self.wm.id_of(surface)387369
}388370
389371
/// Pushes the layout's target geometry into the space and to clients.@@ -412,7 +394,7 @@ impl State {
412394
.tiled413395
.iter()414396
.map(|p| p.window)415
- .filter(|w| self.wm.awaiting.contains(w))397
+ .filter(|&w| self.wm.get(w).is_some_and(|s| s.awaiting_buffer))416398
.collect();417399
let planned = if held.is_empty() {418400
arr.tiled.clone()@@ -426,8 +408,8 @@ impl State {
426408
427409
let mut glides = Vec::new();428410
for &(id, rect, kind, _) in &shown {429
- match self.wm.last_kind.get(&id) {430
- Some(&(was, from, o)) if was != kind => {411
+ match self.wm.get(id).and_then(|s| s.placed) {412
+ Some((was, from, o)) if was != kind => {431413
let from = self432414
.wm433415
.anim@@ -440,7 +422,7 @@ impl State {
440422
.unwrap_or(from);441423
glides.push((id, from, rect));442424
}443
- Some(&(Kind::Floating, from, _)) if from != rect => self.wm.anim.stop_glide(id),425
+ Some((Kind::Floating, from, _)) if from != rect => self.wm.anim.stop_glide(id),444426
_ => {}445427
}446428
}@@ -469,31 +451,39 @@ impl State {
469451
for (id, from, to) in glides {470452
self.wm.anim.glide(id, from, to, now);471453
}472
- self.wm.last_kind = shown.iter().map(|&(id, r, k, o)| (id, (k, r, o))).collect();454
+ for (_, s) in self.wm.iter_mut() {455
+ s.placed = None;456
+ }457
+ for &(id, r, k, o) in &shown {458
+ if let Some(s) = self.wm.get_mut(id) {459
+ s.placed = Some((k, r, o));460
+ }461
+ }473462
474463
let mut mapped = Vec::new();475464
for (id, rect, kind, _) in shown {476
- if let Some(window) = self.wm.windows.get(&id)477
- && configure(window, rect, kind, self.wm.layout.maximized(id))465
+ if let Some(window) = self.wm.window(id).cloned()466
+ && configure(&window, rect, kind, self.wm.layout.maximized(id))478467
{479468
let loc = (rect.x.round() as i32, rect.y.round() as i32);480469
let loc = Point::from(loc) - window.geometry().loc;481
- self.wm.space.map_element(window.clone(), loc, false);470
+ self.wm.space.map_element(window, loc, false);482471
mapped.push(id);483472
}484473
}485474
let hidden: Vec<Window> = self486475
.wm487
- .windows488476
.iter()489477
.filter(|(id, _)| !mapped.contains(id))490
- .map(|(_, w)| w.clone())478
+ .map(|(_, w)| w.window.clone())491479
.collect();492480
for window in hidden {493481
self.wm.space.unmap_elem(&window);494482
}495
- for id in &mapped {496
- self.wm.space.raise_element(&self.wm.windows[id], false);483
+ for id in mapped {484
+ if let Some(window) = self.wm.window(id).cloned() {485
+ self.wm.space.raise_element(&window, false);486
+ }497487
}498488
self.sync_focus();499489
self.update_inhibit();@@ -502,8 +492,9 @@ impl State {
502492
503493
pub fn sync_focus(&mut self) {504494
let focused = self.wm.layout.focused();505
- for (id, window) in &self.wm.windows {506
- if window.set_activated(Some(*id) == focused)495
+ for (id, s) in self.wm.iter() {496
+ let window = &s.window;497
+ if window.set_activated(Some(id) == focused)507498
&& let Some(t) = window.toplevel()508499
&& t.is_initial_configure_sent()509500
{@@ -514,7 +505,7 @@ impl State {
514505
self.lock_keyboard_focus().map(Focus::from)515506
} else {516507
self.layer_keyboard_focus().map(Focus::from).or_else(|| {517
- let w = focused.and_then(|id| self.wm.windows.get(&id))?;508
+ let w = focused.and_then(|id| self.wm.window(id))?;518509
match w.x11_surface() {519510
Some(x) => Some(Focus::X11(Box::new(x.clone()))),520511
None => w.toplevel().map(|t| Focus::from(t.wl_surface().clone())),@@ -7,21 +7,8 @@ use monolith_protocols::toplevel::{
77
Action, ForeignToplevelHandler, ForeignToplevelState, Snapshot, Toplevel,88
};99
use smithay::wayland::foreign_toplevel_list::{10
- ForeignToplevelHandle, ForeignToplevelListHandler, ForeignToplevelListState,10
+ ForeignToplevelListHandler, ForeignToplevelListState,1111
};12
-use std::collections::HashMap;13
-14
-#[derive(Default)]15
-pub struct ListHandles(HashMap<WindowId, ForeignToplevelHandle>);16
-17
-impl ListHandles {18
- pub fn window(&self, toplevel: &ForeignToplevelHandle) -> Option<WindowId> {19
- self.020
- .iter()21
- .find(|(_, h)| h.identifier() == toplevel.identifier())22
- .map(|(id, _)| *id)23
- }24
-}2512
2613
pub fn key(id: WindowId) -> u64 {2714
slotmap::Key::data(&id).as_ffi()@@ -39,7 +26,7 @@ impl ForeignToplevelHandler for State {
3926
}4027
4128
fn foreign_toplevel_action(&mut self, action: Action) {42
- let id_of = |k: u64| self.wm.windows.keys().copied().find(|&id| key(id) == k);29
+ let id_of = |k: u64| self.wm.iter().map(|(id, _)| id).find(|&id| key(id) == k);4330
match action {4431
Action::Activate(k) => {4532
let Some(id) = id_of(k) else { return };@@ -54,15 +41,8 @@ impl ForeignToplevelHandler for State {
5441
self.wm.layout.focus(id);5542
}5643
Action::Close(k) => {57
- let Some(window) = id_of(k).and_then(|id| self.wm.windows.get(&id)) else {58
- return;59
- };60
- match (window.toplevel(), window.x11_surface()) {61
- (Some(t), _) => t.send_close(),62
- (_, Some(x)) => {63
- let _ = x.close();64
- }65
- _ => {}44
+ if let Some(window) = id_of(k).and_then(|id| self.wm.window(id)) {45
+ crate::wm::close(window);6646
}6747
return;6848
}@@ -92,17 +72,10 @@ impl ForeignToplevelHandler for State {
9272
impl State {9373
/// Brings both window lists in line with the windows.9474
pub fn publish_toplevels(&mut self) {95
- let mut handles = std::mem::take(&mut self.wm.list_handles.0);96
- handles.retain(|id, h| {97
- let keep = self.wm.windows.contains_key(id);98
- if !keep {99
- self.protocols.toplevel_list.remove_toplevel(h);100
- }101
- keep102
- });103
- for (id, window) in &self.wm.windows {104
- let (app_id, title) = identity(window);105
- match handles.get(id) {75
+ let list = &mut self.protocols.toplevel_list;76
+ for (_, w) in self.wm.iter_mut() {77
+ let (app_id, title) = identity(&w.window);78
+ match &w.list_handle {10679
Some(h) => {10780
if h.title() != title || h.app_id() != app_id {10881
h.send_title(&title);@@ -110,16 +83,10 @@ impl State {
11083
h.send_done();11184
}11285
}113
- None => {114
- let h = self115
- .protocols116
- .toplevel_list117
- .new_toplevel::<State>(title, app_id);118
- handles.insert(*id, h);119
- }86
+ None => w.list_handle = Some(list.new_toplevel::<State>(title, app_id)),12087
}12188
}122
- self.wm.list_handles.0 = handles;89
+ list.cleanup_closed_handles();12390
12491
if !self.protocols.foreign_toplevels.has_clients() {12592
return;@@ -127,10 +94,9 @@ impl State {
12794
let focused = self.wm.layout.focused();12895
let toplevels = self12996
.wm130
- .windows13197
.iter()132
- .map(|(&id, window)| {133
- let (app_id, title) = identity(window);98
+ .map(|(id, w)| {99
+ let (app_id, title) = identity(&w.window);134100
let w = self.wm.layout.window(id);135101
Toplevel {136102
key: key(id),@@ -1,29 +1,135 @@
1
-//! The window model: the layout and what monolith keeps per window beside it.1
+//! The window model: the layout, and one record per window for everything2
+//! monolith keeps beside it.23
34
use crate::anim::Animator;4
-use crate::state::Kind;5
+use crate::state::{Kind, root};56
use monolith_layout::{Layout, OutputId, Rect, WindowId};7
+use slotmap::SecondaryMap;8
+use smithay::backend::renderer::element::solid::SolidColorBuffer;69
use smithay::desktop::{PopupManager, Space, Window};710
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;8
-use std::collections::{HashMap, HashSet};11
+use smithay::wayland::foreign_toplevel_list::ForeignToplevelHandle;12
+use std::collections::HashMap;913
1014
pub struct Wm {1115
pub layout: Layout,12
- pub windows: HashMap<WindowId, Window>,16
+ windows: SecondaryMap<WindowId, WindowState>,17
+ by_surface: HashMap<WlSurface, WindowId>,1318
/// Toplevels that have not committed yet. Rules need their app_id and1419
/// title, which clients set after creating the toplevel.1520
pub unmapped: Vec<Window>,1621
pub space: Space<Window>,1722
pub popups: PopupManager,1823
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,2724
/// A layer surface given the keyboard by a click, like a launcher.2825
pub layer_focus: Option<WlSurface>,2926
}27
+28
+pub struct WindowState {29
+ pub window: Window,30
+ /// How the last layout pass placed it, to spot switches between tiled,31
+ /// floating and fullscreen.32
+ pub placed: Option<(Kind, Rect, OutputId)>,33
+ /// Held on its seed until its first buffer lands.34
+ pub awaiting_buffer: bool,35
+ /// A rule sent it somewhere, so activation tokens leave it alone.36
+ pub placed_by_rule: bool,37
+ pub list_handle: Option<ForeignToplevelHandle>,38
+ pub borders: Option<[SolidColorBuffer; 4]>,39
+}40
+41
+/// Asks a window to close, through xdg-shell or X11.42
+pub fn close(window: &Window) {43
+ match (window.toplevel(), window.x11_surface()) {44
+ (Some(t), _) => t.send_close(),45
+ (_, Some(x)) => {46
+ if let Err(e) = x.close() {47
+ tracing::debug!("closing an X11 window failed: {e}");48
+ }49
+ }50
+ _ => {}51
+ }52
+}53
+54
+impl Wm {55
+ pub fn new(layout: Layout, anim: Animator) -> Self {56
+ Self {57
+ layout,58
+ windows: SecondaryMap::new(),59
+ by_surface: HashMap::new(),60
+ unmapped: Vec::new(),61
+ space: Space::default(),62
+ popups: PopupManager::default(),63
+ anim,64
+ layer_focus: None,65
+ }66
+ }67
+68
+ pub fn insert(&mut self, id: WindowId, window: Window, placed_by_rule: bool) {69
+ self.windows.insert(70
+ id,71
+ WindowState {72
+ window,73
+ placed: None,74
+ awaiting_buffer: true,75
+ placed_by_rule,76
+ list_handle: None,77
+ borders: None,78
+ },79
+ );80
+ self.index(id);81
+ }82
+83
+ /// Drops every trace of a window. The layout and space are the caller's.84
+ pub fn remove(&mut self, id: WindowId) -> Option<WindowState> {85
+ let state = self.windows.remove(id)?;86
+ self.by_surface.retain(|_, w| *w != id);87
+ if let Some(h) = &state.list_handle {88
+ h.send_closed();89
+ }90
+ self.anim.forget(id);91
+ Some(state)92
+ }93
+94
+ /// Indexes a window by its surface. X11 windows only have one once95
+ /// xwayland-shell associates it, which may be after they are mapped.96
+ pub fn index(&mut self, id: WindowId) {97
+ if let Some(surface) = self.windows.get(id).and_then(|w| root(&w.window)) {98
+ self.by_surface.insert(surface, id);99
+ }100
+ }101
+102
+ pub fn id_of(&self, surface: &WlSurface) -> Option<WindowId> {103
+ self.by_surface.get(surface).copied()104
+ }105
+106
+ pub fn get(&self, id: WindowId) -> Option<&WindowState> {107
+ self.windows.get(id)108
+ }109
+110
+ pub fn get_mut(&mut self, id: WindowId) -> Option<&mut WindowState> {111
+ self.windows.get_mut(id)112
+ }113
+114
+ pub fn window(&self, id: WindowId) -> Option<&Window> {115
+ self.windows.get(id).map(|w| &w.window)116
+ }117
+118
+ pub fn iter(&self) -> impl Iterator<Item = (WindowId, &WindowState)> {119
+ self.windows.iter()120
+ }121
+122
+ pub fn iter_mut(&mut self) -> impl Iterator<Item = (WindowId, &mut WindowState)> {123
+ self.windows.iter_mut()124
+ }125
+126
+ /// The layout and animator to read alongside the records to write, which127
+ /// a frame needs at once.128
+ pub fn parts(&mut self) -> (&Layout, &Animator, &mut SecondaryMap<WindowId, WindowState>) {129
+ (&self.layout, &self.anim, &mut self.windows)130
+ }131
+132
+ pub fn any_awaiting(&self) -> bool {133
+ self.windows.values().any(|w| w.awaiting_buffer)134
+ }135
+}@@ -217,7 +217,10 @@ impl XWaylandShellHandler for State {
217217
&mut self.protocols.xwayland_shell_state218218
}219219
220
- fn surface_associated(&mut self, _xwm: XwmId, surface: WlSurface, _window: X11Surface) {220
+ fn surface_associated(&mut self, _xwm: XwmId, surface: WlSurface, window: X11Surface) {221
+ if let Some(id) = self.x11_window_id(&window) {222
+ self.wm.index(id);223
+ }221224
self.on_buffer(&surface);222225
self.needs_redraw = true;223226
}@@ -321,10 +324,9 @@ impl XwmHandler for State {
321324
tracing::info!("Xwayland exited");322325
let gone: Vec<_> = self323326
.wm324
- .windows325327
.iter()326
- .filter(|(_, w)| w.x11_surface().is_some())327
- .map(|(id, _)| *id)328
+ .filter(|(_, w)| w.window.x11_surface().is_some())329
+ .map(|(id, _)| id)328330
.collect();329331
for id in gone {330332
self.remove_window(id);@@ -359,10 +361,9 @@ fn floats(window: &X11Surface) -> bool {
359361
impl State {360362
pub fn x11_window_id(&self, window: &X11Surface) -> Option<monolith_layout::WindowId> {361363
self.wm362
- .windows363364
.iter()364
- .find(|(_, w)| w.x11_surface() == Some(window))365
- .map(|(id, _)| *id)365
+ .find(|(_, w)| w.window.x11_surface() == Some(window))366
+ .map(|(id, _)| id)366367
}367368
368369
fn forget_x11(&mut self, window: &X11Surface) {