Add output disabling

Still needs an API call
This commit is contained in:
Ottatop 2024-06-02 18:52:35 -05:00
parent a3226a3c62
commit 4b3fbd716f
9 changed files with 274 additions and 138 deletions

View file

@ -1035,9 +1035,14 @@ impl output_service_server::OutputService for OutputService {
if let Some(y) = y { if let Some(y) = y {
loc.y = y; loc.y = y;
} }
state state.pinnacle.change_output_state(
.pinnacle &mut state.backend,
.change_output_state(&output, None, None, None, Some(loc)); &output,
None,
None,
None,
Some(loc),
);
debug!("Mapping output {} to {loc:?}", output.name()); debug!("Mapping output {} to {loc:?}", output.name());
state.pinnacle.request_layout(&output); state.pinnacle.request_layout(&output);
}) })
@ -1067,7 +1072,15 @@ impl output_service_server::OutputService for OutputService {
return; return;
}; };
state.resize_output(&output, mode); state.pinnacle.change_output_state(
&mut state.backend,
&output,
Some(mode),
None,
None,
None,
);
state.pinnacle.request_layout(&output);
}) })
.await .await
} }
@ -1102,6 +1115,7 @@ impl output_service_server::OutputService for OutputService {
}); });
state.pinnacle.change_output_state( state.pinnacle.change_output_state(
&mut state.backend,
&output, &output,
None, None,
None, None,
@ -1154,9 +1168,14 @@ impl output_service_server::OutputService for OutputService {
return; return;
}; };
state state.pinnacle.change_output_state(
.pinnacle &mut state.backend,
.change_output_state(&output, None, Some(smithay_transform), None, None); &output,
None,
Some(smithay_transform),
None,
None,
);
state.pinnacle.request_layout(&output); state.pinnacle.request_layout(&output);
state.schedule_render(&output); state.schedule_render(&output);
}) })

View file

@ -151,6 +151,8 @@ pub trait BackendData: 'static {
// INFO: only for udev in anvil, maybe shouldn't be a trait fn? // INFO: only for udev in anvil, maybe shouldn't be a trait fn?
fn early_import(&mut self, surface: &WlSurface); fn early_import(&mut self, surface: &WlSurface);
fn set_output_mode(&mut self, output: &Output, mode: smithay::output::Mode);
} }
impl BackendData for Backend { impl BackendData for Backend {
@ -180,6 +182,15 @@ impl BackendData for Backend {
Backend::Dummy(dummy) => dummy.early_import(surface), Backend::Dummy(dummy) => dummy.early_import(surface),
} }
} }
fn set_output_mode(&mut self, output: &Output, mode: smithay::output::Mode) {
match self {
Backend::Winit(winit) => winit.set_output_mode(output, mode),
Backend::Udev(udev) => udev.set_output_mode(output, mode),
#[cfg(feature = "testing")]
Backend::Dummy(dummy) => dummy.set_output_mode(output, mode),
}
}
} }
/// Update surface primary scanout outputs and send frames and dmabuf feedback to visible windows /// Update surface primary scanout outputs and send frames and dmabuf feedback to visible windows

View file

@ -1,6 +1,4 @@
use pinnacle_api_defs::pinnacle::signal::v0alpha1::{ use pinnacle_api_defs::pinnacle::signal::v0alpha1::OutputConnectResponse;
OutputConnectResponse, OutputDisconnectResponse,
};
use smithay::backend::renderer::test::DummyRenderer; use smithay::backend::renderer::test::DummyRenderer;
use smithay::backend::renderer::ImportMemWl; use smithay::backend::renderer::ImportMemWl;
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
@ -50,6 +48,10 @@ impl BackendData for Dummy {
fn reset_buffers(&mut self, _output: &Output) {} fn reset_buffers(&mut self, _output: &Output) {}
fn early_import(&mut self, _surface: &WlSurface) {} fn early_import(&mut self, _surface: &WlSurface) {}
fn set_output_mode(&mut self, _output: &Output, _mode: smithay::output::Mode) {
// TODO:
}
} }
impl Dummy { impl Dummy {
@ -141,14 +143,4 @@ impl Pinnacle {
}); });
}); });
} }
pub fn remove_output(&mut self, output: &Output) {
self.space.unmap_output(output);
self.signal_state.output_disconnect.signal(|buffer| {
buffer.push_back(OutputDisconnectResponse {
output_name: Some(output.name()),
})
});
}
} }

View file

@ -71,7 +71,6 @@ use smithay::{
presentation_time::server::wp_presentation_feedback, presentation_time::server::wp_presentation_feedback,
}, },
wayland_server::{ wayland_server::{
backend::GlobalId,
protocol::{wl_shm, wl_surface::WlSurface}, protocol::{wl_shm, wl_surface::WlSurface},
DisplayHandle, DisplayHandle,
}, },
@ -527,9 +526,12 @@ impl Udev {
/// Schedule a new render that will cause the compositor to redraw everything. /// Schedule a new render that will cause the compositor to redraw everything.
pub fn schedule_render(&mut self, loop_handle: &LoopHandle<State>, output: &Output) { pub fn schedule_render(&mut self, loop_handle: &LoopHandle<State>, output: &Output) {
let Some(surface) = render_surface_for_output(output, &mut self.backends) else { let Some(surface) = render_surface_for_output(output, &mut self.backends) else {
tracing::info!("no render surface on output {}", output.name());
return; return;
}; };
// tracing::info!(state = ?surface.render_state, name = output.name());
match &surface.render_state { match &surface.render_state {
RenderState::Idle => { RenderState::Idle => {
let output = output.clone(); let output = output.clone();
@ -577,6 +579,12 @@ impl Udev {
{ {
warn!("Failed to reset compositor state on crtc {crtc:?}: {err}"); warn!("Failed to reset compositor state on crtc {crtc:?}: {err}");
} }
if let Some(surface) = render_surface_for_output(output, &mut self.backends) {
if let RenderState::Scheduled(idle) = std::mem::take(&mut surface.render_state) {
idle.cancel();
}
}
} }
} }
} }
@ -638,51 +646,6 @@ impl State {
// ); // );
} }
} }
/// Resize the output with the given mode.
///
/// TODO: This is in udev.rs but is also used in winit.rs.
/// | I've got no clue how to make things public without making a mess.
pub fn resize_output(&mut self, output: &Output, mode: smithay::output::Mode) {
if let Backend::Udev(udev) = &mut self.backend {
let drm_mode = udev.backends.iter().find_map(|(_, backend)| {
backend
.drm_scanner
.crtcs()
.find(|(_, handle)| {
output
.user_data()
.get::<UdevOutputData>()
.is_some_and(|data| &data.crtc == handle)
})
.and_then(|(info, _)| {
info.modes()
.iter()
.find(|m| smithay::output::Mode::from(**m) == mode)
})
.copied()
});
if let Some(drm_mode) = drm_mode {
if let Some(render_surface) = render_surface_for_output(output, &mut udev.backends)
{
match render_surface.compositor.use_mode(drm_mode) {
Ok(()) => {
self.pinnacle
.change_output_state(output, Some(mode), None, None, None);
}
Err(err) => error!("Failed to resize output: {err}"),
}
}
}
} else {
self.pinnacle
.change_output_state(output, Some(mode), None, None, None);
}
self.pinnacle.request_layout(output);
self.schedule_render(output);
}
} }
impl BackendData for Udev { impl BackendData for Udev {
@ -705,6 +668,45 @@ impl BackendData for Udev {
warn!("early buffer import failed: {}", err); warn!("early buffer import failed: {}", err);
} }
} }
fn set_output_mode(&mut self, output: &Output, mode: smithay::output::Mode) {
let drm_mode = self.backends.iter().find_map(|(_, backend)| {
backend
.drm_scanner
.crtcs()
.find(|(_, handle)| {
output
.user_data()
.get::<UdevOutputData>()
.is_some_and(|data| &data.crtc == handle)
})
.and_then(|(info, _)| {
info.modes()
.iter()
.find(|m| smithay::output::Mode::from(**m) == mode)
})
.copied()
});
if let Some(drm_mode) = drm_mode {
if let Some(render_surface) = render_surface_for_output(output, &mut self.backends) {
match render_surface.compositor.use_mode(drm_mode) {
Ok(()) => {
output.change_current_state(Some(mode), None, None, None);
output.with_state_mut(|state| {
if !state.modes.contains(&mode) {
state.modes.push(mode);
}
});
}
Err(err) => warn!("Failed to resize output: {err}"),
}
}
} else {
// TODO: create new drm mode with cvt
tracing::info!("no drm mode for mode");
}
}
} }
// TODO: document desperately // TODO: document desperately
@ -810,7 +812,6 @@ enum RenderState {
/// The idle token from a render being scheduled. /// The idle token from a render being scheduled.
/// This is used to cancel renders if, for example, /// This is used to cancel renders if, for example,
/// the output being rendered is removed. /// the output being rendered is removed.
#[allow(dead_code)] // TODO:
Idle<'static>, Idle<'static>,
), ),
/// A frame was rendered and scheduled and we are waiting for vblank. /// A frame was rendered and scheduled and we are waiting for vblank.
@ -823,10 +824,6 @@ enum RenderState {
/// Render surface for an output. /// Render surface for an output.
struct RenderSurface { struct RenderSurface {
/// The output global id.
global: Option<GlobalId>,
/// A display handle used to remove the global on drop.
display_handle: DisplayHandle,
/// The node from `connector_connected`. /// The node from `connector_connected`.
device_id: DrmNode, device_id: DrmNode,
/// The node rendering to the screen? idk /// The node rendering to the screen? idk
@ -862,15 +859,6 @@ struct ScreencopyCommitState {
_cursor: CommitCounter, _cursor: CommitCounter,
} }
impl Drop for RenderSurface {
// Stop advertising this output to clients on drop.
fn drop(&mut self) {
if let Some(global) = self.global.take() {
self.display_handle.remove_global::<State>(global);
}
}
}
type GbmDrmCompositor = DrmCompositor< type GbmDrmCompositor = DrmCompositor<
GbmAllocator<DrmDeviceFd>, GbmAllocator<DrmDeviceFd>,
GbmDevice<DrmDeviceFd>, GbmDevice<DrmDeviceFd>,
@ -1055,11 +1043,12 @@ impl Udev {
); );
let global = output.create_global::<State>(&self.display_handle); let global = output.create_global::<State>(&self.display_handle);
pinnacle pinnacle.outputs.insert(output.clone(), global);
.output_management_manager_state
.add_head::<State>(&output);
output.with_state_mut(|state| state.serial = serial); output.with_state_mut(|state| {
state.serial = serial;
state.powered = true;
});
output.set_preferred(wl_mode); output.set_preferred(wl_mode);
@ -1071,6 +1060,10 @@ impl Udev {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
output.with_state_mut(|state| state.modes = modes); output.with_state_mut(|state| state.modes = modes);
pinnacle
.output_management_manager_state
.add_head::<State>(&output);
let x = pinnacle.space.outputs().fold(0, |acc, o| { let x = pinnacle.space.outputs().fold(0, |acc, o| {
let Some(geo) = pinnacle.space.output_geometry(o) else { let Some(geo) = pinnacle.space.output_geometry(o) else {
unreachable!() unreachable!()
@ -1131,10 +1124,8 @@ impl Udev {
); );
let surface = RenderSurface { let surface = RenderSurface {
display_handle: self.display_handle.clone(),
device_id: node, device_id: node,
render_node: device.render_node, render_node: device.render_node,
global: Some(global),
compositor, compositor,
dmabuf_feedback, dmabuf_feedback,
render_state: RenderState::Idle, render_state: RenderState::Idle,
@ -1145,7 +1136,7 @@ impl Udev {
device.surfaces.insert(crtc, surface); device.surfaces.insert(crtc, surface);
pinnacle.change_output_state(&output, Some(wl_mode), None, None, Some(position)); pinnacle.change_output_state(self, &output, Some(wl_mode), None, None, Some(position));
// If there is saved connector state, the connector was previously plugged in. // If there is saved connector state, the connector was previously plugged in.
// In this case, restore its tags and location. // In this case, restore its tags and location.
@ -1157,7 +1148,7 @@ impl Udev {
{ {
let ConnectorSavedState { loc, tags, scale } = saved_state; let ConnectorSavedState { loc, tags, scale } = saved_state;
output.with_state_mut(|state| state.tags.clone_from(tags)); output.with_state_mut(|state| state.tags.clone_from(tags));
pinnacle.change_output_state(&output, None, None, *scale, Some(*loc)); pinnacle.change_output_state(self, &output, None, None, *scale, Some(*loc));
} else { } else {
pinnacle.signal_state.output_connect.signal(|buffer| { pinnacle.signal_state.output_connect.signal(|buffer| {
buffer.push_back(OutputConnectResponse { buffer.push_back(OutputConnectResponse {
@ -1223,6 +1214,11 @@ impl Udev {
pinnacle pinnacle
.output_management_manager_state .output_management_manager_state
.remove_head(&output); .remove_head(&output);
if let Some(global) = pinnacle.outputs.remove(&output) {
// TODO: disable ahead of time
pinnacle.display_handle.remove_global::<State>(global);
}
} }
} }
@ -1298,11 +1294,15 @@ impl Udev {
return; return;
}; };
let output = if let Some(output) = pinnacle.space.outputs().find(|o| { let output = if let Some(output) = pinnacle
let udev_op_data = o.user_data().get::<UdevOutputData>(); .outputs
udev_op_data .keys()
.is_some_and(|data| data.device_id == surface.device_id && data.crtc == crtc) .chain(pinnacle.unmapped_outputs.iter())
}) { .find(|o| {
let udev_op_data = o.user_data().get::<UdevOutputData>();
udev_op_data
.is_some_and(|data| data.device_id == surface.device_id && data.crtc == crtc)
}) {
output.clone() output.clone()
} else { } else {
// somehow we got called with an invalid output // somehow we got called with an invalid output
@ -1394,6 +1394,11 @@ impl Udev {
assert!(matches!(surface.render_state, RenderState::Scheduled(_))); assert!(matches!(surface.render_state, RenderState::Scheduled(_)));
if !pinnacle.outputs.contains_key(output) {
surface.render_state = RenderState::Idle;
return;
}
// TODO: possibly lift this out and make it so that scheduling a render // TODO: possibly lift this out and make it so that scheduling a render
// does nothing on powered off outputs // does nothing on powered off outputs
if output.with_state(|state| !state.powered) { if output.with_state(|state| !state.powered) {

View file

@ -67,6 +67,10 @@ impl BackendData for Winit {
} }
fn early_import(&mut self, _surface: &WlSurface) {} fn early_import(&mut self, _surface: &WlSurface) {}
fn set_output_mode(&mut self, output: &Output, mode: smithay::output::Mode) {
output.change_current_state(Some(mode), None, None, None);
}
} }
impl Backend { impl Backend {
@ -201,6 +205,7 @@ impl Winit {
refresh: 144_000, refresh: 144_000,
}; };
state.pinnacle.change_output_state( state.pinnacle.change_output_state(
&mut state.backend,
&output, &output,
Some(mode), Some(mode),
None, None,

View file

@ -22,7 +22,7 @@ use smithay::{
pointer::{CursorImageStatus, PointerHandle}, pointer::{CursorImageStatus, PointerHandle},
Seat, SeatHandler, SeatState, Seat, SeatHandler, SeatState,
}, },
output::{Output, Scale}, output::{Mode, Output, Scale},
reexports::{ reexports::{
calloop::Interest, calloop::Interest,
wayland_protocols::xdg::shell::server::xdg_positioner::ConstraintAdjustment, wayland_protocols::xdg::shell::server::xdg_positioner::ConstraintAdjustment,
@ -930,21 +930,55 @@ impl OutputManagementHandler for State {
fn apply_configuration(&mut self, config: HashMap<Output, OutputConfiguration>) -> bool { fn apply_configuration(&mut self, config: HashMap<Output, OutputConfiguration>) -> bool {
for (output, config) in config { for (output, config) in config {
match config { match config {
OutputConfiguration::Disabled => todo!(), OutputConfiguration::Disabled => {
self.pinnacle.set_output_enabled(&output, false);
// TODO: split
self.backend.set_output_powered(&output, false);
}
OutputConfiguration::Enabled { OutputConfiguration::Enabled {
mode, mode,
position, position,
transform, transform,
scale, scale,
adaptive_sync, adaptive_sync: _,
} => { } => {
self.pinnacle.set_output_enabled(&output, true);
// TODO: split
self.backend.set_output_powered(&output, true);
self.schedule_render(&output);
let snapshots = self.backend.with_renderer(|renderer| { let snapshots = self.backend.with_renderer(|renderer| {
capture_snapshots_on_output(&mut self.pinnacle, renderer, &output, []) capture_snapshots_on_output(&mut self.pinnacle, renderer, &output, [])
}); });
let mode = mode.map(|(size, refresh)| {
if let Some(refresh) = refresh {
Mode {
size,
refresh: refresh.get() as i32,
}
} else {
output
.with_state(|state| {
state
.modes
.iter()
.filter(|mode| mode.size == size)
.max_by_key(|mode| mode.refresh)
.copied()
})
.unwrap_or(Mode {
size,
refresh: 60_000,
})
}
});
self.pinnacle.change_output_state( self.pinnacle.change_output_state(
&mut self.backend,
&output, &output,
None, mode,
transform, transform,
scale.map(Scale::Fractional), scale.map(Scale::Fractional),
position, position,

View file

@ -2,7 +2,9 @@
use std::{cell::RefCell, num::NonZeroU32}; use std::{cell::RefCell, num::NonZeroU32};
use pinnacle_api_defs::pinnacle::signal::v0alpha1::{OutputMoveResponse, OutputResizeResponse}; use pinnacle_api_defs::pinnacle::signal::v0alpha1::{
OutputDisconnectResponse, OutputMoveResponse, OutputResizeResponse,
};
use smithay::{ use smithay::{
desktop::layer_map_for_output, desktop::layer_map_for_output,
output::{Mode, Output, Scale}, output::{Mode, Output, Scale},
@ -12,6 +14,8 @@ use smithay::{
}; };
use crate::{ use crate::{
backend::BackendData,
config::ConnectorSavedState,
focus::WindowKeyboardFocusStack, focus::WindowKeyboardFocusStack,
layout::transaction::{LayoutTransaction, SnapshotTarget}, layout::transaction::{LayoutTransaction, SnapshotTarget},
protocol::screencopy::Screencopy, protocol::screencopy::Screencopy,
@ -51,7 +55,7 @@ pub enum BlankingState {
} }
/// The state of an output /// The state of an output
#[derive(Debug)] #[derive(Debug, Default)]
pub struct OutputState { pub struct OutputState {
pub tags: Vec<Tag>, pub tags: Vec<Tag>,
pub focus_stack: WindowKeyboardFocusStack, pub focus_stack: WindowKeyboardFocusStack,
@ -69,22 +73,6 @@ pub struct OutputState {
pub powered: bool, pub powered: bool,
} }
impl Default for OutputState {
fn default() -> Self {
Self {
tags: Default::default(),
focus_stack: Default::default(),
screencopy: Default::default(),
serial: Default::default(),
modes: Default::default(),
lock_surface: Default::default(),
blanking_state: Default::default(),
layout_transaction: Default::default(),
powered: true,
}
}
}
impl WithState for Output { impl WithState for Output {
type State = OutputState; type State = OutputState;
@ -135,10 +123,9 @@ impl OutputState {
} }
impl Pinnacle { impl Pinnacle {
/// A wrapper around [`Output::change_current_state`] that additionally sends an output
/// geometry signal.
pub fn change_output_state( pub fn change_output_state(
&mut self, &mut self,
backend: &mut impl BackendData,
output: &Output, output: &Output,
mode: Option<Mode>, mode: Option<Mode>,
transform: Option<Transform>, transform: Option<Transform>,
@ -147,7 +134,7 @@ impl Pinnacle {
) { ) {
let old_scale = output.current_scale().fractional_scale(); let old_scale = output.current_scale().fractional_scale();
output.change_current_state(mode, transform, scale, location); output.change_current_state(None, transform, scale, location);
if let Some(location) = location { if let Some(location) = location {
self.space.map_output(output, location); self.space.map_output(output, location);
self.signal_state.output_move.signal(|buf| { self.signal_state.output_move.signal(|buf| {
@ -158,6 +145,11 @@ impl Pinnacle {
}); });
}); });
} }
if let Some(mode) = mode {
backend.set_output_mode(output, mode);
}
if mode.is_some() || transform.is_some() || scale.is_some() { if mode.is_some() || transform.is_some() || scale.is_some() {
layer_map_for_output(output).arrange(); layer_map_for_output(output).arrange();
self.signal_state.output_resize.signal(|buf| { self.signal_state.output_resize.signal(|buf| {
@ -169,10 +161,6 @@ impl Pinnacle {
}); });
}); });
} }
if let Some(mode) = mode {
output.set_preferred(mode);
output.with_state_mut(|state| state.modes.push(mode));
}
if let Some(scale) = scale { if let Some(scale) = scale {
let pos_multiplier = old_scale / scale.fractional_scale(); let pos_multiplier = old_scale / scale.fractional_scale();
@ -223,4 +211,79 @@ impl Pinnacle {
self.output_management_manager_state self.output_management_manager_state
.update_head::<State>(output); .update_head::<State>(output);
} }
pub fn set_output_enabled(&mut self, output: &Output, enabled: bool) {
if enabled {
self.unmapped_outputs.remove(output);
if !self.outputs.contains_key(output) {
let global = output.create_global::<State>(&self.display_handle);
self.outputs.insert(output.clone(), global);
}
self.space.map_output(output, output.current_location());
// TODO: output connect?
} else {
let global = self.outputs.remove(output);
if let Some(global) = global {
self.display_handle.remove_global::<State>(global);
}
self.space.unmap_output(output);
self.unmapped_outputs.insert(output.clone());
// TODO: should this trigger the signal?
self.signal_state.output_disconnect.signal(|buffer| {
buffer.push_back(OutputDisconnectResponse {
output_name: Some(output.name()),
})
});
self.gamma_control_manager_state.output_removed(output);
self.config.connector_saved_states.insert(
OutputName(output.name()),
ConnectorSavedState {
loc: output.current_location(),
tags: output.with_state(|state| state.tags.clone()),
scale: Some(output.current_scale()),
},
);
for layer in layer_map_for_output(output).layers() {
layer.layer_surface().send_close();
}
}
}
pub fn remove_output(&mut self, output: &Output) {
let global = self.outputs.remove(output);
if let Some(global) = global {
self.display_handle.remove_global::<State>(global);
}
self.unmapped_outputs.remove(output);
self.space.unmap_output(output);
self.gamma_control_manager_state.output_removed(output);
self.output_management_manager_state.remove_head(output);
self.signal_state.output_disconnect.signal(|buffer| {
buffer.push_back(OutputDisconnectResponse {
output_name: Some(output.name()),
})
});
self.config.connector_saved_states.insert(
OutputName(output.name()),
ConnectorSavedState {
loc: output.current_location(),
tags: output.with_state(|state| state.tags.clone()),
scale: Some(output.current_scale()),
},
);
for layer in layer_map_for_output(output).layers() {
layer.layer_surface().send_close();
}
}
} }

View file

@ -101,7 +101,7 @@ pub struct OutputData {
position: Point<i32, Logical>, position: Point<i32, Logical>,
transform: Transform, transform: Transform,
scale: f64, scale: f64,
adaptive_sync: bool, _adaptive_sync: bool,
} }
impl OutputManagementManagerState { impl OutputManagementManagerState {
@ -127,7 +127,7 @@ impl OutputManagementManagerState {
position: output.current_location(), position: output.current_location(),
transform: output.current_transform(), transform: output.current_transform(),
scale: output.current_scale().fractional_scale(), scale: output.current_scale().fractional_scale(),
adaptive_sync: false, // TODO: _adaptive_sync: false, // TODO:
}; };
self.outputs.insert(output.clone(), output_data); self.outputs.insert(output.clone(), output_data);
@ -336,7 +336,7 @@ where
head.physical_size(physical_props.size.w, physical_props.size.h); head.physical_size(physical_props.size.w, physical_props.size.h);
let mut wlr_modes = Vec::new(); let mut wlr_modes = Vec::new();
for mode in output.modes() { for mode in output.with_state(|state| state.modes.clone()) {
let wlr_mode = client let wlr_mode = client
.create_resource::<ZwlrOutputModeV1, _, D>(display, manager.version(), mode) .create_resource::<ZwlrOutputModeV1, _, D>(display, manager.version(), mode)
.unwrap(); .unwrap();
@ -551,13 +551,13 @@ where
impl<D> Dispatch<ZwlrOutputHeadV1, Output, D> for OutputManagementManagerState { impl<D> Dispatch<ZwlrOutputHeadV1, Output, D> for OutputManagementManagerState {
fn request( fn request(
state: &mut D, _state: &mut D,
client: &Client, _client: &Client,
resource: &ZwlrOutputHeadV1, _resource: &ZwlrOutputHeadV1,
request: <ZwlrOutputHeadV1 as Resource>::Request, request: <ZwlrOutputHeadV1 as Resource>::Request,
data: &Output, _data: &Output,
dhandle: &DisplayHandle, _dhandle: &DisplayHandle,
data_init: &mut DataInit<'_, D>, _data_init: &mut DataInit<'_, D>,
) { ) {
match request { match request {
zwlr_output_head_v1::Request::Release => { zwlr_output_head_v1::Request::Release => {
@ -570,13 +570,13 @@ impl<D> Dispatch<ZwlrOutputHeadV1, Output, D> for OutputManagementManagerState {
impl<D> Dispatch<ZwlrOutputModeV1, Mode, D> for OutputManagementManagerState { impl<D> Dispatch<ZwlrOutputModeV1, Mode, D> for OutputManagementManagerState {
fn request( fn request(
state: &mut D, _state: &mut D,
client: &Client, _client: &Client,
resource: &ZwlrOutputModeV1, _resource: &ZwlrOutputModeV1,
request: <ZwlrOutputModeV1 as Resource>::Request, request: <ZwlrOutputModeV1 as Resource>::Request,
data: &Mode, _data: &Mode,
dhandle: &DisplayHandle, _dhandle: &DisplayHandle,
data_init: &mut DataInit<'_, D>, _data_init: &mut DataInit<'_, D>,
) { ) {
match request { match request {
zwlr_output_mode_v1::Request::Release => { zwlr_output_mode_v1::Request::Release => {

View file

@ -22,10 +22,11 @@ use pinnacle_api_defs::pinnacle::v0alpha1::ShutdownWatchResponse;
use smithay::{ use smithay::{
desktop::{PopupManager, Space}, desktop::{PopupManager, Space},
input::{keyboard::XkbConfig, pointer::CursorImageStatus, Seat, SeatState}, input::{keyboard::XkbConfig, pointer::CursorImageStatus, Seat, SeatState},
output::Output,
reexports::{ reexports::{
calloop::{generic::Generic, Interest, LoopHandle, LoopSignal, Mode, PostAction}, calloop::{generic::Generic, Interest, LoopHandle, LoopSignal, Mode, PostAction},
wayland_server::{ wayland_server::{
backend::{ClientData, ClientId, DisconnectReason}, backend::{ClientData, ClientId, DisconnectReason, GlobalId},
protocol::wl_surface::WlSurface, protocol::wl_surface::WlSurface,
Client, Display, DisplayHandle, Client, Display, DisplayHandle,
}, },
@ -149,6 +150,9 @@ pub struct Pinnacle {
/// WlSurfaces with an attached idle inhibitor. /// WlSurfaces with an attached idle inhibitor.
pub idle_inhibiting_surfaces: HashSet<WlSurface>, pub idle_inhibiting_surfaces: HashSet<WlSurface>,
pub outputs: HashMap<Output, GlobalId>,
pub unmapped_outputs: HashSet<Output>,
} }
impl State { impl State {
@ -343,6 +347,9 @@ impl Pinnacle {
root_surface_cache: HashMap::new(), root_surface_cache: HashMap::new(),
idle_inhibiting_surfaces: HashSet::new(), idle_inhibiting_surfaces: HashSet::new(),
outputs: HashMap::new(),
unmapped_outputs: HashSet::new(),
}; };
Ok(pinnacle) Ok(pinnacle)