Compare commits
No commits in common. "c5dd462a6e9507d30c5a3742f433fbdafa98de4d" and "556e4214c40decc9ec454f42d660e4433ca605d2" have entirely different histories.
c5dd462a6e
...
556e4214c4
18 changed files with 2968 additions and 3138 deletions
|
|
@ -1,5 +1,4 @@
|
|||
mod copy_device;
|
||||
mod discovery;
|
||||
mod hardware_cursor;
|
||||
mod lease;
|
||||
mod model;
|
||||
|
|
@ -19,22 +18,19 @@ pub use {
|
|||
properties::{DefaultProperty, TypedProperty},
|
||||
};
|
||||
|
||||
use {
|
||||
discovery::{
|
||||
create_connector, create_connector_display_data, create_crtc, create_encoder, create_plane,
|
||||
get_connectors,
|
||||
},
|
||||
properties::collect_untyped_properties,
|
||||
use properties::{
|
||||
DefaultValue, collect_properties, collect_untyped_properties, create_default_properties,
|
||||
};
|
||||
|
||||
use {
|
||||
crate::{
|
||||
async_engine::Phase,
|
||||
backend::{
|
||||
BackendColorSpace, BackendConnectorState, BackendDrmDevice, BackendDrmLessee,
|
||||
BackendEotfs, BackendEvent, BackendGammaLut, BackendGammaLutElement,
|
||||
CONCAP_CONNECTOR, CONCAP_MODE_SETTING, CONCAP_PHYSICAL_DISPLAY,
|
||||
BackendLuminance, CONCAP_CONNECTOR, CONCAP_MODE_SETTING, CONCAP_PHYSICAL_DISPLAY,
|
||||
Connector, ConnectorCaps, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId,
|
||||
MonitorInfo,
|
||||
Mode, MonitorInfo, OutputId,
|
||||
transaction::{
|
||||
BackendConnectorTransaction, BackendConnectorTransactionError,
|
||||
BackendConnectorTransactionType, BackendConnectorTransactionTypeDyn,
|
||||
|
|
@ -42,33 +38,42 @@ use {
|
|||
},
|
||||
backends::metal::{
|
||||
MetalBackend, MetalError,
|
||||
present::{DEFAULT_POST_COMMIT_MARGIN, POST_COMMIT_MARGIN_DELTA},
|
||||
transaction::MetalDeviceTransaction,
|
||||
present::{
|
||||
DEFAULT_POST_COMMIT_MARGIN, DEFAULT_PRE_COMMIT_MARGIN, POST_COMMIT_MARGIN_DELTA,
|
||||
},
|
||||
transaction::{DrmConnectorState, DrmCrtcState, DrmPlaneState, MetalDeviceTransaction},
|
||||
},
|
||||
cmm::cmm_primaries::Primaries,
|
||||
drm_feedback::DrmFeedback,
|
||||
edid::{CtaDataBlock, Descriptor, EdidExtension},
|
||||
format::XRGB8888,
|
||||
gfx_api::GfxApi,
|
||||
ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC, KIND_ZERO_COPY},
|
||||
tree::OutputNode,
|
||||
udev::UdevDevice,
|
||||
utils::{
|
||||
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt, oserror::OsError,
|
||||
binary_search_map::BinarySearchMap, bitflags::BitflagsExt, cell_ext::CellExt,
|
||||
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
|
||||
geometric_decay::GeometricDecay, numcell::NumCell, ordered_float::F64,
|
||||
oserror::OsError,
|
||||
},
|
||||
video::{
|
||||
INVALID_MODIFIER,
|
||||
drm::{
|
||||
ConnectorStatus, DRM_CLIENT_CAP_ATOMIC, DrmBlob, DrmCardResources, DrmConnector,
|
||||
DrmCrtc, DrmError, DrmEvent, DrmFb, DrmMaster, DrmObject, DrmProperty,
|
||||
DrmVersion, drm_mode_modeinfo, hdr_output_metadata,
|
||||
ConnectorStatus, ConnectorType, DRM_CLIENT_CAP_ATOMIC, DrmBlob, DrmCardResources,
|
||||
DrmConnector, DrmCrtc, DrmEncoder, DrmError, DrmEvent, DrmFb, DrmMaster,
|
||||
DrmObject, DrmPlane, DrmProperty, DrmPropertyType, DrmVersion,
|
||||
HDMI_EOTF_TRADITIONAL_GAMMA_SDR, drm_mode_modeinfo, hdr_output_metadata,
|
||||
},
|
||||
gbm::GbmDevice,
|
||||
},
|
||||
},
|
||||
ahash::{AHashMap, AHashSet},
|
||||
bstr::ByteSlice,
|
||||
indexmap::indexset,
|
||||
isnt::std_1::collections::IsntHashMapExt,
|
||||
std::{
|
||||
cell::Cell,
|
||||
cell::{Cell, RefCell},
|
||||
collections::hash_map::Entry,
|
||||
mem,
|
||||
ops::DerefMut,
|
||||
|
|
@ -550,6 +555,626 @@ 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 {
|
||||
pub fn check_render_context(&self, dev: &Rc<MetalDrmDevice>) -> bool {
|
||||
let ctx = match self.ctx.get() {
|
||||
|
|
|
|||
|
|
@ -1,660 +0,0 @@
|
|||
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),
|
||||
})
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ use {
|
|||
format::{FORMATS, Format},
|
||||
gfx_api::FdSync,
|
||||
io_uring::IoUring,
|
||||
rect::{Rect, Region},
|
||||
utils::{
|
||||
clonecell::CloneCell,
|
||||
errorfmt::ErrorFmt,
|
||||
|
|
@ -20,10 +21,12 @@ use {
|
|||
},
|
||||
vulkan_core::{
|
||||
self, VULKAN_API_VERSION, VulkanCoreError, VulkanCoreInstance, device::VulkanDeviceInf,
|
||||
map_extension_properties, timeline_semaphore::VulkanDeviceTimelineSemaphoreExt,
|
||||
map_extension_properties, sync::VulkanDeviceSyncExt,
|
||||
timeline_semaphore::VulkanDeviceTimelineSemaphoreExt,
|
||||
},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
arrayvec::ArrayVec,
|
||||
ash::{
|
||||
Device,
|
||||
ext::{
|
||||
|
|
@ -32,9 +35,12 @@ use {
|
|||
},
|
||||
khr::{external_fence_fd, external_memory_fd, external_semaphore_fd},
|
||||
vk::{
|
||||
self, BindImageMemoryInfo, BindImagePlaneMemoryInfo, BufferCopy2, BufferCreateInfo,
|
||||
BufferImageCopy2, BufferUsageFlags, CommandBuffer, CommandBufferAllocateInfo,
|
||||
CommandPoolCreateFlags, CommandPoolCreateInfo, DeviceCreateInfo, DeviceMemory,
|
||||
self, AccessFlags2, BindImageMemoryInfo, BindImagePlaneMemoryInfo, BlitImageInfo2,
|
||||
BufferCopy2, BufferCreateInfo, BufferImageCopy2, BufferMemoryBarrier2,
|
||||
BufferUsageFlags, CommandBuffer, CommandBufferAllocateInfo, CommandBufferBeginInfo,
|
||||
CommandBufferSubmitInfo, CommandBufferUsageFlags, CommandPoolCreateFlags,
|
||||
CommandPoolCreateInfo, CopyBufferInfo2, CopyBufferToImageInfo2, CopyImageInfo2,
|
||||
CopyImageToBufferInfo2, DependencyInfo, DeviceCreateInfo, DeviceMemory,
|
||||
DeviceQueueCreateInfo, DrmFormatModifierPropertiesEXT,
|
||||
DrmFormatModifierPropertiesListEXT, ExportMemoryAllocateInfo, Extent3D,
|
||||
ExternalBufferProperties, ExternalFenceFeatureFlags, ExternalFenceHandleTypeFlags,
|
||||
|
|
@ -42,21 +48,23 @@ use {
|
|||
ExternalMemoryBufferCreateInfo, ExternalMemoryBufferCreateInfoKHR,
|
||||
ExternalMemoryFeatureFlags, ExternalMemoryHandleTypeFlags,
|
||||
ExternalMemoryImageCreateInfo, ExternalSemaphoreFeatureFlags,
|
||||
ExternalSemaphoreHandleTypeFlags, ExternalSemaphoreProperties, FormatFeatureFlags,
|
||||
FormatProperties2, ImageAspectFlags, ImageBlit2, ImageCopy2, ImageCreateFlags,
|
||||
ImageCreateInfo, ImageDrmFormatModifierExplicitCreateInfoEXT, ImageFormatProperties2,
|
||||
ImageLayout, ImageMemoryRequirementsInfo2, ImagePlaneMemoryRequirementsInfo,
|
||||
ImageTiling, ImageType, ImageUsageFlags, ImportMemoryFdInfoKHR, ImportSemaphoreFdInfoKHR,
|
||||
MemoryAllocateInfo, MemoryDedicatedAllocateInfo, MemoryFdPropertiesKHR,
|
||||
MemoryGetFdInfoKHR, MemoryPropertyFlags, MemoryRequirements2, MemoryType,
|
||||
PhysicalDevice, PhysicalDeviceDrmPropertiesEXT,
|
||||
ExternalSemaphoreHandleTypeFlags, ExternalSemaphoreProperties, Filter,
|
||||
FormatFeatureFlags, FormatProperties2, ImageAspectFlags, ImageBlit2, ImageCopy2,
|
||||
ImageCreateFlags, ImageCreateInfo, ImageDrmFormatModifierExplicitCreateInfoEXT,
|
||||
ImageFormatProperties2, ImageLayout, ImageMemoryBarrier2, ImageMemoryRequirementsInfo2,
|
||||
ImagePlaneMemoryRequirementsInfo, ImageSubresourceLayers, ImageSubresourceRange,
|
||||
ImageTiling, ImageType, ImageUsageFlags, ImportMemoryFdInfoKHR,
|
||||
ImportSemaphoreFdInfoKHR, MemoryAllocateInfo, MemoryDedicatedAllocateInfo,
|
||||
MemoryFdPropertiesKHR, MemoryGetFdInfoKHR, MemoryPropertyFlags, MemoryRequirements2,
|
||||
MemoryType, Offset3D, PhysicalDevice, PhysicalDeviceDrmPropertiesEXT,
|
||||
PhysicalDeviceExternalBufferInfo, PhysicalDeviceExternalFenceInfo,
|
||||
PhysicalDeviceExternalImageFormatInfoKHR, PhysicalDeviceExternalSemaphoreInfo,
|
||||
PhysicalDeviceFeatures2, PhysicalDeviceImageDrmFormatModifierInfoEXT,
|
||||
PhysicalDeviceImageFormatInfo2, PhysicalDeviceProperties2,
|
||||
PhysicalDeviceSynchronization2Features, PhysicalDeviceTimelineSemaphoreFeatures,
|
||||
Queue, QueueFlags, SampleCountFlags, SemaphoreCreateInfo, SemaphoreImportFlags,
|
||||
SharingMode, SubresourceLayout,
|
||||
PipelineStageFlags2, QUEUE_FAMILY_FOREIGN_EXT, Queue, QueueFlags, SampleCountFlags,
|
||||
SemaphoreCreateInfo, SemaphoreImportFlags, SemaphoreSubmitInfo, SharingMode,
|
||||
SubmitInfo2, SubresourceLayout, WHOLE_SIZE,
|
||||
},
|
||||
},
|
||||
bstr::ByteSlice,
|
||||
|
|
@ -77,7 +85,6 @@ use {
|
|||
vk::{Buffer, CommandPool, Image, Semaphore},
|
||||
};
|
||||
|
||||
mod execute;
|
||||
mod queue_allocation;
|
||||
mod registry;
|
||||
|
||||
|
|
@ -1268,6 +1275,429 @@ 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));
|
||||
®ion_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, ©_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(®ions);
|
||||
dev.cmd_copy_buffer_to_image2(cmd, ©);
|
||||
}
|
||||
false => {
|
||||
let copy = CopyImageToBufferInfo2::default()
|
||||
.src_image(img.img)
|
||||
.src_image_layout(image_layout)
|
||||
.dst_buffer(buf.buf)
|
||||
.regions(®ions);
|
||||
dev.cmd_copy_image_to_buffer2(cmd, ©);
|
||||
}
|
||||
}
|
||||
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, ©_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 {
|
||||
fn import(&self, sync_file: &OwnedFd) -> Result<(), CopyDeviceError> {
|
||||
let fd = uapi::fcntl_dupfd_cloexec(sync_file.raw(), 0)
|
||||
|
|
|
|||
|
|
@ -1,442 +0,0 @@
|
|||
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));
|
||||
®ion_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, ©_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(®ions);
|
||||
dev.cmd_copy_buffer_to_image2(cmd, ©);
|
||||
}
|
||||
false => {
|
||||
let copy = CopyImageToBufferInfo2::default()
|
||||
.src_image(img.img)
|
||||
.src_image_layout(image_layout)
|
||||
.dst_buffer(buf.buf)
|
||||
.regions(®ions);
|
||||
dev.cmd_copy_image_to_buffer2(cmd, ©);
|
||||
}
|
||||
}
|
||||
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, ©_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)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
mod color;
|
||||
mod op;
|
||||
mod pipeline_cache;
|
||||
mod pipelines;
|
||||
mod paint_region;
|
||||
|
||||
use {
|
||||
|
|
@ -11,7 +10,7 @@ use {
|
|||
VulkanRoundedTexOp, VulkanTexOp,
|
||||
},
|
||||
paint_region::{PaintRegion, Point, constrain_to_fb},
|
||||
pipeline_cache::{FillPipelines, OutPipelineKey, TexPipelines},
|
||||
pipeline_cache::{FillPipelines, OutPipelineKey, TexPipelineKey, TexPipelines},
|
||||
crate::{
|
||||
async_engine::{AsyncEngine, SpawnedFuture},
|
||||
cmm::{
|
||||
|
|
@ -20,8 +19,8 @@ use {
|
|||
},
|
||||
cpu_worker::PendingJob,
|
||||
gfx_api::{
|
||||
AcquireSync, BufferResv, BufferResvUser, FdSync, GfxApiOpt, GfxBlendBuffer, GfxFormat,
|
||||
GfxTexture, GfxWriteModifier, ReleaseSync,
|
||||
AcquireSync, AlphaMode, BufferResv, BufferResvUser, FdSync, GfxApiOpt, GfxBlendBuffer,
|
||||
GfxFormat, GfxTexture, GfxWriteModifier, ReleaseSync,
|
||||
},
|
||||
gfx_apis::vulkan::{
|
||||
VulkanError, VulkanSync, VulkanTimelineSemaphore,
|
||||
|
|
@ -31,9 +30,9 @@ use {
|
|||
descriptor::VulkanDescriptorSetLayout,
|
||||
descriptor_buffer::VulkanDescriptorBufferWriter,
|
||||
device::VulkanDevice,
|
||||
eotfs::VulkanEotf,
|
||||
eotfs::{EOTF_LINEAR, EotfExt, VulkanEotf},
|
||||
image::{QueueFamily, QueueState, QueueTransfer, VulkanImage, VulkanImageMemory},
|
||||
pipeline::VulkanPipeline,
|
||||
pipeline::{PipelineCreateInfo, VulkanPipeline},
|
||||
sampler::VulkanSampler,
|
||||
semaphore::VulkanSemaphore,
|
||||
shaders::{
|
||||
|
|
@ -79,7 +78,7 @@ use {
|
|||
},
|
||||
},
|
||||
isnt::std_1::{collections::IsntHashMapExt, primitive::IsntSliceExt},
|
||||
linearize::{Linearize, LinearizeExt, StaticMap},
|
||||
linearize::{Linearize, LinearizeExt, StaticMap, static_map},
|
||||
std::{
|
||||
any::Any,
|
||||
borrow::Cow,
|
||||
|
|
@ -379,6 +378,261 @@ impl VulkanDevice {
|
|||
}
|
||||
|
||||
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 {
|
||||
self.last_point.fetch_add(1) + 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,282 +0,0 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,20 +2,17 @@ mod event_handling;
|
|||
mod device_handler;
|
||||
pub mod ext_transient_seat_manager_v1;
|
||||
pub mod ext_transient_seat_v1;
|
||||
mod focus;
|
||||
mod gesture_owner;
|
||||
mod kb_owner;
|
||||
mod pointer_owner;
|
||||
mod position_hint;
|
||||
mod seat_object;
|
||||
mod selection;
|
||||
pub mod tablet;
|
||||
pub mod text_input;
|
||||
mod touch_owner;
|
||||
pub mod wl_keyboard;
|
||||
pub mod wl_pointer;
|
||||
pub mod wl_touch;
|
||||
mod window_management;
|
||||
pub mod wp_pointer_warp_v1;
|
||||
pub mod zwp_pointer_constraints_v1;
|
||||
pub mod zwp_pointer_gesture_hold_v1;
|
||||
|
|
@ -41,12 +38,16 @@ use {
|
|||
ifs::{
|
||||
ext_idle_notification_v1::ExtIdleNotificationV1,
|
||||
data_transfer::{
|
||||
self, DynDataSource, TransferError,
|
||||
self, DynDataSource, TransferError, TransferLocation,
|
||||
data_control::{DataControlDeviceId, DynDataControlDevice},
|
||||
wl_data_device::WlDataDevice,
|
||||
offer_source_to_regular_client,
|
||||
wl_data_device::{ClipboardTransfer, WlDataDevice},
|
||||
wl_data_source::WlDataSource,
|
||||
x_data_device::{XTransferDevice, XTransferDeviceId},
|
||||
zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1,
|
||||
x_data_device::{XClipboardTransfer, XTransferDevice, XTransferDeviceId, XPrimarySelectionTransfer},
|
||||
zwp_primary_selection_device_v1::{
|
||||
PrimarySelectionTransfer, ZwpPrimarySelectionDeviceV1,
|
||||
},
|
||||
zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
|
||||
},
|
||||
wl_output::WlOutputGlobal,
|
||||
wl_seat::{
|
||||
|
|
@ -74,6 +75,7 @@ use {
|
|||
dnd_icon::DndIcon,
|
||||
tray::{DynTrayItem, TrayItemId},
|
||||
xdg_surface::{xdg_popup::XdgPopup, xdg_toplevel::ResizeEdges},
|
||||
zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
|
||||
},
|
||||
xdg_toplevel_drag_v1::XdgToplevelDragV1,
|
||||
},
|
||||
|
|
@ -84,8 +86,10 @@ use {
|
|||
rect::Rect,
|
||||
state::{DeviceHandlerData, State},
|
||||
tree::{
|
||||
FoundNode, Node, NodeId, NodeLocation, OutputNode, ToplevelNode, WorkspaceNode,
|
||||
generic_node_visitor, toplevel_set_workspace,
|
||||
ChangeGroupAction, ContainerNode, ContainerSplit, Direction, FoundNode, Node, NodeId,
|
||||
NodeLayer, NodeLayerLink, NodeLocation, OutputNode, StackedNode, ToplevelNode,
|
||||
WorkspaceNode, generic_node_visitor, toplevel_create_split, toplevel_parent_container,
|
||||
toplevel_set_floating, toplevel_set_workspace,
|
||||
},
|
||||
utils::{
|
||||
asyncevent::AsyncEvent,
|
||||
|
|
@ -93,9 +97,9 @@ use {
|
|||
clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap,
|
||||
event_listener::{EventListener, EventSource},
|
||||
linkedlist::{LinkedList, LinkedNode},
|
||||
linkedlist::{LinkedList, LinkedNode, NodeRef},
|
||||
numcell::NumCell,
|
||||
rc_eq::rc_eq,
|
||||
rc_eq::{rc_eq, rc_weak_eq},
|
||||
smallmap::SmallMap,
|
||||
static_text::StaticText,
|
||||
},
|
||||
|
|
@ -113,12 +117,13 @@ use {
|
|||
},
|
||||
kbvm::Keycode,
|
||||
linearize::Linearize,
|
||||
run_on_drop::on_drop,
|
||||
smallvec::SmallVec,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
collections::hash_map::Entry,
|
||||
mem,
|
||||
ops::DerefMut,
|
||||
ops::{Deref, DerefMut},
|
||||
rc::{Rc, Weak},
|
||||
},
|
||||
thiserror::Error,
|
||||
|
|
@ -708,6 +713,104 @@ impl WlSeatGlobal {
|
|||
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) {
|
||||
self.repeat_rate.get()
|
||||
}
|
||||
|
|
@ -731,6 +834,519 @@ 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 ¤t_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(
|
||||
self: &Rc<Self>,
|
||||
origin: &Rc<WlSurface>,
|
||||
|
|
@ -782,6 +1398,88 @@ impl WlSeatGlobal {
|
|||
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>> {
|
||||
self.pointer_owner.dnd_icon()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,471 +0,0 @@
|
|||
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 ¤t_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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,160 +0,0 @@
|
|||
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()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
mod drag_destination;
|
||||
mod layout;
|
||||
mod tasks;
|
||||
|
||||
pub use drag_destination::default_tile_drag_destination;
|
||||
|
|
@ -46,7 +45,7 @@ use {
|
|||
cell::{Cell, RefCell},
|
||||
fmt::{Debug, Formatter},
|
||||
mem,
|
||||
ops::{Deref, DerefMut},
|
||||
ops::{Deref, DerefMut, Sub},
|
||||
rc::Rc,
|
||||
},
|
||||
};
|
||||
|
|
@ -191,6 +190,23 @@ struct CursorState {
|
|||
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 {
|
||||
pub fn new(
|
||||
state: &Rc<State>,
|
||||
|
|
@ -375,6 +391,218 @@ 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(
|
||||
self: &Rc<Self>,
|
||||
_seat: &Rc<WlSeatGlobal>,
|
||||
|
|
|
|||
|
|
@ -1,236 +0,0 @@
|
|||
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),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
mod captures;
|
||||
mod policy;
|
||||
mod render_data;
|
||||
mod workspaces;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub use {
|
||||
|
|
@ -19,14 +17,17 @@ use {
|
|||
HardwareCursor, Mode, transaction::BackendConnectorTransactionError,
|
||||
},
|
||||
client::ClientId,
|
||||
cmm::cmm_description::ColorDescription,
|
||||
cursor::KnownCursor,
|
||||
fixed::Fixed,
|
||||
gfx_api::{AcquireSync, BufferResv, GfxTexture, ReleaseSync},
|
||||
ifs::{
|
||||
ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
|
||||
jay_output::JayOutput,
|
||||
wl_buffer::WlBufferStorage,
|
||||
wl_output::{BlendSpace, WlOutputGlobal},
|
||||
wl_seat::{
|
||||
BTN_LEFT, NodeSeatState, SeatId, WlSeatGlobal,
|
||||
BTN_LEFT, NodeSeatState, SeatId, WlSeatGlobal, collect_kb_foci2,
|
||||
tablet::{TabletTool, TabletToolChanges, TabletToolId},
|
||||
wl_pointer::PendingScroll,
|
||||
},
|
||||
|
|
@ -67,7 +68,8 @@ use {
|
|||
copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt,
|
||||
event_listener::{EventSource, LazyEventSource},
|
||||
linkedlist::LinkedList,
|
||||
hash_map_ext::HashMapExt,
|
||||
linkedlist::{LinkedList, NodeRef},
|
||||
on_drop_event::OnDropEvent,
|
||||
scroller::Scroller,
|
||||
},
|
||||
|
|
@ -75,7 +77,9 @@ use {
|
|||
ExtImageCopyCaptureSessionV1Id, JayOutputId, ZwlrScreencopyFrameV1Id,
|
||||
},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
numeric_sort::cmp,
|
||||
smallvec::SmallVec,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
fmt::{Debug, Formatter},
|
||||
|
|
@ -276,6 +280,162 @@ 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) {
|
||||
self.global.clear();
|
||||
self.workspace.set(None);
|
||||
|
|
@ -488,6 +648,117 @@ 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>) {
|
||||
let rect = self.global.pos.get();
|
||||
let bh = self.state.theme.sizes.bar_height();
|
||||
|
|
|
|||
|
|
@ -1,168 +0,0 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
#![allow(clippy::await_holding_refcell_ref)] // all borrows are to data that is only used by this task
|
||||
|
||||
mod selection;
|
||||
mod properties;
|
||||
mod transfer;
|
||||
|
||||
use selection::SelectionData;
|
||||
|
|
@ -10,6 +9,7 @@ use {
|
|||
crate::{
|
||||
async_engine::SpawnedFuture,
|
||||
client::Client,
|
||||
criteria::tlm::{TL_CHANGED_CLASS_INST, TL_CHANGED_ROLE},
|
||||
ifs::{
|
||||
data_transfer::{
|
||||
DataOfferId, DataSourceId, DynDataOffer, DynDataSource, TransferLocation, TransferVtable,
|
||||
|
|
@ -22,7 +22,7 @@ use {
|
|||
wl_seat::{SeatId, WlSeatGlobal},
|
||||
wl_surface::{
|
||||
WlSurface,
|
||||
x_surface::xwindow::{Xwindow, XwindowData},
|
||||
x_surface::xwindow::{XInputModel, Xwindow, XwindowData},
|
||||
},
|
||||
},
|
||||
rect::Rect,
|
||||
|
|
@ -46,7 +46,7 @@ use {
|
|||
ChangeProperty, ChangeWindowAttributes, ClientMessage, CompositeRedirectSubwindows,
|
||||
ConfigureNotify, ConfigureRequest, ConfigureWindow, ConfigureWindowValues,
|
||||
ConvertSelection, CreateNotify, CreateWindow, CreateWindowValues, DestroyNotify,
|
||||
Extension, FocusIn, GetGeometry, InternAtom, KillClient, MapNotify,
|
||||
Extension, FocusIn, GetAtomName, GetGeometry, InternAtom, KillClient, MapNotify,
|
||||
MapRequest, MapWindow, PropertyNotify, ResClientIdSpec, ResQueryClientIds,
|
||||
SelectSelectionInput, SelectionNotify, SelectionRequest, SetInputFocus,
|
||||
SetSelectionOwner, UnmapNotify, XfixesQueryVersion, XfixesSelectionNotify,
|
||||
|
|
@ -56,13 +56,14 @@ use {
|
|||
consts::{
|
||||
_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_WM_TRANSIENT_FOR, COMPOSITE_REDIRECT_MANUAL, CONFIG_WINDOW_HEIGHT,
|
||||
CONFIG_WINDOW_WIDTH, CONFIG_WINDOW_X, CONFIG_WINDOW_Y, EVENT_MASK_FOCUS_CHANGE,
|
||||
EVENT_MASK_PROPERTY_CHANGE, EVENT_MASK_SUBSTRUCTURE_NOTIFY,
|
||||
EVENT_MASK_SUBSTRUCTURE_REDIRECT, ICCCM_WM_STATE_ICONIC, ICCCM_WM_STATE_NORMAL,
|
||||
ICCCM_WM_STATE_WITHDRAWN, INPUT_FOCUS_POINTER_ROOT, NOTIFY_DETAIL_POINTER,
|
||||
NOTIFY_MODE_GRAB, NOTIFY_MODE_UNGRAB, PROP_MODE_REPLACE,
|
||||
RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID, SELECTION_CLIENT_CLOSE_MASK,
|
||||
ATOM_WM_SIZE_HINTS, ATOM_WM_TRANSIENT_FOR, COMPOSITE_REDIRECT_MANUAL,
|
||||
CONFIG_WINDOW_HEIGHT, CONFIG_WINDOW_WIDTH, CONFIG_WINDOW_X, CONFIG_WINDOW_Y,
|
||||
EVENT_MASK_FOCUS_CHANGE, EVENT_MASK_PROPERTY_CHANGE,
|
||||
EVENT_MASK_SUBSTRUCTURE_NOTIFY, EVENT_MASK_SUBSTRUCTURE_REDIRECT,
|
||||
ICCCM_WM_HINT_INPUT, ICCCM_WM_STATE_ICONIC, ICCCM_WM_STATE_NORMAL,
|
||||
ICCCM_WM_STATE_WITHDRAWN, INPUT_FOCUS_POINTER_ROOT, MWM_HINTS_DECORATIONS_FIELD,
|
||||
MWM_HINTS_FLAGS_FIELD, NOTIFY_DETAIL_POINTER, NOTIFY_MODE_GRAB, NOTIFY_MODE_UNGRAB,
|
||||
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,
|
||||
STACK_MODE_BELOW, WINDOW_CLASS_INPUT_OUTPUT,
|
||||
},
|
||||
|
|
@ -70,7 +71,7 @@ use {
|
|||
xwayland::{XWaylandError, XWaylandEvent},
|
||||
},
|
||||
ahash::{AHashMap, AHashSet},
|
||||
bstr::ByteSlice,
|
||||
bstr::{ByteSlice, ByteVec},
|
||||
futures_util::{FutureExt, select},
|
||||
smallvec::SmallVec,
|
||||
std::{
|
||||
|
|
@ -837,6 +838,398 @@ 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>) {
|
||||
if data.window.is_some() {
|
||||
log::error!("The xwindow has already been constructed");
|
||||
|
|
|
|||
|
|
@ -1,413 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,4 @@
|
|||
use {
|
||||
super::*,
|
||||
super::transfer::{WaylandToXTransfer, XToWaylandTransfer},
|
||||
crate::wire_xcon::GetAtomName,
|
||||
};
|
||||
use {super::*, super::transfer::{WaylandToXTransfer, XToWaylandTransfer}};
|
||||
|
||||
pub(super) struct EnhancedOffer {
|
||||
offer: Rc<XDataOffer>,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue