kms: Output mirroring
This commit is contained in:
parent
19ba568f02
commit
3eb6c02008
4 changed files with 365 additions and 108 deletions
|
|
@ -5,7 +5,7 @@ use crate::{
|
|||
element::{CosmicElement, DamageElement},
|
||||
workspace_elements, CLEAR_COLOR,
|
||||
},
|
||||
config::OutputConfig,
|
||||
config::{OutputConfig, OutputState},
|
||||
shell::Shell,
|
||||
state::{BackendData, Common, Fps, SurfaceDmabufFeedback},
|
||||
utils::prelude::*,
|
||||
|
|
@ -36,14 +36,18 @@ use smithay::{
|
|||
libinput::{LibinputInputBackend, LibinputSessionInterface},
|
||||
renderer::{
|
||||
buffer_dimensions,
|
||||
damage::Error as RenderError,
|
||||
element::{Element, RenderElementStates},
|
||||
gles::GlesRenderbuffer,
|
||||
damage::{Error as RenderError, OutputDamageTracker},
|
||||
element::{
|
||||
texture::{TextureRenderBuffer, TextureRenderElement},
|
||||
utils::{constrain_render_elements, ConstrainAlign, ConstrainScaleBehavior},
|
||||
Element, Kind, RenderElementStates,
|
||||
},
|
||||
gles::{GlesRenderbuffer, GlesTexture},
|
||||
glow::GlowRenderer,
|
||||
multigpu::{gbm::GbmGlesBackend, Error as MultiError, GpuManager},
|
||||
sync::SyncPoint,
|
||||
utils::with_renderer_surface_state,
|
||||
Bind, ImportDma, Offscreen,
|
||||
Bind, ImportDma, Offscreen, Renderer, Texture,
|
||||
},
|
||||
session::{libseat::LibSeatSession, Event as SessionEvent, Session},
|
||||
udev::{all_gpus, primary_gpu, UdevBackend, UdevEvent},
|
||||
|
|
@ -155,7 +159,11 @@ impl fmt::Debug for Device {
|
|||
pub struct Surface {
|
||||
surface: Option<GbmDrmCompositor>,
|
||||
connector: connector::Handle,
|
||||
|
||||
output: Output,
|
||||
mirroring: Option<Output>,
|
||||
mirroring_textures: HashMap<DrmNode, MirroringState>,
|
||||
|
||||
refresh_rate: u32,
|
||||
vrr: bool,
|
||||
scheduled: bool,
|
||||
|
|
@ -165,6 +173,45 @@ pub struct Surface {
|
|||
feedback: HashMap<DrmNode, SurfaceDmabufFeedback>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MirroringState {
|
||||
texture: TextureRenderBuffer<GlesTexture>,
|
||||
damage_tracker: OutputDamageTracker,
|
||||
}
|
||||
|
||||
impl MirroringState {
|
||||
fn new_with_renderer(
|
||||
renderer: &mut GlMultiRenderer,
|
||||
format: Fourcc,
|
||||
output: &Output,
|
||||
) -> Result<Self> {
|
||||
let size = output
|
||||
.current_mode()
|
||||
.map(|mode| mode.size)
|
||||
.unwrap_or_default()
|
||||
.to_logical(1)
|
||||
.to_buffer(1, Transform::Normal);
|
||||
let opaque_regions = vec![Rectangle::from_loc_and_size((0, 0), size)];
|
||||
|
||||
let texture = Offscreen::<GlesTexture>::create_buffer(renderer, format, size)?;
|
||||
let transform = output.current_transform();
|
||||
let texture_buffer = TextureRenderBuffer::from_texture(
|
||||
renderer,
|
||||
texture,
|
||||
1,
|
||||
transform,
|
||||
Some(opaque_regions),
|
||||
);
|
||||
|
||||
let damage_tracker = OutputDamageTracker::from_output(output);
|
||||
|
||||
Ok(MirroringState {
|
||||
texture: texture_buffer,
|
||||
damage_tracker,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub type GbmDrmCompositor = DrmCompositor<
|
||||
GbmAllocator<DrmDeviceFd>,
|
||||
GbmDevice<DrmDeviceFd>,
|
||||
|
|
@ -1036,6 +1083,8 @@ impl Device {
|
|||
|
||||
let data = Surface {
|
||||
output: output.clone(),
|
||||
mirroring: None,
|
||||
mirroring_textures: HashMap::new(),
|
||||
surface: None,
|
||||
connector: conn,
|
||||
vrr,
|
||||
|
|
@ -1183,7 +1232,7 @@ impl Surface {
|
|||
api.renderer(&render_node, &target_node, compositor.format())
|
||||
.unwrap(),
|
||||
),
|
||||
None => (target_node, api.single_renderer(&target_node).unwrap()),
|
||||
_ => (target_node, api.single_renderer(&target_node).unwrap()),
|
||||
};
|
||||
|
||||
self.fps.start();
|
||||
|
|
@ -1197,8 +1246,10 @@ impl Surface {
|
|||
|
||||
let mut elements = {
|
||||
let shell = state.shell.read().unwrap();
|
||||
let (previous_workspace, workspace) = shell.workspaces.active(&self.output);
|
||||
let (previous_idx, idx) = shell.workspaces.active_num(&self.output);
|
||||
let output = self.mirroring.as_ref().unwrap_or(&self.output);
|
||||
|
||||
let (previous_workspace, workspace) = shell.workspaces.active(output);
|
||||
let (previous_idx, idx) = shell.workspaces.active_num(output);
|
||||
let previous_workspace = previous_workspace
|
||||
.zip(previous_idx)
|
||||
.map(|((w, start), idx)| (w.handle, idx, start));
|
||||
|
|
@ -1211,7 +1262,7 @@ impl Surface {
|
|||
&state.config,
|
||||
&state.theme,
|
||||
state.clock.now(),
|
||||
&self.output,
|
||||
output,
|
||||
previous_workspace,
|
||||
workspace,
|
||||
CursorMode::All,
|
||||
|
|
@ -1229,73 +1280,166 @@ impl Surface {
|
|||
ScreencopyFrame,
|
||||
Result<(Option<Vec<Rectangle<i32, Physical>>>, RenderElementStates), OutputNoMode>,
|
||||
)> = self
|
||||
.output
|
||||
.take_pending_frames()
|
||||
.into_iter()
|
||||
.map(|(session, frame)| {
|
||||
let additional_damage = frame.damage();
|
||||
let session_data = session.user_data().get::<SessionData>().unwrap();
|
||||
let mut damage_tracking = session_data.borrow_mut();
|
||||
.mirroring
|
||||
.is_none()
|
||||
.then(|| {
|
||||
self.output
|
||||
.take_pending_frames()
|
||||
.into_iter()
|
||||
.map(|(session, frame)| {
|
||||
let additional_damage = frame.damage();
|
||||
let session_data = session.user_data().get::<SessionData>().unwrap();
|
||||
let mut damage_tracking = session_data.borrow_mut();
|
||||
|
||||
let old_len = if !additional_damage.is_empty() {
|
||||
let area = self
|
||||
.output
|
||||
.current_mode()
|
||||
.unwrap()
|
||||
/* TODO: Mode is Buffer..., why is this Physical in the first place */
|
||||
.size
|
||||
.to_logical(1)
|
||||
.to_buffer(1, Transform::Normal)
|
||||
.to_f64();
|
||||
let old_len = if !additional_damage.is_empty() {
|
||||
let area = self
|
||||
.output
|
||||
.current_mode()
|
||||
.unwrap()
|
||||
/* TODO: Mode is Buffer..., why is this Physical in the first place */
|
||||
.size
|
||||
.to_logical(1)
|
||||
.to_buffer(1, Transform::Normal)
|
||||
.to_f64();
|
||||
|
||||
let old_len = elements.len();
|
||||
elements.extend(
|
||||
additional_damage
|
||||
.into_iter()
|
||||
.map(|rect| {
|
||||
rect.to_f64()
|
||||
.to_logical(
|
||||
self.output.current_scale().fractional_scale(),
|
||||
self.output.current_transform(),
|
||||
&area,
|
||||
)
|
||||
.to_i32_round()
|
||||
})
|
||||
.map(DamageElement::new)
|
||||
.map(Into::into),
|
||||
);
|
||||
let old_len = elements.len();
|
||||
elements.extend(
|
||||
additional_damage
|
||||
.into_iter()
|
||||
.map(|rect| {
|
||||
rect.to_f64()
|
||||
.to_logical(
|
||||
self.output.current_scale().fractional_scale(),
|
||||
self.output.current_transform(),
|
||||
&area,
|
||||
)
|
||||
.to_i32_round()
|
||||
})
|
||||
.map(DamageElement::new)
|
||||
.map(Into::into),
|
||||
);
|
||||
|
||||
Some(old_len)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(old_len)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let buffer = frame.buffer();
|
||||
let age = damage_tracking.age_for_buffer(&buffer);
|
||||
let res = damage_tracking.dt.damage_output(age, &elements);
|
||||
let buffer = frame.buffer();
|
||||
let age = damage_tracking.age_for_buffer(&buffer);
|
||||
let res = damage_tracking.dt.damage_output(age, &elements);
|
||||
|
||||
if let Some(old_len) = old_len {
|
||||
elements.truncate(old_len);
|
||||
}
|
||||
if let Some(old_len) = old_len {
|
||||
elements.truncate(old_len);
|
||||
}
|
||||
|
||||
let res = res.map(|(a, b)| (a.cloned(), b));
|
||||
std::mem::drop(damage_tracking);
|
||||
(session, frame, res)
|
||||
let res = res.map(|(a, b)| (a.cloned(), b));
|
||||
std::mem::drop(damage_tracking);
|
||||
(session, frame, res)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
.unwrap_or_default();
|
||||
|
||||
let res = compositor.render_frame(
|
||||
&mut renderer,
|
||||
&elements,
|
||||
CLEAR_COLOR, // TODO use a theme neutral color
|
||||
);
|
||||
let res = if let Some(mirrored_output) = self.mirroring.as_ref().filter(|mirrored_output| {
|
||||
mirrored_output.current_mode().is_some_and(|mirror_mode| {
|
||||
self.output
|
||||
.current_mode()
|
||||
.is_some_and(|mode| mode != mirror_mode)
|
||||
})
|
||||
}) {
|
||||
let mirroring_state = {
|
||||
let entry = self.mirroring_textures.entry(*target_node);
|
||||
let mut new_state = None;
|
||||
if matches!(entry, std::collections::hash_map::Entry::Vacant(_)) {
|
||||
new_state = Some(MirroringState::new_with_renderer(
|
||||
&mut renderer,
|
||||
compositor.format(),
|
||||
mirrored_output,
|
||||
)?);
|
||||
}
|
||||
// I really want a failable initializer...
|
||||
entry.or_insert_with(|| new_state.unwrap())
|
||||
};
|
||||
|
||||
mirroring_state
|
||||
.texture
|
||||
.render()
|
||||
.draw::<_, <GlMultiRenderer as Renderer>::Error>(|tex| {
|
||||
let res = match mirroring_state.damage_tracker.render_output_with(
|
||||
&mut renderer,
|
||||
tex.clone(),
|
||||
1,
|
||||
&elements,
|
||||
CLEAR_COLOR,
|
||||
) {
|
||||
Ok(res) => res,
|
||||
Err(RenderError::Rendering(err)) => return Err(err),
|
||||
Err(RenderError::OutputNoMode(_)) => unreachable!(),
|
||||
};
|
||||
|
||||
renderer.wait(&res.sync)?;
|
||||
|
||||
let transform = mirrored_output.current_transform();
|
||||
let area = tex.size().to_logical(1, transform);
|
||||
|
||||
Ok(res
|
||||
.damage
|
||||
.cloned()
|
||||
.map(|v| {
|
||||
v.into_iter()
|
||||
.map(|r| r.to_logical(1).to_buffer(1, transform, &area))
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.unwrap_or_default())
|
||||
})
|
||||
.context("Failed to draw to offscreen render target")?;
|
||||
|
||||
let texture_elem = TextureRenderElement::from_texture_render_buffer(
|
||||
(0., 0.),
|
||||
&mirroring_state.texture,
|
||||
Some(1.0),
|
||||
None,
|
||||
None,
|
||||
Kind::Unspecified,
|
||||
);
|
||||
let texture_geometry = texture_elem.geometry(1.0.into());
|
||||
elements = constrain_render_elements(
|
||||
std::iter::once(texture_elem),
|
||||
(0, 0),
|
||||
Rectangle::from_loc_and_size(
|
||||
(0, 0),
|
||||
self.output
|
||||
.geometry()
|
||||
.size
|
||||
.as_logical()
|
||||
.to_f64()
|
||||
.to_physical(self.output.current_scale().fractional_scale())
|
||||
.to_i32_round(),
|
||||
),
|
||||
texture_geometry,
|
||||
ConstrainScaleBehavior::Fit,
|
||||
ConstrainAlign::CENTER,
|
||||
1.0,
|
||||
)
|
||||
.map(CosmicElement::Mirror)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
renderer = api.single_renderer(&target_node).unwrap();
|
||||
compositor.render_frame(&mut renderer, &elements, [0.0, 0.0, 0.0, 1.0])
|
||||
} else {
|
||||
compositor.render_frame(
|
||||
&mut renderer,
|
||||
&elements,
|
||||
CLEAR_COLOR, // TODO use a theme neutral color
|
||||
)
|
||||
};
|
||||
self.fps.render();
|
||||
|
||||
match res {
|
||||
Ok(frame_result) => {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
let feedback = if !frame_result.is_empty {
|
||||
let feedback = if !frame_result.is_empty && self.mirroring.is_none() {
|
||||
Some((
|
||||
state.take_presentation_feedback(&self.output, &frame_result.states),
|
||||
rx,
|
||||
|
|
@ -1462,31 +1606,33 @@ impl Surface {
|
|||
}
|
||||
};
|
||||
|
||||
state.send_frames(&self.output, &frame_result.states, |source_node| {
|
||||
Some(
|
||||
self.feedback
|
||||
.entry(source_node)
|
||||
.or_insert_with(|| {
|
||||
let render_formats = api
|
||||
.single_renderer(&source_node)
|
||||
.unwrap()
|
||||
.dmabuf_formats()
|
||||
.collect::<HashSet<_>>();
|
||||
let target_formats = api
|
||||
.single_renderer(target_node)
|
||||
.unwrap()
|
||||
.dmabuf_formats()
|
||||
.collect::<HashSet<_>>();
|
||||
get_surface_dmabuf_feedback(
|
||||
source_node,
|
||||
render_formats,
|
||||
target_formats,
|
||||
compositor,
|
||||
)
|
||||
})
|
||||
.clone(),
|
||||
)
|
||||
});
|
||||
if self.mirroring.is_none() {
|
||||
state.send_frames(&self.output, &frame_result.states, |source_node| {
|
||||
Some(
|
||||
self.feedback
|
||||
.entry(source_node)
|
||||
.or_insert_with(|| {
|
||||
let render_formats = api
|
||||
.single_renderer(&source_node)
|
||||
.unwrap()
|
||||
.dmabuf_formats()
|
||||
.collect::<HashSet<_>>();
|
||||
let target_formats = api
|
||||
.single_renderer(target_node)
|
||||
.unwrap()
|
||||
.dmabuf_formats()
|
||||
.collect::<HashSet<_>>();
|
||||
get_surface_dmabuf_feedback(
|
||||
source_node,
|
||||
render_formats,
|
||||
target_formats,
|
||||
compositor,
|
||||
)
|
||||
})
|
||||
.clone(),
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
compositor.reset_buffers();
|
||||
|
|
@ -1513,6 +1659,17 @@ impl KmsState {
|
|||
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
|
||||
xdg_activation_state: &XdgActivationState,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let outputs = self
|
||||
.devices
|
||||
.values()
|
||||
.flat_map(|device| {
|
||||
device
|
||||
.surfaces
|
||||
.values()
|
||||
.map(|surface| surface.output.clone())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let recreated = if let Some(device) = self
|
||||
.devices
|
||||
.values_mut()
|
||||
|
|
@ -1528,8 +1685,19 @@ impl KmsState {
|
|||
.get::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow();
|
||||
let mirrored_output = if let OutputState::Mirroring(conn) = &output_config.enabled {
|
||||
Some(
|
||||
outputs
|
||||
.iter()
|
||||
.find(|output| &output.name() == conn)
|
||||
.cloned()
|
||||
.ok_or(anyhow::anyhow!("Unable to find mirroring output"))?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if !output_config.enabled {
|
||||
if output_config.enabled == OutputState::Disabled {
|
||||
if !test_only {
|
||||
shell.workspaces.remove_output(
|
||||
output,
|
||||
|
|
@ -1541,6 +1709,7 @@ impl KmsState {
|
|||
// just drop it
|
||||
surface.pending = false;
|
||||
}
|
||||
surface.mirroring_textures.clear();
|
||||
}
|
||||
false
|
||||
} else {
|
||||
|
|
@ -1635,9 +1804,22 @@ impl KmsState {
|
|||
surface.surface = Some(target);
|
||||
true
|
||||
};
|
||||
shell
|
||||
.workspaces
|
||||
.add_output(output, workspace_state, xdg_activation_state);
|
||||
|
||||
if mirrored_output != surface.mirroring {
|
||||
shell.workspaces.remove_output(
|
||||
output,
|
||||
shell.seats.iter(),
|
||||
workspace_state,
|
||||
xdg_activation_state,
|
||||
);
|
||||
surface.mirroring = mirrored_output.clone();
|
||||
surface.mirroring_textures.clear();
|
||||
}
|
||||
if mirrored_output.is_none() {
|
||||
shell
|
||||
.workspaces
|
||||
.add_output(output, workspace_state, xdg_activation_state);
|
||||
}
|
||||
res
|
||||
} else {
|
||||
false
|
||||
|
|
@ -1737,11 +1919,13 @@ impl KmsState {
|
|||
output: &Output,
|
||||
estimated_rendertime: Option<Duration>,
|
||||
) -> Result<(), InsertError<Timer>> {
|
||||
if let Some((device, crtc, surface)) = self
|
||||
for (device, crtc, surface) in self
|
||||
.devices
|
||||
.iter_mut()
|
||||
.flat_map(|(node, d)| d.surfaces.iter_mut().map(move |(c, s)| (node, c, s)))
|
||||
.find(|(_, _, s)| s.output == *output)
|
||||
.filter(|(_, _, s)| {
|
||||
s.output == *output || s.mirroring.as_ref().is_some_and(|o| o == output)
|
||||
})
|
||||
{
|
||||
if surface.surface.is_none() {
|
||||
return Ok(());
|
||||
|
|
@ -1774,7 +1958,7 @@ impl KmsState {
|
|||
let common = &mut state.common;
|
||||
let target_node = target_device.render_node;
|
||||
let render_node = render_node_for_output(
|
||||
&surface.output,
|
||||
surface.mirroring.as_ref().unwrap_or(&surface.output),
|
||||
backend.primary_node,
|
||||
target_node,
|
||||
&*common.shell.read().unwrap(),
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ use smithay::{
|
|||
backend::renderer::{
|
||||
element::{
|
||||
surface::WaylandSurfaceRenderElement,
|
||||
utils::{Relocate, RelocateRenderElement},
|
||||
texture::TextureRenderElement,
|
||||
utils::{CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement},
|
||||
Element, Id, Kind, RenderElement, UnderlyingStorage,
|
||||
},
|
||||
gles::GlesTexture,
|
||||
glow::{GlowFrame, GlowRenderer},
|
||||
utils::{CommitCounter, DamageSet},
|
||||
Frame, ImportAll, ImportMem, Renderer,
|
||||
|
|
@ -30,6 +32,11 @@ where
|
|||
Dnd(WaylandSurfaceRenderElement<R>),
|
||||
MoveGrab(CosmicMappedRenderElement<R>),
|
||||
AdditionalDamage(DamageElement),
|
||||
Mirror(
|
||||
CropRenderElement<
|
||||
RelocateRenderElement<RescaleRenderElement<TextureRenderElement<GlesTexture>>>,
|
||||
>,
|
||||
),
|
||||
#[cfg(feature = "debug")]
|
||||
Egui(TextureRenderElement<GlesTexture>),
|
||||
}
|
||||
|
|
@ -47,6 +54,7 @@ where
|
|||
CosmicElement::Dnd(elem) => elem.id(),
|
||||
CosmicElement::MoveGrab(elem) => elem.id(),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.id(),
|
||||
CosmicElement::Mirror(elem) => elem.id(),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.id(),
|
||||
}
|
||||
|
|
@ -59,6 +67,7 @@ where
|
|||
CosmicElement::Dnd(elem) => elem.current_commit(),
|
||||
CosmicElement::MoveGrab(elem) => elem.current_commit(),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.current_commit(),
|
||||
CosmicElement::Mirror(elem) => elem.current_commit(),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.current_commit(),
|
||||
}
|
||||
|
|
@ -71,6 +80,7 @@ where
|
|||
CosmicElement::Dnd(elem) => elem.src(),
|
||||
CosmicElement::MoveGrab(elem) => elem.src(),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.src(),
|
||||
CosmicElement::Mirror(elem) => elem.src(),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.src(),
|
||||
}
|
||||
|
|
@ -83,6 +93,7 @@ where
|
|||
CosmicElement::Dnd(elem) => elem.geometry(scale),
|
||||
CosmicElement::MoveGrab(elem) => elem.geometry(scale),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.geometry(scale),
|
||||
CosmicElement::Mirror(elem) => elem.geometry(scale),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.geometry(scale),
|
||||
}
|
||||
|
|
@ -95,6 +106,7 @@ where
|
|||
CosmicElement::Dnd(elem) => elem.location(scale),
|
||||
CosmicElement::MoveGrab(elem) => elem.location(scale),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.location(scale),
|
||||
CosmicElement::Mirror(elem) => elem.location(scale),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.location(scale),
|
||||
}
|
||||
|
|
@ -107,6 +119,7 @@ where
|
|||
CosmicElement::Dnd(elem) => elem.transform(),
|
||||
CosmicElement::MoveGrab(elem) => elem.transform(),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.transform(),
|
||||
CosmicElement::Mirror(elem) => elem.transform(),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.transform(),
|
||||
}
|
||||
|
|
@ -123,6 +136,7 @@ where
|
|||
CosmicElement::Dnd(elem) => elem.damage_since(scale, commit),
|
||||
CosmicElement::MoveGrab(elem) => elem.damage_since(scale, commit),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.damage_since(scale, commit),
|
||||
CosmicElement::Mirror(elem) => elem.damage_since(scale, commit),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.damage_since(scale, commit),
|
||||
}
|
||||
|
|
@ -135,6 +149,7 @@ where
|
|||
CosmicElement::Dnd(elem) => elem.opaque_regions(scale),
|
||||
CosmicElement::MoveGrab(elem) => elem.opaque_regions(scale),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.opaque_regions(scale),
|
||||
CosmicElement::Mirror(elem) => elem.opaque_regions(scale),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.opaque_regions(scale),
|
||||
}
|
||||
|
|
@ -147,6 +162,7 @@ where
|
|||
CosmicElement::Dnd(elem) => elem.alpha(),
|
||||
CosmicElement::MoveGrab(elem) => elem.alpha(),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.alpha(),
|
||||
CosmicElement::Mirror(elem) => elem.alpha(),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.alpha(),
|
||||
}
|
||||
|
|
@ -159,6 +175,7 @@ where
|
|||
CosmicElement::Dnd(elem) => elem.kind(),
|
||||
CosmicElement::MoveGrab(elem) => elem.kind(),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.kind(),
|
||||
CosmicElement::Mirror(elem) => elem.kind(),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.kind(),
|
||||
}
|
||||
|
|
@ -181,6 +198,9 @@ impl RenderElement<GlowRenderer> for CosmicElement<GlowRenderer> {
|
|||
CosmicElement::AdditionalDamage(elem) => {
|
||||
RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage)
|
||||
}
|
||||
CosmicElement::Mirror(elem) => {
|
||||
RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage)
|
||||
}
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => {
|
||||
RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage)
|
||||
|
|
@ -195,6 +215,7 @@ impl RenderElement<GlowRenderer> for CosmicElement<GlowRenderer> {
|
|||
CosmicElement::Dnd(elem) => elem.underlying_storage(renderer),
|
||||
CosmicElement::MoveGrab(elem) => elem.underlying_storage(renderer),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.underlying_storage(renderer),
|
||||
CosmicElement::Mirror(elem) => elem.underlying_storage(renderer),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.underlying_storage(renderer),
|
||||
}
|
||||
|
|
@ -217,6 +238,14 @@ impl<'a> RenderElement<GlMultiRenderer<'a>> for CosmicElement<GlMultiRenderer<'a
|
|||
CosmicElement::AdditionalDamage(elem) => {
|
||||
RenderElement::<GlMultiRenderer<'a>>::draw(elem, frame, src, dst, damage)
|
||||
}
|
||||
CosmicElement::Mirror(elem) => {
|
||||
let elem = {
|
||||
let glow_frame = frame.glow_frame_mut();
|
||||
RenderElement::<GlowRenderer>::draw(elem, glow_frame, src, dst, damage)
|
||||
.map_err(|err| GlMultiError::Render(err))
|
||||
};
|
||||
elem
|
||||
}
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => {
|
||||
let elem = {
|
||||
|
|
@ -236,6 +265,15 @@ impl<'a> RenderElement<GlMultiRenderer<'a>> for CosmicElement<GlMultiRenderer<'a
|
|||
CosmicElement::Dnd(elem) => elem.underlying_storage(renderer),
|
||||
CosmicElement::MoveGrab(elem) => elem.underlying_storage(renderer),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.underlying_storage(renderer),
|
||||
CosmicElement::Mirror(elem) => {
|
||||
let glow_renderer = renderer.glow_renderer_mut();
|
||||
match elem.underlying_storage(glow_renderer) {
|
||||
Some(UnderlyingStorage::Wayland(buffer)) => {
|
||||
Some(UnderlyingStorage::Wayland(buffer))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => {
|
||||
let glow_renderer = renderer.glow_renderer_mut();
|
||||
|
|
|
|||
|
|
@ -109,8 +109,18 @@ impl From<Output> for OutputInfo {
|
|||
}
|
||||
}
|
||||
|
||||
fn default_enabled() -> bool {
|
||||
true
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum OutputState {
|
||||
#[serde(rename = "true")]
|
||||
Enabled,
|
||||
#[serde(rename = "false")]
|
||||
Disabled,
|
||||
Mirroring(String),
|
||||
}
|
||||
|
||||
fn default_enabled() -> OutputState {
|
||||
OutputState::Enabled
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||
|
|
@ -122,7 +132,7 @@ pub struct OutputConfig {
|
|||
pub transform: Transform,
|
||||
pub position: (i32, i32),
|
||||
#[serde(default = "default_enabled")]
|
||||
pub enabled: bool,
|
||||
pub enabled: OutputState,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub max_bpc: Option<u32>,
|
||||
}
|
||||
|
|
@ -135,7 +145,7 @@ impl Default for OutputConfig {
|
|||
scale: 1.0,
|
||||
transform: Transform::Normal,
|
||||
position: (0, 0),
|
||||
enabled: true,
|
||||
enabled: OutputState::Enabled,
|
||||
max_bpc: None,
|
||||
}
|
||||
}
|
||||
|
|
@ -257,8 +267,32 @@ impl Config {
|
|||
fn load_outputs(path: &Option<PathBuf>) -> OutputsConfig {
|
||||
if let Some(path) = path.as_ref() {
|
||||
if path.exists() {
|
||||
match ron::de::from_reader(OpenOptions::new().read(true).open(path).unwrap()) {
|
||||
Ok(config) => return config,
|
||||
match ron::de::from_reader::<_, OutputsConfig>(
|
||||
OpenOptions::new().read(true).open(path).unwrap(),
|
||||
) {
|
||||
Ok(mut config) => {
|
||||
for (info, config) in config.config.iter_mut() {
|
||||
let config_clone = config.clone();
|
||||
for conf in config.iter_mut() {
|
||||
if let OutputState::Mirroring(conn) = &conf.enabled {
|
||||
if let Some((j, _)) = info
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, info)| &info.connector == conn)
|
||||
{
|
||||
if config_clone[j].enabled != OutputState::Enabled {
|
||||
warn!("Invalid Mirroring tag, overriding with `Enabled` instead");
|
||||
conf.enabled = OutputState::Enabled;
|
||||
}
|
||||
} else {
|
||||
warn!("Invalid Mirroring tag, overriding with `Enabled` instead");
|
||||
conf.enabled = OutputState::Enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(?err, "Failed to read output_config, resetting..");
|
||||
if let Err(err) = std::fs::remove_file(path) {
|
||||
|
|
@ -307,7 +341,7 @@ impl Config {
|
|||
for (name, output_config) in infos.iter().map(|o| &o.connector).zip(configs.into_iter())
|
||||
{
|
||||
let output = outputs.iter().find(|o| &o.name() == name).unwrap().clone();
|
||||
let enabled = output_config.enabled;
|
||||
let enabled = output_config.enabled.clone();
|
||||
*output
|
||||
.user_data()
|
||||
.get::<RefCell<OutputConfig>>()
|
||||
|
|
@ -329,7 +363,7 @@ impl Config {
|
|||
reset = true;
|
||||
break;
|
||||
} else {
|
||||
if enabled {
|
||||
if enabled == OutputState::Enabled {
|
||||
output_state.enable_head(&output);
|
||||
} else {
|
||||
output_state.disable_head(&output);
|
||||
|
|
@ -343,7 +377,7 @@ impl Config {
|
|||
.into_iter()
|
||||
.zip(known_good_configs.into_iter())
|
||||
{
|
||||
let enabled = output_config.enabled;
|
||||
let enabled = output_config.enabled.clone();
|
||||
*output
|
||||
.user_data()
|
||||
.get::<RefCell<OutputConfig>>()
|
||||
|
|
@ -359,7 +393,7 @@ impl Config {
|
|||
) {
|
||||
error!(?err, "Failed to reset config for output {}.", output.name());
|
||||
} else {
|
||||
if enabled {
|
||||
if enabled == OutputState::Enabled {
|
||||
output_state.enable_head(&output);
|
||||
} else {
|
||||
output_state.disable_head(&output);
|
||||
|
|
@ -392,6 +426,7 @@ impl Config {
|
|||
.unwrap()
|
||||
.borrow()
|
||||
.enabled
|
||||
== OutputState::Enabled
|
||||
{
|
||||
output_state.enable_head(&output);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use smithay::output::Output;
|
|||
use tracing::{error, warn};
|
||||
|
||||
use crate::{
|
||||
config::OutputConfig,
|
||||
config::{OutputConfig, OutputState},
|
||||
state::State,
|
||||
wayland::protocols::output_configuration::{
|
||||
delegate_output_configuration, ModeConfiguration, OutputConfiguration,
|
||||
|
|
@ -76,9 +76,9 @@ impl State {
|
|||
if let Some(position) = position {
|
||||
current_config.position = (*position).into();
|
||||
}
|
||||
current_config.enabled = true;
|
||||
current_config.enabled = OutputState::Enabled;
|
||||
} else {
|
||||
current_config.enabled = false;
|
||||
current_config.enabled = OutputState::Disabled;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue