use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::iter::zip;
use std::mem;
use niri_config::{FloatOrInt, OutputName, Vrr};
use niri_ipc::Transform;
use smithay::reexports::wayland_protocols_wlr::output_management::v1::server::{
zwlr_output_configuration_head_v1, zwlr_output_configuration_v1, zwlr_output_head_v1,
zwlr_output_manager_v1, zwlr_output_mode_v1,
};
use smithay::reexports::wayland_server::backend::ClientId;
use smithay::reexports::wayland_server::protocol::wl_output::Transform as WlTransform;
use smithay::reexports::wayland_server::{
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, WEnum,
};
use zwlr_output_configuration_head_v1::ZwlrOutputConfigurationHeadV1;
use zwlr_output_configuration_v1::ZwlrOutputConfigurationV1;
use zwlr_output_head_v1::{AdaptiveSyncState, ZwlrOutputHeadV1};
use zwlr_output_manager_v1::ZwlrOutputManagerV1;
use zwlr_output_mode_v1::ZwlrOutputModeV1;
use crate::backend::OutputId;
use crate::niri::State;
use crate::utils::ipc_transform_to_smithay;
const VERSION: u32 = 4;
#[derive(Debug)]
struct ClientData {
heads: HashMap<OutputId, (ZwlrOutputHeadV1, Vec<ZwlrOutputModeV1>)>,
confs: HashMap<ZwlrOutputConfigurationV1, OutputConfigurationState>,
manager: ZwlrOutputManagerV1,
}
pub struct OutputManagementManagerState {
display: DisplayHandle,
serial: u32,
clients: HashMap<ClientId, ClientData>,
current_state: HashMap<OutputId, niri_ipc::Output>,
current_config: niri_config::Outputs,
}
pub struct OutputManagementManagerGlobalData {
filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>,
}
pub trait OutputManagementHandler {
fn output_management_state(&mut self) -> &mut OutputManagementManagerState;
fn apply_output_config(&mut self, config: niri_config::Outputs);
}
#[derive(Debug)]
enum OutputConfigurationState {
Ongoing(HashMap<OutputId, niri_config::Output>),
Finished,
}
pub enum OutputConfigurationHeadState {
Cancelled,
Ok(OutputId, ZwlrOutputConfigurationV1),
}
impl OutputManagementManagerState {
pub fn new<D, F>(display: &DisplayHandle, filter: F) -> Self
where
D: GlobalDispatch<ZwlrOutputManagerV1, OutputManagementManagerGlobalData>,
D: Dispatch<ZwlrOutputManagerV1, ()>,
D: Dispatch<ZwlrOutputHeadV1, OutputId>,
D: Dispatch<ZwlrOutputConfigurationV1, u32>,
D: Dispatch<ZwlrOutputConfigurationHeadV1, OutputConfigurationHeadState>,
D: Dispatch<ZwlrOutputModeV1, ()>,
D: OutputManagementHandler,
D: 'static,
F: for<'c> Fn(&'c Client) -> bool + Send + Sync + 'static,
{
let global_data = OutputManagementManagerGlobalData {
filter: Box::new(filter),
};
display.create_global::<D, ZwlrOutputManagerV1, _>(VERSION, global_data);
Self {
display: display.clone(),
clients: HashMap::new(),
serial: 0,
current_state: HashMap::new(),
current_config: Default::default(),
}
}
pub fn on_config_changed(&mut self, new_config: niri_config::Outputs) {
self.current_config = new_config;
}
pub fn notify_changes(&mut self, new_state: HashMap<OutputId, niri_ipc::Output>) {
let mut changed = false; /* most likely to end up true */
for (output, conf) in new_state.iter() {
if let Some(old) = self.current_state.get(output) {
if old.vrr_enabled != conf.vrr_enabled {
changed = true;
for client in self.clients.values() {
if let Some((head, _)) = client.heads.get(output) {
if head.version() >= zwlr_output_head_v1::EVT_ADAPTIVE_SYNC_SINCE {
head.adaptive_sync(match conf.vrr_enabled {
true => AdaptiveSyncState::Enabled,
false => AdaptiveSyncState::Disabled,
});
}
}
}
}
// TTY outputs can't change modes I think, however, winit and virtual outputs can.
let modes_changed = old.modes != conf.modes;
if modes_changed {
changed = true;
if old.modes.len() != conf