Merge pull request #499 from mahkoh/jorth/output-transaction
backend: implement output transactions
This commit is contained in:
commit
5375dee007
28 changed files with 2995 additions and 1468 deletions
|
|
@ -1,6 +1,10 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::SpawnedFuture,
|
async_engine::SpawnedFuture,
|
||||||
|
backend::transaction::{
|
||||||
|
BackendConnectorTransaction, BackendConnectorTransactionError,
|
||||||
|
BackendConnectorTransactionType, BackendConnectorTransactionTypeDyn,
|
||||||
|
},
|
||||||
cmm::cmm_primaries::Primaries,
|
cmm::cmm_primaries::Primaries,
|
||||||
drm_feedback::DrmFeedback,
|
drm_feedback::DrmFeedback,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
|
|
@ -30,11 +34,14 @@ use {
|
||||||
any::Any,
|
any::Any,
|
||||||
error::Error,
|
error::Error,
|
||||||
fmt::{Debug, Display, Formatter},
|
fmt::{Debug, Display, Formatter},
|
||||||
|
hash::Hash,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
},
|
},
|
||||||
uapi::{OwnedFd, c},
|
uapi::{OwnedFd, c},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod transaction;
|
||||||
|
|
||||||
linear_ids!(ConnectorIds, ConnectorId);
|
linear_ids!(ConnectorIds, ConnectorId);
|
||||||
linear_ids!(InputDeviceIds, InputDeviceId);
|
linear_ids!(InputDeviceIds, InputDeviceId);
|
||||||
linear_ids!(DrmDeviceIds, DrmDeviceId);
|
linear_ids!(DrmDeviceIds, DrmDeviceId);
|
||||||
|
|
@ -49,10 +56,6 @@ pub trait Backend: Any {
|
||||||
let _ = vtnr;
|
let _ = vtnr;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_idle(&self, idle: bool) {
|
|
||||||
let _ = idle;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn import_environment(&self) -> bool {
|
fn import_environment(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
@ -82,21 +85,31 @@ impl Mode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for Mode {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}x{}@{}",
|
||||||
|
self.width,
|
||||||
|
self.height,
|
||||||
|
self.refresh_rate_millihz as f64 / 1000.0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MonitorInfo {
|
pub struct MonitorInfo {
|
||||||
pub modes: Vec<Mode>,
|
pub modes: Vec<Mode>,
|
||||||
pub output_id: Rc<OutputId>,
|
pub output_id: Rc<OutputId>,
|
||||||
pub initial_mode: Mode,
|
|
||||||
pub width_mm: i32,
|
pub width_mm: i32,
|
||||||
pub height_mm: i32,
|
pub height_mm: i32,
|
||||||
pub non_desktop: bool,
|
pub non_desktop: bool,
|
||||||
pub vrr_capable: bool,
|
pub vrr_capable: bool,
|
||||||
pub transfer_functions: Vec<BackendTransferFunction>,
|
pub transfer_functions: Vec<BackendTransferFunction>,
|
||||||
pub transfer_function: BackendTransferFunction,
|
|
||||||
pub color_spaces: Vec<BackendColorSpace>,
|
pub color_spaces: Vec<BackendColorSpace>,
|
||||||
pub color_space: BackendColorSpace,
|
|
||||||
pub primaries: Primaries,
|
pub primaries: Primaries,
|
||||||
pub luminance: Option<BackendLuminance>,
|
pub luminance: Option<BackendLuminance>,
|
||||||
|
pub state: BackendConnectorState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
|
@ -111,41 +124,35 @@ impl Display for ConnectorKernelId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Connector {
|
pub trait Connector: Any {
|
||||||
fn id(&self) -> ConnectorId;
|
fn id(&self) -> ConnectorId;
|
||||||
fn kernel_id(&self) -> ConnectorKernelId;
|
fn kernel_id(&self) -> ConnectorKernelId;
|
||||||
fn event(&self) -> Option<ConnectorEvent>;
|
fn event(&self) -> Option<ConnectorEvent>;
|
||||||
fn on_change(&self, cb: Rc<dyn Fn()>);
|
fn on_change(&self, cb: Rc<dyn Fn()>);
|
||||||
fn damage(&self);
|
fn damage(&self);
|
||||||
fn drm_dev(&self) -> Option<DrmDeviceId>;
|
fn drm_dev(&self) -> Option<DrmDeviceId>;
|
||||||
fn enabled(&self) -> bool {
|
fn effectively_locked(&self) -> bool;
|
||||||
true
|
|
||||||
}
|
|
||||||
fn set_enabled(&self, enabled: bool) {
|
|
||||||
let _ = enabled;
|
|
||||||
}
|
|
||||||
fn drm_feedback(&self) -> Option<Rc<DrmFeedback>> {
|
fn drm_feedback(&self) -> Option<Rc<DrmFeedback>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn set_mode(&self, mode: Mode);
|
|
||||||
fn set_non_desktop_override(&self, non_desktop: Option<bool>) {
|
|
||||||
let _ = non_desktop;
|
|
||||||
}
|
|
||||||
fn drm_object_id(&self) -> Option<DrmConnector> {
|
fn drm_object_id(&self) -> Option<DrmConnector> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn set_vrr_enabled(&self, enabled: bool) {
|
fn before_non_desktop_override_update(&self, overrd: Option<bool>) {
|
||||||
let _ = enabled;
|
let _ = overrd;
|
||||||
}
|
}
|
||||||
fn set_tearing_enabled(&self, enabled: bool) {
|
fn transaction_type(&self) -> Box<dyn BackendConnectorTransactionTypeDyn> {
|
||||||
let _ = enabled;
|
#[derive(Hash, Eq, PartialEq)]
|
||||||
|
struct UnimplementedConnectorTransactionType;
|
||||||
|
impl BackendConnectorTransactionType for UnimplementedConnectorTransactionType {}
|
||||||
|
Box::new(UnimplementedConnectorTransactionType)
|
||||||
}
|
}
|
||||||
fn set_fb_format(&self, format: &'static Format) {
|
fn create_transaction(
|
||||||
let _ = format;
|
&self,
|
||||||
}
|
) -> Result<Box<dyn BackendConnectorTransaction>, BackendConnectorTransactionError> {
|
||||||
fn set_colors(&self, bcs: BackendColorSpace, btf: BackendTransferFunction) {
|
Err(BackendConnectorTransactionError::TransactionsNotSupported(
|
||||||
let _ = bcs;
|
self.kernel_id(),
|
||||||
let _ = btf;
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,12 +162,10 @@ pub enum ConnectorEvent {
|
||||||
HardwareCursor(Option<Rc<dyn HardwareCursor>>),
|
HardwareCursor(Option<Rc<dyn HardwareCursor>>),
|
||||||
Disconnected,
|
Disconnected,
|
||||||
Removed,
|
Removed,
|
||||||
ModeChanged(Mode),
|
|
||||||
Unavailable,
|
Unavailable,
|
||||||
Available,
|
Available,
|
||||||
VrrChanged(bool),
|
State(BackendConnectorState),
|
||||||
FormatsChanged(Rc<Vec<&'static Format>>, &'static Format),
|
FormatsChanged(Rc<Vec<&'static Format>>),
|
||||||
ColorsChanged(BackendColorSpace, BackendTransferFunction),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HardwareCursorUpdate {
|
pub trait HardwareCursorUpdate {
|
||||||
|
|
@ -570,3 +575,23 @@ impl BackendColorSpace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linear_ids!(
|
||||||
|
BackendConnectorStateSerials,
|
||||||
|
BackendConnectorStateSerial,
|
||||||
|
u64
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct BackendConnectorState {
|
||||||
|
pub serial: BackendConnectorStateSerial,
|
||||||
|
pub enabled: bool,
|
||||||
|
pub active: bool,
|
||||||
|
pub mode: Mode,
|
||||||
|
pub non_desktop_override: Option<bool>,
|
||||||
|
pub vrr: bool,
|
||||||
|
pub tearing: bool,
|
||||||
|
pub format: &'static Format,
|
||||||
|
pub color_space: BackendColorSpace,
|
||||||
|
pub transfer_function: BackendTransferFunction,
|
||||||
|
}
|
||||||
|
|
|
||||||
219
src/backend/transaction.rs
Normal file
219
src/backend/transaction.rs
Normal file
|
|
@ -0,0 +1,219 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
backend::{
|
||||||
|
BackendColorSpace, BackendConnectorState, BackendTransferFunction, Connector,
|
||||||
|
ConnectorKernelId, Mode,
|
||||||
|
},
|
||||||
|
backends::metal::MetalError,
|
||||||
|
utils::{errorfmt::ErrorFmt, hash_map_ext::HashMapExt},
|
||||||
|
video::drm::DrmError,
|
||||||
|
},
|
||||||
|
ahash::AHashMap,
|
||||||
|
std::{
|
||||||
|
any::{Any, TypeId},
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
collections::hash_map::Entry,
|
||||||
|
hash::{Hash, Hasher},
|
||||||
|
rc::Rc,
|
||||||
|
},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait BackendConnectorTransactionType: Hash + Eq + Any {}
|
||||||
|
|
||||||
|
pub trait BackendConnectorTransactionTypeDyn: Any {
|
||||||
|
fn eq(&self, other: &dyn BackendConnectorTransactionTypeDyn) -> bool;
|
||||||
|
fn hash(&self, hasher: &mut dyn Hasher);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> BackendConnectorTransactionTypeDyn for T
|
||||||
|
where
|
||||||
|
T: BackendConnectorTransactionType,
|
||||||
|
{
|
||||||
|
fn eq(&self, other: &dyn BackendConnectorTransactionTypeDyn) -> bool {
|
||||||
|
let Some(other) = (other as &dyn Any).downcast_ref::<Self>() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
self.eq(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash(&self, hasher: &mut dyn Hasher) {
|
||||||
|
struct BufHasher<'a> {
|
||||||
|
buf: Vec<u8>,
|
||||||
|
clear: Cell<bool>,
|
||||||
|
any: Cell<bool>,
|
||||||
|
hasher: RefCell<&'a mut dyn Hasher>,
|
||||||
|
}
|
||||||
|
impl Hasher for BufHasher<'_> {
|
||||||
|
fn finish(&self) -> u64 {
|
||||||
|
let hasher = &mut *self.hasher.borrow_mut();
|
||||||
|
if self.any.take() {
|
||||||
|
self.clear.set(true);
|
||||||
|
hasher.write(&self.buf);
|
||||||
|
}
|
||||||
|
hasher.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, bytes: &[u8]) {
|
||||||
|
if self.clear.take() {
|
||||||
|
self.buf.clear();
|
||||||
|
}
|
||||||
|
self.any.set(true);
|
||||||
|
self.buf.extend_from_slice(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut hasher = BufHasher {
|
||||||
|
buf: Default::default(),
|
||||||
|
clear: Cell::new(false),
|
||||||
|
any: Cell::new(false),
|
||||||
|
hasher: RefCell::new(hasher),
|
||||||
|
};
|
||||||
|
TypeId::of::<Self>().hash(&mut hasher);
|
||||||
|
self.hash(&mut hasher)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for dyn BackendConnectorTransactionTypeDyn {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.eq(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for dyn BackendConnectorTransactionTypeDyn {}
|
||||||
|
|
||||||
|
impl Hash for dyn BackendConnectorTransactionTypeDyn {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum BackendConnectorTransactionError {
|
||||||
|
#[error("The underlying DRM device of connector {} no longer exists", .0)]
|
||||||
|
MissingDrmDevice(ConnectorKernelId),
|
||||||
|
#[error("Connector {} does not support transactions", .0)]
|
||||||
|
TransactionsNotSupported(ConnectorKernelId),
|
||||||
|
#[error("Connector {} is not supported by this transaction", .0)]
|
||||||
|
UnsupportedConnectorType(ConnectorKernelId),
|
||||||
|
#[error("Connector {} cannot be modified because it is leased", .0)]
|
||||||
|
LeasedConnector(ConnectorKernelId),
|
||||||
|
#[error("Connector {} does not exist", .0)]
|
||||||
|
UnknownConnector(ConnectorKernelId),
|
||||||
|
#[error("Cannot initialize connector {} because no CRTC is available", .0)]
|
||||||
|
NoCrtcForConnector(ConnectorKernelId),
|
||||||
|
#[error("Cannot initialize connector {} because no primary plane is available", .0)]
|
||||||
|
NoPrimaryPlaneForConnector(ConnectorKernelId),
|
||||||
|
#[error("Connector {} does not support the requested mode {}", .0, .1)]
|
||||||
|
UnsupportedMode(ConnectorKernelId, Mode),
|
||||||
|
#[error("Connector {} does not support VRR", .0)]
|
||||||
|
NotVrrCapable(ConnectorKernelId),
|
||||||
|
#[error("Connector {} does not support tearing", .0)]
|
||||||
|
TearingNotSupported(ConnectorKernelId),
|
||||||
|
#[error("Connector {} does not support color space {:?}", .0, .1)]
|
||||||
|
ColorSpaceNotSupported(ConnectorKernelId, BackendColorSpace),
|
||||||
|
#[error("Connector {} does not support transfer function {:?}", .0, .1)]
|
||||||
|
TransferFunctionNotSupported(ConnectorKernelId, BackendTransferFunction),
|
||||||
|
#[error("Could not create an hdr metadata blob")]
|
||||||
|
CreateHdrMetadataBlob(#[source] DrmError),
|
||||||
|
#[error("Could not create a mode blob")]
|
||||||
|
CreateModeBlob(#[source] DrmError),
|
||||||
|
#[error("Could not allocate buffers for connector {}", .0)]
|
||||||
|
AllocateScanoutBuffers(ConnectorKernelId, #[source] Box<MetalError>),
|
||||||
|
#[error("Test commit failed")]
|
||||||
|
AtomicTestFailed(#[source] DrmError),
|
||||||
|
#[error("Commit failed")]
|
||||||
|
AtomicCommitFailed(#[source] DrmError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BackendConnectorTransaction {
|
||||||
|
fn add(
|
||||||
|
&mut self,
|
||||||
|
connector: &Rc<dyn Connector>,
|
||||||
|
change: BackendConnectorState,
|
||||||
|
) -> Result<(), BackendConnectorTransactionError>;
|
||||||
|
|
||||||
|
fn prepare(
|
||||||
|
self: Box<Self>,
|
||||||
|
) -> Result<Box<dyn BackendPreparedConnectorTransaction>, BackendConnectorTransactionError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BackendPreparedConnectorTransaction {
|
||||||
|
fn apply(
|
||||||
|
self: Box<Self>,
|
||||||
|
) -> Result<Box<dyn BackendAppliedConnectorTransaction>, BackendConnectorTransactionError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BackendAppliedConnectorTransaction {
|
||||||
|
fn commit(self: Box<Self>);
|
||||||
|
|
||||||
|
fn rollback(self: Box<Self>) -> Result<(), BackendConnectorTransactionError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ConnectorTransaction {
|
||||||
|
parts:
|
||||||
|
AHashMap<Box<dyn BackendConnectorTransactionTypeDyn>, Box<dyn BackendConnectorTransaction>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct PreparedConnectorTransaction {
|
||||||
|
parts: Vec<Box<dyn BackendPreparedConnectorTransaction>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct AppliedConnectorTransaction {
|
||||||
|
parts: Vec<Box<dyn BackendAppliedConnectorTransaction>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConnectorTransaction {
|
||||||
|
pub fn add(
|
||||||
|
&mut self,
|
||||||
|
connector: &Rc<dyn Connector>,
|
||||||
|
change: BackendConnectorState,
|
||||||
|
) -> Result<(), BackendConnectorTransactionError> {
|
||||||
|
let ty = connector.transaction_type();
|
||||||
|
let tran = match self.parts.entry(ty) {
|
||||||
|
Entry::Occupied(v) => v.into_mut(),
|
||||||
|
Entry::Vacant(v) => v.insert(connector.create_transaction()?),
|
||||||
|
};
|
||||||
|
tran.add(connector, change)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepare(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<PreparedConnectorTransaction, BackendConnectorTransactionError> {
|
||||||
|
let mut new = vec![];
|
||||||
|
for tran in self.parts.drain_values() {
|
||||||
|
new.push(tran.prepare()?);
|
||||||
|
}
|
||||||
|
Ok(PreparedConnectorTransaction { parts: new })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PreparedConnectorTransaction {
|
||||||
|
pub fn apply(self) -> Result<AppliedConnectorTransaction, BackendConnectorTransactionError> {
|
||||||
|
let mut applied = AppliedConnectorTransaction::default();
|
||||||
|
for tran in self.parts {
|
||||||
|
applied.parts.push(tran.apply()?);
|
||||||
|
}
|
||||||
|
Ok(applied)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppliedConnectorTransaction {
|
||||||
|
pub fn commit(mut self) {
|
||||||
|
for tran in self.parts.drain(..) {
|
||||||
|
tran.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for AppliedConnectorTransaction {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
for tran in self.parts.drain(..).rev() {
|
||||||
|
if let Err(e) = tran.rollback() {
|
||||||
|
log::error!("Could not roll back transaction: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::SpawnedFuture,
|
async_engine::SpawnedFuture,
|
||||||
backend::{
|
backend::{
|
||||||
Backend, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId, Mode,
|
Backend, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId,
|
||||||
},
|
},
|
||||||
video::drm::ConnectorType,
|
video::drm::ConnectorType,
|
||||||
},
|
},
|
||||||
|
|
@ -49,7 +49,7 @@ impl Connector for DummyOutput {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_mode(&self, _mode: Mode) {
|
fn effectively_locked(&self) -> bool {
|
||||||
// nothing
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
mod input;
|
mod input;
|
||||||
mod monitor;
|
mod monitor;
|
||||||
mod present;
|
mod present;
|
||||||
|
mod transaction;
|
||||||
mod video;
|
mod video;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
|
|
@ -9,7 +10,7 @@ use {
|
||||||
backend::{
|
backend::{
|
||||||
Backend, InputDevice, InputDeviceAccelProfile, InputDeviceCapability,
|
Backend, InputDevice, InputDeviceAccelProfile, InputDeviceCapability,
|
||||||
InputDeviceClickMethod, InputDeviceGroupId, InputDeviceId, InputEvent, KeyState,
|
InputDeviceClickMethod, InputDeviceGroupId, InputDeviceId, InputEvent, KeyState,
|
||||||
TransformMatrix,
|
TransformMatrix, transaction::BackendConnectorTransactionError,
|
||||||
},
|
},
|
||||||
backends::metal::video::{
|
backends::metal::video::{
|
||||||
MetalDrmDeviceData, MetalLeaseData, MetalRenderContext, PendingDrmDevice,
|
MetalDrmDeviceData, MetalLeaseData, MetalRenderContext, PendingDrmDevice,
|
||||||
|
|
@ -47,10 +48,7 @@ use {
|
||||||
smallmap::SmallMap,
|
smallmap::SmallMap,
|
||||||
syncqueue::SyncQueue,
|
syncqueue::SyncQueue,
|
||||||
},
|
},
|
||||||
video::{
|
video::{drm::DrmError, gbm::GbmError},
|
||||||
drm::{DRM_MODE_ATOMIC_ALLOW_MODESET, DrmError},
|
|
||||||
gbm::GbmError,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
bstr::ByteSlice,
|
bstr::ByteSlice,
|
||||||
std::{
|
std::{
|
||||||
|
|
@ -87,12 +85,6 @@ pub enum MetalError {
|
||||||
UpdateProperties(#[source] DrmError),
|
UpdateProperties(#[source] DrmError),
|
||||||
#[error("Could not create a render context")]
|
#[error("Could not create a render context")]
|
||||||
CreateRenderContex(#[source] GfxError),
|
CreateRenderContex(#[source] GfxError),
|
||||||
#[error("Cannot initialize connector because no CRTC is available")]
|
|
||||||
NoCrtcForConnector,
|
|
||||||
#[error("Cannot initialize connector because no primary plane is available")]
|
|
||||||
NoPrimaryPlaneForConnector,
|
|
||||||
#[error("Cannot initialize connector because no mode is available")]
|
|
||||||
NoModeForConnector,
|
|
||||||
#[error("Could not allocate scanout buffer")]
|
#[error("Could not allocate scanout buffer")]
|
||||||
ScanoutBuffer(#[source] GbmError),
|
ScanoutBuffer(#[source] GbmError),
|
||||||
#[error("addfb2 failed")]
|
#[error("addfb2 failed")]
|
||||||
|
|
@ -104,7 +96,7 @@ pub enum MetalError {
|
||||||
#[error("Could not import an image into the graphics API")]
|
#[error("Could not import an image into the graphics API")]
|
||||||
ImportImage(#[source] GfxError),
|
ImportImage(#[source] GfxError),
|
||||||
#[error("Could not perform modeset")]
|
#[error("Could not perform modeset")]
|
||||||
Modeset(#[source] DrmError),
|
Modeset(#[source] BackendConnectorTransactionError),
|
||||||
#[error("Could not enable atomic modesetting")]
|
#[error("Could not enable atomic modesetting")]
|
||||||
AtomicModesetting(#[source] OsError),
|
AtomicModesetting(#[source] OsError),
|
||||||
#[error("Could not inspect a plane")]
|
#[error("Could not inspect a plane")]
|
||||||
|
|
@ -137,6 +129,12 @@ pub enum MetalError {
|
||||||
Clear(#[source] GfxError),
|
Clear(#[source] GfxError),
|
||||||
#[error("The present configuration is out of date")]
|
#[error("The present configuration is out of date")]
|
||||||
OutOfDate,
|
OutOfDate,
|
||||||
|
#[error("Could not add connector to transaction")]
|
||||||
|
AddToTransaction(#[source] BackendConnectorTransactionError),
|
||||||
|
#[error("Could not calculate DRM state")]
|
||||||
|
CalculateDrmState(#[source] BackendConnectorTransactionError),
|
||||||
|
#[error("Could not calculate DRM change set")]
|
||||||
|
CalculateDrmChange(#[source] BackendConnectorTransactionError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MetalBackend {
|
pub struct MetalBackend {
|
||||||
|
|
@ -204,6 +202,7 @@ impl Backend for MetalBackend {
|
||||||
dev.futures.clear();
|
dev.futures.clear();
|
||||||
for crtc in dev.dev.crtcs.values() {
|
for crtc in dev.dev.crtcs.values() {
|
||||||
crtc.connector.take();
|
crtc.connector.take();
|
||||||
|
crtc.pending_flip.take();
|
||||||
}
|
}
|
||||||
dev.dev.handle_events.handle_events.take();
|
dev.dev.handle_events.handle_events.take();
|
||||||
dev.dev.on_change.clear();
|
dev.dev.on_change.clear();
|
||||||
|
|
@ -242,36 +241,6 @@ impl Backend for MetalBackend {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_idle(&self, idle: bool) {
|
|
||||||
let devices = self.device_holder.drm_devices.lock();
|
|
||||||
for device in devices.values() {
|
|
||||||
let mut change = device.dev.master.change();
|
|
||||||
for connector in device.connectors.lock().values() {
|
|
||||||
if let Some(crtc) = connector.crtc.get()
|
|
||||||
&& idle == crtc.active.value.get()
|
|
||||||
{
|
|
||||||
crtc.active.value.set(!idle);
|
|
||||||
change.change_object(crtc.id, |c| {
|
|
||||||
c.change(crtc.active.id, (!idle) as _);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Err(e) = change.commit(DRM_MODE_ATOMIC_ALLOW_MODESET, 0) {
|
|
||||||
log::error!("Could not set monitors idle/not idle: {}", ErrorFmt(e));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if idle {
|
|
||||||
self.state.set_backend_idle(true);
|
|
||||||
} else {
|
|
||||||
for device in devices.values() {
|
|
||||||
for connector in device.connectors.lock().values() {
|
|
||||||
connector.schedule_present();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn import_environment(&self) -> bool {
|
fn import_environment(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use {
|
||||||
backend::Connector,
|
backend::Connector,
|
||||||
backends::metal::{
|
backends::metal::{
|
||||||
MetalError,
|
MetalError,
|
||||||
|
transaction::{DrmConnectorState, DrmPlaneState},
|
||||||
video::{
|
video::{
|
||||||
MetalConnector, MetalCrtc, MetalHardwareCursorChange, MetalPlane, RenderBuffer,
|
MetalConnector, MetalCrtc, MetalHardwareCursorChange, MetalPlane, RenderBuffer,
|
||||||
},
|
},
|
||||||
|
|
@ -22,18 +23,20 @@ use {
|
||||||
dmabuf::DmaBufId,
|
dmabuf::DmaBufId,
|
||||||
drm::{
|
drm::{
|
||||||
DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_ASYNC, DRM_MODE_PAGE_FLIP_EVENT,
|
DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_ASYNC, DRM_MODE_PAGE_FLIP_EVENT,
|
||||||
DrmError, DrmFramebuffer,
|
DrmCrtc, DrmError, DrmFb, DrmFramebuffer, DrmObject,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
arrayvec::ArrayVec,
|
||||||
std::rc::{Rc, Weak},
|
std::rc::{Rc, Weak},
|
||||||
uapi::c,
|
uapi::{OwnedFd, c},
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Latched {
|
struct Latched {
|
||||||
pass: GfxRenderPass,
|
pass: GfxRenderPass,
|
||||||
damage_count: u64,
|
damage_count: u64,
|
||||||
damage: Region,
|
damage: Region,
|
||||||
|
locked: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -68,11 +71,18 @@ pub struct PresentFb {
|
||||||
tex: Rc<dyn GfxTexture>,
|
tex: Rc<dyn GfxTexture>,
|
||||||
direct_scanout_data: Option<DirectScanoutData>,
|
direct_scanout_data: Option<DirectScanoutData>,
|
||||||
sync_file: Option<SyncFile>,
|
sync_file: Option<SyncFile>,
|
||||||
|
pub locked: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CursorProgramming {
|
#[derive(Debug)]
|
||||||
|
struct CursorProgramming {
|
||||||
|
plane: Rc<MetalPlane>,
|
||||||
|
ty: CursorProgrammingType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum CursorProgrammingType {
|
||||||
Enable {
|
Enable {
|
||||||
plane: Rc<MetalPlane>,
|
|
||||||
fb: Rc<DrmFramebuffer>,
|
fb: Rc<DrmFramebuffer>,
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
|
|
@ -80,9 +90,12 @@ enum CursorProgramming {
|
||||||
height: i32,
|
height: i32,
|
||||||
swap: bool,
|
swap: bool,
|
||||||
},
|
},
|
||||||
Disable {
|
Disable,
|
||||||
plane: Rc<MetalPlane>,
|
}
|
||||||
},
|
|
||||||
|
struct ChangedPlane {
|
||||||
|
plane: Rc<MetalPlane>,
|
||||||
|
state: DrmPlaneState,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_PRE_COMMIT_MARGIN: u64 = 16_000_000; // 16ms
|
pub const DEFAULT_PRE_COMMIT_MARGIN: u64 = 16_000_000; // 16ms
|
||||||
|
|
@ -101,13 +114,17 @@ impl MetalConnector {
|
||||||
let mut max = 0;
|
let mut max = 0;
|
||||||
loop {
|
loop {
|
||||||
self.present_trigger.triggered().await;
|
self.present_trigger.triggered().await;
|
||||||
if !self.can_present.get() {
|
if !self.buffers_idle.get() || !self.crtc_idle.get() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
let Some(crtc) = self.crtc.get() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let Some(node) = self.state.root.outputs.get(&self.connector_id) else {
|
let Some(node) = self.state.root.outputs.get(&self.connector_id) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let mut expected_sequence = self.sequence.get() + 1;
|
let version = self.version.get();
|
||||||
|
let mut expected_sequence = crtc.sequence.get() + 1;
|
||||||
let mut start = Time::now_unchecked();
|
let mut start = Time::now_unchecked();
|
||||||
let use_frame_scheduling = !self.try_async_flip();
|
let use_frame_scheduling = !self.try_async_flip();
|
||||||
if use_frame_scheduling {
|
if use_frame_scheduling {
|
||||||
|
|
@ -132,7 +149,11 @@ impl MetalConnector {
|
||||||
};
|
};
|
||||||
node.before_latch(flip).await;
|
node.before_latch(flip).await;
|
||||||
}
|
}
|
||||||
if let Err(e) = self.present_once(&node).await {
|
if version != self.version.get() {
|
||||||
|
self.present_trigger.trigger();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Err(e) = self.present_once(&node, &crtc).await {
|
||||||
log::error!("Could not present: {}", ErrorFmt(e));
|
log::error!("Could not present: {}", ErrorFmt(e));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -152,19 +173,19 @@ impl MetalConnector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn present_once(&self, node: &Rc<OutputNode>) -> Result<(), MetalError> {
|
async fn present_once(
|
||||||
|
self: &Rc<Self>,
|
||||||
|
node: &Rc<OutputNode>,
|
||||||
|
crtc: &Rc<MetalCrtc>,
|
||||||
|
) -> Result<(), MetalError> {
|
||||||
let version = self.version.get();
|
let version = self.version.get();
|
||||||
if !self.can_present.get() {
|
if !self.buffers_idle.get() || !self.crtc_idle.get() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if !self.backend.check_render_context(&self.dev) {
|
if !self.backend.check_render_context(&self.dev) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let crtc = match self.crtc.get() {
|
if !crtc.drm_state.borrow().active {
|
||||||
Some(crtc) => crtc,
|
|
||||||
_ => return Ok(()),
|
|
||||||
};
|
|
||||||
if !crtc.active.value.get() {
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let plane = match self.primary_plane.get() {
|
let plane = match self.primary_plane.get() {
|
||||||
|
|
@ -175,7 +196,9 @@ impl MetalConnector {
|
||||||
Some(b) => b,
|
Some(b) => b,
|
||||||
_ => return Ok(()),
|
_ => return Ok(()),
|
||||||
};
|
};
|
||||||
let buffer = &buffers[self.next_buffer.get() % buffers.len()];
|
let mut connector_drm_state = self.display.borrow().drm_state.clone();
|
||||||
|
let next_buffer_idx = ((connector_drm_state.fb_idx + 1) % buffers.len() as u64) as usize;
|
||||||
|
let buffer = &buffers[next_buffer_idx];
|
||||||
|
|
||||||
let cd = node.global.color_description.get();
|
let cd = node.global.color_description.get();
|
||||||
let linear_cd = node.global.linear_color_description.get();
|
let linear_cd = node.global.linear_color_description.get();
|
||||||
|
|
@ -183,8 +206,8 @@ impl MetalConnector {
|
||||||
if self.has_damage.get() > 0 || self.cursor_damage.get() {
|
if self.has_damage.get() > 0 || self.cursor_damage.get() {
|
||||||
node.schedule.commit_cursor();
|
node.schedule.commit_cursor();
|
||||||
}
|
}
|
||||||
self.latch_cursor(&node, &cd)?;
|
self.latch_cursor(&node, &connector_drm_state, &cd)?;
|
||||||
let cursor_programming = self.compute_cursor_programming();
|
let cursor_programming = self.compute_cursor_programming(&connector_drm_state);
|
||||||
let latched = self.latch(&node, buffer);
|
let latched = self.latch(&node, buffer);
|
||||||
node.latched(self.try_async_flip());
|
node.latched(self.try_async_flip());
|
||||||
|
|
||||||
|
|
@ -209,12 +232,15 @@ impl MetalConnector {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self.await_present_fb(present_fb.as_mut()).await;
|
self.await_present_fb(present_fb.as_mut()).await;
|
||||||
|
let mut changed_planes = ArrayVec::new();
|
||||||
let mut res = self.program_connector(
|
let mut res = self.program_connector(
|
||||||
version,
|
version,
|
||||||
&crtc,
|
&crtc,
|
||||||
&plane,
|
&plane,
|
||||||
cursor_programming.as_ref(),
|
cursor_programming.as_ref(),
|
||||||
present_fb.as_ref(),
|
present_fb.as_ref(),
|
||||||
|
&mut changed_planes,
|
||||||
|
&mut connector_drm_state,
|
||||||
);
|
);
|
||||||
if res.is_err()
|
if res.is_err()
|
||||||
&& let Some(dsd_id) = direct_scanout_id
|
&& let Some(dsd_id) = direct_scanout_id
|
||||||
|
|
@ -235,6 +261,8 @@ impl MetalConnector {
|
||||||
&plane,
|
&plane,
|
||||||
cursor_programming.as_ref(),
|
cursor_programming.as_ref(),
|
||||||
present_fb.as_ref(),
|
present_fb.as_ref(),
|
||||||
|
&mut changed_planes,
|
||||||
|
&mut connector_drm_state,
|
||||||
);
|
);
|
||||||
if res.is_ok() {
|
if res.is_ok() {
|
||||||
let mut cache = self.scanout_buffers.borrow_mut();
|
let mut cache = self.scanout_buffers.borrow_mut();
|
||||||
|
|
@ -265,35 +293,30 @@ impl MetalConnector {
|
||||||
}
|
}
|
||||||
Err(e)
|
Err(e)
|
||||||
} else {
|
} else {
|
||||||
macro_rules! apply_change {
|
crtc.pending_flip.set(Some(self.clone()));
|
||||||
($prop:expr) => {
|
self.crtc_idle.set(false);
|
||||||
if let Some(v) = $prop.pending_value.take() {
|
self.color_description.set(cd);
|
||||||
$prop.value.set(v);
|
self.display.borrow_mut().drm_state = connector_drm_state;
|
||||||
}
|
for plane in changed_planes {
|
||||||
};
|
*plane.plane.drm_state.borrow_mut() = plane.state;
|
||||||
}
|
}
|
||||||
apply_change!(plane.src_w);
|
|
||||||
apply_change!(plane.src_h);
|
|
||||||
apply_change!(plane.crtc_x);
|
|
||||||
apply_change!(plane.crtc_y);
|
|
||||||
apply_change!(plane.crtc_w);
|
|
||||||
apply_change!(plane.crtc_h);
|
|
||||||
if let Some(fb) = present_fb {
|
if let Some(fb) = present_fb {
|
||||||
self.presentation_is_zero_copy
|
self.presentation_is_zero_copy
|
||||||
.set(fb.direct_scanout_data.is_some());
|
.set(fb.direct_scanout_data.is_some());
|
||||||
if fb.direct_scanout_data.is_none() {
|
if fb.direct_scanout_data.is_none() {
|
||||||
buffer.damage_queue.clear();
|
buffer.damage_queue.clear();
|
||||||
self.next_buffer.fetch_add(1);
|
|
||||||
} else {
|
} else {
|
||||||
reset_damage();
|
reset_damage();
|
||||||
}
|
}
|
||||||
|
buffer.locked.set(fb.locked);
|
||||||
self.next_framebuffer.set(Some(fb));
|
self.next_framebuffer.set(Some(fb));
|
||||||
}
|
}
|
||||||
if let Some(CursorProgramming::Enable { swap: true, .. }) = cursor_programming {
|
if let Some(programming) = cursor_programming
|
||||||
|
&& let CursorProgrammingType::Enable { swap: true, .. } = &programming.ty
|
||||||
|
{
|
||||||
self.cursor_swap_buffer.set(false);
|
self.cursor_swap_buffer.set(false);
|
||||||
self.cursor_front_buffer.fetch_add(1);
|
|
||||||
}
|
}
|
||||||
self.can_present.set(false);
|
self.buffers_idle.set(false);
|
||||||
if let Some(latched) = latched {
|
if let Some(latched) = latched {
|
||||||
self.has_damage.fetch_sub(latched.damage_count);
|
self.has_damage.fetch_sub(latched.damage_count);
|
||||||
}
|
}
|
||||||
|
|
@ -318,7 +341,7 @@ impl MetalConnector {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_async_flip(&self) -> bool {
|
fn try_async_flip(&self) -> bool {
|
||||||
self.tearing_requested.get() && self.dev.supports_async_commit
|
self.display.borrow().persistent.state.borrow().tearing && self.dev.supports_async_commit
|
||||||
}
|
}
|
||||||
|
|
||||||
fn program_connector(
|
fn program_connector(
|
||||||
|
|
@ -328,19 +351,15 @@ impl MetalConnector {
|
||||||
plane: &Rc<MetalPlane>,
|
plane: &Rc<MetalPlane>,
|
||||||
cursor: Option<&CursorProgramming>,
|
cursor: Option<&CursorProgramming>,
|
||||||
new_fb: Option<&PresentFb>,
|
new_fb: Option<&PresentFb>,
|
||||||
|
changed_planes: &mut ArrayVec<ChangedPlane, 2>,
|
||||||
|
connector_drm_state: &mut DrmConnectorState,
|
||||||
) -> Result<(), MetalError> {
|
) -> Result<(), MetalError> {
|
||||||
zone!("program_connector");
|
zone!("program_connector");
|
||||||
let mut changes = self.master.change();
|
let mut changes = self.master.change();
|
||||||
let mut try_async_flip = self.try_async_flip();
|
let mut try_async_flip = self.try_async_flip();
|
||||||
macro_rules! change {
|
let mut drm_state = plane.drm_state.borrow().clone();
|
||||||
($c:expr, $prop:expr, $new:expr) => {{
|
changed_planes.clear();
|
||||||
if $prop.value.get() != $new {
|
let mut connector_state = connector_drm_state.clone();
|
||||||
$c.change($prop.id, $new as u64);
|
|
||||||
try_async_flip = false;
|
|
||||||
$prop.pending_value.set(Some($new));
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
if let Some(fb) = new_fb {
|
if let Some(fb) = new_fb {
|
||||||
let (crtc_x, crtc_y, crtc_w, crtc_h, src_width, src_height) =
|
let (crtc_x, crtc_y, crtc_w, crtc_h, src_width, src_height) =
|
||||||
match &fb.direct_scanout_data {
|
match &fb.direct_scanout_data {
|
||||||
|
|
@ -362,69 +381,90 @@ impl MetalConnector {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
changes.change_object(plane.id, |c| {
|
changes.change_object(plane.id, |c| {
|
||||||
c.change(plane.fb_id, fb.fb.id().0 as _);
|
c.change(plane.fb_id, fb.fb.id());
|
||||||
change!(c, plane.src_w, (src_width as u32) << 16);
|
drm_state.fb_id = fb.fb.id();
|
||||||
change!(c, plane.src_h, (src_height as u32) << 16);
|
connector_state.fb = fb.fb.id();
|
||||||
change!(c, plane.crtc_x, crtc_x);
|
connector_state.locked = fb.locked;
|
||||||
change!(c, plane.crtc_y, crtc_y);
|
if fb.direct_scanout_data.is_none() {
|
||||||
change!(c, plane.crtc_w, crtc_w);
|
connector_state.fb_idx += 1;
|
||||||
change!(c, plane.crtc_h, crtc_h);
|
|
||||||
if !try_async_flip
|
|
||||||
&& !self.dev.is_nvidia
|
|
||||||
&& let Some(sf) = self.backend.signaled_sync_file.get()
|
|
||||||
{
|
|
||||||
c.change(plane.in_fence_fd, sf.0.raw() as u64);
|
|
||||||
}
|
}
|
||||||
|
macro_rules! change {
|
||||||
|
($prop:ident, $new:expr) => {{
|
||||||
|
if drm_state.$prop != $new {
|
||||||
|
c.change(plane.$prop, $new as u64);
|
||||||
|
try_async_flip = false;
|
||||||
|
drm_state.$prop = $new;
|
||||||
|
}
|
||||||
|
connector_state.$prop = drm_state.$prop;
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
change!(src_w, (src_width as u32) << 16);
|
||||||
|
change!(src_h, (src_height as u32) << 16);
|
||||||
|
change!(crtc_x, crtc_x);
|
||||||
|
change!(crtc_y, crtc_y);
|
||||||
|
change!(crtc_w, crtc_w);
|
||||||
|
change!(crtc_h, crtc_h);
|
||||||
|
});
|
||||||
|
changed_planes.push(ChangedPlane {
|
||||||
|
plane: plane.clone(),
|
||||||
|
state: drm_state,
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
// Work around https://gitlab.freedesktop.org/drm/amd/-/issues/2186
|
|
||||||
if self.dev.is_amd
|
|
||||||
&& crtc.vrr_enabled.value.get()
|
|
||||||
&& let Some(fb) = &*self.active_framebuffer.borrow()
|
|
||||||
{
|
|
||||||
changes.change_object(plane.id, |c| {
|
|
||||||
c.change(plane.fb_id, fb.fb.id().0 as _);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if let Some(cursor) = cursor {
|
if let Some(cursor) = cursor {
|
||||||
|
let plane = &cursor.plane;
|
||||||
|
let mut drm_state = plane.drm_state.borrow().clone();
|
||||||
try_async_flip = false;
|
try_async_flip = false;
|
||||||
match cursor {
|
changes.change_object(plane.id, |c| {
|
||||||
CursorProgramming::Enable {
|
macro_rules! change {
|
||||||
plane,
|
($prop:ident, $new:expr) => {{
|
||||||
fb,
|
c.change(plane.$prop, $new);
|
||||||
x,
|
drm_state.$prop = $new;
|
||||||
y,
|
}};
|
||||||
width,
|
}
|
||||||
height,
|
match &cursor.ty {
|
||||||
..
|
CursorProgrammingType::Enable {
|
||||||
} => {
|
fb,
|
||||||
changes.change_object(plane.id, |c| {
|
x,
|
||||||
c.change(plane.fb_id, fb.id().0 as _);
|
y,
|
||||||
c.change(plane.crtc_id.id, crtc.id.0 as _);
|
width,
|
||||||
c.change(plane.crtc_x.id, *x as _);
|
height,
|
||||||
c.change(plane.crtc_y.id, *y as _);
|
swap,
|
||||||
c.change(plane.crtc_w.id, *width as _);
|
} => {
|
||||||
c.change(plane.crtc_h.id, *height as _);
|
connector_state.cursor_fb = fb.id();
|
||||||
c.change(plane.src_x.id, 0);
|
if *swap {
|
||||||
c.change(plane.src_y.id, 0);
|
connector_state.cursor_fb_idx += 1;
|
||||||
c.change(plane.src_w.id, (*width as u64) << 16);
|
}
|
||||||
c.change(plane.src_h.id, (*height as u64) << 16);
|
connector_state.cursor_x = *x;
|
||||||
|
connector_state.cursor_y = *y;
|
||||||
|
change!(fb_id, fb.id());
|
||||||
|
change!(crtc_id, crtc.id);
|
||||||
|
change!(crtc_x, *x);
|
||||||
|
change!(crtc_y, *y);
|
||||||
|
change!(crtc_w, *width);
|
||||||
|
change!(crtc_h, *height);
|
||||||
|
change!(src_x, 0);
|
||||||
|
change!(src_y, 0);
|
||||||
|
change!(src_w, (*width as u32) << 16);
|
||||||
|
change!(src_h, (*height as u32) << 16);
|
||||||
if !self.dev.is_nvidia
|
if !self.dev.is_nvidia
|
||||||
&& let Some(sf) = self.backend.signaled_sync_file.get()
|
&& let Some(sf) = self.backend.signaled_sync_file.get()
|
||||||
{
|
{
|
||||||
c.change(plane.in_fence_fd, sf.0.raw() as u64);
|
c.change(plane.in_fence_fd, sf.0.raw() as u64);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
CursorProgrammingType::Disable => {
|
||||||
|
connector_state.cursor_fb = DrmFb::NONE;
|
||||||
|
change!(fb_id, DrmFb::NONE);
|
||||||
|
change!(crtc_id, DrmCrtc::NONE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
CursorProgramming::Disable { plane } => {
|
});
|
||||||
changes.change_object(plane.id, |c| {
|
changed_planes.push(ChangedPlane {
|
||||||
c.change(plane.fb_id, 0);
|
plane: plane.clone(),
|
||||||
c.change(plane.crtc_id.id, 0);
|
state: drm_state,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let mut out_fd: c::c_int = -1;
|
||||||
if version != self.version.get() {
|
if version != self.version.get() {
|
||||||
return Err(MetalError::OutOfDate);
|
return Err(MetalError::OutOfDate);
|
||||||
}
|
}
|
||||||
|
|
@ -439,14 +479,32 @@ impl MetalConnector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.presentation_is_sync.set(true);
|
self.presentation_is_sync.set(true);
|
||||||
|
if !self.dev.is_nvidia {
|
||||||
|
if new_fb.is_some()
|
||||||
|
&& let Some(sf) = self.backend.signaled_sync_file.get()
|
||||||
|
{
|
||||||
|
changes.change_object(plane.id, |c| {
|
||||||
|
c.change(plane.in_fence_fd, sf.0.raw() as u64);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
changes.change_object(crtc.id, |c| {
|
||||||
|
c.change(crtc.out_fence_ptr, &raw mut out_fd as u64);
|
||||||
|
});
|
||||||
|
}
|
||||||
res = changes.commit(FLAGS, 0);
|
res = changes.commit(FLAGS, 0);
|
||||||
}
|
}
|
||||||
|
if res.is_ok() {
|
||||||
|
connector_state.out_fd =
|
||||||
|
(out_fd != -1).then(|| SyncFile(Rc::new(OwnedFd::new(out_fd))));
|
||||||
|
*connector_drm_state = connector_state;
|
||||||
|
}
|
||||||
res.map_err(MetalError::Commit)
|
res.map_err(MetalError::Commit)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn latch_cursor(
|
fn latch_cursor(
|
||||||
&self,
|
&self,
|
||||||
node: &Rc<OutputNode>,
|
node: &Rc<OutputNode>,
|
||||||
|
connector_drm_state: &DrmConnectorState,
|
||||||
cd: &Rc<ColorDescription>,
|
cd: &Rc<ColorDescription>,
|
||||||
) -> Result<(), MetalError> {
|
) -> Result<(), MetalError> {
|
||||||
if !self.cursor_damage.take() {
|
if !self.cursor_damage.take() {
|
||||||
|
|
@ -456,12 +514,13 @@ impl MetalConnector {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let buffers = self.cursor_buffers.get().unwrap();
|
let buffers = self.cursor_buffers.get().unwrap();
|
||||||
|
let buffer_idx = ((connector_drm_state.cursor_fb_idx + 1) % buffers.len() as u64) as usize;
|
||||||
let mut c = MetalHardwareCursorChange {
|
let mut c = MetalHardwareCursorChange {
|
||||||
cursor_enabled: self.cursor_enabled.get(),
|
cursor_enabled: self.cursor_enabled.get(),
|
||||||
cursor_swap_buffer: false,
|
cursor_swap_buffer: false,
|
||||||
cursor_x: self.cursor_x.get(),
|
cursor_x: self.cursor_x.get(),
|
||||||
cursor_y: self.cursor_y.get(),
|
cursor_y: self.cursor_y.get(),
|
||||||
cursor_buffer: &buffers[(self.cursor_front_buffer.get() + 1) % buffers.len()],
|
cursor_buffer: &buffers[buffer_idx],
|
||||||
sync_file: None,
|
sync_file: None,
|
||||||
cursor_size: (self.dev.cursor_width as _, self.dev.cursor_height as _),
|
cursor_size: (self.dev.cursor_width as _, self.dev.cursor_height as _),
|
||||||
};
|
};
|
||||||
|
|
@ -484,22 +543,25 @@ impl MetalConnector {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_cursor_programming(&self) -> Option<CursorProgramming> {
|
fn compute_cursor_programming(
|
||||||
|
&self,
|
||||||
|
connector_drm_state: &DrmConnectorState,
|
||||||
|
) -> Option<CursorProgramming> {
|
||||||
if !self.cursor_changed.get() {
|
if !self.cursor_changed.get() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let plane = self.cursor_plane.get()?;
|
let plane = self.cursor_plane.get()?;
|
||||||
let programming = if self.cursor_enabled.get() {
|
let ty = if self.cursor_enabled.get() {
|
||||||
let swap = self.cursor_swap_buffer.get();
|
let swap = self.cursor_swap_buffer.get();
|
||||||
let mut front_buffer = self.cursor_front_buffer.get();
|
|
||||||
if swap {
|
|
||||||
front_buffer = front_buffer.wrapping_add(1);
|
|
||||||
}
|
|
||||||
let buffers = self.cursor_buffers.get().unwrap();
|
let buffers = self.cursor_buffers.get().unwrap();
|
||||||
let buffer = &buffers[front_buffer % buffers.len()];
|
let mut front_buffer = connector_drm_state.cursor_fb_idx;
|
||||||
|
if swap {
|
||||||
|
front_buffer += 1;
|
||||||
|
}
|
||||||
|
let buffer_idx = (front_buffer % buffers.len() as u64) as usize;
|
||||||
|
let buffer = &buffers[buffer_idx];
|
||||||
let (width, height) = buffer.dev_fb.physical_size();
|
let (width, height) = buffer.dev_fb.physical_size();
|
||||||
CursorProgramming::Enable {
|
CursorProgrammingType::Enable {
|
||||||
plane,
|
|
||||||
fb: buffer.drm.clone(),
|
fb: buffer.drm.clone(),
|
||||||
x: self.cursor_x.get(),
|
x: self.cursor_x.get(),
|
||||||
y: self.cursor_y.get(),
|
y: self.cursor_y.get(),
|
||||||
|
|
@ -508,9 +570,9 @@ impl MetalConnector {
|
||||||
swap,
|
swap,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
CursorProgramming::Disable { plane }
|
CursorProgrammingType::Disable
|
||||||
};
|
};
|
||||||
Some(programming)
|
Some(CursorProgramming { plane, ty })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn latch(&self, node: &Rc<OutputNode>, buffer: &RenderBuffer) -> Option<Latched> {
|
fn latch(&self, node: &Rc<OutputNode>, buffer: &RenderBuffer) -> Option<Latched> {
|
||||||
|
|
@ -545,6 +607,7 @@ impl MetalConnector {
|
||||||
pass,
|
pass,
|
||||||
damage_count,
|
damage_count,
|
||||||
damage,
|
damage,
|
||||||
|
locked: self.state.lock.locked.get(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -797,6 +860,7 @@ impl MetalConnector {
|
||||||
tex,
|
tex,
|
||||||
direct_scanout_data,
|
direct_scanout_data,
|
||||||
sync_file,
|
sync_file,
|
||||||
|
locked: latched.locked,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
1145
src/backends/metal/transaction.rs
Normal file
1145
src/backends/metal/transaction.rs
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -3,15 +3,19 @@ use {
|
||||||
allocator::BufferObject,
|
allocator::BufferObject,
|
||||||
async_engine::{Phase, SpawnedFuture},
|
async_engine::{Phase, SpawnedFuture},
|
||||||
backend::{
|
backend::{
|
||||||
AXIS_120, AxisSource, Backend, BackendColorSpace, BackendDrmDevice, BackendEvent,
|
AXIS_120, AxisSource, Backend, BackendConnectorState, BackendDrmDevice, BackendEvent,
|
||||||
BackendTransferFunction, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId,
|
Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId, DrmEvent,
|
||||||
DrmDeviceId, DrmEvent, InputDevice, InputDeviceAccelProfile, InputDeviceCapability,
|
InputDevice, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceClickMethod,
|
||||||
InputDeviceClickMethod, InputDeviceId, InputEvent, KeyState, Mode, MonitorInfo,
|
InputDeviceId, InputEvent, KeyState, Mode, MonitorInfo, ScrollAxis, TransformMatrix,
|
||||||
ScrollAxis, TransformMatrix,
|
transaction::{
|
||||||
|
BackendAppliedConnectorTransaction, BackendConnectorTransaction,
|
||||||
|
BackendConnectorTransactionError, BackendConnectorTransactionType,
|
||||||
|
BackendConnectorTransactionTypeDyn, BackendPreparedConnectorTransaction,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
cmm::cmm_primaries::Primaries,
|
cmm::cmm_primaries::Primaries,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
format::XRGB8888,
|
format::{Format, XRGB8888},
|
||||||
gfx_api::{AcquireSync, GfxContext, GfxError, GfxFramebuffer, GfxTexture, ReleaseSync},
|
gfx_api::{AcquireSync, GfxContext, GfxError, GfxFramebuffer, GfxTexture, ReleaseSync},
|
||||||
ifs::wl_output::OutputId,
|
ifs::wl_output::OutputId,
|
||||||
state::State,
|
state::State,
|
||||||
|
|
@ -51,8 +55,10 @@ use {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ahash::AHashMap,
|
||||||
jay_config::video::GfxApi,
|
jay_config::video::GfxApi,
|
||||||
std::{
|
std::{
|
||||||
|
any::Any,
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
|
|
@ -120,6 +126,8 @@ pub enum XBackendError {
|
||||||
XRGB8888,
|
XRGB8888,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FORMAT: &Format = XRGB8888;
|
||||||
|
|
||||||
pub async fn create(state: &Rc<State>) -> Result<Rc<XBackend>, XBackendError> {
|
pub async fn create(state: &Rc<State>) -> Result<Rc<XBackend>, XBackendError> {
|
||||||
let c = match Xcon::connect(state).await {
|
let c = match Xcon::connect(state).await {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
|
|
@ -376,7 +384,7 @@ impl XBackend {
|
||||||
) -> Result<[XImage; 2], XBackendError> {
|
) -> Result<[XImage; 2], XBackendError> {
|
||||||
let mut images = [None, None];
|
let mut images = [None, None];
|
||||||
let formats = self.ctx.formats();
|
let formats = self.ctx.formats();
|
||||||
let format = match formats.get(&XRGB8888.drm) {
|
let format = match formats.get(&FORMAT.drm) {
|
||||||
Some(f) => f,
|
Some(f) => f,
|
||||||
None => return Err(XBackendError::XRGB8888),
|
None => return Err(XBackendError::XRGB8888),
|
||||||
};
|
};
|
||||||
|
|
@ -385,7 +393,7 @@ impl XBackend {
|
||||||
&self.state.dma_buf_ids,
|
&self.state.dma_buf_ids,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
XRGB8888,
|
FORMAT,
|
||||||
format.write_modifiers.keys(),
|
format.write_modifiers.keys(),
|
||||||
GBM_BO_USE_RENDERING,
|
GBM_BO_USE_RENDERING,
|
||||||
)?;
|
)?;
|
||||||
|
|
@ -469,6 +477,22 @@ impl XBackend {
|
||||||
cw.wid
|
cw.wid
|
||||||
};
|
};
|
||||||
let images = self.create_images(window_id, WIDTH, HEIGHT).await?;
|
let images = self.create_images(window_id, WIDTH, HEIGHT).await?;
|
||||||
|
let state = BackendConnectorState {
|
||||||
|
serial: self.state.backend_connector_state_serials.next(),
|
||||||
|
enabled: true,
|
||||||
|
active: true,
|
||||||
|
mode: Mode {
|
||||||
|
width: WIDTH,
|
||||||
|
height: HEIGHT,
|
||||||
|
refresh_rate_millihz: 60_000, // TODO
|
||||||
|
},
|
||||||
|
non_desktop_override: None,
|
||||||
|
vrr: false,
|
||||||
|
tearing: false,
|
||||||
|
format: FORMAT,
|
||||||
|
color_space: Default::default(),
|
||||||
|
transfer_function: Default::default(),
|
||||||
|
};
|
||||||
let output = Rc::new(XOutput {
|
let output = Rc::new(XOutput {
|
||||||
id: self.state.connector_ids.next(),
|
id: self.state.connector_ids.next(),
|
||||||
backend: self.clone(),
|
backend: self.clone(),
|
||||||
|
|
@ -481,6 +505,7 @@ impl XBackend {
|
||||||
next_image: Default::default(),
|
next_image: Default::default(),
|
||||||
cb: CloneCell::new(None),
|
cb: CloneCell::new(None),
|
||||||
images,
|
images,
|
||||||
|
state: Cell::new(state),
|
||||||
});
|
});
|
||||||
{
|
{
|
||||||
let class = "jay\0jay\0";
|
let class = "jay\0jay\0";
|
||||||
|
|
@ -569,21 +594,15 @@ impl XBackend {
|
||||||
format!("X-Window-{}", output.window),
|
format!("X-Window-{}", output.window),
|
||||||
output.window.to_string(),
|
output.window.to_string(),
|
||||||
)),
|
)),
|
||||||
initial_mode: Mode {
|
|
||||||
width: output.width.get(),
|
|
||||||
height: output.height.get(),
|
|
||||||
refresh_rate_millihz: 60_000, // TODO
|
|
||||||
},
|
|
||||||
width_mm: output.width.get(),
|
width_mm: output.width.get(),
|
||||||
height_mm: output.height.get(),
|
height_mm: output.height.get(),
|
||||||
non_desktop: false,
|
non_desktop: false,
|
||||||
vrr_capable: false,
|
vrr_capable: false,
|
||||||
transfer_functions: vec![],
|
transfer_functions: vec![],
|
||||||
transfer_function: BackendTransferFunction::Default,
|
|
||||||
color_spaces: vec![],
|
color_spaces: vec![],
|
||||||
color_space: BackendColorSpace::Default,
|
|
||||||
primaries: Primaries::SRGB,
|
primaries: Primaries::SRGB,
|
||||||
luminance: None,
|
luminance: None,
|
||||||
|
state: output.state.get(),
|
||||||
}));
|
}));
|
||||||
output.changed();
|
output.changed();
|
||||||
self.present(output).await;
|
self.present(output).await;
|
||||||
|
|
@ -962,11 +981,12 @@ impl XBackend {
|
||||||
old.tex.set(new.tex.get());
|
old.tex.set(new.tex.get());
|
||||||
old.pixmap.set(new.pixmap.get());
|
old.pixmap.set(new.pixmap.get());
|
||||||
}
|
}
|
||||||
output.events.push(ConnectorEvent::ModeChanged(Mode {
|
let mut state = output.state.get();
|
||||||
width,
|
state.serial = self.state.backend_connector_state_serials.next();
|
||||||
height,
|
state.mode.width = width;
|
||||||
refresh_rate_millihz: 60, // TODO
|
state.mode.height = height;
|
||||||
}));
|
output.state.set(state);
|
||||||
|
output.events.push(ConnectorEvent::State(state));
|
||||||
output.changed();
|
output.changed();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -1035,6 +1055,7 @@ struct XOutput {
|
||||||
next_image: NumCell<usize>,
|
next_image: NumCell<usize>,
|
||||||
images: [XImage; 2],
|
images: [XImage; 2],
|
||||||
cb: CloneCell<Option<Rc<dyn Fn()>>>,
|
cb: CloneCell<Option<Rc<dyn Fn()>>>,
|
||||||
|
state: Cell<BackendConnectorState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct XImage {
|
struct XImage {
|
||||||
|
|
@ -1083,8 +1104,75 @@ impl Connector for XOutput {
|
||||||
Some(self.backend.drm_device_id)
|
Some(self.backend.drm_device_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_mode(&self, _mode: Mode) {
|
fn effectively_locked(&self) -> bool {
|
||||||
log::warn!("X backend doesn't support changing the connector mode");
|
// todo
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transaction_type(&self) -> Box<dyn BackendConnectorTransactionTypeDyn> {
|
||||||
|
Box::new(XTransactionType)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_transaction(
|
||||||
|
&self,
|
||||||
|
) -> Result<Box<dyn BackendConnectorTransaction>, BackendConnectorTransactionError> {
|
||||||
|
Ok(Box::new(XTransaction::default()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Hash, Eq, PartialEq)]
|
||||||
|
struct XTransactionType;
|
||||||
|
impl BackendConnectorTransactionType for XTransactionType {}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct XTransaction {
|
||||||
|
connectors: AHashMap<ConnectorId, Rc<XOutput>>,
|
||||||
|
}
|
||||||
|
impl XTransaction {
|
||||||
|
fn send_state(&self) {
|
||||||
|
for con in self.connectors.values() {
|
||||||
|
let mut state = con.state.get();
|
||||||
|
state.serial = con.backend.state.backend_connector_state_serials.next();
|
||||||
|
con.events.push(ConnectorEvent::State(state));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl BackendConnectorTransaction for XTransaction {
|
||||||
|
fn add(
|
||||||
|
&mut self,
|
||||||
|
connector: &Rc<dyn Connector>,
|
||||||
|
_change: BackendConnectorState,
|
||||||
|
) -> Result<(), BackendConnectorTransactionError> {
|
||||||
|
let con = (connector.clone() as Rc<dyn Any>)
|
||||||
|
.downcast::<XOutput>()
|
||||||
|
.map_err(|_| {
|
||||||
|
BackendConnectorTransactionError::UnsupportedConnectorType(connector.kernel_id())
|
||||||
|
})?;
|
||||||
|
self.connectors.insert(con.id, con.clone());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn prepare(
|
||||||
|
self: Box<Self>,
|
||||||
|
) -> Result<Box<dyn BackendPreparedConnectorTransaction>, BackendConnectorTransactionError>
|
||||||
|
{
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl BackendPreparedConnectorTransaction for XTransaction {
|
||||||
|
fn apply(
|
||||||
|
self: Box<Self>,
|
||||||
|
) -> Result<Box<dyn BackendAppliedConnectorTransaction>, BackendConnectorTransactionError> {
|
||||||
|
self.send_state();
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl BackendAppliedConnectorTransaction for XTransaction {
|
||||||
|
fn commit(self: Box<Self>) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
fn rollback(self: Box<Self>) -> Result<(), BackendConnectorTransactionError> {
|
||||||
|
self.send_state();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -853,10 +853,11 @@ impl Randr {
|
||||||
|
|
||||||
fn print_connector(&self, connector: &Connector, modes: bool, formats: bool) {
|
fn print_connector(&self, connector: &Connector, modes: bool, formats: bool) {
|
||||||
println!(" {}:", connector.name);
|
println!(" {}:", connector.name);
|
||||||
|
if !connector.enabled {
|
||||||
|
println!(" disabled");
|
||||||
|
}
|
||||||
let Some(o) = &connector.output else {
|
let Some(o) = &connector.output else {
|
||||||
if !connector.enabled {
|
if connector.enabled {
|
||||||
println!(" disabled");
|
|
||||||
} else {
|
|
||||||
println!(" disconnected");
|
println!(" disconnected");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
@ -869,7 +870,9 @@ impl Randr {
|
||||||
o.width_mm, o.height_mm
|
o.width_mm, o.height_mm
|
||||||
);
|
);
|
||||||
if o.non_desktop {
|
if o.non_desktop {
|
||||||
println!(" non-desktop");
|
if connector.enabled {
|
||||||
|
println!(" non-desktop");
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
println!(" VRR capable: {}", o.vrr_capable);
|
println!(" VRR capable: {}", o.vrr_capable);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
acceptor::{Acceptor, AcceptorError},
|
acceptor::{Acceptor, AcceptorError},
|
||||||
async_engine::{AsyncEngine, Phase, SpawnedFuture},
|
async_engine::{AsyncEngine, Phase, SpawnedFuture},
|
||||||
backend::{self, Backend, BackendColorSpace, BackendTransferFunction, Connector},
|
backend::{self, Backend, BackendConnectorState, BackendConnectorStateSerial, Connector},
|
||||||
backends::{
|
backends::{
|
||||||
dummy::{DummyBackend, DummyOutput},
|
dummy::{DummyBackend, DummyOutput},
|
||||||
metal, x,
|
metal, x,
|
||||||
|
|
@ -26,6 +26,7 @@ use {
|
||||||
dbus::Dbus,
|
dbus::Dbus,
|
||||||
ei::ei_client::EiClients,
|
ei::ei_client::EiClients,
|
||||||
forker,
|
forker,
|
||||||
|
format::XRGB8888,
|
||||||
globals::Globals,
|
globals::Globals,
|
||||||
ifs::{
|
ifs::{
|
||||||
jay_screencast::{perform_screencast_realloc, perform_toplevel_screencasts},
|
jay_screencast::{perform_screencast_realloc, perform_toplevel_screencasts},
|
||||||
|
|
@ -337,6 +338,7 @@ fn start_compositor2(
|
||||||
toplevel_managers: Default::default(),
|
toplevel_managers: Default::default(),
|
||||||
node_at_tree: Default::default(),
|
node_at_tree: Default::default(),
|
||||||
position_hint_requests: Default::default(),
|
position_hint_requests: Default::default(),
|
||||||
|
backend_connector_state_serials: Default::default(),
|
||||||
});
|
});
|
||||||
state.tracker.register(ClientId::from_raw(0));
|
state.tracker.register(ClientId::from_raw(0));
|
||||||
create_dummy_output(&state);
|
create_dummy_output(&state);
|
||||||
|
|
@ -396,7 +398,7 @@ async fn start_compositor3(state: Rc<State>, test_future: Option<TestFuture>) {
|
||||||
}
|
}
|
||||||
state.update_ei_acceptor();
|
state.update_ei_acceptor();
|
||||||
|
|
||||||
let _geh = start_global_event_handlers(&state, &backend);
|
let _geh = start_global_event_handlers(&state);
|
||||||
state.start_xwayland();
|
state.start_xwayland();
|
||||||
|
|
||||||
match backend.run().await {
|
match backend.run().await {
|
||||||
|
|
@ -424,10 +426,7 @@ fn load_config(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_global_event_handlers(
|
fn start_global_event_handlers(state: &Rc<State>) -> Vec<SpawnedFuture<()>> {
|
||||||
state: &Rc<State>,
|
|
||||||
backend: &Rc<dyn Backend>,
|
|
||||||
) -> Vec<SpawnedFuture<()>> {
|
|
||||||
let eng = &state.eng;
|
let eng = &state.eng;
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
|
|
@ -471,11 +470,7 @@ fn start_global_event_handlers(
|
||||||
Phase::PostLayout,
|
Phase::PostLayout,
|
||||||
float_titles(state.clone()),
|
float_titles(state.clone()),
|
||||||
),
|
),
|
||||||
eng.spawn2(
|
eng.spawn2("idle", Phase::PostLayout, idle(state.clone())),
|
||||||
"idle",
|
|
||||||
Phase::PostLayout,
|
|
||||||
idle(state.clone(), backend.clone()),
|
|
||||||
),
|
|
||||||
eng.spawn2(
|
eng.spawn2(
|
||||||
"input, popup positioning",
|
"input, popup positioning",
|
||||||
Phase::PostLayout,
|
Phase::PostLayout,
|
||||||
|
|
@ -610,6 +605,23 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
let connector = Rc::new(DummyOutput {
|
let connector = Rc::new(DummyOutput {
|
||||||
id: state.connector_ids.next(),
|
id: state.connector_ids.next(),
|
||||||
}) as Rc<dyn Connector>;
|
}) as Rc<dyn Connector>;
|
||||||
|
let mode = backend::Mode {
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
refresh_rate_millihz: 40_000,
|
||||||
|
};
|
||||||
|
let backend_state = BackendConnectorState {
|
||||||
|
serial: BackendConnectorStateSerial::from_raw(0),
|
||||||
|
enabled: true,
|
||||||
|
active: false,
|
||||||
|
mode,
|
||||||
|
non_desktop_override: None,
|
||||||
|
vrr: false,
|
||||||
|
tearing: false,
|
||||||
|
format: XRGB8888,
|
||||||
|
color_space: Default::default(),
|
||||||
|
transfer_function: Default::default(),
|
||||||
|
};
|
||||||
let connector_data = Rc::new(ConnectorData {
|
let connector_data = Rc::new(ConnectorData {
|
||||||
connector,
|
connector,
|
||||||
handler: Cell::new(None),
|
handler: Cell::new(None),
|
||||||
|
|
@ -621,6 +633,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
damage: Default::default(),
|
damage: Default::default(),
|
||||||
needs_vblank_emulation: Cell::new(false),
|
needs_vblank_emulation: Cell::new(false),
|
||||||
damage_intersect: Default::default(),
|
damage_intersect: Default::default(),
|
||||||
|
state: Cell::new(backend_state),
|
||||||
});
|
});
|
||||||
let schedule = Rc::new(OutputSchedule::new(
|
let schedule = Rc::new(OutputSchedule::new(
|
||||||
&state.ring,
|
&state.ring,
|
||||||
|
|
@ -635,19 +648,12 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
state,
|
state,
|
||||||
&connector_data,
|
&connector_data,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
&backend::Mode {
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
refresh_rate_millihz: 40_000,
|
|
||||||
},
|
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
&output_id,
|
&output_id,
|
||||||
&persistent_state,
|
&persistent_state,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
BackendTransferFunction::Default,
|
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
BackendColorSpace::Default,
|
|
||||||
Primaries::SRGB,
|
Primaries::SRGB,
|
||||||
None,
|
None,
|
||||||
)),
|
)),
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use {
|
||||||
backend::{
|
backend::{
|
||||||
self, BackendColorSpace, BackendTransferFunction, ConnectorId, DrmDeviceId,
|
self, BackendColorSpace, BackendTransferFunction, ConnectorId, DrmDeviceId,
|
||||||
InputDeviceAccelProfile, InputDeviceCapability, InputDeviceClickMethod, InputDeviceId,
|
InputDeviceAccelProfile, InputDeviceCapability, InputDeviceClickMethod, InputDeviceId,
|
||||||
|
transaction::BackendConnectorTransactionError,
|
||||||
},
|
},
|
||||||
client::{Client, ClientId},
|
client::{Client, ClientId},
|
||||||
cmm::cmm_transfer_function::TransferFunction,
|
cmm::cmm_transfer_function::TransferFunction,
|
||||||
|
|
@ -1141,12 +1142,16 @@ impl ConfigProxyHandler {
|
||||||
connector: Connector,
|
connector: Connector,
|
||||||
mode: WireMode,
|
mode: WireMode,
|
||||||
) -> Result<(), CphError> {
|
) -> Result<(), CphError> {
|
||||||
let connector = self.get_output(connector)?;
|
let connector = self.get_connector(connector)?;
|
||||||
connector.connector.connector.set_mode(backend::Mode {
|
connector
|
||||||
width: mode.width,
|
.modify_state(&self.state, |s| {
|
||||||
height: mode.height,
|
s.mode = backend::Mode {
|
||||||
refresh_rate_millihz: mode.refresh_millihz,
|
width: mode.width,
|
||||||
});
|
height: mode.height,
|
||||||
|
refresh_rate_millihz: mode.refresh_millihz,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.map_err(CphError::ModifyConnectorState)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1265,7 +1270,9 @@ impl ConfigProxyHandler {
|
||||||
return Err(CphError::UnknownFormat(format));
|
return Err(CphError::UnknownFormat(format));
|
||||||
};
|
};
|
||||||
let connector = self.get_connector(connector)?;
|
let connector = self.get_connector(connector)?;
|
||||||
connector.connector.set_fb_format(format);
|
connector
|
||||||
|
.modify_state(&self.state, |s| s.format = format)
|
||||||
|
.map_err(CphError::ModifyConnectorState)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1286,7 +1293,12 @@ impl ConfigProxyHandler {
|
||||||
_ => return Err(CphError::UnknownTransferFunction(transfer_function)),
|
_ => return Err(CphError::UnknownTransferFunction(transfer_function)),
|
||||||
};
|
};
|
||||||
let connector = self.get_connector(connector)?;
|
let connector = self.get_connector(connector)?;
|
||||||
connector.connector.set_colors(bcs, btf);
|
connector
|
||||||
|
.modify_state(&self.state, |s| {
|
||||||
|
s.color_space = bcs;
|
||||||
|
s.transfer_function = btf;
|
||||||
|
})
|
||||||
|
.map_err(CphError::ModifyConnectorState)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1447,7 +1459,11 @@ impl ConfigProxyHandler {
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
) -> Result<(), CphError> {
|
) -> Result<(), CphError> {
|
||||||
let connector = self.get_connector(connector)?;
|
let connector = self.get_connector(connector)?;
|
||||||
connector.connector.set_enabled(enabled);
|
connector
|
||||||
|
.modify_state(&self.state, |s| {
|
||||||
|
s.enabled = enabled;
|
||||||
|
})
|
||||||
|
.map_err(CphError::ModifyConnectorState)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3057,6 +3073,8 @@ enum CphError {
|
||||||
InvalidRegex(#[source] regex::Error),
|
InvalidRegex(#[source] regex::Error),
|
||||||
#[error("Window matcher {0:?} does not exist")]
|
#[error("Window matcher {0:?} does not exist")]
|
||||||
WindowMatcherDoesNotExist(WindowMatcher),
|
WindowMatcherDoesNotExist(WindowMatcher),
|
||||||
|
#[error("Could not modify the connector state")]
|
||||||
|
ModifyConnectorState(#[source] BackendConnectorTransactionError),
|
||||||
}
|
}
|
||||||
|
|
||||||
trait WithRequestName {
|
trait WithRequestName {
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ impl ExtSessionLockManagerV1RequestHandler for ExtSessionLockManagerV1 {
|
||||||
client: self.client.clone(),
|
client: self.client.clone(),
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
did_lock,
|
did_lock,
|
||||||
|
awaiting_locked: Cell::new(true),
|
||||||
finished: Cell::new(false),
|
finished: Cell::new(false),
|
||||||
version: self.version,
|
version: self.version,
|
||||||
});
|
});
|
||||||
|
|
@ -75,7 +76,7 @@ impl ExtSessionLockManagerV1RequestHandler for ExtSessionLockManagerV1 {
|
||||||
state.lock.lock.set(Some(new.clone()));
|
state.lock.lock.set(Some(new.clone()));
|
||||||
state.tree_changed();
|
state.tree_changed();
|
||||||
state.damage(state.root.extents.get());
|
state.damage(state.root.extents.get());
|
||||||
new.send_locked();
|
new.check_locked();
|
||||||
} else {
|
} else {
|
||||||
new.finish();
|
new.finish();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,26 @@ pub struct ExtSessionLockV1 {
|
||||||
pub client: Rc<Client>,
|
pub client: Rc<Client>,
|
||||||
pub tracker: Tracker<Self>,
|
pub tracker: Tracker<Self>,
|
||||||
pub did_lock: bool,
|
pub did_lock: bool,
|
||||||
|
pub awaiting_locked: Cell<bool>,
|
||||||
pub finished: Cell<bool>,
|
pub finished: Cell<bool>,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtSessionLockV1 {
|
impl ExtSessionLockV1 {
|
||||||
pub fn send_locked(&self) {
|
pub fn check_locked(&self) {
|
||||||
|
if !self.awaiting_locked.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for output in self.client.state.outputs.lock().values() {
|
||||||
|
if !output.connector.connector.effectively_locked() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.send_locked();
|
||||||
|
self.awaiting_locked.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_locked(&self) {
|
||||||
self.client.event(Locked { self_id: self.id })
|
self.client.event(Locked { self_id: self.id })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ use {
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
state::{ConnectorData, DrmDevData, OutputData},
|
state::{ConnectorData, DrmDevData, OutputData, State},
|
||||||
tree::{OutputNode, TearingMode, VrrMode},
|
tree::{OutputNode, TearingMode, VrrMode},
|
||||||
utils::{gfx_api_ext::GfxApiExt, transform_ext::TransformExt},
|
utils::{errorfmt::ErrorFmt, gfx_api_ext::GfxApiExt, transform_ext::TransformExt},
|
||||||
wire::{JayRandrId, jay_randr::*},
|
wire::{JayRandrId, jay_randr::*},
|
||||||
},
|
},
|
||||||
jay_config::video::{
|
jay_config::video::{
|
||||||
|
|
@ -23,6 +23,7 @@ use {
|
||||||
pub struct JayRandr {
|
pub struct JayRandr {
|
||||||
pub id: JayRandrId,
|
pub id: JayRandrId,
|
||||||
pub client: Rc<Client>,
|
pub client: Rc<Client>,
|
||||||
|
pub state: Rc<State>,
|
||||||
pub tracker: Tracker<Self>,
|
pub tracker: Tracker<Self>,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
}
|
}
|
||||||
|
|
@ -39,6 +40,7 @@ impl JayRandr {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
|
state: client.state.clone(),
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
version,
|
version,
|
||||||
}
|
}
|
||||||
|
|
@ -67,6 +69,7 @@ impl JayRandr {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_connector(&self, data: &ConnectorData) {
|
fn send_connector(&self, data: &ConnectorData) {
|
||||||
|
let state = data.state.get();
|
||||||
self.client.event(Connector {
|
self.client.event(Connector {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
id: data.connector.id().raw() as _,
|
id: data.connector.id().raw() as _,
|
||||||
|
|
@ -75,7 +78,7 @@ impl JayRandr {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|d| d.dev.id().raw() as _)
|
.map(|d| d.dev.id().raw() as _)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
enabled: data.connector.enabled() as _,
|
enabled: state.enabled as _,
|
||||||
name: &data.name,
|
name: &data.name,
|
||||||
});
|
});
|
||||||
let Some(output) = self.client.state.outputs.get(&data.connector.id()) else {
|
let Some(output) = self.client.state.outputs.get(&data.connector.id()) else {
|
||||||
|
|
@ -347,14 +350,19 @@ impl JayRandrRequestHandler for JayRandr {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_mode(&self, req: SetMode, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn set_mode(&self, req: SetMode, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
let Some(c) = self.get_output(req.output) else {
|
let Some(c) = self.get_connector(req.output) else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
c.connector.connector.set_mode(backend::Mode {
|
let res = c.modify_state(&self.state, |s| {
|
||||||
width: req.width,
|
s.mode = backend::Mode {
|
||||||
height: req.height,
|
width: req.width,
|
||||||
refresh_rate_millihz: req.refresh_rate_millihz,
|
height: req.height,
|
||||||
|
refresh_rate_millihz: req.refresh_rate_millihz,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
if let Err(e) = res {
|
||||||
|
self.send_error(&format!("Could not modify connector mode: {}", ErrorFmt(e)));
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -378,7 +386,10 @@ impl JayRandrRequestHandler for JayRandr {
|
||||||
let Some(c) = self.get_connector(req.output) else {
|
let Some(c) = self.get_connector(req.output) else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
c.connector.set_enabled(req.enabled != 0);
|
let res = c.modify_state(&self.state, |s| s.enabled = req.enabled != 0);
|
||||||
|
if let Err(e) = res {
|
||||||
|
self.send_error(&format!("Could not en/disable connector: {}", ErrorFmt(e)));
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -391,7 +402,13 @@ impl JayRandrRequestHandler for JayRandr {
|
||||||
1 => Some(false),
|
1 => Some(false),
|
||||||
_ => Some(true),
|
_ => Some(true),
|
||||||
};
|
};
|
||||||
c.connector.set_non_desktop_override(non_desktop);
|
c.connector.before_non_desktop_override_update(non_desktop);
|
||||||
|
let res = c.modify_state(&self.state, |s| {
|
||||||
|
s.non_desktop_override = non_desktop;
|
||||||
|
});
|
||||||
|
if let Err(e) = res {
|
||||||
|
self.send_error(&format!("Could not change non-desktop override: {}", e));
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -439,10 +456,13 @@ impl JayRandrRequestHandler for JayRandr {
|
||||||
let Some(&format) = named_formats().get(req.format) else {
|
let Some(&format) = named_formats().get(req.format) else {
|
||||||
return Err(JayRandrError::UnknownFormat(req.format.to_string()));
|
return Err(JayRandrError::UnknownFormat(req.format.to_string()));
|
||||||
};
|
};
|
||||||
let Some(c) = self.get_output_node(req.output) else {
|
let Some(c) = self.get_connector(req.output) else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
c.global.connector.connector.set_fb_format(format);
|
let res = c.modify_state(&self.state, |s| s.format = format);
|
||||||
|
if let Err(e) = res {
|
||||||
|
self.send_error(&format!("Could not modify connector format: {}", e));
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -478,7 +498,16 @@ impl JayRandrRequestHandler for JayRandr {
|
||||||
let Some(c) = self.get_connector(req.output) else {
|
let Some(c) = self.get_connector(req.output) else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
c.connector.set_colors(cs, tf);
|
let res = c.modify_state(&self.state, |s| {
|
||||||
|
s.color_space = cs;
|
||||||
|
s.transfer_function = tf;
|
||||||
|
});
|
||||||
|
if let Err(e) = res {
|
||||||
|
self.send_error(&format!(
|
||||||
|
"Could not modify connector colors: {}",
|
||||||
|
ErrorFmt(e),
|
||||||
|
));
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -161,22 +161,20 @@ impl WlOutputGlobal {
|
||||||
state: &Rc<State>,
|
state: &Rc<State>,
|
||||||
connector: &Rc<ConnectorData>,
|
connector: &Rc<ConnectorData>,
|
||||||
modes: Vec<backend::Mode>,
|
modes: Vec<backend::Mode>,
|
||||||
mode: &backend::Mode,
|
|
||||||
width_mm: i32,
|
width_mm: i32,
|
||||||
height_mm: i32,
|
height_mm: i32,
|
||||||
output_id: &Rc<OutputId>,
|
output_id: &Rc<OutputId>,
|
||||||
persistent_state: &Rc<PersistentOutputState>,
|
persistent_state: &Rc<PersistentOutputState>,
|
||||||
transfer_functions: Vec<BackendTransferFunction>,
|
transfer_functions: Vec<BackendTransferFunction>,
|
||||||
btf: BackendTransferFunction,
|
|
||||||
color_spaces: Vec<BackendColorSpace>,
|
color_spaces: Vec<BackendColorSpace>,
|
||||||
bcs: BackendColorSpace,
|
|
||||||
primaries: Primaries,
|
primaries: Primaries,
|
||||||
luminance: Option<BackendLuminance>,
|
luminance: Option<BackendLuminance>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (x, y) = persistent_state.pos.get();
|
let (x, y) = persistent_state.pos.get();
|
||||||
let scale = persistent_state.scale.get();
|
let scale = persistent_state.scale.get();
|
||||||
|
let connector_state = connector.state.get();
|
||||||
let (width, height) = calculate_logical_size(
|
let (width, height) = calculate_logical_size(
|
||||||
(mode.width, mode.height),
|
(connector_state.mode.width, connector_state.mode.height),
|
||||||
persistent_state.transform.get(),
|
persistent_state.transform.get(),
|
||||||
scale,
|
scale,
|
||||||
);
|
);
|
||||||
|
|
@ -186,8 +184,8 @@ impl WlOutputGlobal {
|
||||||
connector: connector.clone(),
|
connector: connector.clone(),
|
||||||
pos: Cell::new(Rect::new_sized(x, y, width, height).unwrap()),
|
pos: Cell::new(Rect::new_sized(x, y, width, height).unwrap()),
|
||||||
output_id: output_id.clone(),
|
output_id: output_id.clone(),
|
||||||
mode: Cell::new(*mode),
|
mode: Cell::new(connector_state.mode),
|
||||||
refresh_nsec: Cell::new(mode.refresh_nsec()),
|
refresh_nsec: Cell::new(connector_state.mode.refresh_nsec()),
|
||||||
modes,
|
modes,
|
||||||
formats: CloneCell::new(Rc::new(vec![])),
|
formats: CloneCell::new(Rc::new(vec![])),
|
||||||
format: Cell::new(XRGB8888),
|
format: Cell::new(XRGB8888),
|
||||||
|
|
@ -203,8 +201,8 @@ impl WlOutputGlobal {
|
||||||
persistent: persistent_state.clone(),
|
persistent: persistent_state.clone(),
|
||||||
opt: Default::default(),
|
opt: Default::default(),
|
||||||
damage_matrix: Default::default(),
|
damage_matrix: Default::default(),
|
||||||
btf: Cell::new(btf),
|
btf: Cell::new(connector_state.transfer_function),
|
||||||
bcs: Cell::new(bcs),
|
bcs: Cell::new(connector_state.color_space),
|
||||||
color_description: CloneCell::new(state.color_manager.srgb_srgb().clone()),
|
color_description: CloneCell::new(state.color_manager.srgb_srgb().clone()),
|
||||||
linear_color_description: CloneCell::new(state.color_manager.srgb_linear().clone()),
|
linear_color_description: CloneCell::new(state.color_manager.srgb_linear().clone()),
|
||||||
color_description_listeners: Default::default(),
|
color_description_listeners: Default::default(),
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,21 @@ use {
|
||||||
allocator::{Allocator, AllocatorError},
|
allocator::{Allocator, AllocatorError},
|
||||||
async_engine::SpawnedFuture,
|
async_engine::SpawnedFuture,
|
||||||
backend::{
|
backend::{
|
||||||
AxisSource, Backend, BackendColorSpace, BackendEvent, BackendTransferFunction,
|
AxisSource, Backend, BackendConnectorState, BackendEvent, Connector, ConnectorEvent,
|
||||||
Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId, InputDevice,
|
ConnectorId, ConnectorKernelId, DrmDeviceId, InputDevice, InputDeviceAccelProfile,
|
||||||
InputDeviceAccelProfile, InputDeviceCapability, InputDeviceClickMethod, InputDeviceId,
|
InputDeviceCapability, InputDeviceClickMethod, InputDeviceId, InputEvent, KeyState,
|
||||||
InputEvent, KeyState, Mode, MonitorInfo, ScrollAxis, TransformMatrix,
|
Mode, MonitorInfo, ScrollAxis, TransformMatrix,
|
||||||
|
transaction::{
|
||||||
|
BackendAppliedConnectorTransaction, BackendConnectorTransaction,
|
||||||
|
BackendConnectorTransactionError, BackendConnectorTransactionType,
|
||||||
|
BackendConnectorTransactionTypeDyn, BackendPreparedConnectorTransaction,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
cmm::cmm_primaries::Primaries,
|
cmm::cmm_primaries::Primaries,
|
||||||
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,
|
||||||
|
|
@ -29,8 +35,9 @@ use {
|
||||||
gbm::{GbmDevice, GbmError},
|
gbm::{GbmDevice, GbmError},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ahash::AHashMap,
|
||||||
bstr::ByteSlice,
|
bstr::ByteSlice,
|
||||||
std::{cell::Cell, error::Error, io, os::unix::ffi::OsStrExt, pin::Pin, rc::Rc},
|
std::{any::Any, cell::Cell, error::Error, io, os::unix::ffi::OsStrExt, pin::Pin, rc::Rc},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
uapi::c,
|
uapi::c,
|
||||||
};
|
};
|
||||||
|
|
@ -63,7 +70,6 @@ pub struct TestBackend {
|
||||||
pub default_mouse: Rc<TestBackendMouse>,
|
pub default_mouse: Rc<TestBackendMouse>,
|
||||||
pub default_kb: Rc<TestBackendKb>,
|
pub default_kb: Rc<TestBackendKb>,
|
||||||
pub render_context_installed: Cell<bool>,
|
pub render_context_installed: Cell<bool>,
|
||||||
pub idle: TEEH<bool>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestBackend {
|
impl TestBackend {
|
||||||
|
|
@ -77,6 +83,7 @@ impl TestBackend {
|
||||||
},
|
},
|
||||||
events: Default::default(),
|
events: Default::default(),
|
||||||
feedback: Default::default(),
|
feedback: Default::default(),
|
||||||
|
idle: Default::default(),
|
||||||
});
|
});
|
||||||
let default_mouse = Rc::new(TestBackendMouse {
|
let default_mouse = Rc::new(TestBackendMouse {
|
||||||
common: TestInputDeviceCommon {
|
common: TestInputDeviceCommon {
|
||||||
|
|
@ -125,17 +132,26 @@ impl TestBackend {
|
||||||
model: "TestConnector".to_string(),
|
model: "TestConnector".to_string(),
|
||||||
serial_number: default_connector.id.to_string(),
|
serial_number: default_connector.id.to_string(),
|
||||||
}),
|
}),
|
||||||
initial_mode: mode,
|
|
||||||
width_mm: 80,
|
width_mm: 80,
|
||||||
height_mm: 60,
|
height_mm: 60,
|
||||||
non_desktop: false,
|
non_desktop: false,
|
||||||
vrr_capable: false,
|
vrr_capable: false,
|
||||||
transfer_functions: vec![],
|
transfer_functions: vec![],
|
||||||
transfer_function: BackendTransferFunction::Default,
|
|
||||||
color_spaces: vec![],
|
color_spaces: vec![],
|
||||||
color_space: BackendColorSpace::Default,
|
|
||||||
primaries: Primaries::SRGB,
|
primaries: Primaries::SRGB,
|
||||||
luminance: None,
|
luminance: None,
|
||||||
|
state: BackendConnectorState {
|
||||||
|
serial: state.backend_connector_state_serials.next(),
|
||||||
|
enabled: true,
|
||||||
|
active: true,
|
||||||
|
mode,
|
||||||
|
non_desktop_override: None,
|
||||||
|
vrr: false,
|
||||||
|
tearing: false,
|
||||||
|
format: XRGB8888,
|
||||||
|
color_space: Default::default(),
|
||||||
|
transfer_function: Default::default(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
|
|
@ -145,7 +161,6 @@ impl TestBackend {
|
||||||
default_mouse,
|
default_mouse,
|
||||||
default_kb,
|
default_kb,
|
||||||
render_context_installed: Cell::new(false),
|
render_context_installed: Cell::new(false),
|
||||||
idle: Rc::new(Default::default()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -291,10 +306,6 @@ impl Backend for TestBackend {
|
||||||
let _ = vtnr;
|
let _ = vtnr;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_idle(&self, idle: bool) {
|
|
||||||
self.idle.push(idle);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn supports_presentation_feedback(&self) -> bool {
|
fn supports_presentation_feedback(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -305,6 +316,7 @@ pub struct TestConnector {
|
||||||
pub kernel_id: ConnectorKernelId,
|
pub kernel_id: ConnectorKernelId,
|
||||||
pub events: OnChange<ConnectorEvent>,
|
pub events: OnChange<ConnectorEvent>,
|
||||||
pub feedback: CloneCell<Option<Rc<DrmFeedback>>>,
|
pub feedback: CloneCell<Option<Rc<DrmFeedback>>>,
|
||||||
|
pub idle: TEEH<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Connector for TestConnector {
|
impl Connector for TestConnector {
|
||||||
|
|
@ -332,13 +344,70 @@ impl Connector for TestConnector {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_mode(&self, _mode: Mode) {
|
fn effectively_locked(&self) -> bool {
|
||||||
// todo
|
// todo
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drm_feedback(&self) -> Option<Rc<DrmFeedback>> {
|
fn drm_feedback(&self) -> Option<Rc<DrmFeedback>> {
|
||||||
self.feedback.get()
|
self.feedback.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transaction_type(&self) -> Box<dyn BackendConnectorTransactionTypeDyn> {
|
||||||
|
Box::new(TestBackendTransactionType)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_transaction(
|
||||||
|
&self,
|
||||||
|
) -> Result<Box<dyn BackendConnectorTransaction>, BackendConnectorTransactionError> {
|
||||||
|
Ok(Box::new(TestBackendTransaction::default()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Hash, Eq, PartialEq)]
|
||||||
|
struct TestBackendTransactionType;
|
||||||
|
impl BackendConnectorTransactionType for TestBackendTransactionType {}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct TestBackendTransaction {
|
||||||
|
connectors: AHashMap<ConnectorId, (Rc<TestConnector>, BackendConnectorState)>,
|
||||||
|
}
|
||||||
|
impl BackendConnectorTransaction for TestBackendTransaction {
|
||||||
|
fn add(
|
||||||
|
&mut self,
|
||||||
|
connector: &Rc<dyn Connector>,
|
||||||
|
change: BackendConnectorState,
|
||||||
|
) -> Result<(), BackendConnectorTransactionError> {
|
||||||
|
let c = (connector.clone() as Rc<dyn Any>)
|
||||||
|
.downcast::<TestConnector>()
|
||||||
|
.unwrap();
|
||||||
|
self.connectors.insert(c.id(), (c, change));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn prepare(
|
||||||
|
self: Box<Self>,
|
||||||
|
) -> Result<Box<dyn BackendPreparedConnectorTransaction>, BackendConnectorTransactionError>
|
||||||
|
{
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl BackendPreparedConnectorTransaction for TestBackendTransaction {
|
||||||
|
fn apply(
|
||||||
|
self: Box<Self>,
|
||||||
|
) -> Result<Box<dyn BackendAppliedConnectorTransaction>, BackendConnectorTransactionError> {
|
||||||
|
for (c, s) in self.connectors.values() {
|
||||||
|
c.idle.push(!s.active);
|
||||||
|
}
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl BackendAppliedConnectorTransaction for TestBackendTransaction {
|
||||||
|
fn commit(self: Box<Self>) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
fn rollback(self: Box<Self>) -> Result<(), BackendConnectorTransactionError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestMouseClick {
|
pub struct TestMouseClick {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::{
|
backend::{
|
||||||
BackendColorSpace, BackendEvent, BackendTransferFunction, ConnectorEvent,
|
BackendConnectorState, BackendEvent, ConnectorEvent, ConnectorKernelId, MonitorInfo,
|
||||||
ConnectorKernelId, Mode, MonitorInfo,
|
|
||||||
},
|
},
|
||||||
cmm::cmm_primaries::Primaries,
|
cmm::cmm_primaries::Primaries,
|
||||||
|
format::XRGB8888,
|
||||||
ifs::wl_output::OutputId,
|
ifs::wl_output::OutputId,
|
||||||
it::{test_backend::TestConnector, test_error::TestResult, testrun::TestRun},
|
it::{test_backend::TestConnector, test_error::TestResult, testrun::TestRun},
|
||||||
video::drm::ConnectorType,
|
video::drm::ConnectorType,
|
||||||
|
|
@ -34,6 +34,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
},
|
},
|
||||||
events: Default::default(),
|
events: Default::default(),
|
||||||
feedback: Default::default(),
|
feedback: Default::default(),
|
||||||
|
idle: Default::default(),
|
||||||
});
|
});
|
||||||
let new_monitor_info = MonitorInfo {
|
let new_monitor_info = MonitorInfo {
|
||||||
modes: vec![],
|
modes: vec![],
|
||||||
|
|
@ -43,21 +44,26 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
model: "jay second connector".to_string(),
|
model: "jay second connector".to_string(),
|
||||||
serial_number: "".to_string(),
|
serial_number: "".to_string(),
|
||||||
}),
|
}),
|
||||||
initial_mode: Mode {
|
|
||||||
width: 400,
|
|
||||||
height: 400,
|
|
||||||
refresh_rate_millihz: 60000,
|
|
||||||
},
|
|
||||||
width_mm: 0,
|
width_mm: 0,
|
||||||
height_mm: 0,
|
height_mm: 0,
|
||||||
non_desktop: false,
|
non_desktop: false,
|
||||||
vrr_capable: false,
|
vrr_capable: false,
|
||||||
transfer_functions: vec![],
|
transfer_functions: vec![],
|
||||||
transfer_function: BackendTransferFunction::Default,
|
|
||||||
color_spaces: vec![],
|
color_spaces: vec![],
|
||||||
color_space: BackendColorSpace::Default,
|
|
||||||
primaries: Primaries::SRGB,
|
primaries: Primaries::SRGB,
|
||||||
luminance: None,
|
luminance: None,
|
||||||
|
state: BackendConnectorState {
|
||||||
|
serial: run.state.backend_connector_state_serials.next(),
|
||||||
|
enabled: true,
|
||||||
|
active: true,
|
||||||
|
mode: Default::default(),
|
||||||
|
non_desktop_override: None,
|
||||||
|
vrr: false,
|
||||||
|
tearing: false,
|
||||||
|
format: XRGB8888,
|
||||||
|
color_space: Default::default(),
|
||||||
|
transfer_function: Default::default(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
run.backend
|
run.backend
|
||||||
.state
|
.state
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
run.cfg.set_idle(Duration::from_micros(100))?;
|
run.cfg.set_idle(Duration::from_micros(100))?;
|
||||||
run.cfg.set_idle_grace_period(Duration::from_secs(0))?;
|
run.cfg.set_idle_grace_period(Duration::from_secs(0))?;
|
||||||
|
|
||||||
let idle = run.backend.idle.expect()?;
|
let idle = ds.connector.idle.expect()?;
|
||||||
tassert!(idle.next().is_err());
|
tassert!(idle.next().is_err());
|
||||||
|
|
||||||
run.state.wheel.timeout(3).await?;
|
run.state.wheel.timeout(3).await?;
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,7 @@ macro_rules! linear_ids {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub struct $id($ty);
|
pub struct $id($ty);
|
||||||
|
|
||||||
impl $id {
|
impl $id {
|
||||||
|
|
@ -772,3 +772,22 @@ macro_rules! jay_allow_realtime_config_so {
|
||||||
"JAY_ALLOW_REALTIME_CONFIG_SO"
|
"JAY_ALLOW_REALTIME_CONFIG_SO"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::allow_attributes, unused_macros)]
|
||||||
|
macro_rules! dbg {
|
||||||
|
($val:expr) => {
|
||||||
|
match $val {
|
||||||
|
tmp => {
|
||||||
|
log::warn!(
|
||||||
|
"[{}:{}:{}] {} = {:#?}",
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
stringify!($val),
|
||||||
|
&tmp
|
||||||
|
);
|
||||||
|
tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -327,6 +327,13 @@ impl DamageQueue {
|
||||||
data.clear();
|
data.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clear_all(&self) {
|
||||||
|
let datas = unsafe { self.datas.get().deref_mut() };
|
||||||
|
for data in datas {
|
||||||
|
data.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get(&self) -> Region {
|
pub fn get(&self) -> Region {
|
||||||
let data = unsafe { &self.datas.get().deref()[self.this] };
|
let data = unsafe { &self.datas.get().deref()[self.this] };
|
||||||
Region::from_rects2(data)
|
Region::from_rects2(data)
|
||||||
|
|
|
||||||
34
src/state.rs
34
src/state.rs
|
|
@ -3,9 +3,10 @@ use {
|
||||||
acceptor::Acceptor,
|
acceptor::Acceptor,
|
||||||
async_engine::{AsyncEngine, SpawnedFuture},
|
async_engine::{AsyncEngine, SpawnedFuture},
|
||||||
backend::{
|
backend::{
|
||||||
Backend, BackendDrmDevice, BackendEvent, Connector, ConnectorId, ConnectorIds,
|
Backend, BackendConnectorState, BackendConnectorStateSerials, BackendDrmDevice,
|
||||||
DrmDeviceId, DrmDeviceIds, HardwareCursorUpdate, InputDevice, InputDeviceGroupIds,
|
BackendEvent, Connector, ConnectorId, ConnectorIds, DrmDeviceId, DrmDeviceIds,
|
||||||
InputDeviceId, InputDeviceIds, MonitorInfo,
|
HardwareCursorUpdate, InputDevice, InputDeviceGroupIds, InputDeviceId, InputDeviceIds,
|
||||||
|
MonitorInfo, transaction::BackendConnectorTransactionError,
|
||||||
},
|
},
|
||||||
backends::dummy::DummyBackend,
|
backends::dummy::DummyBackend,
|
||||||
cli::RunArgs,
|
cli::RunArgs,
|
||||||
|
|
@ -255,6 +256,7 @@ pub struct State {
|
||||||
pub caps_thread: Option<PrCapsThread>,
|
pub caps_thread: Option<PrCapsThread>,
|
||||||
pub node_at_tree: RefCell<Vec<FoundNode>>,
|
pub node_at_tree: RefCell<Vec<FoundNode>>,
|
||||||
pub position_hint_requests: AsyncQueue<PositionHintRequest>,
|
pub position_hint_requests: AsyncQueue<PositionHintRequest>,
|
||||||
|
pub backend_connector_state_serials: BackendConnectorStateSerials,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Drop for State {
|
// impl Drop for State {
|
||||||
|
|
@ -376,6 +378,7 @@ pub struct ConnectorData {
|
||||||
pub damage: RefCell<Vec<Rect>>,
|
pub damage: RefCell<Vec<Rect>>,
|
||||||
pub needs_vblank_emulation: Cell<bool>,
|
pub needs_vblank_emulation: Cell<bool>,
|
||||||
pub damage_intersect: Cell<Rect>,
|
pub damage_intersect: Cell<Rect>,
|
||||||
|
pub state: Cell<BackendConnectorState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OutputData {
|
pub struct OutputData {
|
||||||
|
|
@ -403,6 +406,31 @@ impl ConnectorData {
|
||||||
self.connector.damage();
|
self.connector.damage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn modify_state(
|
||||||
|
&self,
|
||||||
|
state: &State,
|
||||||
|
f: impl FnOnce(&mut BackendConnectorState),
|
||||||
|
) -> Result<(), BackendConnectorTransactionError> {
|
||||||
|
let old = self.state.get();
|
||||||
|
let mut s = old;
|
||||||
|
f(&mut s);
|
||||||
|
if old == s {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
s.serial = state.backend_connector_state_serials.next();
|
||||||
|
let mut tran = self.connector.create_transaction()?;
|
||||||
|
tran.add(&self.connector, s)?;
|
||||||
|
tran.prepare()?.apply()?.commit();
|
||||||
|
if let Some(output) = state.outputs.get(&self.connector.id())
|
||||||
|
&& let Some(node) = &output.node
|
||||||
|
{
|
||||||
|
node.update_state(s);
|
||||||
|
} else {
|
||||||
|
self.state.set(s);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrmDevData {
|
impl DrmDevData {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::{Connector, ConnectorEvent, ConnectorId, MonitorInfo},
|
backend::{
|
||||||
|
BackendConnectorState, BackendConnectorStateSerial, Connector, ConnectorEvent,
|
||||||
|
ConnectorId, MonitorInfo,
|
||||||
|
},
|
||||||
|
format::XRGB8888,
|
||||||
globals::GlobalName,
|
globals::GlobalName,
|
||||||
ifs::{
|
ifs::{
|
||||||
jay_tray_v1::JayTrayV1Global,
|
jay_tray_v1::JayTrayV1Global,
|
||||||
|
|
@ -22,6 +26,18 @@ pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
|
||||||
_ => panic!("connector's drm device does not exist"),
|
_ => panic!("connector's drm device does not exist"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
let backend_state = BackendConnectorState {
|
||||||
|
serial: BackendConnectorStateSerial::from_raw(0),
|
||||||
|
enabled: true,
|
||||||
|
active: false,
|
||||||
|
mode: Default::default(),
|
||||||
|
non_desktop_override: None,
|
||||||
|
vrr: false,
|
||||||
|
tearing: false,
|
||||||
|
format: XRGB8888,
|
||||||
|
color_space: Default::default(),
|
||||||
|
transfer_function: Default::default(),
|
||||||
|
};
|
||||||
let id = connector.id();
|
let id = connector.id();
|
||||||
let data = Rc::new(ConnectorData {
|
let data = Rc::new(ConnectorData {
|
||||||
connector: connector.clone(),
|
connector: connector.clone(),
|
||||||
|
|
@ -34,6 +50,7 @@ pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
|
||||||
damage: Default::default(),
|
damage: Default::default(),
|
||||||
needs_vblank_emulation: Cell::new(false),
|
needs_vblank_emulation: Cell::new(false),
|
||||||
damage_intersect: Default::default(),
|
damage_intersect: Default::default(),
|
||||||
|
state: Cell::new(backend_state),
|
||||||
});
|
});
|
||||||
if let Some(dev) = drm_dev {
|
if let Some(dev) = drm_dev {
|
||||||
dev.connectors.set(id, data.clone());
|
dev.connectors.set(id, data.clone());
|
||||||
|
|
@ -88,6 +105,10 @@ impl ConnectorHandler {
|
||||||
async fn handle_connected(&self, info: MonitorInfo) {
|
async fn handle_connected(&self, info: MonitorInfo) {
|
||||||
log::info!("Connector {} connected", self.data.connector.kernel_id());
|
log::info!("Connector {} connected", self.data.connector.kernel_id());
|
||||||
self.data.connected.set(true);
|
self.data.connected.set(true);
|
||||||
|
let old_state = self.data.state.get();
|
||||||
|
if old_state.serial < info.state.serial {
|
||||||
|
self.data.state.set(info.state);
|
||||||
|
}
|
||||||
let name = self.state.globals.name();
|
let name = self.state.globals.name();
|
||||||
if info.non_desktop {
|
if info.non_desktop {
|
||||||
self.handle_non_desktop_connected(info).await;
|
self.handle_non_desktop_connected(info).await;
|
||||||
|
|
@ -132,15 +153,12 @@ impl ConnectorHandler {
|
||||||
&self.state,
|
&self.state,
|
||||||
&self.data,
|
&self.data,
|
||||||
info.modes.clone(),
|
info.modes.clone(),
|
||||||
&info.initial_mode,
|
|
||||||
info.width_mm,
|
info.width_mm,
|
||||||
info.height_mm,
|
info.height_mm,
|
||||||
&output_id,
|
&output_id,
|
||||||
&desired_state,
|
&desired_state,
|
||||||
info.transfer_functions.clone(),
|
info.transfer_functions.clone(),
|
||||||
info.transfer_function,
|
|
||||||
info.color_spaces.clone(),
|
info.color_spaces.clone(),
|
||||||
info.color_space,
|
|
||||||
info.primaries,
|
info.primaries,
|
||||||
info.luminance,
|
info.luminance,
|
||||||
));
|
));
|
||||||
|
|
@ -268,18 +286,11 @@ impl ConnectorHandler {
|
||||||
on.hardware_cursor.set(hc);
|
on.hardware_cursor.set(hc);
|
||||||
self.state.refresh_hardware_cursors();
|
self.state.refresh_hardware_cursors();
|
||||||
}
|
}
|
||||||
ConnectorEvent::ModeChanged(mode) => {
|
ConnectorEvent::FormatsChanged(formats) => {
|
||||||
on.update_mode(mode);
|
|
||||||
}
|
|
||||||
ConnectorEvent::VrrChanged(enabled) => {
|
|
||||||
on.schedule.set_vrr_enabled(enabled);
|
|
||||||
}
|
|
||||||
ConnectorEvent::FormatsChanged(formats, format) => {
|
|
||||||
on.global.formats.set(formats);
|
on.global.formats.set(formats);
|
||||||
on.global.format.set(format);
|
|
||||||
}
|
}
|
||||||
ConnectorEvent::ColorsChanged(bcs, btf) => {
|
ConnectorEvent::State(state) => {
|
||||||
on.update_btf_and_bcs(btf, bcs);
|
on.update_state(state);
|
||||||
}
|
}
|
||||||
ev => unreachable!("received unexpected event {:?}", ev),
|
ev => unreachable!("received unexpected event {:?}", ev),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::Backend,
|
backend::transaction::{BackendConnectorTransactionError, ConnectorTransaction},
|
||||||
state::State,
|
state::State,
|
||||||
utils::{
|
utils::{
|
||||||
errorfmt::ErrorFmt,
|
errorfmt::ErrorFmt,
|
||||||
|
|
@ -12,7 +12,7 @@ use {
|
||||||
uapi::c,
|
uapi::c,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn idle(state: Rc<State>, backend: Rc<dyn Backend>) {
|
pub async fn idle(state: Rc<State>) {
|
||||||
let timer = match TimerFd::new(c::CLOCK_MONOTONIC) {
|
let timer = match TimerFd::new(c::CLOCK_MONOTONIC) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -24,7 +24,6 @@ pub async fn idle(state: Rc<State>, backend: Rc<dyn Backend>) {
|
||||||
state.idle.timeout_changed.set(true);
|
state.idle.timeout_changed.set(true);
|
||||||
let mut idle = Idle {
|
let mut idle = Idle {
|
||||||
state,
|
state,
|
||||||
backend,
|
|
||||||
timer,
|
timer,
|
||||||
idle: false,
|
idle: false,
|
||||||
dead: false,
|
dead: false,
|
||||||
|
|
@ -36,7 +35,6 @@ pub async fn idle(state: Rc<State>, backend: Rc<dyn Backend>) {
|
||||||
|
|
||||||
struct Idle {
|
struct Idle {
|
||||||
state: Rc<State>,
|
state: Rc<State>,
|
||||||
backend: Rc<dyn Backend>,
|
|
||||||
timer: TimerFd,
|
timer: TimerFd,
|
||||||
idle: bool,
|
idle: bool,
|
||||||
dead: bool,
|
dead: bool,
|
||||||
|
|
@ -71,7 +69,7 @@ impl Idle {
|
||||||
if let Some(config) = self.state.config.get() {
|
if let Some(config) = self.state.config.get() {
|
||||||
config.idle();
|
config.idle();
|
||||||
}
|
}
|
||||||
self.backend.set_idle(true);
|
self.set_idle(true);
|
||||||
self.idle = true;
|
self.idle = true;
|
||||||
}
|
}
|
||||||
} else if since >= timeout {
|
} else if since >= timeout {
|
||||||
|
|
@ -110,7 +108,7 @@ impl Idle {
|
||||||
self.last_input = now();
|
self.last_input = now();
|
||||||
self.set_in_grace_period(false);
|
self.set_in_grace_period(false);
|
||||||
if self.idle {
|
if self.idle {
|
||||||
self.backend.set_idle(false);
|
self.set_idle(false);
|
||||||
self.idle = false;
|
self.idle = false;
|
||||||
self.program_timer();
|
self.program_timer();
|
||||||
}
|
}
|
||||||
|
|
@ -127,6 +125,27 @@ impl Idle {
|
||||||
self.dead = true;
|
self.dead = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_idle(&self, idle: bool) {
|
||||||
|
if let Err(e) = self.try_set_idle(idle) {
|
||||||
|
log::error!("Could not change idle status of backend: {}", ErrorFmt(e))
|
||||||
|
}
|
||||||
|
if let Some(lock) = self.state.lock.lock.get() {
|
||||||
|
lock.check_locked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_set_idle(&self, idle: bool) -> Result<(), BackendConnectorTransactionError> {
|
||||||
|
let mut tran = ConnectorTransaction::default();
|
||||||
|
for connector in self.state.connectors.lock().values() {
|
||||||
|
let mut state = connector.state.get();
|
||||||
|
state.active = !idle;
|
||||||
|
tran.add(&connector.connector, state)?;
|
||||||
|
}
|
||||||
|
tran.prepare()?.apply()?.commit();
|
||||||
|
self.state.set_backend_idle(idle);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn now() -> c::timespec {
|
fn now() -> c::timespec {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::{BackendColorSpace, BackendTransferFunction, HardwareCursor, KeyState, Mode},
|
backend::{
|
||||||
|
BackendColorSpace, BackendConnectorState, BackendTransferFunction, HardwareCursor,
|
||||||
|
KeyState, Mode,
|
||||||
|
},
|
||||||
client::ClientId,
|
client::ClientId,
|
||||||
cmm::cmm_description::ColorDescription,
|
cmm::cmm_description::ColorDescription,
|
||||||
cursor::KnownCursor,
|
cursor::KnownCursor,
|
||||||
|
|
@ -207,10 +210,14 @@ impl OutputNode {
|
||||||
seq: u64,
|
seq: u64,
|
||||||
flags: u32,
|
flags: u32,
|
||||||
vrr: bool,
|
vrr: bool,
|
||||||
|
locked: bool,
|
||||||
) {
|
) {
|
||||||
for listener in self.presentation_event.iter() {
|
for listener in self.presentation_event.iter() {
|
||||||
listener.presented(self, tv_sec, tv_nsec, refresh, seq, flags, vrr);
|
listener.presented(self, tv_sec, tv_nsec, refresh, seq, flags, vrr);
|
||||||
}
|
}
|
||||||
|
if locked && let Some(lock) = self.state.lock.lock.get() {
|
||||||
|
lock.check_locked()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_exclusive_zones(self: &Rc<Self>) {
|
pub fn update_exclusive_zones(self: &Rc<Self>) {
|
||||||
|
|
@ -842,7 +849,23 @@ impl OutputNode {
|
||||||
self.state.tree_changed();
|
self.state.tree_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_btf_and_bcs(&self, btf: BackendTransferFunction, bcs: BackendColorSpace) {
|
pub fn update_state(self: &Rc<Self>, state: BackendConnectorState) {
|
||||||
|
let old = self.global.connector.state.get();
|
||||||
|
if old.serial >= state.serial {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.global.connector.state.set(state);
|
||||||
|
self.update_btf_and_bcs(state.transfer_function, state.color_space);
|
||||||
|
if old.vrr != state.vrr {
|
||||||
|
self.schedule.set_vrr_enabled(state.vrr);
|
||||||
|
}
|
||||||
|
if old.mode != state.mode {
|
||||||
|
self.update_mode(state.mode);
|
||||||
|
}
|
||||||
|
self.global.format.set(state.format);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_btf_and_bcs(&self, btf: BackendTransferFunction, bcs: BackendColorSpace) {
|
||||||
let old_btf = self.global.btf.replace(btf);
|
let old_btf = self.global.btf.replace(btf);
|
||||||
let old_bcs = self.global.bcs.replace(bcs);
|
let old_bcs = self.global.bcs.replace(bcs);
|
||||||
if (old_btf, old_bcs) == (btf, bcs) {
|
if (old_btf, old_bcs) == (btf, bcs) {
|
||||||
|
|
@ -1067,7 +1090,13 @@ impl OutputNode {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.global.connector.connector.set_vrr_enabled(enabled);
|
let res = self
|
||||||
|
.global
|
||||||
|
.connector
|
||||||
|
.modify_state(&self.state, |s| s.vrr = enabled);
|
||||||
|
if let Err(e) = res {
|
||||||
|
log::error!("Could not set vrr mode: {}", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_tearing(&self) {
|
fn update_tearing(&self) {
|
||||||
|
|
@ -1094,7 +1123,13 @@ impl OutputNode {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.global.connector.connector.set_tearing_enabled(enabled);
|
let res = self
|
||||||
|
.global
|
||||||
|
.connector
|
||||||
|
.modify_state(&self.state, |s| s.tearing = enabled);
|
||||||
|
if let Err(e) = res {
|
||||||
|
log::error!("Could not set tearing mode: {}", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tile_drag_destination(
|
pub fn tile_drag_destination(
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ pub mod activation_token;
|
||||||
pub mod array;
|
pub mod array;
|
||||||
pub mod array_to_tuple;
|
pub mod array_to_tuple;
|
||||||
pub mod asyncevent;
|
pub mod asyncevent;
|
||||||
|
pub mod binary_search_map;
|
||||||
pub mod bindings;
|
pub mod bindings;
|
||||||
pub mod bitfield;
|
pub mod bitfield;
|
||||||
pub mod bitflags;
|
pub mod bitflags;
|
||||||
|
|
|
||||||
219
src/utils/binary_search_map.rs
Normal file
219
src/utils/binary_search_map.rs
Normal file
|
|
@ -0,0 +1,219 @@
|
||||||
|
use {
|
||||||
|
crate::utils::ptr_ext::{MutPtrExt, PtrExt},
|
||||||
|
smallvec::SmallVec,
|
||||||
|
std::{
|
||||||
|
fmt::{Debug, Formatter},
|
||||||
|
mem,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct BinarySearchMap<K, V, const N: usize> {
|
||||||
|
m: SmallVec<[(K, V); N]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Debug, V: Debug, const N: usize> Debug for BinarySearchMap<K, V, N> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_map()
|
||||||
|
.entries(self.m.iter().map(|e| (&e.0, &e.1)))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V, const N: usize> Default for BinarySearchMap<K, V, N> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
m: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V, const N: usize> BinarySearchMap<K, V, N> {
|
||||||
|
pub fn new_with(k: K, v: V) -> Self {
|
||||||
|
let mut sv = SmallVec::new();
|
||||||
|
sv.push((k, v));
|
||||||
|
Self { m: sv }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
m: SmallVec::new_const(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.m.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pos(&self, k: &K) -> Result<usize, usize>
|
||||||
|
where
|
||||||
|
K: Ord + Eq,
|
||||||
|
{
|
||||||
|
self.m.binary_search_by(|(c, _)| c.cmp(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, k: &K) -> bool
|
||||||
|
where
|
||||||
|
K: Ord + Eq,
|
||||||
|
{
|
||||||
|
self.pos(k).is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn not_contains(&self, k: &K) -> bool
|
||||||
|
where
|
||||||
|
K: Ord + Eq,
|
||||||
|
{
|
||||||
|
!self.contains(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, k: K, v: V) -> Option<V>
|
||||||
|
where
|
||||||
|
K: Ord + Eq,
|
||||||
|
{
|
||||||
|
match self.pos(&k) {
|
||||||
|
Ok(p) => Some(mem::replace(&mut self.m[p], (k, v)).1),
|
||||||
|
Err(p) => {
|
||||||
|
self.m.insert(p, (k, v));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, k: &K) -> Option<&V>
|
||||||
|
where
|
||||||
|
K: Ord + Eq,
|
||||||
|
{
|
||||||
|
self.pos(k).ok().map(|p| &self.m[p].1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self, k: &K) -> Option<&mut V>
|
||||||
|
where
|
||||||
|
K: Ord + Eq,
|
||||||
|
{
|
||||||
|
self.pos(k).ok().map(|p| &mut self.m[p].1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_or_default_mut(&mut self, k: K) -> &mut V
|
||||||
|
where
|
||||||
|
K: Ord + Eq,
|
||||||
|
V: Default,
|
||||||
|
{
|
||||||
|
self.get_or_insert_with(k, || V::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_or_insert_with<F>(&mut self, k: K, f: F) -> &mut V
|
||||||
|
where
|
||||||
|
K: Ord + Eq,
|
||||||
|
F: FnOnce() -> V,
|
||||||
|
{
|
||||||
|
let p = match self.pos(&k) {
|
||||||
|
Ok(p) => return &mut self.m[p].1,
|
||||||
|
Err(p) => p,
|
||||||
|
};
|
||||||
|
self.m.insert(p, (k, f()));
|
||||||
|
&mut self.m[p].1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.m.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, k: &K) -> Option<V>
|
||||||
|
where
|
||||||
|
K: Ord + Eq,
|
||||||
|
{
|
||||||
|
if let Ok(p) = self.pos(k) {
|
||||||
|
return Some(self.m.remove(p).1);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
let _v = mem::replace(&mut self.m, SmallVec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take(&mut self) -> SmallVec<[(K, V); N]> {
|
||||||
|
mem::take(&mut self.m)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter<'a>(&'a self) -> BinarySearchMapIter<'a, K, V, N> {
|
||||||
|
BinarySearchMapIter { pos: 0, map: self }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn values<'a>(&'a self) -> impl Iterator<Item = &'a V> + 'a {
|
||||||
|
self.iter().map(|(_, v)| v)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter_mut<'a>(&'a mut self) -> BinarySearchMapMutIterMut<'a, K, V, N> {
|
||||||
|
BinarySearchMapMutIterMut { pos: 0, map: self }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn values_mut<'a>(&'a mut self) -> impl Iterator<Item = &'a mut V> + 'a {
|
||||||
|
self.iter_mut().map(|(_, v)| v)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_if<F: FnMut(&K, &V) -> bool>(&mut self, mut f: F) {
|
||||||
|
let mut i = 0;
|
||||||
|
while i < self.m.len() {
|
||||||
|
let (k, v) = &self.m[i];
|
||||||
|
if f(k, v) {
|
||||||
|
self.m.remove(i);
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, K: Copy, V, const N: usize> IntoIterator for &'a BinarySearchMap<K, V, N> {
|
||||||
|
type Item = (&'a K, &'a V);
|
||||||
|
type IntoIter = BinarySearchMapIter<'a, K, V, N>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
BinarySearchMapIter { pos: 0, map: self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, K: Copy, V, const N: usize> IntoIterator for &'a mut BinarySearchMap<K, V, N> {
|
||||||
|
type Item = (&'a K, &'a mut V);
|
||||||
|
type IntoIter = BinarySearchMapMutIterMut<'a, K, V, N>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
BinarySearchMapMutIterMut { pos: 0, map: self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BinarySearchMapIter<'a, K, V, const N: usize> {
|
||||||
|
pos: usize,
|
||||||
|
map: &'a BinarySearchMap<K, V, N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, K, V, const N: usize> Iterator for BinarySearchMapIter<'a, K, V, N> {
|
||||||
|
type Item = (&'a K, &'a V);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.pos >= self.map.m.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let (k, v) = &self.map.m[self.pos];
|
||||||
|
self.pos += 1;
|
||||||
|
Some((k, v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BinarySearchMapMutIterMut<'a, K, V, const N: usize> {
|
||||||
|
pos: usize,
|
||||||
|
map: &'a mut BinarySearchMap<K, V, N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, K, V, const N: usize> Iterator for BinarySearchMapMutIterMut<'a, K, V, N> {
|
||||||
|
type Item = (&'a K, &'a mut V);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.pos >= self.map.m.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let (k, v) = &mut self.map.m[self.pos];
|
||||||
|
self.pos += 1;
|
||||||
|
unsafe { Some(((k as *const K).deref(), (v as *mut V).deref_mut())) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -710,7 +710,7 @@ pub trait DrmObject {
|
||||||
macro_rules! drm_obj {
|
macro_rules! drm_obj {
|
||||||
($name:ident, $ty:expr) => {
|
($name:ident, $ty:expr) => {
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default, Ord, PartialOrd)]
|
||||||
pub struct $name(pub u32);
|
pub struct $name(pub u32);
|
||||||
|
|
||||||
impl DrmObject for $name {
|
impl DrmObject for $name {
|
||||||
|
|
@ -929,11 +929,6 @@ pub struct hdr_metadata_infoframe {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrmModeInfo {
|
impl DrmModeInfo {
|
||||||
pub fn create_blob(&self, master: &Rc<DrmMaster>) -> Result<PropBlob, DrmError> {
|
|
||||||
let raw = self.to_raw();
|
|
||||||
master.create_blob(&raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_raw(&self) -> drm_mode_modeinfo {
|
pub fn to_raw(&self) -> drm_mode_modeinfo {
|
||||||
let mut name = [0u8; DRM_DISPLAY_MODE_LEN];
|
let mut name = [0u8; DRM_DISPLAY_MODE_LEN];
|
||||||
let len = name.len().min(self.name.len());
|
let len = name.len().min(self.name.len());
|
||||||
|
|
@ -1006,7 +1001,6 @@ pub struct ObjectChange<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Change {
|
impl Change {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn test(&self, flags: u32) -> Result<(), DrmError> {
|
pub fn test(&self, flags: u32) -> Result<(), DrmError> {
|
||||||
mode_atomic(
|
mode_atomic(
|
||||||
self.master.raw(),
|
self.master.raw(),
|
||||||
|
|
@ -1031,7 +1025,15 @@ impl Change {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_object<T, F>(&mut self, obj: T, f: F)
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.values.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_not_empty(&self) -> bool {
|
||||||
|
!self.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn change_object<T, F>(&mut self, obj: T, f: F) -> bool
|
||||||
where
|
where
|
||||||
T: DrmObject,
|
T: DrmObject,
|
||||||
F: FnOnce(&mut ObjectChange),
|
F: FnOnce(&mut ObjectChange),
|
||||||
|
|
@ -1047,14 +1049,17 @@ impl Change {
|
||||||
self.objects.push(obj.id());
|
self.objects.push(obj.id());
|
||||||
self.object_lengths.push(new);
|
self.object_lengths.push(new);
|
||||||
}
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ObjectChange<'a> {
|
impl<'a> ObjectChange<'a> {
|
||||||
pub fn change(&mut self, property_id: DrmProperty, value: u64) {
|
pub fn change(&mut self, property_id: DrmProperty, value: impl ObjectChangeValue) {
|
||||||
self.change.props.push(property_id.0);
|
self.change.props.push(property_id.0);
|
||||||
self.change.values.push(value);
|
self.change.values.push(value.into_u64());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1069,6 +1074,36 @@ impl Drop for Change {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ObjectChangeValue {
|
||||||
|
fn into_u64(self) -> u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! num {
|
||||||
|
($ty:ty) => {
|
||||||
|
impl ObjectChangeValue for $ty {
|
||||||
|
fn into_u64(self) -> u64 {
|
||||||
|
self as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
num!(u16);
|
||||||
|
num!(i32);
|
||||||
|
num!(u32);
|
||||||
|
num!(i64);
|
||||||
|
num!(u64);
|
||||||
|
num!(bool);
|
||||||
|
|
||||||
|
impl<T> ObjectChangeValue for T
|
||||||
|
where
|
||||||
|
T: DrmObject,
|
||||||
|
{
|
||||||
|
fn into_u64(self) -> u64 {
|
||||||
|
self.id() as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[expect(non_camel_case_types)]
|
#[expect(non_camel_case_types)]
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum ConnectorType {
|
pub enum ConnectorType {
|
||||||
|
|
|
||||||
|
|
@ -693,6 +693,7 @@ pub fn mode_getencoder(fd: c::c_int, encoder_id: u32) -> Result<DrmEncoderInfo,
|
||||||
pub const DRM_DISPLAY_MODE_LEN: usize = 32;
|
pub const DRM_DISPLAY_MODE_LEN: usize = 32;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct drm_mode_modeinfo {
|
pub struct drm_mode_modeinfo {
|
||||||
pub clock: u32,
|
pub clock: u32,
|
||||||
pub hdisplay: u16,
|
pub hdisplay: u16,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue