metal: send non-desktop outputs to the frontend
This commit is contained in:
parent
fa3d870935
commit
24d08918c4
11 changed files with 184 additions and 62 deletions
|
|
@ -59,6 +59,7 @@ pub struct MonitorInfo {
|
|||
pub initial_mode: Mode,
|
||||
pub width_mm: i32,
|
||||
pub height_mm: i32,
|
||||
pub non_desktop: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
|
|||
|
|
@ -409,9 +409,7 @@ impl MetalConnector {
|
|||
|
||||
fn connected(&self) -> bool {
|
||||
let dd = self.display.borrow_mut();
|
||||
self.enabled.get()
|
||||
&& dd.connection == ConnectorStatus::Connected
|
||||
&& self.primary_plane.is_some()
|
||||
self.enabled.get() && dd.connection == ConnectorStatus::Connected
|
||||
}
|
||||
|
||||
pub fn schedule_present(&self) {
|
||||
|
|
@ -909,6 +907,9 @@ impl Connector for MetalConnector {
|
|||
|
||||
fn set_mode(&self, be_mode: Mode) {
|
||||
let mut dd = self.display.borrow_mut();
|
||||
if dd.non_desktop {
|
||||
return;
|
||||
}
|
||||
let Some(mode) = dd.modes.iter().find(|m| m.to_backend() == be_mode) else {
|
||||
log::warn!("Connector does not support mode {:?}", be_mode);
|
||||
return;
|
||||
|
|
@ -1561,6 +1562,7 @@ impl MetalBackend {
|
|||
initial_mode: dd.mode.clone().unwrap().to_backend(),
|
||||
width_mm: dd.mm_width as _,
|
||||
height_mm: dd.mm_height as _,
|
||||
non_desktop: dd.non_desktop,
|
||||
}));
|
||||
connector.connect_sent.set(true);
|
||||
connector.send_hardware_cursor();
|
||||
|
|
@ -2457,6 +2459,9 @@ impl MetalBackend {
|
|||
if !connector.connect_sent.get() {
|
||||
self.send_connected(connector, &dd);
|
||||
}
|
||||
if connector.primary_plane.is_none() {
|
||||
return;
|
||||
}
|
||||
if log_mode {
|
||||
log::info!(
|
||||
"Initialized connector {}-{} with mode {:?}",
|
||||
|
|
|
|||
|
|
@ -574,6 +574,7 @@ impl XBackend {
|
|||
},
|
||||
width_mm: output.width.get(),
|
||||
height_mm: output.height.get(),
|
||||
non_desktop: false,
|
||||
}));
|
||||
output.changed();
|
||||
self.present(output).await;
|
||||
|
|
|
|||
|
|
@ -217,6 +217,7 @@ struct Output {
|
|||
pub height_mm: i32,
|
||||
pub current_mode: Option<Mode>,
|
||||
pub modes: Vec<Mode>,
|
||||
pub non_desktop: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
@ -479,12 +480,16 @@ impl Randr {
|
|||
println!(" product: {}", o.product);
|
||||
println!(" manufacturer: {}", o.manufacturer);
|
||||
println!(" serial number: {}", o.serial_number);
|
||||
println!(" position: {} x {}", o.x, o.y);
|
||||
println!(" logical size: {} x {}", o.width, o.height);
|
||||
println!(
|
||||
" physical size: {}mm x {}mm",
|
||||
o.width_mm, o.height_mm
|
||||
);
|
||||
if o.non_desktop {
|
||||
println!(" non-desktop");
|
||||
return;
|
||||
}
|
||||
println!(" position: {} x {}", o.x, o.y);
|
||||
println!(" logical size: {} x {}", o.width, o.height);
|
||||
if let Some(mode) = &o.current_mode {
|
||||
print!(" mode: ");
|
||||
self.print_mode(mode, false);
|
||||
|
|
@ -570,6 +575,27 @@ impl Randr {
|
|||
height_mm: msg.height_mm,
|
||||
modes: Default::default(),
|
||||
current_mode: None,
|
||||
non_desktop: false,
|
||||
});
|
||||
});
|
||||
jay_randr::NonDesktopOutput::handle(tc, randr, data.clone(), |data, msg| {
|
||||
let mut data = data.borrow_mut();
|
||||
let c = data.connectors.last_mut().unwrap();
|
||||
c.output = Some(Output {
|
||||
scale: 1.0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
transform: Transform::None,
|
||||
manufacturer: msg.manufacturer.to_string(),
|
||||
product: msg.product.to_string(),
|
||||
serial_number: msg.serial_number.to_string(),
|
||||
width_mm: msg.width_mm,
|
||||
height_mm: msg.height_mm,
|
||||
modes: Default::default(),
|
||||
current_mode: None,
|
||||
non_desktop: true,
|
||||
});
|
||||
});
|
||||
jay_randr::Mode::handle(tc, randr, data.clone(), |data, msg| {
|
||||
|
|
|
|||
|
|
@ -514,6 +514,14 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_output_node(&self, connector: Connector) -> Result<Rc<OutputNode>, CphError> {
|
||||
let data = self.get_output(connector)?;
|
||||
match data.node.clone() {
|
||||
Some(d) => Ok(d),
|
||||
_ => Err(CphError::OutputIsNotDesktop(connector)),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_drm_device(&self, dev: DrmDevice) -> Result<Rc<DrmDevData>, CphError> {
|
||||
match self.state.drm_devs.get(&DrmDeviceId::from_raw(dev.0 as _)) {
|
||||
Some(dev) => Ok(dev),
|
||||
|
|
@ -783,7 +791,7 @@ impl ConfigProxyHandler {
|
|||
workspace: WorkspaceSource,
|
||||
connector: Connector,
|
||||
) -> Result<(), CphError> {
|
||||
let output = self.get_output(connector)?;
|
||||
let output = self.get_output_node(connector)?;
|
||||
let ws = match workspace {
|
||||
WorkspaceSource::Explicit(ws) => {
|
||||
let name = self.get_workspace(ws)?;
|
||||
|
|
@ -797,10 +805,10 @@ impl ConfigProxyHandler {
|
|||
_ => return Ok(()),
|
||||
},
|
||||
};
|
||||
if ws.is_dummy || output.node.is_dummy {
|
||||
if ws.is_dummy || output.is_dummy {
|
||||
return Ok(());
|
||||
}
|
||||
if ws.output.get().id == output.node.id {
|
||||
if ws.output.get().id == output.id {
|
||||
return Ok(());
|
||||
}
|
||||
let link = match &*ws.output_link.borrow() {
|
||||
|
|
@ -811,8 +819,8 @@ impl ConfigProxyHandler {
|
|||
make_visible_if_empty: true,
|
||||
source_is_destroyed: false,
|
||||
};
|
||||
move_ws_to_output(&link, &output.node, config);
|
||||
ws.desired_output.set(output.node.global.output_id.clone());
|
||||
move_ws_to_output(&link, &output, config);
|
||||
ws.desired_output.set(output.global.output_id.clone());
|
||||
self.state.tree_changed();
|
||||
self.state.damage();
|
||||
Ok(())
|
||||
|
|
@ -856,8 +864,8 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
|
||||
fn handle_connector_mode(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_output(connector)?;
|
||||
let mode = connector.node.global.mode.get();
|
||||
let connector = self.get_output_node(connector)?;
|
||||
let mode = connector.global.mode.get();
|
||||
self.respond(Response::ConnectorMode {
|
||||
width: mode.width,
|
||||
height: mode.height,
|
||||
|
|
@ -881,10 +889,9 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
|
||||
fn handle_connector_modes(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_output(connector)?;
|
||||
let connector = self.get_output_node(connector)?;
|
||||
self.respond(Response::ConnectorModes {
|
||||
modes: connector
|
||||
.node
|
||||
.global
|
||||
.modes
|
||||
.iter()
|
||||
|
|
@ -964,8 +971,8 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
|
||||
fn handle_connector_size(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_output(connector)?;
|
||||
let pos = connector.node.global.pos.get();
|
||||
let connector = self.get_output_node(connector)?;
|
||||
let pos = connector.global.pos.get();
|
||||
self.respond(Response::ConnectorSize {
|
||||
width: pos.width(),
|
||||
height: pos.height(),
|
||||
|
|
@ -974,9 +981,9 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
|
||||
fn handle_connector_get_scale(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_output(connector)?;
|
||||
let connector = self.get_output_node(connector)?;
|
||||
self.respond(Response::ConnectorGetScale {
|
||||
scale: connector.node.global.persistent.scale.get().to_f64(),
|
||||
scale: connector.global.persistent.scale.get().to_f64(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -989,8 +996,8 @@ impl ConfigProxyHandler {
|
|||
return Err(CphError::ScaleTooLarge(scale));
|
||||
}
|
||||
let scale = Scale::from_f64(scale);
|
||||
let connector = self.get_output(connector)?;
|
||||
connector.node.set_preferred_scale(scale);
|
||||
let connector = self.get_output_node(connector)?;
|
||||
connector.set_preferred_scale(scale);
|
||||
self.state.damage();
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1000,8 +1007,8 @@ impl ConfigProxyHandler {
|
|||
connector: Connector,
|
||||
transform: Transform,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self.get_output(connector)?;
|
||||
connector.node.update_transform(transform);
|
||||
let connector = self.get_output_node(connector)?;
|
||||
connector.update_transform(transform);
|
||||
self.state.damage();
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1012,15 +1019,15 @@ impl ConfigProxyHandler {
|
|||
x: i32,
|
||||
y: i32,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self.get_output(connector)?;
|
||||
let connector = self.get_output_node(connector)?;
|
||||
if x < 0 || y < 0 || x > MAX_EXTENTS || y > MAX_EXTENTS {
|
||||
return Err(CphError::InvalidConnectorPosition(x, y));
|
||||
}
|
||||
let old_pos = connector.node.global.pos.get();
|
||||
connector.node.set_position(x, y);
|
||||
let old_pos = connector.global.pos.get();
|
||||
connector.set_position(x, y);
|
||||
let seats = self.state.globals.seats.lock();
|
||||
for seat in seats.values() {
|
||||
if seat.get_output().id == connector.node.id {
|
||||
if seat.get_output().id == connector.id {
|
||||
let seat_pos = seat.position();
|
||||
seat.set_position(
|
||||
seat_pos.0.round_down() + x - old_pos.x1(),
|
||||
|
|
@ -1032,8 +1039,8 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
|
||||
fn handle_connector_get_position(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_output(connector)?;
|
||||
let (x, y) = connector.node.global.pos.get().position();
|
||||
let connector = self.get_output_node(connector)?;
|
||||
let (x, y) = connector.global.pos.get().position();
|
||||
self.respond(Response::ConnectorGetPosition { x, y });
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1813,6 +1820,8 @@ enum CphError {
|
|||
TimerDoesNotExist(JayTimer),
|
||||
#[error("Connector {0:?} does not exist or is not connected")]
|
||||
OutputDoesNotExist(Connector),
|
||||
#[error("Output {0:?} is not a desktop output")]
|
||||
OutputIsNotDesktop(Connector),
|
||||
#[error("{0}x{1} is not a valid connector position")]
|
||||
InvalidConnectorPosition(i32, i32),
|
||||
#[error("Keymap {0:?} does not exist")]
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use {
|
|||
object::{Object, Version},
|
||||
scale::Scale,
|
||||
state::{ConnectorData, DrmDevData, OutputData},
|
||||
tree::OutputNode,
|
||||
utils::{gfx_api_ext::GfxApiExt, transform_ext::TransformExt},
|
||||
wire::{jay_randr::*, JayRandrId},
|
||||
},
|
||||
|
|
@ -64,33 +65,47 @@ impl JayRandr {
|
|||
enabled: data.connector.enabled() as _,
|
||||
name: &data.name,
|
||||
});
|
||||
if let Some(output) = self.client.state.outputs.get(&data.connector.id()) {
|
||||
let global = &output.node.global;
|
||||
let pos = global.pos.get();
|
||||
self.client.event(Output {
|
||||
self_id: self.id,
|
||||
scale: global.persistent.scale.get().to_wl(),
|
||||
width: pos.width(),
|
||||
height: pos.height(),
|
||||
x: pos.x1(),
|
||||
y: pos.y1(),
|
||||
transform: global.persistent.transform.get().to_wl(),
|
||||
manufacturer: &output.monitor_info.manufacturer,
|
||||
product: &output.monitor_info.product,
|
||||
serial_number: &output.monitor_info.serial_number,
|
||||
width_mm: global.width_mm,
|
||||
height_mm: global.height_mm,
|
||||
});
|
||||
let current_mode = global.mode.get();
|
||||
for mode in &global.modes {
|
||||
self.client.event(Mode {
|
||||
let Some(output) = self.client.state.outputs.get(&data.connector.id()) else {
|
||||
return;
|
||||
};
|
||||
let global = match output.node.as_ref().map(|n| &n.global) {
|
||||
Some(g) => g,
|
||||
_ => {
|
||||
self.client.event(NonDesktopOutput {
|
||||
self_id: self.id,
|
||||
width: mode.width,
|
||||
height: mode.height,
|
||||
refresh_rate_millihz: mode.refresh_rate_millihz,
|
||||
current: (mode == ¤t_mode) as _,
|
||||
manufacturer: &output.monitor_info.manufacturer,
|
||||
product: &output.monitor_info.product,
|
||||
serial_number: &output.monitor_info.serial_number,
|
||||
width_mm: output.monitor_info.width_mm,
|
||||
height_mm: output.monitor_info.height_mm,
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
let pos = global.pos.get();
|
||||
self.client.event(Output {
|
||||
self_id: self.id,
|
||||
scale: global.persistent.scale.get().to_wl(),
|
||||
width: pos.width(),
|
||||
height: pos.height(),
|
||||
x: pos.x1(),
|
||||
y: pos.y1(),
|
||||
transform: global.persistent.transform.get().to_wl(),
|
||||
manufacturer: &output.monitor_info.manufacturer,
|
||||
product: &output.monitor_info.product,
|
||||
serial_number: &output.monitor_info.serial_number,
|
||||
width_mm: global.width_mm,
|
||||
height_mm: global.height_mm,
|
||||
});
|
||||
let current_mode = global.mode.get();
|
||||
for mode in &global.modes {
|
||||
self.client.event(Mode {
|
||||
self_id: self.id,
|
||||
width: mode.width,
|
||||
height: mode.height,
|
||||
refresh_rate_millihz: mode.refresh_rate_millihz,
|
||||
current: (mode == ¤t_mode) as _,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,6 +159,18 @@ impl JayRandr {
|
|||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn get_output_node(&self, name: &str) -> Option<Rc<OutputNode>> {
|
||||
let output = self.get_output(name)?;
|
||||
match output.node.clone() {
|
||||
Some(n) => return Some(n),
|
||||
_ => self.send_error(&format!(
|
||||
"Display connected to {} is not a desktop display",
|
||||
output.connector.name
|
||||
)),
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl JayRandrRequestHandler for JayRandr {
|
||||
|
|
@ -203,22 +230,22 @@ impl JayRandrRequestHandler for JayRandr {
|
|||
}
|
||||
|
||||
fn set_transform(&self, req: SetTransform, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let Some(c) = self.get_output(req.output) else {
|
||||
let Some(c) = self.get_output_node(req.output) else {
|
||||
return Ok(());
|
||||
};
|
||||
let Some(transform) = Transform::from_wl(req.transform) else {
|
||||
self.send_error(&format!("Unknown transform {}", req.transform));
|
||||
return Ok(());
|
||||
};
|
||||
c.node.update_transform(transform);
|
||||
c.update_transform(transform);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_scale(&self, req: SetScale, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let Some(c) = self.get_output(req.output) else {
|
||||
let Some(c) = self.get_output_node(req.output) else {
|
||||
return Ok(());
|
||||
};
|
||||
c.node.set_preferred_scale(Scale::from_wl(req.scale));
|
||||
c.set_preferred_scale(Scale::from_wl(req.scale));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -235,7 +262,7 @@ impl JayRandrRequestHandler for JayRandr {
|
|||
}
|
||||
|
||||
fn set_position(&self, req: SetPosition, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let Some(c) = self.get_output(req.output) else {
|
||||
let Some(c) = self.get_output_node(req.output) else {
|
||||
return Ok(());
|
||||
};
|
||||
if req.x < 0 || req.y < 0 {
|
||||
|
|
@ -246,7 +273,7 @@ impl JayRandrRequestHandler for JayRandr {
|
|||
self.send_error(&format!("x and y cannot be greater than {MAX_EXTENTS}"));
|
||||
return Ok(());
|
||||
}
|
||||
c.node.set_position(req.x, req.y);
|
||||
c.set_position(req.x, req.y);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ impl TestBackend {
|
|||
initial_mode: mode,
|
||||
width_mm: 80,
|
||||
height_mm: 60,
|
||||
non_desktop: false,
|
||||
};
|
||||
Self {
|
||||
state: state.clone(),
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
},
|
||||
width_mm: 0,
|
||||
height_mm: 0,
|
||||
non_desktop: false,
|
||||
};
|
||||
run.backend
|
||||
.state
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ pub struct ConnectorData {
|
|||
pub struct OutputData {
|
||||
pub connector: Rc<ConnectorData>,
|
||||
pub monitor_info: MonitorInfo,
|
||||
pub node: Rc<OutputNode>,
|
||||
pub node: Option<Rc<OutputNode>>,
|
||||
}
|
||||
|
||||
pub struct DrmDevData {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::{Connector, ConnectorEvent, ConnectorId, MonitorInfo},
|
||||
globals::GlobalName,
|
||||
ifs::wl_output::{OutputId, PersistentOutputState, WlOutputGlobal},
|
||||
state::{ConnectorData, OutputData, State},
|
||||
tree::{move_ws_to_output, OutputNode, OutputRenderData, WsMoveConfig},
|
||||
|
|
@ -70,6 +71,9 @@ impl ConnectorHandler {
|
|||
}
|
||||
self.data.async_event.triggered().await;
|
||||
}
|
||||
if let Some(dev) = &self.data.drm_dev {
|
||||
dev.connectors.remove(&self.id);
|
||||
}
|
||||
if let Some(config) = self.state.config.get() {
|
||||
config.del_connector(self.id);
|
||||
}
|
||||
|
|
@ -87,6 +91,21 @@ impl ConnectorHandler {
|
|||
model: info.product.clone(),
|
||||
serial_number: info.serial_number.clone(),
|
||||
});
|
||||
if info.non_desktop {
|
||||
self.handle_non_desktop_connected(info).await;
|
||||
} else {
|
||||
self.handle_desktop_connected(info, name, output_id).await;
|
||||
}
|
||||
self.data.connected.set(false);
|
||||
log::info!("Connector {} disconnected", self.data.connector.kernel_id());
|
||||
}
|
||||
|
||||
async fn handle_desktop_connected(
|
||||
&self,
|
||||
info: MonitorInfo,
|
||||
name: GlobalName,
|
||||
output_id: Rc<OutputId>,
|
||||
) {
|
||||
let desired_state = match self.state.persistent_output_states.get(&output_id) {
|
||||
Some(ds) => ds,
|
||||
_ => {
|
||||
|
|
@ -155,7 +174,7 @@ impl ConnectorHandler {
|
|||
let output_data = Rc::new(OutputData {
|
||||
connector: self.data.clone(),
|
||||
monitor_info: info,
|
||||
node: on.clone(),
|
||||
node: Some(on.clone()),
|
||||
});
|
||||
self.state.outputs.set(self.id, output_data);
|
||||
on.schedule_update_render_data();
|
||||
|
|
@ -224,7 +243,6 @@ impl ConnectorHandler {
|
|||
}
|
||||
self.data.async_event.triggered().await;
|
||||
}
|
||||
log::info!("Connector {} disconnected", self.data.connector.kernel_id());
|
||||
if let Some(config) = self.state.config.get() {
|
||||
config.connector_disconnected(self.id);
|
||||
}
|
||||
|
|
@ -242,7 +260,6 @@ impl ConnectorHandler {
|
|||
global.destroyed.set(true);
|
||||
self.state.root.outputs.remove(&self.id);
|
||||
self.state.root.update_extents();
|
||||
self.data.connected.set(false);
|
||||
self.state.outputs.remove(&self.id);
|
||||
on.lock_surface.take();
|
||||
{
|
||||
|
|
@ -285,4 +302,30 @@ impl ConnectorHandler {
|
|||
self.state.tree_changed();
|
||||
self.state.damage();
|
||||
}
|
||||
|
||||
async fn handle_non_desktop_connected(&self, monitor_info: MonitorInfo) {
|
||||
let output_data = Rc::new(OutputData {
|
||||
connector: self.data.clone(),
|
||||
monitor_info,
|
||||
node: None,
|
||||
});
|
||||
self.state.outputs.set(self.id, output_data);
|
||||
if let Some(config) = self.state.config.get() {
|
||||
config.connector_connected(self.id);
|
||||
}
|
||||
'outer: loop {
|
||||
while let Some(event) = self.data.connector.event() {
|
||||
match event {
|
||||
ConnectorEvent::Disconnected => break 'outer,
|
||||
ConnectorEvent::HardwareCursor(None) => {}
|
||||
ev => unreachable!("received unexpected event {:?}", ev),
|
||||
}
|
||||
}
|
||||
self.data.async_event.triggered().await;
|
||||
}
|
||||
self.state.outputs.remove(&self.id);
|
||||
if let Some(config) = self.state.config.get() {
|
||||
config.connector_disconnected(self.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,3 +99,11 @@ event mode {
|
|||
event error {
|
||||
msg: str,
|
||||
}
|
||||
|
||||
event non_desktop_output {
|
||||
manufacturer: str,
|
||||
product: str,
|
||||
serial_number: str,
|
||||
width_mm: i32,
|
||||
height_mm: i32,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue