Commit 272bdf93aead
tarball6 files changed+71-21
@@ -23,6 +23,7 @@ use smithay::wayland::drm_syncobj::{DrmSyncobjCachedState, DrmSyncobjHandler, Dr
2323
use smithay::wayland::dmabuf::{2424
DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier, get_dmabuf,2525
};26
+use smithay::output::Output;2627
use smithay::wayland::output::OutputHandler;2728
use smithay::wayland::seat::WaylandFocus;2829
use smithay::wayland::selection::SelectionHandler;@@ -82,10 +83,11 @@ impl CompositorHandler for State {
8283
if let Some(window) = window {8384
window.on_commit();8485
}85
- if self.services.captures.wants_damage()86
- && let Some(id) = self.window_id(&root)87
- {88
- self.services.captures.window_damaged(id);86
+ if let Some(id) = self.window_id(&root) {87
+ self.refresh_identity(id);88
+ if self.services.captures.wants_damage() {89
+ self.services.captures.window_damaged(id);90
+ }8991
}9092
}9193
if let Some(i) = self@@ -254,7 +256,13 @@ impl WaylandDndGrabHandler for State {
254256
}255257
}256258
257
-impl OutputHandler for State {}259
+impl OutputHandler for State {260
+ /// Workspace and toplevel lists name outputs by the client's wl_output,261
+ /// so a newly bound one needs announcing.262
+ fn output_bound(&mut self, _output: Output, _wl_output: WlOutput) {263
+ self.dirty.snapshot = true;264
+ }265
+}258266
259267
impl smithay::wayland::pointer_constraints::PointerConstraintsHandler for State {260268
fn new_constraint(@@ -2,7 +2,6 @@
22
//! subcommands that talk to it.33
44
use crate::state::State;5
-use crate::wm::identity;65
use monolith_ipc::{Answer, Envelope, Query, Reply, Request, VERSION};76
use smithay::reexports::calloop::generic::Generic;87
use smithay::reexports::calloop::{EventLoop, Interest, LoopHandle, Mode, PostAction};@@ -174,7 +173,11 @@ fn snapshot(state: &State) -> monolith_ipc::State {
174173
let mut windows: Vec<monolith_ipc::Window> = l175174
.windows()176175
.map(|(id, w)| {177
- let (app_id, title) = state.wm.window(id).map(identity).unwrap_or_default();176
+ let (app_id, title) = state177
+ .wm178
+ .get(id)179
+ .map(|w| (w.app_id.clone(), w.title.clone()))180
+ .unwrap_or_default();178181
monolith_ipc::Window {179182
id: slotmap::Key::data(&id).as_ffi(),180183
app_id,@@ -63,6 +63,8 @@ pub struct Dirty {
6363
pub redraw: bool,6464
/// The pointer moved, which on its own only redraws the cursor.6565
pub cursor: bool,66
+ /// Something clients outside see changed: windows, titles, tags, focus.67
+ pub snapshot: bool,6668
}6769
6870
/// The event loop and Wayland display, which everything uses and nothing owns.@@ -171,6 +173,7 @@ impl State {
171173
dirty: Dirty {172174
redraw: true,173175
cursor: false,176
+ snapshot: true,174177
},175178
services: Services {176179
lock: Default::default(),@@ -196,9 +199,11 @@ impl State {
196199
self.wm.anim.prune(now_ns());197200
self.confirm_lock();198201
self.complete_captures();199
- crate::ipc::broadcast(self);200
- self.publish_workspaces();201
- self.publish_toplevels();202
+ if std::mem::take(&mut self.dirty.snapshot) {203
+ crate::ipc::broadcast(self);204
+ self.publish_workspaces();205
+ self.publish_toplevels();206
+ }202207
let _ = self.core.display.flush_clients();203208
}204209
@@ -266,8 +271,8 @@ impl State {
266271
if let Some(name) = &rule.scratchpad {267272
self.wm.layout.scratchpad_add(id, name);268273
}269
- self.wm270
- .insert(id, window, rule.tag.is_some() || rule.output.is_some());274
+ let by_rule = rule.tag.is_some() || rule.output.is_some();275
+ self.wm.insert(id, window, by_rule, app_id, title);271276
self.await_first_buffer(id);272277
if self.wm.layout.visible(id) {273278
self.wm.layout.focus(id);@@ -376,6 +381,19 @@ impl State {
376381
}377382
}378383
384
+ /// Picks up a changed title or app id, which clients send at any time.385
+ pub fn refresh_identity(&mut self, id: WindowId) {386
+ let Some(w) = self.wm.get_mut(id) else {387
+ return;388
+ };389
+ let (app_id, title) = identity(&w.window);390
+ if w.app_id != app_id || w.title != title {391
+ w.app_id = app_id;392
+ w.title = title;393
+ self.dirty.snapshot = true;394
+ }395
+ }396
+379397
pub fn window_id(&self, surface: &WlSurface) -> Option<WindowId> {380398
self.wm.id_of(surface)381399
}@@ -500,6 +518,7 @@ impl State {
500518
self.sync_focus();501519
self.update_inhibit();502520
self.dirty.redraw = true;521
+ self.dirty.snapshot = true;503522
}504523
505524
pub fn sync_focus(&mut self) {@@ -2,7 +2,6 @@
22
//! and wlr-foreign-toplevel-management for taskbars that also act on them.33
44
use crate::state::State;5
-use crate::wm::identity;65
use monolith_layout::WindowId;76
use monolith_protocols::toplevel::{87
Action, ForeignToplevelHandler, ForeignToplevelState, Snapshot, Toplevel,@@ -75,16 +74,18 @@ impl State {
7574
pub fn publish_toplevels(&mut self) {7675
let list = &mut self.protocols.toplevel_list;7776
for (_, w) in self.wm.iter_mut() {78
- let (app_id, title) = identity(&w.window);7977
match &w.list_handle {8078
Some(h) => {81
- if h.title() != title || h.app_id() != app_id {82
- h.send_title(&title);83
- h.send_app_id(&app_id);79
+ if h.title() != w.title || h.app_id() != w.app_id {80
+ h.send_title(&w.title);81
+ h.send_app_id(&w.app_id);8482
h.send_done();8583
}8684
}87
- None => w.list_handle = Some(list.new_toplevel::<State>(title, app_id)),85
+ None => {86
+ let h = list.new_toplevel::<State>(w.title.clone(), w.app_id.clone());87
+ w.list_handle = Some(h);88
+ }8889
}8990
}9091
list.cleanup_closed_handles();@@ -97,7 +98,7 @@ impl State {
9798
.wm9899
.iter()99100
.map(|(id, w)| {100
- let (app_id, title) = identity(&w.window);101
+ let (app_id, title) = (w.app_id.clone(), w.title.clone());101102
let w = self.wm.layout.window(id);102103
Toplevel {103104
key: key(id),@@ -41,6 +41,8 @@ pub struct WindowState {
4141
pub placed_by_rule: bool,4242
pub list_handle: Option<ForeignToplevelHandle>,4343
pub borders: Option<[SolidColorBuffer; 4]>,44
+ pub app_id: String,45
+ pub title: String,4446
}4547
4648
/// Asks a window to close, through xdg-shell or X11.@@ -70,7 +72,14 @@ impl Wm {
7072
}7173
}7274
73
- pub fn insert(&mut self, id: WindowId, window: Window, placed_by_rule: bool) {75
+ pub fn insert(76
+ &mut self,77
+ id: WindowId,78
+ window: Window,79
+ placed_by_rule: bool,80
+ app_id: String,81
+ title: String,82
+ ) {7483
self.windows.insert(7584
id,7685
WindowState {@@ -80,6 +89,8 @@ impl Wm {
8089
placed_by_rule,8190
list_handle: None,8291
borders: None,92
+ app_id,93
+ title,8394
},8495
);8596
self.index(id);@@ -11,7 +11,7 @@ use smithay::reexports::rustix::io::{FdFlags, fcntl_getfd, fcntl_setfd};
1111
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;1212
use smithay::utils::{Logical, Rectangle};1313
use smithay::wayland::xwayland_shell::{XWaylandShellHandler, XWaylandShellState};14
-use smithay::xwayland::xwm::{Reorder, ResizeEdge, WmWindowType, XwmId};14
+use smithay::xwayland::xwm::{Reorder, ResizeEdge, WmWindowProperty, WmWindowType, XwmId};1515
use smithay::xwayland::{X11Surface, X11Wm, XWayland, XWaylandEvent, XwmHandler};1616
use std::os::fd::AsFd;1717
use std::os::linux::net::SocketAddrExt;@@ -320,6 +320,14 @@ impl XwmHandler for State {
320320
self.x11_fullscreen(&window, false);321321
}322322
323
+ fn property_notify(&mut self, _xwm: XwmId, window: X11Surface, property: WmWindowProperty) {324
+ if matches!(property, WmWindowProperty::Title | WmWindowProperty::Class)325
+ && let Some(id) = self.x11_window_id(&window)326
+ {327
+ self.refresh_identity(id);328
+ }329
+ }330
+323331
fn disconnected(&mut self, _xwm: XwmId) {324332
tracing::info!("Xwayland exited");325333
let gone: Vec<_> = self