metal: allow changing the connector mode
This commit is contained in:
parent
558bea47b7
commit
98b6eba81c
9 changed files with 124 additions and 4 deletions
|
|
@ -570,6 +570,10 @@ impl Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn connector_set_mode(&self, connector: Connector, mode: WireMode) {
|
||||||
|
self.send(&ClientMessage::ConnectorSetMode { connector, mode });
|
||||||
|
}
|
||||||
|
|
||||||
pub fn connector_modes(&self, connector: Connector) -> Vec<Mode> {
|
pub fn connector_modes(&self, connector: Connector) -> Vec<Mode> {
|
||||||
let res = self.send_with_response(&ClientMessage::ConnectorModes { connector });
|
let res = self.send_with_response(&ClientMessage::ConnectorModes { connector });
|
||||||
get_response!(res, Vec::new(), ConnectorModes { modes });
|
get_response!(res, Vec::new(), ConnectorModes { modes });
|
||||||
|
|
|
||||||
|
|
@ -356,6 +356,10 @@ pub enum ClientMessage<'a> {
|
||||||
ConnectorModes {
|
ConnectorModes {
|
||||||
connector: Connector,
|
connector: Connector,
|
||||||
},
|
},
|
||||||
|
ConnectorSetMode {
|
||||||
|
connector: Connector,
|
||||||
|
mode: WireMode,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use {
|
||||||
CON_VIRTUAL, CON_WRITEBACK,
|
CON_VIRTUAL, CON_WRITEBACK,
|
||||||
},
|
},
|
||||||
PciId,
|
PciId,
|
||||||
|
_private::WireMode,
|
||||||
},
|
},
|
||||||
serde::{Deserialize, Serialize},
|
serde::{Deserialize, Serialize},
|
||||||
std::str::FromStr,
|
std::str::FromStr,
|
||||||
|
|
@ -112,6 +113,42 @@ impl Connector {
|
||||||
get!(Mode::zeroed()).connector_mode(self)
|
get!(Mode::zeroed()).connector_mode(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to set the mode of the connector.
|
||||||
|
///
|
||||||
|
/// If the refresh rate is not specified, tries to use the first mode with the given
|
||||||
|
/// width and height.
|
||||||
|
///
|
||||||
|
/// The default mode is the first mode advertised by the connector. This is usually
|
||||||
|
/// the native mode.
|
||||||
|
pub fn set_mode(self, width: i32, height: i32, refresh_millihz: Option<u32>) {
|
||||||
|
if !self.exists() {
|
||||||
|
log::warn!("set_mode called on a connector that does not exist");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let refresh_millihz = match refresh_millihz {
|
||||||
|
Some(r) => r,
|
||||||
|
_ => match self
|
||||||
|
.modes()
|
||||||
|
.iter()
|
||||||
|
.find(|m| m.width == width && m.height == height)
|
||||||
|
{
|
||||||
|
Some(m) => m.refresh_millihz,
|
||||||
|
_ => {
|
||||||
|
log::warn!("Could not find any mode with width {width} and height {height}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
get!().connector_set_mode(
|
||||||
|
self,
|
||||||
|
WireMode {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
refresh_millihz,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the available modes of the connector.
|
/// Returns the available modes of the connector.
|
||||||
pub fn modes(self) -> Vec<Mode> {
|
pub fn modes(self) -> Vec<Mode> {
|
||||||
if !self.exists() {
|
if !self.exists() {
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ pub trait Connector {
|
||||||
fn drm_feedback(&self) -> Option<Rc<DrmFeedback>> {
|
fn drm_feedback(&self) -> Option<Rc<DrmFeedback>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
fn set_mode(&self, mode: Mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::SpawnedFuture,
|
async_engine::SpawnedFuture,
|
||||||
backend::{
|
backend::{
|
||||||
Backend, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId,
|
Backend, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId, Mode,
|
||||||
},
|
},
|
||||||
video::drm::ConnectorType,
|
video::drm::ConnectorType,
|
||||||
},
|
},
|
||||||
|
|
@ -56,4 +56,8 @@ impl Connector for DummyOutput {
|
||||||
fn set_enabled(&self, _enabled: bool) {
|
fn set_enabled(&self, _enabled: bool) {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_mode(&self, _mode: Mode) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use {
|
||||||
async_engine::{Phase, SpawnedFuture},
|
async_engine::{Phase, SpawnedFuture},
|
||||||
backend::{
|
backend::{
|
||||||
BackendDrmDevice, BackendEvent, Connector, ConnectorEvent, ConnectorId,
|
BackendDrmDevice, BackendEvent, Connector, ConnectorEvent, ConnectorId,
|
||||||
ConnectorKernelId, DrmDeviceId, HardwareCursor, MonitorInfo,
|
ConnectorKernelId, DrmDeviceId, HardwareCursor, Mode, MonitorInfo,
|
||||||
},
|
},
|
||||||
backends::metal::{MetalBackend, MetalError},
|
backends::metal::{MetalBackend, MetalError},
|
||||||
drm_feedback::DrmFeedback,
|
drm_feedback::DrmFeedback,
|
||||||
|
|
@ -155,7 +155,7 @@ pub struct ConnectorDisplayData {
|
||||||
pub crtc_id: MutableProperty<DrmCrtc>,
|
pub crtc_id: MutableProperty<DrmCrtc>,
|
||||||
pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>,
|
pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>,
|
||||||
pub modes: Vec<DrmModeInfo>,
|
pub modes: Vec<DrmModeInfo>,
|
||||||
pub mode: Option<Rc<DrmModeInfo>>,
|
pub mode: Option<DrmModeInfo>,
|
||||||
pub refresh: u32,
|
pub refresh: u32,
|
||||||
|
|
||||||
pub monitor_manufacturer: String,
|
pub monitor_manufacturer: String,
|
||||||
|
|
@ -862,6 +862,44 @@ impl Connector for MetalConnector {
|
||||||
fn drm_feedback(&self) -> Option<Rc<DrmFeedback>> {
|
fn drm_feedback(&self) -> Option<Rc<DrmFeedback>> {
|
||||||
self.drm_feedback.get()
|
self.drm_feedback.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_mode(&self, be_mode: Mode) {
|
||||||
|
let mut dd = self.display.borrow_mut();
|
||||||
|
let Some(mode) = dd.modes.iter().find(|m| m.to_backend() == be_mode) else {
|
||||||
|
log::warn!("Connector does not support mode {:?}", be_mode);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let prev = dd.mode.clone();
|
||||||
|
if prev.as_ref() == Some(mode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if dd.connection != ConnectorStatus::Connected {
|
||||||
|
log::warn!("Cannot change mode of connector that is not connected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(dev) = self.backend.device_holder.drm_devices.get(&self.dev.devnum) else {
|
||||||
|
log::warn!("Cannot change mode because underlying device does not exist?");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
log::info!("Trying to change mode from {:?} to {:?}", prev, mode);
|
||||||
|
dd.mode = Some(mode.clone());
|
||||||
|
drop(dd);
|
||||||
|
let Err(e) = self.backend.handle_drm_change_(&dev, true) else {
|
||||||
|
self.on_change
|
||||||
|
.send_event(ConnectorEvent::ModeChanged(be_mode));
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
log::warn!("Could not change mode: {}", ErrorFmt(&e));
|
||||||
|
self.display.borrow_mut().mode = prev;
|
||||||
|
if let MetalError::Modeset(DrmError::Atomic(OsError(c::EACCES))) = e {
|
||||||
|
log::warn!("Failed due to access denied. Resetting in memory only.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log::warn!("Trying to re-initialize the drm device");
|
||||||
|
if let Err(e) = self.backend.handle_drm_change_(&dev, true) {
|
||||||
|
log::warn!("Could not restore the previous mode: {}", ErrorFmt(e));
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -1021,7 +1059,7 @@ fn create_connector_display_data(
|
||||||
let mut name = String::new();
|
let mut name = String::new();
|
||||||
let mut manufacturer = String::new();
|
let mut manufacturer = String::new();
|
||||||
let mut serial_number = String::new();
|
let mut serial_number = String::new();
|
||||||
let mode = info.modes.first().cloned().map(Rc::new);
|
let mode = info.modes.first().cloned();
|
||||||
let refresh = mode
|
let refresh = mode
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|m| 1_000_000_000_000u64 / (m.refresh_rate_millihz() as u64))
|
.map(|m| 1_000_000_000_000u64 / (m.refresh_rate_millihz() as u64))
|
||||||
|
|
@ -1402,6 +1440,13 @@ impl MetalBackend {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut old = c.display.borrow_mut();
|
let mut old = c.display.borrow_mut();
|
||||||
|
if old.is_same_monitor(&dd) {
|
||||||
|
if let Some(mode) = &old.mode {
|
||||||
|
if dd.modes.contains(mode) {
|
||||||
|
dd.mode = Some(mode.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
mem::swap(old.deref_mut(), &mut dd);
|
mem::swap(old.deref_mut(), &mut dd);
|
||||||
if c.connect_sent.get() {
|
if c.connect_sent.get() {
|
||||||
if !c.enabled.get()
|
if !c.enabled.get()
|
||||||
|
|
|
||||||
|
|
@ -1058,6 +1058,10 @@ impl Connector for XOutput {
|
||||||
fn set_enabled(&self, _enabled: bool) {
|
fn set_enabled(&self, _enabled: bool) {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_mode(&self, _mode: Mode) {
|
||||||
|
log::warn!("X backend doesn't support changing the connector mode");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct XSeat {
|
struct XSeat {
|
||||||
|
|
|
||||||
|
|
@ -695,6 +695,20 @@ impl ConfigProxyHandler {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_connector_set_mode(
|
||||||
|
&self,
|
||||||
|
connector: Connector,
|
||||||
|
mode: WireMode,
|
||||||
|
) -> Result<(), CphError> {
|
||||||
|
let connector = self.get_output(connector)?;
|
||||||
|
connector.connector.connector.set_mode(backend::Mode {
|
||||||
|
width: mode.width,
|
||||||
|
height: mode.height,
|
||||||
|
refresh_rate_millihz: mode.refresh_millihz,
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_connector_modes(&self, connector: Connector) -> Result<(), CphError> {
|
fn handle_connector_modes(&self, connector: Connector) -> Result<(), CphError> {
|
||||||
let connector = self.get_output(connector)?;
|
let connector = self.get_output(connector)?;
|
||||||
self.respond(Response::ConnectorModes {
|
self.respond(Response::ConnectorModes {
|
||||||
|
|
@ -1391,6 +1405,9 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::ConnectorModes { connector } => self
|
ClientMessage::ConnectorModes { connector } => self
|
||||||
.handle_connector_modes(connector)
|
.handle_connector_modes(connector)
|
||||||
.wrn("connector_modes")?,
|
.wrn("connector_modes")?,
|
||||||
|
ClientMessage::ConnectorSetMode { connector, mode } => self
|
||||||
|
.handle_connector_set_mode(connector, mode)
|
||||||
|
.wrn("connector_set_mode")?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -246,6 +246,10 @@ impl Connector for TestConnector {
|
||||||
fn set_enabled(&self, _enabled: bool) {
|
fn set_enabled(&self, _enabled: bool) {
|
||||||
// todo
|
// todo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_mode(&self, _mode: Mode) {
|
||||||
|
// todo
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestMouseClick {
|
pub struct TestMouseClick {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue