mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-26 21:58:10 +01:00
Add output disabling
Still needs an API call
This commit is contained in:
parent
a3226a3c62
commit
4b3fbd716f
9 changed files with 274 additions and 138 deletions
33
src/api.rs
33
src/api.rs
|
@ -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);
|
||||||
})
|
})
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()),
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
113
src/output.rs
113
src/output.rs
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue