1
0
Fork 0
forked from wry/wry

metal: allow configuring framebuffer formats

This commit is contained in:
Julian Orth 2024-09-04 19:56:01 +02:00
parent 9bab4f7ce1
commit b4ca15fec0
24 changed files with 713 additions and 58 deletions

View file

@ -25,7 +25,7 @@ use {
timer::Timer, timer::Timer,
video::{ video::{
connector_type::{ConnectorType, CON_UNKNOWN}, connector_type::{ConnectorType, CON_UNKNOWN},
Connector, DrmDevice, GfxApi, Mode, TearingMode, Transform, VrrMode, Connector, DrmDevice, Format, GfxApi, Mode, TearingMode, Transform, VrrMode,
}, },
Axis, Direction, ModifiedKeySym, PciId, Workspace, Axis, Direction, ModifiedKeySym, PciId, Workspace,
}, },
@ -754,6 +754,10 @@ impl Client {
self.send(&ClientMessage::ConnectorSetScale { connector, scale }); self.send(&ClientMessage::ConnectorSetScale { connector, scale });
} }
pub fn connector_set_format(&self, connector: Connector, format: Format) {
self.send(&ClientMessage::ConnectorSetFormat { connector, format });
}
pub fn connector_get_scale(&self, connector: Connector) -> f64 { pub fn connector_get_scale(&self, connector: Connector) -> f64 {
let res = self.send_with_response(&ClientMessage::ConnectorGetScale { connector }); let res = self.send_with_response(&ClientMessage::ConnectorGetScale { connector });
get_response!(res, 1.0, ConnectorGetScale { scale }); get_response!(res, 1.0, ConnectorGetScale { scale });

View file

@ -9,8 +9,8 @@ use {
theme::{colors::Colorable, sized::Resizable, Color}, theme::{colors::Colorable, sized::Resizable, Color},
timer::Timer, timer::Timer,
video::{ video::{
connector_type::ConnectorType, Connector, DrmDevice, GfxApi, TearingMode, Transform, connector_type::ConnectorType, Connector, DrmDevice, Format, GfxApi, TearingMode,
VrrMode, Transform, VrrMode,
}, },
Axis, Direction, PciId, Workspace, Axis, Direction, PciId, Workspace,
_private::{PollableId, WireMode}, _private::{PollableId, WireMode},
@ -509,6 +509,10 @@ pub enum ClientMessage<'a> {
SetEiSocketEnabled { SetEiSocketEnabled {
enabled: bool, enabled: bool,
}, },
ConnectorSetFormat {
connector: Connector,
format: Format,
},
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]

View file

@ -267,6 +267,11 @@ impl Connector {
pub fn set_tearing_mode(self, mode: TearingMode) { pub fn set_tearing_mode(self, mode: TearingMode) {
get!().set_tearing_mode(Some(self), mode) get!().set_tearing_mode(Some(self), mode)
} }
/// Sets the format to use for framebuffers.
pub fn set_format(self, format: Format) {
get!().connector_set_format(self, format);
}
} }
/// Returns all available DRM devices. /// Returns all available DRM devices.
@ -612,3 +617,38 @@ impl TearingMode {
pub fn set_tearing_mode(mode: TearingMode) { pub fn set_tearing_mode(mode: TearingMode) {
get!().set_tearing_mode(None, mode) get!().set_tearing_mode(None, mode)
} }
/// A graphics format.
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct Format(pub u32);
impl Format {
pub const ARGB8888: Self = Self(0);
pub const XRGB8888: Self = Self(1);
pub const ABGR8888: Self = Self(2);
pub const XBGR8888: Self = Self(3);
pub const R8: Self = Self(4);
pub const GR88: Self = Self(5);
pub const RGB888: Self = Self(6);
pub const BGR888: Self = Self(7);
pub const RGBA4444: Self = Self(8);
pub const RGBX4444: Self = Self(9);
pub const BGRA4444: Self = Self(10);
pub const BGRX4444: Self = Self(11);
pub const RGB565: Self = Self(12);
pub const BGR565: Self = Self(13);
pub const RGBA5551: Self = Self(14);
pub const RGBX5551: Self = Self(15);
pub const BGRA5551: Self = Self(16);
pub const BGRX5551: Self = Self(17);
pub const ARGB1555: Self = Self(18);
pub const XRGB1555: Self = Self(19);
pub const ARGB2101010: Self = Self(20);
pub const XRGB2101010: Self = Self(21);
pub const ABGR2101010: Self = Self(22);
pub const XBGR2101010: Self = Self(23);
pub const ABGR16161616: Self = Self(24);
pub const XBGR16161616: Self = Self(25);
pub const ABGR16161616F: Self = Self(26);
pub const XBGR16161616F: Self = Self(27);
}

View file

@ -3,6 +3,7 @@ use {
async_engine::SpawnedFuture, async_engine::SpawnedFuture,
drm_feedback::DrmFeedback, drm_feedback::DrmFeedback,
fixed::Fixed, fixed::Fixed,
format::Format,
gfx_api::{GfxFramebuffer, SyncFile}, gfx_api::{GfxFramebuffer, SyncFile},
ifs::{ ifs::{
wl_output::OutputId, wl_output::OutputId,
@ -116,6 +117,9 @@ pub trait Connector {
fn set_tearing_enabled(&self, enabled: bool) { fn set_tearing_enabled(&self, enabled: bool) {
let _ = enabled; let _ = enabled;
} }
fn set_fb_format(&self, format: &'static Format) {
let _ = format;
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -128,6 +132,7 @@ pub enum ConnectorEvent {
Unavailable, Unavailable,
Available, Available,
VrrChanged(bool), VrrChanged(bool),
FormatsChanged(Rc<Vec<&'static Format>>, &'static Format),
} }
pub trait HardwareCursor: Debug { pub trait HardwareCursor: Debug {

View file

@ -301,6 +301,7 @@ pub struct MetalDrmDeviceData {
pub struct PersistentDisplayData { pub struct PersistentDisplayData {
pub mode: RefCell<Option<DrmModeInfo>>, pub mode: RefCell<Option<DrmModeInfo>>,
pub vrr_requested: Cell<bool>, pub vrr_requested: Cell<bool>,
pub format: Cell<&'static Format>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -415,6 +416,7 @@ pub struct MetalConnector {
pub connector_id: ConnectorId, pub connector_id: ConnectorId,
pub buffer_format: Cell<&'static Format>,
pub buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>, pub buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>,
pub next_buffer: NumCell<usize>, pub next_buffer: NumCell<usize>,
@ -460,6 +462,7 @@ pub struct MetalConnector {
pub direct_scanout_active: Cell<bool>, pub direct_scanout_active: Cell<bool>,
pub tearing_requested: Cell<bool>, pub tearing_requested: Cell<bool>,
pub try_switch_format: Cell<bool>,
} }
impl Debug for MetalConnector { impl Debug for MetalConnector {
@ -641,6 +644,25 @@ impl MetalConnector {
} }
} }
fn send_formats(&self) {
match self.frontend_state.get() {
FrontState::Removed
| FrontState::Disconnected
| FrontState::Unavailable
| FrontState::Connected { non_desktop: true } => return,
FrontState::Connected { non_desktop: false } => {}
}
let mut formats = vec![];
if let Some(plane) = self.primary_plane.get() {
formats = plane.formats.values().map(|f| f.format).collect();
}
let formats = Rc::new(formats);
self.send_event(ConnectorEvent::FormatsChanged(
formats,
self.buffer_format.get(),
));
}
fn send_hardware_cursor(self: &Rc<Self>) { fn send_hardware_cursor(self: &Rc<Self>) {
match self.frontend_state.get() { match self.frontend_state.get() {
FrontState::Removed FrontState::Removed
@ -1264,6 +1286,17 @@ impl MetalConnector {
log::error!("Tried to send vrr-changed event in invalid state: {state:?}"); log::error!("Tried to send vrr-changed event in invalid state: {state:?}");
} }
}, },
ConnectorEvent::FormatsChanged(_, _) => match state {
FrontState::Connected { non_desktop: false } => {
self.on_change.send_event(event);
}
FrontState::Connected { non_desktop: true }
| FrontState::Removed
| FrontState::Disconnected
| FrontState::Unavailable => {
log::error!("Tried to send format-changed event in invalid state: {state:?}");
}
},
} }
} }
} }
@ -1425,6 +1458,24 @@ impl Connector for MetalConnector {
log::debug!("{msg} tearing on output {}", self.kernel_id()); log::debug!("{msg} tearing on output {}", self.kernel_id());
} }
} }
fn set_fb_format(&self, format: &'static Format) {
{
let dd = self.display.borrow().persistent.clone();
dd.format.set(format);
if format == self.buffer_format.get() {
self.try_switch_format.set(false);
return;
}
self.try_switch_format.set(true);
}
if let Some(dev) = self.backend.device_holder.drm_devices.get(&self.dev.devnum) {
if let Err(e) = self.backend.handle_drm_change_(&dev, true) {
dev.unprocessed_change.set(true);
log::error!("Could not change format: {}", ErrorFmt(e));
}
}
}
} }
pub struct MetalCrtc { pub struct MetalCrtc {
@ -1544,6 +1595,7 @@ fn create_connector(
dev: dev.clone(), dev: dev.clone(),
backend: backend.clone(), backend: backend.clone(),
connector_id: backend.state.connector_ids.next(), connector_id: backend.state.connector_ids.next(),
buffer_format: Cell::new(XRGB8888),
buffers: Default::default(), buffers: Default::default(),
next_buffer: Default::default(), next_buffer: Default::default(),
enabled: Cell::new(true), enabled: Cell::new(true),
@ -1576,6 +1628,7 @@ fn create_connector(
direct_scanout_active: Cell::new(false), direct_scanout_active: Cell::new(false),
next_flip_nsec: Cell::new(0), next_flip_nsec: Cell::new(0),
tearing_requested: Cell::new(false), tearing_requested: Cell::new(false),
try_switch_format: Cell::new(false),
}); });
let futures = ConnectorFutures { let futures = ConnectorFutures {
_present: backend _present: backend
@ -1686,6 +1739,7 @@ fn create_connector_display_data(
let ds = Rc::new(PersistentDisplayData { let ds = Rc::new(PersistentDisplayData {
mode: RefCell::new(info.modes.first().cloned()), mode: RefCell::new(info.modes.first().cloned()),
vrr_requested: Default::default(), vrr_requested: Default::default(),
format: Cell::new(XRGB8888),
}); });
dev.backend dev.backend
.persistent_display_data .persistent_display_data
@ -2043,6 +2097,7 @@ impl MetalBackend {
}; };
let mut old = c.display.borrow_mut(); let mut old = c.display.borrow_mut();
mem::swap(old.deref_mut(), &mut dd); mem::swap(old.deref_mut(), &mut dd);
let mut preserve_connector = false;
match c.frontend_state.get() { match c.frontend_state.get() {
FrontState::Removed | FrontState::Disconnected => {} FrontState::Removed | FrontState::Disconnected => {}
FrontState::Connected { .. } | FrontState::Unavailable => { FrontState::Connected { .. } | FrontState::Unavailable => {
@ -2074,10 +2129,16 @@ impl MetalBackend {
} }
c.send_event(ConnectorEvent::Disconnected); c.send_event(ConnectorEvent::Disconnected);
} else if preserve_any { } else if preserve_any {
preserve.connectors.insert(c.id); preserve_connector = true;
} }
} }
} }
if c.try_switch_format.get() && old.persistent.format.get() != c.buffer_format.get() {
preserve_connector = false;
}
if preserve_connector {
preserve.connectors.insert(c.id);
}
} }
for c in new_connectors { for c in new_connectors {
let (connector, future) = match create_connector(self, c, &dev.dev) { let (connector, future) = match create_connector(self, c, &dev.dev) {
@ -2131,6 +2192,7 @@ impl MetalBackend {
})); }));
connector.send_hardware_cursor(); connector.send_hardware_cursor();
connector.send_vrr_enabled(); connector.send_vrr_enabled();
connector.send_formats();
} }
pub fn create_drm_device( pub fn create_drm_device(
@ -2662,6 +2724,7 @@ impl MetalBackend {
connector.send_hardware_cursor(); connector.send_hardware_cursor();
connector.send_vrr_enabled(); connector.send_vrr_enabled();
connector.update_drm_feedback(); connector.update_drm_feedback();
connector.send_formats();
} }
Ok(()) Ok(())
} }
@ -2954,7 +3017,7 @@ impl MetalBackend {
ctx: &MetalRenderContext, ctx: &MetalRenderContext,
old_buffers: &mut Vec<Rc<dyn Any>>, old_buffers: &mut Vec<Rc<dyn Any>>,
) -> Result<(), MetalError> { ) -> Result<(), MetalError> {
let dd = connector.display.borrow_mut(); let dd = &mut *connector.display.borrow_mut();
let crtc = match connector.crtc.get() { let crtc = match connector.crtc.get() {
Some(c) => c, Some(c) => c,
_ => return Ok(()), _ => return Ok(()),
@ -2966,26 +3029,55 @@ impl MetalBackend {
return Ok(()); return Ok(());
} }
}; };
let (primary_plane, primary_modifiers) = 'primary_plane: { let allocate_primary_plane = |format: &'static Format| {
for plane in crtc.possible_planes.values() { let (primary_plane, primary_modifiers) = 'primary_plane: {
if plane.ty == PlaneType::Primary && !plane.assigned.get() && plane.lease.is_none() for plane in crtc.possible_planes.values() {
{ if plane.ty == PlaneType::Primary
if let Some(format) = plane.formats.get(&XRGB8888.drm) { && !plane.assigned.get()
break 'primary_plane (plane.clone(), &format.modifiers); && plane.lease.is_none()
{
if let Some(format) = plane.formats.get(&format.drm) {
break 'primary_plane (plane.clone(), &format.modifiers);
}
}
}
return Err(MetalError::NoPrimaryPlaneForConnector);
};
let buffers = Rc::new(self.create_scanout_buffers(
&connector.dev,
format,
primary_modifiers,
mode.hdisplay as _,
mode.vdisplay as _,
ctx,
false,
)?);
Ok((primary_plane, buffers))
};
let primary_plane;
let buffers;
let buffer_format;
'primary_plane: {
let format = dd.persistent.format.get();
if format != XRGB8888 {
match allocate_primary_plane(format) {
Ok(v) => {
(primary_plane, buffers) = v;
buffer_format = format;
break 'primary_plane;
}
Err(e) => {
log::error!(
"Could not allocate framebuffer with requested format {}: {}",
format.name,
ErrorFmt(e)
);
} }
} }
} }
return Err(MetalError::NoPrimaryPlaneForConnector); (primary_plane, buffers) = allocate_primary_plane(XRGB8888)?;
}; buffer_format = XRGB8888;
let buffers = Rc::new(self.create_scanout_buffers( }
&connector.dev,
XRGB8888,
primary_modifiers,
mode.hdisplay as _,
mode.vdisplay as _,
ctx,
false,
)?);
let mut cursor_plane = None; let mut cursor_plane = None;
let mut cursor_modifiers = &IndexSet::new(); let mut cursor_modifiers = &IndexSet::new();
for plane in crtc.possible_planes.values() { for plane in crtc.possible_planes.values() {
@ -3060,6 +3152,8 @@ impl MetalBackend {
} }
connector.cursor_plane.set(cursor_plane); connector.cursor_plane.set(cursor_plane);
connector.cursor_enabled.set(false); connector.cursor_enabled.set(false);
connector.buffer_format.set(buffer_format);
connector.try_switch_format.set(false);
Ok(()) Ok(())
} }

View file

@ -17,10 +17,11 @@ use {
crate::{ crate::{
cli::{damage_tracking::DamageTrackingArgs, input::InputArgs, randr::RandrArgs}, cli::{damage_tracking::DamageTrackingArgs, input::InputArgs, randr::RandrArgs},
compositor::start_compositor, compositor::start_compositor,
format::{ref_formats, Format},
portal, portal,
}, },
::log::Level, ::log::Level,
clap::{Args, Parser, Subcommand, ValueEnum}, clap::{builder::PossibleValue, Args, Parser, Subcommand, ValueEnum},
clap_complete::Shell, clap_complete::Shell,
}; };
@ -231,6 +232,16 @@ pub struct GenerateArgs {
shell: Shell, shell: Shell,
} }
impl ValueEnum for &'static Format {
fn value_variants<'a>() -> &'a [Self] {
ref_formats()
}
fn to_possible_value(&self) -> Option<PossibleValue> {
Some(PossibleValue::new(self.name))
}
}
pub fn main() { pub fn main() {
let cli = Jay::parse(); let cli = Jay::parse();
match cli.command { match cli.command {

View file

@ -1,6 +1,7 @@
use { use {
crate::{ crate::{
cli::GlobalArgs, cli::GlobalArgs,
format::{Format, XRGB8888},
scale::Scale, scale::Scale,
tools::tool_client::{with_tool_client, Handle, ToolClient}, tools::tool_client::{with_tool_client, Handle, ToolClient},
utils::{errorfmt::ErrorFmt, transform_ext::TransformExt}, utils::{errorfmt::ErrorFmt, transform_ext::TransformExt},
@ -44,6 +45,9 @@ pub struct ShowArgs {
/// Show all available modes. /// Show all available modes.
#[arg(long)] #[arg(long)]
pub modes: bool, pub modes: bool,
/// Show all available formats.
#[arg(long)]
pub formats: bool,
} }
#[derive(Args, Debug)] #[derive(Args, Debug)]
@ -122,6 +126,8 @@ pub enum OutputCommand {
Vrr(VrrArgs), Vrr(VrrArgs),
/// Change tearing settings. /// Change tearing settings.
Tearing(TearingArgs), Tearing(TearingArgs),
/// Change format settings.
Format(FormatSettings),
} }
#[derive(ValueEnum, Debug, Clone)] #[derive(ValueEnum, Debug, Clone)]
@ -177,6 +183,21 @@ pub struct CursorHzArgs {
pub rate: String, pub rate: String,
} }
#[derive(Args, Debug, Clone)]
pub struct FormatSettings {
#[clap(subcommand)]
pub command: FormatCommand,
}
#[derive(Subcommand, Debug, Clone)]
pub enum FormatCommand {
/// Sets the format of the framebuffer.
Set {
#[clap(value_enum)]
format: &'static Format,
},
}
#[derive(Args, Debug, Clone)] #[derive(Args, Debug, Clone)]
pub struct TearingArgs { pub struct TearingArgs {
#[clap(subcommand)] #[clap(subcommand)]
@ -318,6 +339,8 @@ struct Output {
pub vrr_mode: VrrMode, pub vrr_mode: VrrMode,
pub vrr_cursor_hz: Option<f64>, pub vrr_cursor_hz: Option<f64>,
pub tearing_mode: TearingMode, pub tearing_mode: TearingMode,
pub formats: Vec<String>,
pub format: Option<String>,
} }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@ -546,6 +569,20 @@ impl Randr {
} }
} }
} }
OutputCommand::Format(a) => {
self.handle_error(randr, move |msg| {
eprintln!("Could not change the framebuffer format: {}", msg);
});
match a.command {
FormatCommand::Set { format } => {
tc.send(jay_randr::SetFbFormat {
self_id: randr,
output: &args.output,
format: format.name,
});
}
}
}
} }
tc.round_trip().await; tc.round_trip().await;
} }
@ -609,7 +646,7 @@ impl Randr {
.collect(); .collect();
connectors.sort_by_key(|c| &c.name); connectors.sort_by_key(|c| &c.name);
for c in connectors { for c in connectors {
self.print_connector(c, args.modes); self.print_connector(c, args.modes, args.formats);
} }
} }
{ {
@ -622,7 +659,7 @@ impl Randr {
connectors.sort_by_key(|c| &c.name); connectors.sort_by_key(|c| &c.name);
println!("unbound connectors:"); println!("unbound connectors:");
for c in connectors { for c in connectors {
self.print_connector(c, args.modes); self.print_connector(c, args.modes, args.formats);
} }
} }
} }
@ -639,7 +676,7 @@ impl Randr {
} }
} }
fn print_connector(&self, connector: &Connector, modes: bool) { fn print_connector(&self, connector: &Connector, modes: bool, formats: bool) {
println!(" {}:", connector.name); println!(" {}:", connector.name);
let Some(o) = &connector.output else { let Some(o) = &connector.output else {
if !connector.enabled { if !connector.enabled {
@ -701,6 +738,11 @@ impl Randr {
print!(" mode: "); print!(" mode: ");
self.print_mode(mode, false); self.print_mode(mode, false);
} }
if let Some(format) = &o.format {
if format != XRGB8888.name {
println!(" format: {format}");
}
}
if o.scale != 1.0 { if o.scale != 1.0 {
println!(" scale: {}", o.scale); println!(" scale: {}", o.scale);
} }
@ -724,6 +766,12 @@ impl Randr {
self.print_mode(mode, true); self.print_mode(mode, true);
} }
} }
if o.formats.is_not_empty() && formats {
println!(" formats:");
for format in &o.formats {
println!(" {format}");
}
}
} }
fn print_mode(&self, m: &Mode, print_current: bool) { fn print_mode(&self, m: &Mode, print_current: bool) {
@ -788,6 +836,8 @@ impl Randr {
vrr_mode: VrrMode::NEVER, vrr_mode: VrrMode::NEVER,
vrr_cursor_hz: None, vrr_cursor_hz: None,
tearing_mode: TearingMode::NEVER, tearing_mode: TearingMode::NEVER,
formats: vec![],
format: None,
}); });
}); });
jay_randr::NonDesktopOutput::handle(tc, randr, data.clone(), |data, msg| { jay_randr::NonDesktopOutput::handle(tc, randr, data.clone(), |data, msg| {
@ -813,6 +863,8 @@ impl Randr {
vrr_mode: VrrMode::NEVER, vrr_mode: VrrMode::NEVER,
vrr_cursor_hz: None, vrr_cursor_hz: None,
tearing_mode: TearingMode::NEVER, tearing_mode: TearingMode::NEVER,
formats: vec![],
format: None,
}); });
}); });
jay_randr::VrrState::handle(tc, randr, data.clone(), |data, msg| { jay_randr::VrrState::handle(tc, randr, data.clone(), |data, msg| {
@ -835,6 +887,15 @@ impl Randr {
let output = c.output.as_mut().unwrap(); let output = c.output.as_mut().unwrap();
output.tearing_mode = TearingMode(msg.mode); output.tearing_mode = TearingMode(msg.mode);
}); });
jay_randr::FbFormat::handle(tc, randr, data.clone(), |data, msg| {
let mut data = data.borrow_mut();
let c = data.connectors.last_mut().unwrap();
let output = c.output.as_mut().unwrap();
output.formats.push(msg.name.to_string());
if msg.current != 0 {
output.format = Some(msg.name.to_string());
}
});
jay_randr::Mode::handle(tc, randr, data.clone(), |data, msg| { jay_randr::Mode::handle(tc, randr, data.clone(), |data, msg| {
let mut data = data.borrow_mut(); let mut data = data.borrow_mut();
let c = data.connectors.last_mut().unwrap(); let c = data.connectors.last_mut().unwrap();

View file

@ -7,6 +7,7 @@ use {
}, },
compositor::MAX_EXTENTS, compositor::MAX_EXTENTS,
config::ConfigProxy, config::ConfigProxy,
format::config_formats,
ifs::wl_seat::{SeatId, WlSeatGlobal}, ifs::wl_seat::{SeatId, WlSeatGlobal},
io_uring::TaskResultExt, io_uring::TaskResultExt,
output_schedule::map_cursor_hz, output_schedule::map_cursor_hz,
@ -49,8 +50,8 @@ use {
theme::{colors::Colorable, sized::Resizable}, theme::{colors::Colorable, sized::Resizable},
timer::Timer as JayTimer, timer::Timer as JayTimer,
video::{ video::{
Connector, DrmDevice, GfxApi, TearingMode as ConfigTearingMode, Transform, Connector, DrmDevice, Format as ConfigFormat, GfxApi, TearingMode as ConfigTearingMode,
VrrMode as ConfigVrrMode, Transform, VrrMode as ConfigVrrMode,
}, },
Axis, Direction, Workspace, Axis, Direction, Workspace,
}, },
@ -1051,6 +1052,19 @@ impl ConfigProxyHandler {
Ok(()) Ok(())
} }
fn handle_connector_set_format(
&self,
connector: Connector,
format: ConfigFormat,
) -> Result<(), CphError> {
let Some(&format) = config_formats().get(&format) else {
return Err(CphError::UnknownFormat(format));
};
let connector = self.get_connector(connector)?;
connector.connector.set_fb_format(format);
Ok(())
}
fn handle_set_vrr_mode( fn handle_set_vrr_mode(
&self, &self,
connector: Option<Connector>, connector: Option<Connector>,
@ -1919,6 +1933,9 @@ impl ConfigProxyHandler {
ClientMessage::SetEiSocketEnabled { enabled } => { ClientMessage::SetEiSocketEnabled { enabled } => {
self.handle_set_ei_socket_enabled(enabled) self.handle_set_ei_socket_enabled(enabled)
} }
ClientMessage::ConnectorSetFormat { connector, format } => self
.handle_connector_set_format(connector, format)
.wrn("connector_set_format")?,
} }
Ok(()) Ok(())
} }
@ -1986,6 +2003,8 @@ enum CphError {
InvalidCursorHz(f64), InvalidCursorHz(f64),
#[error("Unknown tearing mode {0:?}")] #[error("Unknown tearing mode {0:?}")]
UnknownTearingMode(ConfigTearingMode), UnknownTearingMode(ConfigTearingMode),
#[error("The format {0:?} is unknown")]
UnknownFormat(ConfigFormat),
} }
trait WithRequestName { trait WithRequestName {

View file

@ -13,6 +13,7 @@ use {
}, },
ahash::AHashMap, ahash::AHashMap,
ash::vk, ash::vk,
jay_config::video::Format as ConfigFormat,
once_cell::sync::Lazy, once_cell::sync::Lazy,
std::fmt::{Debug, Write}, std::fmt::{Debug, Write},
}; };
@ -36,9 +37,10 @@ pub struct Format {
pub pipewire: SpaVideoFormat, pub pipewire: SpaVideoFormat,
pub opaque: Option<&'static Format>, pub opaque: Option<&'static Format>,
pub shm_info: Option<FormatShmInfo>, pub shm_info: Option<FormatShmInfo>,
pub config: ConfigFormat,
} }
const fn default() -> Format { const fn default(config: ConfigFormat) -> Format {
Format { Format {
name: "", name: "",
vk_format: vk::Format::UNDEFINED, vk_format: vk::Format::UNDEFINED,
@ -49,6 +51,7 @@ const fn default() -> Format {
pipewire: SPA_VIDEO_FORMAT_UNKNOWN, pipewire: SPA_VIDEO_FORMAT_UNKNOWN,
opaque: None, opaque: None,
shm_info: None, shm_info: None,
config,
} }
} }
@ -78,10 +81,30 @@ static PW_FORMATS_MAP: Lazy<AHashMap<SpaVideoFormat, &'static Format>> = Lazy::n
map map
}); });
static FORMATS_REFS: Lazy<Vec<&'static Format>> = Lazy::new(|| FORMATS.iter().collect());
static FORMATS_NAMES: Lazy<AHashMap<&'static str, &'static Format>> = Lazy::new(|| {
let mut map = AHashMap::new();
for format in FORMATS {
assert!(map.insert(format.name, format).is_none());
}
map
});
static FORMATS_CONFIG: Lazy<AHashMap<ConfigFormat, &'static Format>> = Lazy::new(|| {
let mut map = AHashMap::new();
for format in FORMATS {
assert!(map.insert(format.config, format).is_none());
}
map
});
#[test] #[test]
fn formats_dont_panic() { fn formats_dont_panic() {
formats(); formats();
pw_formats(); pw_formats();
named_formats();
config_formats();
} }
pub fn formats() -> &'static AHashMap<u32, &'static Format> { pub fn formats() -> &'static AHashMap<u32, &'static Format> {
@ -92,6 +115,18 @@ pub fn pw_formats() -> &'static AHashMap<SpaVideoFormat, &'static Format> {
&PW_FORMATS_MAP &PW_FORMATS_MAP
} }
pub fn ref_formats() -> &'static [&'static Format] {
&FORMATS_REFS
}
pub fn named_formats() -> &'static AHashMap<&'static str, &'static Format> {
&FORMATS_NAMES
}
pub fn config_formats() -> &'static AHashMap<ConfigFormat, &'static Format> {
&FORMATS_CONFIG
}
const fn fourcc_code(a: char, b: char, c: char, d: char) -> u32 { const fn fourcc_code(a: char, b: char, c: char, d: char) -> u32 {
(a as u32) | ((b as u32) << 8) | ((c as u32) << 16) | ((d as u32) << 24) (a as u32) | ((b as u32) << 8) | ((c as u32) << 16) | ((d as u32) << 24)
} }
@ -136,6 +171,7 @@ pub static ARGB8888: &Format = &Format {
has_alpha: true, has_alpha: true,
pipewire: SPA_VIDEO_FORMAT_BGRA, pipewire: SPA_VIDEO_FORMAT_BGRA,
opaque: Some(XRGB8888), opaque: Some(XRGB8888),
config: ConfigFormat::ARGB8888,
}; };
pub static XRGB8888: &Format = &Format { pub static XRGB8888: &Format = &Format {
@ -153,6 +189,7 @@ pub static XRGB8888: &Format = &Format {
has_alpha: false, has_alpha: false,
pipewire: SPA_VIDEO_FORMAT_BGRx, pipewire: SPA_VIDEO_FORMAT_BGRx,
opaque: None, opaque: None,
config: ConfigFormat::XRGB8888,
}; };
static ABGR8888: &Format = &Format { static ABGR8888: &Format = &Format {
@ -170,6 +207,7 @@ static ABGR8888: &Format = &Format {
has_alpha: true, has_alpha: true,
pipewire: SPA_VIDEO_FORMAT_RGBA, pipewire: SPA_VIDEO_FORMAT_RGBA,
opaque: Some(XBGR8888), opaque: Some(XBGR8888),
config: ConfigFormat::ABGR8888,
}; };
static XBGR8888: &Format = &Format { static XBGR8888: &Format = &Format {
@ -187,6 +225,7 @@ static XBGR8888: &Format = &Format {
has_alpha: false, has_alpha: false,
pipewire: SPA_VIDEO_FORMAT_RGBx, pipewire: SPA_VIDEO_FORMAT_RGBx,
opaque: None, opaque: None,
config: ConfigFormat::XBGR8888,
}; };
static R8: &Format = &Format { static R8: &Format = &Format {
@ -194,14 +233,14 @@ static R8: &Format = &Format {
vk_format: vk::Format::R8_UNORM, vk_format: vk::Format::R8_UNORM,
drm: fourcc_code('R', '8', ' ', ' '), drm: fourcc_code('R', '8', ' ', ' '),
pipewire: SPA_VIDEO_FORMAT_GRAY8, pipewire: SPA_VIDEO_FORMAT_GRAY8,
..default() ..default(ConfigFormat::R8)
}; };
static GR88: &Format = &Format { static GR88: &Format = &Format {
name: "gr88", name: "gr88",
vk_format: vk::Format::R8G8_UNORM, vk_format: vk::Format::R8G8_UNORM,
drm: fourcc_code('G', 'R', '8', '8'), drm: fourcc_code('G', 'R', '8', '8'),
..default() ..default(ConfigFormat::GR88)
}; };
static RGB888: &Format = &Format { static RGB888: &Format = &Format {
@ -209,7 +248,7 @@ static RGB888: &Format = &Format {
vk_format: vk::Format::B8G8R8_UNORM, vk_format: vk::Format::B8G8R8_UNORM,
drm: fourcc_code('R', 'G', '2', '4'), drm: fourcc_code('R', 'G', '2', '4'),
pipewire: SPA_VIDEO_FORMAT_BGR, pipewire: SPA_VIDEO_FORMAT_BGR,
..default() ..default(ConfigFormat::RGB888)
}; };
static BGR888: &Format = &Format { static BGR888: &Format = &Format {
@ -217,7 +256,7 @@ static BGR888: &Format = &Format {
vk_format: vk::Format::R8G8B8_UNORM, vk_format: vk::Format::R8G8B8_UNORM,
drm: fourcc_code('B', 'G', '2', '4'), drm: fourcc_code('B', 'G', '2', '4'),
pipewire: SPA_VIDEO_FORMAT_RGB, pipewire: SPA_VIDEO_FORMAT_RGB,
..default() ..default(ConfigFormat::BGR888)
}; };
static RGBA4444: &Format = &Format { static RGBA4444: &Format = &Format {
@ -226,14 +265,14 @@ static RGBA4444: &Format = &Format {
drm: fourcc_code('R', 'A', '1', '2'), drm: fourcc_code('R', 'A', '1', '2'),
has_alpha: true, has_alpha: true,
opaque: Some(RGBX4444), opaque: Some(RGBX4444),
..default() ..default(ConfigFormat::RGBA4444)
}; };
static RGBX4444: &Format = &Format { static RGBX4444: &Format = &Format {
name: "rgbx4444", name: "rgbx4444",
vk_format: vk::Format::R4G4B4A4_UNORM_PACK16, vk_format: vk::Format::R4G4B4A4_UNORM_PACK16,
drm: fourcc_code('R', 'X', '1', '2'), drm: fourcc_code('R', 'X', '1', '2'),
..default() ..default(ConfigFormat::RGBX4444)
}; };
static BGRA4444: &Format = &Format { static BGRA4444: &Format = &Format {
@ -242,14 +281,14 @@ static BGRA4444: &Format = &Format {
drm: fourcc_code('B', 'A', '1', '2'), drm: fourcc_code('B', 'A', '1', '2'),
has_alpha: true, has_alpha: true,
opaque: Some(BGRX4444), opaque: Some(BGRX4444),
..default() ..default(ConfigFormat::BGRA4444)
}; };
static BGRX4444: &Format = &Format { static BGRX4444: &Format = &Format {
name: "bgrx4444", name: "bgrx4444",
vk_format: vk::Format::B4G4R4A4_UNORM_PACK16, vk_format: vk::Format::B4G4R4A4_UNORM_PACK16,
drm: fourcc_code('B', 'X', '1', '2'), drm: fourcc_code('B', 'X', '1', '2'),
..default() ..default(ConfigFormat::BGRX4444)
}; };
static RGB565: &Format = &Format { static RGB565: &Format = &Format {
@ -257,7 +296,7 @@ static RGB565: &Format = &Format {
vk_format: vk::Format::R5G6B5_UNORM_PACK16, vk_format: vk::Format::R5G6B5_UNORM_PACK16,
drm: fourcc_code('R', 'G', '1', '6'), drm: fourcc_code('R', 'G', '1', '6'),
pipewire: SPA_VIDEO_FORMAT_BGR16, pipewire: SPA_VIDEO_FORMAT_BGR16,
..default() ..default(ConfigFormat::RGB565)
}; };
static BGR565: &Format = &Format { static BGR565: &Format = &Format {
@ -265,7 +304,7 @@ static BGR565: &Format = &Format {
vk_format: vk::Format::B5G6R5_UNORM_PACK16, vk_format: vk::Format::B5G6R5_UNORM_PACK16,
drm: fourcc_code('B', 'G', '1', '6'), drm: fourcc_code('B', 'G', '1', '6'),
pipewire: SPA_VIDEO_FORMAT_RGB16, pipewire: SPA_VIDEO_FORMAT_RGB16,
..default() ..default(ConfigFormat::BGR565)
}; };
static RGBA5551: &Format = &Format { static RGBA5551: &Format = &Format {
@ -274,14 +313,14 @@ static RGBA5551: &Format = &Format {
drm: fourcc_code('R', 'A', '1', '5'), drm: fourcc_code('R', 'A', '1', '5'),
has_alpha: true, has_alpha: true,
opaque: Some(RGBX5551), opaque: Some(RGBX5551),
..default() ..default(ConfigFormat::RGBA5551)
}; };
static RGBX5551: &Format = &Format { static RGBX5551: &Format = &Format {
name: "rgbx5551", name: "rgbx5551",
vk_format: vk::Format::R5G5B5A1_UNORM_PACK16, vk_format: vk::Format::R5G5B5A1_UNORM_PACK16,
drm: fourcc_code('R', 'X', '1', '5'), drm: fourcc_code('R', 'X', '1', '5'),
..default() ..default(ConfigFormat::RGBX5551)
}; };
static BGRA5551: &Format = &Format { static BGRA5551: &Format = &Format {
@ -290,14 +329,14 @@ static BGRA5551: &Format = &Format {
drm: fourcc_code('B', 'A', '1', '5'), drm: fourcc_code('B', 'A', '1', '5'),
has_alpha: true, has_alpha: true,
opaque: Some(BGRX5551), opaque: Some(BGRX5551),
..default() ..default(ConfigFormat::BGRA5551)
}; };
static BGRX5551: &Format = &Format { static BGRX5551: &Format = &Format {
name: "bgrx5551", name: "bgrx5551",
vk_format: vk::Format::B5G5R5A1_UNORM_PACK16, vk_format: vk::Format::B5G5R5A1_UNORM_PACK16,
drm: fourcc_code('B', 'X', '1', '5'), drm: fourcc_code('B', 'X', '1', '5'),
..default() ..default(ConfigFormat::BGRX5551)
}; };
static ARGB1555: &Format = &Format { static ARGB1555: &Format = &Format {
@ -306,7 +345,7 @@ static ARGB1555: &Format = &Format {
drm: fourcc_code('A', 'R', '1', '5'), drm: fourcc_code('A', 'R', '1', '5'),
has_alpha: true, has_alpha: true,
opaque: Some(XRGB1555), opaque: Some(XRGB1555),
..default() ..default(ConfigFormat::ARGB1555)
}; };
static XRGB1555: &Format = &Format { static XRGB1555: &Format = &Format {
@ -314,7 +353,7 @@ static XRGB1555: &Format = &Format {
vk_format: vk::Format::A1R5G5B5_UNORM_PACK16, vk_format: vk::Format::A1R5G5B5_UNORM_PACK16,
drm: fourcc_code('X', 'R', '1', '5'), drm: fourcc_code('X', 'R', '1', '5'),
pipewire: SPA_VIDEO_FORMAT_BGR15, pipewire: SPA_VIDEO_FORMAT_BGR15,
..default() ..default(ConfigFormat::XRGB1555)
}; };
static ARGB2101010: &Format = &Format { static ARGB2101010: &Format = &Format {
@ -324,7 +363,7 @@ static ARGB2101010: &Format = &Format {
has_alpha: true, has_alpha: true,
opaque: Some(XRGB2101010), opaque: Some(XRGB2101010),
pipewire: SPA_VIDEO_FORMAT_ARGB_210LE, pipewire: SPA_VIDEO_FORMAT_ARGB_210LE,
..default() ..default(ConfigFormat::ARGB2101010)
}; };
static XRGB2101010: &Format = &Format { static XRGB2101010: &Format = &Format {
@ -332,7 +371,7 @@ static XRGB2101010: &Format = &Format {
vk_format: vk::Format::A2R10G10B10_UNORM_PACK32, vk_format: vk::Format::A2R10G10B10_UNORM_PACK32,
drm: fourcc_code('X', 'R', '3', '0'), drm: fourcc_code('X', 'R', '3', '0'),
pipewire: SPA_VIDEO_FORMAT_xRGB_210LE, pipewire: SPA_VIDEO_FORMAT_xRGB_210LE,
..default() ..default(ConfigFormat::XRGB2101010)
}; };
static ABGR2101010: &Format = &Format { static ABGR2101010: &Format = &Format {
@ -342,7 +381,7 @@ static ABGR2101010: &Format = &Format {
has_alpha: true, has_alpha: true,
opaque: Some(XBGR2101010), opaque: Some(XBGR2101010),
pipewire: SPA_VIDEO_FORMAT_ABGR_210LE, pipewire: SPA_VIDEO_FORMAT_ABGR_210LE,
..default() ..default(ConfigFormat::ABGR2101010)
}; };
static XBGR2101010: &Format = &Format { static XBGR2101010: &Format = &Format {
@ -350,7 +389,7 @@ static XBGR2101010: &Format = &Format {
vk_format: vk::Format::A2B10G10R10_UNORM_PACK32, vk_format: vk::Format::A2B10G10R10_UNORM_PACK32,
drm: fourcc_code('X', 'B', '3', '0'), drm: fourcc_code('X', 'B', '3', '0'),
pipewire: SPA_VIDEO_FORMAT_xBGR_210LE, pipewire: SPA_VIDEO_FORMAT_xBGR_210LE,
..default() ..default(ConfigFormat::XBGR2101010)
}; };
static ABGR16161616: &Format = &Format { static ABGR16161616: &Format = &Format {
@ -359,14 +398,14 @@ static ABGR16161616: &Format = &Format {
drm: fourcc_code('A', 'B', '4', '8'), drm: fourcc_code('A', 'B', '4', '8'),
has_alpha: true, has_alpha: true,
opaque: Some(XBGR16161616), opaque: Some(XBGR16161616),
..default() ..default(ConfigFormat::ABGR16161616)
}; };
static XBGR16161616: &Format = &Format { static XBGR16161616: &Format = &Format {
name: "xbgr16161616", name: "xbgr16161616",
vk_format: vk::Format::R16G16B16A16_UNORM, vk_format: vk::Format::R16G16B16A16_UNORM,
drm: fourcc_code('X', 'B', '4', '8'), drm: fourcc_code('X', 'B', '4', '8'),
..default() ..default(ConfigFormat::XBGR16161616)
}; };
static ABGR16161616F: &Format = &Format { static ABGR16161616F: &Format = &Format {
@ -375,14 +414,14 @@ static ABGR16161616F: &Format = &Format {
drm: fourcc_code('A', 'B', '4', 'H'), drm: fourcc_code('A', 'B', '4', 'H'),
has_alpha: true, has_alpha: true,
opaque: Some(XBGR16161616F), opaque: Some(XBGR16161616F),
..default() ..default(ConfigFormat::ABGR16161616F)
}; };
static XBGR16161616F: &Format = &Format { static XBGR16161616F: &Format = &Format {
name: "xbgr16161616f", name: "xbgr16161616f",
vk_format: vk::Format::R16G16B16A16_SFLOAT, vk_format: vk::Format::R16G16B16A16_SFLOAT,
drm: fourcc_code('X', 'B', '4', 'H'), drm: fourcc_code('X', 'B', '4', 'H'),
..default() ..default(ConfigFormat::XBGR16161616F)
}; };
pub static FORMATS: &[Format] = &[ pub static FORMATS: &[Format] = &[

View file

@ -70,7 +70,7 @@ impl Global for JayCompositorGlobal {
} }
fn version(&self) -> u32 { fn version(&self) -> u32 {
7 8
} }
fn required_caps(&self) -> ClientCaps { fn required_caps(&self) -> ClientCaps {

View file

@ -3,6 +3,7 @@ use {
backend, backend,
client::{Client, ClientError}, client::{Client, ClientError},
compositor::MAX_EXTENTS, compositor::MAX_EXTENTS,
format::named_formats,
leaks::Tracker, leaks::Tracker,
object::{Object, Version}, object::{Object, Version},
scale::Scale, scale::Scale,
@ -27,6 +28,7 @@ pub struct JayRandr {
const VRR_CAPABLE_SINCE: Version = Version(2); const VRR_CAPABLE_SINCE: Version = Version(2);
const TEARING_SINCE: Version = Version(3); const TEARING_SINCE: Version = Version(3);
const FORMAT_SINCE: Version = Version(8);
impl JayRandr { impl JayRandr {
pub fn new(id: JayRandrId, client: &Rc<Client>, version: Version) -> Self { pub fn new(id: JayRandrId, client: &Rc<Client>, version: Version) -> Self {
@ -125,6 +127,23 @@ impl JayRandr {
mode: node.global.persistent.tearing_mode.get().to_config().0, mode: node.global.persistent.tearing_mode.get().to_config().0,
}); });
} }
if self.version >= FORMAT_SINCE {
let current = node.global.format.get();
self.client.event(FbFormat {
self_id: self.id,
name: current.name,
current: 1,
});
for &format in &*node.global.formats.get() {
if format != current {
self.client.event(FbFormat {
self_id: self.id,
name: format.name,
current: 0,
});
}
}
}
let current_mode = global.mode.get(); let current_mode = global.mode.get();
for mode in &global.modes { for mode in &global.modes {
self.client.event(Mode { self.client.event(Mode {
@ -365,6 +384,17 @@ impl JayRandrRequestHandler for JayRandr {
c.update_presentation_type(); c.update_presentation_type();
return Ok(()); return Ok(());
} }
fn set_fb_format(&self, req: SetFbFormat<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let Some(&format) = named_formats().get(req.format) else {
return Err(JayRandrError::UnknownFormat(req.format.to_string()));
};
let Some(c) = self.get_output_node(req.output) else {
return Ok(());
};
c.global.connector.connector.set_fb_format(format);
Ok(())
}
} }
object_base! { object_base! {
@ -384,5 +414,7 @@ pub enum JayRandrError {
UnknownVrrMode(u32), UnknownVrrMode(u32),
#[error("Unknown tearing mode {0}")] #[error("Unknown tearing mode {0}")]
UnknownTearingMode(u32), UnknownTearingMode(u32),
#[error("Unknown format {0}")]
UnknownFormat(String),
} }
efrom!(JayRandrError, ClientError); efrom!(JayRandrError, ClientError);

View file

@ -4,6 +4,7 @@ use {
crate::{ crate::{
backend, backend,
client::{Client, ClientError, ClientId}, client::{Client, ClientError, ClientId},
format::{Format, XRGB8888},
globals::{Global, GlobalName}, globals::{Global, GlobalName},
ifs::{wl_surface::WlSurface, zxdg_output_v1::ZxdgOutputV1}, ifs::{wl_surface::WlSurface, zxdg_output_v1::ZxdgOutputV1},
leaks::Tracker, leaks::Tracker,
@ -57,6 +58,8 @@ pub struct WlOutputGlobal {
pub output_id: Rc<OutputId>, pub output_id: Rc<OutputId>,
pub mode: Cell<backend::Mode>, pub mode: Cell<backend::Mode>,
pub modes: Vec<backend::Mode>, pub modes: Vec<backend::Mode>,
pub formats: CloneCell<Rc<Vec<&'static Format>>>,
pub format: Cell<&'static Format>,
pub width_mm: i32, pub width_mm: i32,
pub height_mm: i32, pub height_mm: i32,
pub bindings: RefCell<AHashMap<ClientId, AHashMap<WlOutputId, Rc<WlOutput>>>>, pub bindings: RefCell<AHashMap<ClientId, AHashMap<WlOutputId, Rc<WlOutput>>>>,
@ -152,6 +155,8 @@ impl WlOutputGlobal {
output_id: output_id.clone(), output_id: output_id.clone(),
mode: Cell::new(*mode), mode: Cell::new(*mode),
modes, modes,
formats: CloneCell::new(Rc::new(vec![])),
format: Cell::new(XRGB8888),
width_mm, width_mm,
height_mm, height_mm,
bindings: Default::default(), bindings: Default::default(),

View file

@ -11,6 +11,7 @@ use {
compositor::TestFuture, compositor::TestFuture,
drm_feedback::DrmFeedback, drm_feedback::DrmFeedback,
fixed::Fixed, fixed::Fixed,
format::XRGB8888,
gfx_api::GfxError, gfx_api::GfxError,
gfx_apis::create_vulkan_allocator, gfx_apis::create_vulkan_allocator,
ifs::wl_output::OutputId, ifs::wl_output::OutputId,

View file

@ -250,6 +250,10 @@ impl ConnectorHandler {
ConnectorEvent::VrrChanged(enabled) => { ConnectorEvent::VrrChanged(enabled) => {
on.schedule.set_vrr_enabled(enabled); on.schedule.set_vrr_enabled(enabled);
} }
ConnectorEvent::FormatsChanged(formats, format) => {
on.global.formats.set(formats);
on.global.format.set(format);
}
ev => unreachable!("received unexpected event {:?}", ev), ev => unreachable!("received unexpected event {:?}", ev),
} }
} }

View file

@ -330,7 +330,7 @@ impl ToolClient {
self_id: s.registry, self_id: s.registry,
name: s.jay_compositor.0, name: s.jay_compositor.0,
interface: JayCompositor.name(), interface: JayCompositor.name(),
version: s.jay_compositor.1.min(6), version: s.jay_compositor.1.min(8),
id: id.into(), id: id.into(),
}); });
self.jay_compositor.set(Some(id)); self.jay_compositor.set(Some(id));

View file

@ -22,7 +22,7 @@ use {
logging::LogLevel, logging::LogLevel,
status::MessageFormat, status::MessageFormat,
theme::Color, theme::Color,
video::{GfxApi, TearingMode, Transform, VrrMode}, video::{Format, GfxApi, TearingMode, Transform, VrrMode},
Axis, Direction, Workspace, Axis, Direction, Workspace,
}, },
std::{ std::{
@ -208,6 +208,7 @@ pub struct Output {
pub mode: Option<Mode>, pub mode: Option<Mode>,
pub vrr: Option<Vrr>, pub vrr: Option<Vrr>,
pub tearing: Option<Tearing>, pub tearing: Option<Tearing>,
pub format: Option<Format>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -15,6 +15,7 @@ mod drm_device;
mod drm_device_match; mod drm_device_match;
mod env; mod env;
pub mod exec; pub mod exec;
mod format;
mod gfx_api; mod gfx_api;
mod idle; mod idle;
mod input; mod input;

View file

@ -0,0 +1,59 @@
use {
crate::{
config::parser::{DataType, ParseResult, Parser, UnexpectedDataType},
toml::toml_span::{Span, SpannedExt},
},
jay_config::video::Format,
thiserror::Error,
};
#[derive(Debug, Error)]
pub enum FormatParserError {
#[error(transparent)]
Expected(#[from] UnexpectedDataType),
#[error("Unknown format {0}")]
UnknownFormat(String),
}
pub struct FormatParser;
impl Parser for FormatParser {
type Value = Format;
type Error = FormatParserError;
const EXPECTED: &'static [DataType] = &[DataType::String];
fn parse_string(&mut self, span: Span, string: &str) -> ParseResult<Self> {
let format = match string {
"argb8888" => Format::ARGB8888,
"xrgb8888" => Format::XRGB8888,
"abgr8888" => Format::ABGR8888,
"xbgr8888" => Format::XBGR8888,
"r8" => Format::R8,
"gr88" => Format::GR88,
"rgb888" => Format::RGB888,
"bgr888" => Format::BGR888,
"rgba4444" => Format::RGBA4444,
"rgbx4444" => Format::RGBX4444,
"bgra4444" => Format::BGRA4444,
"bgrx4444" => Format::BGRX4444,
"rgb565" => Format::RGB565,
"bgr565" => Format::BGR565,
"rgba5551" => Format::RGBA5551,
"rgbx5551" => Format::RGBX5551,
"bgra5551" => Format::BGRA5551,
"bgrx5551" => Format::BGRX5551,
"argb1555" => Format::ARGB1555,
"xrgb1555" => Format::XRGB1555,
"argb2101010" => Format::ARGB2101010,
"xrgb2101010" => Format::XRGB2101010,
"abgr2101010" => Format::ABGR2101010,
"xbgr2101010" => Format::XBGR2101010,
"abgr16161616" => Format::ABGR16161616,
"xbgr16161616" => Format::XBGR16161616,
"abgr16161616f" => Format::ABGR16161616F,
"xbgr16161616f" => Format::XBGR16161616F,
_ => return Err(FormatParserError::UnknownFormat(string.to_string()).spanned(span)),
};
Ok(format)
}
}

View file

@ -5,6 +5,7 @@ use {
extractor::{fltorint, opt, recover, s32, str, val, Extractor, ExtractorError}, extractor::{fltorint, opt, recover, s32, str, val, Extractor, ExtractorError},
parser::{DataType, ParseResult, Parser, UnexpectedDataType}, parser::{DataType, ParseResult, Parser, UnexpectedDataType},
parsers::{ parsers::{
format::FormatParser,
mode::ModeParser, mode::ModeParser,
output_match::{OutputMatchParser, OutputMatchParserError}, output_match::{OutputMatchParser, OutputMatchParserError},
tearing::TearingParser, tearing::TearingParser,
@ -48,8 +49,8 @@ impl<'a> Parser for OutputParser<'a> {
table: &IndexMap<Spanned<String>, Spanned<Value>>, table: &IndexMap<Spanned<String>, Spanned<Value>>,
) -> ParseResult<Self> { ) -> ParseResult<Self> {
let mut ext = Extractor::new(self.cx, span, table); let mut ext = Extractor::new(self.cx, span, table);
let (name, match_val, x, y, scale, transform, mode, vrr_val, tearing_val) = let (name, match_val, x, y, scale, transform, mode, vrr_val, tearing_val, format_val) = ext
ext.extract(( .extract((
opt(str("name")), opt(str("name")),
val("match"), val("match"),
recover(opt(s32("x"))), recover(opt(s32("x"))),
@ -59,6 +60,7 @@ impl<'a> Parser for OutputParser<'a> {
opt(val("mode")), opt(val("mode")),
opt(val("vrr")), opt(val("vrr")),
opt(val("tearing")), opt(val("tearing")),
opt(val("format")),
))?; ))?;
let transform = match transform { let transform = match transform {
None => None, None => None,
@ -119,6 +121,18 @@ impl<'a> Parser for OutputParser<'a> {
} }
} }
} }
let mut format = None;
if let Some(value) = format_val {
match value.parse(&mut FormatParser) {
Ok(v) => format = Some(v),
Err(e) => {
log::warn!(
"Could not parse framebuffer format setting: {}",
self.cx.error(e)
);
}
}
}
Ok(Output { Ok(Output {
name: name.despan().map(|v| v.to_string()), name: name.despan().map(|v| v.to_string()),
match_: match_val.parse_map(&mut OutputMatchParser(self.cx))?, match_: match_val.parse_map(&mut OutputMatchParser(self.cx))?,
@ -129,6 +143,7 @@ impl<'a> Parser for OutputParser<'a> {
mode, mode,
vrr, vrr,
tearing, tearing,
format,
}) })
} }
} }

View file

@ -573,6 +573,9 @@ impl Output {
c.set_tearing_mode(mode); c.set_tearing_mode(mode);
} }
} }
if let Some(format) = self.format {
c.set_format(format);
}
} }
} }

View file

@ -757,6 +757,40 @@
} }
] ]
}, },
"Format": {
"type": "string",
"description": "A graphics format.\n\nThese formats are documented in https://github.com/torvalds/linux/blob/master/include/uapi/drm/drm_fourcc.h\n\n- Example:\n\n ```toml\n [[outputs]]\n match.serial-number = \"33K03894SL0\"\n format = \"rgb565\"\n ```\n",
"enum": [
"argb8888",
"xrgb8888",
"abgr8888",
"xbgr8888",
"r8",
"gr88",
"rgb888",
"bgr888",
"rgba4444",
"rgbx4444",
"bgra4444",
"bgrx4444",
"rgb565",
"bgr565",
"rgba5551",
"rgbx5551",
"bgra5551",
"bgrx5551",
"argb1555",
"xrgb1555",
"argb2101010",
"xrgb2101010",
"abgr2101010",
"xbgr2101010",
"abgr16161616",
"xbgr16161616",
"abgr16161616f",
"xbgr16161616f"
]
},
"GfxApi": { "GfxApi": {
"type": "string", "type": "string",
"description": "A graphics API used for rendering.", "description": "A graphics API used for rendering.",
@ -1066,6 +1100,10 @@
"tearing": { "tearing": {
"description": "Configures the tearing settings of this output.\n\nBy default, the tearing mode is `variant3`.\n\n- Example:\n\n ```toml\n [[outputs]]\n match.serial-number = \"33K03894SL0\"\n tearing.mode = \"never\"\n ```\n", "description": "Configures the tearing settings of this output.\n\nBy default, the tearing mode is `variant3`.\n\n- Example:\n\n ```toml\n [[outputs]]\n match.serial-number = \"33K03894SL0\"\n tearing.mode = \"never\"\n ```\n",
"$ref": "#/$defs/Tearing" "$ref": "#/$defs/Tearing"
},
"format": {
"description": "Configures the framebuffer format of this output.\n\nBy default, the format is `xrgb8888`.\n\n- Example:\n\n ```toml\n [[outputs]]\n match.serial-number = \"33K03894SL0\"\n format = \"rgb565\"\n ```\n",
"$ref": "#/$defs/Format"
} }
}, },
"required": [ "required": [

View file

@ -1507,6 +1507,111 @@ The table has the following fields:
The value of this field should be a boolean. The value of this field should be a boolean.
<a name="types-Format"></a>
### `Format`
A graphics format.
These formats are documented in https://github.com/torvalds/linux/blob/master/include/uapi/drm/drm_fourcc.h
- Example:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
format = "rgb565"
```
Values of this type should be strings.
The string should have one of the following values:
- `argb8888`:
- `xrgb8888`:
- `abgr8888`:
- `xbgr8888`:
- `r8`:
- `gr88`:
- `rgb888`:
- `bgr888`:
- `rgba4444`:
- `rgbx4444`:
- `bgra4444`:
- `bgrx4444`:
- `rgb565`:
- `bgr565`:
- `rgba5551`:
- `rgbx5551`:
- `bgra5551`:
- `bgrx5551`:
- `argb1555`:
- `xrgb1555`:
- `argb2101010`:
- `xrgb2101010`:
- `abgr2101010`:
- `xbgr2101010`:
- `abgr16161616`:
- `xbgr16161616`:
- `abgr16161616f`:
- `xbgr16161616f`:
<a name="types-GfxApi"></a> <a name="types-GfxApi"></a>
### `GfxApi` ### `GfxApi`
@ -2283,6 +2388,22 @@ The table has the following fields:
The value of this field should be a [Tearing](#types-Tearing). The value of this field should be a [Tearing](#types-Tearing).
- `format` (optional):
Configures the framebuffer format of this output.
By default, the format is `xrgb8888`.
- Example:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
format = "rgb565"
```
The value of this field should be a [Format](#types-Format).
<a name="types-OutputMatch"></a> <a name="types-OutputMatch"></a>
### `OutputMatch` ### `OutputMatch`

View file

@ -1606,6 +1606,21 @@ Output:
match.serial-number = "33K03894SL0" match.serial-number = "33K03894SL0"
tearing.mode = "never" tearing.mode = "never"
``` ```
format:
ref: Format
required: false
description: |
Configures the framebuffer format of this output.
By default, the format is `xrgb8888`.
- Example:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
format = "rgb565"
```
Transform: Transform:
@ -2490,3 +2505,76 @@ Libei:
Even if the socket is disabled, application can still request access via the portal. Even if the socket is disabled, application can still request access via the portal.
The default is `false`. The default is `false`.
Format:
description: |
A graphics format.
These formats are documented in https://github.com/torvalds/linux/blob/master/include/uapi/drm/drm_fourcc.h
- Example:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
format = "rgb565"
```
kind: string
values:
- value: argb8888
description: ""
- value: xrgb8888
description: ""
- value: abgr8888
description: ""
- value: xbgr8888
description: ""
- value: r8
description: ""
- value: gr88
description: ""
- value: rgb888
description: ""
- value: bgr888
description: ""
- value: rgba4444
description: ""
- value: rgbx4444
description: ""
- value: bgra4444
description: ""
- value: bgrx4444
description: ""
- value: rgb565
description: ""
- value: bgr565
description: ""
- value: rgba5551
description: ""
- value: rgbx5551
description: ""
- value: bgra5551
description: ""
- value: bgrx5551
description: ""
- value: argb1555
description: ""
- value: xrgb1555
description: ""
- value: argb2101010
description: ""
- value: xrgb2101010
description: ""
- value: abgr2101010
description: ""
- value: xbgr2101010
description: ""
- value: abgr16161616
description: ""
- value: xbgr16161616
description: ""
- value: abgr16161616f
description: ""
- value: xbgr16161616f
description: ""

View file

@ -70,6 +70,11 @@ request set_tearing_mode (since = 3) {
mode: u32, mode: u32,
} }
request set_fb_format (since = 8) {
output: str,
format: str,
}
# events # events
event global { event global {
@ -141,3 +146,8 @@ event vrr_cursor_hz (since = 2) {
event tearing_state (since = 3) { event tearing_state (since = 3) {
mode: u32, mode: u32,
} }
event fb_format (since = 8) {
name: str,
current: u32,
}