Skip to content

Commit 1118aa2

Browse files
ids1024Drakulix
authored andcommitted
When removing output global, use disable_global, remove with timer
This should fix an issue where output hotplug can sometimes cause clients (including XWayland) to crash with a protocol error trying to bind the output. Using a timer doesn't seem ideal, but seems to be the correct way to do this at present. Wlroots `wlr_global_destroy_safe` is basically the same as this. Adding a `LoopHandle` argument to `OutputConfigurationState::new` seems awkward, but maybe better than a handler method for removing globals. (`IdleNotifierState::new` also takes a `LoopHandle`). Perhaps Smithay could provide some kind of helper for this.
1 parent 7ac204e commit 1118aa2

File tree

2 files changed

+40
-5
lines changed

2 files changed

+40
-5
lines changed

src/state.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,8 @@ impl State {
499499
let fractional_scale_state = FractionalScaleManagerState::new::<State>(dh);
500500
let keyboard_shortcuts_inhibit_state = KeyboardShortcutsInhibitState::new::<Self>(dh);
501501
let output_state = OutputManagerState::new_with_xdg_output::<Self>(dh);
502-
let output_configuration_state = OutputConfigurationState::new(dh, client_is_privileged);
502+
let output_configuration_state =
503+
OutputConfigurationState::new(dh, handle.clone(), client_is_privileged);
503504
let output_power_state = OutputPowerState::new::<Self, _>(dh, client_is_privileged);
504505
let overlap_notify_state =
505506
OverlapNotifyState::new::<Self, _>(dh, client_has_no_security_context);

src/wayland/protocols/output_configuration/mod.rs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
// SPDX-License-Identifier: GPL-3.0-only
22

3+
use calloop::{
4+
timer::{TimeoutAction, Timer},
5+
LoopHandle,
6+
};
37
use cosmic_protocols::output_management::v1::server::{
48
zcosmic_output_configuration_head_v1::ZcosmicOutputConfigurationHeadV1,
59
zcosmic_output_configuration_v1::ZcosmicOutputConfigurationV1,
@@ -25,7 +29,7 @@ use smithay::{
2529
utils::{Logical, Physical, Point, Size, Transform},
2630
wayland::output::WlOutputData,
2731
};
28-
use std::{convert::TryFrom, sync::Mutex};
32+
use std::{convert::TryFrom, sync::Mutex, time::Duration};
2933

3034
mod handlers;
3135

@@ -45,6 +49,7 @@ pub struct OutputConfigurationState<D> {
4549
global: GlobalId,
4650
extension_global: GlobalId,
4751
dh: DisplayHandle,
52+
event_loop_handle: LoopHandle<'static, D>,
4853
_dispatch: std::marker::PhantomData<D>,
4954
}
5055

@@ -168,7 +173,11 @@ where
168173
+ OutputConfigurationHandler
169174
+ 'static,
170175
{
171-
pub fn new<F>(dh: &DisplayHandle, client_filter: F) -> OutputConfigurationState<D>
176+
pub fn new<F>(
177+
dh: &DisplayHandle,
178+
event_loop_handle: LoopHandle<'static, D>,
179+
client_filter: F,
180+
) -> OutputConfigurationState<D>
172181
where
173182
F: for<'a> Fn(&'a Client) -> bool + Clone + Send + Sync + 'static,
174183
{
@@ -194,6 +203,7 @@ where
194203
global,
195204
extension_global,
196205
dh: dh.clone(),
206+
event_loop_handle: event_loop_handle.clone(),
197207
_dispatch: std::marker::PhantomData,
198208
}
199209
}
@@ -240,7 +250,7 @@ where
240250
let mut inner = inner.lock().unwrap();
241251
inner.enabled = false;
242252
if let Some(global) = inner.global.take() {
243-
self.dh.remove_global::<D>(global);
253+
remove_global_with_timer(&self.dh, &self.event_loop_handle, global);
244254
}
245255
}
246256
}
@@ -292,7 +302,11 @@ where
292302
inner.global = Some(output.create_global::<D>(&self.dh));
293303
}
294304
if !inner.enabled && inner.global.is_some() {
295-
self.dh.remove_global::<D>(inner.global.take().unwrap());
305+
remove_global_with_timer(
306+
&self.dh,
307+
&self.event_loop_handle,
308+
inner.global.take().unwrap(),
309+
);
296310
}
297311
}
298312
for manager in self.instances.iter_mut() {
@@ -493,6 +507,26 @@ where
493507
}
494508
}
495509

510+
fn remove_global_with_timer<D: 'static>(
511+
dh: &DisplayHandle,
512+
event_loop_handle: &LoopHandle<D>,
513+
id: GlobalId,
514+
) {
515+
dh.disable_global::<D>(id.clone());
516+
let source = Timer::from_duration(Duration::from_secs(5));
517+
let dh = dh.clone();
518+
let res = event_loop_handle.insert_source(source, move |_, _, _state| {
519+
dh.remove_global::<D>(id.clone());
520+
TimeoutAction::Drop
521+
});
522+
if let Err(err) = res {
523+
tracing::error!(
524+
"failed to insert timer source to destroy output global: {}",
525+
err
526+
);
527+
}
528+
}
529+
496530
macro_rules! delegate_output_configuration {
497531
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
498532
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [

0 commit comments

Comments
 (0)