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 {
|
||||
crate::{
|
||||
async_engine::SpawnedFuture,
|
||||
backend::transaction::{
|
||||
BackendConnectorTransaction, BackendConnectorTransactionError,
|
||||
BackendConnectorTransactionType, BackendConnectorTransactionTypeDyn,
|
||||
},
|
||||
cmm::cmm_primaries::Primaries,
|
||||
drm_feedback::DrmFeedback,
|
||||
fixed::Fixed,
|
||||
|
|
@ -30,11 +34,14 @@ use {
|
|||
any::Any,
|
||||
error::Error,
|
||||
fmt::{Debug, Display, Formatter},
|
||||
hash::Hash,
|
||||
rc::Rc,
|
||||
},
|
||||
uapi::{OwnedFd, c},
|
||||
};
|
||||
|
||||
pub mod transaction;
|
||||
|
||||
linear_ids!(ConnectorIds, ConnectorId);
|
||||
linear_ids!(InputDeviceIds, InputDeviceId);
|
||||
linear_ids!(DrmDeviceIds, DrmDeviceId);
|
||||
|
|
@ -49,10 +56,6 @@ pub trait Backend: Any {
|
|||
let _ = vtnr;
|
||||
}
|
||||
|
||||
fn set_idle(&self, idle: bool) {
|
||||
let _ = idle;
|
||||
}
|
||||
|
||||
fn import_environment(&self) -> bool {
|
||||
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)]
|
||||
pub struct MonitorInfo {
|
||||
pub modes: Vec<Mode>,
|
||||
pub output_id: Rc<OutputId>,
|
||||
pub initial_mode: Mode,
|
||||
pub width_mm: i32,
|
||||
pub height_mm: i32,
|
||||
pub non_desktop: bool,
|
||||
pub vrr_capable: bool,
|
||||
pub transfer_functions: Vec<BackendTransferFunction>,
|
||||
pub transfer_function: BackendTransferFunction,
|
||||
pub color_spaces: Vec<BackendColorSpace>,
|
||||
pub color_space: BackendColorSpace,
|
||||
pub primaries: Primaries,
|
||||
pub luminance: Option<BackendLuminance>,
|
||||
pub state: BackendConnectorState,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
@ -111,41 +124,35 @@ impl Display for ConnectorKernelId {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait Connector {
|
||||
pub trait Connector: Any {
|
||||
fn id(&self) -> ConnectorId;
|
||||
fn kernel_id(&self) -> ConnectorKernelId;
|
||||
fn event(&self) -> Option<ConnectorEvent>;
|
||||
fn on_change(&self, cb: Rc<dyn Fn()>);
|
||||
fn damage(&self);
|
||||
fn drm_dev(&self) -> Option<DrmDeviceId>;
|
||||
fn enabled(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn set_enabled(&self, enabled: bool) {
|
||||
let _ = enabled;
|
||||
}
|
||||
fn effectively_locked(&self) -> bool;
|
||||
fn drm_feedback(&self) -> Option<Rc<DrmFeedback>> {
|
||||
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> {
|
||||
None
|
||||
}
|
||||
fn set_vrr_enabled(&self, enabled: bool) {
|
||||
let _ = enabled;
|
||||
fn before_non_desktop_override_update(&self, overrd: Option<bool>) {
|
||||
let _ = overrd;
|
||||
}
|
||||
fn set_tearing_enabled(&self, enabled: bool) {
|
||||
let _ = enabled;
|
||||
fn transaction_type(&self) -> Box<dyn BackendConnectorTransactionTypeDyn> {
|
||||
#[derive(Hash, Eq, PartialEq)]
|
||||
struct UnimplementedConnectorTransactionType;
|
||||
impl BackendConnectorTransactionType for UnimplementedConnectorTransactionType {}
|
||||
Box::new(UnimplementedConnectorTransactionType)
|
||||
}
|
||||
fn set_fb_format(&self, format: &'static Format) {
|
||||
let _ = format;
|
||||
}
|
||||
fn set_colors(&self, bcs: BackendColorSpace, btf: BackendTransferFunction) {
|
||||
let _ = bcs;
|
||||
let _ = btf;
|
||||
fn create_transaction(
|
||||
&self,
|
||||
) -> Result<Box<dyn BackendConnectorTransaction>, BackendConnectorTransactionError> {
|
||||
Err(BackendConnectorTransactionError::TransactionsNotSupported(
|
||||
self.kernel_id(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -155,12 +162,10 @@ pub enum ConnectorEvent {
|
|||
HardwareCursor(Option<Rc<dyn HardwareCursor>>),
|
||||
Disconnected,
|
||||
Removed,
|
||||
ModeChanged(Mode),
|
||||
Unavailable,
|
||||
Available,
|
||||
VrrChanged(bool),
|
||||
FormatsChanged(Rc<Vec<&'static Format>>, &'static Format),
|
||||
ColorsChanged(BackendColorSpace, BackendTransferFunction),
|
||||
State(BackendConnectorState),
|
||||
FormatsChanged(Rc<Vec<&'static Format>>),
|
||||
}
|
||||
|
||||
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::{
|
||||
async_engine::SpawnedFuture,
|
||||
backend::{
|
||||
Backend, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId, Mode,
|
||||
Backend, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId,
|
||||
},
|
||||
video::drm::ConnectorType,
|
||||
},
|
||||
|
|
@ -49,7 +49,7 @@ impl Connector for DummyOutput {
|
|||
None
|
||||
}
|
||||
|
||||
fn set_mode(&self, _mode: Mode) {
|
||||
// nothing
|
||||
fn effectively_locked(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
mod input;
|
||||
mod monitor;
|
||||
mod present;
|
||||
mod transaction;
|
||||
mod video;
|
||||
|
||||
use {
|
||||
|
|
@ -9,7 +10,7 @@ use {
|
|||
backend::{
|
||||
Backend, InputDevice, InputDeviceAccelProfile, InputDeviceCapability,
|
||||
InputDeviceClickMethod, InputDeviceGroupId, InputDeviceId, InputEvent, KeyState,
|
||||
TransformMatrix,
|
||||
TransformMatrix, transaction::BackendConnectorTransactionError,
|
||||
},
|
||||
backends::metal::video::{
|
||||
MetalDrmDeviceData, MetalLeaseData, MetalRenderContext, PendingDrmDevice,
|
||||
|
|
@ -47,10 +48,7 @@ use {
|
|||
smallmap::SmallMap,
|
||||
syncqueue::SyncQueue,
|
||||
},
|
||||
video::{
|
||||
drm::{DRM_MODE_ATOMIC_ALLOW_MODESET, DrmError},
|
||||
gbm::GbmError,
|
||||
},
|
||||
video::{drm::DrmError, gbm::GbmError},
|
||||
},
|
||||
bstr::ByteSlice,
|
||||
std::{
|
||||
|
|
@ -87,12 +85,6 @@ pub enum MetalError {
|
|||
UpdateProperties(#[source] DrmError),
|
||||
#[error("Could not create a render context")]
|
||||
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")]
|
||||
ScanoutBuffer(#[source] GbmError),
|
||||
#[error("addfb2 failed")]
|
||||
|
|
@ -104,7 +96,7 @@ pub enum MetalError {
|
|||
#[error("Could not import an image into the graphics API")]
|
||||
ImportImage(#[source] GfxError),
|
||||
#[error("Could not perform modeset")]
|
||||
Modeset(#[source] DrmError),
|
||||
Modeset(#[source] BackendConnectorTransactionError),
|
||||
#[error("Could not enable atomic modesetting")]
|
||||
AtomicModesetting(#[source] OsError),
|
||||
#[error("Could not inspect a plane")]
|
||||
|
|
@ -137,6 +129,12 @@ pub enum MetalError {
|
|||
Clear(#[source] GfxError),
|
||||
#[error("The present configuration is out of date")]
|
||||
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 {
|
||||
|
|
@ -204,6 +202,7 @@ impl Backend for MetalBackend {
|
|||
dev.futures.clear();
|
||||
for crtc in dev.dev.crtcs.values() {
|
||||
crtc.connector.take();
|
||||
crtc.pending_flip.take();
|
||||
}
|
||||
dev.dev.handle_events.handle_events.take();
|
||||
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 {
|
||||
true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use {
|
|||
backend::Connector,
|
||||
backends::metal::{
|
||||
MetalError,
|
||||
transaction::{DrmConnectorState, DrmPlaneState},
|
||||
video::{
|
||||
MetalConnector, MetalCrtc, MetalHardwareCursorChange, MetalPlane, RenderBuffer,
|
||||
},
|
||||
|
|
@ -22,18 +23,20 @@ use {
|
|||
dmabuf::DmaBufId,
|
||||
drm::{
|
||||
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},
|
||||
uapi::c,
|
||||
uapi::{OwnedFd, c},
|
||||
};
|
||||
|
||||
struct Latched {
|
||||
pass: GfxRenderPass,
|
||||
damage_count: u64,
|
||||
damage: Region,
|
||||
locked: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -68,11 +71,18 @@ pub struct PresentFb {
|
|||
tex: Rc<dyn GfxTexture>,
|
||||
direct_scanout_data: Option<DirectScanoutData>,
|
||||
sync_file: Option<SyncFile>,
|
||||
pub locked: bool,
|
||||
}
|
||||
|
||||
enum CursorProgramming {
|
||||
#[derive(Debug)]
|
||||
struct CursorProgramming {
|
||||
plane: Rc<MetalPlane>,
|
||||
ty: CursorProgrammingType,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum CursorProgrammingType {
|
||||
Enable {
|
||||
plane: Rc<MetalPlane>,
|
||||
fb: Rc<DrmFramebuffer>,
|
||||
x: i32,
|
||||
y: i32,
|
||||
|
|
@ -80,9 +90,12 @@ enum CursorProgramming {
|
|||
height: i32,
|
||||
swap: bool,
|
||||
},
|
||||
Disable {
|
||||
plane: Rc<MetalPlane>,
|
||||
},
|
||||
Disable,
|
||||
}
|
||||
|
||||
struct ChangedPlane {
|
||||
plane: Rc<MetalPlane>,
|
||||
state: DrmPlaneState,
|
||||
}
|
||||
|
||||
pub const DEFAULT_PRE_COMMIT_MARGIN: u64 = 16_000_000; // 16ms
|
||||
|
|
@ -101,13 +114,17 @@ impl MetalConnector {
|
|||
let mut max = 0;
|
||||
loop {
|
||||
self.present_trigger.triggered().await;
|
||||
if !self.can_present.get() {
|
||||
if !self.buffers_idle.get() || !self.crtc_idle.get() {
|
||||
continue;
|
||||
}
|
||||
let Some(crtc) = self.crtc.get() else {
|
||||
continue;
|
||||
};
|
||||
let Some(node) = self.state.root.outputs.get(&self.connector_id) else {
|
||||
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 use_frame_scheduling = !self.try_async_flip();
|
||||
if use_frame_scheduling {
|
||||
|
|
@ -132,7 +149,11 @@ impl MetalConnector {
|
|||
};
|
||||
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));
|
||||
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();
|
||||
if !self.can_present.get() {
|
||||
if !self.buffers_idle.get() || !self.crtc_idle.get() {
|
||||
return Ok(());
|
||||
}
|
||||
if !self.backend.check_render_context(&self.dev) {
|
||||
return Ok(());
|
||||
}
|
||||
let crtc = match self.crtc.get() {
|
||||
Some(crtc) => crtc,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
if !crtc.active.value.get() {
|
||||
if !crtc.drm_state.borrow().active {
|
||||
return Ok(());
|
||||
}
|
||||
let plane = match self.primary_plane.get() {
|
||||
|
|
@ -175,7 +196,9 @@ impl MetalConnector {
|
|||
Some(b) => b,
|
||||
_ => 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 linear_cd = node.global.linear_color_description.get();
|
||||
|
|
@ -183,8 +206,8 @@ impl MetalConnector {
|
|||
if self.has_damage.get() > 0 || self.cursor_damage.get() {
|
||||
node.schedule.commit_cursor();
|
||||
}
|
||||
self.latch_cursor(&node, &cd)?;
|
||||
let cursor_programming = self.compute_cursor_programming();
|
||||
self.latch_cursor(&node, &connector_drm_state, &cd)?;
|
||||
let cursor_programming = self.compute_cursor_programming(&connector_drm_state);
|
||||
let latched = self.latch(&node, buffer);
|
||||
node.latched(self.try_async_flip());
|
||||
|
||||
|
|
@ -209,12 +232,15 @@ impl MetalConnector {
|
|||
);
|
||||
}
|
||||
self.await_present_fb(present_fb.as_mut()).await;
|
||||
let mut changed_planes = ArrayVec::new();
|
||||
let mut res = self.program_connector(
|
||||
version,
|
||||
&crtc,
|
||||
&plane,
|
||||
cursor_programming.as_ref(),
|
||||
present_fb.as_ref(),
|
||||
&mut changed_planes,
|
||||
&mut connector_drm_state,
|
||||
);
|
||||
if res.is_err()
|
||||
&& let Some(dsd_id) = direct_scanout_id
|
||||
|
|
@ -235,6 +261,8 @@ impl MetalConnector {
|
|||
&plane,
|
||||
cursor_programming.as_ref(),
|
||||
present_fb.as_ref(),
|
||||
&mut changed_planes,
|
||||
&mut connector_drm_state,
|
||||
);
|
||||
if res.is_ok() {
|
||||
let mut cache = self.scanout_buffers.borrow_mut();
|
||||
|
|
@ -265,35 +293,30 @@ impl MetalConnector {
|
|||
}
|
||||
Err(e)
|
||||
} else {
|
||||
macro_rules! apply_change {
|
||||
($prop:expr) => {
|
||||
if let Some(v) = $prop.pending_value.take() {
|
||||
$prop.value.set(v);
|
||||
}
|
||||
};
|
||||
crtc.pending_flip.set(Some(self.clone()));
|
||||
self.crtc_idle.set(false);
|
||||
self.color_description.set(cd);
|
||||
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 {
|
||||
self.presentation_is_zero_copy
|
||||
.set(fb.direct_scanout_data.is_some());
|
||||
if fb.direct_scanout_data.is_none() {
|
||||
buffer.damage_queue.clear();
|
||||
self.next_buffer.fetch_add(1);
|
||||
} else {
|
||||
reset_damage();
|
||||
}
|
||||
buffer.locked.set(fb.locked);
|
||||
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_front_buffer.fetch_add(1);
|
||||
}
|
||||
self.can_present.set(false);
|
||||
self.buffers_idle.set(false);
|
||||
if let Some(latched) = latched {
|
||||
self.has_damage.fetch_sub(latched.damage_count);
|
||||
}
|
||||
|
|
@ -318,7 +341,7 @@ impl MetalConnector {
|
|||
}
|
||||
|
||||
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(
|
||||
|
|
@ -328,19 +351,15 @@ impl MetalConnector {
|
|||
plane: &Rc<MetalPlane>,
|
||||
cursor: Option<&CursorProgramming>,
|
||||
new_fb: Option<&PresentFb>,
|
||||
changed_planes: &mut ArrayVec<ChangedPlane, 2>,
|
||||
connector_drm_state: &mut DrmConnectorState,
|
||||
) -> Result<(), MetalError> {
|
||||
zone!("program_connector");
|
||||
let mut changes = self.master.change();
|
||||
let mut try_async_flip = self.try_async_flip();
|
||||
macro_rules! change {
|
||||
($c:expr, $prop:expr, $new:expr) => {{
|
||||
if $prop.value.get() != $new {
|
||||
$c.change($prop.id, $new as u64);
|
||||
try_async_flip = false;
|
||||
$prop.pending_value.set(Some($new));
|
||||
}
|
||||
}};
|
||||
}
|
||||
let mut drm_state = plane.drm_state.borrow().clone();
|
||||
changed_planes.clear();
|
||||
let mut connector_state = connector_drm_state.clone();
|
||||
if let Some(fb) = new_fb {
|
||||
let (crtc_x, crtc_y, crtc_w, crtc_h, src_width, src_height) =
|
||||
match &fb.direct_scanout_data {
|
||||
|
|
@ -362,69 +381,90 @@ impl MetalConnector {
|
|||
}
|
||||
};
|
||||
changes.change_object(plane.id, |c| {
|
||||
c.change(plane.fb_id, fb.fb.id().0 as _);
|
||||
change!(c, plane.src_w, (src_width as u32) << 16);
|
||||
change!(c, plane.src_h, (src_height as u32) << 16);
|
||||
change!(c, plane.crtc_x, crtc_x);
|
||||
change!(c, plane.crtc_y, crtc_y);
|
||||
change!(c, plane.crtc_w, crtc_w);
|
||||
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);
|
||||
c.change(plane.fb_id, fb.fb.id());
|
||||
drm_state.fb_id = fb.fb.id();
|
||||
connector_state.fb = fb.fb.id();
|
||||
connector_state.locked = fb.locked;
|
||||
if fb.direct_scanout_data.is_none() {
|
||||
connector_state.fb_idx += 1;
|
||||
}
|
||||
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 {
|
||||
let plane = &cursor.plane;
|
||||
let mut drm_state = plane.drm_state.borrow().clone();
|
||||
try_async_flip = false;
|
||||
match cursor {
|
||||
CursorProgramming::Enable {
|
||||
plane,
|
||||
fb,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
..
|
||||
} => {
|
||||
changes.change_object(plane.id, |c| {
|
||||
c.change(plane.fb_id, fb.id().0 as _);
|
||||
c.change(plane.crtc_id.id, crtc.id.0 as _);
|
||||
c.change(plane.crtc_x.id, *x as _);
|
||||
c.change(plane.crtc_y.id, *y as _);
|
||||
c.change(plane.crtc_w.id, *width as _);
|
||||
c.change(plane.crtc_h.id, *height as _);
|
||||
c.change(plane.src_x.id, 0);
|
||||
c.change(plane.src_y.id, 0);
|
||||
c.change(plane.src_w.id, (*width as u64) << 16);
|
||||
c.change(plane.src_h.id, (*height as u64) << 16);
|
||||
changes.change_object(plane.id, |c| {
|
||||
macro_rules! change {
|
||||
($prop:ident, $new:expr) => {{
|
||||
c.change(plane.$prop, $new);
|
||||
drm_state.$prop = $new;
|
||||
}};
|
||||
}
|
||||
match &cursor.ty {
|
||||
CursorProgrammingType::Enable {
|
||||
fb,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
swap,
|
||||
} => {
|
||||
connector_state.cursor_fb = fb.id();
|
||||
if *swap {
|
||||
connector_state.cursor_fb_idx += 1;
|
||||
}
|
||||
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
|
||||
&& let Some(sf) = self.backend.signaled_sync_file.get()
|
||||
{
|
||||
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| {
|
||||
c.change(plane.fb_id, 0);
|
||||
c.change(plane.crtc_id.id, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
changed_planes.push(ChangedPlane {
|
||||
plane: plane.clone(),
|
||||
state: drm_state,
|
||||
});
|
||||
}
|
||||
let mut out_fd: c::c_int = -1;
|
||||
if version != self.version.get() {
|
||||
return Err(MetalError::OutOfDate);
|
||||
}
|
||||
|
|
@ -439,14 +479,32 @@ impl MetalConnector {
|
|||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
fn latch_cursor(
|
||||
&self,
|
||||
node: &Rc<OutputNode>,
|
||||
connector_drm_state: &DrmConnectorState,
|
||||
cd: &Rc<ColorDescription>,
|
||||
) -> Result<(), MetalError> {
|
||||
if !self.cursor_damage.take() {
|
||||
|
|
@ -456,12 +514,13 @@ impl MetalConnector {
|
|||
return Ok(());
|
||||
}
|
||||
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 {
|
||||
cursor_enabled: self.cursor_enabled.get(),
|
||||
cursor_swap_buffer: false,
|
||||
cursor_x: self.cursor_x.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,
|
||||
cursor_size: (self.dev.cursor_width as _, self.dev.cursor_height as _),
|
||||
};
|
||||
|
|
@ -484,22 +543,25 @@ impl MetalConnector {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_cursor_programming(&self) -> Option<CursorProgramming> {
|
||||
fn compute_cursor_programming(
|
||||
&self,
|
||||
connector_drm_state: &DrmConnectorState,
|
||||
) -> Option<CursorProgramming> {
|
||||
if !self.cursor_changed.get() {
|
||||
return None;
|
||||
}
|
||||
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 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 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();
|
||||
CursorProgramming::Enable {
|
||||
plane,
|
||||
CursorProgrammingType::Enable {
|
||||
fb: buffer.drm.clone(),
|
||||
x: self.cursor_x.get(),
|
||||
y: self.cursor_y.get(),
|
||||
|
|
@ -508,9 +570,9 @@ impl MetalConnector {
|
|||
swap,
|
||||
}
|
||||
} else {
|
||||
CursorProgramming::Disable { plane }
|
||||
CursorProgrammingType::Disable
|
||||
};
|
||||
Some(programming)
|
||||
Some(CursorProgramming { plane, ty })
|
||||
}
|
||||
|
||||
fn latch(&self, node: &Rc<OutputNode>, buffer: &RenderBuffer) -> Option<Latched> {
|
||||
|
|
@ -545,6 +607,7 @@ impl MetalConnector {
|
|||
pass,
|
||||
damage_count,
|
||||
damage,
|
||||
locked: self.state.lock.locked.get(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -797,6 +860,7 @@ impl MetalConnector {
|
|||
tex,
|
||||
direct_scanout_data,
|
||||
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,
|
||||
async_engine::{Phase, SpawnedFuture},
|
||||
backend::{
|
||||
AXIS_120, AxisSource, Backend, BackendColorSpace, BackendDrmDevice, BackendEvent,
|
||||
BackendTransferFunction, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId,
|
||||
DrmDeviceId, DrmEvent, InputDevice, InputDeviceAccelProfile, InputDeviceCapability,
|
||||
InputDeviceClickMethod, InputDeviceId, InputEvent, KeyState, Mode, MonitorInfo,
|
||||
ScrollAxis, TransformMatrix,
|
||||
AXIS_120, AxisSource, Backend, BackendConnectorState, BackendDrmDevice, BackendEvent,
|
||||
Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId, DrmEvent,
|
||||
InputDevice, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceClickMethod,
|
||||
InputDeviceId, InputEvent, KeyState, Mode, MonitorInfo, ScrollAxis, TransformMatrix,
|
||||
transaction::{
|
||||
BackendAppliedConnectorTransaction, BackendConnectorTransaction,
|
||||
BackendConnectorTransactionError, BackendConnectorTransactionType,
|
||||
BackendConnectorTransactionTypeDyn, BackendPreparedConnectorTransaction,
|
||||
},
|
||||
},
|
||||
cmm::cmm_primaries::Primaries,
|
||||
fixed::Fixed,
|
||||
format::XRGB8888,
|
||||
format::{Format, XRGB8888},
|
||||
gfx_api::{AcquireSync, GfxContext, GfxError, GfxFramebuffer, GfxTexture, ReleaseSync},
|
||||
ifs::wl_output::OutputId,
|
||||
state::State,
|
||||
|
|
@ -51,8 +55,10 @@ use {
|
|||
},
|
||||
},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
jay_config::video::GfxApi,
|
||||
std::{
|
||||
any::Any,
|
||||
borrow::Cow,
|
||||
cell::{Cell, RefCell},
|
||||
collections::VecDeque,
|
||||
|
|
@ -120,6 +126,8 @@ pub enum XBackendError {
|
|||
XRGB8888,
|
||||
}
|
||||
|
||||
const FORMAT: &Format = XRGB8888;
|
||||
|
||||
pub async fn create(state: &Rc<State>) -> Result<Rc<XBackend>, XBackendError> {
|
||||
let c = match Xcon::connect(state).await {
|
||||
Ok(c) => c,
|
||||
|
|
@ -376,7 +384,7 @@ impl XBackend {
|
|||
) -> Result<[XImage; 2], XBackendError> {
|
||||
let mut images = [None, None];
|
||||
let formats = self.ctx.formats();
|
||||
let format = match formats.get(&XRGB8888.drm) {
|
||||
let format = match formats.get(&FORMAT.drm) {
|
||||
Some(f) => f,
|
||||
None => return Err(XBackendError::XRGB8888),
|
||||
};
|
||||
|
|
@ -385,7 +393,7 @@ impl XBackend {
|
|||
&self.state.dma_buf_ids,
|
||||
width,
|
||||
height,
|
||||
XRGB8888,
|
||||
FORMAT,
|
||||
format.write_modifiers.keys(),
|
||||
GBM_BO_USE_RENDERING,
|
||||
)?;
|
||||
|
|
@ -469,6 +477,22 @@ impl XBackend {
|
|||
cw.wid
|
||||
};
|
||||
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 {
|
||||
id: self.state.connector_ids.next(),
|
||||
backend: self.clone(),
|
||||
|
|
@ -481,6 +505,7 @@ impl XBackend {
|
|||
next_image: Default::default(),
|
||||
cb: CloneCell::new(None),
|
||||
images,
|
||||
state: Cell::new(state),
|
||||
});
|
||||
{
|
||||
let class = "jay\0jay\0";
|
||||
|
|
@ -569,21 +594,15 @@ impl XBackend {
|
|||
format!("X-Window-{}", output.window),
|
||||
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(),
|
||||
height_mm: output.height.get(),
|
||||
non_desktop: false,
|
||||
vrr_capable: false,
|
||||
transfer_functions: vec![],
|
||||
transfer_function: BackendTransferFunction::Default,
|
||||
color_spaces: vec![],
|
||||
color_space: BackendColorSpace::Default,
|
||||
primaries: Primaries::SRGB,
|
||||
luminance: None,
|
||||
state: output.state.get(),
|
||||
}));
|
||||
output.changed();
|
||||
self.present(output).await;
|
||||
|
|
@ -962,11 +981,12 @@ impl XBackend {
|
|||
old.tex.set(new.tex.get());
|
||||
old.pixmap.set(new.pixmap.get());
|
||||
}
|
||||
output.events.push(ConnectorEvent::ModeChanged(Mode {
|
||||
width,
|
||||
height,
|
||||
refresh_rate_millihz: 60, // TODO
|
||||
}));
|
||||
let mut state = output.state.get();
|
||||
state.serial = self.state.backend_connector_state_serials.next();
|
||||
state.mode.width = width;
|
||||
state.mode.height = height;
|
||||
output.state.set(state);
|
||||
output.events.push(ConnectorEvent::State(state));
|
||||
output.changed();
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -1035,6 +1055,7 @@ struct XOutput {
|
|||
next_image: NumCell<usize>,
|
||||
images: [XImage; 2],
|
||||
cb: CloneCell<Option<Rc<dyn Fn()>>>,
|
||||
state: Cell<BackendConnectorState>,
|
||||
}
|
||||
|
||||
struct XImage {
|
||||
|
|
@ -1083,8 +1104,75 @@ impl Connector for XOutput {
|
|||
Some(self.backend.drm_device_id)
|
||||
}
|
||||
|
||||
fn set_mode(&self, _mode: Mode) {
|
||||
log::warn!("X backend doesn't support changing the connector mode");
|
||||
fn effectively_locked(&self) -> bool {
|
||||
// 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) {
|
||||
println!(" {}:", connector.name);
|
||||
if !connector.enabled {
|
||||
println!(" disabled");
|
||||
}
|
||||
let Some(o) = &connector.output else {
|
||||
if !connector.enabled {
|
||||
println!(" disabled");
|
||||
} else {
|
||||
if connector.enabled {
|
||||
println!(" disconnected");
|
||||
}
|
||||
return;
|
||||
|
|
@ -869,7 +870,9 @@ impl Randr {
|
|||
o.width_mm, o.height_mm
|
||||
);
|
||||
if o.non_desktop {
|
||||
println!(" non-desktop");
|
||||
if connector.enabled {
|
||||
println!(" non-desktop");
|
||||
}
|
||||
return;
|
||||
}
|
||||
println!(" VRR capable: {}", o.vrr_capable);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use {
|
|||
crate::{
|
||||
acceptor::{Acceptor, AcceptorError},
|
||||
async_engine::{AsyncEngine, Phase, SpawnedFuture},
|
||||
backend::{self, Backend, BackendColorSpace, BackendTransferFunction, Connector},
|
||||
backend::{self, Backend, BackendConnectorState, BackendConnectorStateSerial, Connector},
|
||||
backends::{
|
||||
dummy::{DummyBackend, DummyOutput},
|
||||
metal, x,
|
||||
|
|
@ -26,6 +26,7 @@ use {
|
|||
dbus::Dbus,
|
||||
ei::ei_client::EiClients,
|
||||
forker,
|
||||
format::XRGB8888,
|
||||
globals::Globals,
|
||||
ifs::{
|
||||
jay_screencast::{perform_screencast_realloc, perform_toplevel_screencasts},
|
||||
|
|
@ -337,6 +338,7 @@ fn start_compositor2(
|
|||
toplevel_managers: Default::default(),
|
||||
node_at_tree: Default::default(),
|
||||
position_hint_requests: Default::default(),
|
||||
backend_connector_state_serials: Default::default(),
|
||||
});
|
||||
state.tracker.register(ClientId::from_raw(0));
|
||||
create_dummy_output(&state);
|
||||
|
|
@ -396,7 +398,7 @@ async fn start_compositor3(state: Rc<State>, test_future: Option<TestFuture>) {
|
|||
}
|
||||
state.update_ei_acceptor();
|
||||
|
||||
let _geh = start_global_event_handlers(&state, &backend);
|
||||
let _geh = start_global_event_handlers(&state);
|
||||
state.start_xwayland();
|
||||
|
||||
match backend.run().await {
|
||||
|
|
@ -424,10 +426,7 @@ fn load_config(
|
|||
}
|
||||
}
|
||||
|
||||
fn start_global_event_handlers(
|
||||
state: &Rc<State>,
|
||||
backend: &Rc<dyn Backend>,
|
||||
) -> Vec<SpawnedFuture<()>> {
|
||||
fn start_global_event_handlers(state: &Rc<State>) -> Vec<SpawnedFuture<()>> {
|
||||
let eng = &state.eng;
|
||||
|
||||
vec![
|
||||
|
|
@ -471,11 +470,7 @@ fn start_global_event_handlers(
|
|||
Phase::PostLayout,
|
||||
float_titles(state.clone()),
|
||||
),
|
||||
eng.spawn2(
|
||||
"idle",
|
||||
Phase::PostLayout,
|
||||
idle(state.clone(), backend.clone()),
|
||||
),
|
||||
eng.spawn2("idle", Phase::PostLayout, idle(state.clone())),
|
||||
eng.spawn2(
|
||||
"input, popup positioning",
|
||||
Phase::PostLayout,
|
||||
|
|
@ -610,6 +605,23 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
let connector = Rc::new(DummyOutput {
|
||||
id: state.connector_ids.next(),
|
||||
}) 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 {
|
||||
connector,
|
||||
handler: Cell::new(None),
|
||||
|
|
@ -621,6 +633,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
damage: Default::default(),
|
||||
needs_vblank_emulation: Cell::new(false),
|
||||
damage_intersect: Default::default(),
|
||||
state: Cell::new(backend_state),
|
||||
});
|
||||
let schedule = Rc::new(OutputSchedule::new(
|
||||
&state.ring,
|
||||
|
|
@ -635,19 +648,12 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
state,
|
||||
&connector_data,
|
||||
Vec::new(),
|
||||
&backend::Mode {
|
||||
width: 0,
|
||||
height: 0,
|
||||
refresh_rate_millihz: 40_000,
|
||||
},
|
||||
0,
|
||||
0,
|
||||
&output_id,
|
||||
&persistent_state,
|
||||
Vec::new(),
|
||||
BackendTransferFunction::Default,
|
||||
Vec::new(),
|
||||
BackendColorSpace::Default,
|
||||
Primaries::SRGB,
|
||||
None,
|
||||
)),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use {
|
|||
backend::{
|
||||
self, BackendColorSpace, BackendTransferFunction, ConnectorId, DrmDeviceId,
|
||||
InputDeviceAccelProfile, InputDeviceCapability, InputDeviceClickMethod, InputDeviceId,
|
||||
transaction::BackendConnectorTransactionError,
|
||||
},
|
||||
client::{Client, ClientId},
|
||||
cmm::cmm_transfer_function::TransferFunction,
|
||||
|
|
@ -1141,12 +1142,16 @@ impl ConfigProxyHandler {
|
|||
connector: Connector,
|
||||
mode: WireMode,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self.get_output(connector)?;
|
||||
connector.connector.connector.set_mode(backend::Mode {
|
||||
width: mode.width,
|
||||
height: mode.height,
|
||||
refresh_rate_millihz: mode.refresh_millihz,
|
||||
});
|
||||
let connector = self.get_connector(connector)?;
|
||||
connector
|
||||
.modify_state(&self.state, |s| {
|
||||
s.mode = backend::Mode {
|
||||
width: mode.width,
|
||||
height: mode.height,
|
||||
refresh_rate_millihz: mode.refresh_millihz,
|
||||
};
|
||||
})
|
||||
.map_err(CphError::ModifyConnectorState)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -1265,7 +1270,9 @@ impl ConfigProxyHandler {
|
|||
return Err(CphError::UnknownFormat(format));
|
||||
};
|
||||
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(())
|
||||
}
|
||||
|
||||
|
|
@ -1286,7 +1293,12 @@ impl ConfigProxyHandler {
|
|||
_ => return Err(CphError::UnknownTransferFunction(transfer_function)),
|
||||
};
|
||||
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(())
|
||||
}
|
||||
|
||||
|
|
@ -1447,7 +1459,11 @@ impl ConfigProxyHandler {
|
|||
enabled: bool,
|
||||
) -> Result<(), CphError> {
|
||||
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(())
|
||||
}
|
||||
|
||||
|
|
@ -3057,6 +3073,8 @@ enum CphError {
|
|||
InvalidRegex(#[source] regex::Error),
|
||||
#[error("Window matcher {0:?} does not exist")]
|
||||
WindowMatcherDoesNotExist(WindowMatcher),
|
||||
#[error("Could not modify the connector state")]
|
||||
ModifyConnectorState(#[source] BackendConnectorTransactionError),
|
||||
}
|
||||
|
||||
trait WithRequestName {
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ impl ExtSessionLockManagerV1RequestHandler for ExtSessionLockManagerV1 {
|
|||
client: self.client.clone(),
|
||||
tracker: Default::default(),
|
||||
did_lock,
|
||||
awaiting_locked: Cell::new(true),
|
||||
finished: Cell::new(false),
|
||||
version: self.version,
|
||||
});
|
||||
|
|
@ -75,7 +76,7 @@ impl ExtSessionLockManagerV1RequestHandler for ExtSessionLockManagerV1 {
|
|||
state.lock.lock.set(Some(new.clone()));
|
||||
state.tree_changed();
|
||||
state.damage(state.root.extents.get());
|
||||
new.send_locked();
|
||||
new.check_locked();
|
||||
} else {
|
||||
new.finish();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,26 @@ pub struct ExtSessionLockV1 {
|
|||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub did_lock: bool,
|
||||
pub awaiting_locked: Cell<bool>,
|
||||
pub finished: Cell<bool>,
|
||||
pub version: Version,
|
||||
}
|
||||
|
||||
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 })
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ use {
|
|||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
scale::Scale,
|
||||
state::{ConnectorData, DrmDevData, OutputData},
|
||||
state::{ConnectorData, DrmDevData, OutputData, State},
|
||||
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::*},
|
||||
},
|
||||
jay_config::video::{
|
||||
|
|
@ -23,6 +23,7 @@ use {
|
|||
pub struct JayRandr {
|
||||
pub id: JayRandrId,
|
||||
pub client: Rc<Client>,
|
||||
pub state: Rc<State>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
}
|
||||
|
|
@ -39,6 +40,7 @@ impl JayRandr {
|
|||
Self {
|
||||
id,
|
||||
client: client.clone(),
|
||||
state: client.state.clone(),
|
||||
tracker: Default::default(),
|
||||
version,
|
||||
}
|
||||
|
|
@ -67,6 +69,7 @@ impl JayRandr {
|
|||
}
|
||||
|
||||
fn send_connector(&self, data: &ConnectorData) {
|
||||
let state = data.state.get();
|
||||
self.client.event(Connector {
|
||||
self_id: self.id,
|
||||
id: data.connector.id().raw() as _,
|
||||
|
|
@ -75,7 +78,7 @@ impl JayRandr {
|
|||
.as_ref()
|
||||
.map(|d| d.dev.id().raw() as _)
|
||||
.unwrap_or_default(),
|
||||
enabled: data.connector.enabled() as _,
|
||||
enabled: state.enabled as _,
|
||||
name: &data.name,
|
||||
});
|
||||
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> {
|
||||
let Some(c) = self.get_output(req.output) else {
|
||||
let Some(c) = self.get_connector(req.output) else {
|
||||
return Ok(());
|
||||
};
|
||||
c.connector.connector.set_mode(backend::Mode {
|
||||
width: req.width,
|
||||
height: req.height,
|
||||
refresh_rate_millihz: req.refresh_rate_millihz,
|
||||
let res = c.modify_state(&self.state, |s| {
|
||||
s.mode = backend::Mode {
|
||||
width: req.width,
|
||||
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(())
|
||||
}
|
||||
|
||||
|
|
@ -378,7 +386,10 @@ impl JayRandrRequestHandler for JayRandr {
|
|||
let Some(c) = self.get_connector(req.output) else {
|
||||
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(())
|
||||
}
|
||||
|
||||
|
|
@ -391,7 +402,13 @@ impl JayRandrRequestHandler for JayRandr {
|
|||
1 => Some(false),
|
||||
_ => 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(())
|
||||
}
|
||||
|
||||
|
|
@ -439,10 +456,13 @@ impl JayRandrRequestHandler for JayRandr {
|
|||
let Some(&format) = named_formats().get(req.format) else {
|
||||
return Err(JayRandrError::UnknownFormat(req.format.to_string()));
|
||||
};
|
||||
let Some(c) = self.get_output_node(req.output) else {
|
||||
let Some(c) = self.get_connector(req.output) else {
|
||||
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(())
|
||||
}
|
||||
|
||||
|
|
@ -478,7 +498,16 @@ impl JayRandrRequestHandler for JayRandr {
|
|||
let Some(c) = self.get_connector(req.output) else {
|
||||
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(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -161,22 +161,20 @@ impl WlOutputGlobal {
|
|||
state: &Rc<State>,
|
||||
connector: &Rc<ConnectorData>,
|
||||
modes: Vec<backend::Mode>,
|
||||
mode: &backend::Mode,
|
||||
width_mm: i32,
|
||||
height_mm: i32,
|
||||
output_id: &Rc<OutputId>,
|
||||
persistent_state: &Rc<PersistentOutputState>,
|
||||
transfer_functions: Vec<BackendTransferFunction>,
|
||||
btf: BackendTransferFunction,
|
||||
color_spaces: Vec<BackendColorSpace>,
|
||||
bcs: BackendColorSpace,
|
||||
primaries: Primaries,
|
||||
luminance: Option<BackendLuminance>,
|
||||
) -> Self {
|
||||
let (x, y) = persistent_state.pos.get();
|
||||
let scale = persistent_state.scale.get();
|
||||
let connector_state = connector.state.get();
|
||||
let (width, height) = calculate_logical_size(
|
||||
(mode.width, mode.height),
|
||||
(connector_state.mode.width, connector_state.mode.height),
|
||||
persistent_state.transform.get(),
|
||||
scale,
|
||||
);
|
||||
|
|
@ -186,8 +184,8 @@ impl WlOutputGlobal {
|
|||
connector: connector.clone(),
|
||||
pos: Cell::new(Rect::new_sized(x, y, width, height).unwrap()),
|
||||
output_id: output_id.clone(),
|
||||
mode: Cell::new(*mode),
|
||||
refresh_nsec: Cell::new(mode.refresh_nsec()),
|
||||
mode: Cell::new(connector_state.mode),
|
||||
refresh_nsec: Cell::new(connector_state.mode.refresh_nsec()),
|
||||
modes,
|
||||
formats: CloneCell::new(Rc::new(vec![])),
|
||||
format: Cell::new(XRGB8888),
|
||||
|
|
@ -203,8 +201,8 @@ impl WlOutputGlobal {
|
|||
persistent: persistent_state.clone(),
|
||||
opt: Default::default(),
|
||||
damage_matrix: Default::default(),
|
||||
btf: Cell::new(btf),
|
||||
bcs: Cell::new(bcs),
|
||||
btf: Cell::new(connector_state.transfer_function),
|
||||
bcs: Cell::new(connector_state.color_space),
|
||||
color_description: CloneCell::new(state.color_manager.srgb_srgb().clone()),
|
||||
linear_color_description: CloneCell::new(state.color_manager.srgb_linear().clone()),
|
||||
color_description_listeners: Default::default(),
|
||||
|
|
|
|||
|
|
@ -3,15 +3,21 @@ use {
|
|||
allocator::{Allocator, AllocatorError},
|
||||
async_engine::SpawnedFuture,
|
||||
backend::{
|
||||
AxisSource, Backend, BackendColorSpace, BackendEvent, BackendTransferFunction,
|
||||
Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId, InputDevice,
|
||||
InputDeviceAccelProfile, InputDeviceCapability, InputDeviceClickMethod, InputDeviceId,
|
||||
InputEvent, KeyState, Mode, MonitorInfo, ScrollAxis, TransformMatrix,
|
||||
AxisSource, Backend, BackendConnectorState, BackendEvent, Connector, ConnectorEvent,
|
||||
ConnectorId, ConnectorKernelId, DrmDeviceId, InputDevice, InputDeviceAccelProfile,
|
||||
InputDeviceCapability, InputDeviceClickMethod, InputDeviceId, InputEvent, KeyState,
|
||||
Mode, MonitorInfo, ScrollAxis, TransformMatrix,
|
||||
transaction::{
|
||||
BackendAppliedConnectorTransaction, BackendConnectorTransaction,
|
||||
BackendConnectorTransactionError, BackendConnectorTransactionType,
|
||||
BackendConnectorTransactionTypeDyn, BackendPreparedConnectorTransaction,
|
||||
},
|
||||
},
|
||||
cmm::cmm_primaries::Primaries,
|
||||
compositor::TestFuture,
|
||||
drm_feedback::DrmFeedback,
|
||||
fixed::Fixed,
|
||||
format::XRGB8888,
|
||||
gfx_api::GfxError,
|
||||
gfx_apis::create_vulkan_allocator,
|
||||
ifs::wl_output::OutputId,
|
||||
|
|
@ -29,8 +35,9 @@ use {
|
|||
gbm::{GbmDevice, GbmError},
|
||||
},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
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,
|
||||
uapi::c,
|
||||
};
|
||||
|
|
@ -63,7 +70,6 @@ pub struct TestBackend {
|
|||
pub default_mouse: Rc<TestBackendMouse>,
|
||||
pub default_kb: Rc<TestBackendKb>,
|
||||
pub render_context_installed: Cell<bool>,
|
||||
pub idle: TEEH<bool>,
|
||||
}
|
||||
|
||||
impl TestBackend {
|
||||
|
|
@ -77,6 +83,7 @@ impl TestBackend {
|
|||
},
|
||||
events: Default::default(),
|
||||
feedback: Default::default(),
|
||||
idle: Default::default(),
|
||||
});
|
||||
let default_mouse = Rc::new(TestBackendMouse {
|
||||
common: TestInputDeviceCommon {
|
||||
|
|
@ -125,17 +132,26 @@ impl TestBackend {
|
|||
model: "TestConnector".to_string(),
|
||||
serial_number: default_connector.id.to_string(),
|
||||
}),
|
||||
initial_mode: mode,
|
||||
width_mm: 80,
|
||||
height_mm: 60,
|
||||
non_desktop: false,
|
||||
vrr_capable: false,
|
||||
transfer_functions: vec![],
|
||||
transfer_function: BackendTransferFunction::Default,
|
||||
color_spaces: vec![],
|
||||
color_space: BackendColorSpace::Default,
|
||||
primaries: Primaries::SRGB,
|
||||
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 {
|
||||
state: state.clone(),
|
||||
|
|
@ -145,7 +161,6 @@ impl TestBackend {
|
|||
default_mouse,
|
||||
default_kb,
|
||||
render_context_installed: Cell::new(false),
|
||||
idle: Rc::new(Default::default()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -291,10 +306,6 @@ impl Backend for TestBackend {
|
|||
let _ = vtnr;
|
||||
}
|
||||
|
||||
fn set_idle(&self, idle: bool) {
|
||||
self.idle.push(idle);
|
||||
}
|
||||
|
||||
fn supports_presentation_feedback(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
|
@ -305,6 +316,7 @@ pub struct TestConnector {
|
|||
pub kernel_id: ConnectorKernelId,
|
||||
pub events: OnChange<ConnectorEvent>,
|
||||
pub feedback: CloneCell<Option<Rc<DrmFeedback>>>,
|
||||
pub idle: TEEH<bool>,
|
||||
}
|
||||
|
||||
impl Connector for TestConnector {
|
||||
|
|
@ -332,13 +344,70 @@ impl Connector for TestConnector {
|
|||
None
|
||||
}
|
||||
|
||||
fn set_mode(&self, _mode: Mode) {
|
||||
fn effectively_locked(&self) -> bool {
|
||||
// todo
|
||||
true
|
||||
}
|
||||
|
||||
fn drm_feedback(&self) -> Option<Rc<DrmFeedback>> {
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::{
|
||||
BackendColorSpace, BackendEvent, BackendTransferFunction, ConnectorEvent,
|
||||
ConnectorKernelId, Mode, MonitorInfo,
|
||||
BackendConnectorState, BackendEvent, ConnectorEvent, ConnectorKernelId, MonitorInfo,
|
||||
},
|
||||
cmm::cmm_primaries::Primaries,
|
||||
format::XRGB8888,
|
||||
ifs::wl_output::OutputId,
|
||||
it::{test_backend::TestConnector, test_error::TestResult, testrun::TestRun},
|
||||
video::drm::ConnectorType,
|
||||
|
|
@ -34,6 +34,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
},
|
||||
events: Default::default(),
|
||||
feedback: Default::default(),
|
||||
idle: Default::default(),
|
||||
});
|
||||
let new_monitor_info = MonitorInfo {
|
||||
modes: vec![],
|
||||
|
|
@ -43,21 +44,26 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
model: "jay second connector".to_string(),
|
||||
serial_number: "".to_string(),
|
||||
}),
|
||||
initial_mode: Mode {
|
||||
width: 400,
|
||||
height: 400,
|
||||
refresh_rate_millihz: 60000,
|
||||
},
|
||||
width_mm: 0,
|
||||
height_mm: 0,
|
||||
non_desktop: false,
|
||||
vrr_capable: false,
|
||||
transfer_functions: vec![],
|
||||
transfer_function: BackendTransferFunction::Default,
|
||||
color_spaces: vec![],
|
||||
color_space: BackendColorSpace::Default,
|
||||
primaries: Primaries::SRGB,
|
||||
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
|
||||
.state
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
run.cfg.set_idle(Duration::from_micros(100))?;
|
||||
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());
|
||||
|
||||
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);
|
||||
|
||||
impl $id {
|
||||
|
|
@ -772,3 +772,22 @@ macro_rules! 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();
|
||||
}
|
||||
|
||||
pub fn clear_all(&self) {
|
||||
let datas = unsafe { self.datas.get().deref_mut() };
|
||||
for data in datas {
|
||||
data.clear();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Region {
|
||||
let data = unsafe { &self.datas.get().deref()[self.this] };
|
||||
Region::from_rects2(data)
|
||||
|
|
|
|||
34
src/state.rs
34
src/state.rs
|
|
@ -3,9 +3,10 @@ use {
|
|||
acceptor::Acceptor,
|
||||
async_engine::{AsyncEngine, SpawnedFuture},
|
||||
backend::{
|
||||
Backend, BackendDrmDevice, BackendEvent, Connector, ConnectorId, ConnectorIds,
|
||||
DrmDeviceId, DrmDeviceIds, HardwareCursorUpdate, InputDevice, InputDeviceGroupIds,
|
||||
InputDeviceId, InputDeviceIds, MonitorInfo,
|
||||
Backend, BackendConnectorState, BackendConnectorStateSerials, BackendDrmDevice,
|
||||
BackendEvent, Connector, ConnectorId, ConnectorIds, DrmDeviceId, DrmDeviceIds,
|
||||
HardwareCursorUpdate, InputDevice, InputDeviceGroupIds, InputDeviceId, InputDeviceIds,
|
||||
MonitorInfo, transaction::BackendConnectorTransactionError,
|
||||
},
|
||||
backends::dummy::DummyBackend,
|
||||
cli::RunArgs,
|
||||
|
|
@ -255,6 +256,7 @@ pub struct State {
|
|||
pub caps_thread: Option<PrCapsThread>,
|
||||
pub node_at_tree: RefCell<Vec<FoundNode>>,
|
||||
pub position_hint_requests: AsyncQueue<PositionHintRequest>,
|
||||
pub backend_connector_state_serials: BackendConnectorStateSerials,
|
||||
}
|
||||
|
||||
// impl Drop for State {
|
||||
|
|
@ -376,6 +378,7 @@ pub struct ConnectorData {
|
|||
pub damage: RefCell<Vec<Rect>>,
|
||||
pub needs_vblank_emulation: Cell<bool>,
|
||||
pub damage_intersect: Cell<Rect>,
|
||||
pub state: Cell<BackendConnectorState>,
|
||||
}
|
||||
|
||||
pub struct OutputData {
|
||||
|
|
@ -403,6 +406,31 @@ impl ConnectorData {
|
|||
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 {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::{Connector, ConnectorEvent, ConnectorId, MonitorInfo},
|
||||
backend::{
|
||||
BackendConnectorState, BackendConnectorStateSerial, Connector, ConnectorEvent,
|
||||
ConnectorId, MonitorInfo,
|
||||
},
|
||||
format::XRGB8888,
|
||||
globals::GlobalName,
|
||||
ifs::{
|
||||
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"),
|
||||
};
|
||||
}
|
||||
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 data = Rc::new(ConnectorData {
|
||||
connector: connector.clone(),
|
||||
|
|
@ -34,6 +50,7 @@ pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
|
|||
damage: Default::default(),
|
||||
needs_vblank_emulation: Cell::new(false),
|
||||
damage_intersect: Default::default(),
|
||||
state: Cell::new(backend_state),
|
||||
});
|
||||
if let Some(dev) = drm_dev {
|
||||
dev.connectors.set(id, data.clone());
|
||||
|
|
@ -88,6 +105,10 @@ impl ConnectorHandler {
|
|||
async fn handle_connected(&self, info: MonitorInfo) {
|
||||
log::info!("Connector {} connected", self.data.connector.kernel_id());
|
||||
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();
|
||||
if info.non_desktop {
|
||||
self.handle_non_desktop_connected(info).await;
|
||||
|
|
@ -132,15 +153,12 @@ impl ConnectorHandler {
|
|||
&self.state,
|
||||
&self.data,
|
||||
info.modes.clone(),
|
||||
&info.initial_mode,
|
||||
info.width_mm,
|
||||
info.height_mm,
|
||||
&output_id,
|
||||
&desired_state,
|
||||
info.transfer_functions.clone(),
|
||||
info.transfer_function,
|
||||
info.color_spaces.clone(),
|
||||
info.color_space,
|
||||
info.primaries,
|
||||
info.luminance,
|
||||
));
|
||||
|
|
@ -268,18 +286,11 @@ impl ConnectorHandler {
|
|||
on.hardware_cursor.set(hc);
|
||||
self.state.refresh_hardware_cursors();
|
||||
}
|
||||
ConnectorEvent::ModeChanged(mode) => {
|
||||
on.update_mode(mode);
|
||||
}
|
||||
ConnectorEvent::VrrChanged(enabled) => {
|
||||
on.schedule.set_vrr_enabled(enabled);
|
||||
}
|
||||
ConnectorEvent::FormatsChanged(formats, format) => {
|
||||
ConnectorEvent::FormatsChanged(formats) => {
|
||||
on.global.formats.set(formats);
|
||||
on.global.format.set(format);
|
||||
}
|
||||
ConnectorEvent::ColorsChanged(bcs, btf) => {
|
||||
on.update_btf_and_bcs(btf, bcs);
|
||||
ConnectorEvent::State(state) => {
|
||||
on.update_state(state);
|
||||
}
|
||||
ev => unreachable!("received unexpected event {:?}", ev),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::Backend,
|
||||
backend::transaction::{BackendConnectorTransactionError, ConnectorTransaction},
|
||||
state::State,
|
||||
utils::{
|
||||
errorfmt::ErrorFmt,
|
||||
|
|
@ -12,7 +12,7 @@ use {
|
|||
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) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
|
|
@ -24,7 +24,6 @@ pub async fn idle(state: Rc<State>, backend: Rc<dyn Backend>) {
|
|||
state.idle.timeout_changed.set(true);
|
||||
let mut idle = Idle {
|
||||
state,
|
||||
backend,
|
||||
timer,
|
||||
idle: false,
|
||||
dead: false,
|
||||
|
|
@ -36,7 +35,6 @@ pub async fn idle(state: Rc<State>, backend: Rc<dyn Backend>) {
|
|||
|
||||
struct Idle {
|
||||
state: Rc<State>,
|
||||
backend: Rc<dyn Backend>,
|
||||
timer: TimerFd,
|
||||
idle: bool,
|
||||
dead: bool,
|
||||
|
|
@ -71,7 +69,7 @@ impl Idle {
|
|||
if let Some(config) = self.state.config.get() {
|
||||
config.idle();
|
||||
}
|
||||
self.backend.set_idle(true);
|
||||
self.set_idle(true);
|
||||
self.idle = true;
|
||||
}
|
||||
} else if since >= timeout {
|
||||
|
|
@ -110,7 +108,7 @@ impl Idle {
|
|||
self.last_input = now();
|
||||
self.set_in_grace_period(false);
|
||||
if self.idle {
|
||||
self.backend.set_idle(false);
|
||||
self.set_idle(false);
|
||||
self.idle = false;
|
||||
self.program_timer();
|
||||
}
|
||||
|
|
@ -127,6 +125,27 @@ impl Idle {
|
|||
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 {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::{BackendColorSpace, BackendTransferFunction, HardwareCursor, KeyState, Mode},
|
||||
backend::{
|
||||
BackendColorSpace, BackendConnectorState, BackendTransferFunction, HardwareCursor,
|
||||
KeyState, Mode,
|
||||
},
|
||||
client::ClientId,
|
||||
cmm::cmm_description::ColorDescription,
|
||||
cursor::KnownCursor,
|
||||
|
|
@ -207,10 +210,14 @@ impl OutputNode {
|
|||
seq: u64,
|
||||
flags: u32,
|
||||
vrr: bool,
|
||||
locked: bool,
|
||||
) {
|
||||
for listener in self.presentation_event.iter() {
|
||||
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>) {
|
||||
|
|
@ -842,7 +849,23 @@ impl OutputNode {
|
|||
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_bcs = self.global.bcs.replace(bcs);
|
||||
if (old_btf, old_bcs) == (btf, bcs) {
|
||||
|
|
@ -1067,7 +1090,13 @@ impl OutputNode {
|
|||
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) {
|
||||
|
|
@ -1094,7 +1123,13 @@ impl OutputNode {
|
|||
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(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ pub mod activation_token;
|
|||
pub mod array;
|
||||
pub mod array_to_tuple;
|
||||
pub mod asyncevent;
|
||||
pub mod binary_search_map;
|
||||
pub mod bindings;
|
||||
pub mod bitfield;
|
||||
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 {
|
||||
($name:ident, $ty:expr) => {
|
||||
#[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);
|
||||
|
||||
impl DrmObject for $name {
|
||||
|
|
@ -929,11 +929,6 @@ pub struct hdr_metadata_infoframe {
|
|||
}
|
||||
|
||||
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 {
|
||||
let mut name = [0u8; DRM_DISPLAY_MODE_LEN];
|
||||
let len = name.len().min(self.name.len());
|
||||
|
|
@ -1006,7 +1001,6 @@ pub struct ObjectChange<'a> {
|
|||
}
|
||||
|
||||
impl Change {
|
||||
#[expect(dead_code)]
|
||||
pub fn test(&self, flags: u32) -> Result<(), DrmError> {
|
||||
mode_atomic(
|
||||
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
|
||||
T: DrmObject,
|
||||
F: FnOnce(&mut ObjectChange),
|
||||
|
|
@ -1047,14 +1049,17 @@ impl Change {
|
|||
self.objects.push(obj.id());
|
||||
self.object_lengths.push(new);
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.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)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
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;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct drm_mode_modeinfo {
|
||||
pub clock: u32,
|
||||
pub hdisplay: u16,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue