mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-25 09:59:21 +01:00
Impl wlr-output-management
This commit is contained in:
parent
4e796ce8f6
commit
a3226a3c62
7 changed files with 1017 additions and 17 deletions
|
@ -1055,6 +1055,10 @@ impl Udev {
|
|||
);
|
||||
let global = output.create_global::<State>(&self.display_handle);
|
||||
|
||||
pinnacle
|
||||
.output_management_manager_state
|
||||
.add_head::<State>(&output);
|
||||
|
||||
output.with_state_mut(|state| state.serial = serial);
|
||||
|
||||
output.set_preferred(wl_mode);
|
||||
|
@ -1215,6 +1219,10 @@ impl Udev {
|
|||
output_name: Some(output.name()),
|
||||
})
|
||||
});
|
||||
|
||||
pinnacle
|
||||
.output_management_manager_state
|
||||
.remove_head(&output);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
pub mod idle;
|
||||
pub mod session_lock;
|
||||
pub mod window;
|
||||
mod xdg_shell;
|
||||
mod xwayland;
|
||||
|
||||
use std::{mem, os::fd::OwnedFd, sync::Arc};
|
||||
use std::{collections::HashMap, mem, os::fd::OwnedFd, sync::Arc};
|
||||
|
||||
use smithay::{
|
||||
backend::renderer::utils::{self, with_renderer_surface_state},
|
||||
delegate_compositor, delegate_data_control, delegate_data_device, delegate_fractional_scale,
|
||||
delegate_idle_notify, delegate_layer_shell, delegate_output, delegate_pointer_constraints,
|
||||
delegate_presentation, delegate_primary_selection, delegate_relative_pointer, delegate_seat,
|
||||
delegate_layer_shell, delegate_output, delegate_pointer_constraints, delegate_presentation,
|
||||
delegate_primary_selection, delegate_relative_pointer, delegate_seat,
|
||||
delegate_security_context, delegate_shm, delegate_viewporter, delegate_xwayland_shell,
|
||||
desktop::{
|
||||
self, find_popup_root_surface, get_popup_toplevel_coords, layer_map_for_output, PopupKind,
|
||||
|
@ -21,7 +22,7 @@ use smithay::{
|
|||
pointer::{CursorImageStatus, PointerHandle},
|
||||
Seat, SeatHandler, SeatState,
|
||||
},
|
||||
output::Output,
|
||||
output::{Output, Scale},
|
||||
reexports::{
|
||||
calloop::Interest,
|
||||
wayland_protocols::xdg::shell::server::xdg_positioner::ConstraintAdjustment,
|
||||
|
@ -42,7 +43,6 @@ use smithay::{
|
|||
},
|
||||
dmabuf,
|
||||
fractional_scale::{self, FractionalScaleHandler},
|
||||
idle_notify::{IdleNotifierHandler, IdleNotifierState},
|
||||
output::OutputHandler,
|
||||
pointer_constraints::{with_pointer_constraint, PointerConstraintsHandler},
|
||||
seat::WaylandFocus,
|
||||
|
@ -69,16 +69,20 @@ use smithay::{
|
|||
},
|
||||
xwayland::{X11Wm, XWaylandClientData},
|
||||
};
|
||||
use tracing::{error, trace, warn};
|
||||
use tracing::{debug, error, trace, warn};
|
||||
|
||||
use crate::{
|
||||
backend::Backend,
|
||||
delegate_foreign_toplevel, delegate_gamma_control, delegate_screencopy,
|
||||
delegate_foreign_toplevel, delegate_gamma_control, delegate_output_management,
|
||||
delegate_screencopy,
|
||||
focus::{keyboard::KeyboardFocusTarget, pointer::PointerFocusTarget},
|
||||
handlers::xdg_shell::snapshot_pre_commit_hook,
|
||||
protocol::{
|
||||
foreign_toplevel::{self, ForeignToplevelHandler, ForeignToplevelManagerState},
|
||||
gamma_control::{GammaControlHandler, GammaControlManagerState},
|
||||
output_management::{
|
||||
OutputConfiguration, OutputManagementHandler, OutputManagementManagerState,
|
||||
},
|
||||
screencopy::{Screencopy, ScreencopyHandler},
|
||||
},
|
||||
render::util::snapshot::capture_snapshots_on_output,
|
||||
|
@ -918,12 +922,57 @@ impl XWaylandShellHandler for State {
|
|||
}
|
||||
delegate_xwayland_shell!(State);
|
||||
|
||||
impl IdleNotifierHandler for State {
|
||||
fn idle_notifier_state(&mut self) -> &mut IdleNotifierState<Self> {
|
||||
&mut self.pinnacle.idle_notifier_state
|
||||
impl OutputManagementHandler for State {
|
||||
fn output_management_manager_state(&mut self) -> &mut OutputManagementManagerState {
|
||||
&mut self.pinnacle.output_management_manager_state
|
||||
}
|
||||
|
||||
fn apply_configuration(&mut self, config: HashMap<Output, OutputConfiguration>) -> bool {
|
||||
for (output, config) in config {
|
||||
match config {
|
||||
OutputConfiguration::Disabled => todo!(),
|
||||
OutputConfiguration::Enabled {
|
||||
mode,
|
||||
position,
|
||||
transform,
|
||||
scale,
|
||||
adaptive_sync,
|
||||
} => {
|
||||
let snapshots = self.backend.with_renderer(|renderer| {
|
||||
capture_snapshots_on_output(&mut self.pinnacle, renderer, &output, [])
|
||||
});
|
||||
|
||||
self.pinnacle.change_output_state(
|
||||
&output,
|
||||
None,
|
||||
transform,
|
||||
scale.map(Scale::Fractional),
|
||||
position,
|
||||
);
|
||||
|
||||
if let Some((a, b)) = snapshots {
|
||||
output.with_state_mut(|state| {
|
||||
state.new_wait_layout_transaction(
|
||||
self.pinnacle.loop_handle.clone(),
|
||||
a,
|
||||
b,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
self.pinnacle.request_layout(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn test_configuration(&mut self, config: HashMap<Output, OutputConfiguration>) -> bool {
|
||||
debug!(?config);
|
||||
true
|
||||
}
|
||||
}
|
||||
delegate_idle_notify!(State);
|
||||
delegate_output_management!(State);
|
||||
|
||||
impl Pinnacle {
|
||||
fn position_popup(&self, popup: &PopupSurface) {
|
||||
|
|
|
@ -32,20 +32,24 @@ impl Pinnacle {
|
|||
output: &Output,
|
||||
geometries: Vec<Rectangle<i32, Logical>>,
|
||||
) -> Vec<(WindowElement, Serial)> {
|
||||
let windows_on_foc_tags = output.with_state(|state| {
|
||||
let (windows_on_foc_tags, to_unmap) = output.with_state(|state| {
|
||||
let focused_tags = state.focused_tags().collect::<Vec<_>>();
|
||||
self.windows
|
||||
.iter()
|
||||
.filter(|win| !win.is_x11_override_redirect())
|
||||
.filter(|win| {
|
||||
.filter(|win| win.output(self).as_ref() == Some(output))
|
||||
.cloned()
|
||||
.partition::<Vec<_>, _>(|win| {
|
||||
win.with_state(|state| state.tags.iter().any(|tg| focused_tags.contains(&tg)))
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
for win in to_unmap {
|
||||
self.space.unmap_elem(&win);
|
||||
}
|
||||
|
||||
let tiled_windows = windows_on_foc_tags
|
||||
.iter()
|
||||
.filter(|win| !win.is_x11_override_redirect())
|
||||
.filter(|win| {
|
||||
win.with_state(|state| {
|
||||
state.floating_or_tiled.is_tiled() && state.fullscreen_or_maximized.is_neither()
|
||||
|
|
|
@ -219,5 +219,8 @@ impl Pinnacle {
|
|||
|
||||
lock_surface.send_configure();
|
||||
}
|
||||
|
||||
self.output_management_manager_state
|
||||
.update_head::<State>(output);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub mod foreign_toplevel;
|
||||
pub mod gamma_control;
|
||||
pub mod output_management;
|
||||
pub mod screencopy;
|
||||
|
|
918
src/protocol/output_management.rs
Normal file
918
src/protocol/output_management.rs
Normal file
|
@ -0,0 +1,918 @@
|
|||
use smithay::{
|
||||
output::Output,
|
||||
reexports::{
|
||||
wayland_protocols_wlr::output_management::v1::server::{
|
||||
zwlr_output_configuration_head_v1::{self, ZwlrOutputConfigurationHeadV1},
|
||||
zwlr_output_configuration_v1,
|
||||
zwlr_output_head_v1::{self, AdaptiveSyncState},
|
||||
zwlr_output_mode_v1::{self, ZwlrOutputModeV1},
|
||||
},
|
||||
wayland_server::{Resource, WEnum},
|
||||
},
|
||||
utils::{Logical, Physical, Point, Size, Transform, SERIAL_COUNTER},
|
||||
};
|
||||
use std::{collections::HashMap, num::NonZeroU32, sync::Mutex};
|
||||
|
||||
use smithay::{
|
||||
output::Mode,
|
||||
reexports::{
|
||||
wayland_protocols_wlr::output_management::v1::server::{
|
||||
zwlr_output_configuration_v1::ZwlrOutputConfigurationV1,
|
||||
zwlr_output_head_v1::ZwlrOutputHeadV1,
|
||||
zwlr_output_manager_v1::{self, ZwlrOutputManagerV1},
|
||||
},
|
||||
wayland_server::{
|
||||
self, backend::ClientId, Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::state::WithState;
|
||||
|
||||
const VERSION: u32 = 4;
|
||||
|
||||
pub struct OutputManagementManagerState {
|
||||
display_handle: DisplayHandle,
|
||||
managers: HashMap<ZwlrOutputManagerV1, OutputManagerData>,
|
||||
outputs: HashMap<Output, OutputData>,
|
||||
}
|
||||
|
||||
struct OutputManagerData {
|
||||
serial: u32,
|
||||
configurations: Vec<ZwlrOutputConfigurationV1>,
|
||||
heads: HashMap<ZwlrOutputHeadV1, Vec<ZwlrOutputModeV1>>,
|
||||
}
|
||||
|
||||
pub struct OutputManagementGlobalData {
|
||||
filter: Box<dyn Fn(&Client) -> bool + Send + Sync>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum PendingHead {
|
||||
NotConfigured,
|
||||
Enabled(ZwlrOutputConfigurationHeadV1),
|
||||
Disabled,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PendingOutputConfiguration {
|
||||
serial: u32,
|
||||
inner: Mutex<PendingOutputConfigurationInner>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct PendingOutputConfigurationInner {
|
||||
cancelled: bool,
|
||||
pending_heads: HashMap<ZwlrOutputHeadV1, PendingHead>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
pub struct PendingOutputHeadConfiguration {
|
||||
pub mode: Option<(Size<i32, Physical>, Option<NonZeroU32>)>,
|
||||
pub position: Option<Point<i32, Logical>>,
|
||||
pub transform: Option<Transform>,
|
||||
pub scale: Option<f64>,
|
||||
pub adaptive_sync: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OutputConfiguration {
|
||||
Disabled,
|
||||
Enabled {
|
||||
mode: Option<(Size<i32, Physical>, Option<NonZeroU32>)>,
|
||||
position: Option<Point<i32, Logical>>,
|
||||
transform: Option<Transform>,
|
||||
scale: Option<f64>,
|
||||
adaptive_sync: Option<bool>,
|
||||
},
|
||||
}
|
||||
|
||||
pub trait OutputManagementHandler {
|
||||
fn output_management_manager_state(&mut self) -> &mut OutputManagementManagerState;
|
||||
fn apply_configuration(&mut self, config: HashMap<Output, OutputConfiguration>) -> bool;
|
||||
fn test_configuration(&mut self, config: HashMap<Output, OutputConfiguration>) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OutputData {
|
||||
// modes: Vec<Mode>,
|
||||
enabled: bool,
|
||||
current_mode: Option<Mode>,
|
||||
position: Point<i32, Logical>,
|
||||
transform: Transform,
|
||||
scale: f64,
|
||||
adaptive_sync: bool,
|
||||
}
|
||||
|
||||
impl OutputManagementManagerState {
|
||||
pub fn add_head<D>(&mut self, output: &Output)
|
||||
where
|
||||
D: Dispatch<ZwlrOutputHeadV1, Output>
|
||||
+ Dispatch<ZwlrOutputModeV1, Mode>
|
||||
+ OutputManagementHandler
|
||||
+ 'static,
|
||||
{
|
||||
if self.outputs.contains_key(output) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (manager, manager_data) in self.managers.iter_mut() {
|
||||
let (head, modes) = advertise_output::<D>(&self.display_handle, manager, output);
|
||||
manager_data.heads.insert(head, modes);
|
||||
}
|
||||
|
||||
let output_data = OutputData {
|
||||
enabled: true,
|
||||
current_mode: output.current_mode(),
|
||||
position: output.current_location(),
|
||||
transform: output.current_transform(),
|
||||
scale: output.current_scale().fractional_scale(),
|
||||
adaptive_sync: false, // TODO:
|
||||
};
|
||||
|
||||
self.outputs.insert(output.clone(), output_data);
|
||||
}
|
||||
|
||||
pub fn remove_head(&mut self, output: &Output) {
|
||||
self.outputs.remove(output);
|
||||
|
||||
for data in self.managers.values_mut() {
|
||||
let heads = data.heads.keys().cloned().collect::<Vec<_>>();
|
||||
for head in heads {
|
||||
if head.data::<Output>() == Some(output) {
|
||||
let modes = data.heads.remove(&head);
|
||||
if let Some(modes) = modes {
|
||||
for mode in modes {
|
||||
mode.finished();
|
||||
}
|
||||
}
|
||||
head.finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_head_enabled(&mut self, output: &Output, enabled: bool) {
|
||||
let Some(output_data) = self.outputs.get_mut(output) else {
|
||||
return;
|
||||
};
|
||||
|
||||
output_data.enabled = enabled;
|
||||
|
||||
for manager_data in self.managers.values() {
|
||||
for (head, wlr_modes) in manager_data.heads.iter() {
|
||||
if head.data::<Output>() == Some(output) {
|
||||
head.enabled(enabled as i32);
|
||||
|
||||
if enabled {
|
||||
if let Some(current_mode) = output.current_mode() {
|
||||
let wlr_current_mode = wlr_modes
|
||||
.iter()
|
||||
.find(|wlr_mode| wlr_mode.data::<Mode>() == Some(¤t_mode));
|
||||
if let Some(wlr_current_mode) = wlr_current_mode {
|
||||
head.current_mode(wlr_current_mode);
|
||||
}
|
||||
}
|
||||
head.position(output.current_location().x, output.current_location().y);
|
||||
head.transform(output.current_transform().into());
|
||||
head.scale(output.current_scale().fractional_scale());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_head<D>(&mut self, output: &Output)
|
||||
where
|
||||
D: Dispatch<ZwlrOutputHeadV1, Output>
|
||||
+ Dispatch<ZwlrOutputModeV1, Mode>
|
||||
+ OutputManagementHandler
|
||||
+ 'static,
|
||||
{
|
||||
let Some(output_data) = self.outputs.get_mut(output) else {
|
||||
tracing::error!("Called `update_head` without `advertise_output`");
|
||||
return;
|
||||
};
|
||||
|
||||
for (manager, manager_data) in self.managers.iter_mut() {
|
||||
for (head, wlr_modes) in manager_data.heads.iter_mut() {
|
||||
if head.data::<Output>() != Some(output) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: modes
|
||||
let modes = output.with_state(|state| state.modes.clone());
|
||||
|
||||
wlr_modes.retain(|wlr_mode| {
|
||||
if !modes.contains(wlr_mode.data::<Mode>().unwrap()) {
|
||||
wlr_mode.finished();
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
for mode in modes {
|
||||
if !wlr_modes
|
||||
.iter()
|
||||
.any(|wlr_mode| wlr_mode.data::<Mode>().unwrap() == &mode)
|
||||
{
|
||||
if let Some(client) = head.client() {
|
||||
let new_wlr_mode = client
|
||||
.create_resource::<ZwlrOutputModeV1, _, D>(
|
||||
&self.display_handle,
|
||||
head.version(),
|
||||
mode,
|
||||
)
|
||||
.expect("TODO");
|
||||
|
||||
new_wlr_mode.size(mode.size.w, mode.size.h);
|
||||
new_wlr_mode.refresh(mode.refresh);
|
||||
|
||||
if Some(mode) == output.preferred_mode() {
|
||||
new_wlr_mode.preferred();
|
||||
}
|
||||
|
||||
head.mode(&new_wlr_mode);
|
||||
|
||||
wlr_modes.push(new_wlr_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// enabled handled in `set_head_enabled`
|
||||
|
||||
if output.current_mode() != output_data.current_mode {
|
||||
if let Some(new_cur_mode) = output.current_mode() {
|
||||
let new_cur_wlr_mode = wlr_modes
|
||||
.iter()
|
||||
.find(|wlr_mode| wlr_mode.data::<Mode>() == Some(&new_cur_mode));
|
||||
|
||||
match new_cur_wlr_mode {
|
||||
Some(new_cur_wlr_mode) => {
|
||||
head.current_mode(new_cur_wlr_mode);
|
||||
}
|
||||
// TODO: don't do this branch
|
||||
None => {
|
||||
if let Some(client) = head.client() {
|
||||
let new_cur_wlr_mode = client
|
||||
.create_resource::<ZwlrOutputModeV1, _, D>(
|
||||
&self.display_handle,
|
||||
head.version(),
|
||||
new_cur_mode,
|
||||
)
|
||||
.expect("TODO");
|
||||
|
||||
new_cur_wlr_mode.size(new_cur_mode.size.w, new_cur_mode.size.h);
|
||||
new_cur_wlr_mode.refresh(new_cur_mode.refresh);
|
||||
|
||||
if Some(new_cur_mode) == output.preferred_mode() {
|
||||
new_cur_wlr_mode.preferred();
|
||||
}
|
||||
|
||||
head.mode(&new_cur_wlr_mode);
|
||||
head.current_mode(&new_cur_wlr_mode);
|
||||
wlr_modes.push(new_cur_wlr_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output_data.current_mode = Some(new_cur_mode);
|
||||
}
|
||||
}
|
||||
|
||||
if output.current_location() != output_data.position {
|
||||
let new_loc = output.current_location();
|
||||
head.position(new_loc.x, new_loc.y);
|
||||
output_data.position = new_loc;
|
||||
}
|
||||
|
||||
if output.current_transform() != output_data.transform {
|
||||
let new_transform = output.current_transform();
|
||||
head.transform(new_transform.into());
|
||||
output_data.transform = new_transform;
|
||||
}
|
||||
|
||||
if output.current_scale().fractional_scale() != output_data.scale {
|
||||
let new_scale = output.current_scale().fractional_scale();
|
||||
head.scale(new_scale);
|
||||
output_data.scale = new_scale;
|
||||
}
|
||||
|
||||
// TODO: adaptive sync
|
||||
}
|
||||
|
||||
let serial = u32::from(SERIAL_COUNTER.next_serial());
|
||||
|
||||
manager_data.serial = serial;
|
||||
manager.done(serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn advertise_output<D>(
|
||||
display: &DisplayHandle,
|
||||
manager: &ZwlrOutputManagerV1,
|
||||
output: &Output,
|
||||
) -> (ZwlrOutputHeadV1, Vec<ZwlrOutputModeV1>)
|
||||
where
|
||||
D: Dispatch<ZwlrOutputHeadV1, Output>
|
||||
+ Dispatch<ZwlrOutputModeV1, Mode>
|
||||
+ OutputManagementHandler
|
||||
+ 'static,
|
||||
{
|
||||
let client = manager.client().expect("TODO");
|
||||
|
||||
let head = client
|
||||
.create_resource::<ZwlrOutputHeadV1, _, D>(display, manager.version(), output.clone())
|
||||
.unwrap();
|
||||
|
||||
manager.head(&head);
|
||||
|
||||
head.name(output.name());
|
||||
head.description(output.description());
|
||||
|
||||
let physical_props = output.physical_properties();
|
||||
head.physical_size(physical_props.size.w, physical_props.size.h);
|
||||
|
||||
let mut wlr_modes = Vec::new();
|
||||
for mode in output.modes() {
|
||||
let wlr_mode = client
|
||||
.create_resource::<ZwlrOutputModeV1, _, D>(display, manager.version(), mode)
|
||||
.unwrap();
|
||||
head.mode(&wlr_mode);
|
||||
wlr_mode.size(mode.size.w, mode.size.h);
|
||||
wlr_mode.refresh(mode.refresh);
|
||||
if Some(mode) == output.preferred_mode() {
|
||||
wlr_mode.preferred();
|
||||
}
|
||||
wlr_modes.push(wlr_mode);
|
||||
}
|
||||
|
||||
if head.version() >= zwlr_output_head_v1::EVT_MAKE_SINCE {
|
||||
head.make(physical_props.make);
|
||||
head.model(physical_props.model);
|
||||
|
||||
if let Some(serial_number) = output.with_state(|state| state.serial) {
|
||||
head.serial_number(serial_number.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// SINCE FOUR
|
||||
// head.adaptive_sync(match data.adaptive_sync {
|
||||
// true => AdaptiveSyncState::Enabled,
|
||||
// false => AdaptiveSyncState::Disabled,
|
||||
// });
|
||||
|
||||
// TODO:
|
||||
// head.enabled(data.enabled as i32);
|
||||
head.enabled(true as i32);
|
||||
if true
|
||||
/* data.enabled */
|
||||
{
|
||||
if let Some(current_mode) = output.current_mode() {
|
||||
let wlr_current_mode = wlr_modes
|
||||
.iter()
|
||||
.find(|wlr_mode| wlr_mode.data::<Mode>() == Some(¤t_mode));
|
||||
if let Some(wlr_current_mode) = wlr_current_mode {
|
||||
head.current_mode(wlr_current_mode);
|
||||
}
|
||||
}
|
||||
head.position(output.current_location().x, output.current_location().y);
|
||||
head.transform(output.current_transform().into());
|
||||
head.scale(output.current_scale().fractional_scale());
|
||||
}
|
||||
|
||||
(head, wlr_modes)
|
||||
}
|
||||
|
||||
fn manager_for_configuration<'a, D>(
|
||||
state: &'a mut D,
|
||||
configuration: &ZwlrOutputConfigurationV1,
|
||||
) -> Option<(&'a ZwlrOutputManagerV1, &'a mut OutputManagerData)>
|
||||
where
|
||||
D: OutputManagementHandler,
|
||||
{
|
||||
state
|
||||
.output_management_manager_state()
|
||||
.managers
|
||||
.iter_mut()
|
||||
.find(|(_, manager_data)| manager_data.configurations.contains(configuration))
|
||||
}
|
||||
|
||||
impl OutputManagementManagerState {
|
||||
pub fn new<D, F>(display: &DisplayHandle, filter: F) -> Self
|
||||
where
|
||||
D: GlobalDispatch<ZwlrOutputManagerV1, OutputManagementGlobalData>
|
||||
+ Dispatch<ZwlrOutputManagerV1, ()>
|
||||
+ 'static,
|
||||
F: Fn(&Client) -> bool + Send + Sync + 'static,
|
||||
{
|
||||
let global_data = OutputManagementGlobalData {
|
||||
filter: Box::new(filter),
|
||||
};
|
||||
|
||||
display.create_global::<D, ZwlrOutputManagerV1, _>(VERSION, global_data);
|
||||
|
||||
Self {
|
||||
display_handle: display.clone(),
|
||||
managers: HashMap::new(),
|
||||
outputs: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> GlobalDispatch<ZwlrOutputManagerV1, OutputManagementGlobalData, D>
|
||||
for OutputManagementManagerState
|
||||
where
|
||||
D: GlobalDispatch<ZwlrOutputManagerV1, OutputManagementGlobalData>
|
||||
+ Dispatch<ZwlrOutputManagerV1, ()>
|
||||
+ Dispatch<ZwlrOutputHeadV1, Output>
|
||||
+ Dispatch<ZwlrOutputModeV1, Mode>
|
||||
+ OutputManagementHandler,
|
||||
{
|
||||
fn bind(
|
||||
state: &mut D,
|
||||
handle: &DisplayHandle,
|
||||
_client: &Client,
|
||||
resource: wayland_server::New<ZwlrOutputManagerV1>,
|
||||
_global_data: &OutputManagementGlobalData,
|
||||
data_init: &mut DataInit<'_, D>,
|
||||
) {
|
||||
let manager = data_init.init(resource, ());
|
||||
|
||||
let heads = state
|
||||
.output_management_manager_state()
|
||||
.outputs
|
||||
.keys()
|
||||
.map(|output| advertise_output::<D>(handle, &manager, output))
|
||||
.collect();
|
||||
|
||||
let serial = u32::from(SERIAL_COUNTER.next_serial());
|
||||
|
||||
manager.done(serial);
|
||||
|
||||
let state = state.output_management_manager_state();
|
||||
|
||||
let data = OutputManagerData {
|
||||
serial,
|
||||
configurations: Vec::new(),
|
||||
heads,
|
||||
};
|
||||
|
||||
state.managers.insert(manager, data);
|
||||
}
|
||||
|
||||
fn can_view(client: Client, global_data: &OutputManagementGlobalData) -> bool {
|
||||
(global_data.filter)(&client)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Dispatch<ZwlrOutputManagerV1, (), D> for OutputManagementManagerState
|
||||
where
|
||||
D: Dispatch<ZwlrOutputManagerV1, ()> + OutputManagementHandler,
|
||||
D: Dispatch<ZwlrOutputConfigurationV1, PendingOutputConfiguration> + OutputManagementHandler,
|
||||
{
|
||||
fn request(
|
||||
state: &mut D,
|
||||
_client: &Client,
|
||||
resource: &ZwlrOutputManagerV1,
|
||||
request: <ZwlrOutputManagerV1 as wayland_server::Resource>::Request,
|
||||
_data: &(),
|
||||
_dhandle: &DisplayHandle,
|
||||
data_init: &mut DataInit<'_, D>,
|
||||
) {
|
||||
match request {
|
||||
zwlr_output_manager_v1::Request::CreateConfiguration { id, serial } => {
|
||||
let Some(manager_data) = state
|
||||
.output_management_manager_state()
|
||||
.managers
|
||||
.get_mut(resource)
|
||||
else {
|
||||
let config = PendingOutputConfiguration {
|
||||
serial,
|
||||
inner: Mutex::new(PendingOutputConfigurationInner {
|
||||
cancelled: false,
|
||||
pending_heads: HashMap::new(),
|
||||
}),
|
||||
};
|
||||
|
||||
let config = data_init.init(id, config);
|
||||
|
||||
config.cancelled();
|
||||
return;
|
||||
};
|
||||
|
||||
let pending_heads = manager_data
|
||||
.heads
|
||||
.keys()
|
||||
.map(|head| (head.clone(), PendingHead::NotConfigured))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let config = PendingOutputConfiguration {
|
||||
serial,
|
||||
inner: Mutex::new(PendingOutputConfigurationInner {
|
||||
cancelled: false,
|
||||
pending_heads,
|
||||
}),
|
||||
};
|
||||
|
||||
let config = data_init.init(id, config);
|
||||
|
||||
let correct_serial = manager_data.serial == serial;
|
||||
|
||||
if !correct_serial {
|
||||
config.cancelled();
|
||||
return;
|
||||
}
|
||||
|
||||
manager_data.configurations.push(config);
|
||||
}
|
||||
zwlr_output_manager_v1::Request::Stop => {
|
||||
resource.finished();
|
||||
|
||||
state
|
||||
.output_management_manager_state()
|
||||
.managers
|
||||
.remove(resource);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn destroyed(state: &mut D, _client: ClientId, resource: &ZwlrOutputManagerV1, _data: &()) {
|
||||
state
|
||||
.output_management_manager_state()
|
||||
.managers
|
||||
.remove(resource);
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Dispatch<ZwlrOutputHeadV1, Output, D> for OutputManagementManagerState {
|
||||
fn request(
|
||||
state: &mut D,
|
||||
client: &Client,
|
||||
resource: &ZwlrOutputHeadV1,
|
||||
request: <ZwlrOutputHeadV1 as Resource>::Request,
|
||||
data: &Output,
|
||||
dhandle: &DisplayHandle,
|
||||
data_init: &mut DataInit<'_, D>,
|
||||
) {
|
||||
match request {
|
||||
zwlr_output_head_v1::Request::Release => {
|
||||
// TODO:
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Dispatch<ZwlrOutputModeV1, Mode, D> for OutputManagementManagerState {
|
||||
fn request(
|
||||
state: &mut D,
|
||||
client: &Client,
|
||||
resource: &ZwlrOutputModeV1,
|
||||
request: <ZwlrOutputModeV1 as Resource>::Request,
|
||||
data: &Mode,
|
||||
dhandle: &DisplayHandle,
|
||||
data_init: &mut DataInit<'_, D>,
|
||||
) {
|
||||
match request {
|
||||
zwlr_output_mode_v1::Request::Release => {
|
||||
// TODO:
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Dispatch<ZwlrOutputConfigurationV1, PendingOutputConfiguration, D>
|
||||
for OutputManagementManagerState
|
||||
where
|
||||
D: Dispatch<ZwlrOutputManagerV1, ()>
|
||||
+ Dispatch<ZwlrOutputConfigurationHeadV1, Mutex<PendingOutputHeadConfiguration>>
|
||||
+ OutputManagementHandler,
|
||||
{
|
||||
fn request(
|
||||
state: &mut D,
|
||||
_client: &Client,
|
||||
resource: &ZwlrOutputConfigurationV1,
|
||||
request: <ZwlrOutputConfigurationV1 as Resource>::Request,
|
||||
pending_data: &PendingOutputConfiguration,
|
||||
_dhandle: &DisplayHandle,
|
||||
data_init: &mut DataInit<'_, D>,
|
||||
) {
|
||||
match request {
|
||||
zwlr_output_configuration_v1::Request::EnableHead { id, head } => {
|
||||
let config_head =
|
||||
data_init.init(id, Mutex::new(PendingOutputHeadConfiguration::default()));
|
||||
|
||||
let mut data = pending_data.inner.lock().unwrap();
|
||||
|
||||
let manager_serial =
|
||||
manager_for_configuration(state, resource).map(|(_, data)| data.serial);
|
||||
|
||||
if manager_serial != Some(pending_data.serial) {
|
||||
resource.cancelled();
|
||||
data.cancelled = true;
|
||||
}
|
||||
|
||||
if data.cancelled {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(pending_data) = data.pending_heads.get_mut(&head) {
|
||||
if !matches!(pending_data, PendingHead::NotConfigured) {
|
||||
head.post_error(
|
||||
zwlr_output_configuration_v1::Error::AlreadyConfiguredHead,
|
||||
"head has already been configured",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
*pending_data = PendingHead::Enabled(config_head);
|
||||
}
|
||||
}
|
||||
zwlr_output_configuration_v1::Request::DisableHead { head } => {
|
||||
let mut data = pending_data.inner.lock().unwrap();
|
||||
|
||||
let manager_serial =
|
||||
manager_for_configuration(state, resource).map(|(_, data)| data.serial);
|
||||
|
||||
if manager_serial != Some(pending_data.serial) {
|
||||
resource.cancelled();
|
||||
data.cancelled = true;
|
||||
}
|
||||
|
||||
if data.cancelled {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(pending_data) = data.pending_heads.get_mut(&head) {
|
||||
if !matches!(pending_data, PendingHead::NotConfigured) {
|
||||
head.post_error(
|
||||
zwlr_output_configuration_v1::Error::AlreadyConfiguredHead,
|
||||
"head has already been configured",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
*pending_data = PendingHead::Disabled;
|
||||
}
|
||||
}
|
||||
req @ (zwlr_output_configuration_v1::Request::Apply
|
||||
| zwlr_output_configuration_v1::Request::Test) => {
|
||||
let mut data = pending_data.inner.lock().unwrap();
|
||||
|
||||
let manager_serial =
|
||||
manager_for_configuration(state, resource).map(|(_, data)| data.serial);
|
||||
|
||||
if manager_serial != Some(pending_data.serial) {
|
||||
resource.cancelled();
|
||||
data.cancelled = true;
|
||||
}
|
||||
|
||||
if data.cancelled {
|
||||
return;
|
||||
}
|
||||
|
||||
if data
|
||||
.pending_heads
|
||||
.values()
|
||||
.any(|cfg| matches!(cfg, PendingHead::NotConfigured))
|
||||
{
|
||||
resource.post_error(
|
||||
zwlr_output_configuration_v1::Error::UnconfiguredHead,
|
||||
"a head was unconfigured",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let config = data
|
||||
.pending_heads
|
||||
.iter()
|
||||
.map(|(head, head_cfg)| {
|
||||
let output = head.data::<Output>().unwrap().clone();
|
||||
|
||||
let cfg = match head_cfg {
|
||||
PendingHead::NotConfigured => unreachable!(),
|
||||
PendingHead::Enabled(cfg_head) => {
|
||||
let pending = cfg_head
|
||||
.data::<Mutex<PendingOutputHeadConfiguration>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
OutputConfiguration::Enabled {
|
||||
mode: pending.mode,
|
||||
position: pending.position,
|
||||
transform: pending.transform,
|
||||
scale: pending.scale,
|
||||
adaptive_sync: pending.adaptive_sync,
|
||||
}
|
||||
}
|
||||
PendingHead::Disabled => OutputConfiguration::Disabled,
|
||||
};
|
||||
|
||||
(output, cfg)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let apply = matches!(req, zwlr_output_configuration_v1::Request::Apply);
|
||||
let success = if apply {
|
||||
state.apply_configuration(config)
|
||||
} else {
|
||||
state.test_configuration(config)
|
||||
};
|
||||
|
||||
if success {
|
||||
resource.succeeded();
|
||||
} else {
|
||||
resource.failed();
|
||||
}
|
||||
}
|
||||
zwlr_output_configuration_v1::Request::Destroy => (),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn destroyed(
|
||||
state: &mut D,
|
||||
_client: ClientId,
|
||||
resource: &ZwlrOutputConfigurationV1,
|
||||
_data: &PendingOutputConfiguration,
|
||||
) {
|
||||
for output_manager_data in state
|
||||
.output_management_manager_state()
|
||||
.managers
|
||||
.values_mut()
|
||||
{
|
||||
output_manager_data
|
||||
.configurations
|
||||
.retain(|config| config != resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Dispatch<ZwlrOutputConfigurationHeadV1, Mutex<PendingOutputHeadConfiguration>, D>
|
||||
for OutputManagementManagerState
|
||||
where
|
||||
D: Dispatch<ZwlrOutputModeV1, Mode> + 'static,
|
||||
{
|
||||
fn request(
|
||||
_state: &mut D,
|
||||
_client: &Client,
|
||||
resource: &ZwlrOutputConfigurationHeadV1,
|
||||
request: <ZwlrOutputConfigurationHeadV1 as Resource>::Request,
|
||||
data: &Mutex<PendingOutputHeadConfiguration>,
|
||||
_dhandle: &DisplayHandle,
|
||||
_data_init: &mut DataInit<'_, D>,
|
||||
) {
|
||||
match request {
|
||||
zwlr_output_configuration_head_v1::Request::SetMode { mode } => {
|
||||
let mut data = data.lock().unwrap();
|
||||
if data.mode.is_some() {
|
||||
resource.post_error(
|
||||
zwlr_output_configuration_head_v1::Error::AlreadySet,
|
||||
"mode has already been set",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let mode = mode.data::<Mode>().unwrap();
|
||||
|
||||
let mode = (mode.size, NonZeroU32::new(mode.refresh as u32));
|
||||
|
||||
data.mode = Some(mode);
|
||||
}
|
||||
zwlr_output_configuration_head_v1::Request::SetCustomMode {
|
||||
width,
|
||||
height,
|
||||
refresh,
|
||||
} => {
|
||||
let mut data = data.lock().unwrap();
|
||||
if data.mode.is_some() {
|
||||
resource.post_error(
|
||||
zwlr_output_configuration_head_v1::Error::AlreadySet,
|
||||
"mode has already been set",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if width <= 0 || height <= 0 || refresh < 0 {
|
||||
resource.post_error(
|
||||
zwlr_output_configuration_head_v1::Error::InvalidCustomMode,
|
||||
"invalid custom mode",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
data.mode = Some(((width, height).into(), NonZeroU32::new(refresh as u32)));
|
||||
}
|
||||
zwlr_output_configuration_head_v1::Request::SetPosition { x, y } => {
|
||||
let mut data = data.lock().unwrap();
|
||||
if data.position.is_some() {
|
||||
resource.post_error(
|
||||
zwlr_output_configuration_head_v1::Error::AlreadySet,
|
||||
"position has already been set",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
data.position = Some((x, y).into());
|
||||
}
|
||||
zwlr_output_configuration_head_v1::Request::SetTransform { transform } => {
|
||||
let mut data = data.lock().unwrap();
|
||||
if data.transform.is_some() {
|
||||
resource.post_error(
|
||||
zwlr_output_configuration_head_v1::Error::AlreadySet,
|
||||
"transform has already been set",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let transform = match transform {
|
||||
WEnum::Value(transform) => transform,
|
||||
WEnum::Unknown(val) => {
|
||||
resource.post_error(
|
||||
zwlr_output_configuration_head_v1::Error::InvalidTransform,
|
||||
format!("transform has an invalid value of {val}"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
data.transform = Some(transform.into());
|
||||
}
|
||||
zwlr_output_configuration_head_v1::Request::SetScale { scale } => {
|
||||
let mut data = data.lock().unwrap();
|
||||
if data.scale.is_some() {
|
||||
resource.post_error(
|
||||
zwlr_output_configuration_head_v1::Error::AlreadySet,
|
||||
"scale has already been set",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
data.scale = Some(scale);
|
||||
}
|
||||
zwlr_output_configuration_head_v1::Request::SetAdaptiveSync { state } => {
|
||||
let mut data = data.lock().unwrap();
|
||||
if data.adaptive_sync.is_some() {
|
||||
resource.post_error(
|
||||
zwlr_output_configuration_head_v1::Error::AlreadySet,
|
||||
"adaptive sync has already been set",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let adaptive_sync = match state {
|
||||
WEnum::Value(adaptive_sync) => match adaptive_sync {
|
||||
AdaptiveSyncState::Disabled => false,
|
||||
AdaptiveSyncState::Enabled => true,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
WEnum::Unknown(val) => {
|
||||
resource.post_error(
|
||||
zwlr_output_configuration_head_v1::Error::InvalidAdaptiveSyncState,
|
||||
format!("adaptive sync has an invalid value of {val}"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
data.adaptive_sync = Some(adaptive_sync);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! delegate_output_management {
|
||||
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
|
||||
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||
smithay::reexports::wayland_protocols_wlr::output_management::v1::server::zwlr_output_manager_v1::ZwlrOutputManagerV1: $crate::protocol::output_management::OutputManagementGlobalData
|
||||
] => $crate::protocol::output_management::OutputManagementManagerState);
|
||||
|
||||
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||
smithay::reexports::wayland_protocols_wlr::output_management::v1::server::zwlr_output_manager_v1::ZwlrOutputManagerV1: ()
|
||||
] => $crate::protocol::output_management::OutputManagementManagerState);
|
||||
|
||||
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||
smithay::reexports::wayland_protocols_wlr::output_management::v1::server::zwlr_output_head_v1::ZwlrOutputHeadV1: smithay::output::Output
|
||||
] => $crate::protocol::output_management::OutputManagementManagerState);
|
||||
|
||||
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||
smithay::reexports::wayland_protocols_wlr::output_management::v1::server::zwlr_output_mode_v1::ZwlrOutputModeV1: smithay::output::Mode
|
||||
] => $crate::protocol::output_management::OutputManagementManagerState);
|
||||
|
||||
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||
smithay::reexports::wayland_protocols_wlr::output_management::v1::server::zwlr_output_configuration_v1::ZwlrOutputConfigurationV1: $crate::protocol::output_management::PendingOutputConfiguration
|
||||
] => $crate::protocol::output_management::OutputManagementManagerState);
|
||||
|
||||
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||
smithay::reexports::wayland_protocols_wlr::output_management::v1::server::zwlr_output_configuration_head_v1::ZwlrOutputConfigurationHeadV1: std::sync::Mutex<$crate::protocol::output_management::PendingOutputHeadConfiguration>
|
||||
] => $crate::protocol::output_management::OutputManagementManagerState);
|
||||
};
|
||||
}
|
19
src/state.rs
19
src/state.rs
|
@ -12,6 +12,7 @@ use crate::{
|
|||
protocol::{
|
||||
foreign_toplevel::{self, ForeignToplevelManagerState},
|
||||
gamma_control::GammaControlManagerState,
|
||||
output_management::OutputManagementManagerState,
|
||||
screencopy::ScreencopyManagerState,
|
||||
},
|
||||
window::WindowElement,
|
||||
|
@ -52,7 +53,12 @@ use smithay::{
|
|||
},
|
||||
xwayland::{X11Wm, XWaylandClientData},
|
||||
};
|
||||
use std::{cell::RefCell, collections::HashMap, path::PathBuf, sync::Arc};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::{HashMap, HashSet},
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
};
|
||||
use sysinfo::{ProcessRefreshKind, RefreshKind};
|
||||
use tracing::{info, warn};
|
||||
use xdg::BaseDirectories;
|
||||
|
@ -101,6 +107,7 @@ pub struct Pinnacle {
|
|||
pub session_lock_manager_state: SessionLockManagerState,
|
||||
pub xwayland_shell_state: XWaylandShellState,
|
||||
pub idle_notifier_state: IdleNotifierState<State>,
|
||||
pub output_management_manager_state: OutputManagementManagerState,
|
||||
|
||||
pub lock_state: LockState,
|
||||
|
||||
|
@ -139,6 +146,9 @@ pub struct Pinnacle {
|
|||
|
||||
/// A cache of surfaces to their root surface.
|
||||
pub root_surface_cache: HashMap<WlSurface, WlSurface>,
|
||||
|
||||
/// WlSurfaces with an attached idle inhibitor.
|
||||
pub idle_inhibiting_surfaces: HashSet<WlSurface>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
|
@ -148,6 +158,7 @@ impl State {
|
|||
self.pinnacle.popup_manager.cleanup();
|
||||
self.update_pointer_focus();
|
||||
foreign_toplevel::refresh(self);
|
||||
self.pinnacle.refresh_idle_inhibit();
|
||||
|
||||
if let Backend::Winit(winit) = &mut self.backend {
|
||||
winit.render_if_scheduled(&mut self.pinnacle);
|
||||
|
@ -290,6 +301,10 @@ impl Pinnacle {
|
|||
),
|
||||
xwayland_shell_state: XWaylandShellState::new::<State>(&display_handle),
|
||||
idle_notifier_state: IdleNotifierState::new(&display_handle, loop_handle),
|
||||
output_management_manager_state: OutputManagementManagerState::new::<State, _>(
|
||||
&display_handle,
|
||||
filter_restricted_client,
|
||||
),
|
||||
|
||||
lock_state: LockState::default(),
|
||||
|
||||
|
@ -326,6 +341,8 @@ impl Pinnacle {
|
|||
layout_state: LayoutState::default(),
|
||||
|
||||
root_surface_cache: HashMap::new(),
|
||||
|
||||
idle_inhibiting_surfaces: HashSet::new(),
|
||||
};
|
||||
|
||||
Ok(pinnacle)
|
||||
|
|
Loading…
Reference in a new issue