1
0
Fork 0
forked from wry/wry

Compare commits

..

10 commits

18 changed files with 3138 additions and 2968 deletions

View file

@ -1,4 +1,5 @@
mod copy_device; mod copy_device;
mod discovery;
mod hardware_cursor; mod hardware_cursor;
mod lease; mod lease;
mod model; mod model;
@ -18,19 +19,22 @@ pub use {
properties::{DefaultProperty, TypedProperty}, properties::{DefaultProperty, TypedProperty},
}; };
use properties::{ use {
DefaultValue, collect_properties, collect_untyped_properties, create_default_properties, discovery::{
create_connector, create_connector_display_data, create_crtc, create_encoder, create_plane,
get_connectors,
},
properties::collect_untyped_properties,
}; };
use { use {
crate::{ crate::{
async_engine::Phase,
backend::{ backend::{
BackendColorSpace, BackendConnectorState, BackendDrmDevice, BackendDrmLessee, BackendColorSpace, BackendConnectorState, BackendDrmDevice, BackendDrmLessee,
BackendEotfs, BackendEvent, BackendGammaLut, BackendGammaLutElement, BackendEotfs, BackendEvent, BackendGammaLut, BackendGammaLutElement,
BackendLuminance, CONCAP_CONNECTOR, CONCAP_MODE_SETTING, CONCAP_PHYSICAL_DISPLAY, CONCAP_CONNECTOR, CONCAP_MODE_SETTING, CONCAP_PHYSICAL_DISPLAY,
Connector, ConnectorCaps, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId, Connector, ConnectorCaps, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId,
Mode, MonitorInfo, OutputId, MonitorInfo,
transaction::{ transaction::{
BackendConnectorTransaction, BackendConnectorTransactionError, BackendConnectorTransaction, BackendConnectorTransactionError,
BackendConnectorTransactionType, BackendConnectorTransactionTypeDyn, BackendConnectorTransactionType, BackendConnectorTransactionTypeDyn,
@ -38,42 +42,33 @@ use {
}, },
backends::metal::{ backends::metal::{
MetalBackend, MetalError, MetalBackend, MetalError,
present::{ present::{DEFAULT_POST_COMMIT_MARGIN, POST_COMMIT_MARGIN_DELTA},
DEFAULT_POST_COMMIT_MARGIN, DEFAULT_PRE_COMMIT_MARGIN, POST_COMMIT_MARGIN_DELTA, transaction::MetalDeviceTransaction,
},
transaction::{DrmConnectorState, DrmCrtcState, DrmPlaneState, MetalDeviceTransaction},
}, },
cmm::cmm_primaries::Primaries,
drm_feedback::DrmFeedback, drm_feedback::DrmFeedback,
edid::{CtaDataBlock, Descriptor, EdidExtension},
format::XRGB8888, format::XRGB8888,
gfx_api::GfxApi, gfx_api::GfxApi,
ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC, KIND_ZERO_COPY}, ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC, KIND_ZERO_COPY},
tree::OutputNode, tree::OutputNode,
udev::UdevDevice, udev::UdevDevice,
utils::{ utils::{
binary_search_map::BinarySearchMap, bitflags::BitflagsExt, cell_ext::CellExt, cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap,
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, errorfmt::ErrorFmt, oserror::OsError,
geometric_decay::GeometricDecay, numcell::NumCell, ordered_float::F64,
oserror::OsError,
}, },
video::{ video::{
INVALID_MODIFIER,
drm::{ drm::{
ConnectorStatus, ConnectorType, DRM_CLIENT_CAP_ATOMIC, DrmBlob, DrmCardResources, ConnectorStatus, DRM_CLIENT_CAP_ATOMIC, DrmBlob, DrmCardResources, DrmConnector,
DrmConnector, DrmCrtc, DrmEncoder, DrmError, DrmEvent, DrmFb, DrmMaster, DrmCrtc, DrmError, DrmEvent, DrmFb, DrmMaster, DrmObject, DrmProperty,
DrmObject, DrmPlane, DrmProperty, DrmPropertyType, DrmVersion, DrmVersion, drm_mode_modeinfo, hdr_output_metadata,
HDMI_EOTF_TRADITIONAL_GAMMA_SDR, drm_mode_modeinfo, hdr_output_metadata,
}, },
gbm::GbmDevice, gbm::GbmDevice,
}, },
}, },
ahash::{AHashMap, AHashSet}, ahash::{AHashMap, AHashSet},
bstr::ByteSlice, bstr::ByteSlice,
indexmap::indexset,
isnt::std_1::collections::IsntHashMapExt, isnt::std_1::collections::IsntHashMapExt,
std::{ std::{
cell::{Cell, RefCell}, cell::Cell,
collections::hash_map::Entry, collections::hash_map::Entry,
mem, mem,
ops::DerefMut, ops::DerefMut,
@ -555,626 +550,6 @@ impl Connector for MetalConnector {
} }
} }
fn get_connectors(
backend: &Rc<MetalBackend>,
dev: &Rc<MetalDrmDevice>,
ids: &[DrmConnector],
) -> Result<
(
CopyHashMap<DrmConnector, Rc<MetalConnector>>,
CopyHashMap<DrmConnector, ConnectorFutures>,
),
DrmError,
> {
let connectors = CopyHashMap::new();
let futures = CopyHashMap::new();
for connector in ids {
match create_connector(backend, *connector, dev) {
Ok((con, fut)) => {
let id = con.id;
connectors.set(id, con);
futures.set(id, fut);
}
Err(e) => return Err(DrmError::CreateConnector(Box::new(e))),
}
}
Ok((connectors, futures))
}
fn create_connector(
backend: &Rc<MetalBackend>,
connector: DrmConnector,
dev: &Rc<MetalDrmDevice>,
) -> Result<(Rc<MetalConnector>, ConnectorFutures), DrmError> {
let display = create_connector_display_data(connector, dev)?;
log::info!(
"Creating connector {} for device {}",
display.connector_id,
dev.devnode.as_bytes().as_bstr(),
);
let slf = Rc::new(MetalConnector {
id: connector,
kernel_id: Cell::new(display.connector_id),
master: dev.master.clone(),
state: backend.state.clone(),
dev: dev.clone(),
backend: backend.clone(),
connector_id: backend.state.connector_ids.next(),
buffers: Default::default(),
color_description: CloneCell::new(backend.state.color_manager.srgb_gamma22().clone()),
lease: Cell::new(None),
buffers_idle: Cell::new(true),
crtc_idle: Cell::new(true),
has_damage: NumCell::new(1),
primary_plane: Default::default(),
cursor_plane: Default::default(),
crtc: Default::default(),
on_change: Default::default(),
present_trigger: Default::default(),
cursor_x: Cell::new(0),
cursor_y: Cell::new(0),
cursor_enabled: Cell::new(false),
cursor_buffers: Default::default(),
display: RefCell::new(display),
frontend_state: Cell::new(FrontState::Removed),
cursor_changed: Cell::new(false),
cursor_damage: Cell::new(false),
cursor_swap_buffer: Cell::new(false),
cursor_sync: Default::default(),
drm_feedback: Default::default(),
scanout_buffers: Default::default(),
active_framebuffer: Default::default(),
next_framebuffer: Default::default(),
direct_scanout_active: Cell::new(false),
next_vblank_nsec: Cell::new(0),
version: Default::default(),
expected_sequence: Default::default(),
pre_commit_margin_decay: GeometricDecay::new(0.5, DEFAULT_PRE_COMMIT_MARGIN),
pre_commit_margin: Cell::new(DEFAULT_PRE_COMMIT_MARGIN),
post_commit_margin_decay: GeometricDecay::new(0.1, dev.min_post_commit_margin.get()),
post_commit_margin: Cell::new(dev.min_post_commit_margin.get()),
vblank_miss_sec: Cell::new(0),
vblank_miss_this_sec: Default::default(),
presentation_is_sync: Cell::new(false),
presentation_is_zero_copy: Cell::new(false),
});
let futures = ConnectorFutures {
_present: backend.state.eng.spawn2(
"present loop",
Phase::Present,
slf.clone().present_loop(),
),
};
Ok((slf, futures))
}
fn create_connector_display_data(
connector: DrmConnector,
dev: &Rc<MetalDrmDevice>,
) -> Result<ConnectorDisplayData, DrmError> {
let info = dev.master.get_connector_info(connector, true)?;
let mut crtcs = BinarySearchMap::new();
for encoder in info.encoders {
if let Some(encoder) = dev.encoders.get(&encoder) {
for (_, crtc) in &encoder.crtcs {
crtcs.insert(crtc.id, crtc.clone());
}
}
}
let props = collect_properties(&dev.master, connector)?;
let connection = ConnectorStatus::from_drm(info.connection);
let mut name = String::new();
let mut manufacturer = String::new();
let mut serial_number = String::new();
let mut vrr_refresh_max_nsec = u64::MAX;
let connector_id = ConnectorKernelId {
ty: ConnectorType::from_drm(info.connector_type),
idx: info.connector_type_id,
};
let mut supports_bt2020 = false;
let mut supports_pq = false;
let mut luminance = None;
let mut primaries = Primaries::SRGB;
'fetch_edid: {
if connection != ConnectorStatus::Connected {
break 'fetch_edid;
}
let edid = match props.get("EDID") {
Ok(e) => e,
_ => {
log::warn!(
"Connector {} is connected but has no EDID blob",
connector_id,
);
break 'fetch_edid;
}
};
let blob = match dev.master.getblob_vec::<u8>(DrmBlob(edid.value as _)) {
Ok(b) => b,
Err(e) => {
log::error!(
"Could not fetch edid property of connector {}: {}",
connector_id,
ErrorFmt(e)
);
break 'fetch_edid;
}
};
let edid = match crate::edid::parse(&blob) {
Ok(e) => e,
Err(e) => {
log::error!(
"Could not parse edid property of connector {}: {}",
connector_id,
ErrorFmt(e)
);
break 'fetch_edid;
}
};
manufacturer = edid.base_block.id_manufacturer_name.to_string();
for descriptor in edid.base_block.descriptors.iter().flatten() {
match descriptor {
Descriptor::DisplayProductSerialNumber(s) => {
serial_number.clone_from(s);
}
Descriptor::DisplayProductName(s) => {
name.clone_from(s);
}
_ => {}
}
}
if name.is_empty() {
log::warn!(
"The display attached to connector {} does not have a product name descriptor",
connector_id,
);
}
if serial_number.is_empty() {
log::warn!(
"The display attached to connector {} does not have a serial number descriptor",
connector_id,
);
serial_number = edid.base_block.id_serial_number.to_string();
}
let min_vrr_hz = 'fetch_min_hz: {
for ext in &edid.extension_blocks {
if let EdidExtension::CtaV3(cta) = ext {
for data_block in &cta.data_blocks {
if let CtaDataBlock::VendorAmd(amd) = data_block {
break 'fetch_min_hz amd.minimum_refresh_hz as u64;
}
}
}
}
for desc in &edid.base_block.descriptors {
if let Some(desc) = desc
&& let Descriptor::DisplayRangeLimitsAndAdditionalTiming(timings) = desc
{
break 'fetch_min_hz timings.vertical_field_rate_min as u64;
}
}
0
};
if min_vrr_hz > 0 {
vrr_refresh_max_nsec = 1_000_000_000 / min_vrr_hz;
}
let cc = &edid.base_block.chromaticity_coordinates;
let map = |c: u16| F64(c as f64 / 1024.0);
primaries = Primaries {
r: (map(cc.red_x), map(cc.red_y)),
g: (map(cc.green_x), map(cc.green_y)),
b: (map(cc.blue_x), map(cc.blue_y)),
wp: (map(cc.white_x), map(cc.white_y)),
};
for ext in &edid.extension_blocks {
if let EdidExtension::CtaV3(cta) = ext {
for data_block in &cta.data_blocks {
match data_block {
CtaDataBlock::Colorimetry(c) => {
if c.bt2020_rgb {
supports_bt2020 = true;
}
}
CtaDataBlock::StaticHdrMetadata(h) => {
if h.smpte_st_2084 {
supports_pq = true;
}
if let Some(max) = h.max_luminance {
luminance = Some(BackendLuminance {
min: h.min_luminance.unwrap_or(0.0),
max,
max_fall: h.max_luminance.unwrap_or(max),
});
}
}
_ => {}
}
}
}
}
}
let output_id = OutputId::new(connector_id.to_string(), manufacturer, name, serial_number);
let first_mode = info
.modes
.first()
.cloned()
.map(|m| m.to_backend())
.unwrap_or_default();
let persistent = match dev.backend.persistent_display_data.get(&output_id) {
Some(ds) => {
if connection != ConnectorStatus::Disconnected {
log::info!("Reusing desired state for {:?}", output_id);
}
ds
}
None => {
let ds = Rc::new(PersistentDisplayData {
state: RefCell::new(BackendConnectorState {
serial: dev.backend.state.backend_connector_state_serials.next(),
enabled: true,
active: true,
mode: first_mode,
non_desktop_override: None,
vrr: false,
tearing: false,
format: XRGB8888,
color_space: Default::default(),
eotf: Default::default(),
gamma_lut: Default::default(),
}),
});
dev.backend
.persistent_display_data
.set(output_id.clone(), ds.clone());
ds
}
};
let mut desired_state = persistent.state.borrow_mut();
if desired_state.mode == Mode::default() {
desired_state.mode = first_mode;
} else if info
.modes
.iter()
.all(|m| m.to_backend() != desired_state.mode)
{
log::warn!("Discarding previously desired mode");
desired_state.mode = first_mode;
}
let non_desktop = props.get("non-desktop")?.value != 0;
let vrr_capable = match props.get("vrr_capable") {
Ok(c) => c.value == 1,
Err(_) => false,
};
if !vrr_capable && desired_state.vrr {
log::warn!("Connector has lost VRR capability");
desired_state.vrr = false;
}
{
let viable = match desired_state.eotf {
BackendEotfs::Default => true,
BackendEotfs::Pq => supports_pq,
};
if !viable {
log::warn!("Discarding previously desired EOTF");
desired_state.eotf = BackendEotfs::Default;
}
}
{
let viable = match desired_state.color_space {
BackendColorSpace::Default => true,
BackendColorSpace::Bt2020 => supports_bt2020,
};
if !viable {
log::warn!("Discarding previously desired color space");
desired_state.color_space = BackendColorSpace::Default;
}
}
drop(desired_state);
let default_properties = create_default_properties(
&props,
&[
("Broadcast RGB", DefaultValue::Enum("Automatic")),
("HDR_SOURCE_METADATA", DefaultValue::Fixed(0)),
("Output format", DefaultValue::Enum("Default")),
("WRITEBACK_FB_ID", DefaultValue::Fixed(0)),
("WRITEBACK_OUT_FENCE_PTR", DefaultValue::Fixed(0)),
("content type", DefaultValue::Enum("No Data")),
("dither", DefaultValue::Enum("off")),
("max bpc", DefaultValue::RangeMax),
],
);
let hdr_metadata_prop = props
.get("HDR_OUTPUT_METADATA")
.map(|p| p.map(|v| DrmBlob(v as _)))
.ok();
let mut hdr_metadata = None;
let mut hdr_metadata_blob_id = DrmBlob::NONE;
if let Some(p) = &hdr_metadata_prop {
hdr_metadata_blob_id = p.value;
hdr_metadata = Some(hdr_output_metadata::from_eotf(
HDMI_EOTF_TRADITIONAL_GAMMA_SDR,
));
if p.value.is_some() {
match dev.master.getblob::<hdr_output_metadata>(p.value) {
Ok(m) => hdr_metadata = Some(m),
_ => {
log::debug!("Could not retrieve hdr output metadata");
}
}
}
}
let colorspace_prop = props.get("Colorspace").ok();
let crtc_id = props.get("CRTC_ID")?.map(|v| DrmCrtc(v as _));
let drm_state = DrmConnectorState {
crtc_id: crtc_id.value,
color_space: colorspace_prop.map(|p| p.value),
hdr_metadata,
hdr_metadata_blob_id,
hdr_metadata_blob: None,
locked: true,
fb: DrmFb::NONE,
fb_idx: 0,
cursor_fb: DrmFb::NONE,
cursor_fb_idx: 0,
cursor_x: 0,
cursor_y: 0,
out_fd: None,
src_w: 0,
src_h: 0,
crtc_x: 0,
crtc_y: 0,
crtc_w: 0,
crtc_h: 0,
};
Ok(ConnectorDisplayData {
crtc_id: props.get("CRTC_ID")?.id,
crtcs,
first_mode,
modes: info.modes,
persistent,
refresh: 0,
non_desktop,
non_desktop_effective: non_desktop,
vrr_capable,
_vrr_refresh_max_nsec: vrr_refresh_max_nsec,
default_properties,
untyped_properties: props.to_untyped(),
connection,
mm_width: info.mm_width,
mm_height: info.mm_height,
_subpixel: info.subpixel,
supports_bt2020,
supports_pq,
primaries,
luminance,
connector_id,
output_id,
colorspace: colorspace_prop.map(|p| p.id),
hdr_metadata: hdr_metadata_prop.map(|p| p.id),
drm_state,
})
}
fn create_encoder(
encoder: DrmEncoder,
master: &Rc<DrmMaster>,
crtcs: &AHashMap<DrmCrtc, Rc<MetalCrtc>>,
) -> Result<MetalEncoder, DrmError> {
let info = master.get_encoder_info(encoder)?;
let mut possible = AHashMap::new();
for crtc in crtcs.values() {
if info.possible_crtcs.contains(1 << crtc.idx) {
possible.insert(crtc.id, crtc.clone());
}
}
Ok(MetalEncoder {
id: encoder,
crtcs: possible,
})
}
fn create_crtc(
crtc: DrmCrtc,
idx: usize,
master: &Rc<DrmMaster>,
planes: &AHashMap<DrmPlane, Rc<MetalPlane>>,
) -> Result<MetalCrtc, DrmError> {
let mask = 1 << idx;
let mut possible_planes = BinarySearchMap::new();
for plane in planes.values() {
if plane.possible_crtcs.contains(mask) {
possible_planes.insert(plane.id, plane.clone());
}
}
let props = collect_properties(master, crtc)?;
let default_properties = create_default_properties(
&props,
&[
("AMD_CRTC_REGAMMA_TF", DefaultValue::Enum("Default")),
("CTM", DefaultValue::Fixed(0)),
("DEGAMMA_LUT", DefaultValue::Fixed(0)),
("OUT_FENCE_PTR", DefaultValue::Fixed(0)),
],
);
let active = props.get("ACTIVE")?.map(|v| v == 1);
let mode_id = props.get("MODE_ID")?.map(|v| DrmBlob(v as u32));
let vrr_enabled = props.get("VRR_ENABLED")?.map(|v| v == 1);
let out_fence_ptr = props.get("OUT_FENCE_PTR")?;
let gamma_lut = props
.get("GAMMA_LUT")
.ok()
.map(|v| v.map(|v| DrmBlob(v as u32)));
let mut gamma_lut_size = None;
if gamma_lut.is_some() {
gamma_lut_size = props.get("GAMMA_LUT_SIZE").ok().map(|v| v.value as u32);
}
let mut mode = None;
if mode_id.value.is_some() {
match master.getblob::<drm_mode_modeinfo>(mode_id.value) {
Ok(m) => mode = Some(m.into()),
_ => {
log::debug!("Could not retrieve current mode of connector");
}
}
}
let state = DrmCrtcState {
active: active.value,
mode,
mode_blob_id: mode_id.value,
mode_blob: None,
vrr_enabled: vrr_enabled.value,
assigned_connector: DrmConnector::NONE,
gamma_lut: None,
gamma_lut_blob_id: gamma_lut.map_or(DrmBlob::NONE, |v| v.value),
gamma_lut_blob: None,
};
Ok(MetalCrtc {
id: crtc,
idx,
master: master.clone(),
default_properties,
untyped_properties: RefCell::new(props.to_untyped()),
lease: Cell::new(None),
possible_planes,
connector: Default::default(),
pending_flip: Default::default(),
drm_state: RefCell::new(state),
active: active.id,
mode_id: mode_id.id,
vrr_enabled: vrr_enabled.id,
out_fence_ptr: out_fence_ptr.id,
gamma_lut: gamma_lut.map(|v| v.id),
gamma_lut_size,
sequence: Cell::new(0),
have_queued_sequence: Cell::new(false),
needs_vblank_emulation: Cell::new(false),
})
}
fn create_plane(plane: DrmPlane, master: &Rc<DrmMaster>) -> Result<MetalPlane, DrmError> {
let info = master.get_plane_info(plane)?;
let props = collect_properties(master, plane)?;
let mut formats = AHashMap::new();
if let Some((_, v)) = props.props.get(b"IN_FORMATS".as_bstr()) {
for format in master.get_in_formats(*v as _)? {
if format.modifiers.is_empty() {
continue;
}
if let Some(f) = crate::format::formats().get(&format.format) {
formats.insert(
format.format,
PlaneFormat {
format: f,
modifiers: format.modifiers,
},
);
}
}
} else {
for format in info.format_types {
if let Some(f) = crate::format::formats().get(&format) {
formats.insert(
format,
PlaneFormat {
format: f,
modifiers: indexset![INVALID_MODIFIER],
},
);
}
}
}
let ty = match props.props.get(b"type".as_bstr()) {
Some((def, val)) => match &def.ty {
DrmPropertyType::Enum { values, .. } => 'ty: {
for v in values {
if v.value == *val {
match v.name.as_bytes() {
b"Overlay" => break 'ty PlaneType::Overlay,
b"Primary" => break 'ty PlaneType::Primary,
b"Cursor" => break 'ty PlaneType::Cursor,
_ => return Err(DrmError::UnknownPlaneType(v.name.to_owned())),
}
}
}
return Err(DrmError::InvalidPlaneType(*val));
}
_ => return Err(DrmError::InvalidPlaneTypeProperty),
},
_ => {
return Err(DrmError::MissingProperty(
"type".to_string().into_boxed_str(),
));
}
};
let default_properties = create_default_properties(
&props,
&[
("AMD_PLANE_BLEND_LUT", DefaultValue::Fixed(0)),
("AMD_PLANE_BLEND_TF", DefaultValue::Enum("Default")),
("AMD_PLANE_CTM", DefaultValue::Fixed(0)),
("AMD_PLANE_DEGAMMA_LUT", DefaultValue::Fixed(0)),
("AMD_PLANE_HDR_MULT", DefaultValue::Fixed(0)),
("AMD_PLANE_LUT3D", DefaultValue::Fixed(0)),
("AMD_PLANE_SHAPER_LUT", DefaultValue::Fixed(0)),
("AMD_PLANE_SHAPER_TF", DefaultValue::Enum("Default")),
("alpha", DefaultValue::RangeMax),
("pixel blend mode", DefaultValue::Enum("Pre-multiplied")),
("rotation", DefaultValue::Bitmask(&["rotate-0"])),
],
);
let fb_id = props.get("FB_ID")?.map(|v| DrmFb(v as _));
let crtc_id = props.get("CRTC_ID")?.map(|v| DrmCrtc(v as _));
let crtc_x = props.get("CRTC_X")?.map(|v| v as i32);
let crtc_y = props.get("CRTC_Y")?.map(|v| v as i32);
let crtc_w = props.get("CRTC_W")?.map(|v| v as i32);
let crtc_h = props.get("CRTC_H")?.map(|v| v as i32);
let src_x = props.get("SRC_X")?.map(|v| v as u32);
let src_y = props.get("SRC_Y")?.map(|v| v as u32);
let src_w = props.get("SRC_W")?.map(|v| v as u32);
let src_h = props.get("SRC_H")?.map(|v| v as u32);
let in_fence_fd = props.get("IN_FENCE_FD")?;
let state = DrmPlaneState {
fb_id: fb_id.value,
src_x: src_x.value,
src_y: src_y.value,
src_w: src_w.value,
src_h: src_h.value,
assigned_crtc: DrmCrtc::NONE,
crtc_id: crtc_id.value,
crtc_x: crtc_x.value,
crtc_y: crtc_y.value,
crtc_w: crtc_w.value,
crtc_h: crtc_h.value,
buffers: None,
};
Ok(MetalPlane {
id: plane,
master: master.clone(),
default_properties,
untyped_properties: RefCell::new(props.to_untyped()),
ty,
possible_crtcs: info.possible_crtcs,
formats,
drm_state: RefCell::new(state),
fb_id: fb_id.id,
crtc_id: crtc_id.id,
crtc_x: crtc_x.id,
crtc_y: crtc_y.id,
crtc_w: crtc_w.id,
crtc_h: crtc_h.id,
src_x: src_x.id,
src_y: src_y.id,
src_w: src_w.id,
src_h: src_h.id,
in_fence_fd: in_fence_fd.id,
mode_w: Cell::new(0),
mode_h: Cell::new(0),
lease: Cell::new(None),
})
}
impl MetalBackend { impl MetalBackend {
pub fn check_render_context(&self, dev: &Rc<MetalDrmDevice>) -> bool { pub fn check_render_context(&self, dev: &Rc<MetalDrmDevice>) -> bool {
let ctx = match self.ctx.get() { let ctx = match self.ctx.get() {

View file

@ -0,0 +1,660 @@
use {
super::{
ConnectorDisplayData, ConnectorFutures, FrontState, MetalConnector, MetalCrtc,
MetalDrmDevice, MetalEncoder, MetalPlane, PersistentDisplayData, PlaneFormat, PlaneType,
properties::{DefaultValue, collect_properties, create_default_properties},
},
crate::{
async_engine::Phase,
backend::{
BackendColorSpace, BackendConnectorState, BackendEotfs, BackendLuminance,
ConnectorKernelId, Mode, OutputId,
},
backends::metal::{
MetalBackend,
present::DEFAULT_PRE_COMMIT_MARGIN,
transaction::{DrmConnectorState, DrmCrtcState, DrmPlaneState},
},
cmm::cmm_primaries::Primaries,
edid::{CtaDataBlock, Descriptor, EdidExtension},
format::XRGB8888,
utils::{
binary_search_map::BinarySearchMap, bitflags::BitflagsExt, clonecell::CloneCell,
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, geometric_decay::GeometricDecay,
numcell::NumCell, ordered_float::F64,
},
video::{
INVALID_MODIFIER,
drm::{
ConnectorStatus, ConnectorType, DrmBlob, DrmConnector, DrmCrtc, DrmEncoder,
DrmError, DrmFb, DrmMaster, DrmObject, DrmPlane, DrmPropertyType,
HDMI_EOTF_TRADITIONAL_GAMMA_SDR, drm_mode_modeinfo, hdr_output_metadata,
},
},
},
ahash::AHashMap,
bstr::ByteSlice,
indexmap::indexset,
std::{
cell::{Cell, RefCell},
rc::Rc,
},
};
pub(super) fn get_connectors(
backend: &Rc<MetalBackend>,
dev: &Rc<MetalDrmDevice>,
ids: &[DrmConnector],
) -> Result<
(
CopyHashMap<DrmConnector, Rc<MetalConnector>>,
CopyHashMap<DrmConnector, ConnectorFutures>,
),
DrmError,
> {
let connectors = CopyHashMap::new();
let futures = CopyHashMap::new();
for connector in ids {
match create_connector(backend, *connector, dev) {
Ok((con, fut)) => {
let id = con.id;
connectors.set(id, con);
futures.set(id, fut);
}
Err(e) => return Err(DrmError::CreateConnector(Box::new(e))),
}
}
Ok((connectors, futures))
}
pub(super) fn create_connector(
backend: &Rc<MetalBackend>,
connector: DrmConnector,
dev: &Rc<MetalDrmDevice>,
) -> Result<(Rc<MetalConnector>, ConnectorFutures), DrmError> {
let display = create_connector_display_data(connector, dev)?;
log::info!(
"Creating connector {} for device {}",
display.connector_id,
dev.devnode.as_bytes().as_bstr(),
);
let slf = Rc::new(MetalConnector {
id: connector,
kernel_id: Cell::new(display.connector_id),
master: dev.master.clone(),
state: backend.state.clone(),
dev: dev.clone(),
backend: backend.clone(),
connector_id: backend.state.connector_ids.next(),
buffers: Default::default(),
color_description: CloneCell::new(backend.state.color_manager.srgb_gamma22().clone()),
lease: Cell::new(None),
buffers_idle: Cell::new(true),
crtc_idle: Cell::new(true),
has_damage: NumCell::new(1),
primary_plane: Default::default(),
cursor_plane: Default::default(),
crtc: Default::default(),
on_change: Default::default(),
present_trigger: Default::default(),
cursor_x: Cell::new(0),
cursor_y: Cell::new(0),
cursor_enabled: Cell::new(false),
cursor_buffers: Default::default(),
display: RefCell::new(display),
frontend_state: Cell::new(FrontState::Removed),
cursor_changed: Cell::new(false),
cursor_damage: Cell::new(false),
cursor_swap_buffer: Cell::new(false),
cursor_sync: Default::default(),
drm_feedback: Default::default(),
scanout_buffers: Default::default(),
active_framebuffer: Default::default(),
next_framebuffer: Default::default(),
direct_scanout_active: Cell::new(false),
next_vblank_nsec: Cell::new(0),
version: Default::default(),
expected_sequence: Default::default(),
pre_commit_margin_decay: GeometricDecay::new(0.5, DEFAULT_PRE_COMMIT_MARGIN),
pre_commit_margin: Cell::new(DEFAULT_PRE_COMMIT_MARGIN),
post_commit_margin_decay: GeometricDecay::new(0.1, dev.min_post_commit_margin.get()),
post_commit_margin: Cell::new(dev.min_post_commit_margin.get()),
vblank_miss_sec: Cell::new(0),
vblank_miss_this_sec: Default::default(),
presentation_is_sync: Cell::new(false),
presentation_is_zero_copy: Cell::new(false),
});
let futures = ConnectorFutures {
_present: backend.state.eng.spawn2(
"present loop",
Phase::Present,
slf.clone().present_loop(),
),
};
Ok((slf, futures))
}
pub(super) fn create_connector_display_data(
connector: DrmConnector,
dev: &Rc<MetalDrmDevice>,
) -> Result<ConnectorDisplayData, DrmError> {
let info = dev.master.get_connector_info(connector, true)?;
let mut crtcs = BinarySearchMap::new();
for encoder in info.encoders {
if let Some(encoder) = dev.encoders.get(&encoder) {
for (_, crtc) in &encoder.crtcs {
crtcs.insert(crtc.id, crtc.clone());
}
}
}
let props = collect_properties(&dev.master, connector)?;
let connection = ConnectorStatus::from_drm(info.connection);
let mut name = String::new();
let mut manufacturer = String::new();
let mut serial_number = String::new();
let mut vrr_refresh_max_nsec = u64::MAX;
let connector_id = ConnectorKernelId {
ty: ConnectorType::from_drm(info.connector_type),
idx: info.connector_type_id,
};
let mut supports_bt2020 = false;
let mut supports_pq = false;
let mut luminance = None;
let mut primaries = Primaries::SRGB;
'fetch_edid: {
if connection != ConnectorStatus::Connected {
break 'fetch_edid;
}
let edid = match props.get("EDID") {
Ok(e) => e,
_ => {
log::warn!(
"Connector {} is connected but has no EDID blob",
connector_id,
);
break 'fetch_edid;
}
};
let blob = match dev.master.getblob_vec::<u8>(DrmBlob(edid.value as _)) {
Ok(b) => b,
Err(e) => {
log::error!(
"Could not fetch edid property of connector {}: {}",
connector_id,
ErrorFmt(e)
);
break 'fetch_edid;
}
};
let edid = match crate::edid::parse(&blob) {
Ok(e) => e,
Err(e) => {
log::error!(
"Could not parse edid property of connector {}: {}",
connector_id,
ErrorFmt(e)
);
break 'fetch_edid;
}
};
manufacturer = edid.base_block.id_manufacturer_name.to_string();
for descriptor in edid.base_block.descriptors.iter().flatten() {
match descriptor {
Descriptor::DisplayProductSerialNumber(s) => {
serial_number.clone_from(s);
}
Descriptor::DisplayProductName(s) => {
name.clone_from(s);
}
_ => {}
}
}
if name.is_empty() {
log::warn!(
"The display attached to connector {} does not have a product name descriptor",
connector_id,
);
}
if serial_number.is_empty() {
log::warn!(
"The display attached to connector {} does not have a serial number descriptor",
connector_id,
);
serial_number = edid.base_block.id_serial_number.to_string();
}
let min_vrr_hz = 'fetch_min_hz: {
for ext in &edid.extension_blocks {
if let EdidExtension::CtaV3(cta) = ext {
for data_block in &cta.data_blocks {
if let CtaDataBlock::VendorAmd(amd) = data_block {
break 'fetch_min_hz amd.minimum_refresh_hz as u64;
}
}
}
}
for desc in &edid.base_block.descriptors {
if let Some(desc) = desc
&& let Descriptor::DisplayRangeLimitsAndAdditionalTiming(timings) = desc
{
break 'fetch_min_hz timings.vertical_field_rate_min as u64;
}
}
0
};
if min_vrr_hz > 0 {
vrr_refresh_max_nsec = 1_000_000_000 / min_vrr_hz;
}
let cc = &edid.base_block.chromaticity_coordinates;
let map = |c: u16| F64(c as f64 / 1024.0);
primaries = Primaries {
r: (map(cc.red_x), map(cc.red_y)),
g: (map(cc.green_x), map(cc.green_y)),
b: (map(cc.blue_x), map(cc.blue_y)),
wp: (map(cc.white_x), map(cc.white_y)),
};
for ext in &edid.extension_blocks {
if let EdidExtension::CtaV3(cta) = ext {
for data_block in &cta.data_blocks {
match data_block {
CtaDataBlock::Colorimetry(c) => {
if c.bt2020_rgb {
supports_bt2020 = true;
}
}
CtaDataBlock::StaticHdrMetadata(h) => {
if h.smpte_st_2084 {
supports_pq = true;
}
if let Some(max) = h.max_luminance {
luminance = Some(BackendLuminance {
min: h.min_luminance.unwrap_or(0.0),
max,
max_fall: h.max_luminance.unwrap_or(max),
});
}
}
_ => {}
}
}
}
}
}
let output_id = OutputId::new(connector_id.to_string(), manufacturer, name, serial_number);
let first_mode = info
.modes
.first()
.cloned()
.map(|m| m.to_backend())
.unwrap_or_default();
let persistent = match dev.backend.persistent_display_data.get(&output_id) {
Some(ds) => {
if connection != ConnectorStatus::Disconnected {
log::info!("Reusing desired state for {:?}", output_id);
}
ds
}
None => {
let ds = Rc::new(PersistentDisplayData {
state: RefCell::new(BackendConnectorState {
serial: dev.backend.state.backend_connector_state_serials.next(),
enabled: true,
active: true,
mode: first_mode,
non_desktop_override: None,
vrr: false,
tearing: false,
format: XRGB8888,
color_space: Default::default(),
eotf: Default::default(),
gamma_lut: Default::default(),
}),
});
dev.backend
.persistent_display_data
.set(output_id.clone(), ds.clone());
ds
}
};
let mut desired_state = persistent.state.borrow_mut();
if desired_state.mode == Mode::default() {
desired_state.mode = first_mode;
} else if info
.modes
.iter()
.all(|m| m.to_backend() != desired_state.mode)
{
log::warn!("Discarding previously desired mode");
desired_state.mode = first_mode;
}
let non_desktop = props.get("non-desktop")?.value != 0;
let vrr_capable = match props.get("vrr_capable") {
Ok(c) => c.value == 1,
Err(_) => false,
};
if !vrr_capable && desired_state.vrr {
log::warn!("Connector has lost VRR capability");
desired_state.vrr = false;
}
{
let viable = match desired_state.eotf {
BackendEotfs::Default => true,
BackendEotfs::Pq => supports_pq,
};
if !viable {
log::warn!("Discarding previously desired EOTF");
desired_state.eotf = BackendEotfs::Default;
}
}
{
let viable = match desired_state.color_space {
BackendColorSpace::Default => true,
BackendColorSpace::Bt2020 => supports_bt2020,
};
if !viable {
log::warn!("Discarding previously desired color space");
desired_state.color_space = BackendColorSpace::Default;
}
}
drop(desired_state);
let default_properties = create_default_properties(
&props,
&[
("Broadcast RGB", DefaultValue::Enum("Automatic")),
("HDR_SOURCE_METADATA", DefaultValue::Fixed(0)),
("Output format", DefaultValue::Enum("Default")),
("WRITEBACK_FB_ID", DefaultValue::Fixed(0)),
("WRITEBACK_OUT_FENCE_PTR", DefaultValue::Fixed(0)),
("content type", DefaultValue::Enum("No Data")),
("dither", DefaultValue::Enum("off")),
("max bpc", DefaultValue::RangeMax),
],
);
let hdr_metadata_prop = props
.get("HDR_OUTPUT_METADATA")
.map(|p| p.map(|v| DrmBlob(v as _)))
.ok();
let mut hdr_metadata = None;
let mut hdr_metadata_blob_id = DrmBlob::NONE;
if let Some(p) = &hdr_metadata_prop {
hdr_metadata_blob_id = p.value;
hdr_metadata = Some(hdr_output_metadata::from_eotf(
HDMI_EOTF_TRADITIONAL_GAMMA_SDR,
));
if p.value.is_some() {
match dev.master.getblob::<hdr_output_metadata>(p.value) {
Ok(m) => hdr_metadata = Some(m),
_ => {
log::debug!("Could not retrieve hdr output metadata");
}
}
}
}
let colorspace_prop = props.get("Colorspace").ok();
let crtc_id = props.get("CRTC_ID")?.map(|v| DrmCrtc(v as _));
let drm_state = DrmConnectorState {
crtc_id: crtc_id.value,
color_space: colorspace_prop.map(|p| p.value),
hdr_metadata,
hdr_metadata_blob_id,
hdr_metadata_blob: None,
locked: true,
fb: DrmFb::NONE,
fb_idx: 0,
cursor_fb: DrmFb::NONE,
cursor_fb_idx: 0,
cursor_x: 0,
cursor_y: 0,
out_fd: None,
src_w: 0,
src_h: 0,
crtc_x: 0,
crtc_y: 0,
crtc_w: 0,
crtc_h: 0,
};
Ok(ConnectorDisplayData {
crtc_id: props.get("CRTC_ID")?.id,
crtcs,
first_mode,
modes: info.modes,
persistent,
refresh: 0,
non_desktop,
non_desktop_effective: non_desktop,
vrr_capable,
_vrr_refresh_max_nsec: vrr_refresh_max_nsec,
default_properties,
untyped_properties: props.to_untyped(),
connection,
mm_width: info.mm_width,
mm_height: info.mm_height,
_subpixel: info.subpixel,
supports_bt2020,
supports_pq,
primaries,
luminance,
connector_id,
output_id,
colorspace: colorspace_prop.map(|p| p.id),
hdr_metadata: hdr_metadata_prop.map(|p| p.id),
drm_state,
})
}
pub(super) fn create_encoder(
encoder: DrmEncoder,
master: &Rc<DrmMaster>,
crtcs: &AHashMap<DrmCrtc, Rc<MetalCrtc>>,
) -> Result<MetalEncoder, DrmError> {
let info = master.get_encoder_info(encoder)?;
let mut possible = AHashMap::new();
for crtc in crtcs.values() {
if info.possible_crtcs.contains(1 << crtc.idx) {
possible.insert(crtc.id, crtc.clone());
}
}
Ok(MetalEncoder {
id: encoder,
crtcs: possible,
})
}
pub(super) fn create_crtc(
crtc: DrmCrtc,
idx: usize,
master: &Rc<DrmMaster>,
planes: &AHashMap<DrmPlane, Rc<MetalPlane>>,
) -> Result<MetalCrtc, DrmError> {
let mask = 1 << idx;
let mut possible_planes = BinarySearchMap::new();
for plane in planes.values() {
if plane.possible_crtcs.contains(mask) {
possible_planes.insert(plane.id, plane.clone());
}
}
let props = collect_properties(master, crtc)?;
let default_properties = create_default_properties(
&props,
&[
("AMD_CRTC_REGAMMA_TF", DefaultValue::Enum("Default")),
("CTM", DefaultValue::Fixed(0)),
("DEGAMMA_LUT", DefaultValue::Fixed(0)),
("OUT_FENCE_PTR", DefaultValue::Fixed(0)),
],
);
let active = props.get("ACTIVE")?.map(|v| v == 1);
let mode_id = props.get("MODE_ID")?.map(|v| DrmBlob(v as u32));
let vrr_enabled = props.get("VRR_ENABLED")?.map(|v| v == 1);
let out_fence_ptr = props.get("OUT_FENCE_PTR")?;
let gamma_lut = props
.get("GAMMA_LUT")
.ok()
.map(|v| v.map(|v| DrmBlob(v as u32)));
let mut gamma_lut_size = None;
if gamma_lut.is_some() {
gamma_lut_size = props.get("GAMMA_LUT_SIZE").ok().map(|v| v.value as u32);
}
let mut mode = None;
if mode_id.value.is_some() {
match master.getblob::<drm_mode_modeinfo>(mode_id.value) {
Ok(m) => mode = Some(m.into()),
_ => {
log::debug!("Could not retrieve current mode of connector");
}
}
}
let state = DrmCrtcState {
active: active.value,
mode,
mode_blob_id: mode_id.value,
mode_blob: None,
vrr_enabled: vrr_enabled.value,
assigned_connector: DrmConnector::NONE,
gamma_lut: None,
gamma_lut_blob_id: gamma_lut.map_or(DrmBlob::NONE, |v| v.value),
gamma_lut_blob: None,
};
Ok(MetalCrtc {
id: crtc,
idx,
master: master.clone(),
default_properties,
untyped_properties: RefCell::new(props.to_untyped()),
lease: Cell::new(None),
possible_planes,
connector: Default::default(),
pending_flip: Default::default(),
drm_state: RefCell::new(state),
active: active.id,
mode_id: mode_id.id,
vrr_enabled: vrr_enabled.id,
out_fence_ptr: out_fence_ptr.id,
gamma_lut: gamma_lut.map(|v| v.id),
gamma_lut_size,
sequence: Cell::new(0),
have_queued_sequence: Cell::new(false),
needs_vblank_emulation: Cell::new(false),
})
}
pub(super) fn create_plane(plane: DrmPlane, master: &Rc<DrmMaster>) -> Result<MetalPlane, DrmError> {
let info = master.get_plane_info(plane)?;
let props = collect_properties(master, plane)?;
let mut formats = AHashMap::new();
if let Some((_, v)) = props.props.get(b"IN_FORMATS".as_bstr()) {
for format in master.get_in_formats(*v as _)? {
if format.modifiers.is_empty() {
continue;
}
if let Some(f) = crate::format::formats().get(&format.format) {
formats.insert(
format.format,
PlaneFormat {
format: f,
modifiers: format.modifiers,
},
);
}
}
} else {
for format in info.format_types {
if let Some(f) = crate::format::formats().get(&format) {
formats.insert(
format,
PlaneFormat {
format: f,
modifiers: indexset![INVALID_MODIFIER],
},
);
}
}
}
let ty = match props.props.get(b"type".as_bstr()) {
Some((def, val)) => match &def.ty {
DrmPropertyType::Enum { values, .. } => 'ty: {
for v in values {
if v.value == *val {
match v.name.as_bytes() {
b"Overlay" => break 'ty PlaneType::Overlay,
b"Primary" => break 'ty PlaneType::Primary,
b"Cursor" => break 'ty PlaneType::Cursor,
_ => return Err(DrmError::UnknownPlaneType(v.name.to_owned())),
}
}
}
return Err(DrmError::InvalidPlaneType(*val));
}
_ => return Err(DrmError::InvalidPlaneTypeProperty),
},
_ => {
return Err(DrmError::MissingProperty(
"type".to_string().into_boxed_str(),
));
}
};
let default_properties = create_default_properties(
&props,
&[
("AMD_PLANE_BLEND_LUT", DefaultValue::Fixed(0)),
("AMD_PLANE_BLEND_TF", DefaultValue::Enum("Default")),
("AMD_PLANE_CTM", DefaultValue::Fixed(0)),
("AMD_PLANE_DEGAMMA_LUT", DefaultValue::Fixed(0)),
("AMD_PLANE_HDR_MULT", DefaultValue::Fixed(0)),
("AMD_PLANE_LUT3D", DefaultValue::Fixed(0)),
("AMD_PLANE_SHAPER_LUT", DefaultValue::Fixed(0)),
("AMD_PLANE_SHAPER_TF", DefaultValue::Enum("Default")),
("alpha", DefaultValue::RangeMax),
("pixel blend mode", DefaultValue::Enum("Pre-multiplied")),
("rotation", DefaultValue::Bitmask(&["rotate-0"])),
],
);
let fb_id = props.get("FB_ID")?.map(|v| DrmFb(v as _));
let crtc_id = props.get("CRTC_ID")?.map(|v| DrmCrtc(v as _));
let crtc_x = props.get("CRTC_X")?.map(|v| v as i32);
let crtc_y = props.get("CRTC_Y")?.map(|v| v as i32);
let crtc_w = props.get("CRTC_W")?.map(|v| v as i32);
let crtc_h = props.get("CRTC_H")?.map(|v| v as i32);
let src_x = props.get("SRC_X")?.map(|v| v as u32);
let src_y = props.get("SRC_Y")?.map(|v| v as u32);
let src_w = props.get("SRC_W")?.map(|v| v as u32);
let src_h = props.get("SRC_H")?.map(|v| v as u32);
let in_fence_fd = props.get("IN_FENCE_FD")?;
let state = DrmPlaneState {
fb_id: fb_id.value,
src_x: src_x.value,
src_y: src_y.value,
src_w: src_w.value,
src_h: src_h.value,
assigned_crtc: DrmCrtc::NONE,
crtc_id: crtc_id.value,
crtc_x: crtc_x.value,
crtc_y: crtc_y.value,
crtc_w: crtc_w.value,
crtc_h: crtc_h.value,
buffers: None,
};
Ok(MetalPlane {
id: plane,
master: master.clone(),
default_properties,
untyped_properties: RefCell::new(props.to_untyped()),
ty,
possible_crtcs: info.possible_crtcs,
formats,
drm_state: RefCell::new(state),
fb_id: fb_id.id,
crtc_id: crtc_id.id,
crtc_x: crtc_x.id,
crtc_y: crtc_y.id,
crtc_w: crtc_w.id,
crtc_h: crtc_h.id,
src_x: src_x.id,
src_y: src_y.id,
src_w: src_w.id,
src_h: src_h.id,
in_fence_fd: in_fence_fd.id,
mode_w: Cell::new(0),
mode_h: Cell::new(0),
lease: Cell::new(None),
})
}

View file

@ -5,7 +5,6 @@ use {
format::{FORMATS, Format}, format::{FORMATS, Format},
gfx_api::FdSync, gfx_api::FdSync,
io_uring::IoUring, io_uring::IoUring,
rect::{Rect, Region},
utils::{ utils::{
clonecell::CloneCell, clonecell::CloneCell,
errorfmt::ErrorFmt, errorfmt::ErrorFmt,
@ -21,12 +20,10 @@ use {
}, },
vulkan_core::{ vulkan_core::{
self, VULKAN_API_VERSION, VulkanCoreError, VulkanCoreInstance, device::VulkanDeviceInf, self, VULKAN_API_VERSION, VulkanCoreError, VulkanCoreInstance, device::VulkanDeviceInf,
map_extension_properties, sync::VulkanDeviceSyncExt, map_extension_properties, timeline_semaphore::VulkanDeviceTimelineSemaphoreExt,
timeline_semaphore::VulkanDeviceTimelineSemaphoreExt,
}, },
}, },
ahash::AHashMap, ahash::AHashMap,
arrayvec::ArrayVec,
ash::{ ash::{
Device, Device,
ext::{ ext::{
@ -35,12 +32,9 @@ use {
}, },
khr::{external_fence_fd, external_memory_fd, external_semaphore_fd}, khr::{external_fence_fd, external_memory_fd, external_semaphore_fd},
vk::{ vk::{
self, AccessFlags2, BindImageMemoryInfo, BindImagePlaneMemoryInfo, BlitImageInfo2, self, BindImageMemoryInfo, BindImagePlaneMemoryInfo, BufferCopy2, BufferCreateInfo,
BufferCopy2, BufferCreateInfo, BufferImageCopy2, BufferMemoryBarrier2, BufferImageCopy2, BufferUsageFlags, CommandBuffer, CommandBufferAllocateInfo,
BufferUsageFlags, CommandBuffer, CommandBufferAllocateInfo, CommandBufferBeginInfo, CommandPoolCreateFlags, CommandPoolCreateInfo, DeviceCreateInfo, DeviceMemory,
CommandBufferSubmitInfo, CommandBufferUsageFlags, CommandPoolCreateFlags,
CommandPoolCreateInfo, CopyBufferInfo2, CopyBufferToImageInfo2, CopyImageInfo2,
CopyImageToBufferInfo2, DependencyInfo, DeviceCreateInfo, DeviceMemory,
DeviceQueueCreateInfo, DrmFormatModifierPropertiesEXT, DeviceQueueCreateInfo, DrmFormatModifierPropertiesEXT,
DrmFormatModifierPropertiesListEXT, ExportMemoryAllocateInfo, Extent3D, DrmFormatModifierPropertiesListEXT, ExportMemoryAllocateInfo, Extent3D,
ExternalBufferProperties, ExternalFenceFeatureFlags, ExternalFenceHandleTypeFlags, ExternalBufferProperties, ExternalFenceFeatureFlags, ExternalFenceHandleTypeFlags,
@ -48,23 +42,21 @@ use {
ExternalMemoryBufferCreateInfo, ExternalMemoryBufferCreateInfoKHR, ExternalMemoryBufferCreateInfo, ExternalMemoryBufferCreateInfoKHR,
ExternalMemoryFeatureFlags, ExternalMemoryHandleTypeFlags, ExternalMemoryFeatureFlags, ExternalMemoryHandleTypeFlags,
ExternalMemoryImageCreateInfo, ExternalSemaphoreFeatureFlags, ExternalMemoryImageCreateInfo, ExternalSemaphoreFeatureFlags,
ExternalSemaphoreHandleTypeFlags, ExternalSemaphoreProperties, Filter, ExternalSemaphoreHandleTypeFlags, ExternalSemaphoreProperties, FormatFeatureFlags,
FormatFeatureFlags, FormatProperties2, ImageAspectFlags, ImageBlit2, ImageCopy2, FormatProperties2, ImageAspectFlags, ImageBlit2, ImageCopy2, ImageCreateFlags,
ImageCreateFlags, ImageCreateInfo, ImageDrmFormatModifierExplicitCreateInfoEXT, ImageCreateInfo, ImageDrmFormatModifierExplicitCreateInfoEXT, ImageFormatProperties2,
ImageFormatProperties2, ImageLayout, ImageMemoryBarrier2, ImageMemoryRequirementsInfo2, ImageLayout, ImageMemoryRequirementsInfo2, ImagePlaneMemoryRequirementsInfo,
ImagePlaneMemoryRequirementsInfo, ImageSubresourceLayers, ImageSubresourceRange, ImageTiling, ImageType, ImageUsageFlags, ImportMemoryFdInfoKHR, ImportSemaphoreFdInfoKHR,
ImageTiling, ImageType, ImageUsageFlags, ImportMemoryFdInfoKHR, MemoryAllocateInfo, MemoryDedicatedAllocateInfo, MemoryFdPropertiesKHR,
ImportSemaphoreFdInfoKHR, MemoryAllocateInfo, MemoryDedicatedAllocateInfo, MemoryGetFdInfoKHR, MemoryPropertyFlags, MemoryRequirements2, MemoryType,
MemoryFdPropertiesKHR, MemoryGetFdInfoKHR, MemoryPropertyFlags, MemoryRequirements2, PhysicalDevice, PhysicalDeviceDrmPropertiesEXT,
MemoryType, Offset3D, PhysicalDevice, PhysicalDeviceDrmPropertiesEXT,
PhysicalDeviceExternalBufferInfo, PhysicalDeviceExternalFenceInfo, PhysicalDeviceExternalBufferInfo, PhysicalDeviceExternalFenceInfo,
PhysicalDeviceExternalImageFormatInfoKHR, PhysicalDeviceExternalSemaphoreInfo, PhysicalDeviceExternalImageFormatInfoKHR, PhysicalDeviceExternalSemaphoreInfo,
PhysicalDeviceFeatures2, PhysicalDeviceImageDrmFormatModifierInfoEXT, PhysicalDeviceFeatures2, PhysicalDeviceImageDrmFormatModifierInfoEXT,
PhysicalDeviceImageFormatInfo2, PhysicalDeviceProperties2, PhysicalDeviceImageFormatInfo2, PhysicalDeviceProperties2,
PhysicalDeviceSynchronization2Features, PhysicalDeviceTimelineSemaphoreFeatures, PhysicalDeviceSynchronization2Features, PhysicalDeviceTimelineSemaphoreFeatures,
PipelineStageFlags2, QUEUE_FAMILY_FOREIGN_EXT, Queue, QueueFlags, SampleCountFlags, Queue, QueueFlags, SampleCountFlags, SemaphoreCreateInfo, SemaphoreImportFlags,
SemaphoreCreateInfo, SemaphoreImportFlags, SemaphoreSubmitInfo, SharingMode, SharingMode, SubresourceLayout,
SubmitInfo2, SubresourceLayout, WHOLE_SIZE,
}, },
}, },
bstr::ByteSlice, bstr::ByteSlice,
@ -85,6 +77,7 @@ use {
vk::{Buffer, CommandPool, Image, Semaphore}, vk::{Buffer, CommandPool, Image, Semaphore},
}; };
mod execute;
mod queue_allocation; mod queue_allocation;
mod registry; mod registry;
@ -1275,429 +1268,6 @@ impl CopyDeviceInner {
} }
} }
impl CopyDeviceCopy {
fn ensure_not_busy(&self) -> Result<(), CopyDeviceError> {
let slf = &*self.inner;
if let Some(sync) = slf.busy.get()
&& sync.is_unsignaled()
{
return Err(CopyDeviceError::Busy);
}
slf.busy.take();
Ok(())
}
pub fn execute(
&self,
sync: Option<&FdSync>,
region: Option<&Region>,
) -> Result<Option<FdSync>, CopyDeviceError> {
self.ensure_not_busy()?;
let slf = &*self.inner;
let tt = slf.tt;
let dev = &slf.dev.dev;
let cmd = slf.command_buffer;
let queue_family = slf.dev.phy.queues[tt].family;
let region_buf;
let width = slf.width;
let height = slf.height;
let region = match region {
Some(r) => r,
_ => {
region_buf = Region::new(Rect::new_saturating(0, 0, width as i32, height as i32));
&region_buf
}
};
let (x_mask, y_mask) = slf.dev.phy.queues[tt].transfer_granularity_mask;
let rects = &mut *slf.dev.phy.rects.borrow_mut();
rects.clear();
for rect in region.iter() {
let x1 = (rect.x1().max(0) as u32 & !x_mask).min(width);
let y1 = (rect.y1().max(0) as u32 & !y_mask).min(height);
let x2 = ((rect.x2().max(0) as u32 + x_mask) & !x_mask).min(width);
let y2 = ((rect.y2().max(0) as u32 + y_mask) & !y_mask).min(height);
let width = x2 - x1;
let height = y2 - y1;
if width == 0 || height == 0 {
continue;
}
rects.push((x1 as i32, y1 as i32, width, height));
}
if rects.is_empty() {
return Ok(None);
}
let begin_info =
CommandBufferBeginInfo::default().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT);
unsafe {
dev.begin_command_buffer(cmd, &begin_info)
.map_err(CopyDeviceError::BeginCommandBuffer)?;
}
macro_rules! initial_buffer_barriers {
($($buf:expr, $access:expr;)*) => {
[$(
BufferMemoryBarrier2::default()
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
.dst_access_mask($access)
.src_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
.dst_queue_family_index(queue_family)
.buffer($buf.buf)
.size(WHOLE_SIZE),
)*]
};
}
macro_rules! final_buffer_barriers {
($($buf:expr, $access:expr;)*) => {
[$(
BufferMemoryBarrier2::default()
.src_stage_mask(PipelineStageFlags2::TRANSFER)
.src_access_mask($access)
.src_queue_family_index(queue_family)
.dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
.buffer($buf.buf)
.size(WHOLE_SIZE),
)*]
};
}
let image_subresource_range = ImageSubresourceRange {
aspect_mask: ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: 1,
base_array_layer: 0,
layer_count: 1,
};
let image_subresource = ImageSubresourceLayers {
aspect_mask: ImageAspectFlags::COLOR,
mip_level: 0,
base_array_layer: 0,
layer_count: 1,
};
macro_rules! initial_image_barriers {
($($img:expr, $layout:expr, $access:expr;)*) => {
[$(
ImageMemoryBarrier2::default()
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
.dst_access_mask($access)
.old_layout(ImageLayout::GENERAL)
.new_layout(ImageLayout::GENERAL)
.src_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
.dst_queue_family_index(queue_family)
.image($img.img)
.subresource_range(image_subresource_range),
ImageMemoryBarrier2::default()
.src_stage_mask(PipelineStageFlags2::TRANSFER)
.src_access_mask($access)
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
.dst_access_mask($access)
.old_layout(ImageLayout::GENERAL)
.new_layout($layout)
.src_queue_family_index(queue_family)
.dst_queue_family_index(queue_family)
.image($img.img)
.subresource_range(image_subresource_range),
)*]
};
}
macro_rules! final_image_barriers {
($($img:expr, $layout:expr, $access:expr;)*) => {
[$(
ImageMemoryBarrier2::default()
.src_stage_mask(PipelineStageFlags2::TRANSFER)
.src_access_mask($access)
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
.dst_access_mask($access)
.old_layout($layout)
.new_layout(ImageLayout::GENERAL)
.src_queue_family_index(queue_family)
.dst_queue_family_index(queue_family)
.image($img.img)
.subresource_range(image_subresource_range),
ImageMemoryBarrier2::default()
.src_stage_mask(PipelineStageFlags2::TRANSFER)
.src_access_mask($access)
.old_layout(ImageLayout::GENERAL)
.new_layout(ImageLayout::GENERAL)
.src_queue_family_index(queue_family)
.dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
.image($img.img)
.subresource_range(image_subresource_range),
)*]
};
}
match &slf.ty {
CopyDeviceCopyType::BufferToBuffer {
src,
dst,
stride,
bpp,
} => {
let regions = &mut *slf.dev.phy.buffer_copy_2.borrow_mut();
regions.clear();
let stride = *stride as u64;
let bpp = *bpp as u64;
for &mut (x, y, width, height) in rects {
let lo = y as u64 * stride + x as u64 * bpp;
let size = (height as u64 - 1) * stride + width as u64 * bpp;
let region = BufferCopy2::default()
.src_offset(lo)
.dst_offset(lo)
.size(size);
regions.push(region);
}
use AccessFlags2 as A;
let initial_barriers = initial_buffer_barriers![
src, A::TRANSFER_READ;
dst, A::TRANSFER_WRITE;
];
let final_barriers = final_buffer_barriers![
src, A::TRANSFER_READ;
dst, A::TRANSFER_WRITE;
];
let initial_dependency_info =
DependencyInfo::default().buffer_memory_barriers(&initial_barriers);
let final_dependency_info =
DependencyInfo::default().buffer_memory_barriers(&final_barriers);
let copy_buffer_info = CopyBufferInfo2::default()
.src_buffer(src.buf)
.dst_buffer(dst.buf)
.regions(regions);
unsafe {
dev.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
dev.cmd_copy_buffer2(cmd, &copy_buffer_info);
dev.cmd_pipeline_barrier2(cmd, &final_dependency_info);
}
}
CopyDeviceCopyType::BufferToImage {
buf,
buf_format,
buf_stride,
img,
}
| CopyDeviceCopyType::ImageToBuffer {
img,
buf,
buf_format,
buf_stride,
} => {
let regions = &mut *slf.dev.phy.buffer_image_copy_2.borrow_mut();
regions.clear();
for &mut (x, y, width, height) in rects {
let offset = y as u64 * *buf_stride as u64 + x as u64 * buf_format.bpp as u64;
let region = BufferImageCopy2::default()
.buffer_offset(offset)
.buffer_row_length(*buf_stride / buf_format.bpp)
.buffer_image_height(slf.height)
.image_subresource(image_subresource)
.image_offset(Offset3D { x, y, z: 0 })
.image_extent(Extent3D {
width,
height,
depth: 1,
});
regions.push(region);
}
let buffer_to_image = match &slf.ty {
CopyDeviceCopyType::BufferToImage { .. } => true,
CopyDeviceCopyType::ImageToBuffer { .. } => false,
_ => unreachable!(),
};
let image_access_mask;
let image_layout;
let buffer_access_mask;
match buffer_to_image {
true => {
image_access_mask = AccessFlags2::TRANSFER_WRITE;
image_layout = ImageLayout::TRANSFER_DST_OPTIMAL;
buffer_access_mask = AccessFlags2::TRANSFER_READ;
}
false => {
image_access_mask = AccessFlags2::TRANSFER_READ;
image_layout = ImageLayout::TRANSFER_SRC_OPTIMAL;
buffer_access_mask = AccessFlags2::TRANSFER_WRITE;
}
}
let initial_image_barriers = initial_image_barriers![
img, image_layout, image_access_mask;
];
let final_image_barriers = final_image_barriers![
img, image_layout, image_access_mask;
];
let initial_buffer_barriers = initial_buffer_barriers![
buf, buffer_access_mask;
];
let final_buffer_barriers = final_buffer_barriers![
buf, buffer_access_mask;
];
let initial_dependency_info = DependencyInfo::default()
.buffer_memory_barriers(&initial_buffer_barriers)
.image_memory_barriers(&initial_image_barriers);
let final_dependency_info = DependencyInfo::default()
.buffer_memory_barriers(&final_buffer_barriers)
.image_memory_barriers(&final_image_barriers);
unsafe {
dev.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
match buffer_to_image {
true => {
let copy = CopyBufferToImageInfo2::default()
.src_buffer(buf.buf)
.dst_image(img.img)
.dst_image_layout(image_layout)
.regions(&regions);
dev.cmd_copy_buffer_to_image2(cmd, &copy);
}
false => {
let copy = CopyImageToBufferInfo2::default()
.src_image(img.img)
.src_image_layout(image_layout)
.dst_buffer(buf.buf)
.regions(&regions);
dev.cmd_copy_image_to_buffer2(cmd, &copy);
}
}
dev.cmd_pipeline_barrier2(cmd, &final_dependency_info);
}
}
CopyDeviceCopyType::ImageToImage { src, dst } => {
let regions = &mut *slf.dev.phy.image_copy_2.borrow_mut();
regions.clear();
for &mut (x, y, width, height) in rects {
let region = ImageCopy2::default()
.src_subresource(image_subresource)
.src_offset(Offset3D { x, y, z: 0 })
.dst_subresource(image_subresource)
.dst_offset(Offset3D { x, y, z: 0 })
.extent(Extent3D {
width,
height,
depth: 1,
});
regions.push(region);
}
use {AccessFlags2 as A, ImageLayout as L};
let initial_barriers = initial_image_barriers![
src, L::TRANSFER_SRC_OPTIMAL, A::TRANSFER_READ;
dst, L::TRANSFER_DST_OPTIMAL, A::TRANSFER_WRITE;
];
let final_barriers = final_image_barriers![
src, L::TRANSFER_SRC_OPTIMAL, A::TRANSFER_READ;
dst, L::TRANSFER_DST_OPTIMAL, A::TRANSFER_WRITE;
];
let initial_dependency_info =
DependencyInfo::default().image_memory_barriers(&initial_barriers);
let final_dependency_info =
DependencyInfo::default().image_memory_barriers(&final_barriers);
let copy_image_info = CopyImageInfo2::default()
.src_image(src.img)
.src_image_layout(L::TRANSFER_SRC_OPTIMAL)
.dst_image(dst.img)
.dst_image_layout(L::TRANSFER_DST_OPTIMAL)
.regions(regions);
unsafe {
dev.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
dev.cmd_copy_image2(cmd, &copy_image_info);
dev.cmd_pipeline_barrier2(cmd, &final_dependency_info);
}
}
CopyDeviceCopyType::Blit { src, dst } => {
let regions = &mut *slf.dev.phy.image_blit_2.borrow_mut();
regions.clear();
for &mut (x, y, width, height) in rects {
let x1 = x;
let y1 = y;
let x2 = x1 + width as i32;
let y2 = y1 + height as i32;
let offsets = [
Offset3D { x: x1, y: y1, z: 0 },
Offset3D { x: x2, y: y2, z: 1 },
];
let region = ImageBlit2::default()
.src_subresource(image_subresource)
.src_offsets(offsets)
.dst_subresource(image_subresource)
.dst_offsets(offsets);
regions.push(region);
}
use {AccessFlags2 as A, ImageLayout as L};
let initial_barriers = initial_image_barriers![
src, L::TRANSFER_SRC_OPTIMAL, A::TRANSFER_READ;
dst, L::TRANSFER_DST_OPTIMAL, A::TRANSFER_WRITE;
];
let final_barriers = final_image_barriers![
src, L::TRANSFER_SRC_OPTIMAL, A::TRANSFER_READ;
dst, L::TRANSFER_DST_OPTIMAL, A::TRANSFER_WRITE;
];
let initial_dependency_info =
DependencyInfo::default().image_memory_barriers(&initial_barriers);
let final_dependency_info =
DependencyInfo::default().image_memory_barriers(&final_barriers);
let blit_image_info = BlitImageInfo2::default()
.src_image(src.img)
.src_image_layout(L::TRANSFER_SRC_OPTIMAL)
.dst_image(dst.img)
.dst_image_layout(L::TRANSFER_DST_OPTIMAL)
.regions(regions)
.filter(Filter::NEAREST);
unsafe {
dev.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
dev.cmd_blit_image2(cmd, &blit_image_info);
dev.cmd_pipeline_barrier2(cmd, &final_dependency_info);
}
}
};
unsafe {
dev.end_command_buffer(cmd)
.map_err(CopyDeviceError::EndCommandBuffer)?;
}
let mut wait_semaphore = None;
let mut wait_semaphores = ArrayVec::<_, 1>::new();
if let Some(sync) = sync
&& let Some(sync_file) = sync.get_sync_file()
{
let semaphore = match slf.dev.semaphores.pop() {
Some(s) => s,
_ => slf.dev.create_semaphore()?,
};
semaphore.import(sync_file)?;
let info = SemaphoreSubmitInfo::default()
.semaphore(semaphore.semaphore)
.stage_mask(PipelineStageFlags2::TRANSFER);
wait_semaphores.push(info);
wait_semaphore = Some(semaphore);
}
let command_buffer_info = CommandBufferSubmitInfo::default().command_buffer(cmd);
let mut semaphore_submit_info = SemaphoreSubmitInfo::default();
let mut submit_info = SubmitInfo2::default()
.command_buffer_infos(slice::from_ref(&command_buffer_info))
.wait_semaphore_infos(&wait_semaphores);
let vulkan_sync = slf.dev.create_sync(
self.dev.timeline_semaphore.as_ref(),
&mut semaphore_submit_info,
&mut submit_info,
)?;
unsafe {
slf.dev
.dev
.queue_submit2(
slf.dev.queues[tt],
slice::from_ref(&submit_info),
vulkan_sync.fence(),
)
.map_err(CopyDeviceError::SubmitCopy)?;
}
let sync = vulkan_sync.to_sync(|| slf.dev.wait_idle());
slf.busy.set(sync.clone());
let pending = Pending {
dev: slf.dev.clone(),
busy_id: slf.busy_id.add_fetch(1),
sync: sync.clone(),
copy: self.inner.clone(),
semaphore: wait_semaphore,
vulkan_sync,
};
slf.dev.submissions[tt].pending.push(pending);
Ok(sync)
}
}
impl VulkanSemaphore { impl VulkanSemaphore {
fn import(&self, sync_file: &OwnedFd) -> Result<(), CopyDeviceError> { fn import(&self, sync_file: &OwnedFd) -> Result<(), CopyDeviceError> {
let fd = uapi::fcntl_dupfd_cloexec(sync_file.raw(), 0) let fd = uapi::fcntl_dupfd_cloexec(sync_file.raw(), 0)

442
src/copy_device/execute.rs Normal file
View file

@ -0,0 +1,442 @@
use {
super::{CopyDeviceCopy, CopyDeviceCopyType, CopyDeviceError, Pending},
crate::{
gfx_api::FdSync,
rect::{Rect, Region},
vulkan_core::sync::VulkanDeviceSyncExt,
},
arrayvec::ArrayVec,
ash::vk::{
AccessFlags2, BlitImageInfo2, BufferCopy2, BufferImageCopy2, BufferMemoryBarrier2,
CommandBufferBeginInfo, CommandBufferSubmitInfo, CommandBufferUsageFlags,
CopyBufferInfo2, CopyBufferToImageInfo2, CopyImageInfo2, CopyImageToBufferInfo2,
DependencyInfo, Extent3D, Filter, ImageAspectFlags, ImageBlit2, ImageCopy2, ImageLayout,
ImageMemoryBarrier2, ImageSubresourceLayers, ImageSubresourceRange, Offset3D,
PipelineStageFlags2, QUEUE_FAMILY_FOREIGN_EXT, SemaphoreSubmitInfo, SubmitInfo2,
WHOLE_SIZE,
},
std::slice,
};
impl CopyDeviceCopy {
fn ensure_not_busy(&self) -> Result<(), CopyDeviceError> {
let slf = &*self.inner;
if let Some(sync) = slf.busy.get()
&& sync.is_unsignaled()
{
return Err(CopyDeviceError::Busy);
}
slf.busy.take();
Ok(())
}
pub fn execute(
&self,
sync: Option<&FdSync>,
region: Option<&Region>,
) -> Result<Option<FdSync>, CopyDeviceError> {
self.ensure_not_busy()?;
let slf = &*self.inner;
let tt = slf.tt;
let dev = &slf.dev.dev;
let cmd = slf.command_buffer;
let queue_family = slf.dev.phy.queues[tt].family;
let region_buf;
let width = slf.width;
let height = slf.height;
let region = match region {
Some(r) => r,
_ => {
region_buf = Region::new(Rect::new_saturating(0, 0, width as i32, height as i32));
&region_buf
}
};
let (x_mask, y_mask) = slf.dev.phy.queues[tt].transfer_granularity_mask;
let rects = &mut *slf.dev.phy.rects.borrow_mut();
rects.clear();
for rect in region.iter() {
let x1 = (rect.x1().max(0) as u32 & !x_mask).min(width);
let y1 = (rect.y1().max(0) as u32 & !y_mask).min(height);
let x2 = ((rect.x2().max(0) as u32 + x_mask) & !x_mask).min(width);
let y2 = ((rect.y2().max(0) as u32 + y_mask) & !y_mask).min(height);
let width = x2 - x1;
let height = y2 - y1;
if width == 0 || height == 0 {
continue;
}
rects.push((x1 as i32, y1 as i32, width, height));
}
if rects.is_empty() {
return Ok(None);
}
let begin_info =
CommandBufferBeginInfo::default().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT);
unsafe {
dev.begin_command_buffer(cmd, &begin_info)
.map_err(CopyDeviceError::BeginCommandBuffer)?;
}
macro_rules! initial_buffer_barriers {
($($buf:expr, $access:expr;)*) => {
[$(
BufferMemoryBarrier2::default()
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
.dst_access_mask($access)
.src_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
.dst_queue_family_index(queue_family)
.buffer($buf.buf)
.size(WHOLE_SIZE),
)*]
};
}
macro_rules! final_buffer_barriers {
($($buf:expr, $access:expr;)*) => {
[$(
BufferMemoryBarrier2::default()
.src_stage_mask(PipelineStageFlags2::TRANSFER)
.src_access_mask($access)
.src_queue_family_index(queue_family)
.dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
.buffer($buf.buf)
.size(WHOLE_SIZE),
)*]
};
}
let image_subresource_range = ImageSubresourceRange {
aspect_mask: ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: 1,
base_array_layer: 0,
layer_count: 1,
};
let image_subresource = ImageSubresourceLayers {
aspect_mask: ImageAspectFlags::COLOR,
mip_level: 0,
base_array_layer: 0,
layer_count: 1,
};
macro_rules! initial_image_barriers {
($($img:expr, $layout:expr, $access:expr;)*) => {
[$(
ImageMemoryBarrier2::default()
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
.dst_access_mask($access)
.old_layout(ImageLayout::GENERAL)
.new_layout(ImageLayout::GENERAL)
.src_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
.dst_queue_family_index(queue_family)
.image($img.img)
.subresource_range(image_subresource_range),
ImageMemoryBarrier2::default()
.src_stage_mask(PipelineStageFlags2::TRANSFER)
.src_access_mask($access)
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
.dst_access_mask($access)
.old_layout(ImageLayout::GENERAL)
.new_layout($layout)
.src_queue_family_index(queue_family)
.dst_queue_family_index(queue_family)
.image($img.img)
.subresource_range(image_subresource_range),
)*]
};
}
macro_rules! final_image_barriers {
($($img:expr, $layout:expr, $access:expr;)*) => {
[$(
ImageMemoryBarrier2::default()
.src_stage_mask(PipelineStageFlags2::TRANSFER)
.src_access_mask($access)
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
.dst_access_mask($access)
.old_layout($layout)
.new_layout(ImageLayout::GENERAL)
.src_queue_family_index(queue_family)
.dst_queue_family_index(queue_family)
.image($img.img)
.subresource_range(image_subresource_range),
ImageMemoryBarrier2::default()
.src_stage_mask(PipelineStageFlags2::TRANSFER)
.src_access_mask($access)
.old_layout(ImageLayout::GENERAL)
.new_layout(ImageLayout::GENERAL)
.src_queue_family_index(queue_family)
.dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
.image($img.img)
.subresource_range(image_subresource_range),
)*]
};
}
match &slf.ty {
CopyDeviceCopyType::BufferToBuffer {
src,
dst,
stride,
bpp,
} => {
let regions = &mut *slf.dev.phy.buffer_copy_2.borrow_mut();
regions.clear();
let stride = *stride as u64;
let bpp = *bpp as u64;
for &mut (x, y, width, height) in rects {
let lo = y as u64 * stride + x as u64 * bpp;
let size = (height as u64 - 1) * stride + width as u64 * bpp;
let region = BufferCopy2::default()
.src_offset(lo)
.dst_offset(lo)
.size(size);
regions.push(region);
}
use AccessFlags2 as A;
let initial_barriers = initial_buffer_barriers![
src, A::TRANSFER_READ;
dst, A::TRANSFER_WRITE;
];
let final_barriers = final_buffer_barriers![
src, A::TRANSFER_READ;
dst, A::TRANSFER_WRITE;
];
let initial_dependency_info =
DependencyInfo::default().buffer_memory_barriers(&initial_barriers);
let final_dependency_info =
DependencyInfo::default().buffer_memory_barriers(&final_barriers);
let copy_buffer_info = CopyBufferInfo2::default()
.src_buffer(src.buf)
.dst_buffer(dst.buf)
.regions(regions);
unsafe {
dev.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
dev.cmd_copy_buffer2(cmd, &copy_buffer_info);
dev.cmd_pipeline_barrier2(cmd, &final_dependency_info);
}
}
CopyDeviceCopyType::BufferToImage {
buf,
buf_format,
buf_stride,
img,
}
| CopyDeviceCopyType::ImageToBuffer {
img,
buf,
buf_format,
buf_stride,
} => {
let regions = &mut *slf.dev.phy.buffer_image_copy_2.borrow_mut();
regions.clear();
for &mut (x, y, width, height) in rects {
let offset = y as u64 * *buf_stride as u64 + x as u64 * buf_format.bpp as u64;
let region = BufferImageCopy2::default()
.buffer_offset(offset)
.buffer_row_length(*buf_stride / buf_format.bpp)
.buffer_image_height(slf.height)
.image_subresource(image_subresource)
.image_offset(Offset3D { x, y, z: 0 })
.image_extent(Extent3D {
width,
height,
depth: 1,
});
regions.push(region);
}
let buffer_to_image = match &slf.ty {
CopyDeviceCopyType::BufferToImage { .. } => true,
CopyDeviceCopyType::ImageToBuffer { .. } => false,
_ => unreachable!(),
};
let image_access_mask;
let image_layout;
let buffer_access_mask;
match buffer_to_image {
true => {
image_access_mask = AccessFlags2::TRANSFER_WRITE;
image_layout = ImageLayout::TRANSFER_DST_OPTIMAL;
buffer_access_mask = AccessFlags2::TRANSFER_READ;
}
false => {
image_access_mask = AccessFlags2::TRANSFER_READ;
image_layout = ImageLayout::TRANSFER_SRC_OPTIMAL;
buffer_access_mask = AccessFlags2::TRANSFER_WRITE;
}
}
let initial_image_barriers = initial_image_barriers![
img, image_layout, image_access_mask;
];
let final_image_barriers = final_image_barriers![
img, image_layout, image_access_mask;
];
let initial_buffer_barriers = initial_buffer_barriers![
buf, buffer_access_mask;
];
let final_buffer_barriers = final_buffer_barriers![
buf, buffer_access_mask;
];
let initial_dependency_info = DependencyInfo::default()
.buffer_memory_barriers(&initial_buffer_barriers)
.image_memory_barriers(&initial_image_barriers);
let final_dependency_info = DependencyInfo::default()
.buffer_memory_barriers(&final_buffer_barriers)
.image_memory_barriers(&final_image_barriers);
unsafe {
dev.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
match buffer_to_image {
true => {
let copy = CopyBufferToImageInfo2::default()
.src_buffer(buf.buf)
.dst_image(img.img)
.dst_image_layout(image_layout)
.regions(&regions);
dev.cmd_copy_buffer_to_image2(cmd, &copy);
}
false => {
let copy = CopyImageToBufferInfo2::default()
.src_image(img.img)
.src_image_layout(image_layout)
.dst_buffer(buf.buf)
.regions(&regions);
dev.cmd_copy_image_to_buffer2(cmd, &copy);
}
}
dev.cmd_pipeline_barrier2(cmd, &final_dependency_info);
}
}
CopyDeviceCopyType::ImageToImage { src, dst } => {
let regions = &mut *slf.dev.phy.image_copy_2.borrow_mut();
regions.clear();
for &mut (x, y, width, height) in rects {
let region = ImageCopy2::default()
.src_subresource(image_subresource)
.src_offset(Offset3D { x, y, z: 0 })
.dst_subresource(image_subresource)
.dst_offset(Offset3D { x, y, z: 0 })
.extent(Extent3D {
width,
height,
depth: 1,
});
regions.push(region);
}
use {AccessFlags2 as A, ImageLayout as L};
let initial_barriers = initial_image_barriers![
src, L::TRANSFER_SRC_OPTIMAL, A::TRANSFER_READ;
dst, L::TRANSFER_DST_OPTIMAL, A::TRANSFER_WRITE;
];
let final_barriers = final_image_barriers![
src, L::TRANSFER_SRC_OPTIMAL, A::TRANSFER_READ;
dst, L::TRANSFER_DST_OPTIMAL, A::TRANSFER_WRITE;
];
let initial_dependency_info =
DependencyInfo::default().image_memory_barriers(&initial_barriers);
let final_dependency_info =
DependencyInfo::default().image_memory_barriers(&final_barriers);
let copy_image_info = CopyImageInfo2::default()
.src_image(src.img)
.src_image_layout(L::TRANSFER_SRC_OPTIMAL)
.dst_image(dst.img)
.dst_image_layout(L::TRANSFER_DST_OPTIMAL)
.regions(regions);
unsafe {
dev.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
dev.cmd_copy_image2(cmd, &copy_image_info);
dev.cmd_pipeline_barrier2(cmd, &final_dependency_info);
}
}
CopyDeviceCopyType::Blit { src, dst } => {
let regions = &mut *slf.dev.phy.image_blit_2.borrow_mut();
regions.clear();
for &mut (x, y, width, height) in rects {
let x1 = x;
let y1 = y;
let x2 = x1 + width as i32;
let y2 = y1 + height as i32;
let offsets = [
Offset3D { x: x1, y: y1, z: 0 },
Offset3D { x: x2, y: y2, z: 1 },
];
let region = ImageBlit2::default()
.src_subresource(image_subresource)
.src_offsets(offsets)
.dst_subresource(image_subresource)
.dst_offsets(offsets);
regions.push(region);
}
use {AccessFlags2 as A, ImageLayout as L};
let initial_barriers = initial_image_barriers![
src, L::TRANSFER_SRC_OPTIMAL, A::TRANSFER_READ;
dst, L::TRANSFER_DST_OPTIMAL, A::TRANSFER_WRITE;
];
let final_barriers = final_image_barriers![
src, L::TRANSFER_SRC_OPTIMAL, A::TRANSFER_READ;
dst, L::TRANSFER_DST_OPTIMAL, A::TRANSFER_WRITE;
];
let initial_dependency_info =
DependencyInfo::default().image_memory_barriers(&initial_barriers);
let final_dependency_info =
DependencyInfo::default().image_memory_barriers(&final_barriers);
let blit_image_info = BlitImageInfo2::default()
.src_image(src.img)
.src_image_layout(L::TRANSFER_SRC_OPTIMAL)
.dst_image(dst.img)
.dst_image_layout(L::TRANSFER_DST_OPTIMAL)
.regions(regions)
.filter(Filter::NEAREST);
unsafe {
dev.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
dev.cmd_blit_image2(cmd, &blit_image_info);
dev.cmd_pipeline_barrier2(cmd, &final_dependency_info);
}
}
};
unsafe {
dev.end_command_buffer(cmd)
.map_err(CopyDeviceError::EndCommandBuffer)?;
}
let mut wait_semaphore = None;
let mut wait_semaphores = ArrayVec::<_, 1>::new();
if let Some(sync) = sync
&& let Some(sync_file) = sync.get_sync_file()
{
let semaphore = match slf.dev.semaphores.pop() {
Some(s) => s,
_ => slf.dev.create_semaphore()?,
};
semaphore.import(sync_file)?;
let info = SemaphoreSubmitInfo::default()
.semaphore(semaphore.semaphore)
.stage_mask(PipelineStageFlags2::TRANSFER);
wait_semaphores.push(info);
wait_semaphore = Some(semaphore);
}
let command_buffer_info = CommandBufferSubmitInfo::default().command_buffer(cmd);
let mut semaphore_submit_info = SemaphoreSubmitInfo::default();
let mut submit_info = SubmitInfo2::default()
.command_buffer_infos(slice::from_ref(&command_buffer_info))
.wait_semaphore_infos(&wait_semaphores);
let vulkan_sync = slf.dev.create_sync(
self.dev.timeline_semaphore.as_ref(),
&mut semaphore_submit_info,
&mut submit_info,
)?;
unsafe {
slf.dev
.dev
.queue_submit2(
slf.dev.queues[tt],
slice::from_ref(&submit_info),
vulkan_sync.fence(),
)
.map_err(CopyDeviceError::SubmitCopy)?;
}
let sync = vulkan_sync.to_sync(|| slf.dev.wait_idle());
slf.busy.set(sync.clone());
let pending = Pending {
dev: slf.dev.clone(),
busy_id: slf.busy_id.add_fetch(1),
sync: sync.clone(),
copy: self.inner.clone(),
semaphore: wait_semaphore,
vulkan_sync,
};
slf.dev.submissions[tt].pending.push(pending);
Ok(sync)
}
}

View file

@ -1,6 +1,7 @@
mod color; mod color;
mod op; mod op;
mod pipeline_cache; mod pipeline_cache;
mod pipelines;
mod paint_region; mod paint_region;
use { use {
@ -10,7 +11,7 @@ use {
VulkanRoundedTexOp, VulkanTexOp, VulkanRoundedTexOp, VulkanTexOp,
}, },
paint_region::{PaintRegion, Point, constrain_to_fb}, paint_region::{PaintRegion, Point, constrain_to_fb},
pipeline_cache::{FillPipelines, OutPipelineKey, TexPipelineKey, TexPipelines}, pipeline_cache::{FillPipelines, OutPipelineKey, TexPipelines},
crate::{ crate::{
async_engine::{AsyncEngine, SpawnedFuture}, async_engine::{AsyncEngine, SpawnedFuture},
cmm::{ cmm::{
@ -19,8 +20,8 @@ use {
}, },
cpu_worker::PendingJob, cpu_worker::PendingJob,
gfx_api::{ gfx_api::{
AcquireSync, AlphaMode, BufferResv, BufferResvUser, FdSync, GfxApiOpt, GfxBlendBuffer, AcquireSync, BufferResv, BufferResvUser, FdSync, GfxApiOpt, GfxBlendBuffer, GfxFormat,
GfxFormat, GfxTexture, GfxWriteModifier, ReleaseSync, GfxTexture, GfxWriteModifier, ReleaseSync,
}, },
gfx_apis::vulkan::{ gfx_apis::vulkan::{
VulkanError, VulkanSync, VulkanTimelineSemaphore, VulkanError, VulkanSync, VulkanTimelineSemaphore,
@ -30,9 +31,9 @@ use {
descriptor::VulkanDescriptorSetLayout, descriptor::VulkanDescriptorSetLayout,
descriptor_buffer::VulkanDescriptorBufferWriter, descriptor_buffer::VulkanDescriptorBufferWriter,
device::VulkanDevice, device::VulkanDevice,
eotfs::{EOTF_LINEAR, EotfExt, VulkanEotf}, eotfs::VulkanEotf,
image::{QueueFamily, QueueState, QueueTransfer, VulkanImage, VulkanImageMemory}, image::{QueueFamily, QueueState, QueueTransfer, VulkanImage, VulkanImageMemory},
pipeline::{PipelineCreateInfo, VulkanPipeline}, pipeline::VulkanPipeline,
sampler::VulkanSampler, sampler::VulkanSampler,
semaphore::VulkanSemaphore, semaphore::VulkanSemaphore,
shaders::{ shaders::{
@ -78,7 +79,7 @@ use {
}, },
}, },
isnt::std_1::{collections::IsntHashMapExt, primitive::IsntSliceExt}, isnt::std_1::{collections::IsntHashMapExt, primitive::IsntSliceExt},
linearize::{Linearize, LinearizeExt, StaticMap, static_map}, linearize::{Linearize, LinearizeExt, StaticMap},
std::{ std::{
any::Any, any::Any,
borrow::Cow, borrow::Cow,
@ -378,261 +379,6 @@ impl VulkanDevice {
} }
impl VulkanRenderer { impl VulkanRenderer {
fn get_or_create_fill_pipelines(
&self,
format: vk::Format,
) -> Result<FillPipelines, VulkanError> {
if let Some(pl) = self.fill_pipelines.get(&format) {
return Ok(pl);
}
let create_fill_pipeline = |src_has_alpha| {
let push_size = if self.device.descriptor_buffer.is_some() {
size_of::<FillPushConstants>()
} else {
size_of::<LegacyFillPushConstants>()
};
let info = PipelineCreateInfo {
format,
vert: self.fill_vert_shader.clone(),
frag: self.fill_frag_shader.clone(),
blend: src_has_alpha,
src_has_alpha,
has_alpha_mult: false,
alpha_mode: AlphaMode::PremultipliedOptical,
// all transformations are applied in the compositor
eotf: EOTF_LINEAR,
inv_eotf: EOTF_LINEAR,
descriptor_set_layouts: Default::default(),
has_color_management_data: false,
};
self.device.create_pipeline2(info, push_size)
};
let fill_pipelines = Rc::new(static_map! {
TexSourceType::HasAlpha => create_fill_pipeline(true)?,
TexSourceType::Opaque => create_fill_pipeline(false)?,
});
self.fill_pipelines.set(format, fill_pipelines.clone());
Ok(fill_pipelines)
}
fn get_or_create_rounded_fill_pipelines(
&self,
format: vk::Format,
) -> Result<FillPipelines, VulkanError> {
if let Some(pl) = self.rounded_fill_pipelines.get(&format) {
return Ok(pl);
}
let create_pipeline = |src_has_alpha| {
let push_size = if self.device.descriptor_buffer.is_some() {
size_of::<RoundedFillPushConstants>()
} else {
size_of::<LegacyRoundedFillPushConstants>()
};
let info = PipelineCreateInfo {
format,
vert: self.rounded_fill_vert_shader.clone(),
frag: self.rounded_fill_frag_shader.clone(),
blend: src_has_alpha,
src_has_alpha,
has_alpha_mult: false,
alpha_mode: AlphaMode::PremultipliedOptical,
eotf: EOTF_LINEAR,
inv_eotf: EOTF_LINEAR,
descriptor_set_layouts: Default::default(),
has_color_management_data: false,
};
self.device.create_pipeline2(info, push_size)
};
let pipelines = Rc::new(static_map! {
TexSourceType::HasAlpha => create_pipeline(true)?,
TexSourceType::Opaque => create_pipeline(false)?,
});
self.rounded_fill_pipelines.set(format, pipelines.clone());
Ok(pipelines)
}
fn get_or_create_rounded_tex_pipelines(
&self,
format: vk::Format,
target_cd: &ColorDescription,
) -> Rc<TexPipelines> {
let eotf = target_cd.eotf.to_vulkan();
let pipelines = &self.rounded_tex_pipelines[eotf];
match pipelines.get(&format) {
Some(pl) => pl,
_ => {
let pl = Rc::new(TexPipelines {
format,
eotf,
pipelines: Default::default(),
});
pipelines.set(format, pl.clone());
pl
}
}
}
fn get_or_create_rounded_tex_pipeline(
&self,
pipelines: &TexPipelines,
tex_cd: &ColorDescription,
tex_copy_type: TexCopyType,
tex_source_type: TexSourceType,
mut tex_alpha_mode: AlphaMode,
has_color_management_data: bool,
) -> Result<Rc<VulkanPipeline>, VulkanError> {
if tex_source_type == TexSourceType::Opaque {
tex_alpha_mode = AlphaMode::PremultipliedElectrical;
}
let key = TexPipelineKey {
tex_copy_type,
tex_source_type,
tex_alpha_mode,
eotf: tex_cd.eotf.to_vulkan(),
has_color_management_data,
};
if let Some(pl) = pipelines.pipelines.get(&key) {
return Ok(pl);
}
let has_alpha_mult = match tex_copy_type {
TexCopyType::Identity => false,
TexCopyType::Multiply => true,
};
let push_size = if self.device.descriptor_buffer.is_some() {
size_of::<RoundedTexPushConstants>()
} else {
size_of::<LegacyRoundedTexPushConstants>()
};
let info = PipelineCreateInfo {
format: pipelines.format,
vert: self.rounded_tex_vert_shader.clone(),
frag: self.rounded_tex_frag_shader.clone(),
blend: true, // always blend since corners are transparent
src_has_alpha: true, // rounding makes everything have alpha
has_alpha_mult,
alpha_mode: key.tex_alpha_mode,
eotf: key.eotf.to_vulkan(),
inv_eotf: pipelines.eotf.to_vulkan(),
descriptor_set_layouts: self.tex_descriptor_set_layouts.clone(),
has_color_management_data,
};
let pl = self.device.create_pipeline2(info, push_size)?;
pipelines.pipelines.set(key, pl.clone());
Ok(pl)
}
fn get_or_create_tex_pipelines(
&self,
format: vk::Format,
target_cd: &ColorDescription,
) -> Rc<TexPipelines> {
let eotf = target_cd.eotf.to_vulkan();
let pipelines = &self.tex_pipelines[eotf];
match pipelines.get(&format) {
Some(pl) => pl,
_ => {
let pl = Rc::new(TexPipelines {
format,
eotf,
pipelines: Default::default(),
});
pipelines.set(format, pl.clone());
pl
}
}
}
fn get_or_create_tex_pipeline(
&self,
pipelines: &TexPipelines,
tex_cd: &ColorDescription,
tex_copy_type: TexCopyType,
tex_source_type: TexSourceType,
mut tex_alpha_mode: AlphaMode,
has_color_management_data: bool,
) -> Result<Rc<VulkanPipeline>, VulkanError> {
if tex_source_type == TexSourceType::Opaque {
tex_alpha_mode = AlphaMode::PremultipliedElectrical;
}
let key = TexPipelineKey {
tex_copy_type,
tex_source_type,
tex_alpha_mode,
eotf: tex_cd.eotf.to_vulkan(),
has_color_management_data,
};
if let Some(pl) = pipelines.pipelines.get(&key) {
return Ok(pl);
}
let src_has_alpha = match tex_source_type {
TexSourceType::Opaque => false,
TexSourceType::HasAlpha => true,
};
let has_alpha_mult = match tex_copy_type {
TexCopyType::Identity => false,
TexCopyType::Multiply => true,
};
let push_size = if self.device.descriptor_buffer.is_some() {
size_of::<TexPushConstants>()
} else {
size_of::<LegacyTexPushConstants>()
};
let info = PipelineCreateInfo {
format: pipelines.format,
vert: self.tex_vert_shader.clone(),
frag: self.tex_frag_shader.clone(),
blend: src_has_alpha || has_alpha_mult,
src_has_alpha,
has_alpha_mult,
alpha_mode: key.tex_alpha_mode,
eotf: key.eotf.to_vulkan(),
inv_eotf: pipelines.eotf.to_vulkan(),
descriptor_set_layouts: self.tex_descriptor_set_layouts.clone(),
has_color_management_data,
};
let pl = self.device.create_pipeline2(info, push_size)?;
pipelines.pipelines.set(key, pl.clone());
Ok(pl)
}
fn get_or_create_out_pipeline(
&self,
format: vk::Format,
bb_cd: &ColorDescription,
fb_cd: &ColorDescription,
has_color_management_data: bool,
) -> Result<Rc<VulkanPipeline>, VulkanError> {
let key = OutPipelineKey {
format,
eotf: bb_cd.eotf.to_vulkan(),
has_color_management_data,
};
let fb_eotf = fb_cd.eotf.to_vulkan();
let pipelines = &self.out_pipelines[fb_eotf];
if let Some(pl) = pipelines.get(&key) {
return Ok(pl);
}
let mut descriptor_set_layouts = ArrayVec::new();
descriptor_set_layouts.push(self.out_descriptor_set_layout.clone().unwrap());
let out = self
.device
.create_pipeline::<OutPushConstants>(PipelineCreateInfo {
format: key.format,
vert: self.out_vert_shader.clone().unwrap(),
frag: self.out_frag_shader.clone().unwrap(),
blend: false,
src_has_alpha: true,
has_alpha_mult: false,
alpha_mode: AlphaMode::PremultipliedElectrical,
eotf: key.eotf.to_vulkan(),
inv_eotf: fb_eotf.to_vulkan(),
descriptor_set_layouts,
has_color_management_data,
})?;
pipelines.set(key, out.clone());
Ok(out)
}
pub(super) fn allocate_point(&self) -> u64 { pub(super) fn allocate_point(&self) -> u64 {
self.last_point.fetch_add(1) + 1 self.last_point.fetch_add(1) + 1
} }

View file

@ -0,0 +1,282 @@
use {
super::{
VulkanRenderer,
op::{TexCopyType, TexSourceType},
pipeline_cache::{FillPipelines, OutPipelineKey, TexPipelineKey, TexPipelines},
},
crate::{
cmm::cmm_description::ColorDescription,
gfx_api::AlphaMode,
gfx_apis::vulkan::{
VulkanError,
eotfs::{EOTF_LINEAR, EotfExt},
pipeline::{PipelineCreateInfo, VulkanPipeline},
shaders::{
FillPushConstants, LegacyFillPushConstants, LegacyRoundedFillPushConstants,
LegacyRoundedTexPushConstants, LegacyTexPushConstants, OutPushConstants,
RoundedFillPushConstants, RoundedTexPushConstants, TexPushConstants,
},
},
},
arrayvec::ArrayVec,
ash::vk,
linearize::static_map,
std::{mem::size_of, rc::Rc},
};
impl VulkanRenderer {
pub(super) fn get_or_create_fill_pipelines(
&self,
format: vk::Format,
) -> Result<FillPipelines, VulkanError> {
if let Some(pl) = self.fill_pipelines.get(&format) {
return Ok(pl);
}
let create_fill_pipeline = |src_has_alpha| {
let push_size = if self.device.descriptor_buffer.is_some() {
size_of::<FillPushConstants>()
} else {
size_of::<LegacyFillPushConstants>()
};
let info = PipelineCreateInfo {
format,
vert: self.fill_vert_shader.clone(),
frag: self.fill_frag_shader.clone(),
blend: src_has_alpha,
src_has_alpha,
has_alpha_mult: false,
alpha_mode: AlphaMode::PremultipliedOptical,
// all transformations are applied in the compositor
eotf: EOTF_LINEAR,
inv_eotf: EOTF_LINEAR,
descriptor_set_layouts: Default::default(),
has_color_management_data: false,
};
self.device.create_pipeline2(info, push_size)
};
let fill_pipelines = Rc::new(static_map! {
TexSourceType::HasAlpha => create_fill_pipeline(true)?,
TexSourceType::Opaque => create_fill_pipeline(false)?,
});
self.fill_pipelines.set(format, fill_pipelines.clone());
Ok(fill_pipelines)
}
pub(super) fn get_or_create_rounded_fill_pipelines(
&self,
format: vk::Format,
) -> Result<FillPipelines, VulkanError> {
if let Some(pl) = self.rounded_fill_pipelines.get(&format) {
return Ok(pl);
}
let create_pipeline = |src_has_alpha| {
let push_size = if self.device.descriptor_buffer.is_some() {
size_of::<RoundedFillPushConstants>()
} else {
size_of::<LegacyRoundedFillPushConstants>()
};
let info = PipelineCreateInfo {
format,
vert: self.rounded_fill_vert_shader.clone(),
frag: self.rounded_fill_frag_shader.clone(),
blend: src_has_alpha,
src_has_alpha,
has_alpha_mult: false,
alpha_mode: AlphaMode::PremultipliedOptical,
eotf: EOTF_LINEAR,
inv_eotf: EOTF_LINEAR,
descriptor_set_layouts: Default::default(),
has_color_management_data: false,
};
self.device.create_pipeline2(info, push_size)
};
let pipelines = Rc::new(static_map! {
TexSourceType::HasAlpha => create_pipeline(true)?,
TexSourceType::Opaque => create_pipeline(false)?,
});
self.rounded_fill_pipelines.set(format, pipelines.clone());
Ok(pipelines)
}
pub(super) fn get_or_create_rounded_tex_pipelines(
&self,
format: vk::Format,
target_cd: &ColorDescription,
) -> Rc<TexPipelines> {
let eotf = target_cd.eotf.to_vulkan();
let pipelines = &self.rounded_tex_pipelines[eotf];
match pipelines.get(&format) {
Some(pl) => pl,
_ => {
let pl = Rc::new(TexPipelines {
format,
eotf,
pipelines: Default::default(),
});
pipelines.set(format, pl.clone());
pl
}
}
}
pub(super) fn get_or_create_rounded_tex_pipeline(
&self,
pipelines: &TexPipelines,
tex_cd: &ColorDescription,
tex_copy_type: TexCopyType,
tex_source_type: TexSourceType,
mut tex_alpha_mode: AlphaMode,
has_color_management_data: bool,
) -> Result<Rc<VulkanPipeline>, VulkanError> {
if tex_source_type == TexSourceType::Opaque {
tex_alpha_mode = AlphaMode::PremultipliedElectrical;
}
let key = TexPipelineKey {
tex_copy_type,
tex_source_type,
tex_alpha_mode,
eotf: tex_cd.eotf.to_vulkan(),
has_color_management_data,
};
if let Some(pl) = pipelines.pipelines.get(&key) {
return Ok(pl);
}
let has_alpha_mult = match tex_copy_type {
TexCopyType::Identity => false,
TexCopyType::Multiply => true,
};
let push_size = if self.device.descriptor_buffer.is_some() {
size_of::<RoundedTexPushConstants>()
} else {
size_of::<LegacyRoundedTexPushConstants>()
};
let info = PipelineCreateInfo {
format: pipelines.format,
vert: self.rounded_tex_vert_shader.clone(),
frag: self.rounded_tex_frag_shader.clone(),
blend: true,
src_has_alpha: true,
has_alpha_mult,
alpha_mode: key.tex_alpha_mode,
eotf: key.eotf.to_vulkan(),
inv_eotf: pipelines.eotf.to_vulkan(),
descriptor_set_layouts: self.tex_descriptor_set_layouts.clone(),
has_color_management_data,
};
let pl = self.device.create_pipeline2(info, push_size)?;
pipelines.pipelines.set(key, pl.clone());
Ok(pl)
}
pub(super) fn get_or_create_tex_pipelines(
&self,
format: vk::Format,
target_cd: &ColorDescription,
) -> Rc<TexPipelines> {
let eotf = target_cd.eotf.to_vulkan();
let pipelines = &self.tex_pipelines[eotf];
match pipelines.get(&format) {
Some(pl) => pl,
_ => {
let pl = Rc::new(TexPipelines {
format,
eotf,
pipelines: Default::default(),
});
pipelines.set(format, pl.clone());
pl
}
}
}
pub(super) fn get_or_create_tex_pipeline(
&self,
pipelines: &TexPipelines,
tex_cd: &ColorDescription,
tex_copy_type: TexCopyType,
tex_source_type: TexSourceType,
mut tex_alpha_mode: AlphaMode,
has_color_management_data: bool,
) -> Result<Rc<VulkanPipeline>, VulkanError> {
if tex_source_type == TexSourceType::Opaque {
tex_alpha_mode = AlphaMode::PremultipliedElectrical;
}
let key = TexPipelineKey {
tex_copy_type,
tex_source_type,
tex_alpha_mode,
eotf: tex_cd.eotf.to_vulkan(),
has_color_management_data,
};
if let Some(pl) = pipelines.pipelines.get(&key) {
return Ok(pl);
}
let src_has_alpha = match tex_source_type {
TexSourceType::Opaque => false,
TexSourceType::HasAlpha => true,
};
let has_alpha_mult = match tex_copy_type {
TexCopyType::Identity => false,
TexCopyType::Multiply => true,
};
let push_size = if self.device.descriptor_buffer.is_some() {
size_of::<TexPushConstants>()
} else {
size_of::<LegacyTexPushConstants>()
};
let info = PipelineCreateInfo {
format: pipelines.format,
vert: self.tex_vert_shader.clone(),
frag: self.tex_frag_shader.clone(),
blend: src_has_alpha || has_alpha_mult,
src_has_alpha,
has_alpha_mult,
alpha_mode: key.tex_alpha_mode,
eotf: key.eotf.to_vulkan(),
inv_eotf: pipelines.eotf.to_vulkan(),
descriptor_set_layouts: self.tex_descriptor_set_layouts.clone(),
has_color_management_data,
};
let pl = self.device.create_pipeline2(info, push_size)?;
pipelines.pipelines.set(key, pl.clone());
Ok(pl)
}
pub(super) fn get_or_create_out_pipeline(
&self,
format: vk::Format,
bb_cd: &ColorDescription,
fb_cd: &ColorDescription,
has_color_management_data: bool,
) -> Result<Rc<VulkanPipeline>, VulkanError> {
let key = OutPipelineKey {
format,
eotf: bb_cd.eotf.to_vulkan(),
has_color_management_data,
};
let fb_eotf = fb_cd.eotf.to_vulkan();
let pipelines = &self.out_pipelines[fb_eotf];
if let Some(pl) = pipelines.get(&key) {
return Ok(pl);
}
let mut descriptor_set_layouts = ArrayVec::new();
descriptor_set_layouts.push(self.out_descriptor_set_layout.clone().unwrap());
let out = self
.device
.create_pipeline::<OutPushConstants>(PipelineCreateInfo {
format: key.format,
vert: self.out_vert_shader.clone().unwrap(),
frag: self.out_frag_shader.clone().unwrap(),
blend: false,
src_has_alpha: true,
has_alpha_mult: false,
alpha_mode: AlphaMode::PremultipliedElectrical,
eotf: key.eotf.to_vulkan(),
inv_eotf: fb_eotf.to_vulkan(),
descriptor_set_layouts,
has_color_management_data,
})?;
pipelines.set(key, out.clone());
Ok(out)
}
}

View file

@ -2,17 +2,20 @@ mod event_handling;
mod device_handler; mod device_handler;
pub mod ext_transient_seat_manager_v1; pub mod ext_transient_seat_manager_v1;
pub mod ext_transient_seat_v1; pub mod ext_transient_seat_v1;
mod focus;
mod gesture_owner; mod gesture_owner;
mod kb_owner; mod kb_owner;
mod pointer_owner; mod pointer_owner;
mod position_hint; mod position_hint;
mod seat_object; mod seat_object;
mod selection;
pub mod tablet; pub mod tablet;
pub mod text_input; pub mod text_input;
mod touch_owner; mod touch_owner;
pub mod wl_keyboard; pub mod wl_keyboard;
pub mod wl_pointer; pub mod wl_pointer;
pub mod wl_touch; pub mod wl_touch;
mod window_management;
pub mod wp_pointer_warp_v1; pub mod wp_pointer_warp_v1;
pub mod zwp_pointer_constraints_v1; pub mod zwp_pointer_constraints_v1;
pub mod zwp_pointer_gesture_hold_v1; pub mod zwp_pointer_gesture_hold_v1;
@ -38,16 +41,12 @@ use {
ifs::{ ifs::{
ext_idle_notification_v1::ExtIdleNotificationV1, ext_idle_notification_v1::ExtIdleNotificationV1,
data_transfer::{ data_transfer::{
self, DynDataSource, TransferError, TransferLocation, self, DynDataSource, TransferError,
data_control::{DataControlDeviceId, DynDataControlDevice}, data_control::{DataControlDeviceId, DynDataControlDevice},
offer_source_to_regular_client, wl_data_device::WlDataDevice,
wl_data_device::{ClipboardTransfer, WlDataDevice},
wl_data_source::WlDataSource, wl_data_source::WlDataSource,
x_data_device::{XClipboardTransfer, XTransferDevice, XTransferDeviceId, XPrimarySelectionTransfer}, x_data_device::{XTransferDevice, XTransferDeviceId},
zwp_primary_selection_device_v1::{ zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1,
PrimarySelectionTransfer, ZwpPrimarySelectionDeviceV1,
},
zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
}, },
wl_output::WlOutputGlobal, wl_output::WlOutputGlobal,
wl_seat::{ wl_seat::{
@ -75,7 +74,6 @@ use {
dnd_icon::DndIcon, dnd_icon::DndIcon,
tray::{DynTrayItem, TrayItemId}, tray::{DynTrayItem, TrayItemId},
xdg_surface::{xdg_popup::XdgPopup, xdg_toplevel::ResizeEdges}, xdg_surface::{xdg_popup::XdgPopup, xdg_toplevel::ResizeEdges},
zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
}, },
xdg_toplevel_drag_v1::XdgToplevelDragV1, xdg_toplevel_drag_v1::XdgToplevelDragV1,
}, },
@ -86,10 +84,8 @@ use {
rect::Rect, rect::Rect,
state::{DeviceHandlerData, State}, state::{DeviceHandlerData, State},
tree::{ tree::{
ChangeGroupAction, ContainerNode, ContainerSplit, Direction, FoundNode, Node, NodeId, FoundNode, Node, NodeId, NodeLocation, OutputNode, ToplevelNode, WorkspaceNode,
NodeLayer, NodeLayerLink, NodeLocation, OutputNode, StackedNode, ToplevelNode, generic_node_visitor, toplevel_set_workspace,
WorkspaceNode, generic_node_visitor, toplevel_create_split, toplevel_parent_container,
toplevel_set_floating, toplevel_set_workspace,
}, },
utils::{ utils::{
asyncevent::AsyncEvent, asyncevent::AsyncEvent,
@ -97,9 +93,9 @@ use {
clonecell::CloneCell, clonecell::CloneCell,
copyhashmap::CopyHashMap, copyhashmap::CopyHashMap,
event_listener::{EventListener, EventSource}, event_listener::{EventListener, EventSource},
linkedlist::{LinkedList, LinkedNode, NodeRef}, linkedlist::{LinkedList, LinkedNode},
numcell::NumCell, numcell::NumCell,
rc_eq::{rc_eq, rc_weak_eq}, rc_eq::rc_eq,
smallmap::SmallMap, smallmap::SmallMap,
static_text::StaticText, static_text::StaticText,
}, },
@ -117,13 +113,12 @@ use {
}, },
kbvm::Keycode, kbvm::Keycode,
linearize::Linearize, linearize::Linearize,
run_on_drop::on_drop,
smallvec::SmallVec, smallvec::SmallVec,
std::{ std::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
collections::hash_map::Entry, collections::hash_map::Entry,
mem, mem,
ops::{Deref, DerefMut}, ops::DerefMut,
rc::{Rc, Weak}, rc::{Rc, Weak},
}, },
thiserror::Error, thiserror::Error,
@ -713,104 +708,6 @@ impl WlSeatGlobal {
self.kb_owner.ungrab(self); self.kb_owner.ungrab(self);
} }
pub fn kb_parent_container(&self) -> Option<Rc<ContainerNode>> {
if let Some(tl) = self.keyboard_node.get().node_toplevel() {
return toplevel_parent_container(&*tl);
}
None
}
pub fn get_mono(&self) -> Option<bool> {
self.kb_parent_container().map(|c| c.mono_child.is_some())
}
pub fn get_split(&self) -> Option<ContainerSplit> {
self.kb_parent_container().map(|c| c.split.get())
}
pub fn set_mono(&self, mono: bool) {
if let Some(tl) = self.keyboard_node.get().node_toplevel()
&& let Some(parent) = tl.tl_data().parent.get()
&& let Some(container) = parent.node_into_container()
{
let node = if mono { Some(tl.deref()) } else { None };
container.set_mono(node);
}
}
pub fn set_split(&self, axis: ContainerSplit) {
if let Some(c) = self.kb_parent_container() {
c.set_split(axis);
}
}
pub fn create_split(&self, axis: ContainerSplit) {
let tl = match self.keyboard_node.get().node_toplevel() {
Some(tl) => tl,
_ => return,
};
toplevel_create_split(&self.state, tl, axis);
}
pub fn toggle_tab(&self) {
if let Some(c) = self.kb_parent_container() {
c.change_group(ChangeGroupAction::ToggleTab);
}
}
pub fn make_group(&self, axis: ContainerSplit, ephemeral: bool) {
if let Some(c) = self.kb_parent_container() {
c.make_group(axis, ephemeral);
}
}
pub fn change_group_opposite(&self) {
if let Some(c) = self.kb_parent_container() {
c.change_group(ChangeGroupAction::Opposite);
}
}
pub fn equalize(&self, recursive: bool) {
if let Some(c) = self.kb_parent_container() {
if recursive {
c.equalize_recursive();
} else {
c.equalize();
}
}
}
pub fn move_tab(&self, right: bool) {
if let Some(c) = self.kb_parent_container() {
c.move_tab(right);
}
}
pub fn focus_parent(self: &Rc<Self>) {
if let Some(tl) = self.keyboard_node.get().node_toplevel()
&& let Some(parent) = tl.tl_data().parent.get()
&& let Some(tl) = parent.node_toplevel()
{
self.focus_node(tl);
self.maybe_schedule_warp_mouse_to_focus();
}
}
pub fn get_floating(self: &Rc<Self>) -> Option<bool> {
match self.keyboard_node.get().node_toplevel() {
Some(tl) => Some(tl.tl_data().parent_is_float.get()),
_ => None,
}
}
pub fn set_floating(self: &Rc<Self>, floating: bool) {
let tl = match self.keyboard_node.get().node_toplevel() {
Some(tl) => tl,
_ => return,
};
toplevel_set_floating(&self.state, tl, floating);
}
pub fn get_rate(&self) -> (i32, i32) { pub fn get_rate(&self) -> (i32, i32) {
self.repeat_rate.get() self.repeat_rate.get()
} }
@ -834,519 +731,6 @@ impl WlSeatGlobal {
} }
} }
pub fn close(self: &Rc<Self>) {
let kb_node = self.keyboard_node.get();
if let Some(tl) = kb_node.node_toplevel() {
tl.tl_close();
}
}
pub fn move_focus(self: &Rc<Self>, direction: Direction) {
let tl = match self.keyboard_node.get().node_toplevel() {
Some(tl) => tl,
_ => {
if let Some(ws) = self.keyboard_node.get().node_into_workspace()
&& let Some(target) = self
.state
.find_output_in_direction(&ws.output.get(), direction)
{
target.take_keyboard_navigation_focus(self, direction);
self.maybe_schedule_warp_mouse_to_focus();
}
return;
}
};
if direction == Direction::Down && tl.node_is_container() {
tl.node_do_focus(self, direction);
} else {
let data = tl.tl_data();
if data.is_fullscreen.get()
&& let Some(output) = data.output_opt()
&& let Some(target) = self.state.find_output_in_direction(&output, direction)
{
target.take_keyboard_navigation_focus(self, direction);
} else if let Some(p) = data.parent.get()
&& let Some(c) = p.node_into_container()
{
c.move_focus_from_child(self, tl.deref(), direction);
} else if let Some(float) = data.float.get() {
let ws = float.workspace.get();
let floats: Vec<_> = ws
.stacked
.iter()
.filter_map(|node| (*node).clone().node_into_float())
.filter(|f| f.child.get().is_some())
.collect();
if let Some(pos) = floats.iter().position(|f| f.id == float.id) {
let target = match direction {
Direction::Left | Direction::Down => {
if pos == 0 {
floats.last()
} else {
floats.get(pos - 1)
}
}
_ => {
if pos + 1 >= floats.len() {
floats.first()
} else {
floats.get(pos + 1)
}
}
};
if let Some(f) = target
&& f.id != float.id
{
f.clone().node_do_focus(self, Direction::Unspecified);
}
}
}
}
self.maybe_schedule_warp_mouse_to_focus();
}
pub fn maybe_schedule_warp_mouse_to_focus(self: &Rc<Self>) {
if self.mouse_follows_focus() {
self.warp_mouse_to_focus_skip_target_check.set(true);
self.schedule_warp_mouse_to_focus();
}
}
pub fn schedule_warp_mouse_to_focus(self: &Rc<Self>) {
if !self.warp_mouse_to_focus_scheduled.replace(true) {
self.state.pending_warp_mouse_to_focus.push(self.clone());
}
}
pub fn move_focused(self: &Rc<Self>, direction: Direction) {
let kb_node = self.keyboard_node.get();
let Some(tl) = kb_node.node_toplevel() else {
if let Some(ws) = self.keyboard_node.get().node_into_workspace()
&& let Some(target) = self
.state
.find_output_in_direction(&ws.output.get(), direction)
{
self.state.move_ws_to_output(&ws, &target);
}
return;
};
let data = tl.tl_data();
if data.is_fullscreen.get()
&& let Some(output) = data.output_opt()
&& let Some(target) = self.state.find_output_in_direction(&output, direction)
{
let ws = target.ensure_workspace();
toplevel_set_workspace(&self.state, tl, &ws);
self.maybe_schedule_warp_mouse_to_focus();
} else if let Some(parent) = data.parent.get()
&& let Some(c) = parent.node_into_container()
{
c.move_child(tl, direction);
self.maybe_schedule_warp_mouse_to_focus();
} else if let Some(float) = data.float.get() {
float.move_by_direction(direction);
self.maybe_schedule_warp_mouse_to_focus();
}
}
pub fn get_last_focus_on_workspace(&self, ws: &WorkspaceNode) -> Option<Rc<dyn Node>> {
let mut node = self.focus_history.last()?;
loop {
if let Some(node) = node.node.upgrade()
&& let Some(NodeLocation::Workspace(_, new)) = node.node_location()
&& new == ws.id
{
return Some(node);
}
node = node.prev()?;
}
}
fn get_focus_history(
&self,
next: impl Fn(&NodeRef<FocusHistoryData>) -> Option<NodeRef<FocusHistoryData>>,
first: impl FnOnce(&LinkedList<FocusHistoryData>) -> Option<NodeRef<FocusHistoryData>>,
) -> Option<(Rc<dyn Node>, bool)> {
let original = self.keyboard_node.get();
let mut output = None;
let mut workspace = None;
if let Some(old) = original.node_location() {
match old {
NodeLocation::Workspace(o, w) => {
workspace = Some(w);
output = Some(o);
}
NodeLocation::Output(o) => {
output = Some(o);
}
}
}
if (output.is_none() || workspace.is_none())
&& let Some(old) = self.last_focus_location.get()
{
match old {
NodeLocation::Workspace(o, w) => {
workspace = workspace.or(Some(w));
output = output.or(Some(o));
}
NodeLocation::Output(o) => {
output = output.or(Some(o));
}
}
}
if workspace.is_none()
&& let Some(output) = original.node_output()
&& let Some(ws) = output.workspace.get()
{
workspace = Some(ws.id);
}
let matches = |node: &FocusHistoryData| {
let visible = node.visible.get();
if self.focus_history_visible_only.get() && !visible {
return None;
}
let node = node.node.upgrade()?;
if self.focus_history_same_workspace.get() {
let new = node.node_location()?;
let o = match new {
NodeLocation::Workspace(o, w) => {
if workspace != Some(w) {
return None;
}
o
}
NodeLocation::Output(o) => o,
};
if output != Some(o) {
return None;
}
}
Some((node, visible))
};
let node = original.node_seat_state().get_focus_history(self);
if let Some(mut node) = node {
loop {
node = match next(&node) {
Some(n) => n,
_ => break,
};
if let Some(matches) = matches(&node) {
return Some(matches);
}
}
}
let mut node = first(&self.focus_history)?;
loop {
if rc_weak_eq(&original, &node.node) {
return None;
}
if let Some(matches) = matches(&node) {
return Some(matches);
}
node = next(&node)?;
}
}
fn focus_history(
self: &Rc<Self>,
next: impl Fn(&NodeRef<FocusHistoryData>) -> Option<NodeRef<FocusHistoryData>>,
first: impl FnOnce(&LinkedList<FocusHistoryData>) -> Option<NodeRef<FocusHistoryData>>,
) {
let Some((node, visible)) = self.get_focus_history(next, first) else {
return;
};
self.focus_history_rotate.fetch_add(1);
let _reset = on_drop(|| {
self.focus_history_rotate.fetch_sub(1);
});
if !visible {
node.clone().node_make_visible();
if !node.node_visible() {
return;
}
}
self.focus_node(node);
self.maybe_schedule_warp_mouse_to_focus();
}
pub fn focus_prev(self: &Rc<Self>) {
self.focus_history(|s| s.prev(), |l| l.last());
}
pub fn focus_next(self: &Rc<Self>) {
self.focus_history(|s| s.next(), |l| l.first());
}
pub fn focus_history_set_visible(&self, visible: bool) {
self.focus_history_visible_only.set(visible);
}
pub fn focus_history_set_same_workspace(&self, same_workspace: bool) {
self.focus_history_same_workspace.set(same_workspace);
}
fn focus_layer_rel<LI, SI>(
self: &Rc<Self>,
next_layer: impl Fn(NodeLayer) -> NodeLayer,
layer_node_next: impl Fn(
&NodeRef<Rc<ZwlrLayerSurfaceV1>>,
) -> Option<NodeRef<Rc<ZwlrLayerSurfaceV1>>>,
stacked_node_next: impl Fn(
&NodeRef<Rc<dyn StackedNode>>,
) -> Option<NodeRef<Rc<dyn StackedNode>>>,
layer_list_iter: impl Fn(&LinkedList<Rc<ZwlrLayerSurfaceV1>>) -> LI,
stacked_list_iter: impl Fn(&LinkedList<Rc<dyn StackedNode>>) -> SI,
) where
LI: Iterator<Item = NodeRef<Rc<ZwlrLayerSurfaceV1>>>,
SI: Iterator<Item = NodeRef<Rc<dyn StackedNode>>>,
{
fn node_viable(n: &(impl Node + ?Sized)) -> bool {
n.node_visible() && n.node_accepts_focus()
}
let current = self.keyboard_node.get();
let Some(output) = current.node_output() else {
return;
};
let current_layer = current.node_layer();
match &current_layer {
NodeLayerLink::Layer0(l)
| NodeLayerLink::Layer1(l)
| NodeLayerLink::Layer2(l)
| NodeLayerLink::Layer3(l) => {
if let Some(n) = layer_node_next(l)
&& node_viable(&**n)
{
n.deref()
.clone()
.node_do_focus(self, Direction::Unspecified);
self.maybe_schedule_warp_mouse_to_focus();
return;
}
}
NodeLayerLink::Stacked(l) | NodeLayerLink::StackedAboveLayers(l) => {
if let Some(n) = stacked_node_next(l)
&& node_viable(&**n)
&& n.node_output().map(|o| o.id) == Some(output.id)
{
n.deref()
.clone()
.node_do_focus(self, Direction::Unspecified);
self.maybe_schedule_warp_mouse_to_focus();
return;
}
}
NodeLayerLink::Display => {}
NodeLayerLink::Output => {}
NodeLayerLink::Workspace => {}
NodeLayerLink::Tiled => {}
NodeLayerLink::Fullscreen => {}
NodeLayerLink::Lock => {}
NodeLayerLink::InputMethod => {}
}
let handle_layer_shell = |l: &LinkedList<Rc<ZwlrLayerSurfaceV1>>| {
for n in layer_list_iter(l) {
if node_viable(&**n) {
return Some(n.deref().clone() as Rc<dyn Node>);
}
}
None
};
let handle_stacked = |l: &LinkedList<Rc<dyn StackedNode>>| {
for n in stacked_list_iter(l) {
if node_viable(&**n) && n.node_output().map(|o| o.id) == Some(output.id) {
return Some(n.deref().clone() as Rc<dyn Node>);
}
}
None
};
let ws = output.workspace.get();
let first = next_layer(current_layer.layer());
let mut layer = first;
loop {
let node = match layer {
NodeLayer::Display => None,
NodeLayer::Layer0 => handle_layer_shell(&output.layers[0]),
NodeLayer::Layer1 => handle_layer_shell(&output.layers[1]),
NodeLayer::Output => None,
NodeLayer::Workspace => {
if let Some(ws) = &ws
&& ws.container_visible()
{
self.focus_node(ws.clone());
self.maybe_schedule_warp_mouse_to_focus();
return;
}
None
}
NodeLayer::Tiled => ws
.as_ref()
.and_then(|w| w.container.get())
.map(|n| n as Rc<dyn Node>),
NodeLayer::Fullscreen => ws
.as_ref()
.and_then(|w| w.fullscreen.get())
.map(|n| n as Rc<dyn Node>),
NodeLayer::Stacked => handle_stacked(&self.state.root.stacked),
NodeLayer::Layer2 => handle_layer_shell(&output.layers[2]),
NodeLayer::Layer3 => handle_layer_shell(&output.layers[3]),
NodeLayer::StackedAboveLayers => {
handle_stacked(&self.state.root.stacked_above_layers)
}
NodeLayer::Lock => None,
NodeLayer::InputMethod => None,
};
if let Some(n) = node {
if node_viable(&*n) {
n.node_do_focus(self, Direction::Unspecified);
self.maybe_schedule_warp_mouse_to_focus();
return;
}
}
layer = next_layer(layer);
if layer == first {
return;
}
}
}
pub fn focus_layer_below(self: &Rc<Self>) {
self.focus_layer_rel(
|l| l.prev(),
|n| n.prev(),
|n| n.prev(),
|l| l.rev_iter(),
|l| l.rev_iter(),
);
}
pub fn focus_layer_above(self: &Rc<Self>) {
self.focus_layer_rel(
|l| l.next(),
|n| n.next(),
|n| n.next(),
|l| l.iter(),
|l| l.iter(),
);
}
pub fn toggle_focus_float_tiled(self: &Rc<Self>) {
let current = self.keyboard_node.get();
match current.node_layer().layer() {
NodeLayer::Tiled | NodeLayer::Fullscreen => self.focus_floats(),
_ => self.focus_tiles(),
}
self.maybe_schedule_warp_mouse_to_focus();
}
pub fn focus_floats(self: &Rc<Self>) {
let current = self.keyboard_node.get();
if current.node_layer().layer() == NodeLayer::Stacked {
return;
}
let Some(output) = current.node_output() else {
return;
};
let Some(ws) = output.workspace.get() else {
return;
};
if let Some(child) = ws
.stacked
.rev_iter()
.filter_map(|node| (*node).clone().node_into_float())
.find_map(|float| float.child.get())
{
child.node_do_focus(self, Direction::Unspecified);
}
}
pub fn focus_tiles(self: &Rc<Self>) {
let current = self.keyboard_node.get();
if matches!(
current.node_layer().layer(),
NodeLayer::Tiled | NodeLayer::Fullscreen,
) {
return;
}
let Some(output) = current.node_output() else {
return;
};
let Some(ws) = output.workspace.get() else {
return;
};
let node = match ws.fullscreen.get() {
Some(fs) => fs as Rc<dyn Node>,
_ => match ws.container.get() {
Some(c) => c,
_ => return,
},
};
if node.node_visible() && node.node_accepts_focus() {
node.node_do_focus(self, Direction::Unspecified);
self.maybe_schedule_warp_mouse_to_focus();
}
}
fn set_selection_<T, X, S>(
self: &Rc<Self>,
field: &CloneCell<Option<Rc<dyn DynDataSource>>>,
src: Option<Rc<S>>,
location: TransferLocation,
) -> Result<(), WlSeatError>
where
T: data_transfer::IterableTransferVtable,
X: data_transfer::TransferVtable<Device = XTransferDevice>,
S: DynDataSource,
{
if let (Some(new), Some(old)) = (&src, &field.get())
&& new.source_data().id == old.source_data().id
{
return Ok(());
}
if let Some(new) = &src {
data_transfer::attach_seat(&**new, self, data_transfer::Role::Selection)?;
}
let src_dyn = src.clone().map(|s| s as Rc<dyn DynDataSource>);
if let Some(old) = field.set(src_dyn) {
old.detach_seat(self);
}
if let Some(client) = self.keyboard_node.get().node_client() {
self.offer_selection_to_client::<T, X>(src.clone().map(|v| v as Rc<_>), &client);
// client.flush();
}
let dyn_source = src.map(|s| s as Rc<dyn DynDataSource>);
for dd in self.data_control_devices.lock().values() {
dd.clone().handle_new_source(location, dyn_source.clone());
}
Ok(())
}
fn offer_selection_to_client<T, X>(
&self,
selection: Option<Rc<dyn DynDataSource>>,
client: &Rc<Client>,
) where
T: data_transfer::IterableTransferVtable,
X: data_transfer::TransferVtable<Device = XTransferDevice>,
{
if let Some(src) = &selection {
src.cancel_unprivileged_offers();
}
if client.is_xwayland {
self.for_each_x_data_device(|dd| match &selection {
Some(src) => src.clone().offer_to_x(&dd),
_ => X::send_selection(&dd, None),
});
} else {
match selection {
Some(src) => offer_source_to_regular_client::<T>(src, client),
_ => T::for_each_device(self, client.id, |device| {
T::send_selection(device, None);
}),
}
}
}
pub fn start_drag( pub fn start_drag(
self: &Rc<Self>, self: &Rc<Self>,
origin: &Rc<WlSurface>, origin: &Rc<WlSurface>,
@ -1398,88 +782,6 @@ impl WlSeatGlobal {
self.pointer_owner.cancel_dnd(self); self.pointer_owner.cancel_dnd(self);
} }
pub fn unset_selection(self: &Rc<Self>) {
let _ = self.set_wl_data_source_selection(None, None);
}
pub fn set_wl_data_source_selection(
self: &Rc<Self>,
selection: Option<Rc<WlDataSource>>,
serial: Option<u64>,
) -> Result<(), WlSeatError> {
if let Some(serial) = serial {
self.selection_serial.set(serial);
}
if let Some(selection) = &selection
&& selection.toplevel_drag.is_some()
{
return Err(WlSeatError::OfferHasDrag);
}
self.set_selection(selection)
}
pub fn set_selection<S: DynDataSource>(
self: &Rc<Self>,
selection: Option<Rc<S>>,
) -> Result<(), WlSeatError> {
self.set_selection_::<ClipboardTransfer, XClipboardTransfer, _>(
&self.selection,
selection,
TransferLocation::Clipboard,
)
}
pub fn get_selection(&self) -> Option<Rc<dyn DynDataSource>> {
self.selection.get()
}
pub fn may_modify_selection(&self, client: &Rc<Client>, serial: u64) -> bool {
if serial < self.selection_serial.get() {
return false;
}
self.keyboard_node.get().node_client_id() == Some(client.id)
}
pub fn may_modify_primary_selection(&self, client: &Rc<Client>, serial: Option<u64>) -> bool {
if let Some(serial) = serial
&& serial < self.primary_selection_serial.get()
{
return false;
}
self.keyboard_node.get().node_client_id() == Some(client.id)
|| self.pointer_node().and_then(|n| n.node_client_id()) == Some(client.id)
}
pub fn unset_primary_selection(self: &Rc<Self>) {
let _ = self.set_zwp_primary_selection(None, None);
}
pub fn set_zwp_primary_selection(
self: &Rc<Self>,
selection: Option<Rc<ZwpPrimarySelectionSourceV1>>,
serial: Option<u64>,
) -> Result<(), WlSeatError> {
if let Some(serial) = serial {
self.primary_selection_serial.set(serial);
}
self.set_primary_selection(selection)
}
pub fn set_primary_selection<S: DynDataSource>(
self: &Rc<Self>,
selection: Option<Rc<S>>,
) -> Result<(), WlSeatError> {
self.set_selection_::<PrimarySelectionTransfer, XPrimarySelectionTransfer, _>(
&self.primary_selection,
selection,
TransferLocation::PrimarySelection,
)
}
pub fn get_primary_selection(&self) -> Option<Rc<dyn DynDataSource>> {
self.primary_selection.get()
}
pub fn dnd_icon(&self) -> Option<Rc<DndIcon>> { pub fn dnd_icon(&self) -> Option<Rc<DndIcon>> {
self.pointer_owner.dnd_icon() self.pointer_owner.dnd_icon()
} }

471
src/ifs/wl_seat/focus.rs Normal file
View file

@ -0,0 +1,471 @@
use {
super::{WlSeatGlobal, event_handling::FocusHistoryData},
crate::{
ifs::wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
tree::{
Direction, Node, NodeLayer, NodeLayerLink, NodeLocation, StackedNode, WorkspaceNode,
toplevel_set_workspace,
},
utils::{
linkedlist::{LinkedList, NodeRef},
rc_eq::rc_weak_eq,
},
},
run_on_drop::on_drop,
std::{ops::Deref, rc::Rc},
};
impl WlSeatGlobal {
pub fn close(self: &Rc<Self>) {
let kb_node = self.keyboard_node.get();
if let Some(tl) = kb_node.node_toplevel() {
tl.tl_close();
}
}
pub fn move_focus(self: &Rc<Self>, direction: Direction) {
let tl = match self.keyboard_node.get().node_toplevel() {
Some(tl) => tl,
_ => {
if let Some(ws) = self.keyboard_node.get().node_into_workspace()
&& let Some(target) = self
.state
.find_output_in_direction(&ws.output.get(), direction)
{
target.take_keyboard_navigation_focus(self, direction);
self.maybe_schedule_warp_mouse_to_focus();
}
return;
}
};
if direction == Direction::Down && tl.node_is_container() {
tl.node_do_focus(self, direction);
} else {
let data = tl.tl_data();
if data.is_fullscreen.get()
&& let Some(output) = data.output_opt()
&& let Some(target) = self.state.find_output_in_direction(&output, direction)
{
target.take_keyboard_navigation_focus(self, direction);
} else if let Some(p) = data.parent.get()
&& let Some(c) = p.node_into_container()
{
c.move_focus_from_child(self, tl.deref(), direction);
} else if let Some(float) = data.float.get() {
let ws = float.workspace.get();
let floats: Vec<_> = ws
.stacked
.iter()
.filter_map(|node| (*node).clone().node_into_float())
.filter(|f| f.child.get().is_some())
.collect();
if let Some(pos) = floats.iter().position(|f| f.id == float.id) {
let target = match direction {
Direction::Left | Direction::Down => {
if pos == 0 {
floats.last()
} else {
floats.get(pos - 1)
}
}
_ => {
if pos + 1 >= floats.len() {
floats.first()
} else {
floats.get(pos + 1)
}
}
};
if let Some(f) = target
&& f.id != float.id
{
f.clone().node_do_focus(self, Direction::Unspecified);
}
}
}
}
self.maybe_schedule_warp_mouse_to_focus();
}
pub fn maybe_schedule_warp_mouse_to_focus(self: &Rc<Self>) {
if self.mouse_follows_focus() {
self.warp_mouse_to_focus_skip_target_check.set(true);
self.schedule_warp_mouse_to_focus();
}
}
pub fn schedule_warp_mouse_to_focus(self: &Rc<Self>) {
if !self.warp_mouse_to_focus_scheduled.replace(true) {
self.state.pending_warp_mouse_to_focus.push(self.clone());
}
}
pub fn move_focused(self: &Rc<Self>, direction: Direction) {
let kb_node = self.keyboard_node.get();
let Some(tl) = kb_node.node_toplevel() else {
if let Some(ws) = self.keyboard_node.get().node_into_workspace()
&& let Some(target) = self
.state
.find_output_in_direction(&ws.output.get(), direction)
{
self.state.move_ws_to_output(&ws, &target);
}
return;
};
let data = tl.tl_data();
if data.is_fullscreen.get()
&& let Some(output) = data.output_opt()
&& let Some(target) = self.state.find_output_in_direction(&output, direction)
{
let ws = target.ensure_workspace();
toplevel_set_workspace(&self.state, tl, &ws);
self.maybe_schedule_warp_mouse_to_focus();
} else if let Some(parent) = data.parent.get()
&& let Some(c) = parent.node_into_container()
{
c.move_child(tl, direction);
self.maybe_schedule_warp_mouse_to_focus();
} else if let Some(float) = data.float.get() {
float.move_by_direction(direction);
self.maybe_schedule_warp_mouse_to_focus();
}
}
pub fn get_last_focus_on_workspace(&self, ws: &WorkspaceNode) -> Option<Rc<dyn Node>> {
let mut node = self.focus_history.last()?;
loop {
if let Some(node) = node.node.upgrade()
&& let Some(NodeLocation::Workspace(_, new)) = node.node_location()
&& new == ws.id
{
return Some(node);
}
node = node.prev()?;
}
}
fn get_focus_history(
&self,
next: impl Fn(&NodeRef<FocusHistoryData>) -> Option<NodeRef<FocusHistoryData>>,
first: impl FnOnce(&LinkedList<FocusHistoryData>) -> Option<NodeRef<FocusHistoryData>>,
) -> Option<(Rc<dyn Node>, bool)> {
let original = self.keyboard_node.get();
let mut output = None;
let mut workspace = None;
if let Some(old) = original.node_location() {
match old {
NodeLocation::Workspace(o, w) => {
workspace = Some(w);
output = Some(o);
}
NodeLocation::Output(o) => {
output = Some(o);
}
}
}
if (output.is_none() || workspace.is_none())
&& let Some(old) = self.last_focus_location.get()
{
match old {
NodeLocation::Workspace(o, w) => {
workspace = workspace.or(Some(w));
output = output.or(Some(o));
}
NodeLocation::Output(o) => {
output = output.or(Some(o));
}
}
}
if workspace.is_none()
&& let Some(output) = original.node_output()
&& let Some(ws) = output.workspace.get()
{
workspace = Some(ws.id);
}
let matches = |node: &FocusHistoryData| {
let visible = node.visible.get();
if self.focus_history_visible_only.get() && !visible {
return None;
}
let node = node.node.upgrade()?;
if self.focus_history_same_workspace.get() {
let new = node.node_location()?;
let o = match new {
NodeLocation::Workspace(o, w) => {
if workspace != Some(w) {
return None;
}
o
}
NodeLocation::Output(o) => o,
};
if output != Some(o) {
return None;
}
}
Some((node, visible))
};
let node = original.node_seat_state().get_focus_history(self);
if let Some(mut node) = node {
loop {
node = match next(&node) {
Some(n) => n,
_ => break,
};
if let Some(matches) = matches(&node) {
return Some(matches);
}
}
}
let mut node = first(&self.focus_history)?;
loop {
if rc_weak_eq(&original, &node.node) {
return None;
}
if let Some(matches) = matches(&node) {
return Some(matches);
}
node = next(&node)?;
}
}
fn focus_history(
self: &Rc<Self>,
next: impl Fn(&NodeRef<FocusHistoryData>) -> Option<NodeRef<FocusHistoryData>>,
first: impl FnOnce(&LinkedList<FocusHistoryData>) -> Option<NodeRef<FocusHistoryData>>,
) {
let Some((node, visible)) = self.get_focus_history(next, first) else {
return;
};
self.focus_history_rotate.fetch_add(1);
let _reset = on_drop(|| {
self.focus_history_rotate.fetch_sub(1);
});
if !visible {
node.clone().node_make_visible();
if !node.node_visible() {
return;
}
}
self.focus_node(node);
self.maybe_schedule_warp_mouse_to_focus();
}
pub fn focus_prev(self: &Rc<Self>) {
self.focus_history(|s| s.prev(), |l| l.last());
}
pub fn focus_next(self: &Rc<Self>) {
self.focus_history(|s| s.next(), |l| l.first());
}
pub fn focus_history_set_visible(&self, visible: bool) {
self.focus_history_visible_only.set(visible);
}
pub fn focus_history_set_same_workspace(&self, same_workspace: bool) {
self.focus_history_same_workspace.set(same_workspace);
}
fn focus_layer_rel<LI, SI>(
self: &Rc<Self>,
next_layer: impl Fn(NodeLayer) -> NodeLayer,
layer_node_next: impl Fn(
&NodeRef<Rc<ZwlrLayerSurfaceV1>>,
) -> Option<NodeRef<Rc<ZwlrLayerSurfaceV1>>>,
stacked_node_next: impl Fn(
&NodeRef<Rc<dyn StackedNode>>,
) -> Option<NodeRef<Rc<dyn StackedNode>>>,
layer_list_iter: impl Fn(&LinkedList<Rc<ZwlrLayerSurfaceV1>>) -> LI,
stacked_list_iter: impl Fn(&LinkedList<Rc<dyn StackedNode>>) -> SI,
) where
LI: Iterator<Item = NodeRef<Rc<ZwlrLayerSurfaceV1>>>,
SI: Iterator<Item = NodeRef<Rc<dyn StackedNode>>>,
{
fn node_viable(n: &(impl Node + ?Sized)) -> bool {
n.node_visible() && n.node_accepts_focus()
}
let current = self.keyboard_node.get();
let Some(output) = current.node_output() else {
return;
};
let current_layer = current.node_layer();
match &current_layer {
NodeLayerLink::Layer0(l)
| NodeLayerLink::Layer1(l)
| NodeLayerLink::Layer2(l)
| NodeLayerLink::Layer3(l) => {
if let Some(n) = layer_node_next(l)
&& node_viable(&**n)
{
n.deref()
.clone()
.node_do_focus(self, Direction::Unspecified);
self.maybe_schedule_warp_mouse_to_focus();
return;
}
}
NodeLayerLink::Stacked(l) | NodeLayerLink::StackedAboveLayers(l) => {
if let Some(n) = stacked_node_next(l)
&& node_viable(&**n)
&& n.node_output().map(|o| o.id) == Some(output.id)
{
n.deref()
.clone()
.node_do_focus(self, Direction::Unspecified);
self.maybe_schedule_warp_mouse_to_focus();
return;
}
}
NodeLayerLink::Display => {}
NodeLayerLink::Output => {}
NodeLayerLink::Workspace => {}
NodeLayerLink::Tiled => {}
NodeLayerLink::Fullscreen => {}
NodeLayerLink::Lock => {}
NodeLayerLink::InputMethod => {}
}
let handle_layer_shell = |l: &LinkedList<Rc<ZwlrLayerSurfaceV1>>| {
for n in layer_list_iter(l) {
if node_viable(&**n) {
return Some(n.deref().clone() as Rc<dyn Node>);
}
}
None
};
let handle_stacked = |l: &LinkedList<Rc<dyn StackedNode>>| {
for n in stacked_list_iter(l) {
if node_viable(&**n) && n.node_output().map(|o| o.id) == Some(output.id) {
return Some(n.deref().clone() as Rc<dyn Node>);
}
}
None
};
let ws = output.workspace.get();
let first = next_layer(current_layer.layer());
let mut layer = first;
loop {
let node = match layer {
NodeLayer::Display => None,
NodeLayer::Layer0 => handle_layer_shell(&output.layers[0]),
NodeLayer::Layer1 => handle_layer_shell(&output.layers[1]),
NodeLayer::Output => None,
NodeLayer::Workspace => {
if let Some(ws) = &ws
&& ws.container_visible()
{
self.focus_node(ws.clone());
self.maybe_schedule_warp_mouse_to_focus();
return;
}
None
}
NodeLayer::Tiled => ws
.as_ref()
.and_then(|w| w.container.get())
.map(|n| n as Rc<dyn Node>),
NodeLayer::Fullscreen => ws
.as_ref()
.and_then(|w| w.fullscreen.get())
.map(|n| n as Rc<dyn Node>),
NodeLayer::Stacked => handle_stacked(&self.state.root.stacked),
NodeLayer::Layer2 => handle_layer_shell(&output.layers[2]),
NodeLayer::Layer3 => handle_layer_shell(&output.layers[3]),
NodeLayer::StackedAboveLayers => {
handle_stacked(&self.state.root.stacked_above_layers)
}
NodeLayer::Lock => None,
NodeLayer::InputMethod => None,
};
if let Some(n) = node {
if node_viable(&*n) {
n.node_do_focus(self, Direction::Unspecified);
self.maybe_schedule_warp_mouse_to_focus();
return;
}
}
layer = next_layer(layer);
if layer == first {
return;
}
}
}
pub fn focus_layer_below(self: &Rc<Self>) {
self.focus_layer_rel(
|l| l.prev(),
|n| n.prev(),
|n| n.prev(),
|l| l.rev_iter(),
|l| l.rev_iter(),
);
}
pub fn focus_layer_above(self: &Rc<Self>) {
self.focus_layer_rel(
|l| l.next(),
|n| n.next(),
|n| n.next(),
|l| l.iter(),
|l| l.iter(),
);
}
pub fn toggle_focus_float_tiled(self: &Rc<Self>) {
let current = self.keyboard_node.get();
match current.node_layer().layer() {
NodeLayer::Tiled | NodeLayer::Fullscreen => self.focus_floats(),
_ => self.focus_tiles(),
}
self.maybe_schedule_warp_mouse_to_focus();
}
pub fn focus_floats(self: &Rc<Self>) {
let current = self.keyboard_node.get();
if current.node_layer().layer() == NodeLayer::Stacked {
return;
}
let Some(output) = current.node_output() else {
return;
};
let Some(ws) = output.workspace.get() else {
return;
};
if let Some(child) = ws
.stacked
.rev_iter()
.filter_map(|node| (*node).clone().node_into_float())
.find_map(|float| float.child.get())
{
child.node_do_focus(self, Direction::Unspecified);
}
}
pub fn focus_tiles(self: &Rc<Self>) {
let current = self.keyboard_node.get();
if matches!(
current.node_layer().layer(),
NodeLayer::Tiled | NodeLayer::Fullscreen,
) {
return;
}
let Some(output) = current.node_output() else {
return;
};
let Some(ws) = output.workspace.get() else {
return;
};
let node = match ws.fullscreen.get() {
Some(fs) => fs as Rc<dyn Node>,
_ => match ws.container.get() {
Some(c) => c,
_ => return,
},
};
if node.node_visible() && node.node_accepts_focus() {
node.node_do_focus(self, Direction::Unspecified);
self.maybe_schedule_warp_mouse_to_focus();
}
}
}

View file

@ -0,0 +1,160 @@
use {
super::{WlSeatError, WlSeatGlobal},
crate::{
client::Client,
ifs::data_transfer::{
self, DynDataSource, TransferLocation, offer_source_to_regular_client,
wl_data_device::ClipboardTransfer,
wl_data_source::WlDataSource,
x_data_device::{XClipboardTransfer, XPrimarySelectionTransfer, XTransferDevice},
zwp_primary_selection_device_v1::PrimarySelectionTransfer,
zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
},
utils::clonecell::CloneCell,
},
std::rc::Rc,
};
impl WlSeatGlobal {
fn set_selection_<T, X, S>(
self: &Rc<Self>,
field: &CloneCell<Option<Rc<dyn DynDataSource>>>,
src: Option<Rc<S>>,
location: TransferLocation,
) -> Result<(), WlSeatError>
where
T: data_transfer::IterableTransferVtable,
X: data_transfer::TransferVtable<Device = XTransferDevice>,
S: DynDataSource,
{
if let (Some(new), Some(old)) = (&src, &field.get())
&& new.source_data().id == old.source_data().id
{
return Ok(());
}
if let Some(new) = &src {
data_transfer::attach_seat(&**new, self, data_transfer::Role::Selection)?;
}
let src_dyn = src.clone().map(|s| s as Rc<dyn DynDataSource>);
if let Some(old) = field.set(src_dyn) {
old.detach_seat(self);
}
if let Some(client) = self.keyboard_node.get().node_client() {
self.offer_selection_to_client::<T, X>(src.clone().map(|v| v as Rc<_>), &client);
// client.flush();
}
let dyn_source = src.map(|s| s as Rc<dyn DynDataSource>);
for dd in self.data_control_devices.lock().values() {
dd.clone().handle_new_source(location, dyn_source.clone());
}
Ok(())
}
pub(super) fn offer_selection_to_client<T, X>(
&self,
selection: Option<Rc<dyn DynDataSource>>,
client: &Rc<Client>,
) where
T: data_transfer::IterableTransferVtable,
X: data_transfer::TransferVtable<Device = XTransferDevice>,
{
if let Some(src) = &selection {
src.cancel_unprivileged_offers();
}
if client.is_xwayland {
self.for_each_x_data_device(|dd| match &selection {
Some(src) => src.clone().offer_to_x(&dd),
_ => X::send_selection(&dd, None),
});
} else {
match selection {
Some(src) => offer_source_to_regular_client::<T>(src, client),
_ => T::for_each_device(self, client.id, |device| {
T::send_selection(device, None);
}),
}
}
}
pub fn unset_selection(self: &Rc<Self>) {
let _ = self.set_wl_data_source_selection(None, None);
}
pub fn set_wl_data_source_selection(
self: &Rc<Self>,
selection: Option<Rc<WlDataSource>>,
serial: Option<u64>,
) -> Result<(), WlSeatError> {
if let Some(serial) = serial {
self.selection_serial.set(serial);
}
if let Some(selection) = &selection
&& selection.toplevel_drag.is_some()
{
return Err(WlSeatError::OfferHasDrag);
}
self.set_selection(selection)
}
pub fn set_selection<S: DynDataSource>(
self: &Rc<Self>,
selection: Option<Rc<S>>,
) -> Result<(), WlSeatError> {
self.set_selection_::<ClipboardTransfer, XClipboardTransfer, _>(
&self.selection,
selection,
TransferLocation::Clipboard,
)
}
pub fn get_selection(&self) -> Option<Rc<dyn DynDataSource>> {
self.selection.get()
}
pub fn may_modify_selection(&self, client: &Rc<Client>, serial: u64) -> bool {
if serial < self.selection_serial.get() {
return false;
}
self.keyboard_node.get().node_client_id() == Some(client.id)
}
pub fn may_modify_primary_selection(&self, client: &Rc<Client>, serial: Option<u64>) -> bool {
if let Some(serial) = serial
&& serial < self.primary_selection_serial.get()
{
return false;
}
self.keyboard_node.get().node_client_id() == Some(client.id)
|| self.pointer_node().and_then(|n| n.node_client_id()) == Some(client.id)
}
pub fn unset_primary_selection(self: &Rc<Self>) {
let _ = self.set_zwp_primary_selection(None, None);
}
pub fn set_zwp_primary_selection(
self: &Rc<Self>,
selection: Option<Rc<ZwpPrimarySelectionSourceV1>>,
serial: Option<u64>,
) -> Result<(), WlSeatError> {
if let Some(serial) = serial {
self.primary_selection_serial.set(serial);
}
self.set_primary_selection(selection)
}
pub fn set_primary_selection<S: DynDataSource>(
self: &Rc<Self>,
selection: Option<Rc<S>>,
) -> Result<(), WlSeatError> {
self.set_selection_::<PrimarySelectionTransfer, XPrimarySelectionTransfer, _>(
&self.primary_selection,
selection,
TransferLocation::PrimarySelection,
)
}
pub fn get_primary_selection(&self) -> Option<Rc<dyn DynDataSource>> {
self.primary_selection.get()
}
}

View file

@ -0,0 +1,108 @@
use {
super::WlSeatGlobal,
crate::tree::{
ChangeGroupAction, ContainerNode, ContainerSplit, toplevel_create_split,
toplevel_parent_container, toplevel_set_floating,
},
std::{ops::Deref, rc::Rc},
};
impl WlSeatGlobal {
pub fn kb_parent_container(&self) -> Option<Rc<ContainerNode>> {
if let Some(tl) = self.keyboard_node.get().node_toplevel() {
return toplevel_parent_container(&*tl);
}
None
}
pub fn get_mono(&self) -> Option<bool> {
self.kb_parent_container().map(|c| c.mono_child.is_some())
}
pub fn get_split(&self) -> Option<ContainerSplit> {
self.kb_parent_container().map(|c| c.split.get())
}
pub fn set_mono(&self, mono: bool) {
if let Some(tl) = self.keyboard_node.get().node_toplevel()
&& let Some(parent) = tl.tl_data().parent.get()
&& let Some(container) = parent.node_into_container()
{
let node = if mono { Some(tl.deref()) } else { None };
container.set_mono(node);
}
}
pub fn set_split(&self, axis: ContainerSplit) {
if let Some(c) = self.kb_parent_container() {
c.set_split(axis);
}
}
pub fn create_split(&self, axis: ContainerSplit) {
let tl = match self.keyboard_node.get().node_toplevel() {
Some(tl) => tl,
_ => return,
};
toplevel_create_split(&self.state, tl, axis);
}
pub fn toggle_tab(&self) {
if let Some(c) = self.kb_parent_container() {
c.change_group(ChangeGroupAction::ToggleTab);
}
}
pub fn make_group(&self, axis: ContainerSplit, ephemeral: bool) {
if let Some(c) = self.kb_parent_container() {
c.make_group(axis, ephemeral);
}
}
pub fn change_group_opposite(&self) {
if let Some(c) = self.kb_parent_container() {
c.change_group(ChangeGroupAction::Opposite);
}
}
pub fn equalize(&self, recursive: bool) {
if let Some(c) = self.kb_parent_container() {
if recursive {
c.equalize_recursive();
} else {
c.equalize();
}
}
}
pub fn move_tab(&self, right: bool) {
if let Some(c) = self.kb_parent_container() {
c.move_tab(right);
}
}
pub fn focus_parent(self: &Rc<Self>) {
if let Some(tl) = self.keyboard_node.get().node_toplevel()
&& let Some(parent) = tl.tl_data().parent.get()
&& let Some(tl) = parent.node_toplevel()
{
self.focus_node(tl);
self.maybe_schedule_warp_mouse_to_focus();
}
}
pub fn get_floating(self: &Rc<Self>) -> Option<bool> {
match self.keyboard_node.get().node_toplevel() {
Some(tl) => Some(tl.tl_data().parent_is_float.get()),
_ => None,
}
}
pub fn set_floating(self: &Rc<Self>, floating: bool) {
let tl = match self.keyboard_node.get().node_toplevel() {
Some(tl) => tl,
_ => return,
};
toplevel_set_floating(&self.state, tl, floating);
}
}

View file

@ -1,4 +1,5 @@
mod drag_destination; mod drag_destination;
mod layout;
mod tasks; mod tasks;
pub use drag_destination::default_tile_drag_destination; pub use drag_destination::default_tile_drag_destination;
@ -45,7 +46,7 @@ use {
cell::{Cell, RefCell}, cell::{Cell, RefCell},
fmt::{Debug, Formatter}, fmt::{Debug, Formatter},
mem, mem,
ops::{Deref, DerefMut, Sub}, ops::{Deref, DerefMut},
rc::Rc, rc::Rc,
}, },
}; };
@ -190,23 +191,6 @@ struct CursorState {
op: Option<SeatOp>, op: Option<SeatOp>,
} }
impl ContainerChild {
fn position_content(&self) {
let mut content = self.content.get();
let body = self.body.get();
let width = content.width();
let height = content.height();
// let x1 = body.x1() + (body.width() - width) / 2;
// let y1 = body.y1() + (body.height() - height) / 2;
let x1 = body.x1();
let y1 = body.y1();
content = Rect::new_sized_saturating(x1, y1, width, height);
// log::debug!("body: {:?}", body);
// log::debug!("content: {:?}", content);
self.content.set(content);
}
}
impl ContainerNode { impl ContainerNode {
pub fn new( pub fn new(
state: &Rc<State>, state: &Rc<State>,
@ -391,218 +375,6 @@ impl ContainerNode {
} }
} }
pub fn predict_child_body_size(&self) -> (i32, i32) {
if self.mono_child.is_some() {
let mb = self.mono_body.get();
return (mb.width(), mb.height());
}
let nc = self.num_children.get() as i32 + 1;
match self.split.get() {
ContainerSplit::Horizontal => {
let spacing = self.child_spacing();
let content_w = self.width.get().sub((nc - 1) * spacing).max(0);
(content_w / nc, self.height.get())
}
ContainerSplit::Vertical => {
let spacing = self.child_spacing();
let content_h = self.height.get().sub((nc - 1) * spacing).max(0);
(self.width.get(), content_h / nc)
}
}
}
pub fn on_spaces_changed(self: &Rc<Self>) {
self.update_content_size();
// log::info!("on_spaces_changed");
self.schedule_layout();
self.schedule_compute_render_positions();
}
pub fn on_colors_changed(self: &Rc<Self>) {
self.schedule_compute_render_positions();
}
fn damage(&self) {
let bw = if self.state.theme.sizes.gap.get() != 0 {
self.state.theme.sizes.border_width.get()
} else {
0
};
self.state.damage(Rect::new_sized_saturating(
self.abs_x1.get() - bw,
self.abs_y1.get() - bw,
self.width.get() + 2 * bw,
self.height.get() + 2 * bw,
));
}
fn child_spacing(&self) -> i32 {
let gap = self.state.theme.sizes.gap.get();
let bw = self.state.theme.sizes.border_width.get();
if gap == 0 { bw } else { gap + 2 * bw }
}
fn schedule_layout(self: &Rc<Self>) {
if self.state.layout_animations_requested.get() || self.state.layout_animations_active.get()
{
self.animate_next_layout.set(true);
}
if !self.layout_scheduled.replace(true) {
self.state.pending_container_layout.push(self.clone());
}
}
fn schedule_layout_immediate(self: &Rc<Self>) {
self.schedule_layout();
if self.toplevel_data.visible.get() {
self.damage();
}
}
fn all_children_match_body(&self) -> bool {
if let Some(mono) = self.mono_child.get() {
let body = self.mono_body.get();
let content = mono.content.get();
return content.width() == body.width() && content.height() == body.height();
}
for child in self.children.iter() {
let body = child.body.get();
let content = child.content.get();
if content.width() != body.width() || content.height() != body.height() {
return false;
}
}
true
}
fn perform_layout(self: &Rc<Self>) {
self.layout_scheduled.set(false);
if self.num_children.get() == 0 {
self.mono_transition_animation_pending.set(false);
return;
}
if let Some(child) = self.mono_child.get() {
self.perform_mono_layout(&child);
} else {
self.perform_split_layout();
}
self.state.tree_changed();
// log::info!("perform_layout");
self.schedule_compute_render_positions();
self.layout_complete.trigger();
if self.all_children_match_body() {
self.all_children_resized.trigger();
if self.toplevel_data.visible.get() {
self.damage();
}
}
self.mono_transition_animation_pending.set(false);
}
fn perform_mono_layout(self: &Rc<Self>, child: &ContainerChild) {
let mb = self.mono_body.get();
child
.node
.clone()
.tl_change_extents(&mb.move_(self.abs_x1.get(), self.abs_y1.get()));
self.mono_content
.set(child.content.get().at_point(mb.x1(), mb.y1()));
}
fn perform_split_layout(self: &Rc<Self>) {
let sum_factors = self.sum_factors.get();
let split = self.split.get();
let spacing = self.child_spacing();
let (content_size, other_content_size) = match split {
ContainerSplit::Horizontal => (self.content_width.get(), self.content_height.get()),
ContainerSplit::Vertical => (self.content_height.get(), self.content_width.get()),
};
let num_children = self.num_children.get();
if num_children == 0 {
return;
}
let mut pos = 0;
let mut remaining_content_size = content_size;
for child in self.children.iter() {
let factor = child.factor.get() / sum_factors;
child.factor.set(factor);
let mut body_size = (content_size as f64 * factor).round() as i32;
body_size = body_size.min(remaining_content_size);
remaining_content_size -= body_size;
let (x1, y1, width, height) = match split {
ContainerSplit::Horizontal => (pos, 0, body_size, other_content_size),
_ => (0, pos, other_content_size, body_size),
};
let body = Rect::new_sized_saturating(x1, y1, width, height);
child.body.set(body);
pos += body_size + spacing;
}
if remaining_content_size > 0 {
let size_per = remaining_content_size / num_children as i32;
let mut rem = remaining_content_size % num_children as i32;
pos = 0;
for child in self.children.iter() {
let mut body = child.body.get();
let mut add = size_per;
if rem > 0 {
rem -= 1;
add += 1;
}
let (x1, y1, width, height, size) = match split {
ContainerSplit::Horizontal => {
let width = body.width() + add;
(pos, 0, width, other_content_size, width)
}
_ => {
let height = body.height() + add;
(0, pos, other_content_size, height, height)
}
};
body = Rect::new_sized_saturating(x1, y1, width, height);
child.body.set(body);
pos += size + spacing;
}
}
self.sum_factors.set(1.0);
for child in self.children.iter() {
let body = child.body.get();
let body = body.move_(self.abs_x1.get(), self.abs_y1.get());
child.node.clone().tl_change_extents(&body);
child.position_content();
}
}
fn update_content_size(&self) {
let nc = self.num_children.get();
let spacing = self.child_spacing();
match self.split.get() {
ContainerSplit::Horizontal => {
let new_content_size = self.width.get().sub((nc - 1) as i32 * spacing).max(0);
self.content_width.set(new_content_size);
self.content_height.set(self.height.get());
}
ContainerSplit::Vertical => {
let new_content_size = self.height.get().sub((nc - 1) as i32 * spacing).max(0);
self.content_height.set(new_content_size);
self.content_width.set(self.width.get());
}
}
let tab_bar_height = if self.mono_child.is_some() {
// Tab bar sits above the window with a configurable gap.
let tbh = self.state.theme.sizes.tab_bar_height.get();
let gap = self.state.theme.sizes.tab_bar_gap.get();
tbh + gap
} else {
0
};
self.mono_body.set(Rect::new_sized_saturating(
0,
tab_bar_height,
self.width.get(),
(self.height.get() - tab_bar_height).max(0),
));
}
fn pointer_move( fn pointer_move(
self: &Rc<Self>, self: &Rc<Self>,
_seat: &Rc<WlSeatGlobal>, _seat: &Rc<WlSeatGlobal>,

View file

@ -0,0 +1,236 @@
use {
super::{ContainerChild, ContainerNode, ContainerSplit},
crate::rect::Rect,
std::{ops::Sub, rc::Rc},
};
impl ContainerChild {
pub(super) fn position_content(&self) {
let mut content = self.content.get();
let body = self.body.get();
let width = content.width();
let height = content.height();
// let x1 = body.x1() + (body.width() - width) / 2;
// let y1 = body.y1() + (body.height() - height) / 2;
let x1 = body.x1();
let y1 = body.y1();
content = Rect::new_sized_saturating(x1, y1, width, height);
// log::debug!("body: {:?}", body);
// log::debug!("content: {:?}", content);
self.content.set(content);
}
}
impl ContainerNode {
pub fn predict_child_body_size(&self) -> (i32, i32) {
if self.mono_child.is_some() {
let mb = self.mono_body.get();
return (mb.width(), mb.height());
}
let nc = self.num_children.get() as i32 + 1;
match self.split.get() {
ContainerSplit::Horizontal => {
let spacing = self.child_spacing();
let content_w = self.width.get().sub((nc - 1) * spacing).max(0);
(content_w / nc, self.height.get())
}
ContainerSplit::Vertical => {
let spacing = self.child_spacing();
let content_h = self.height.get().sub((nc - 1) * spacing).max(0);
(self.width.get(), content_h / nc)
}
}
}
pub fn on_spaces_changed(self: &Rc<Self>) {
self.update_content_size();
// log::info!("on_spaces_changed");
self.schedule_layout();
self.schedule_compute_render_positions();
}
pub fn on_colors_changed(self: &Rc<Self>) {
self.schedule_compute_render_positions();
}
pub(super) fn damage(&self) {
let bw = if self.state.theme.sizes.gap.get() != 0 {
self.state.theme.sizes.border_width.get()
} else {
0
};
self.state.damage(Rect::new_sized_saturating(
self.abs_x1.get() - bw,
self.abs_y1.get() - bw,
self.width.get() + 2 * bw,
self.height.get() + 2 * bw,
));
}
pub(super) fn child_spacing(&self) -> i32 {
let gap = self.state.theme.sizes.gap.get();
let bw = self.state.theme.sizes.border_width.get();
if gap == 0 { bw } else { gap + 2 * bw }
}
pub(super) fn schedule_layout(self: &Rc<Self>) {
if self.state.layout_animations_requested.get() || self.state.layout_animations_active.get()
{
self.animate_next_layout.set(true);
}
if !self.layout_scheduled.replace(true) {
self.state.pending_container_layout.push(self.clone());
}
}
pub(super) fn schedule_layout_immediate(self: &Rc<Self>) {
self.schedule_layout();
if self.toplevel_data.visible.get() {
self.damage();
}
}
pub(super) fn all_children_match_body(&self) -> bool {
if let Some(mono) = self.mono_child.get() {
let body = self.mono_body.get();
let content = mono.content.get();
return content.width() == body.width() && content.height() == body.height();
}
for child in self.children.iter() {
let body = child.body.get();
let content = child.content.get();
if content.width() != body.width() || content.height() != body.height() {
return false;
}
}
true
}
pub(super) fn perform_layout(self: &Rc<Self>) {
self.layout_scheduled.set(false);
if self.num_children.get() == 0 {
self.mono_transition_animation_pending.set(false);
return;
}
if let Some(child) = self.mono_child.get() {
self.perform_mono_layout(&child);
} else {
self.perform_split_layout();
}
self.state.tree_changed();
// log::info!("perform_layout");
self.schedule_compute_render_positions();
self.layout_complete.trigger();
if self.all_children_match_body() {
self.all_children_resized.trigger();
if self.toplevel_data.visible.get() {
self.damage();
}
}
self.mono_transition_animation_pending.set(false);
}
fn perform_mono_layout(self: &Rc<Self>, child: &ContainerChild) {
let mb = self.mono_body.get();
child
.node
.clone()
.tl_change_extents(&mb.move_(self.abs_x1.get(), self.abs_y1.get()));
self.mono_content
.set(child.content.get().at_point(mb.x1(), mb.y1()));
}
fn perform_split_layout(self: &Rc<Self>) {
let sum_factors = self.sum_factors.get();
let split = self.split.get();
let spacing = self.child_spacing();
let (content_size, other_content_size) = match split {
ContainerSplit::Horizontal => (self.content_width.get(), self.content_height.get()),
ContainerSplit::Vertical => (self.content_height.get(), self.content_width.get()),
};
let num_children = self.num_children.get();
if num_children == 0 {
return;
}
let mut pos = 0;
let mut remaining_content_size = content_size;
for child in self.children.iter() {
let factor = child.factor.get() / sum_factors;
child.factor.set(factor);
let mut body_size = (content_size as f64 * factor).round() as i32;
body_size = body_size.min(remaining_content_size);
remaining_content_size -= body_size;
let (x1, y1, width, height) = match split {
ContainerSplit::Horizontal => (pos, 0, body_size, other_content_size),
_ => (0, pos, other_content_size, body_size),
};
let body = Rect::new_sized_saturating(x1, y1, width, height);
child.body.set(body);
pos += body_size + spacing;
}
if remaining_content_size > 0 {
let size_per = remaining_content_size / num_children as i32;
let mut rem = remaining_content_size % num_children as i32;
pos = 0;
for child in self.children.iter() {
let mut body = child.body.get();
let mut add = size_per;
if rem > 0 {
rem -= 1;
add += 1;
}
let (x1, y1, width, height, size) = match split {
ContainerSplit::Horizontal => {
let width = body.width() + add;
(pos, 0, width, other_content_size, width)
}
_ => {
let height = body.height() + add;
(0, pos, other_content_size, height, height)
}
};
body = Rect::new_sized_saturating(x1, y1, width, height);
child.body.set(body);
pos += size + spacing;
}
}
self.sum_factors.set(1.0);
for child in self.children.iter() {
let body = child.body.get();
let body = body.move_(self.abs_x1.get(), self.abs_y1.get());
child.node.clone().tl_change_extents(&body);
child.position_content();
}
}
pub(super) fn update_content_size(&self) {
let nc = self.num_children.get();
let spacing = self.child_spacing();
match self.split.get() {
ContainerSplit::Horizontal => {
let new_content_size = self.width.get().sub((nc - 1) as i32 * spacing).max(0);
self.content_width.set(new_content_size);
self.content_height.set(self.height.get());
}
ContainerSplit::Vertical => {
let new_content_size = self.height.get().sub((nc - 1) as i32 * spacing).max(0);
self.content_height.set(new_content_size);
self.content_width.set(self.width.get());
}
}
let tab_bar_height = if self.mono_child.is_some() {
// Tab bar sits above the window with a configurable gap.
let tbh = self.state.theme.sizes.tab_bar_height.get();
let gap = self.state.theme.sizes.tab_bar_gap.get();
tbh + gap
} else {
0
};
self.mono_body.set(Rect::new_sized_saturating(
0,
tab_bar_height,
self.width.get(),
(self.height.get() - tab_bar_height).max(0),
));
}
}

View file

@ -1,5 +1,7 @@
mod captures;
mod policy; mod policy;
mod render_data; mod render_data;
mod workspaces;
#[allow(unused_imports)] #[allow(unused_imports)]
pub use { pub use {
@ -17,17 +19,14 @@ use {
HardwareCursor, Mode, transaction::BackendConnectorTransactionError, HardwareCursor, Mode, transaction::BackendConnectorTransactionError,
}, },
client::ClientId, client::ClientId,
cmm::cmm_description::ColorDescription,
cursor::KnownCursor, cursor::KnownCursor,
fixed::Fixed, fixed::Fixed,
gfx_api::{AcquireSync, BufferResv, GfxTexture, ReleaseSync},
ifs::{ ifs::{
ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1, ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
jay_output::JayOutput, jay_output::JayOutput,
wl_buffer::WlBufferStorage,
wl_output::{BlendSpace, WlOutputGlobal}, wl_output::{BlendSpace, WlOutputGlobal},
wl_seat::{ wl_seat::{
BTN_LEFT, NodeSeatState, SeatId, WlSeatGlobal, collect_kb_foci2, BTN_LEFT, NodeSeatState, SeatId, WlSeatGlobal,
tablet::{TabletTool, TabletToolChanges, TabletToolId}, tablet::{TabletTool, TabletToolChanges, TabletToolId},
wl_pointer::PendingScroll, wl_pointer::PendingScroll,
}, },
@ -68,8 +67,7 @@ use {
copyhashmap::CopyHashMap, copyhashmap::CopyHashMap,
errorfmt::ErrorFmt, errorfmt::ErrorFmt,
event_listener::{EventSource, LazyEventSource}, event_listener::{EventSource, LazyEventSource},
hash_map_ext::HashMapExt, linkedlist::LinkedList,
linkedlist::{LinkedList, NodeRef},
on_drop_event::OnDropEvent, on_drop_event::OnDropEvent,
scroller::Scroller, scroller::Scroller,
}, },
@ -77,9 +75,7 @@ use {
ExtImageCopyCaptureSessionV1Id, JayOutputId, ZwlrScreencopyFrameV1Id, ExtImageCopyCaptureSessionV1Id, JayOutputId, ZwlrScreencopyFrameV1Id,
}, },
}, },
ahash::AHashMap,
numeric_sort::cmp, numeric_sort::cmp,
smallvec::SmallVec,
std::{ std::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
fmt::{Debug, Formatter}, fmt::{Debug, Formatter},
@ -280,162 +276,6 @@ impl OutputNode {
} }
} }
pub fn captures_changed(&self) {
for ws in self.workspaces.iter() {
ws.update_has_captures();
}
}
pub fn perform_screencopies(
&self,
tex: &Rc<dyn GfxTexture>,
cd: &Rc<ColorDescription>,
resv: Option<&Rc<dyn BufferResv>>,
acquire_sync: &AcquireSync,
release_sync: ReleaseSync,
render_hardware_cursor: bool,
x_off: i32,
y_off: i32,
size: Option<(i32, i32)>,
) {
if let Some(workspace) = self.workspace.get() {
if !workspace.may_capture.get() {
return;
}
}
self.perform_wlr_screencopies(
tex,
cd,
resv,
acquire_sync,
release_sync,
render_hardware_cursor,
x_off,
y_off,
size,
);
for sc in self.ext_copy_sessions.lock().values() {
sc.copy_texture(
self,
tex,
cd,
resv,
acquire_sync,
release_sync,
render_hardware_cursor,
x_off,
y_off,
size,
);
}
}
pub fn perform_wlr_screencopies(
&self,
tex: &Rc<dyn GfxTexture>,
cd: &Rc<ColorDescription>,
resv: Option<&Rc<dyn BufferResv>>,
acquire_sync: &AcquireSync,
release_sync: ReleaseSync,
render_hardware_cursors: bool,
x_off: i32,
y_off: i32,
size: Option<(i32, i32)>,
) {
if self.screencopies.is_empty() {
return;
}
let now = self.state.now();
for capture in self.screencopies.lock().drain_values() {
let wl_buffer = match capture.buffer.take() {
Some(b) => b,
_ => {
log::warn!("Capture frame is pending but has no buffer attached");
capture.send_failed();
continue;
}
};
if wl_buffer.destroyed() {
capture.send_failed();
continue;
}
let mut ready = true;
if let Some(storage) = wl_buffer.storage.borrow_mut().deref() {
match storage {
WlBufferStorage::Shm { mem, stride, .. } => {
let res = self.state.perform_shm_screencopy(
tex,
cd,
acquire_sync,
self.global.pos.get(),
x_off,
y_off,
size,
&capture,
mem,
*stride,
wl_buffer.format,
self.global.persistent.transform.get(),
self.global.persistent.scale.get(),
);
match res {
Ok(p) => {
ready = p.is_none();
capture.pending.set(p);
}
Err(e) => {
log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e));
capture.send_failed();
continue;
}
}
}
WlBufferStorage::Dmabuf { fb, .. } => {
let fb = match fb {
Some(fb) => fb,
_ => {
log::warn!("Capture buffer has no framebuffer");
capture.send_failed();
continue;
}
};
let res = self.state.perform_screencopy(
tex,
resv,
acquire_sync,
release_sync,
cd,
&fb,
AcquireSync::Implicit,
ReleaseSync::Implicit,
self.global.persistent.transform.get(),
self.state.color_manager.srgb_gamma22(),
self.global.pos.get(),
render_hardware_cursors,
x_off - capture.rect.x1(),
y_off - capture.rect.y1(),
size,
self.global.persistent.transform.get(),
self.global.persistent.scale.get(),
);
if let Err(e) = res {
log::warn!("Could not perform screencopy: {}", ErrorFmt(e));
capture.send_failed();
continue;
}
}
}
}
if capture.with_damage.get() {
capture.send_damage();
}
if ready {
capture.send_ready(now.0.tv_sec as _, now.0.tv_nsec as _);
}
}
self.captures_changed();
}
pub fn clear(&self) { pub fn clear(&self) {
self.global.clear(); self.global.clear();
self.workspace.set(None); self.workspace.set(None);
@ -648,117 +488,6 @@ impl OutputNode {
} }
} }
pub fn ensure_workspace(self: &Rc<Self>) -> Rc<WorkspaceNode> {
if let Some(ws) = self.workspace.get() {
if !ws.is_dummy {
return ws;
}
}
self.generate_workspace()
}
pub fn generate_workspace(self: &Rc<Self>) -> Rc<WorkspaceNode> {
let name = 'name: {
for i in 1.. {
let name = i.to_string();
if self.find_workspace(&name).is_none() {
break 'name name;
}
}
unreachable!();
};
self.create_workspace(&name)
}
pub fn find_workspace(&self, name: &str) -> Option<Rc<WorkspaceNode>> {
self.workspaces
.iter()
.find(|ws| ws.name.as_str() == name)
.map(|ws| (*ws).clone())
}
pub fn show_workspace(&self, ws: &Rc<WorkspaceNode>) -> bool {
let mut seats = SmallVec::new();
if let Some(old) = self.workspace.set(Some(ws.clone())) {
if old.id == ws.id {
return false;
}
collect_kb_foci2(old.clone(), &mut seats);
for pinned in self.pinned.iter() {
pinned.deref().clone().set_workspace(ws, false);
}
if old.is_empty() {
for jw in old.jay_workspaces.lock().values() {
jw.send_destroyed();
jw.workspace.set(None);
}
for wh in old.ext_workspaces.lock().values() {
wh.handle_destroyed();
}
old.clear();
self.state.workspaces.remove(&old.id);
} else {
old.set_visible(false);
old.flush_jay_workspaces();
}
}
self.update_visible();
self.update_presentation_type();
if let Some(fs) = ws.fullscreen.get() {
fs.tl_change_extents(&self.global.pos.get());
}
ws.change_extents(&self.workspace_rect.get());
for seat in seats {
ws.clone().node_do_focus(&seat, Direction::Unspecified);
}
if self.node_visible() {
self.state.damage(self.global.pos.get());
}
true
}
pub fn find_workspace_insertion_point(&self, name: &str) -> Option<NodeRef<Rc<WorkspaceNode>>> {
if self.state.workspace_display_order.get() == WorkspaceDisplayOrder::Sorted {
for existing_ws in self.workspaces.iter() {
if cmp(name, &existing_ws.name) == std::cmp::Ordering::Less {
return Some(existing_ws);
}
}
}
None
}
pub fn create_workspace(self: &Rc<Self>, name: &str) -> Rc<WorkspaceNode> {
let ws = WorkspaceNode::new(self, name, false);
ws.opt.set(Some(ws.clone()));
ws.update_has_captures();
let link = if let Some(before) = self.find_workspace_insertion_point(name) {
before.prepend(ws.clone())
} else {
self.workspaces.add_last(ws.clone())
};
*ws.output_link.borrow_mut() = Some(link);
self.state.workspaces.set(ws.id, ws.clone());
if self.workspace.is_none() {
self.show_workspace(&ws);
}
let mut clients_to_kill = AHashMap::new();
for watcher in self.state.workspace_watchers.lock().values() {
if let Err(e) = watcher.send_workspace(&ws) {
clients_to_kill.insert(watcher.client.id, (watcher.client.clone(), e));
}
}
for (client, e) in clients_to_kill.values() {
client.error(e);
}
self.state.workspace_managers.announce_workspace(self, &ws);
self.state
.workspace_managers
.update_workspace_coordinates(self);
self.schedule_update_render_data();
ws
}
pub fn update_rects(self: &Rc<Self>) { pub fn update_rects(self: &Rc<Self>) {
let rect = self.global.pos.get(); let rect = self.global.pos.get();
let bh = self.state.theme.sizes.bar_height(); let bh = self.state.theme.sizes.bar_height();

168
src/tree/output/captures.rs Normal file
View file

@ -0,0 +1,168 @@
use {
super::OutputNode,
crate::{
cmm::cmm_description::ColorDescription,
gfx_api::{AcquireSync, BufferResv, GfxTexture, ReleaseSync},
ifs::wl_buffer::WlBufferStorage,
utils::{errorfmt::ErrorFmt, hash_map_ext::HashMapExt},
},
std::{ops::Deref, rc::Rc},
};
impl OutputNode {
pub fn captures_changed(&self) {
for ws in self.workspaces.iter() {
ws.update_has_captures();
}
}
pub fn perform_screencopies(
&self,
tex: &Rc<dyn GfxTexture>,
cd: &Rc<ColorDescription>,
resv: Option<&Rc<dyn BufferResv>>,
acquire_sync: &AcquireSync,
release_sync: ReleaseSync,
render_hardware_cursor: bool,
x_off: i32,
y_off: i32,
size: Option<(i32, i32)>,
) {
if let Some(workspace) = self.workspace.get() {
if !workspace.may_capture.get() {
return;
}
}
self.perform_wlr_screencopies(
tex,
cd,
resv,
acquire_sync,
release_sync,
render_hardware_cursor,
x_off,
y_off,
size,
);
for sc in self.ext_copy_sessions.lock().values() {
sc.copy_texture(
self,
tex,
cd,
resv,
acquire_sync,
release_sync,
render_hardware_cursor,
x_off,
y_off,
size,
);
}
}
pub fn perform_wlr_screencopies(
&self,
tex: &Rc<dyn GfxTexture>,
cd: &Rc<ColorDescription>,
resv: Option<&Rc<dyn BufferResv>>,
acquire_sync: &AcquireSync,
release_sync: ReleaseSync,
render_hardware_cursors: bool,
x_off: i32,
y_off: i32,
size: Option<(i32, i32)>,
) {
if self.screencopies.is_empty() {
return;
}
let now = self.state.now();
for capture in self.screencopies.lock().drain_values() {
let wl_buffer = match capture.buffer.take() {
Some(b) => b,
_ => {
log::warn!("Capture frame is pending but has no buffer attached");
capture.send_failed();
continue;
}
};
if wl_buffer.destroyed() {
capture.send_failed();
continue;
}
let mut ready = true;
if let Some(storage) = wl_buffer.storage.borrow_mut().deref() {
match storage {
WlBufferStorage::Shm { mem, stride, .. } => {
let res = self.state.perform_shm_screencopy(
tex,
cd,
acquire_sync,
self.global.pos.get(),
x_off,
y_off,
size,
&capture,
mem,
*stride,
wl_buffer.format,
self.global.persistent.transform.get(),
self.global.persistent.scale.get(),
);
match res {
Ok(p) => {
ready = p.is_none();
capture.pending.set(p);
}
Err(e) => {
log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e));
capture.send_failed();
continue;
}
}
}
WlBufferStorage::Dmabuf { fb, .. } => {
let fb = match fb {
Some(fb) => fb,
_ => {
log::warn!("Capture buffer has no framebuffer");
capture.send_failed();
continue;
}
};
let res = self.state.perform_screencopy(
tex,
resv,
acquire_sync,
release_sync,
cd,
&fb,
AcquireSync::Implicit,
ReleaseSync::Implicit,
self.global.persistent.transform.get(),
self.state.color_manager.srgb_gamma22(),
self.global.pos.get(),
render_hardware_cursors,
x_off - capture.rect.x1(),
y_off - capture.rect.y1(),
size,
self.global.persistent.transform.get(),
self.global.persistent.scale.get(),
);
if let Err(e) = res {
log::warn!("Could not perform screencopy: {}", ErrorFmt(e));
capture.send_failed();
continue;
}
}
}
}
if capture.with_damage.get() {
capture.send_damage();
}
if ready {
capture.send_ready(now.0.tv_sec as _, now.0.tv_nsec as _);
}
}
self.captures_changed();
}
}

View file

@ -0,0 +1,125 @@
use {
super::OutputNode,
crate::{
ifs::wl_seat::collect_kb_foci2,
tree::{Direction, Node, WorkspaceDisplayOrder, WorkspaceNode},
utils::linkedlist::NodeRef,
},
ahash::AHashMap,
numeric_sort::cmp,
smallvec::SmallVec,
std::{ops::Deref, rc::Rc},
};
impl OutputNode {
pub fn ensure_workspace(self: &Rc<Self>) -> Rc<WorkspaceNode> {
if let Some(ws) = self.workspace.get() {
if !ws.is_dummy {
return ws;
}
}
self.generate_workspace()
}
pub fn generate_workspace(self: &Rc<Self>) -> Rc<WorkspaceNode> {
let name = 'name: {
for i in 1.. {
let name = i.to_string();
if self.find_workspace(&name).is_none() {
break 'name name;
}
}
unreachable!();
};
self.create_workspace(&name)
}
pub fn find_workspace(&self, name: &str) -> Option<Rc<WorkspaceNode>> {
self.workspaces
.iter()
.find(|ws| ws.name.as_str() == name)
.map(|ws| (*ws).clone())
}
pub fn show_workspace(&self, ws: &Rc<WorkspaceNode>) -> bool {
let mut seats = SmallVec::new();
if let Some(old) = self.workspace.set(Some(ws.clone())) {
if old.id == ws.id {
return false;
}
collect_kb_foci2(old.clone(), &mut seats);
for pinned in self.pinned.iter() {
pinned.deref().clone().set_workspace(ws, false);
}
if old.is_empty() {
for jw in old.jay_workspaces.lock().values() {
jw.send_destroyed();
jw.workspace.set(None);
}
for wh in old.ext_workspaces.lock().values() {
wh.handle_destroyed();
}
old.clear();
self.state.workspaces.remove(&old.id);
} else {
old.set_visible(false);
old.flush_jay_workspaces();
}
}
self.update_visible();
self.update_presentation_type();
if let Some(fs) = ws.fullscreen.get() {
fs.tl_change_extents(&self.global.pos.get());
}
ws.change_extents(&self.workspace_rect.get());
for seat in seats {
ws.clone().node_do_focus(&seat, Direction::Unspecified);
}
if self.node_visible() {
self.state.damage(self.global.pos.get());
}
true
}
pub fn find_workspace_insertion_point(&self, name: &str) -> Option<NodeRef<Rc<WorkspaceNode>>> {
if self.state.workspace_display_order.get() == WorkspaceDisplayOrder::Sorted {
for existing_ws in self.workspaces.iter() {
if cmp(name, &existing_ws.name) == std::cmp::Ordering::Less {
return Some(existing_ws);
}
}
}
None
}
pub fn create_workspace(self: &Rc<Self>, name: &str) -> Rc<WorkspaceNode> {
let ws = WorkspaceNode::new(self, name, false);
ws.opt.set(Some(ws.clone()));
ws.update_has_captures();
let link = if let Some(before) = self.find_workspace_insertion_point(name) {
before.prepend(ws.clone())
} else {
self.workspaces.add_last(ws.clone())
};
*ws.output_link.borrow_mut() = Some(link);
self.state.workspaces.set(ws.id, ws.clone());
if self.workspace.is_none() {
self.show_workspace(&ws);
}
let mut clients_to_kill = AHashMap::new();
for watcher in self.state.workspace_watchers.lock().values() {
if let Err(e) = watcher.send_workspace(&ws) {
clients_to_kill.insert(watcher.client.id, (watcher.client.clone(), e));
}
}
for (client, e) in clients_to_kill.values() {
client.error(e);
}
self.state.workspace_managers.announce_workspace(self, &ws);
self.state
.workspace_managers
.update_workspace_coordinates(self);
self.schedule_update_render_data();
ws
}
}

View file

@ -1,6 +1,7 @@
#![allow(clippy::await_holding_refcell_ref)] // all borrows are to data that is only used by this task #![allow(clippy::await_holding_refcell_ref)] // all borrows are to data that is only used by this task
mod selection; mod selection;
mod properties;
mod transfer; mod transfer;
use selection::SelectionData; use selection::SelectionData;
@ -9,7 +10,6 @@ use {
crate::{ crate::{
async_engine::SpawnedFuture, async_engine::SpawnedFuture,
client::Client, client::Client,
criteria::tlm::{TL_CHANGED_CLASS_INST, TL_CHANGED_ROLE},
ifs::{ ifs::{
data_transfer::{ data_transfer::{
DataOfferId, DataSourceId, DynDataOffer, DynDataSource, TransferLocation, TransferVtable, DataOfferId, DataSourceId, DynDataOffer, DynDataSource, TransferLocation, TransferVtable,
@ -22,7 +22,7 @@ use {
wl_seat::{SeatId, WlSeatGlobal}, wl_seat::{SeatId, WlSeatGlobal},
wl_surface::{ wl_surface::{
WlSurface, WlSurface,
x_surface::xwindow::{XInputModel, Xwindow, XwindowData}, x_surface::xwindow::{Xwindow, XwindowData},
}, },
}, },
rect::Rect, rect::Rect,
@ -46,7 +46,7 @@ use {
ChangeProperty, ChangeWindowAttributes, ClientMessage, CompositeRedirectSubwindows, ChangeProperty, ChangeWindowAttributes, ClientMessage, CompositeRedirectSubwindows,
ConfigureNotify, ConfigureRequest, ConfigureWindow, ConfigureWindowValues, ConfigureNotify, ConfigureRequest, ConfigureWindow, ConfigureWindowValues,
ConvertSelection, CreateNotify, CreateWindow, CreateWindowValues, DestroyNotify, ConvertSelection, CreateNotify, CreateWindow, CreateWindowValues, DestroyNotify,
Extension, FocusIn, GetAtomName, GetGeometry, InternAtom, KillClient, MapNotify, Extension, FocusIn, GetGeometry, InternAtom, KillClient, MapNotify,
MapRequest, MapWindow, PropertyNotify, ResClientIdSpec, ResQueryClientIds, MapRequest, MapWindow, PropertyNotify, ResClientIdSpec, ResQueryClientIds,
SelectSelectionInput, SelectionNotify, SelectionRequest, SetInputFocus, SelectSelectionInput, SelectionNotify, SelectionRequest, SetInputFocus,
SetSelectionOwner, UnmapNotify, XfixesQueryVersion, XfixesSelectionNotify, SetSelectionOwner, UnmapNotify, XfixesQueryVersion, XfixesSelectionNotify,
@ -56,14 +56,13 @@ use {
consts::{ consts::{
_NET_WM_STATE_ADD, _NET_WM_STATE_REMOVE, _NET_WM_STATE_TOGGLE, ATOM_ATOM, _NET_WM_STATE_ADD, _NET_WM_STATE_REMOVE, _NET_WM_STATE_TOGGLE, ATOM_ATOM,
ATOM_NONE, ATOM_STRING, ATOM_WINDOW, ATOM_WM_CLASS, ATOM_WM_NAME, ATOM_NONE, ATOM_STRING, ATOM_WINDOW, ATOM_WM_CLASS, ATOM_WM_NAME,
ATOM_WM_SIZE_HINTS, ATOM_WM_TRANSIENT_FOR, COMPOSITE_REDIRECT_MANUAL, ATOM_WM_TRANSIENT_FOR, COMPOSITE_REDIRECT_MANUAL, CONFIG_WINDOW_HEIGHT,
CONFIG_WINDOW_HEIGHT, CONFIG_WINDOW_WIDTH, CONFIG_WINDOW_X, CONFIG_WINDOW_Y, CONFIG_WINDOW_WIDTH, CONFIG_WINDOW_X, CONFIG_WINDOW_Y, EVENT_MASK_FOCUS_CHANGE,
EVENT_MASK_FOCUS_CHANGE, EVENT_MASK_PROPERTY_CHANGE, EVENT_MASK_PROPERTY_CHANGE, EVENT_MASK_SUBSTRUCTURE_NOTIFY,
EVENT_MASK_SUBSTRUCTURE_NOTIFY, EVENT_MASK_SUBSTRUCTURE_REDIRECT, EVENT_MASK_SUBSTRUCTURE_REDIRECT, ICCCM_WM_STATE_ICONIC, ICCCM_WM_STATE_NORMAL,
ICCCM_WM_HINT_INPUT, ICCCM_WM_STATE_ICONIC, ICCCM_WM_STATE_NORMAL, ICCCM_WM_STATE_WITHDRAWN, INPUT_FOCUS_POINTER_ROOT, NOTIFY_DETAIL_POINTER,
ICCCM_WM_STATE_WITHDRAWN, INPUT_FOCUS_POINTER_ROOT, MWM_HINTS_DECORATIONS_FIELD, NOTIFY_MODE_GRAB, NOTIFY_MODE_UNGRAB, PROP_MODE_REPLACE,
MWM_HINTS_FLAGS_FIELD, NOTIFY_DETAIL_POINTER, NOTIFY_MODE_GRAB, NOTIFY_MODE_UNGRAB, RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID, SELECTION_CLIENT_CLOSE_MASK,
PROP_MODE_REPLACE, RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID, SELECTION_CLIENT_CLOSE_MASK,
SELECTION_WINDOW_DESTROY_MASK, SET_SELECTION_OWNER_MASK, STACK_MODE_ABOVE, SELECTION_WINDOW_DESTROY_MASK, SET_SELECTION_OWNER_MASK, STACK_MODE_ABOVE,
STACK_MODE_BELOW, WINDOW_CLASS_INPUT_OUTPUT, STACK_MODE_BELOW, WINDOW_CLASS_INPUT_OUTPUT,
}, },
@ -71,7 +70,7 @@ use {
xwayland::{XWaylandError, XWaylandEvent}, xwayland::{XWaylandError, XWaylandEvent},
}, },
ahash::{AHashMap, AHashSet}, ahash::{AHashMap, AHashSet},
bstr::{ByteSlice, ByteVec}, bstr::ByteSlice,
futures_util::{FutureExt, select}, futures_util::{FutureExt, select},
smallvec::SmallVec, smallvec::SmallVec,
std::{ std::{
@ -838,398 +837,6 @@ impl Wm {
} }
} }
fn compute_input_model(&self, data: &Rc<XwindowData>) {
let has_wm_take_focus = data.info.protocols.contains(&self.atoms.WM_TAKE_FOCUS);
let accepts_input = data.info.icccm_hints.input.get();
let model = match (accepts_input, has_wm_take_focus) {
(false, false) => XInputModel::None,
(true, false) => XInputModel::Passive,
(true, true) => XInputModel::Local,
(false, true) => XInputModel::Global,
};
data.info.input_model.set(model);
}
async fn load_window_wm_window_role(&self, data: &Rc<XwindowData>) {
let property_changed = || {
if let Some(window) = data.window.get() {
window.toplevel_data.property_changed(TL_CHANGED_ROLE);
}
};
let mut buf = vec![];
match self
.c
.get_property::<u8>(data.window_id, self.atoms.WM_WINDOW_ROLE, 0, &mut buf)
.await
{
Ok(ty) if ty == ATOM_STRING => {}
Ok(ty) if ty == self.atoms.UTF8_STRING => {}
Ok(ty) => {
self.unexpected_type(data.window_id, "WM_WINDOW_ROLE", ty)
.await;
return;
}
Err(XconError::PropertyUnavailable) => {
data.info.role.borrow_mut().take();
property_changed();
return;
}
Err(e) => {
log::error!(
"Could not retrieve WM_WINDOW_ROLE property: {}",
ErrorFmt(e)
);
return;
}
}
// log::info!("{} role {}", data.window_id, buf.as_bstr());
*data.info.role.borrow_mut() = Some(buf.into_string_lossy());
property_changed();
}
async fn load_window_wm_class(&self, data: &Rc<XwindowData>) {
let mut buf = vec![];
let property_changed = || {
if let Some(window) = data.window.get() {
let class = data.info.class.borrow();
for handle in window.toplevel_data.manager_handles.lock().values() {
handle.send_app_id(class.as_deref().unwrap_or_default());
handle.send_done();
}
window.toplevel_data.property_changed(TL_CHANGED_CLASS_INST);
}
};
match self
.c
.get_property::<u8>(data.window_id, ATOM_WM_CLASS, 0, &mut buf)
.await
{
Ok(ty) if ty == ATOM_STRING => {}
Ok(ty) if ty == self.atoms.UTF8_STRING => {}
Ok(ty) => {
self.unexpected_type(data.window_id, "WM_CLASS", ty).await;
return;
}
Err(XconError::PropertyUnavailable) => {
data.info.instance.borrow_mut().take();
data.info.class.borrow_mut().take();
property_changed();
return;
}
Err(e) => {
log::error!("Could not retrieve WM_CLASS property: {}", ErrorFmt(e));
return;
}
}
let mut iter = buf.split(|c| *c == 0);
let mut map = || Some(iter.next().unwrap_or(&[]).to_str_lossy().into_owned());
*data.info.instance.borrow_mut() = map();
*data.info.class.borrow_mut() = map();
property_changed();
}
async fn load_window_wm_name2(&self, data: &Rc<XwindowData>, prop: u32, name: &str) {
let mut buf = vec![];
match self
.c
.get_property::<u8>(data.window_id, prop, 0, &mut buf)
.await
{
Ok(ty) if ty == ATOM_STRING && data.info.utf8_title.get() => return,
Ok(ty) if ty == ATOM_STRING => {}
Ok(ty) if ty == self.atoms.COMPOUND_TEXT => return, // used by java.
Ok(ty) if ty == self.atoms.UTF8_STRING => {
data.info.utf8_title.set(true);
}
Ok(ty) => {
self.unexpected_type(data.window_id, name, ty).await;
return;
}
Err(XconError::PropertyUnavailable) => return,
Err(e) => {
log::error!("Could not retrieve {} property: {}", name, ErrorFmt(e));
return;
}
}
let title = buf.as_bstr().to_string();
if let Some(window) = data.window.get() {
window.toplevel_data.set_title(&title);
window.tl_title_changed();
}
*data.info.title.borrow_mut() = Some(title);
data.title_changed();
}
async fn unexpected_type(&self, window: u32, prop: &str, ty: u32) {
let mut ty_name = "unknown".as_bytes().as_bstr();
let res = self.c.call(&GetAtomName { atom: ty }).await;
if let Ok(res) = &res {
ty_name = res.get().name;
}
log::error!(
"Property {} of window {} has unexpected type {} ({})",
prop,
window,
ty_name,
ty
);
}
async fn load_window_wm_name(&self, data: &Rc<XwindowData>) {
self.load_window_wm_name2(data, ATOM_WM_NAME, "WM_NAME")
.await;
}
async fn load_window_net_wm_name(&self, data: &Rc<XwindowData>) {
self.load_window_wm_name2(data, self.atoms._NET_WM_NAME, "_NET_WM_NAME")
.await;
}
async fn load_window_wm_transient_for(&self, data: &Rc<XwindowData>) {
let mut buf = vec![];
if let Err(e) = self
.c
.get_property::<u32>(data.window_id, ATOM_WM_TRANSIENT_FOR, ATOM_WINDOW, &mut buf)
.await
{
if not_matches!(e, XconError::PropertyUnavailable) {
log::error!(
"Could not retrieve WM_TRANSIENT_FOR property: {}",
ErrorFmt(e)
);
}
}
if let Some(old) = data.parent.take() {
old.children.remove(&data.window_id);
}
if let Some(w) = buf.first()
&& let Some(w) = self.windows.get(w)
{
if data.is_ancestor_of(w.clone()) {
log::error!("Cannot set WM_TRANSIENT_FOR because it would create a cycle");
return;
}
w.children.set(data.window_id, data.clone());
data.parent.set(Some(w.clone()));
}
}
async fn load_window_wm_protocols(&self, data: &Rc<XwindowData>) {
let mut buf = vec![];
if let Err(e) = self
.c
.get_property::<u32>(data.window_id, self.atoms.WM_PROTOCOLS, ATOM_ATOM, &mut buf)
.await
{
if not_matches!(e, XconError::PropertyUnavailable) {
log::error!("Could not retrieve WM_PROTOCOLS property: {}", ErrorFmt(e));
}
return;
}
data.info.protocols.clear();
data.info
.protocols
.lock()
.extend(buf.iter().copied().map(|v| (v, ())));
self.compute_input_model(data);
}
async fn load_window_wm_hints(&self, data: &Rc<XwindowData>) {
let mut buf = vec![];
if let Err(e) = self
.c
.get_property::<u32>(data.window_id, self.atoms.WM_HINTS, 0, &mut buf)
.await
{
if not_matches!(e, XconError::PropertyUnavailable) {
log::error!("Could not retrieve WM_HINTS property: {}", ErrorFmt(e));
}
data.info.icccm_hints.input.set(true);
self.compute_input_model(data);
return;
}
let mut values = [0; 9];
let len = values.len().min(buf.len());
values[..len].copy_from_slice(&buf[..len]);
data.info.icccm_hints.flags.set(values[0] as i32);
data.info.icccm_hints.input.set(values[1] != 0);
data.info.icccm_hints.initial_state.set(values[2] as i32);
data.info.icccm_hints.icon_pixmap.set(values[3]);
data.info.icccm_hints.icon_window.set(values[4]);
data.info.icccm_hints.icon_x.set(values[5] as i32);
data.info.icccm_hints.icon_y.set(values[6] as i32);
data.info.icccm_hints.icon_mask.set(values[7]);
data.info.icccm_hints.window_group.set(values[8]);
if data
.info
.icccm_hints
.flags
.get()
.not_contains(ICCCM_WM_HINT_INPUT)
{
data.info.icccm_hints.input.set(true);
}
self.compute_input_model(data);
}
async fn load_window_wm_normal_hints(&self, data: &Rc<XwindowData>) {
let mut buf = vec![];
if let Err(e) = self
.c
.get_property::<u32>(
data.window_id,
self.atoms.WM_NORMAL_HINTS,
ATOM_WM_SIZE_HINTS,
&mut buf,
)
.await
{
if not_matches!(e, XconError::PropertyUnavailable) {
log::error!(
"Could not retrieve WM_NORMAL_HINTS property: {}",
ErrorFmt(e)
);
}
return;
}
let mut values = [0; 18];
let len = values.len().min(buf.len());
values[..len].copy_from_slice(&buf[..len]);
data.info.normal_hints.flags.set(values[0]);
data.info.normal_hints.x.set(values[1] as i32);
data.info.normal_hints.y.set(values[2] as i32);
data.info.normal_hints.width.set(values[3] as i32);
data.info.normal_hints.height.set(values[4] as i32);
data.info.normal_hints.min_width.set(values[5] as i32);
data.info.normal_hints.min_height.set(values[6] as i32);
data.info.normal_hints.max_width.set(values[7] as i32);
data.info.normal_hints.max_height.set(values[8] as i32);
data.info.normal_hints.width_inc.set(values[9] as i32);
data.info.normal_hints.height_inc.set(values[10] as i32);
data.info.normal_hints.min_aspect_num.set(values[11] as i32);
data.info.normal_hints.min_aspect_den.set(values[12] as i32);
data.info.normal_hints.max_aspect_num.set(values[13] as i32);
data.info.normal_hints.max_aspect_den.set(values[14] as i32);
data.info.normal_hints.base_width.set(values[15] as i32);
data.info.normal_hints.base_height.set(values[16] as i32);
data.info.normal_hints.win_gravity.set(values[17]);
self.update_wants_floating(data);
}
async fn load_window_motif_wm_hints(&self, data: &Rc<XwindowData>) {
let mut buf = vec![];
if let Err(e) = self
.c
.get_property::<u32>(data.window_id, self.atoms._MOTIF_WM_HINTS, 0, &mut buf)
.await
{
if not_matches!(e, XconError::PropertyUnavailable) {
log::error!(
"Could not retrieve _MOTIF_WM_HINTS property: {}",
ErrorFmt(e)
);
}
return;
}
let mut values = [0; 5];
let len = values.len().min(buf.len());
values[..len].copy_from_slice(&buf[..len]);
data.info
.motif_hints
.flags
.set(values[MWM_HINTS_FLAGS_FIELD]);
data.info
.motif_hints
.decorations
.set(values[MWM_HINTS_DECORATIONS_FIELD]);
}
async fn load_window_net_startup_id(&self, data: &Rc<XwindowData>) {
let mut buf = vec![];
match self
.c
.get_property::<u8>(data.window_id, self.atoms._NET_STARTUP_ID, 0, &mut buf)
.await
{
Ok(ty) if ty == ATOM_STRING => {}
Ok(ty) if ty == self.atoms.UTF8_STRING => {}
Ok(ty) => {
self.unexpected_type(data.window_id, "_NET_STARTUP_ID", ty)
.await;
return;
}
Err(XconError::PropertyUnavailable) => return,
Err(e) => {
log::error!(
"Could not retrieve _NET_STARTUP_ID property: {}",
ErrorFmt(e)
);
return;
}
}
*data.info.startup_id.borrow_mut() = Some(buf.into());
}
async fn load_window_net_wm_state(&self, data: &Rc<XwindowData>) {
data.info.fullscreen.set(false);
let mut buf = vec![];
if let Err(e) = self
.c
.get_property::<u32>(data.window_id, self.atoms._NET_WM_STATE, 0, &mut buf)
.await
{
if not_matches!(e, XconError::PropertyUnavailable) {
log::error!("Could not retrieve _NET_WM_STATE property: {}", ErrorFmt(e));
}
return;
}
for prop in buf {
if prop == self.atoms._NET_WM_STATE_MODAL {
data.info.modal.set(true);
self.update_wants_floating(data);
} else if prop == self.atoms._NET_WM_STATE_FULLSCREEN {
data.info.fullscreen.set(true);
} else if prop == self.atoms._NET_WM_STATE_MAXIMIZED_VERT {
data.info.maximized_vert.set(true);
} else if prop == self.atoms._NET_WM_STATE_MAXIMIZED_HORZ {
data.info.maximized_horz.set(true);
} else if prop == self.atoms._NET_WM_STATE_HIDDEN {
data.info.minimized.set(true);
}
}
}
async fn load_window_net_wm_window_type(&self, data: &Rc<XwindowData>) {
let mut buf = vec![];
if let Err(e) = self
.c
.get_property::<u32>(
data.window_id,
self.atoms._NET_WM_WINDOW_TYPE,
ATOM_ATOM,
&mut buf,
)
.await
{
if not_matches!(e, XconError::PropertyUnavailable) {
log::error!(
"Could not retrieve _NET_WM_WINDOW_TYPE property: {}",
ErrorFmt(e)
);
}
return;
}
data.info
.never_focus
.set(buf.iter().any(|t| self.never_focus.contains(t)));
data.info.window_types.clear();
data.info
.window_types
.lock()
.extend(buf.iter().copied().map(|v| (v, ())));
self.update_wants_floating(data);
}
async fn create_window(&mut self, data: &Rc<XwindowData>, surface: Rc<WlSurface>) { async fn create_window(&mut self, data: &Rc<XwindowData>, surface: Rc<WlSurface>) {
if data.window.is_some() { if data.window.is_some() {
log::error!("The xwindow has already been constructed"); log::error!("The xwindow has already been constructed");

View file

@ -0,0 +1,413 @@
use {
super::Wm,
crate::{
criteria::tlm::{TL_CHANGED_CLASS_INST, TL_CHANGED_ROLE},
ifs::wl_surface::x_surface::xwindow::{XInputModel, XwindowData},
tree::ToplevelNode,
utils::{bitflags::BitflagsExt, errorfmt::ErrorFmt},
wire_xcon::GetAtomName,
xcon::{
XconError,
consts::{
ATOM_ATOM, ATOM_STRING, ATOM_WINDOW, ATOM_WM_CLASS, ATOM_WM_NAME,
ATOM_WM_SIZE_HINTS, ATOM_WM_TRANSIENT_FOR, ICCCM_WM_HINT_INPUT,
MWM_HINTS_DECORATIONS_FIELD, MWM_HINTS_FLAGS_FIELD,
},
},
},
bstr::{ByteSlice, ByteVec},
std::rc::Rc,
};
impl Wm {
pub(super) fn compute_input_model(&self, data: &Rc<XwindowData>) {
let has_wm_take_focus = data.info.protocols.contains(&self.atoms.WM_TAKE_FOCUS);
let accepts_input = data.info.icccm_hints.input.get();
let model = match (accepts_input, has_wm_take_focus) {
(false, false) => XInputModel::None,
(true, false) => XInputModel::Passive,
(true, true) => XInputModel::Local,
(false, true) => XInputModel::Global,
};
data.info.input_model.set(model);
}
pub(super) async fn load_window_wm_window_role(&self, data: &Rc<XwindowData>) {
let property_changed = || {
if let Some(window) = data.window.get() {
window.toplevel_data.property_changed(TL_CHANGED_ROLE);
}
};
let mut buf = vec![];
match self
.c
.get_property::<u8>(data.window_id, self.atoms.WM_WINDOW_ROLE, 0, &mut buf)
.await
{
Ok(ty) if ty == ATOM_STRING => {}
Ok(ty) if ty == self.atoms.UTF8_STRING => {}
Ok(ty) => {
self.unexpected_type(data.window_id, "WM_WINDOW_ROLE", ty)
.await;
return;
}
Err(XconError::PropertyUnavailable) => {
data.info.role.borrow_mut().take();
property_changed();
return;
}
Err(e) => {
log::error!(
"Could not retrieve WM_WINDOW_ROLE property: {}",
ErrorFmt(e)
);
return;
}
}
*data.info.role.borrow_mut() = Some(buf.into_string_lossy());
property_changed();
}
pub(super) async fn load_window_wm_class(&self, data: &Rc<XwindowData>) {
let mut buf = vec![];
let property_changed = || {
if let Some(window) = data.window.get() {
let class = data.info.class.borrow();
for handle in window.toplevel_data.manager_handles.lock().values() {
handle.send_app_id(class.as_deref().unwrap_or_default());
handle.send_done();
}
window.toplevel_data.property_changed(TL_CHANGED_CLASS_INST);
}
};
match self
.c
.get_property::<u8>(data.window_id, ATOM_WM_CLASS, 0, &mut buf)
.await
{
Ok(ty) if ty == ATOM_STRING => {}
Ok(ty) if ty == self.atoms.UTF8_STRING => {}
Ok(ty) => {
self.unexpected_type(data.window_id, "WM_CLASS", ty).await;
return;
}
Err(XconError::PropertyUnavailable) => {
data.info.instance.borrow_mut().take();
data.info.class.borrow_mut().take();
property_changed();
return;
}
Err(e) => {
log::error!("Could not retrieve WM_CLASS property: {}", ErrorFmt(e));
return;
}
}
let mut iter = buf.split(|c| *c == 0);
let mut map = || Some(iter.next().unwrap_or(&[]).to_str_lossy().into_owned());
*data.info.instance.borrow_mut() = map();
*data.info.class.borrow_mut() = map();
property_changed();
}
async fn load_window_wm_name2(&self, data: &Rc<XwindowData>, prop: u32, name: &str) {
let mut buf = vec![];
match self
.c
.get_property::<u8>(data.window_id, prop, 0, &mut buf)
.await
{
Ok(ty) if ty == ATOM_STRING && data.info.utf8_title.get() => return,
Ok(ty) if ty == ATOM_STRING => {}
Ok(ty) if ty == self.atoms.COMPOUND_TEXT => return,
Ok(ty) if ty == self.atoms.UTF8_STRING => {
data.info.utf8_title.set(true);
}
Ok(ty) => {
self.unexpected_type(data.window_id, name, ty).await;
return;
}
Err(XconError::PropertyUnavailable) => return,
Err(e) => {
log::error!("Could not retrieve {} property: {}", name, ErrorFmt(e));
return;
}
}
let title = buf.as_bstr().to_string();
if let Some(window) = data.window.get() {
window.toplevel_data.set_title(&title);
window.tl_title_changed();
}
*data.info.title.borrow_mut() = Some(title);
data.title_changed();
}
async fn unexpected_type(&self, window: u32, prop: &str, ty: u32) {
let mut ty_name = "unknown".as_bytes().as_bstr();
let res = self.c.call(&GetAtomName { atom: ty }).await;
if let Ok(res) = &res {
ty_name = res.get().name;
}
log::error!(
"Property {} of window {} has unexpected type {} ({})",
prop,
window,
ty_name,
ty
);
}
pub(super) async fn load_window_wm_name(&self, data: &Rc<XwindowData>) {
self.load_window_wm_name2(data, ATOM_WM_NAME, "WM_NAME")
.await;
}
pub(super) async fn load_window_net_wm_name(&self, data: &Rc<XwindowData>) {
self.load_window_wm_name2(data, self.atoms._NET_WM_NAME, "_NET_WM_NAME")
.await;
}
pub(super) async fn load_window_wm_transient_for(&self, data: &Rc<XwindowData>) {
let mut buf = vec![];
if let Err(e) = self
.c
.get_property::<u32>(data.window_id, ATOM_WM_TRANSIENT_FOR, ATOM_WINDOW, &mut buf)
.await
{
if not_matches!(e, XconError::PropertyUnavailable) {
log::error!(
"Could not retrieve WM_TRANSIENT_FOR property: {}",
ErrorFmt(e)
);
}
}
if let Some(old) = data.parent.take() {
old.children.remove(&data.window_id);
}
if let Some(w) = buf.first()
&& let Some(w) = self.windows.get(w)
{
if data.is_ancestor_of(w.clone()) {
log::error!("Cannot set WM_TRANSIENT_FOR because it would create a cycle");
return;
}
w.children.set(data.window_id, data.clone());
data.parent.set(Some(w.clone()));
}
}
pub(super) async fn load_window_wm_protocols(&self, data: &Rc<XwindowData>) {
let mut buf = vec![];
if let Err(e) = self
.c
.get_property::<u32>(data.window_id, self.atoms.WM_PROTOCOLS, ATOM_ATOM, &mut buf)
.await
{
if not_matches!(e, XconError::PropertyUnavailable) {
log::error!("Could not retrieve WM_PROTOCOLS property: {}", ErrorFmt(e));
}
return;
}
data.info.protocols.clear();
data.info
.protocols
.lock()
.extend(buf.iter().copied().map(|v| (v, ())));
self.compute_input_model(data);
}
pub(super) async fn load_window_wm_hints(&self, data: &Rc<XwindowData>) {
let mut buf = vec![];
if let Err(e) = self
.c
.get_property::<u32>(data.window_id, self.atoms.WM_HINTS, 0, &mut buf)
.await
{
if not_matches!(e, XconError::PropertyUnavailable) {
log::error!("Could not retrieve WM_HINTS property: {}", ErrorFmt(e));
}
data.info.icccm_hints.input.set(true);
self.compute_input_model(data);
return;
}
let mut values = [0; 9];
let len = values.len().min(buf.len());
values[..len].copy_from_slice(&buf[..len]);
data.info.icccm_hints.flags.set(values[0] as i32);
data.info.icccm_hints.input.set(values[1] != 0);
data.info.icccm_hints.initial_state.set(values[2] as i32);
data.info.icccm_hints.icon_pixmap.set(values[3]);
data.info.icccm_hints.icon_window.set(values[4]);
data.info.icccm_hints.icon_x.set(values[5] as i32);
data.info.icccm_hints.icon_y.set(values[6] as i32);
data.info.icccm_hints.icon_mask.set(values[7]);
data.info.icccm_hints.window_group.set(values[8]);
if data
.info
.icccm_hints
.flags
.get()
.not_contains(ICCCM_WM_HINT_INPUT)
{
data.info.icccm_hints.input.set(true);
}
self.compute_input_model(data);
}
pub(super) async fn load_window_wm_normal_hints(&self, data: &Rc<XwindowData>) {
let mut buf = vec![];
if let Err(e) = self
.c
.get_property::<u32>(
data.window_id,
self.atoms.WM_NORMAL_HINTS,
ATOM_WM_SIZE_HINTS,
&mut buf,
)
.await
{
if not_matches!(e, XconError::PropertyUnavailable) {
log::error!(
"Could not retrieve WM_NORMAL_HINTS property: {}",
ErrorFmt(e)
);
}
return;
}
let mut values = [0; 18];
let len = values.len().min(buf.len());
values[..len].copy_from_slice(&buf[..len]);
data.info.normal_hints.flags.set(values[0]);
data.info.normal_hints.x.set(values[1] as i32);
data.info.normal_hints.y.set(values[2] as i32);
data.info.normal_hints.width.set(values[3] as i32);
data.info.normal_hints.height.set(values[4] as i32);
data.info.normal_hints.min_width.set(values[5] as i32);
data.info.normal_hints.min_height.set(values[6] as i32);
data.info.normal_hints.max_width.set(values[7] as i32);
data.info.normal_hints.max_height.set(values[8] as i32);
data.info.normal_hints.width_inc.set(values[9] as i32);
data.info.normal_hints.height_inc.set(values[10] as i32);
data.info.normal_hints.min_aspect_num.set(values[11] as i32);
data.info.normal_hints.min_aspect_den.set(values[12] as i32);
data.info.normal_hints.max_aspect_num.set(values[13] as i32);
data.info.normal_hints.max_aspect_den.set(values[14] as i32);
data.info.normal_hints.base_width.set(values[15] as i32);
data.info.normal_hints.base_height.set(values[16] as i32);
data.info.normal_hints.win_gravity.set(values[17]);
self.update_wants_floating(data);
}
pub(super) async fn load_window_motif_wm_hints(&self, data: &Rc<XwindowData>) {
let mut buf = vec![];
if let Err(e) = self
.c
.get_property::<u32>(data.window_id, self.atoms._MOTIF_WM_HINTS, 0, &mut buf)
.await
{
if not_matches!(e, XconError::PropertyUnavailable) {
log::error!(
"Could not retrieve _MOTIF_WM_HINTS property: {}",
ErrorFmt(e)
);
}
return;
}
let mut values = [0; 5];
let len = values.len().min(buf.len());
values[..len].copy_from_slice(&buf[..len]);
data.info
.motif_hints
.flags
.set(values[MWM_HINTS_FLAGS_FIELD]);
data.info
.motif_hints
.decorations
.set(values[MWM_HINTS_DECORATIONS_FIELD]);
}
pub(super) async fn load_window_net_startup_id(&self, data: &Rc<XwindowData>) {
let mut buf = vec![];
match self
.c
.get_property::<u8>(data.window_id, self.atoms._NET_STARTUP_ID, 0, &mut buf)
.await
{
Ok(ty) if ty == ATOM_STRING => {}
Ok(ty) if ty == self.atoms.UTF8_STRING => {}
Ok(ty) => {
self.unexpected_type(data.window_id, "_NET_STARTUP_ID", ty)
.await;
return;
}
Err(XconError::PropertyUnavailable) => return,
Err(e) => {
log::error!(
"Could not retrieve _NET_STARTUP_ID property: {}",
ErrorFmt(e)
);
return;
}
}
*data.info.startup_id.borrow_mut() = Some(buf.into());
}
pub(super) async fn load_window_net_wm_state(&self, data: &Rc<XwindowData>) {
data.info.fullscreen.set(false);
let mut buf = vec![];
if let Err(e) = self
.c
.get_property::<u32>(data.window_id, self.atoms._NET_WM_STATE, 0, &mut buf)
.await
{
if not_matches!(e, XconError::PropertyUnavailable) {
log::error!("Could not retrieve _NET_WM_STATE property: {}", ErrorFmt(e));
}
return;
}
for prop in buf {
if prop == self.atoms._NET_WM_STATE_MODAL {
data.info.modal.set(true);
self.update_wants_floating(data);
} else if prop == self.atoms._NET_WM_STATE_FULLSCREEN {
data.info.fullscreen.set(true);
} else if prop == self.atoms._NET_WM_STATE_MAXIMIZED_VERT {
data.info.maximized_vert.set(true);
} else if prop == self.atoms._NET_WM_STATE_MAXIMIZED_HORZ {
data.info.maximized_horz.set(true);
} else if prop == self.atoms._NET_WM_STATE_HIDDEN {
data.info.minimized.set(true);
}
}
}
pub(super) async fn load_window_net_wm_window_type(&self, data: &Rc<XwindowData>) {
let mut buf = vec![];
if let Err(e) = self
.c
.get_property::<u32>(
data.window_id,
self.atoms._NET_WM_WINDOW_TYPE,
ATOM_ATOM,
&mut buf,
)
.await
{
if not_matches!(e, XconError::PropertyUnavailable) {
log::error!(
"Could not retrieve _NET_WM_WINDOW_TYPE property: {}",
ErrorFmt(e)
);
}
return;
}
data.info
.never_focus
.set(buf.iter().any(|t| self.never_focus.contains(t)));
data.info.window_types.clear();
data.info
.window_types
.lock()
.extend(buf.iter().copied().map(|v| (v, ())));
self.update_wants_floating(data);
}
}

View file

@ -1,4 +1,8 @@
use {super::*, super::transfer::{WaylandToXTransfer, XToWaylandTransfer}}; use {
super::*,
super::transfer::{WaylandToXTransfer, XToWaylandTransfer},
crate::wire_xcon::GetAtomName,
};
pub(super) struct EnhancedOffer { pub(super) struct EnhancedOffer {
offer: Rc<XDataOffer>, offer: Rc<XDataOffer>,