mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-26 21:58:10 +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);
|
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.with_state_mut(|state| state.serial = serial);
|
||||||
|
|
||||||
output.set_preferred(wl_mode);
|
output.set_preferred(wl_mode);
|
||||||
|
@ -1215,6 +1219,10 @@ impl Udev {
|
||||||
output_name: Some(output.name()),
|
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
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
pub mod idle;
|
||||||
pub mod session_lock;
|
pub mod session_lock;
|
||||||
pub mod window;
|
pub mod window;
|
||||||
mod xdg_shell;
|
mod xdg_shell;
|
||||||
mod xwayland;
|
mod xwayland;
|
||||||
|
|
||||||
use std::{mem, os::fd::OwnedFd, sync::Arc};
|
use std::{collections::HashMap, mem, os::fd::OwnedFd, sync::Arc};
|
||||||
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::renderer::utils::{self, with_renderer_surface_state},
|
backend::renderer::utils::{self, with_renderer_surface_state},
|
||||||
delegate_compositor, delegate_data_control, delegate_data_device, delegate_fractional_scale,
|
delegate_compositor, delegate_data_control, delegate_data_device, delegate_fractional_scale,
|
||||||
delegate_idle_notify, delegate_layer_shell, delegate_output, delegate_pointer_constraints,
|
delegate_layer_shell, delegate_output, delegate_pointer_constraints, delegate_presentation,
|
||||||
delegate_presentation, delegate_primary_selection, delegate_relative_pointer, delegate_seat,
|
delegate_primary_selection, delegate_relative_pointer, delegate_seat,
|
||||||
delegate_security_context, delegate_shm, delegate_viewporter, delegate_xwayland_shell,
|
delegate_security_context, delegate_shm, delegate_viewporter, delegate_xwayland_shell,
|
||||||
desktop::{
|
desktop::{
|
||||||
self, find_popup_root_surface, get_popup_toplevel_coords, layer_map_for_output, PopupKind,
|
self, find_popup_root_surface, get_popup_toplevel_coords, layer_map_for_output, PopupKind,
|
||||||
|
@ -21,7 +22,7 @@ use smithay::{
|
||||||
pointer::{CursorImageStatus, PointerHandle},
|
pointer::{CursorImageStatus, PointerHandle},
|
||||||
Seat, SeatHandler, SeatState,
|
Seat, SeatHandler, SeatState,
|
||||||
},
|
},
|
||||||
output::Output,
|
output::{Output, Scale},
|
||||||
reexports::{
|
reexports::{
|
||||||
calloop::Interest,
|
calloop::Interest,
|
||||||
wayland_protocols::xdg::shell::server::xdg_positioner::ConstraintAdjustment,
|
wayland_protocols::xdg::shell::server::xdg_positioner::ConstraintAdjustment,
|
||||||
|
@ -42,7 +43,6 @@ use smithay::{
|
||||||
},
|
},
|
||||||
dmabuf,
|
dmabuf,
|
||||||
fractional_scale::{self, FractionalScaleHandler},
|
fractional_scale::{self, FractionalScaleHandler},
|
||||||
idle_notify::{IdleNotifierHandler, IdleNotifierState},
|
|
||||||
output::OutputHandler,
|
output::OutputHandler,
|
||||||
pointer_constraints::{with_pointer_constraint, PointerConstraintsHandler},
|
pointer_constraints::{with_pointer_constraint, PointerConstraintsHandler},
|
||||||
seat::WaylandFocus,
|
seat::WaylandFocus,
|
||||||
|
@ -69,16 +69,20 @@ use smithay::{
|
||||||
},
|
},
|
||||||
xwayland::{X11Wm, XWaylandClientData},
|
xwayland::{X11Wm, XWaylandClientData},
|
||||||
};
|
};
|
||||||
use tracing::{error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::Backend,
|
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},
|
focus::{keyboard::KeyboardFocusTarget, pointer::PointerFocusTarget},
|
||||||
handlers::xdg_shell::snapshot_pre_commit_hook,
|
handlers::xdg_shell::snapshot_pre_commit_hook,
|
||||||
protocol::{
|
protocol::{
|
||||||
foreign_toplevel::{self, ForeignToplevelHandler, ForeignToplevelManagerState},
|
foreign_toplevel::{self, ForeignToplevelHandler, ForeignToplevelManagerState},
|
||||||
gamma_control::{GammaControlHandler, GammaControlManagerState},
|
gamma_control::{GammaControlHandler, GammaControlManagerState},
|
||||||
|
output_management::{
|
||||||
|
OutputConfiguration, OutputManagementHandler, OutputManagementManagerState,
|
||||||
|
},
|
||||||
screencopy::{Screencopy, ScreencopyHandler},
|
screencopy::{Screencopy, ScreencopyHandler},
|
||||||
},
|
},
|
||||||
render::util::snapshot::capture_snapshots_on_output,
|
render::util::snapshot::capture_snapshots_on_output,
|
||||||
|
@ -918,12 +922,57 @@ impl XWaylandShellHandler for State {
|
||||||
}
|
}
|
||||||
delegate_xwayland_shell!(State);
|
delegate_xwayland_shell!(State);
|
||||||
|
|
||||||
impl IdleNotifierHandler for State {
|
impl OutputManagementHandler for State {
|
||||||
fn idle_notifier_state(&mut self) -> &mut IdleNotifierState<Self> {
|
fn output_management_manager_state(&mut self) -> &mut OutputManagementManagerState {
|
||||||
&mut self.pinnacle.idle_notifier_state
|
&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 {
|
impl Pinnacle {
|
||||||
fn position_popup(&self, popup: &PopupSurface) {
|
fn position_popup(&self, popup: &PopupSurface) {
|
||||||
|
|
|
@ -32,20 +32,24 @@ impl Pinnacle {
|
||||||
output: &Output,
|
output: &Output,
|
||||||
geometries: Vec<Rectangle<i32, Logical>>,
|
geometries: Vec<Rectangle<i32, Logical>>,
|
||||||
) -> Vec<(WindowElement, Serial)> {
|
) -> 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<_>>();
|
let focused_tags = state.focused_tags().collect::<Vec<_>>();
|
||||||
self.windows
|
self.windows
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|win| !win.is_x11_override_redirect())
|
.filter(|win| win.output(self).as_ref() == Some(output))
|
||||||
.filter(|win| {
|
.cloned()
|
||||||
|
.partition::<Vec<_>, _>(|win| {
|
||||||
win.with_state(|state| state.tags.iter().any(|tg| focused_tags.contains(&tg)))
|
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
|
let tiled_windows = windows_on_foc_tags
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|win| !win.is_x11_override_redirect())
|
||||||
.filter(|win| {
|
.filter(|win| {
|
||||||
win.with_state(|state| {
|
win.with_state(|state| {
|
||||||
state.floating_or_tiled.is_tiled() && state.fullscreen_or_maximized.is_neither()
|
state.floating_or_tiled.is_tiled() && state.fullscreen_or_maximized.is_neither()
|
||||||
|
|
|
@ -219,5 +219,8 @@ impl Pinnacle {
|
||||||
|
|
||||||
lock_surface.send_configure();
|
lock_surface.send_configure();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.output_management_manager_state
|
||||||
|
.update_head::<State>(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
pub mod foreign_toplevel;
|
pub mod foreign_toplevel;
|
||||||
pub mod gamma_control;
|
pub mod gamma_control;
|
||||||
|
pub mod output_management;
|
||||||
pub mod screencopy;
|
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::{
|
protocol::{
|
||||||
foreign_toplevel::{self, ForeignToplevelManagerState},
|
foreign_toplevel::{self, ForeignToplevelManagerState},
|
||||||
gamma_control::GammaControlManagerState,
|
gamma_control::GammaControlManagerState,
|
||||||
|
output_management::OutputManagementManagerState,
|
||||||
screencopy::ScreencopyManagerState,
|
screencopy::ScreencopyManagerState,
|
||||||
},
|
},
|
||||||
window::WindowElement,
|
window::WindowElement,
|
||||||
|
@ -52,7 +53,12 @@ use smithay::{
|
||||||
},
|
},
|
||||||
xwayland::{X11Wm, XWaylandClientData},
|
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 sysinfo::{ProcessRefreshKind, RefreshKind};
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
@ -101,6 +107,7 @@ pub struct Pinnacle {
|
||||||
pub session_lock_manager_state: SessionLockManagerState,
|
pub session_lock_manager_state: SessionLockManagerState,
|
||||||
pub xwayland_shell_state: XWaylandShellState,
|
pub xwayland_shell_state: XWaylandShellState,
|
||||||
pub idle_notifier_state: IdleNotifierState<State>,
|
pub idle_notifier_state: IdleNotifierState<State>,
|
||||||
|
pub output_management_manager_state: OutputManagementManagerState,
|
||||||
|
|
||||||
pub lock_state: LockState,
|
pub lock_state: LockState,
|
||||||
|
|
||||||
|
@ -139,6 +146,9 @@ pub struct Pinnacle {
|
||||||
|
|
||||||
/// A cache of surfaces to their root surface.
|
/// A cache of surfaces to their root surface.
|
||||||
pub root_surface_cache: HashMap<WlSurface, WlSurface>,
|
pub root_surface_cache: HashMap<WlSurface, WlSurface>,
|
||||||
|
|
||||||
|
/// WlSurfaces with an attached idle inhibitor.
|
||||||
|
pub idle_inhibiting_surfaces: HashSet<WlSurface>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
@ -148,6 +158,7 @@ impl State {
|
||||||
self.pinnacle.popup_manager.cleanup();
|
self.pinnacle.popup_manager.cleanup();
|
||||||
self.update_pointer_focus();
|
self.update_pointer_focus();
|
||||||
foreign_toplevel::refresh(self);
|
foreign_toplevel::refresh(self);
|
||||||
|
self.pinnacle.refresh_idle_inhibit();
|
||||||
|
|
||||||
if let Backend::Winit(winit) = &mut self.backend {
|
if let Backend::Winit(winit) = &mut self.backend {
|
||||||
winit.render_if_scheduled(&mut self.pinnacle);
|
winit.render_if_scheduled(&mut self.pinnacle);
|
||||||
|
@ -290,6 +301,10 @@ impl Pinnacle {
|
||||||
),
|
),
|
||||||
xwayland_shell_state: XWaylandShellState::new::<State>(&display_handle),
|
xwayland_shell_state: XWaylandShellState::new::<State>(&display_handle),
|
||||||
idle_notifier_state: IdleNotifierState::new(&display_handle, loop_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(),
|
lock_state: LockState::default(),
|
||||||
|
|
||||||
|
@ -326,6 +341,8 @@ impl Pinnacle {
|
||||||
layout_state: LayoutState::default(),
|
layout_state: LayoutState::default(),
|
||||||
|
|
||||||
root_surface_cache: HashMap::new(),
|
root_surface_cache: HashMap::new(),
|
||||||
|
|
||||||
|
idle_inhibiting_surfaces: HashSet::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(pinnacle)
|
Ok(pinnacle)
|
||||||
|
|
Loading…
Reference in a new issue