kms: Output mirroring

This commit is contained in:
Victoria Brekenfeld 2024-04-24 17:25:33 +02:00 committed by Victoria Brekenfeld
parent 19ba568f02
commit 3eb6c02008
4 changed files with 365 additions and 108 deletions

View file

@ -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(),

View file

@ -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();

View file

@ -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 {

View file

@ -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;
}
}