use {
    crate::{
        backend::{
            BackendColorSpace, BackendEotfs, ConnectorId, Mode,
            transaction::{
                BackendConnectorTransactionError, ConnectorTransaction,
                PreparedConnectorTransaction,
            },
        },
        cmm::cmm_luminance::Luminance,
        compositor::{MAX_EXTENTS, MAX_SCALE, MIN_SCALE},
        control_center::{ControlCenterInner, GridExt, PaneState, grid, grid_label, label, tip},
        egui_adapter::{
            egui_oklch::Color32Ext,
            egui_platform::icons::{ICON_ADD, ICON_REMOVE},
        },
        ifs::{
            head_management::{HeadName, HeadState, ReadOnlyHeadState},
            wl_output::BlendSpace,
        },
        scale::{SCALE_BASE, SCALE_BASEF, Scale},
        state::State,
        tree::{TearingMode, Transform, VrrMode},
        utils::errorfmt::ErrorFmt,
    },
    ahash::AHashMap,
    egui::{
        Align, Button, Checkbox, Color32, ComboBox, DragValue, EventFilter, FontId, Frame, Grid,
        Id, Key, Layout, PointerButton, Rect, ScrollArea, Sense, Shadow, Stroke, StrokeKind, Style,
        TextFormat, Ui, UiBuilder, Vec2, Widget, WidgetText, pos2, text::LayoutJob, vec2,
    },
    egui_tiles::{
        Behavior, Container, Linear, LinearDir, ResizeState, SimplificationOptions, Tile, TileId,
        Tiles, Tree, UiResponse,
    },
    linearize::{Linearize, LinearizeExt},
    rand::random,
    serde::{Deserialize, Serialize},
    std::{
        cell::{Cell, Ref},
        fmt,
        rc::Rc,
    },
    thiserror::Error,
};

pub struct OutputsPane {
    tree: Tree<Pane>,
    root_id: TileId,
    arrangement_id: Option<TileId>,
    inner: OutputsPaneInner,
}

struct OutputsPaneInner {
    state: Rc<State>,
    in_transaction: Cell<bool>,
    heads: AHashMap<HeadName, CompleteHead>,
    ui: UiSettings,
    settings: Settings,
    seed: u64,
}

enum Pane {
    Arrangement,
    Settings,
}

struct CompleteHead {
    id: ConnectorId,
    name: HeadName,
    live_state: ReadOnlyHeadState,
    changed_state: Option<HeadState>,
    z: u64,
    focus: u64,
    drag_pos: Option<(f32, f32)>,
}

struct UiSettings {
    scale: f32,
    origin: Vec2,
    origin_drag: Option<Vec2>,
    next_z: u64,
    focus: u64,
    zoom_to_fit: bool,
    view: View,
}

struct Settings {
    show_guide_lines: bool,
    snap_to_neighbor: bool,
    show_disconnected: bool,
    show_disabled: bool,
    show_arrangement: bool,
    layout: UiLayout,
}

impl Default for Settings {
    fn default() -> Self {
        Self {
            show_guide_lines: false,
            snap_to_neighbor: true,
            show_disconnected: false,
            show_disabled: true,
            show_arrangement: true,
            layout: UiLayout::Auto,
        }
    }
}

#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Linearize)]
enum UiLayout {
    Auto,
    Vertical,
    Horizontal,
}

#[derive(Copy, Clone)]
pub enum View {
    Connectors,
    Settings,
}

#[derive(Error, Debug)]
enum HeadTransactionError {
    #[error("The connector {} has been removed", .0)]
    HeadRemoved(ConnectorId),
    #[error("The display connected to connector {} has changed", .0)]
    MonitorChanged(ConnectorId),
    #[error(transparent)]
    Backend(#[from] BackendConnectorTransactionError),
}

macro_rules! effective {
    ($m:expr, $t:expr) => {
        $t.as_ref().unwrap_or($m)
    };
}

macro_rules! modify {
    ($m:expr, $t:expr) => {
        $t.get_or_insert_with(|| $m.clone())
    };
}

impl ControlCenterInner {
    pub fn create_outputs_pane(self: &Rc<Self>) -> OutputsPane {
        let seed = random();
        let mut tiles = Tiles::default();
        let settings_id = tiles.insert_pane(Pane::Settings);
        let arrangement_id = tiles.insert_pane(Pane::Arrangement);
        let root_id = tiles.insert_container(Linear::new(
            LinearDir::Horizontal,
            vec![arrangement_id, settings_id],
        ));
        let tree = Tree::new(Id::new(("cc_outputs", seed)), root_id, tiles);
        let mut pane = OutputsPane {
            root_id,
            arrangement_id: Some(arrangement_id),
            tree,
            inner: OutputsPaneInner {
                state: self.state.clone(),
                ui: UiSettings {
                    scale: 0.1,
                    origin: Default::default(),
                    origin_drag: None,
                    next_z: 0,
                    focus: 0,
                    zoom_to_fit: true,
                    view: View::Connectors,
                },
                settings: Default::default(),
                in_transaction: Default::default(),
                heads: Default::default(),
                seed,
            },
        };
        pane.inner.reset();
        pane
    }
}

struct B<'a>(&'a mut OutputsPaneInner, &'a mut PaneState);

impl Behavior<Pane> for B<'_> {
    fn pane_ui(&mut self, ui: &mut Ui, _tile_id: TileId, pane: &mut Pane) -> UiResponse {
        Frame::new().inner_margin(5.0).show(ui, |ui| match pane {
            Pane::Arrangement => self.0.show_arrangement(ui),
            Pane::Settings => self.0.show_main_area(self.1, ui),
        });
        UiResponse::None
    }

    fn tab_title_for_pane(&mut self, _pane: &Pane) -> WidgetText {
        "".into()
    }

    fn gap_width(&self, _style: &Style) -> f32 {
        5.0
    }

    fn simplification_options(&self) -> SimplificationOptions {
        SimplificationOptions {
            prune_empty_tabs: false,
            prune_empty_containers: false,
            prune_single_child_tabs: false,
            prune_single_child_containers: false,
            all_panes_must_have_tabs: false,
            join_nested_linear_containers: false,
        }
    }

    fn resize_stroke(&self, style: &Style, resize_state: ResizeState) -> Stroke {
        match resize_state {
            ResizeState::Idle => style.visuals.widgets.noninteractive.bg_stroke,
            ResizeState::Hovering => style.visuals.widgets.hovered.fg_stroke,
            ResizeState::Dragging => style.visuals.widgets.active.fg_stroke,
        }
    }
}

impl OutputsPane {
    pub fn title(&self, res: &mut String) {
        res.push_str("Outputs");
        if self.inner.in_transaction.get() {
            res.push_str(" (*)");
        }
    }

    pub fn show(&mut self, ps: &mut PaneState, ui: &mut Ui) {
        self.inner.add_new_heads();
        if let Some(id) = self.arrangement_id {
            if !self.inner.settings.show_arrangement {
                self.tree.remove_recursively(id);
                self.arrangement_id = None;
            }
        } else {
            if self.inner.settings.show_arrangement {
                let id = self.tree.tiles.insert_pane(Pane::Arrangement);
                self.tree.move_tile_to_container(id, self.root_id, 0, false);
                self.arrangement_id = Some(id);
            }
        }
        let show_vertical = match self.inner.settings.layout {
            UiLayout::Auto => ui.available_width() < 1024.0,
            UiLayout::Vertical => true,
            UiLayout::Horizontal => false,
        };
        if let Some(root) = self.tree.tiles.get_mut(self.root_id)
            && let Tile::Container(root) = root
            && let Container::Linear(root) = root
        {
            root.dir = match show_vertical {
                true => LinearDir::Vertical,
                false => LinearDir::Horizontal,
            };
        }
        self.tree.ui(&mut B(&mut self.inner, ps), ui)
    }
}

impl OutputsPaneInner {
    fn show_main_area(&mut self, ps: &mut PaneState, ui: &mut Ui) {
        ui.scope_builder(UiBuilder::new().id(("main_area", self.seed)), |ui| {
            self.show_settings_bar(ps, ui);
            ScrollArea::vertical().show(ui, |ui| {
                match self.ui.view {
                    View::Connectors => self.show_connectors(ui),
                    View::Settings => self.show_settings(ui),
                }
                ui.allocate_space(ui.available_size());
            });
        });
    }

    fn show_settings_bar(&mut self, ps: &mut PaneState, ui: &mut Ui) {
        ui.horizontal_wrapped(|ui| {
            if ui.button("Connectors").clicked() {
                self.ui.view = View::Connectors;
                ui.ctx().request_repaint();
            }
            if ui.button("Settings").clicked() {
                self.ui.view = View::Settings;
                ui.ctx().request_repaint();
            }
            if ui
                .checkbox(&mut self.ui.zoom_to_fit, "Zoom To Fit")
                .changed()
            {
                ui.ctx().request_repaint();
            }
            {
                let mut reset = !self.in_transaction.get();
                let reset2 = reset;
                let widget = Checkbox::new(&mut reset, "Reset");
                if reset2 {
                    ui.add_enabled(false, widget);
                } else {
                    if widget.ui(ui).changed() {
                        self.reset();
                    }
                }
            }
            ui.with_layout(Layout::right_to_left(Align::LEFT), |ui| {
                let enabled = self.in_transaction.get();
                ui.add_enabled_ui(enabled, |ui| {
                    if ui.button("Test").clicked() {
                        if let Err(e) = self.test_transaction() {
                            ps.errors.push(ErrorFmt(e).to_string());
                        }
                    }
                });
                let enabled = self.in_transaction.get();
                ui.add_enabled_ui(enabled, |ui| {
                    let button = Button::new("Commit").fill(ui.style().visuals.extreme_bg_color);
                    if ui.add(button).clicked() {
                        match self.commit_transaction() {
                            Ok(_) => self.reset(),
                            Err(e) => {
                                ps.errors.push(ErrorFmt(e).to_string());
                            }
                        }
                    }
                });
                ui.add_space(ui.available_width());
            });
        });
        ui.separator();
    }

    fn show_connectors(&mut self, ui: &mut Ui) {
        let mut heads: Vec<_> = self.heads.values_mut().collect();
        heads.sort_by(|a, b| {
            a.live_state
                .borrow()
                .name
                .cmp(&b.live_state.borrow().name)
                .then_with(|| a.name.cmp(&b.name))
        });
        let mut is_in_transaction = false;
        for head in &mut heads {
            show_connector(&self.state, &self.settings, head, ui);
            if head.changed_state.is_some() {
                is_in_transaction = true;
            }
        }
        self.in_transaction.set(is_in_transaction);
    }

    fn show_settings(&mut self, ui: &mut Ui) {
        let mut changed = false;

        {
            changed |= ui
                .checkbox(&mut self.settings.show_guide_lines, "Show guide lines")
                .changed();
        }

        {
            ui.horizontal(|ui| {
                changed |= ui
                    .checkbox(&mut self.settings.snap_to_neighbor, "Snap to neighbor")
                    .changed();
                tip(ui, |ui| {
                    ui.label("Hold Shift to invert this");
                });
            });
        }

        {
            ui.checkbox(&mut self.settings.show_arrangement, "Show arrangement area");
        }

        {
            let layout_text = |l: UiLayout| match l {
                UiLayout::Auto => "Auto",
                UiLayout::Vertical => "Vertical",
                UiLayout::Horizontal => "Horizontal",
            };
            changed |= ComboBox::new("layout", "Layout")
                .selected_text(layout_text(self.settings.layout))
                .show_ui(ui, |ui| {
                    for l in UiLayout::variants() {
                        ui.selectable_value(&mut self.settings.layout, l, layout_text(l));
                    }
                })
                .response
                .changed();
        }

        {
            changed |= ui
                .checkbox(
                    &mut self.settings.show_disconnected,
                    "Show disconnected heads",
                )
                .changed();
        }

        {
            changed |= ui
                .checkbox(&mut self.settings.show_disabled, "Show disabled heads")
                .changed();
        }

        if changed {
            ui.ctx().request_repaint();
        }
    }

    fn show_arrangement(&mut self, ui: &mut Ui) {
        let clip_rect = ui.available_rect_before_wrap();
        let origin = &mut self.ui.origin;
        let ox = origin.x.round();
        let oy = origin.y.round();
        let mut heads = vec![];
        let scale = self.ui.scale;
        struct PreparedHead<'a> {
            name: HeadName,
            m: Ref<'a, HeadState>,
            changed_state: &'a mut Option<HeadState>,
            z: &'a mut u64,
            focus: &'a mut u64,
            drag_pos: &'a mut Option<(f32, f32)>,
            x1: i32,
            y1: i32,
            x2: i32,
            y2: i32,
            rect: Rect,
        }
        for head in self.heads.values_mut() {
            let m = head.live_state.borrow();
            let e = effective!(&*m, head.changed_state);
            if !e.in_compositor_space {
                continue;
            }
            let (x, y) = e.position;
            let (w, h) = e.size;
            let x1 = (x as f32 * scale).round() - ox + clip_rect.min.x;
            let y1 = (y as f32 * scale).round() - oy + clip_rect.min.y;
            let x2 = ((x + w) as f32 * scale).round() - ox + clip_rect.min.x;
            let y2 = ((y + h) as f32 * scale).round() - oy + clip_rect.min.y;
            heads.push(PreparedHead {
                name: head.name,
                m,
                changed_state: &mut head.changed_state,
                z: &mut head.z,
                focus: &mut head.focus,
                drag_pos: &mut head.drag_pos,
                x1: x,
                y1: y,
                x2: x + w,
                y2: y + h,
                rect: Rect {
                    min: pos2(x1, y1),
                    max: pos2(x2, y2),
                },
            });
        }
        if self.ui.zoom_to_fit {
            let mut x_min = i32::MAX;
            let mut x_max = i32::MIN;
            let mut y_min = i32::MAX;
            let mut y_max = i32::MIN;
            for head in &heads {
                x_min = x_min.min(head.x1);
                x_max = x_max.max(head.x2);
                y_min = y_min.min(head.y1);
                y_max = y_max.max(head.y2);
            }
            if x_min > x_max {
                x_min = 0;
                x_max = 0;
            }
            if y_min > y_max {
                y_min = 0;
                y_max = 0;
            }
            x_min -= 100;
            y_min -= 100;
            x_max += 100;
            y_max += 100;
            let dx = x_max - x_min + 1;
            let dy = y_max - y_min + 1;
            let x_scale = clip_rect.width() / dx as f32;
            let y_scale = clip_rect.height() / dy as f32;
            let new_scale = x_scale.min(y_scale);
            let new_ox = x_min as f32 * new_scale;
            let new_oy = y_min as f32 * new_scale;
            if new_scale != scale || new_ox != ox || new_oy != oy {
                self.ui.scale = new_scale;
                origin.x = new_ox;
                origin.y = new_oy;
                ui.ctx().request_repaint();
            }
        }
        heads.sort_by_key(|h| *h.z);
        let style = &ui.style().visuals;
        let mut bg_color = style.panel_fill.to_oklch();
        let mut no_capture_bg_color = bg_color;
        let mut disabled_bg_color = bg_color;
        if bg_color.l > 0.5 {
            bg_color.l -= 0.05;
            disabled_bg_color.l -= 0.1;
            no_capture_bg_color.l -= 0.075;
        } else {
            bg_color.l += 0.05;
            disabled_bg_color.l += 0.1;
            no_capture_bg_color.l += 0.075;
        }
        let fg_color_base = style.widgets.noninteractive.text_color().to_oklab();
        let fg_color_1 = fg_color_base * 1.0 / 3.0 + bg_color.to_oklab() * 2.0 / 3.0;
        let fg_color_2 = fg_color_base * 2.0 / 3.0 + bg_color.to_oklab() * 1.0 / 3.0;
        let painter = ui.painter_at(clip_rect);
        painter.rect(
            Rect::EVERYTHING,
            0.0,
            disabled_bg_color,
            Stroke::NONE,
            StrokeKind::Inside,
        );
        {
            let x_min = 0.0;
            let y_min = 0.0;
            let x_max = MAX_EXTENTS as f32;
            let y_max = MAX_EXTENTS as f32;
            let good = Rect {
                min: clip_rect.min + vec2(x_min, y_min) * scale - *origin,
                max: clip_rect.min + vec2(x_max, y_max) * scale - *origin,
            };
            painter.rect(good, 0.0, bg_color, Stroke::NONE, StrokeKind::Inside);
        }
        painter.hline(
            clip_rect.left()..=clip_rect.right(),
            clip_rect.min.y - origin.y,
            (1.0, fg_color_1),
        );
        painter.vline(
            clip_rect.min.x - origin.x,
            clip_rect.top()..=clip_rect.bottom(),
            (1.0, fg_color_1),
        );
        if self.settings.show_guide_lines {
            for head in &heads {
                let rect = head.rect;
                painter.hline(
                    clip_rect.left()..=clip_rect.right(),
                    rect.top(),
                    (1.0, fg_color_2),
                );
                painter.hline(
                    clip_rect.left()..=clip_rect.right(),
                    rect.bottom(),
                    (1.0, fg_color_2),
                );
                painter.vline(
                    rect.left(),
                    clip_rect.top()..=clip_rect.bottom(),
                    (1.0, fg_color_2),
                );
                painter.vline(
                    rect.right(),
                    clip_rect.top()..=clip_rect.bottom(),
                    (1.0, fg_color_2),
                );
            }
        }
        for head in &mut heads {
            let rect = head.rect;
            let mut color = fg_color_2;
            if *head.focus == self.ui.focus {
                let shape = Shadow {
                    offset: [0, 0],
                    blur: (512.0 * scale).sqrt() as u8,
                    spread: (255.0 * scale).sqrt() as u8,
                    color: Color32::from_black_alpha(200),
                }
                .as_shape(rect, 0.0);
                painter.add(shape);
                color = fg_color_base;
            }
            painter.hline(rect.left()..=rect.right() + 1.0, rect.top(), (1.0, color));
            painter.hline(
                rect.left()..=rect.right() + 1.0,
                rect.bottom(),
                (1.0, color),
            );
            painter.vline(rect.left(), rect.top()..=rect.bottom() + 1.0, (1.0, color));
            painter.vline(rect.right(), rect.top()..=rect.bottom() + 1.0, (1.0, color));
            let content_rect = Rect {
                min: pos2(rect.min.x + 1.0, rect.min.y + 1.0),
                max: pos2(rect.max.x, rect.max.y),
            };
            let painter = painter.with_clip_rect(content_rect);
            painter.rect(
                content_rect,
                0.0,
                no_capture_bg_color,
                Stroke::NONE,
                StrokeKind::Inside,
            );
            let galley =
                painter.layout_no_wrap(head.m.name.to_string(), FontId::default(), Color32::WHITE);
            let rect = Rect::from_min_size(content_rect.min, galley.rect.size() + vec2(2.0, 2.0));
            painter.rect(rect, 0.0, Color32::BLUE, Stroke::NONE, StrokeKind::Inside);
            painter.galley(rect.min + vec2(1.0, 1.0), galley, Color32::WHITE);
        }
        ui.allocate_space(ui.available_size());
        macro_rules! interacted {
            () => {{
                self.ui.zoom_to_fit = false;
            }};
        }
        let response = ui.allocate_rect(clip_rect, Sense::all());
        if response.has_focus() {
            let mut dx = 0;
            let mut dy = 0;
            ui.ctx().input(|i| {
                if i.key_pressed(Key::ArrowUp) {
                    dy -= 1;
                }
                if i.key_pressed(Key::ArrowDown) {
                    dy += 1;
                }
                if i.key_pressed(Key::ArrowLeft) {
                    dx -= 1;
                }
                if i.key_pressed(Key::ArrowRight) {
                    dx += 1;
                }
            });
            if dx != 0 || dy != 0 {
                interacted!();
                for head in &mut heads {
                    if *head.focus == self.ui.focus {
                        let x = (head.x1 + dx).clamp(0, MAX_EXTENTS);
                        let y = (head.y1 + dy).clamp(0, MAX_EXTENTS);
                        let pos = (x, y);
                        if effective!(&*head.m, head.changed_state).position != pos {
                            modify!(&*head.m, head.changed_state).position = pos;
                            ui.ctx().request_repaint();
                        }
                    }
                }
            }
        }
        if let Some(pos) = response.hover_pos() {
            let scroll = ui.ctx().input(|i| i.smooth_scroll_delta);
            let mut new = scale;
            if scroll.y != 0.0 {
                interacted!();
            }
            if scroll.y < 0.0 {
                new /= 1.0 - scroll.y / 1000.0;
            } else {
                new *= 1.0 + scroll.y / 1000.0;
            }
            new = new.max(0.01);
            if new != scale {
                self.ui.scale = new;
                ui.ctx().request_repaint();
                let relative_pos = pos - clip_rect.min;
                let real_pos = (relative_pos + *origin) / scale;
                *origin = real_pos * new - relative_pos;
            }
        }
        if ui
            .ctx()
            .input(|i| i.pointer.button_pressed(PointerButton::Primary))
        {
            self.ui.focus += 1;
            if let Some(pos) = response.hover_pos() {
                interacted!();
                response.request_focus();
                for head in heads.iter_mut().rev() {
                    if head.rect.contains(pos) {
                        *head.z = self.ui.next_z;
                        self.ui.next_z += 1;
                        *head.focus = self.ui.focus;
                        ui.ctx().request_repaint();
                        break;
                    }
                }
            }
        }
        if response.clicked_elsewhere() {
            self.ui.focus += 1;
        }
        if response.drag_started_by(PointerButton::Middle)
            || response.drag_started_by(PointerButton::Secondary)
        {
            interacted!();
            self.ui.origin_drag = Some(self.ui.origin);
        } else if response.drag_started_by(PointerButton::Primary)
            && let Some(pos) = response.hover_pos()
        {
            interacted!();
            for head in heads.iter_mut().rev() {
                if head.rect.contains(pos) {
                    *head.drag_pos = Some((head.x1 as f32, head.y1 as f32));
                    break;
                }
            }
        }
        let drag_delta = response.drag_delta();
        if drag_delta.x != 0.0 || drag_delta.y != 0.0 {
            if let Some(origin_drag) = &mut self.ui.origin_drag {
                *origin_drag -= drag_delta;
                self.ui.origin = *origin_drag;
                ui.ctx().request_repaint();
            }
            let snap = self.settings.snap_to_neighbor ^ ui.ctx().input(|i| i.modifiers.shift);
            let mut head_positions = vec![];
            struct HeadPosition {
                name: HeadName,
                x1: i32,
                y1: i32,
                x2: i32,
                y2: i32,
            }
            if snap {
                for head in &heads {
                    let PreparedHead {
                        name,
                        x1,
                        y1,
                        x2,
                        y2,
                        ..
                    } = *head;
                    head_positions.push(HeadPosition {
                        name,
                        x1,
                        y1,
                        x2,
                        y2,
                    });
                }
            }
            for head in &mut heads {
                if let Some((mut x, mut y)) = *head.drag_pos {
                    x += drag_delta.x / scale;
                    y += drag_delta.y / scale;
                    let mut x_int = if x < 0.0 { x.ceil() } else { x.floor() } as i32;
                    let mut y_int = if y < 0.0 { y.ceil() } else { y.floor() } as i32;
                    if snap {
                        for other in &head_positions {
                            if head.name == other.name {
                                continue;
                            }
                            macro_rules! snap {
                                ($int:ident, $one:ident, $two:ident) => {
                                    if $int.abs() as f32 * scale <= 10.0 {
                                        $int = 0;
                                    } else if ($int - other.$one).abs() as f32 * scale <= 10.0 {
                                        $int = other.$one;
                                    } else if ($int - other.$two).abs() as f32 * scale <= 10.0 {
                                        $int = other.$two;
                                    } else if ($int + head.$two - head.$one - other.$one).abs()
                                        as f32
                                        * scale
                                        <= 10.0
                                    {
                                        $int = other.$one + head.$one - head.$two;
                                    } else if ($int + head.$two - head.$one - other.$two).abs()
                                        as f32
                                        * scale
                                        <= 10.0
                                    {
                                        $int = other.$two + head.$one - head.$two;
                                    }
                                };
                            }
                            snap!(x_int, x1, x2);
                            snap!(y_int, y1, y2);
                        }
                    }
                    x_int = x_int.clamp(0, MAX_EXTENTS);
                    y_int = y_int.clamp(0, MAX_EXTENTS);
                    let pos = (x_int, y_int);
                    if effective!(&*head.m, head.changed_state).position != pos {
                        modify!(&*head.m, head.changed_state).position = pos;
                        ui.ctx().request_repaint();
                    }
                    *head.drag_pos = Some((x, y));
                }
            }
        }
        if response.drag_stopped() {
            self.ui.origin_drag = None;
            for head in heads.iter_mut().rev() {
                *head.drag_pos = None;
            }
        }
        ui.ctx().memory_mut(|mem| {
            mem.set_focus_lock_filter(
                response.id,
                EventFilter {
                    tab: false,
                    horizontal_arrows: true,
                    vertical_arrows: true,
                    escape: false,
                },
            )
        });
    }

    fn prepare_transaction(&self) -> Result<PreparedConnectorTransaction, HeadTransactionError> {
        let mut tran = ConnectorTransaction::new(&self.state);
        for head in self.heads.values() {
            let Some(desired) = &head.changed_state else {
                continue;
            };
            let Some(connector) = self.state.connectors.get(&head.id) else {
                return Err(HeadTransactionError::HeadRemoved(head.id));
            };
            if head.live_state.borrow().monitor_info != desired.monitor_info {
                return Err(HeadTransactionError::MonitorChanged(head.id));
            }
            let old = connector.state.borrow().clone();
            let mut new = old.clone();
            new.enabled = desired.connector_enabled;
            new.mode = desired.mode;
            new.non_desktop_override = desired.override_non_desktop;
            new.format = desired.format;
            new.color_space = desired.color_space;
            new.eotf = desired.eotf;
            if old == new {
                continue;
            }
            tran.add(&connector.connector, new)?;
        }
        Ok(tran.prepare()?)
    }

    fn commit_transaction(&self) -> Result<(), HeadTransactionError> {
        self.prepare_transaction()?.apply()?.commit();
        for head in self.heads.values() {
            let Some(desired) = &head.changed_state else {
                continue;
            };
            if let Some(output) = self.state.outputs.get(&head.id)
                && let Some(node) = &output.node
            {
                node.set_position(desired.position.0, desired.position.1);
                node.set_preferred_scale(desired.scale);
                node.update_transform(desired.transform);
                node.set_vrr_mode(&desired.vrr_mode);
                node.set_tearing_mode(&desired.tearing_mode);
                node.set_brightness(desired.brightness);
                node.set_blend_space(desired.blend_space);
                node.set_use_native_gamut(desired.use_native_gamut);
                node.schedule
                    .set_cursor_hz(&self.state, desired.vrr_cursor_hz.unwrap_or(f64::INFINITY));
            } else if let Some(mi) = &desired.monitor_info {
                let pos = &self.state.persistent_output_states;
                let pos = pos.lock().entry(mi.output_id.clone()).or_default().clone();
                pos.pos.set(desired.position);
                pos.scale.set(desired.scale);
                pos.transform.set(desired.transform);
                pos.vrr_mode.set(desired.vrr_mode);
                pos.tearing_mode.set(desired.tearing_mode);
                pos.brightness.set(desired.brightness);
                pos.blend_space.set(desired.blend_space);
                pos.use_native_gamut.set(desired.use_native_gamut);
                pos.vrr_cursor_hz.set(desired.vrr_cursor_hz);
            }
        }
        Ok(())
    }

    fn test_transaction(&self) -> Result<(), HeadTransactionError> {
        self.prepare_transaction()?;
        Ok(())
    }

    fn reset(&mut self) {
        self.in_transaction.set(false);
        let mut to_remove = vec![];
        for head in self.heads.values_mut() {
            if self.state.connectors.contains(&head.id) {
                head.changed_state = None;
            } else {
                to_remove.push(head.name);
            }
        }
        for name in to_remove {
            self.heads.remove(&name);
        }
    }

    fn add_new_heads(&mut self) {
        for connector in self.state.connectors.lock().values() {
            let mgrs = &connector.head_managers;
            self.heads.entry(mgrs.name).or_insert_with(|| CompleteHead {
                id: connector.id,
                name: mgrs.name,
                live_state: mgrs.state(),
                changed_state: None,
                z: 0,
                focus: 0,
                drag_pos: None,
            });
        }
    }
}

fn show_connector(state: &State, settings: &Settings, head: &mut CompleteHead, ui: &mut Ui) {
    let m = &*head.live_state.borrow();
    let t = &mut head.changed_state;
    if t.is_none() {
        if !m.connector_enabled && !settings.show_disabled {
            return;
        }
        if !m.connected && !settings.show_disconnected {
            return;
        }
    }
    let mut layout_job = LayoutJob::default();
    layout_job.append(
        "Connector",
        0.0,
        TextFormat {
            color: ui.style().visuals.widgets.inactive.text_color(),
            ..Default::default()
        },
    );
    layout_job.append(
        &m.name,
        10.0,
        TextFormat {
            color: ui.style().visuals.widgets.active.text_color(),
            ..Default::default()
        },
    );
    let mut name = String::new();
    if let Some(v) = &m.monitor_info {
        name.push_str(&v.output_id.manufacturer);
        name.push_str(" - ");
        name.push_str(&v.output_id.model);
    }
    layout_job.append(
        &name,
        10.0,
        TextFormat {
            color: ui.style().visuals.widgets.inactive.text_color(),
            ..Default::default()
        },
    );
    ui.collapsing(layout_job, |ui| {
        grid(ui, ("settings", head.name), |ui| {
            let mut diff = false;
            show_serial_number(ui, m);
            diff |= show_enablement(ui, m, t);
            diff |= show_position(ui, m, t);
            diff |= show_scale(ui, m, t);
            diff |= show_mode(ui, m, t);
            diff |= show_size(ui, m, t);
            diff |= show_transform(ui, m, t);
            diff |= show_brightness(ui, m, t);
            diff |= show_color_space(ui, m, t);
            diff |= show_eotf(ui, m, t);
            diff |= show_format(ui, m, t);
            diff |= show_tearing(ui, m, t);
            diff |= show_vrr(ui, m, t);
            diff |= show_non_desktop(ui, m, t);
            diff |= show_blend_space(ui, m, t);
            diff |= show_use_native_gamut(ui, m, t);
            show_native_gamut(ui, m);
            diff |= show_cursor_hz(ui, m, t);
            show_flip_margin(state, ui, m, t, head.id);
            if diff {
                let ui = &mut *ui.row();
                ui.label("");
                ui.label("");
                ui.label("^ current");
            }
        });
    });
}

fn show_serial_number(ui: &mut Ui, m: &HeadState) {
    if let Some(info) = &m.monitor_info {
        let ui = &mut *ui.row();
        grid_label(ui, "Serial Number");
        ui.label(&info.output_id.serial_number);
    }
}

fn show_enablement(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
    let ui = &mut *ui.row();
    grid_label(ui, "Enabled");
    let mut v = effective!(m, t).connector_enabled;
    let changed = Checkbox::without_text(&mut v).ui(ui).changed();
    if changed {
        let t = modify!(m, t);
        t.connector_enabled = v;
        t.update_in_compositor_space(m.wl_output);
    }
    let diff = v != m.connector_enabled;
    if diff {
        ui.label(match m.connector_enabled {
            true => "enabled",
            false => "disabled",
        });
    }
    diff
}

fn show_position(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
    if !effective!(m, t).in_compositor_space {
        return false;
    }
    let ui = &mut *ui.row();
    grid_label(ui, "Position");
    let (mut x, mut y) = effective!(m, t).position;
    ui.horizontal(|ui| {
        let value = |ui: &mut Ui, v, min, max| {
            let res = DragValue::new(v).range(min..=max).speed(1.0).ui(ui);
            res.changed()
        };
        let mut changed = false;
        changed |= value(ui, &mut x, 0, MAX_EXTENTS);
        ui.label("x");
        changed |= value(ui, &mut y, 0, MAX_EXTENTS);
        if changed {
            modify!(m, t).position = (x, y);
        }
    });
    let diff = m.position != (x, y);
    if diff {
        ui.label(format!("{} x {}", m.position.0, m.position.1));
    }
    diff
}

fn show_scale(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
    if !effective!(m, t).in_compositor_space {
        return false;
    }
    let ui = &mut *ui.row();
    grid_label(ui, "Scale");
    let mut v = effective!(m, t).scale;
    let old = v;
    ui.horizontal(|ui| {
        let mut s = v.to_f64();
        let res = DragValue::new(&mut s)
            .range(MIN_SCALE.to_f64()..=MAX_SCALE.to_f64())
            .speed(1.0 / SCALE_BASEF)
            .fixed_decimals(5)
            .ui(ui);
        if res.changed() {
            v = Scale::from_f64(s);
        }
        if ui.button(ICON_REMOVE).clicked() {
            v = Scale::from_wl(v.to_wl().saturating_sub(SCALE_BASE)).clamp(MIN_SCALE, MAX_SCALE);
        }
        if ui.button(ICON_ADD).clicked() {
            v = Scale::from_wl(v.to_wl().saturating_add(SCALE_BASE)).clamp(MIN_SCALE, MAX_SCALE);
        }
    });
    if old != v {
        let t = modify!(m, t);
        t.scale = v;
        t.update_size();
    }
    let diff = m.scale != v;
    if diff {
        ui.label(format!("{}", m.scale.to_f64()));
    }
    diff
}

fn show_mode(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
    if !effective!(m, t).in_compositor_space {
        return false;
    }
    let ui = &mut *ui.row();
    let mut mode = effective!(m, t).mode;
    let old = mode;
    grid_label(ui, "Mode");
    let mode_text = |mode: Mode| {
        format!(
            "{}x{}@{}",
            mode.width,
            mode.height,
            mode.refresh_rate_millihz as f64 / 1000.0
        )
    };
    if let Some(monitor_info) = &m.monitor_info
        && monitor_info.modes.len() > 1
    {
        ComboBox::from_id_salt("modes")
            .selected_text(mode_text(mode))
            .show_ui(ui, |ui| {
                for v in &monitor_info.modes {
                    ui.selectable_value(&mut mode, *v, mode_text(*v));
                }
            });
    } else {
        ui.label(mode_text(mode));
    }
    if old != mode {
        let t = modify!(m, t);
        t.mode = mode;
        t.update_size();
    }
    let mut diff = false;
    if m.mode != mode {
        diff = true;
        ui.label(mode_text(m.mode));
    }
    diff
}

fn show_size(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
    if let Some(info) = &m.monitor_info {
        let ui = &mut *ui.row();
        grid_label(ui, "Physical Size (mm)");
        ui.label(format!("{} x {}", info.width_mm, info.height_mm));
    }
    if !effective!(m, t).in_compositor_space {
        return false;
    }
    let ui = &mut *ui.row();
    grid_label(ui, "Size");
    let (w, h) = effective!(m, t).size;
    ui.label(format!("{w} x {h}"));
    let diff = m.size != (w, h);
    if diff {
        ui.label(format!("{} x {}", m.size.0, m.size.1));
    }
    diff
}

fn show_transform(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
    if !effective!(m, t).in_compositor_space {
        return false;
    }
    let ui = &mut *ui.row();
    grid_label(ui, "Transform");
    let mut v = effective!(m, t).transform;
    let mut changed = false;
    let transform_name = |t: Transform| match t {
        Transform::None => "none",
        Transform::Rotate90 => "rotate-90",
        Transform::Rotate180 => "rotate-180",
        Transform::Rotate270 => "rotate-270",
        Transform::Flip => "flip",
        Transform::FlipRotate90 => "flip-rotate-90",
        Transform::FlipRotate180 => "flip-rotate-180",
        Transform::FlipRotate270 => "flip-rotate-270",
    };
    ComboBox::from_id_salt("transform")
        .selected_text(transform_name(v))
        .show_ui(ui, |ui| {
            let transforms = [
                Transform::None,
                Transform::Rotate90,
                Transform::Rotate180,
                Transform::Rotate270,
                Transform::Flip,
                Transform::FlipRotate90,
                Transform::FlipRotate180,
                Transform::FlipRotate270,
            ];
            for s in transforms {
                changed |= ui.selectable_value(&mut v, s, transform_name(s)).changed();
            }
        });
    if changed {
        let t = modify!(m, t);
        t.transform = v;
        t.update_size();
    }
    let diff = m.transform != v;
    if diff {
        ui.label(transform_name(m.transform));
    }
    diff
}

fn show_brightness(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
    if !effective!(m, t).in_compositor_space {
        return false;
    }
    let old_custom_brightness = effective!(m, t).brightness.is_some();
    let mut custom_brightness = old_custom_brightness;
    let mut changed = false;
    grid_label(ui, "Custom Brightness");
    Checkbox::without_text(&mut custom_brightness).ui(ui);
    changed |= old_custom_brightness != custom_brightness;
    let diff1 = m.brightness.is_some() != custom_brightness;
    if diff1 {
        ui.label(match m.brightness.is_some() {
            true => "enabled",
            false => "disabled",
        });
    }
    ui.end_row();

    if !custom_brightness {
        if changed {
            modify!(m, t).brightness = None;
        }
        return diff1;
    }

    grid_label(ui, "Brightness");
    ui.vertical(|ui| {
        let effective = effective!(m, t);
        let default_brightness = match effective.eotf {
            BackendEotfs::Default => effective
                .monitor_info
                .as_ref()
                .and_then(|m| m.luminance.as_ref())
                .map(|l| l.max)
                .unwrap_or(Luminance::SRGB.white.0),
            BackendEotfs::Pq => Luminance::ST2084_PQ.white.0,
        };
        let mut brightness = effective.brightness.unwrap_or(default_brightness);
        changed |= DragValue::new(&mut brightness)
            .range(0.0..=1000.0)
            .ui(ui)
            .changed();
        ui.label(format!("reference: {default_brightness})"));
        if changed {
            modify!(m, t).brightness = Some(brightness);
        }
    });
    let mut diff2 = false;
    if let Some(t) = t
        && m.brightness != t.brightness
    {
        diff2 = true;
        ui.label(format!(
            "{}",
            fmt::from_fn(|f| match m.brightness {
                None => f.write_str("disabled"),
                Some(v) => write!(f, "{}", v),
            })
        ));
    }
    ui.end_row();
    diff1 || diff2
}

fn show_color_space(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
    if !effective!(m, t).in_compositor_space {
        return false;
    }
    let ui = &mut *ui.row();
    grid_label(ui, "Colorimetry");
    let mut v = effective!(m, t).color_space;
    ui.horizontal(|ui| {
        if let Some(monitor_info) = &effective!(m, t).monitor_info {
            if monitor_info.color_spaces.is_empty() {
                ui.label(v.name());
            } else {
                let mut changed = false;
                ComboBox::from_id_salt("colorimetry")
                    .selected_text(v.name())
                    .show_ui(ui, |ui| {
                        changed |= ui
                            .selectable_value(
                                &mut v,
                                BackendColorSpace::Default,
                                BackendColorSpace::Default.name(),
                            )
                            .changed();
                        for &s in &monitor_info.color_spaces {
                            changed |= ui.selectable_value(&mut v, s, s.name()).changed();
                        }
                    });
                if changed {
                    modify!(m, t).color_space = v;
                }
            }
        }
    });
    let diff = m.color_space != v;
    if diff {
        ui.label(m.color_space.name());
    }
    diff
}

fn show_eotf(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
    if !effective!(m, t).in_compositor_space {
        return false;
    }
    let ui = &mut *ui.row();
    grid_label(ui, "EOTF");
    let mut v = effective!(m, t).eotf;
    ui.horizontal(|ui| {
        if let Some(monitor_info) = &effective!(m, t).monitor_info {
            if monitor_info.eotfs.is_empty() {
                ui.label(v.name());
            } else {
                let mut changed = false;
                ComboBox::from_id_salt("eotf")
                    .selected_text(v.name())
                    .show_ui(ui, |ui| {
                        changed |= ui
                            .selectable_value(
                                &mut v,
                                BackendEotfs::Default,
                                BackendEotfs::Default.name(),
                            )
                            .changed();
                        for &s in &monitor_info.eotfs {
                            changed |= ui.selectable_value(&mut v, s, s.name()).changed();
                        }
                    });
                if changed {
                    modify!(m, t).eotf = v;
                }
            }
        }
    });
    let diff = m.eotf != v;
    if diff {
        ui.label(m.eotf.name());
    }
    diff
}

fn show_format(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
    if !effective!(m, t).in_compositor_space {
        return false;
    }
    let ui = &mut *ui.row();
    grid_label(ui, "Format");
    let mut v = effective!(m, t).format;
    ui.horizontal(|ui| {
        if m.supported_formats.len() < 2 {
            ui.label(v.name);
        } else {
            let mut changed = false;
            ComboBox::from_id_salt("format")
                .selected_text(v.name)
                .show_ui(ui, |ui| {
                    for &s in &*m.supported_formats {
                        changed |= ui.selectable_value(&mut v, s, s.name).changed();
                    }
                });
            if changed {
                modify!(m, t).format = v;
            }
        }
    });
    let diff = m.format != v;
    if diff {
        ui.label(m.format.name);
    }
    diff
}

fn show_tearing(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
    if !effective!(m, t).in_compositor_space {
        return false;
    }
    let render_settings = |ui: &mut Ui, old: TearingMode| {
        #[derive(Copy, Clone, PartialEq, Linearize)]
        enum Mode {
            Never,
            Always,
            Fullscreen,
        }
        fn name(mode: Mode) -> &'static str {
            match mode {
                Mode::Never => "Never",
                Mode::Always => "Always",
                Mode::Fullscreen => "Fullscreen",
            }
        }
        let mut mode = match old {
            TearingMode::Never => Mode::Never,
            TearingMode::Always => Mode::Always,
            TearingMode::Fullscreen { .. } => Mode::Fullscreen,
        };
        let old_mode = mode;
        let mut surface = None;
        ui.vertical(|ui| {
            ComboBox::from_id_salt("tearing mode")
                .selected_text(name(mode))
                .show_ui(ui, |ui| {
                    for s in Mode::variants() {
                        ui.selectable_value(&mut mode, s, name(s));
                    }
                });
            if mode == Mode::Fullscreen {
                if old_mode != mode {
                    surface = Some(Default::default());
                }
                if let TearingMode::Fullscreen { surface: s } = old {
                    surface = s;
                }
                let mut limit_windows = surface.is_some();
                ui.checkbox(&mut limit_windows, "Limit Windows");
                if !limit_windows {
                    surface = None;
                } else {
                    ui.indent("limit windows", |ui| {
                        let surface = surface.get_or_insert_default();
                        ui.checkbox(&mut surface.tearing_requested, "Requests Tearing");
                    });
                }
            }
        });
        match mode {
            Mode::Never => TearingMode::Never,
            Mode::Always => TearingMode::Always,
            Mode::Fullscreen => TearingMode::Fullscreen { surface },
        }
    };
    let ui = &mut *ui.row();
    grid_label(ui, "Tearing");
    let old = effective!(m, t).tearing_mode;
    let v = render_settings(ui, old);
    if v != old {
        modify!(m, t).tearing_mode = v;
    }
    let diff = v != m.tearing_mode;
    if diff {
        ui.add_enabled_ui(false, |ui| {
            render_settings(ui, m.tearing_mode);
        });
    }
    diff
}

fn show_vrr(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
    if !effective!(m, t).in_compositor_space {
        return false;
    }
    if let Some(info) = &m.monitor_info
        && !info.vrr_capable
    {
        return false;
    }
    {
        let ui = &mut *ui.row();
        grid_label(ui, "VRR Active");
        ui.label(effective!(m, t).vrr.to_string());
    }
    let render_settings = |ui: &mut Ui, old: VrrMode| {
        #[derive(Copy, Clone, PartialEq, Linearize)]
        enum Mode {
            Never,
            Always,
            Fullscreen,
        }
        fn name(mode: Mode) -> &'static str {
            match mode {
                Mode::Never => "Never",
                Mode::Always => "Always",
                Mode::Fullscreen => "Fullscreen",
            }
        }
        let mut mode = match old {
            VrrMode::Never => Mode::Never,
            VrrMode::Always => Mode::Always,
            VrrMode::Fullscreen { .. } => Mode::Fullscreen,
        };
        let mut surface = None;
        ui.vertical(|ui| {
            ComboBox::from_id_salt("vrr mode")
                .selected_text(name(mode))
                .show_ui(ui, |ui| {
                    for s in Mode::variants() {
                        ui.selectable_value(&mut mode, s, name(s));
                    }
                });
            if mode == Mode::Fullscreen {
                if let VrrMode::Fullscreen { surface: s } = old {
                    surface = s;
                }
                let mut limit_windows = surface.is_some();
                ui.checkbox(&mut limit_windows, "Limit Windows");
                if !limit_windows {
                    surface = None;
                } else {
                    ui.indent("limit windows", |ui| {
                        let surface = surface.get_or_insert_default();
                        let mut limit_content_type = surface.content_type.is_some();
                        ui.checkbox(&mut limit_content_type, "Limit Content Types");
                        if !limit_content_type {
                            surface.content_type = None;
                        } else {
                            ui.indent("limit content type", |ui| {
                                let limit = surface.content_type.get_or_insert_default();
                                let fields = [
                                    ("Photos", &mut limit.photo),
                                    ("Videos", &mut limit.video),
                                    ("Games", &mut limit.game),
                                ];
                                for (name, field) in fields {
                                    ui.checkbox(field, name);
                                }
                            });
                        }
                    });
                }
            }
        });
        match mode {
            Mode::Never => VrrMode::Never,
            Mode::Always => VrrMode::Always,
            Mode::Fullscreen => VrrMode::Fullscreen { surface },
        }
    };
    let ui = &mut *ui.row();
    grid_label(ui, "VRR");
    let old = effective!(m, t).vrr_mode;
    let v = render_settings(ui, old);
    if v != old {
        modify!(m, t).vrr_mode = v;
    }
    let diff = v != m.vrr_mode;
    if diff {
        ui.add_enabled_ui(false, |ui| {
            render_settings(ui, m.vrr_mode);
        });
    }
    diff
}

fn show_non_desktop(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
    {
        let ui = &mut *ui.row();
        grid_label(ui, "Non-desktop");
        if m.inherent_non_desktop {
            ui.label("Yes");
        } else {
            ui.label("No");
        }
    }

    let ui = &mut *ui.row();
    grid_label(ui, "Override");
    let mut v = effective!(m, t).override_non_desktop;
    let mut changed = false;
    let name = |v: Option<bool>| match v {
        None => "None",
        Some(false) => "Desktop",
        Some(true) => "Non-Desktop",
    };
    ComboBox::from_id_salt("non-desktop-override")
        .selected_text(name(v))
        .show_ui(ui, |ui| {
            for s in [None, Some(false), Some(true)] {
                changed |= ui.selectable_value(&mut v, s, name(s)).changed();
            }
        });
    if changed {
        let t = modify!(m, t);
        t.override_non_desktop = v;
        t.update_in_compositor_space(m.wl_output);
    }
    let diff = v != m.override_non_desktop;
    if diff {
        ui.label(name(m.override_non_desktop));
    }
    diff
}

fn show_blend_space(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
    if !effective!(m, t).in_compositor_space {
        return false;
    }
    let ui = &mut *ui.row();
    grid_label(ui, "Blend Space");
    let mut v = effective!(m, t).blend_space;
    ui.horizontal(|ui| {
        let mut changed = false;
        ComboBox::from_id_salt("blend-space")
            .selected_text(v.name())
            .show_ui(ui, |ui| {
                for s in BlendSpace::variants() {
                    changed |= ui.selectable_value(&mut v, s, s.name()).changed();
                }
            });
        if changed {
            modify!(m, t).blend_space = v;
        }
    });
    let diff = m.blend_space != v;
    if diff {
        ui.label(m.blend_space.name());
    }
    diff
}

fn show_use_native_gamut(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
    if !effective!(m, t).in_compositor_space {
        return false;
    }
    let ui = &mut *ui.row();
    grid_label(ui, "Use Native Gamut");
    let mut use_native_gamut = effective!(m, t).use_native_gamut;
    if Checkbox::without_text(&mut use_native_gamut)
        .ui(ui)
        .changed()
    {
        modify!(m, t).use_native_gamut = use_native_gamut;
    }
    let diff = m.use_native_gamut != use_native_gamut;
    if diff {
        let mut old = m.use_native_gamut;
        ui.add_enabled(false, Checkbox::without_text(&mut old));
    }
    diff
}

fn show_native_gamut(ui: &mut Ui, m: &HeadState) {
    let Some(info) = &m.monitor_info else {
        return;
    };
    let p = info.primaries;
    let ui = &mut *ui.row();
    grid_label(ui, "Native Gamut");
    Grid::new("native gamut").show(ui, |ui| {
        let fields = [
            ("red:", p.r),
            ("green:", p.g),
            ("blue:", p.b),
            ("white:", p.wp),
        ];
        for (name, field) in fields {
            let ui = &mut *ui.row();
            ui.label(name);
            ui.label(format!("{:.6}", field.0));
            ui.label(format!("{:.6}", field.1));
        }
    });
}

fn show_cursor_hz(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool {
    if !effective!(m, t).in_compositor_space {
        return false;
    }
    let old_cursor_hz = effective!(m, t).vrr_cursor_hz.is_some();
    let mut custom_cursor_hz = old_cursor_hz;
    let mut changed = false;
    grid_label(ui, "Limit Cursor HZ");
    Checkbox::without_text(&mut custom_cursor_hz).ui(ui);
    changed |= old_cursor_hz != custom_cursor_hz;
    let diff1 = m.vrr_cursor_hz.is_some() != custom_cursor_hz;
    if diff1 {
        ui.label(match m.vrr_cursor_hz.is_some() {
            true => "enabled",
            false => "disabled",
        });
    }
    ui.end_row();

    if !custom_cursor_hz {
        if changed {
            modify!(m, t).vrr_cursor_hz = None;
        }
        return diff1;
    }

    grid_label(ui, "Cursor HZ");
    let mut cursor_hz = effective!(m, t).vrr_cursor_hz.unwrap_or(60.0);
    changed |= DragValue::new(&mut cursor_hz)
        .range(0.0..=500.0)
        .ui(ui)
        .changed();
    if changed {
        modify!(m, t).vrr_cursor_hz = Some(cursor_hz);
    }
    let mut diff2 = false;
    if let Some(t) = t
        && m.vrr_cursor_hz != t.vrr_cursor_hz
    {
        diff2 = true;
        ui.label(format!(
            "{}",
            fmt::from_fn(|f| match m.vrr_cursor_hz {
                None => f.write_str("disabled"),
                Some(v) => write!(f, "{}", v),
            })
        ));
    }
    ui.end_row();
    diff1 || diff2
}

fn show_flip_margin(
    state: &State,
    ui: &mut Ui,
    m: &HeadState,
    t: &mut Option<HeadState>,
    connector_id: ConnectorId,
) {
    if !effective!(m, t).in_compositor_space {
        return;
    }
    let Some(node) = state.root.outputs.get(&connector_id) else {
        return;
    };
    let Some(margin) = node.flip_margin_ns.get() else {
        return;
    };
    label(
        ui,
        "Flip Margin (ms)",
        format!("{}", margin as f64 / 1_000_000.0),
    );
}
