Skip to content

Commit

Permalink
Merge pull request #122 from mahkoh/jorth/set-mode
Browse files Browse the repository at this point in the history
metal: allow changing the connector mode
  • Loading branch information
mahkoh authored Mar 4, 2024
2 parents 2ab7b43 + 98b6eba commit 5b2bfb8
Show file tree
Hide file tree
Showing 13 changed files with 197 additions and 7 deletions.
24 changes: 23 additions & 1 deletion jay-config/src/_private.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ pub mod client;
pub mod ipc;
mod logging;

use {bincode::Options, std::marker::PhantomData};
use {
crate::video::Mode,
bincode::Options,
serde::{Deserialize, Serialize},
std::marker::PhantomData,
};

pub const VERSION: u32 = 1;

Expand Down Expand Up @@ -36,3 +41,20 @@ pub fn bincode_ops() -> impl Options {
pub trait Config {
extern "C" fn configure();
}

#[derive(Serialize, Deserialize, Debug)]
pub struct WireMode {
pub width: i32,
pub height: i32,
pub refresh_millihz: u32,
}

impl WireMode {
pub fn to_mode(self) -> Mode {
Mode {
width: self.width,
height: self.height,
refresh_millihz: self.refresh_millihz,
}
}
}
12 changes: 11 additions & 1 deletion jay-config/src/_private/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use {
_private::{
bincode_ops,
ipc::{ClientMessage, InitMessage, Response, ServerMessage},
logging, Config, ConfigEntry, ConfigEntryGen, VERSION,
logging, Config, ConfigEntry, ConfigEntryGen, WireMode, VERSION,
},
exec::Command,
input::{acceleration::AccelProfile, capability::Capability, InputDevice, Seat},
Expand Down Expand Up @@ -570,6 +570,16 @@ 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> {
let res = self.send_with_response(&ClientMessage::ConnectorModes { connector });
get_response!(res, Vec::new(), ConnectorModes { modes });
modes.into_iter().map(WireMode::to_mode).collect()
}

pub fn connector_size(&self, connector: Connector) -> (i32, i32) {
let res = self.send_with_response(&ClientMessage::ConnectorSize { connector });
get_response!(res, (0, 0), ConnectorSize { width, height });
Expand Down
11 changes: 11 additions & 0 deletions jay-config/src/_private/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use {
timer::Timer,
video::{connector_type::ConnectorType, Connector, DrmDevice, GfxApi, Transform},
Axis, Direction, PciId, Workspace,
_private::WireMode,
},
serde::{Deserialize, Serialize},
std::time::Duration,
Expand Down Expand Up @@ -352,6 +353,13 @@ pub enum ClientMessage<'a> {
SetDoubleClickDistance {
dist: i32,
},
ConnectorModes {
connector: Connector,
},
ConnectorSetMode {
connector: Connector,
mode: WireMode,
},
}

#[derive(Serialize, Deserialize, Debug)]
Expand Down Expand Up @@ -454,6 +462,9 @@ pub enum Response {
GetWorkspaceCapture {
capture: bool,
},
ConnectorModes {
modes: Vec<WireMode>,
},
}

#[derive(Serialize, Deserialize, Debug)]
Expand Down
47 changes: 46 additions & 1 deletion jay-config/src/video.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use {
CON_VIRTUAL, CON_WRITEBACK,
},
PciId,
_private::WireMode,
},
serde::{Deserialize, Serialize},
std::str::FromStr,
Expand All @@ -21,7 +22,7 @@ use {
/// - width in pixels
/// - height in pixels
/// - refresh rate in mhz.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct Mode {
pub(crate) width: i32,
pub(crate) height: i32,
Expand Down Expand Up @@ -112,6 +113,50 @@ impl Connector {
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.
pub fn modes(self) -> Vec<Mode> {
if !self.exists() {
return Vec::new();
}
get!(Vec::new()).connector_modes(self)
}

/// Returns the logical width of the connector.
///
/// The returned value will be different from `mode().width()` if the scale is not 1.
Expand Down
1 change: 1 addition & 0 deletions src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pub trait Connector {
fn drm_feedback(&self) -> Option<Rc<DrmFeedback>> {
None
}
fn set_mode(&self, mode: Mode);
}

#[derive(Debug)]
Expand Down
6 changes: 5 additions & 1 deletion src/backends/dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use {
crate::{
async_engine::SpawnedFuture,
backend::{
Backend, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId,
Backend, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId, Mode,
},
video::drm::ConnectorType,
},
Expand Down Expand Up @@ -56,4 +56,8 @@ impl Connector for DummyOutput {
fn set_enabled(&self, _enabled: bool) {
// nothing
}

fn set_mode(&self, _mode: Mode) {
// nothing
}
}
51 changes: 48 additions & 3 deletions src/backends/metal/video.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use {
async_engine::{Phase, SpawnedFuture},
backend::{
BackendDrmDevice, BackendEvent, Connector, ConnectorEvent, ConnectorId,
ConnectorKernelId, DrmDeviceId, HardwareCursor, MonitorInfo,
ConnectorKernelId, DrmDeviceId, HardwareCursor, Mode, MonitorInfo,
},
backends::metal::{MetalBackend, MetalError},
drm_feedback::DrmFeedback,
Expand Down Expand Up @@ -155,7 +155,7 @@ pub struct ConnectorDisplayData {
pub crtc_id: MutableProperty<DrmCrtc>,
pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>,
pub modes: Vec<DrmModeInfo>,
pub mode: Option<Rc<DrmModeInfo>>,
pub mode: Option<DrmModeInfo>,
pub refresh: u32,

pub monitor_manufacturer: String,
Expand Down Expand Up @@ -862,6 +862,44 @@ impl Connector for MetalConnector {
fn drm_feedback(&self) -> Option<Rc<DrmFeedback>> {
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)]
Expand Down Expand Up @@ -1021,7 +1059,7 @@ fn create_connector_display_data(
let mut name = String::new();
let mut manufacturer = 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
.as_ref()
.map(|m| 1_000_000_000_000u64 / (m.refresh_rate_millihz() as u64))
Expand Down Expand Up @@ -1402,6 +1440,13 @@ impl MetalBackend {
}
};
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);
if c.connect_sent.get() {
if !c.enabled.get()
Expand Down
4 changes: 4 additions & 0 deletions src/backends/x.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,10 @@ impl Connector for XOutput {
fn set_enabled(&self, _enabled: bool) {
// nothing
}

fn set_mode(&self, _mode: Mode) {
log::warn!("X backend doesn't support changing the connector mode");
}
}

struct XSeat {
Expand Down
1 change: 1 addition & 0 deletions src/compositor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ fn create_dummy_output(state: &Rc<State>) {
async_event: Default::default(),
}),
0,
Vec::new(),
&backend::Mode {
width: 0,
height: 0,
Expand Down
39 changes: 39 additions & 0 deletions src/config/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use {
_private::{
bincode_ops,
ipc::{ClientMessage, Response, ServerMessage},
WireMode,
},
input::{
acceleration::{AccelProfile, ACCEL_PROFILE_ADAPTIVE, ACCEL_PROFILE_FLAT},
Expand Down Expand Up @@ -694,6 +695,38 @@ impl ConfigProxyHandler {
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> {
let connector = self.get_output(connector)?;
self.respond(Response::ConnectorModes {
modes: connector
.node
.global
.modes
.iter()
.map(|m| WireMode {
width: m.width,
height: m.height,
refresh_millihz: m.refresh_rate_millihz,
})
.collect(),
});
Ok(())
}

fn handle_set_cursor_size(&self, seat: Seat, size: i32) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
if size < 0 {
Expand Down Expand Up @@ -1369,6 +1402,12 @@ impl ConfigProxyHandler {
ClientMessage::SetDoubleClickDistance { dist } => {
self.handle_set_double_click_distance(dist)
}
ClientMessage::ConnectorModes { connector } => self
.handle_connector_modes(connector)
.wrn("connector_modes")?,
ClientMessage::ConnectorSetMode { connector, mode } => self
.handle_connector_set_mode(connector, mode)
.wrn("connector_set_mode")?,
}
Ok(())
}
Expand Down
3 changes: 3 additions & 0 deletions src/ifs/wl_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub struct WlOutputGlobal {
pub pos: Cell<Rect>,
pub output_id: Rc<OutputId>,
pub mode: Cell<backend::Mode>,
pub modes: Vec<backend::Mode>,
pub node: CloneCell<Option<Rc<OutputNode>>>,
pub width_mm: i32,
pub height_mm: i32,
Expand Down Expand Up @@ -96,6 +97,7 @@ impl WlOutputGlobal {
state: &Rc<State>,
connector: &Rc<ConnectorData>,
x1: i32,
modes: Vec<backend::Mode>,
mode: &backend::Mode,
manufacturer: &str,
product: &str,
Expand All @@ -122,6 +124,7 @@ impl WlOutputGlobal {
pos: Cell::new(Rect::new_sized(x1, 0, width, height).unwrap()),
output_id,
mode: Cell::new(*mode),
modes,
node: Default::default(),
width_mm,
height_mm,
Expand Down
4 changes: 4 additions & 0 deletions src/it/test_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ impl Connector for TestConnector {
fn set_enabled(&self, _enabled: bool) {
// todo
}

fn set_mode(&self, _mode: Mode) {
// todo
}
}

pub struct TestMouseClick {
Expand Down
1 change: 1 addition & 0 deletions src/tasks/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ impl ConnectorHandler {
&self.state,
&self.data,
x1,
info.modes.clone(),
&info.initial_mode,
&info.manufacturer,
&info.product,
Expand Down

0 comments on commit 5b2bfb8

Please sign in to comment.