From e486f7b87ce20773496adb081c86118806dd4bfd Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sun, 26 Apr 2020 20:23:56 +0200 Subject: [PATCH] legacy: fixup rendering loop after tty switch --- src/backend/drm/legacy/session.rs | 77 ++++++++++++++++++++++++++++++- src/backend/drm/legacy/surface.rs | 7 --- 2 files changed, 75 insertions(+), 9 deletions(-) diff --git a/src/backend/drm/legacy/session.rs b/src/backend/drm/legacy/session.rs index eb5c86e..e4bd243 100644 --- a/src/backend/drm/legacy/session.rs +++ b/src/backend/drm/legacy/session.rs @@ -8,13 +8,14 @@ use drm::Device as BasicDevice; use nix::libc::dev_t; use nix::sys::stat; use std::cell::RefCell; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::{Rc, Weak}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; +use failure::ResultExt; -use super::{Dev, LegacyDrmDevice, LegacyDrmSurfaceInternal}; +use super::{Dev, LegacyDrmDevice, LegacyDrmSurfaceInternal, Error, DevPath}; use crate::backend::session::{AsSessionObserver, SessionObserver}; /// [`SessionObserver`](SessionObserver) @@ -91,5 +92,77 @@ impl SessionObserver for LegacyDrmDeviceObserver { } } } + // okay, the previous session/whatever might left the drm devices in any state... + // lets fix that + if let Err(err) = self.reset_state() { + warn!(self.logger, "Unable to reset state after tty switch: {}", err); + // TODO call drm-handler::error + } } } + +impl LegacyDrmDeviceObserver { + fn reset_state(&mut self) -> Result<(), Error> { + // lets enumerate it the current state + if let Some(dev) = self.dev.upgrade() { + let res_handles = ControlDevice::resource_handles(&*dev) + .compat() + .map_err(|source| Error::Access { + errmsg: "Error loading drm resources", + dev: dev.dev_path(), + source, + })?; + + let mut used_connectors = HashSet::new(); + if let Some(backends) = self.backends.upgrade() { + for surface in backends.borrow().values().filter_map(Weak::upgrade) { + let mut current = surface.state.write().unwrap(); + let pending = surface.pending.read().unwrap(); + + // store (soon to be) used connectors + used_connectors.extend(pending.connectors.clone()); + + // set current connectors + current.connectors.clear(); + for conn in res_handles.connectors() { + let conn_info = dev.get_connector(*conn).compat().map_err(|source| Error::Access { + errmsg: "Could not load connector info", + dev: dev.dev_path(), + source, + })?; + if let Some(enc) = conn_info.current_encoder() { + let enc_info = dev.get_encoder(enc).compat().map_err(|source| Error::Access { + errmsg: "Could not load encoder info", + dev: dev.dev_path(), + source, + })?; + if enc_info.crtc().map(|crtc| crtc == surface.crtc).unwrap_or(false) { + current.connectors.insert(*conn); + } + } + } + + // set current mode + let crtc_info = dev.get_crtc(surface.crtc).compat().map_err(|source| Error::Access { + errmsg: "Could not load crtc info", + dev: dev.dev_path(), + source, + })?; + + // If we have no current mode, we create a fake one, which will not match (and thus gets overriden on the commit below). + // A better fix would probably be making mode an `Option`, but that would mean + // we need to be sure, we require a mode to always be set without relying on the compiler. + // So we cheat, because it works and is easier to handle later. + current.mode = crtc_info.mode().unwrap_or_else(|| unsafe { std::mem::zeroed() }); + } + } + + // Disable unused connectors + let all_set = res_handles.connectors().iter().copied().collect::>(); + let unused = used_connectors.difference(&all_set); + dev.set_connector_state(unused.copied(), false)?; + } + + Ok(()) + } +} diff --git a/src/backend/drm/legacy/surface.rs b/src/backend/drm/legacy/surface.rs index 36d9f6f..47ff30f 100644 --- a/src/backend/drm/legacy/surface.rs +++ b/src/backend/drm/legacy/surface.rs @@ -5,7 +5,6 @@ use drm::control::{ }; use drm::Device as BasicDevice; -use std::cell::Cell; use std::collections::HashSet; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; @@ -31,7 +30,6 @@ pub(super) struct LegacyDrmSurfaceInternal { pub(super) state: RwLock, pub(super) pending: RwLock, pub(super) logger: ::slog::Logger, - init_buffer: Cell>, } impl AsRawFd for LegacyDrmSurfaceInternal { @@ -357,7 +355,6 @@ impl LegacyDrmSurfaceInternal { state: RwLock::new(state), pending: RwLock::new(pending), logger, - init_buffer: Cell::new(None), }; Ok(surface) @@ -416,10 +413,6 @@ impl LegacyDrmSurfaceInternal { impl Drop for LegacyDrmSurfaceInternal { fn drop(&mut self) { // ignore failure at this point - if let Some((db, fb)) = self.init_buffer.take() { - let _ = self.destroy_framebuffer(fb); - let _ = self.destroy_dumb_buffer(db); - } if !self.dev.active.load(Ordering::SeqCst) { // the device is gone or we are on another tty