From 92873b06ed71232457ef5a9107cfb01733eddb25 Mon Sep 17 00:00:00 2001 From: Andreas Johansson <ndreas@users.noreply.github.com> Date: Tue, 20 Nov 2018 21:21:58 +0100 Subject: [PATCH] Handle removed wl_outputs (#719) * Move the event managent to the closure In preparation of more events not relating to the SeatManager being captured. * Handle wl_output remove events In some cases, wl_outputs can be removed without the compositor notifying the surfaces using leave/enter events. This breaks the DPI and resize stuff since the windows' list of monitors were not updated. Now, wl_output removals are handled and windows are updated accordingly. * Add changelog entry for disappearing wl_outputs * Clearer changelog message for wl_output removal changes --- CHANGELOG.md | 1 + src/platform/linux/wayland/event_loop.rs | 84 +++++++++++++----------- src/platform/linux/wayland/window.rs | 21 +++++- 3 files changed, 66 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f23ee23..0fd80b8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - On X11, fixed a segfault when using virtual monitors with XRandR. - Derive `Ord` and `PartialOrd` for `VirtualKeyCode` enum. - On Windows, fix issue where hovering or dropping a non file item would create a panic. +- On Wayland, fix resizing and DPI calculation when a `wl_output` is removed without sending a `leave` event to the `wl_surface`, such as disconnecting a monitor from a laptop. # Version 0.18.0 (2018-11-07) diff --git a/src/platform/linux/wayland/event_loop.rs b/src/platform/linux/wayland/event_loop.rs index edf35f07..3f9ac7d2 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform/linux/wayland/event_loop.rs @@ -123,11 +123,26 @@ impl EventsLoop { }, }; + let cb_store = store.clone(); + let env = Environment::from_display_with_cb( &display, &mut event_queue, move |event, registry| { - seat_manager.receive(event, registry) + match event { + GlobalEvent::New { id, ref interface, version } => { + if interface == "wl_seat" { + seat_manager.add_seat(id, version, registry) + } + }, + GlobalEvent::Removed { id, ref interface } => { + if interface == "wl_seat" { + seat_manager.remove_seat(id) + } else if interface == "wl_output" { + cb_store.lock().unwrap().remove_output(id); + } + }, + } }, ).unwrap(); @@ -286,47 +301,38 @@ struct SeatManager { } impl SeatManager { - fn receive(&mut self, evt: GlobalEvent, registry: Proxy<wl_registry::WlRegistry>) { + fn add_seat(&mut self, id: u32, version: u32, registry: Proxy<wl_registry::WlRegistry>) { use self::wl_registry::RequestsTrait as RegistryRequests; - use self::wl_seat::RequestsTrait as SeatRequests; - match evt { - GlobalEvent::New { - id, - ref interface, - version, - } if interface == "wl_seat" => - { - use std::cmp::min; + use std::cmp::min; - let mut seat_data = SeatData { - sink: self.sink.clone(), - store: self.store.clone(), - pointer: None, - keyboard: None, - touch: None, - events_loop_proxy: self.events_loop_proxy.clone(), - modifiers_tracker: Arc::new(Mutex::new(ModifiersState::default())), - }; - let seat = registry - .bind(min(version, 5), id, move |seat| { - seat.implement(move |event, seat| { - seat_data.receive(event, seat) - }, ()) - }) - .unwrap(); - self.store.lock().unwrap().new_seat(&seat); - self.seats.lock().unwrap().push((id, seat)); + let mut seat_data = SeatData { + sink: self.sink.clone(), + store: self.store.clone(), + pointer: None, + keyboard: None, + touch: None, + events_loop_proxy: self.events_loop_proxy.clone(), + modifiers_tracker: Arc::new(Mutex::new(ModifiersState::default())), + }; + let seat = registry + .bind(min(version, 5), id, move |seat| { + seat.implement(move |event, seat| { + seat_data.receive(event, seat) + }, ()) + }) + .unwrap(); + self.store.lock().unwrap().new_seat(&seat); + self.seats.lock().unwrap().push((id, seat)); + } + + fn remove_seat(&mut self, id: u32) { + use self::wl_seat::RequestsTrait as SeatRequests; + let mut seats = self.seats.lock().unwrap(); + if let Some(idx) = seats.iter().position(|&(i, _)| i == id) { + let (_, seat) = seats.swap_remove(idx); + if seat.version() >= 5 { + seat.release(); } - GlobalEvent::Removed { id, ref interface } if interface == "wl_seat" => { - let mut seats = self.seats.lock().unwrap(); - if let Some(idx) = seats.iter().position(|&(i, _)| i == id) { - let (_, seat) = seats.swap_remove(idx); - if seat.version() >= 5 { - seat.release(); - } - } - } - _ => (), } } } diff --git a/src/platform/linux/wayland/window.rs b/src/platform/linux/wayland/window.rs index b7d5d6d9..01c6de12 100644 --- a/src/platform/linux/wayland/window.rs +++ b/src/platform/linux/wayland/window.rs @@ -148,6 +148,7 @@ impl Window { frame: Arc::downgrade(&frame), current_dpi: 1, new_dpi: None, + monitors: monitor_list.clone(), }); evlp.evq.borrow_mut().sync_roundtrip().unwrap(); @@ -330,7 +331,8 @@ struct InternalWindow { kill_switch: Arc<Mutex<bool>>, frame: Weak<Mutex<SWindow<ConceptFrame>>>, current_dpi: i32, - new_dpi: Option<i32> + new_dpi: Option<i32>, + monitors: Arc<Mutex<MonitorList>> } pub struct WindowStore { @@ -376,6 +378,18 @@ impl WindowStore { } } + pub fn remove_output(&mut self, output: u32) { + for window in &mut self.windows { + let dpi = window.monitors.lock().unwrap().output_disappeared(output); + if window.surface.version() >= 3 { + // without version 3 we can't be dpi aware + window.new_dpi = Some(dpi); + window.surface.set_buffer_scale(dpi); + window.need_refresh = true; + } + } + } + fn dpi_change(&mut self, surface: &Proxy<wl_surface::WlSurface>, new: i32) { for window in &mut self.windows { if surface.equals(&window.surface) { @@ -456,4 +470,9 @@ impl MonitorList { None } } + + fn output_disappeared(&mut self, id: u32) -> i32 { + self.monitors.retain(|m| m.get_native_identifier() != id); + self.compute_hidpi_factor() + } }