Integrate native Rime and stabilize input, output, and rendering

- Add optional modifier-only Super binding whose tap is cancelled by
  other input, focus changes, grabs, locks, and session teardown.
- Ship the native ferese-rime user service, Rime defaults, installer
  inventory, package dependencies, session environment, and docs.
- Vendor Smithay 8a27023 with the SHM texture-cache format fix and add
  GPU regressions for format changes and resize snapshots.
- Release reused CRTC owners before staging output modes, then restore
  changed outputs before former owners when a transaction fails.
- Wait for resize-capture GPU completion, expand filtered surface
  damage, and preserve IME popup origin under fractional scaling.
- Mark weekends like holidays in both calendar layouts.
This commit is contained in:
aria committed 2026-10-30 03:13:43 +09:00
1 parent a2be421974
commit 993bc0f88f
391 files changed
+130908 -76

No files matched your search

+17
View File
@@ -0,0 +1,17 @@
[package]
name = "smithay-drm-extras"
version = "0.1.0"
edition = "2024"
license = "MIT"
authors = ["Bartłomiej Maryńczak <[email protected]>"]
[dependencies]
libdisplay-info = { version = "0.3.0", optional = true }
drm = { version = "0.14.0" }
[features]
default = ["display-info"]
display-info = ["libdisplay-info"]
[dev-dependencies.smithay]
path = "../"
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Bartłomiej Maryńczak
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+9
View File
@@ -0,0 +1,9 @@
# Smithay DRM Extras
This crate contains some extra abstractions and helpers over DRM
- `display_info` module is responsible for extraction of information from DRM connectors (`model` and `manufacturer`)
- `drm_scanner` module contains helpers for detecting connector connected and disconnected events as well as mapping crtc to them.
- `ConnectorScanner` is responsible for tracking connected/disconnected events.
- `CrtcMapper` trait and `SimpleCrtcMapper` are meant for mapping crtc to connector.
- `DrmScanner<CrtcMapper>` combines two above into single abstraction. If it does not fit your needs you can always drop down to using `ConnectScanner` alone.
+220
View File
@@ -0,0 +1,220 @@
use std::{collections::HashMap, path::PathBuf, time::Duration};
use smithay::reexports::drm::control::{connector, crtc};
use smithay_drm_extras::{
display_info,
drm_scanner::{self, DrmScanEvent},
};
use smithay::{
backend::{
drm::{self, DrmDeviceFd, DrmNode},
session::{Session, libseat::LibSeatSession},
udev::{UdevBackend, UdevEvent},
},
reexports::{
calloop::{EventLoop, LoopHandle, timer::Timer},
rustix::fs::OFlags,
},
utils::DeviceFd,
};
struct State {
handle: LoopHandle<'static, Self>,
session: LibSeatSession,
devices: HashMap<DrmNode, Device>,
}
struct Device {
drm: drm::DrmDevice,
drm_scanner: drm_scanner::DrmScanner,
surfaces: HashMap<crtc::Handle, Surface>,
}
#[derive(Clone)]
struct Surface {
// Your gbm surface stuff goes here
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut event_loop = EventLoop::<State>::try_new()?;
let (session, notify) = LibSeatSession::new().unwrap();
event_loop.handle().insert_source(notify, |_, _, _| {}).unwrap();
let mut state = State {
handle: event_loop.handle(),
session,
devices: Default::default(),
};
init_udev(&mut state);
event_loop
.handle()
.insert_source(Timer::from_duration(Duration::from_secs(5)), |_, _, _| {
panic!("Aborted");
})
.unwrap();
event_loop.run(None, &mut state, |_data| {})?;
Ok(())
}
fn init_udev(state: &mut State) {
let backend = UdevBackend::new(state.session.seat()).unwrap();
for (device_id, path) in backend.device_list() {
state.on_udev_event(UdevEvent::Added {
device_id,
path: path.to_owned(),
});
}
state
.handle
.insert_source(backend, |event, _, state| state.on_udev_event(event))
.unwrap();
}
impl State {
fn on_udev_event(&mut self, event: UdevEvent) {
match event {
UdevEvent::Added { device_id, path } => {
if let Ok(node) = DrmNode::from_dev_id(device_id) {
self.device_added(node, path);
}
}
UdevEvent::Changed { device_id } => {
if let Ok(node) = DrmNode::from_dev_id(device_id) {
self.device_changed(node);
}
}
UdevEvent::Removed { device_id } => {
if let Ok(node) = DrmNode::from_dev_id(device_id) {
self.device_removed(node);
}
}
}
}
fn device_added(&mut self, node: DrmNode, path: PathBuf) {
let fd = self
.session
.open(
&path,
OFlags::RDWR | OFlags::CLOEXEC | OFlags::NOCTTY | OFlags::NONBLOCK,
)
.unwrap();
let fd = DrmDeviceFd::new(DeviceFd::from(fd));
let (drm, drm_notifier) = drm::DrmDevice::new(fd, false).unwrap();
self.handle
.insert_source(drm_notifier, move |event, _, _| match event {
drm::DrmEvent::VBlank(_) => {}
drm::DrmEvent::Error(_) => {}
})
.unwrap();
self.devices.insert(
node,
Device {
drm,
drm_scanner: Default::default(),
surfaces: Default::default(),
},
);
self.device_changed(node);
}
fn connector_connected(&mut self, node: DrmNode, connector: connector::Info, crtc: crtc::Handle) {
if let Some(device) = self.devices.get_mut(&node) {
let name = format!("{}-{}", connector.interface().as_str(), connector.interface_id());
let display_info = display_info::for_connector(&device.drm, connector.handle());
let manufacturer = display_info
.as_ref()
.and_then(|info| info.make())
.unwrap_or_else(|| "Unknown".into());
let model = display_info
.as_ref()
.and_then(|info| info.model())
.unwrap_or_else(|| "Unknown".into());
println!("Connected:");
dbg!(name);
dbg!(manufacturer);
dbg!(model);
device.surfaces.insert(crtc, Surface {});
}
}
fn connector_disconnected(&mut self, node: DrmNode, connector: connector::Info, crtc: crtc::Handle) {
let name = format!("{}-{}", connector.interface().as_str(), connector.interface_id());
println!("Disconnected:");
dbg!(name);
if let Some(device) = self.devices.get_mut(&node) {
device.surfaces.remove(&crtc);
}
}
fn device_changed(&mut self, node: DrmNode) {
let device = if let Some(device) = self.devices.get_mut(&node) {
device
} else {
return;
};
for event in device
.drm_scanner
.scan_connectors(&device.drm)
.expect("failed to scan connectors")
{
match event {
DrmScanEvent::Connected {
connector,
crtc: Some(crtc),
} => {
self.connector_connected(node, connector, crtc);
}
DrmScanEvent::Disconnected {
connector,
crtc: Some(crtc),
} => {
self.connector_disconnected(node, connector, crtc);
}
DrmScanEvent::Changed { .. } => {}
_ => {}
}
}
}
fn device_removed(&mut self, node: DrmNode) {
let device = if let Some(device) = self.devices.get_mut(&node) {
device
} else {
return;
};
let crtcs: Vec<_> = device
.drm_scanner
.crtcs()
.map(|(info, crtc)| (info.clone(), crtc))
.collect();
for (connector, crtc) in crtcs {
self.connector_disconnected(node, connector, crtc);
}
self.devices.remove(&node);
}
}
+37
View File
@@ -0,0 +1,37 @@
//! # EDID - Extended Display Identification Data
//!
//! This module is meant to help with extraction of EDID data from connectors
//!
//! ```no_run
//! # mod helpers { include!("./docs/doctest_helpers.rs"); };
//! # let drm_device: helpers::FakeDevice = todo!();
//! # let connector = todo!();
//! use smithay_drm_extras::display_info;
//!
//! let info = display_info::for_connector(&drm_device, connector).unwrap();
//!
//! println!("Monitor name: {:?}", info.model());
//! println!("Manufacturer name: {:?}", info.make());
//! ```
use drm::control::{Device as ControlDevice, connector};
use libdisplay_info::info::Info;
/// Try to read the [`Info`] from the connector EDID property
pub fn for_connector(device: &impl ControlDevice, connector: connector::Handle) -> Option<Info> {
let props = device.get_properties(connector).ok()?;
let (info, value) = props
.into_iter()
.filter_map(|(handle, value)| {
let info = device.get_property(handle).ok()?;
Some((info, value))
})
.find(|(info, _)| info.name().to_str() == Ok("EDID"))?;
let blob = info.value_type().convert_value(value).as_blob()?;
let data = device.get_property_blob(blob).ok()?;
Info::parse_edid(&data).ok()
}
@@ -0,0 +1,8 @@
pub struct FakeDevice;
impl std::os::unix::prelude::AsFd for FakeDevice {
fn as_fd(&self) -> std::os::unix::prelude::BorrowedFd<'_> {
unimplemented!()
}
}
impl drm::Device for FakeDevice {}
impl drm::control::Device for FakeDevice {}
+244
View File
@@ -0,0 +1,244 @@
//! # Drm Scanner
//!
//! - [`ConnectorScanner`] is responsible for tracking connected/disconnected events.
//! - [`CrtcMapper`] trait and [`SimpleCrtcMapper`] are meant for mapping crtc to connector.
//! - [`DrmScanner`] combines two above into single abstraction.
//! If it does not fit your needs you can always drop down to using [`ConnectScanner`] alone.
//!
//! ### Example
//! ```no_run
//! # mod helpers { include!("./docs/doctest_helpers.rs"); };
//! # let drm_device: helpers::FakeDevice = todo!();
//! use smithay_drm_extras::drm_scanner::{DrmScanner, DrmScanEvent};
//!
//! let mut scanner: DrmScanner = DrmScanner::new();
//!
//! for event in scanner.scan_connectors(&drm_device).expect("failed to scan connectors") {
//! match event {
//! DrmScanEvent::Connected { .. } => {},
//! DrmScanEvent::Disconnected { .. } => {},
//! DrmScanEvent::Changed { .. } => {},
//! }
//! }
//! ```
use std::collections::HashMap;
use drm::control::{Device as ControlDevice, connector, crtc};
mod connector_scanner;
pub use connector_scanner::{ConnectorScanEvent, ConnectorScanResult, ConnectorScanner};
mod crtc_mapper;
pub use crtc_mapper::{CrtcMapper, SimpleCrtcMapper};
/// Drm Scanner
///
/// Wrapper over [`ConnectorScanner`] and [`CrtcMapper`]
#[derive(Debug, Default)]
pub struct DrmScanner<Mapper = SimpleCrtcMapper>
where
Mapper: CrtcMapper,
{
connectors: ConnectorScanner,
crtc_mapper: Mapper,
}
impl<M> DrmScanner<M>
where
M: CrtcMapper + Default,
{
/// Create new DrmScanner with default CRTC mapper.
pub fn new() -> Self {
Self::new_with_mapper(Default::default())
}
}
impl<M> DrmScanner<M>
where
M: CrtcMapper,
{
/// Create new DrmScanner with custom CRTC mapper
pub fn new_with_mapper(mapper: M) -> Self {
Self {
connectors: Default::default(),
crtc_mapper: mapper,
}
}
/// [`CrtcMapper`] getter
pub fn crtc_mapper(&self) -> &M {
&self.crtc_mapper
}
/// Muttable [`CrtcMapper`] getter
pub fn crtc_mapper_mut(&mut self) -> &mut M {
&mut self.crtc_mapper
}
/// Scan connectors to find out what has changed since last call to this method.
///
/// Returns [`DrmScanResult`] that contains added and removed connectors,
/// and CRTCs that got assigned to them.
///
/// Should be called on every device changed event
///
/// ```no_run
/// # mod helpers { include!("./docs/doctest_helpers.rs"); };
/// # let drm_device: helpers::FakeDevice = todo!();
/// use smithay_drm_extras::drm_scanner::{DrmScanner, DrmScanEvent};
///
/// let mut scanner: DrmScanner = DrmScanner::new();
/// let res = scanner.scan_connectors(&drm_device).expect("failed to scan connectors");
///
/// // You can extract scan info manually
/// println!("Plugged {} connectors", res.connected.len());
/// println!("Unplugged {} connectors", res.disconnected.len());
///
/// // Or simply iterate over it
/// for event in res {
/// match event {
/// DrmScanEvent::Connected { .. } => {},
/// DrmScanEvent::Disconnected { .. } => {},
/// DrmScanEvent::Changed { .. } => {},
/// }
/// }
/// ```
pub fn scan_connectors(&mut self, drm: &impl ControlDevice) -> std::io::Result<DrmScanResult> {
let scan = self.connectors.scan(drm)?;
let removed = scan
.disconnected
.into_iter()
.map(|info| {
let crtc = self.crtc_mapper.crtc_for_connector(&info.handle());
(info, crtc)
})
.collect();
self.crtc_mapper.map(drm, self.connectors.connectors().values());
let added = scan
.connected
.into_iter()
.map(|info| {
let crtc = self.crtc_mapper.crtc_for_connector(&info.handle());
(info, crtc)
})
.collect();
let changed = scan
.changed
.into_iter()
.map(|info| {
let crtc = self.crtc_mapper.crtc_for_connector(&info.handle());
(info, crtc)
})
.collect();
Ok(DrmScanResult {
disconnected: removed,
connected: added,
changed,
})
}
/// Get map of all connectors, connected and disconnected ones.
pub fn connectors(&self) -> &HashMap<connector::Handle, connector::Info> {
self.connectors.connectors()
}
/// Get CRTC that is mapped to supplied connector
///
/// This will query underlying [`CrtcMapper`]
pub fn crtc_for_connector(&self, connector: &connector::Handle) -> Option<crtc::Handle> {
self.crtc_mapper.crtc_for_connector(connector)
}
/// Get iterator over all `connector -> CRTC` mappings
pub fn crtcs(&self) -> impl Iterator<Item = (&connector::Info, crtc::Handle)> {
self.connectors()
.iter()
.filter_map(|(handle, info)| Some((info, self.crtc_for_connector(handle)?)))
}
}
type DrmScanItem = (connector::Info, Option<crtc::Handle>);
/// Result of [`DrmScanner::scan_connectors`]
///
/// You can use `added` and `removed` fields of this result manually,
/// or you can just iterate (using [`IntoIterator`] or [`DrmScanResult::iter`])
/// over this result to get [`DrmScanEvent`].
#[derive(Debug, Default, Clone)]
pub struct DrmScanResult {
/// Connectors that got plugged in since last scan
pub connected: Vec<DrmScanItem>,
/// Connectors that got unplugged since last scan
pub disconnected: Vec<DrmScanItem>,
/// Connectors whose mode list changed while staying connected
pub changed: Vec<DrmScanItem>,
}
impl DrmScanResult {
/// Creates event iterator for this result
///
/// Internally this clones the data so it is equivalent to [`IntoIterator`]
pub fn iter(&self) -> impl Iterator<Item = DrmScanEvent> {
self.clone().into_iter()
}
}
/// Created from [`DrmScanResult`], informs about connector events.
#[derive(Debug, Clone)]
pub enum DrmScanEvent {
/// A new connector got plugged in since last scan
Connected {
/// Info about connected connector
connector: connector::Info,
/// Crtc that got mapped to this connector
crtc: Option<crtc::Handle>,
},
/// A connector got unplugged in since last scan
Disconnected {
/// Info about disconnected connector
connector: connector::Info,
/// Crtc that is no longer mapped to this connector
crtc: Option<crtc::Handle>,
},
/// The connector's mode list changed while staying connected
Changed {
/// Info about the connector whose modes changed
connector: connector::Info,
/// Crtc that is mapped to this connector
crtc: Option<crtc::Handle>,
},
}
impl DrmScanEvent {
fn connected((connector, crtc): (connector::Info, Option<crtc::Handle>)) -> Self {
DrmScanEvent::Connected { connector, crtc }
}
fn disconnected((connector, crtc): (connector::Info, Option<crtc::Handle>)) -> Self {
DrmScanEvent::Disconnected { connector, crtc }
}
fn changed((connector, crtc): (connector::Info, Option<crtc::Handle>)) -> Self {
DrmScanEvent::Changed { connector, crtc }
}
}
impl IntoIterator for DrmScanResult {
type Item = DrmScanEvent;
type IntoIter = std::vec::IntoIter<DrmScanEvent>;
fn into_iter(self) -> Self::IntoIter {
self.disconnected
.into_iter()
.map(DrmScanEvent::disconnected)
.chain(self.connected.into_iter().map(DrmScanEvent::connected))
.chain(self.changed.into_iter().map(DrmScanEvent::changed))
.collect::<Vec<_>>()
.into_iter()
}
}
@@ -0,0 +1,153 @@
use std::collections::{HashMap, HashSet};
use drm::control::{Device as ControlDevice, connector};
/// Responsible for tracking connected/disconnected events.
///
/// ### Example
/// ```no_run
/// # mod helpers { include!("../docs/doctest_helpers.rs"); };
/// # let drm_device: helpers::FakeDevice = todo!();
/// use smithay_drm_extras::drm_scanner::{ConnectorScanner, ConnectorScanEvent};
///
/// let mut scanner = ConnectorScanner::new();
///
/// for event in scanner.scan(&drm_device).expect("failed to scan connectors") {
/// match event {
/// ConnectorScanEvent::Connected(conn) => {},
/// ConnectorScanEvent::Disconnected(conn) => {},
/// ConnectorScanEvent::Changed(conn) => {},
/// }
/// }
#[derive(Debug, Default)]
pub struct ConnectorScanner {
connectors: HashMap<connector::Handle, connector::Info>,
}
impl ConnectorScanner {
/// Create new [`ConnectorScanner`]
pub fn new() -> Self {
Default::default()
}
/// Should be called on every device changed event
pub fn scan(&mut self, drm: &impl ControlDevice) -> std::io::Result<ConnectorScanResult> {
let res_handles = drm.resource_handles()?;
let connector_handles = res_handles.connectors();
let mut added = Vec::new();
let mut removed = Vec::new();
let mut changed = Vec::new();
for conn in connector_handles
.iter()
.filter_map(|conn| drm.get_connector(*conn, true).ok())
{
let curr_state = conn.state();
use connector::State;
if let Some(old) = self.connectors.insert(conn.handle(), conn.clone()) {
match (old.state(), curr_state) {
(State::Connected, State::Disconnected) => removed.push(conn),
(State::Disconnected | State::Unknown, State::Connected) => added.push(conn),
//
// Emit Changed when the mode list changes while staying
// connected. This covers the EDID race where the kernel
// initially reports a connector as Connected with an
// empty or fallback mode list (EDID not yet read) and a
// later rescan finds the real modes populated.
(State::Connected, State::Connected) => {
if old.modes() != conn.modes() {
changed.push(conn);
}
}
(State::Disconnected, State::Disconnected) => {}
//
(State::Unknown, _) => {}
(_, State::Unknown) => {}
}
} else if curr_state == State::Connected {
added.push(conn)
}
}
// Check for connectors that disappeared entirely (e.g., USB-C dock disconnected while
// suspended). These won't be in resource_handles() anymore, so handle them specially.
let connector_handles_set: HashSet<_> = connector_handles.iter().copied().collect();
let to_remove: Vec<_> = self
.connectors
.keys()
.filter(|handle| !connector_handles_set.contains(handle))
.copied()
.collect();
for handle in to_remove {
if let Some(info) = self.connectors.remove(&handle) {
if info.state() == connector::State::Connected {
removed.push(info);
}
}
}
Ok(ConnectorScanResult {
connected: added,
disconnected: removed,
changed,
})
}
/// Get map of all connectors, connected and disconnected ones.
pub fn connectors(&self) -> &HashMap<connector::Handle, connector::Info> {
&self.connectors
}
}
/// Result of [`ConnectorScanner::scan`]
///
/// You can use `added` and `removed` fields of this result manually,
/// or you can just iterate (using [`IntoIterator`] or [`ConnectorScanResult::iter`])
/// over this result to get [`ConnectorScanEvent`].
#[derive(Debug, Default, Clone)]
pub struct ConnectorScanResult {
/// Connectors that got plugged in since last scan
pub connected: Vec<connector::Info>,
/// Connectors that got unplugged since last scan
pub disconnected: Vec<connector::Info>,
/// Connectors whose mode list changed while staying connected
pub changed: Vec<connector::Info>,
}
/// Created from [`ConnectorScanResult`], informs about connector events.
#[derive(Debug, Clone)]
pub enum ConnectorScanEvent {
/// A new connector got plugged in since last scan
Connected(connector::Info),
/// A connector got unplugged in since last scan
Disconnected(connector::Info),
/// The connector's mode list changed while staying connected
Changed(connector::Info),
}
impl ConnectorScanResult {
/// Creates event iterator for this result
///
/// Internally this clones the data so it is equivalent to [`IntoIterator`]
pub fn iter(&self) -> impl Iterator<Item = ConnectorScanEvent> {
self.clone().into_iter()
}
}
impl IntoIterator for ConnectorScanResult {
type Item = ConnectorScanEvent;
type IntoIter = std::vec::IntoIter<ConnectorScanEvent>;
fn into_iter(self) -> Self::IntoIter {
self.disconnected
.into_iter()
.map(ConnectorScanEvent::Disconnected)
.chain(self.connected.into_iter().map(ConnectorScanEvent::Connected))
.chain(self.changed.into_iter().map(ConnectorScanEvent::Changed))
.collect::<Vec<_>>()
.into_iter()
}
}
@@ -0,0 +1,126 @@
use std::collections::{HashMap, HashSet};
use drm::control::{Device as ControlDevice, connector, crtc};
/// CRTC Mapper trait
///
/// It exists to allow custom mappers in [`super::DrmScanner`].
///
/// It is responsible for mapping CRTCs to connectors.
/// For each connector it has to pick suitable CRTC.
pub trait CrtcMapper {
/// Request mapping of CRTCs to supplied connectors
///
/// Usually called in response to udev device changed event,
/// or on device init.
fn map<'a>(
&mut self,
drm: &impl ControlDevice,
connectors: impl Iterator<Item = &'a connector::Info> + Clone,
);
/// Query CRTC mapped to supplied connector
fn crtc_for_connector(&self, connector: &connector::Handle) -> Option<crtc::Handle>;
}
/// Simple CRTC Mapper
///
/// This is basic mapper that simply chooses one CRTC for every connector.
///
/// It is also capable of recovering mappings that were used by display manger or tty
/// before the compositor was started up.
#[derive(Debug, Default)]
pub struct SimpleCrtcMapper {
crtcs: HashMap<connector::Handle, crtc::Handle>,
}
impl SimpleCrtcMapper {
/// Create new [`SimpleCrtcMapper`]
pub fn new() -> Self {
Self::default()
}
fn is_taken(&self, crtc: &crtc::Handle) -> bool {
self.crtcs.values().any(|v| v == crtc)
}
fn is_available(&self, crtc: &crtc::Handle) -> bool {
!self.is_taken(crtc)
}
fn restored_for_connector(
&self,
drm: &impl ControlDevice,
connector: &connector::Info,
) -> Option<crtc::Handle> {
let encoder = connector.current_encoder()?;
let encoder = drm.get_encoder(encoder).ok()?;
let crtc = encoder.crtc()?;
self.is_available(&crtc).then_some(crtc)
}
fn pick_next_available_for_connector(
&self,
drm: &impl ControlDevice,
connector: &connector::Info,
) -> Option<crtc::Handle> {
let res_handles = drm.resource_handles().ok()?;
connector
.encoders()
.iter()
.flat_map(|encoder_handle| drm.get_encoder(*encoder_handle))
.find_map(|encoder_info| {
res_handles
.filter_crtcs(encoder_info.possible_crtcs())
.into_iter()
.find(|crtc| self.is_available(crtc))
})
}
}
impl super::CrtcMapper for SimpleCrtcMapper {
fn map<'a>(
&mut self,
drm: &impl ControlDevice,
connectors: impl Iterator<Item = &'a connector::Info> + Clone,
) {
// Release CRTC reservations for every connector that is not currently connected. This
// covers connectors that are present but disconnected, as well as connectors that have
// vanished from the resource list entirely (e.g. DP-MST sink connectors when a dock is
// unplugged or the system is suspended).
let connected: HashSet<connector::Handle> = connectors
.clone()
.filter(|conn| conn.state() == connector::State::Connected)
.map(|conn| conn.handle())
.collect();
self.crtcs.retain(|handle, _| connected.contains(handle));
let mut needs_crtc: Vec<&connector::Info> = connectors
.filter(|conn| conn.state() == connector::State::Connected)
.filter(|conn| !self.crtcs.contains_key(&conn.handle()))
.collect();
needs_crtc.retain(|connector| {
if let Some(crtc) = self.restored_for_connector(drm, connector) {
self.crtcs.insert(connector.handle(), crtc);
// This connector no longer needs crtc so let's remove it
false
} else {
true
}
});
for connector in needs_crtc {
if let Some(crtc) = self.pick_next_available_for_connector(drm, connector) {
self.crtcs.insert(connector.handle(), crtc);
}
}
}
fn crtc_for_connector(&self, connector: &connector::Handle) -> Option<crtc::Handle> {
self.crtcs.get(connector).copied()
}
}
+16
View File
@@ -0,0 +1,16 @@
//! # Smithay DRM Extras
//!
//! This crate contains some extra abstractions and helpers over DRM
//!
//! - [`display_info`] is responsible for extraction of information from DRM connectors
//! - [`drm_scanner`] is responsible for detecting connector connected and
//! disconnected events, as well as mapping CRTC to them.
//!
//! ### Features
//! - `display_info` - If enabled `display_info` functionality is enabled through `libdisplay-info` integration
#![warn(missing_docs, missing_debug_implementations)]
#[cfg(feature = "display-info")]
pub mod display_info;
pub mod drm_scanner;