toplevel-info: Fix race between binding wl_ouput and output_enter
This fixes an issue with `cosmic-panel` where, when a workspace is moved back to an output after a monitor is disconnected and reconnected, the panel doesn't hide because `cosmic-panel` thinks no toplevel is open on that monitor. After some testing, it seems `output_enter` isn't being sent here. In particular, the `output_leave` call happens before the client binds the `wl_output`, so there is no `wl_output` to send in an event yet. This is addressed by keeping track of a set of `wl_output`s that we have sent the event to. So if an output is bound, `refresh` can add it to this list and send the event. This is not needed for workspaces (though it could be done similarly) since the handle objects are created by server events. So no race should occur as long as the workspaces global is bound before the toplevel info one.
This commit is contained in:
parent
a2f9340b02
commit
c1bf410466
1 changed files with 26 additions and 22 deletions
|
|
@ -1,12 +1,12 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::sync::Mutex;
|
||||
use std::{collections::HashSet, sync::Mutex};
|
||||
|
||||
use smithay::{
|
||||
output::Output,
|
||||
reexports::wayland_server::{
|
||||
backend::{ClientId, GlobalId},
|
||||
protocol::wl_surface::WlSurface,
|
||||
protocol::{wl_output::WlOutput, wl_surface::WlSurface},
|
||||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak,
|
||||
},
|
||||
utils::{user_data::UserDataMap, IsAlive, Logical, Rectangle},
|
||||
|
|
@ -59,6 +59,7 @@ pub(super) type ToplevelState = Mutex<ToplevelStateInner>;
|
|||
|
||||
pub struct ToplevelHandleStateInner<W: Window> {
|
||||
outputs: Vec<Output>,
|
||||
wl_outputs: HashSet<WlOutput>,
|
||||
workspaces: Vec<WorkspaceHandle>,
|
||||
title: String,
|
||||
app_id: String,
|
||||
|
|
@ -71,6 +72,7 @@ impl<W: Window> ToplevelHandleStateInner<W> {
|
|||
fn from_window(window: &W) -> ToplevelHandleState<W> {
|
||||
ToplevelHandleState::new(ToplevelHandleStateInner {
|
||||
outputs: Vec::new(),
|
||||
wl_outputs: HashSet::new(),
|
||||
workspaces: Vec::new(),
|
||||
title: String::new(),
|
||||
app_id: String::new(),
|
||||
|
|
@ -399,27 +401,29 @@ fn send_toplevel_to_client<D, W: 'static>(
|
|||
}
|
||||
|
||||
if let Ok(client) = dh.get_client(instance.id()) {
|
||||
for new_output in state
|
||||
.outputs
|
||||
.iter()
|
||||
.filter(|o| !handle_state.outputs.contains(o))
|
||||
{
|
||||
for wl_output in new_output.client_outputs(&client) {
|
||||
instance.output_enter(&wl_output);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
for old_output in handle_state
|
||||
.outputs
|
||||
.iter()
|
||||
.filter(|o| !state.outputs.contains(o))
|
||||
{
|
||||
for wl_output in old_output.client_outputs(&client) {
|
||||
instance.output_leave(&wl_output);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
handle_state.outputs = state.outputs.clone();
|
||||
|
||||
let handle_state = &mut *handle_state;
|
||||
for output in &handle_state.outputs {
|
||||
for wl_output in output.client_outputs(&client) {
|
||||
if handle_state.wl_outputs.insert(wl_output.clone()) {
|
||||
instance.output_enter(&wl_output);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
handle_state.wl_outputs.retain(|wl_output| {
|
||||
let retain = wl_output.is_alive()
|
||||
&& handle_state
|
||||
.outputs
|
||||
.iter()
|
||||
.any(|output| output.owns(wl_output));
|
||||
if !retain {
|
||||
instance.output_leave(&wl_output);
|
||||
changed = true;
|
||||
}
|
||||
retain
|
||||
});
|
||||
}
|
||||
|
||||
for new_workspace in state
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue