diff --git a/src/backend/drm/atomic/session.rs b/src/backend/drm/atomic/session.rs index 39b7cbf..b58dbe0 100644 --- a/src/backend/drm/atomic/session.rs +++ b/src/backend/drm/atomic/session.rs @@ -49,7 +49,9 @@ impl SessionObserver for AtomicDrmDeviceObserver { return; } } + // TODO: Clear overlay planes (if we ever use them) + if let Some(backends) = self.backends.upgrade() { for surface in backends.borrow().values().filter_map(Weak::upgrade) { // other ttys that use no cursor, might not clear it themselves. diff --git a/src/backend/drm/atomic/surface.rs b/src/backend/drm/atomic/surface.rs index 4dda5a5..9d69dee 100644 --- a/src/backend/drm/atomic/surface.rs +++ b/src/backend/drm/atomic/surface.rs @@ -181,11 +181,15 @@ impl Surface for AtomicDrmSurfaceInternal { // check if the connector can handle the current mode if info.modes().contains(pending.mode.as_ref().unwrap()) { - let mut conns = pending.connectors.clone(); - conns.insert(conn); - // check if config is supported - let req = self.build_request(&conns, &self.planes, None, pending.mode, pending.blob)?; + let req = self.build_request( + &mut [conn].iter(), + &mut [].iter(), + &self.planes, + None, + pending.mode, + pending.blob, + )?; self.atomic_commit( &[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly], req, @@ -194,7 +198,7 @@ impl Surface for AtomicDrmSurfaceInternal { .map_err(|_| Error::TestFailed(self.crtc))?; // seems to be, lets add the connector - pending.connectors = conns; + pending.connectors.insert(conn); Ok(()) } else { @@ -202,15 +206,18 @@ impl Surface for AtomicDrmSurfaceInternal { } } - fn remove_connector(&self, connector: connector::Handle) -> Result<(), Error> { + fn remove_connector(&self, conn: connector::Handle) -> Result<(), Error> { let mut pending = self.pending.write().unwrap(); - // remove it temporary - let mut conns = pending.connectors.clone(); - conns.remove(&connector); - // check if new config is supported (should be) - let req = self.build_request(&conns, &self.planes, None, pending.mode, pending.blob)?; + let req = self.build_request( + &mut [].iter(), + &mut [conn].iter(), + &self.planes, + None, + pending.mode, + pending.blob, + )?; self.atomic_commit( &[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly], req, @@ -219,37 +226,37 @@ impl Surface for AtomicDrmSurfaceInternal { .map_err(|_| Error::TestFailed(self.crtc))?; // seems to be, lets remove the connector + pending.connectors.remove(&conn); + + Ok(()) + } + + fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Error> { + let current = self.state.write().unwrap(); + let mut pending = self.pending.write().unwrap(); + + let conns = connectors.iter().cloned().collect::>(); + let mut added = conns.difference(¤t.connectors); + let mut removed = current.connectors.difference(&conns); + + let req = self.build_request( + &mut added, + &mut removed, + &self.planes, + None, + pending.mode, + pending.blob, + )?; + + self.atomic_commit( + &[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly], + req, + ) + .map_err(|_| Error::TestFailed(self.crtc))?; + pending.connectors = conns; - // try to disable it - let mut req = AtomicModeReq::new(); - - req.add_property( - connector, - self.conn_prop_handle(connector, "CRTC_ID")?, - property::Value::CRTC(None), - ); - - if let Err(err) = self - .atomic_commit(&[AtomicCommitFlags::TestOnly], req.clone()) - .compat() - .map_err(|_| Error::TestFailed(self.crtc)) - { - warn!( - self.logger, - "Could not disable connector ({:?}) (but rendering will be stopped): {}", connector, err - ); - Ok(()) - } else { - // should succeed, any error is serious - self.atomic_commit(&[AtomicCommitFlags::Nonblock], req.clone()) - .compat() - .map_err(|source| Error::Access { - errmsg: "Failed to commit disable connector", - dev: self.dev_path(), - source, - }) - } + Ok(()) } fn use_mode(&self, mode: Option) -> Result<(), Error> { @@ -269,7 +276,14 @@ impl Surface for AtomicDrmSurfaceInternal { None => property::Value::Unknown(0), }); - let req = self.build_request(&pending.connectors, &self.planes, None, mode, new_blob)?; + let req = self.build_request( + &mut pending.connectors.iter(), + &mut [].iter(), + &self.planes, + None, + mode, + new_blob, + )?; if let Err(err) = self .atomic_commit( &[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly], @@ -304,41 +318,41 @@ impl RawSurface for AtomicDrmSurfaceInternal { "Preparing Commit.\n\tCurrent: {:?}\n\tPending: {:?}\n", *current, *pending ); - { - let current_conns = current.connectors.clone(); - let pending_conns = pending.connectors.clone(); - let removed = current_conns.difference(&pending_conns); - let added = pending_conns.difference(¤t_conns); + let current_conns = current.connectors.clone(); + let pending_conns = pending.connectors.clone(); + let mut removed = current_conns.difference(&pending_conns); + let mut added = pending_conns.difference(¤t_conns); - for conn in removed { - if let Ok(info) = self.get_connector(*conn) { - info!(self.logger, "Removing connector: {:?}", info.interface()); - } else { - info!(self.logger, "Removing unknown connector"); - } - } - - for conn in added { - if let Ok(info) = self.get_connector(*conn) { - info!(self.logger, "Adding connector: {:?}", info.interface()); - } else { - info!(self.logger, "Adding unknown connector"); - } - } - - if current.mode != pending.mode { - info!( - self.logger, - "Setting new mode: {:?}", - pending.mode.as_ref().unwrap().name() - ); + for conn in removed.clone() { + if let Ok(info) = self.get_connector(*conn) { + info!(self.logger, "Removing connector: {:?}", info.interface()); + } else { + info!(self.logger, "Removing unknown connector"); } } + for conn in added.clone() { + if let Ok(info) = self.get_connector(*conn) { + info!(self.logger, "Adding connector: {:?}", info.interface()); + } else { + info!(self.logger, "Adding unknown connector"); + } + } + + if current.mode != pending.mode { + info!( + self.logger, + "Setting new mode: {:?}", + pending.mode.as_ref().unwrap().name() + ); + } + trace!(self.logger, "Testing screen config"); - { + + let req = { let req = self.build_request( - &pending.connectors, + &mut added, + &mut removed, &self.planes, Some(framebuffer), pending.mode, @@ -360,6 +374,15 @@ impl RawSurface for AtomicDrmSurfaceInternal { info!(self.logger, "Reverting back to last know good state"); *pending = current.clone(); + + self.build_request( + &mut [].iter(), + &mut [].iter(), + &self.planes, + Some(framebuffer), + current.mode, + current.blob, + )? } else { if current.mode != pending.mode { if let Some(blob) = current.blob { @@ -369,16 +392,12 @@ impl RawSurface for AtomicDrmSurfaceInternal { } } *current = pending.clone(); - } - } - let req = self.build_request( - ¤t.connectors, - &self.planes, - Some(framebuffer), - current.mode, - current.blob, - )?; + // new config + req + } + }; + debug!(self.logger, "Setting screen: {:#?}", req); self.atomic_commit( &[ @@ -399,9 +418,15 @@ impl RawSurface for AtomicDrmSurfaceInternal { } fn page_flip(&self, framebuffer: framebuffer::Handle) -> Result<(), SwapBuffersError> { - let current = self.state.read().unwrap(); let req = self - .build_request(¤t.connectors, &self.planes, Some(framebuffer), None, None) //current.mode) + .build_request( + &mut [].iter(), + &mut [].iter(), + &self.planes, + Some(framebuffer), + None, + None, + ) //current.mode) .map_err(|_| SwapBuffersError::ContextLost)?; trace!(self.logger, "Queueing page flip: {:#?}", req); self.atomic_commit( @@ -530,7 +555,8 @@ impl AtomicDrmSurfaceInternal { // If a mode is set a matching blob needs to be set (the inverse is not true) fn build_request( &self, - connectors: &HashSet, + new_connectors: &mut dyn Iterator, + removed_connectors: &mut dyn Iterator, planes: &Planes, framebuffer: Option, mode: Option, @@ -538,7 +564,7 @@ impl AtomicDrmSurfaceInternal { ) -> Result { let mut req = AtomicModeReq::new(); - for conn in connectors.iter() { + for conn in new_connectors { req.add_property( *conn, self.conn_prop_handle(*conn, "CRTC_ID")?, @@ -546,6 +572,14 @@ impl AtomicDrmSurfaceInternal { ); } + for conn in removed_connectors { + req.add_property( + *conn, + self.conn_prop_handle(*conn, "CRTC_ID")?, + property::Value::CRTC(None), + ); + } + if let Some(blob) = blob { req.add_property(self.crtc, self.crtc_prop_handle(self.crtc, "MODE_ID")?, blob); } @@ -816,6 +850,10 @@ impl Surface for AtomicDrmSurface { self.0.remove_connector(connector) } + fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Error> { + self.0.set_connectors(connectors) + } + fn use_mode(&self, mode: Option) -> Result<(), Error> { self.0.use_mode(mode) } diff --git a/src/backend/drm/egl/surface.rs b/src/backend/drm/egl/surface.rs index 20e419f..e9107a0 100644 --- a/src/backend/drm/egl/surface.rs +++ b/src/backend/drm/egl/surface.rs @@ -49,6 +49,10 @@ where .map_err(Error::Underlying) } + fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> { + self.surface.set_connectors(connectors).map_err(Error::Underlying) + } + fn current_mode(&self) -> Option { self.surface.current_mode() } diff --git a/src/backend/drm/gbm/surface.rs b/src/backend/drm/gbm/surface.rs index 11d7bf3..ad61a43 100644 --- a/src/backend/drm/gbm/surface.rs +++ b/src/backend/drm/gbm/surface.rs @@ -163,6 +163,10 @@ impl Surface for GbmSurfaceInternal { self.crtc.remove_connector(connector).map_err(Error::Underlying) } + fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> { + self.crtc.set_connectors(connectors).map_err(Error::Underlying) + } + fn current_mode(&self) -> Option { self.crtc.current_mode() } @@ -317,6 +321,10 @@ impl Surface for GbmSurface { self.0.remove_connector(connector) } + fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> { + self.0.set_connectors(connectors) + } + fn current_mode(&self) -> Option { self.0.current_mode() } diff --git a/src/backend/drm/legacy/mod.rs b/src/backend/drm/legacy/mod.rs index 19597f3..a291bd2 100644 --- a/src/backend/drm/legacy/mod.rs +++ b/src/backend/drm/legacy/mod.rs @@ -233,6 +233,9 @@ impl Device for LegacyDrmDevice { } } + // Now try to do the least possible amount of changes to set this to the state the users requested + // TODO! + let state = State { mode, connectors }; let backend = Rc::new(LegacyDrmSurfaceInternal { dev: self.dev.clone(), diff --git a/src/backend/drm/legacy/surface.rs b/src/backend/drm/legacy/surface.rs index 0685a8e..fd9258e 100644 --- a/src/backend/drm/legacy/surface.rs +++ b/src/backend/drm/legacy/surface.rs @@ -105,64 +105,34 @@ impl Surface for LegacyDrmSurfaceInternal { } fn add_connector(&self, conn: connector::Handle) -> Result<(), Error> { - let info = self - .get_connector(conn) - .compat() - .map_err(|source| Error::Access { - errmsg: "Error loading connector info", - dev: self.dev_path(), - source, - })?; - let mut pending = self.pending.write().unwrap(); - // check if the connector can handle the current mode - if info.modes().contains(pending.mode.as_ref().unwrap()) { - // check if there is a valid encoder - let encoders = info - .encoders() - .iter() - .filter(|enc| enc.is_some()) - .map(|enc| enc.unwrap()) - .map(|encoder| { - self.get_encoder(encoder) - .compat() - .map_err(|source| Error::Access { - errmsg: "Error loading encoder info", - dev: self.dev_path(), - source, - }) - }) - .collect::, _>>()?; - - // and if any encoder supports the selected crtc - let resource_handles = self.resource_handles().compat().map_err(|source| Error::Access { - errmsg: "Error loading resources", - dev: self.dev_path(), - source, - })?; - if !encoders - .iter() - .map(|encoder| encoder.possible_crtcs()) - .all(|crtc_list| resource_handles.filter_crtcs(crtc_list).contains(&self.crtc)) - { - return Err(Error::NoSuitableEncoder { - connector: info.handle(), - crtc: self.crtc, - }); - } - + if self.check_connector(conn, pending.mode.as_ref().unwrap())? { pending.connectors.insert(conn); - Ok(()) - } else { - Err(Error::ModeNotSuitable(pending.mode.unwrap())) } + + Ok(()) } fn remove_connector(&self, connector: connector::Handle) -> Result<(), Error> { self.pending.write().unwrap().connectors.remove(&connector); Ok(()) } + + fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> { + let mut pending = self.pending.write().unwrap(); + + if connectors + .iter() + .map(|conn| self.check_connector(*conn, pending.mode.as_ref().unwrap())) + .collect::, _>>()? + .iter().all(|v| *v) + { + pending.connectors = connectors.into_iter().cloned().collect(); + } + + Ok(()) + } fn use_mode(&self, mode: Option) -> Result<(), Error> { let mut pending = self.pending.write().unwrap(); @@ -279,6 +249,57 @@ impl RawSurface for LegacyDrmSurfaceInternal { } } +impl LegacyDrmSurfaceInternal { + fn check_connector(&self, conn: connector::Handle, mode: &Mode) -> Result { + let info = self + .get_connector(conn) + .compat() + .map_err(|source| Error::Access { + errmsg: "Error loading connector info", + dev: self.dev_path(), + source, + })?; + + // check if the connector can handle the current mode + if info.modes().contains(mode) { + // check if there is a valid encoder + let encoders = info + .encoders() + .iter() + .filter(|enc| enc.is_some()) + .map(|enc| enc.unwrap()) + .map(|encoder| { + self.get_encoder(encoder) + .compat() + .map_err(|source| Error::Access { + errmsg: "Error loading encoder info", + dev: self.dev_path(), + source, + }) + }) + .collect::, _>>()?; + + // and if any encoder supports the selected crtc + let resource_handles = self.resource_handles().compat().map_err(|source| Error::Access { + errmsg: "Error loading resources", + dev: self.dev_path(), + source, + })?; + if !encoders + .iter() + .map(|encoder| encoder.possible_crtcs()) + .all(|crtc_list| resource_handles.filter_crtcs(crtc_list).contains(&self.crtc)) + { + Ok(false) + } else { + Ok(true) + } + } else { + Ok(false) + } + } +} + impl Drop for LegacyDrmSurfaceInternal { fn drop(&mut self) { // ignore failure at this point @@ -347,6 +368,10 @@ impl Surface for LegacyDrmSurface { self.0.remove_connector(connector) } + fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> { + self.0.set_connectors(connectors) + } + fn use_mode(&self, mode: Option) -> Result<(), Error> { self.0.use_mode(mode) } diff --git a/src/backend/drm/mod.rs b/src/backend/drm/mod.rs index e9be94d..f5b608c 100644 --- a/src/backend/drm/mod.rs +++ b/src/backend/drm/mod.rs @@ -172,6 +172,13 @@ pub trait Surface { /// Tries to mark a [`connector`](drm::control::connector) /// for removal on the next commit. fn remove_connector(&self, connector: connector::Handle) -> Result<(), Self::Error>; + /// Tries to replace the current connector set with the newly provided one on the next commit. + /// + /// Fails if one new `connector` is not compatible with the underlying [`crtc`](drm::control::crtc) + /// (e.g. no suitable [`encoder`](drm::control::encoder) may be found) + /// or is not compatible with the currently pending + /// [`Mode`](drm::control::Mode). + fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error>; /// Returns the currently active [`Mode`](drm::control::Mode) /// of the underlying [`crtc`](drm::control::crtc) /// if any.