use {
    crate::{
        async_engine::SpawnedFuture,
        backend::{
            self, BackendColorSpace, BackendEotfs, ConnectorId, DrmDeviceId,
            InputDeviceAccelProfile, InputDeviceCapability, InputDeviceClickMethod, InputDeviceId,
            transaction::BackendConnectorTransactionError,
        },
        client::{CAP_JAY_COMPOSITOR, Client, ClientCaps, ClientId},
        cmm::cmm_eotf::Eotf,
        compositor::{MAX_EXTENTS, WAYLAND_DISPLAY},
        criteria::{
            CritLiteralOrRegex, CritMgrExt, CritTarget, CritUpstreamNode,
            clm::ClmLeafMatcher,
            tlm::{TlmLeafMatcher, TlmUpstreamNode},
        },
        format::config_formats,
        ifs::{
            wl_output::BlendSpace,
            wl_seat::{SeatId, WlSeatGlobal},
            wp_content_type_v1::ContentTypeExt,
        },
        io_uring::TaskResultExt,
        kbvm::{KbvmError, KbvmMap},
        output_schedule::map_cursor_hz,
        scale::Scale,
        state::{ConnectorData, DeviceHandlerData, DrmDevData, OutputData, State},
        tagged_acceptor::TaggedAcceptorError,
        theme::{ThemeColor, ThemeSized},
        tree::{
            ContainerSplit, OutputNode, TearingMode, TileState, ToplevelData, ToplevelIdentifier,
            ToplevelNode, VrrMode, WorkspaceNode, toplevel_create_split, toplevel_parent_container,
            toplevel_set_floating, toplevel_set_workspace,
        },
        utils::{
            asyncevent::AsyncEvent,
            copyhashmap::CopyHashMap,
            errorfmt::ErrorFmt,
            numcell::NumCell,
            oserror::{OsError, OsErrorExt},
            stack::Stack,
            timer::{TimerError, TimerFd},
        },
    },
    bincode::Options,
    jay_config::{
        _private::{
            ClientCriterionIpc, ClientCriterionStringField, GenericCriterionIpc, PollableId,
            WindowCriterionIpc, WindowCriterionStringField, WireMode, bincode_ops,
            ipc::{ClientMessage, Response, ServerMessage, WorkspaceSource},
        },
        Axis, Direction, Workspace,
        client::{Client as ConfigClient, ClientCapabilities, ClientMatcher},
        input::{
            FallbackOutputMode, FocusFollowsMouseMode, InputDevice, LayerDirection, Seat, Timeline,
            acceleration::{ACCEL_PROFILE_ADAPTIVE, ACCEL_PROFILE_FLAT, AccelProfile},
            capability::{
                CAP_GESTURE, CAP_KEYBOARD, CAP_POINTER, CAP_SWITCH, CAP_TABLET_PAD,
                CAP_TABLET_TOOL, CAP_TOUCH, Capability,
            },
            clickmethod::{
                CLICK_METHOD_BUTTON_AREAS, CLICK_METHOD_CLICKFINGER, CLICK_METHOD_NONE, ClickMethod,
            },
        },
        keyboard::{Group, Keymap, mods::Modifiers, syms::KeySym},
        logging::LogLevel as ConfigLogLevel,
        theme::{BarPosition, colors::Colorable, sized::Resizable},
        timer::Timer as JayTimer,
        video::{
            BlendSpace as ConfigBlendSpace, ColorSpace, Connector, DrmDevice, Eotf as ConfigEotf,
            Format as ConfigFormat, GfxApi, TearingMode as ConfigTearingMode, Transform,
            VrrMode as ConfigVrrMode,
        },
        window::{TileState as ConfigTileState, Window, WindowMatcher},
        workspace::WorkspaceDisplayOrder,
        xwayland::XScalingMode,
    },
    kbvm::Keycode,
    libloading::Library,
    log::Level,
    regex::Regex,
    std::{
        cell::Cell,
        fmt,
        hash::Hash,
        ops::Deref,
        rc::{Rc, Weak},
        time::{Duration, SystemTime},
    },
    thiserror::Error,
    uapi::{OwnedFd, c, fcntl_dupfd_cloexec},
};

pub(super) struct ConfigProxyHandler {
    pub path: Option<String>,
    pub client_data: Cell<*const u8>,
    pub dropped: Cell<bool>,
    pub _lib: Option<Library>,
    pub _version: u32,
    pub unref: unsafe extern "C" fn(data: *const u8),
    pub handle_msg: unsafe extern "C" fn(data: *const u8, msg: *const u8, size: usize),
    pub state: Rc<State>,
    pub next_id: NumCell<u64>,
    pub keymaps: CopyHashMap<Keymap, Rc<KbvmMap>>,
    pub bufs: Stack<Vec<u8>>,

    pub workspace_ids: NumCell<u64>,
    pub workspaces_by_name: CopyHashMap<Rc<String>, u64>,
    pub workspaces_by_id: CopyHashMap<u64, Rc<String>>,

    pub timer_ids: NumCell<u64>,
    pub timers_by_name: CopyHashMap<Rc<String>, Rc<TimerData>>,
    pub timers_by_id: CopyHashMap<u64, Rc<TimerData>>,

    pub pollable_id: NumCell<u64>,
    pub pollables: CopyHashMap<PollableId, Rc<Pollable>>,

    pub window_ids: NumCell<u64>,
    pub windows_from_tl_id: CopyHashMap<ToplevelIdentifier, Window>,
    pub windows_to_tl_id: CopyHashMap<Window, ToplevelIdentifier>,

    pub client_matcher_ids: NumCell<u64>,
    pub client_matchers:
        CopyHashMap<ClientMatcher, Rc<CachedCriterion<ClientCriterionIpc, Rc<Client>>>>,
    pub client_matcher_cache: CriterionCache<ClientCriterionIpc, Rc<Client>>,
    pub client_matcher_leafs: CopyHashMap<ClientMatcher, Rc<ClmLeafMatcher>>,
    pub client_matcher_capabilities: CopyHashMap<
        ClientMatcher,
        (
            Rc<CachedCriterion<ClientCriterionIpc, Rc<Client>>>,
            ClientCaps,
        ),
    >,
    pub client_matcher_bounding_capabilities: CopyHashMap<
        ClientMatcher,
        (
            Rc<CachedCriterion<ClientCriterionIpc, Rc<Client>>>,
            ClientCaps,
        ),
    >,

    pub window_matcher_ids: NumCell<u64>,
    pub window_matchers:
        CopyHashMap<WindowMatcher, Rc<CachedCriterion<WindowCriterionIpc, ToplevelData>>>,
    pub window_matcher_cache: CriterionCache<WindowCriterionIpc, ToplevelData>,
    pub window_matcher_leafs: CopyHashMap<WindowMatcher, Rc<TlmLeafMatcher>>,
    pub window_matcher_std_kinds: Rc<TlmUpstreamNode>,
    pub window_matcher_no_auto_focus:
        CopyHashMap<WindowMatcher, Rc<CachedCriterion<WindowCriterionIpc, ToplevelData>>>,
    pub window_matcher_initial_tile_state: CopyHashMap<
        WindowMatcher,
        (
            Rc<CachedCriterion<WindowCriterionIpc, ToplevelData>>,
            TileState,
        ),
    >,
}

pub struct Pollable {
    write_trigger: Rc<AsyncEvent>,
    _write_future: SpawnedFuture<()>,
    read_trigger: Rc<AsyncEvent>,
    _read_future: SpawnedFuture<()>,
}

pub(super) struct TimerData {
    timer: TimerFd,
    id: u64,
    name: Rc<String>,
    _handler: SpawnedFuture<()>,
}

pub type CriterionCache<K, T> = Rc<CopyHashMap<K, Weak<CachedCriterion<K, T>>>>;

pub struct CachedCriterion<K, T>
where
    K: Hash + Eq,
    T: CritTarget,
{
    crit: K,
    cache: CriterionCache<K, T>,
    upstream: Vec<Rc<CachedCriterion<K, T>>>,
    node: Rc<dyn CritUpstreamNode<T>>,
}

impl<K, T> Drop for CachedCriterion<K, T>
where
    K: Hash + Eq,
    T: CritTarget,
{
    fn drop(&mut self) {
        self.cache.remove(&self.crit);
    }
}

impl<K, T> CachedCriterion<K, T>
where
    K: Hash + Eq,
    T: CritTarget,
{
    fn any(&self, v: &impl Fn(&K) -> bool) -> bool {
        v(&self.crit) || self.upstream.iter().any(|u| u.any(v))
    }
}

impl ConfigProxyHandler {
    pub fn do_drop(&self) {
        self.dropped.set(true);

        self.timers_by_name.clear();
        self.timers_by_id.clear();

        self.pollables.clear();

        self.client_matcher_leafs.clear();
        self.client_matchers.clear();

        self.window_matcher_leafs.clear();
        self.window_matchers.clear();

        if let Some(path) = &self.path
            && let Err(e) = uapi::unlink(path.as_str())
        {
            log::error!("Could not unlink {}: {}", path, ErrorFmt(OsError(e.0)));
        }
    }

    pub fn send(&self, msg: &ServerMessage) {
        let mut buf = self.bufs.pop().unwrap_or_default();
        buf.clear();
        bincode_ops().serialize_into(&mut buf, msg).unwrap();
        unsafe {
            (self.handle_msg)(self.client_data.get(), buf.as_ptr(), buf.len());
        }
        self.bufs.push(buf);
    }

    pub fn respond(&self, msg: Response) {
        self.send(&ServerMessage::Response { response: msg })
    }

    fn id(&self) -> u64 {
        self.next_id.fetch_add(1)
    }

    fn get_workspace_by_name(&self, name: &String) -> Workspace {
        let id = match self.workspaces_by_name.get(name) {
            None => {
                let id = self.workspace_ids.fetch_add(1);
                let name = Rc::new(name.clone());
                self.workspaces_by_name.set(name.clone(), id);
                self.workspaces_by_id.set(id, name);
                id
            }
            Some(id) => id,
        };
        Workspace(id)
    }

    fn handle_log_request(
        &self,
        level: ConfigLogLevel,
        msg: &str,
        file: Option<&str>,
        line: Option<u32>,
    ) {
        let level = match level {
            ConfigLogLevel::Error => Level::Error,
            ConfigLogLevel::Warn => Level::Warn,
            ConfigLogLevel::Info => Level::Info,
            ConfigLogLevel::Debug => Level::Debug,
            ConfigLogLevel::Trace => Level::Trace,
        };
        let debug = fmt::from_fn(|fmt| {
            if let Some(file) = file {
                write!(fmt, "{}", file)?;
                if let Some(line) = line {
                    write!(fmt, ":{}", line)?;
                }
                write!(fmt, ": ")?;
            }
            write!(fmt, "{}", msg)?;
            Ok(())
        });
        log::log!(level, "{:?}", debug);
    }

    fn handle_get_seat(&self, name: &str) {
        for seat in self.state.globals.seats.lock().values() {
            if seat.seat_name() == name {
                self.respond(Response::GetSeat {
                    seat: Seat(seat.id().raw() as _),
                });
                return;
            }
        }
        let seat = self.state.create_seat(name);
        self.respond(Response::GetSeat {
            seat: Seat(seat.id().raw() as _),
        });
    }

    fn handle_parse_keymap(&self, keymap: &str) -> Result<(), CphError> {
        let (keymap, res) = match self.state.kb_ctx.parse_keymap(keymap.as_bytes()) {
            Ok(keymap) => {
                let id = Keymap(self.id());
                self.keymaps.set(id, keymap);
                (id, Ok(()))
            }
            Err(e) => (Keymap::INVALID, Err(CphError::ParseKeymapError(e))),
        };
        self.respond(Response::ParseKeymap { keymap });
        res
    }

    fn handle_keymap_from_names(
        &self,
        rules: Option<&str>,
        model: Option<&str>,
        groups: Option<Vec<Group<'_>>>,
        options: Option<Vec<&str>>,
    ) -> Result<(), CphError> {
        let kbvm_groups = groups.map(|groups| {
            groups
                .iter()
                .map(|g| kbvm::xkb::rmlvo::Group {
                    layout: g.layout,
                    variant: g.variant,
                })
                .collect::<Vec<_>>()
        });
        let (keymap, res) = match self.state.kb_ctx.keymap_from_names(
            rules,
            model,
            kbvm_groups.as_deref(),
            options.as_deref(),
        ) {
            Ok(keymap) => {
                let id = Keymap(self.id());
                self.keymaps.set(id, keymap);
                (id, Ok(()))
            }
            Err(e) => (Keymap::INVALID, Err(CphError::ParseKeymapError(e))),
        };
        self.respond(Response::KeymapFromNames { keymap });
        res
    }

    fn handle_get_connectors(
        &self,
        dev: Option<DrmDevice>,
        connected_only: bool,
    ) -> Result<(), CphError> {
        let datas: Vec<_>;
        if let Some(dev) = dev {
            let dev = self.get_drm_device(dev)?;
            datas = dev.connectors.lock().values().cloned().collect();
        } else {
            datas = self.state.connectors.lock().values().cloned().collect();
        }
        let connectors = datas
            .iter()
            .flat_map(|d| match (connected_only, d.connected.get()) {
                (false, _) | (true, true) => Some(Connector(d.connector.id().raw() as _)),
                _ => None,
            })
            .collect();
        self.respond(Response::GetConnectors { connectors });
        Ok(())
    }

    fn handle_get_drm_device_syspath(&self, dev: DrmDevice) -> Result<(), CphError> {
        let dev = self.get_drm_device(dev)?;
        let syspath = dev.syspath.clone().unwrap_or_default();
        self.respond(Response::GetDrmDeviceSyspath { syspath });
        Ok(())
    }

    fn handle_get_drm_device_devnode(&self, dev: DrmDevice) -> Result<(), CphError> {
        let dev = self.get_drm_device(dev)?;
        let devnode = dev.devnode.clone().unwrap_or_default();
        self.respond(Response::GetDrmDeviceDevnode { devnode });
        Ok(())
    }

    fn handle_get_drm_device_vendor(&self, dev: DrmDevice) -> Result<(), CphError> {
        let dev = self.get_drm_device(dev)?;
        let vendor = dev.vendor.clone().unwrap_or_default();
        self.respond(Response::GetDrmDeviceVendor { vendor });
        Ok(())
    }

    fn handle_get_drm_devices(&self) {
        let devs = self.state.drm_devs.lock();
        let mut res = vec![];
        for dev in devs.values() {
            res.push(DrmDevice(dev.dev.id().raw() as _));
        }
        self.respond(Response::GetDrmDevices { devices: res });
    }

    fn handle_make_render_device(&self, dev: DrmDevice) -> Result<(), CphError> {
        let dev = self.get_drm_device(dev)?;
        dev.make_render_device();
        Ok(())
    }

    fn handle_get_drm_device_model(&self, dev: DrmDevice) -> Result<(), CphError> {
        let dev = self.get_drm_device(dev)?;
        let model = dev.model.clone().unwrap_or_default();
        self.respond(Response::GetDrmDeviceModel { model });
        Ok(())
    }

    fn handle_get_drm_device_pci_id(&self, dev: DrmDevice) -> Result<(), CphError> {
        let dev = self.get_drm_device(dev)?;
        let pci_id = dev.pci_id.unwrap_or_default();
        self.respond(Response::GetDrmDevicePciId { pci_id });
        Ok(())
    }

    fn handle_reload(&self) {
        self.state.reload_config();
    }

    fn handle_get_seat_fullscreen(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        self.respond(Response::GetFullscreen {
            fullscreen: seat.get_fullscreen(),
        });
        Ok(())
    }

    fn handle_set_seat_fullscreen(&self, seat: Seat, fullscreen: bool) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.set_fullscreen(fullscreen);
        Ok(())
    }

    fn handle_get_window_fullscreen(&self, window: Window) -> Result<(), CphError> {
        let tl = self.get_window(window)?;
        self.respond(Response::GetWindowFullscreen {
            fullscreen: tl.tl_data().is_fullscreen.get(),
        });
        Ok(())
    }

    fn handle_set_window_fullscreen(
        &self,
        window: Window,
        fullscreen: bool,
    ) -> Result<(), CphError> {
        let tl = self.get_window(window)?;
        tl.tl_set_fullscreen(fullscreen, None);
        Ok(())
    }

    fn handle_set_keymap(&self, seat: Seat, keymap: Keymap) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        let keymap = if keymap.is_invalid() {
            self.state.default_keymap.clone()
        } else {
            self.get_keymap(keymap)?
        };
        seat.set_seat_keymap(&keymap);
        Ok(())
    }

    fn handle_set_device_keymap(
        &self,
        device: InputDevice,
        keymap: Keymap,
    ) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        let map = if keymap.is_invalid() {
            None
        } else {
            Some(self.get_keymap(keymap)?)
        };
        dev.set_keymap(&self.state, map);
        Ok(())
    }

    fn handle_set_forward(&self, seat: Seat, forward: bool) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.set_forward(forward);
        Ok(())
    }

    fn handle_set_focus_follows_mouse_mode(
        &self,
        seat: Seat,
        mode: FocusFollowsMouseMode,
    ) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        let focus_follows_mouse = match mode {
            FocusFollowsMouseMode::True => true,
            FocusFollowsMouseMode::False => false,
        };
        seat.set_focus_follows_mouse(focus_follows_mouse);
        Ok(())
    }

    fn handle_set_fallback_output_mode(
        &self,
        seat: Seat,
        mode: FallbackOutputMode,
    ) -> Result<(), CphError> {
        let Ok(mode) = mode.try_into() else {
            return Err(CphError::UnknownFallbackOutputMode(mode));
        };
        let seat = self.get_seat(seat)?;
        seat.set_fallback_output_mode(mode);
        Ok(())
    }

    fn handle_set_window_management_enabled(
        &self,
        seat: Seat,
        enabled: bool,
    ) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.set_window_management_enabled(enabled);
        Ok(())
    }

    fn handle_set_input_device_connector(
        &self,
        input_device: InputDevice,
        connector: Connector,
    ) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(input_device)?;
        let output = self.get_output_node(connector)?;
        dev.set_output(&self.state, Some(&output.global));
        Ok(())
    }

    fn handle_remove_input_mapping(&self, input_device: InputDevice) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(input_device)?;
        dev.set_output(&self.state, None);
        Ok(())
    }

    fn handle_set_status(&self, status: &str) {
        self.state.set_status(status);
    }

    fn get_timer(&self, timer: JayTimer) -> Result<Rc<TimerData>, CphError> {
        match self.timers_by_id.get(&timer.0) {
            Some(t) => Ok(t),
            _ => Err(CphError::TimerDoesNotExist(timer)),
        }
    }

    fn handle_remove_timer(&self, timer: JayTimer) -> Result<(), CphError> {
        let timer = self.get_timer(timer)?;
        self.timers_by_id.remove(&timer.id);
        self.timers_by_name.remove(&timer.name);
        Ok(())
    }

    fn handle_set_env(&self, key: &str, val: &str) {
        if let Some(f) = self.state.forker.get() {
            f.setenv(key.as_bytes(), val.as_bytes());
        }
    }

    fn handle_unset_env(&self, key: &str) {
        if let Some(f) = self.state.forker.get() {
            f.unsetenv(key.as_bytes());
        }
    }

    fn handle_get_config_dir(&self) {
        let dir = self.state.config_dir.clone().unwrap_or_default();
        self.respond(Response::GetConfigDir { dir });
    }

    fn handle_get_workspaces(&self) {
        let mut workspaces = vec![];
        for ws in self.state.workspaces.lock().values() {
            workspaces.push(self.get_workspace_by_name(&ws.name));
        }
        self.respond(Response::GetWorkspaces { workspaces });
    }

    fn handle_program_timer(
        &self,
        timer: JayTimer,
        initial: Option<Duration>,
        periodic: Option<Duration>,
    ) -> Result<(), CphError> {
        let timer = self.get_timer(timer)?;
        timer.timer.program(initial, periodic)?;
        Ok(())
    }

    fn handle_get_timer(self: &Rc<Self>, name: &str) -> Result<(), CphError> {
        let name = Rc::new(name.to_owned());
        if let Some(t) = self.timers_by_name.get(&name) {
            self.respond(Response::GetTimer {
                timer: JayTimer(t.id),
            });
            return Ok(());
        }
        let id = self.timer_ids.fetch_add(1);
        let timer = TimerFd::new(c::CLOCK_BOOTTIME)?;
        let handler = {
            let timer = timer.clone();
            let slf = self.clone();
            self.state.eng.spawn("config timer", async move {
                loop {
                    match timer.expired(&slf.state.ring).await {
                        Ok(_) => slf.send(&ServerMessage::TimerExpired {
                            timer: JayTimer(id),
                        }),
                        Err(e) => {
                            log::error!("Could not wait for timer expiration: {}", ErrorFmt(e));
                            if let Some(timer) = slf.timers_by_id.remove(&id) {
                                slf.timers_by_name.remove(&timer.name);
                            }
                            return;
                        }
                    }
                }
            })
        };
        let td = Rc::new(TimerData {
            timer,
            id,
            name: name.clone(),
            _handler: handler,
        });
        self.timers_by_name.set(name.clone(), td.clone());
        self.timers_by_id.set(id, td.clone());
        self.respond(Response::GetTimer {
            timer: JayTimer(id),
        });
        Ok(())
    }

    fn handle_seat_close(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.close();
        Ok(())
    }

    fn handle_window_close(&self, window: Window) -> Result<(), CphError> {
        let window = self.get_window(window)?;
        window.tl_close();
        Ok(())
    }

    fn handle_seat_focus(&self, seat: Seat, direction: Direction) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.move_focus(direction.into());
        Ok(())
    }

    fn handle_seat_move(&self, seat: Seat, direction: Direction) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.move_focused(direction.into());
        Ok(())
    }

    fn handle_window_move(&self, window: Window, direction: Direction) -> Result<(), CphError> {
        let window = self.get_window(window)?;
        if let Some(c) = toplevel_parent_container(&*window) {
            c.move_child(window, direction.into());
        }
        Ok(())
    }

    fn handle_get_repeat_rate(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        let (rate, delay) = seat.get_rate();
        self.respond(Response::GetRepeatRate { rate, delay });
        Ok(())
    }

    fn handle_set_repeat_rate(&self, seat: Seat, rate: i32, delay: i32) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        if rate < 0 {
            return Err(CphError::NegativeRepeatRate);
        }
        if delay < 0 {
            return Err(CphError::NegativeRepeatDelay);
        }
        seat.set_rate(rate, delay);
        Ok(())
    }

    fn get_workspace(&self, ws: Workspace) -> Result<Rc<String>, CphError> {
        match self.workspaces_by_id.get(&ws.0) {
            Some(ws) => Ok(ws),
            _ => Err(CphError::WorkspaceDoesNotExist(ws)),
        }
    }

    fn get_existing_workspace(&self, ws: Workspace) -> Result<Option<Rc<WorkspaceNode>>, CphError> {
        self.get_workspace(ws)
            .map(|ws| self.state.workspaces.get(&*ws))
    }

    fn get_device_handler_data(
        &self,
        device: InputDevice,
    ) -> Result<Rc<DeviceHandlerData>, CphError> {
        let data = self
            .state
            .input_device_handlers
            .borrow_mut()
            .get(&InputDeviceId::from_raw(device.0 as _))
            .map(|d| d.data.clone());
        match data {
            Some(d) => Ok(d),
            _ => Err(CphError::DeviceDoesNotExist(device)),
        }
    }

    fn get_connector(&self, connector: Connector) -> Result<Rc<ConnectorData>, CphError> {
        let data = self
            .state
            .connectors
            .get(&ConnectorId::from_raw(connector.0 as _));
        match data {
            Some(d) => Ok(d),
            _ => Err(CphError::ConnectorDoesNotExist(connector)),
        }
    }

    fn get_output(&self, connector: Connector) -> Result<Rc<OutputData>, CphError> {
        let data = self
            .state
            .outputs
            .get(&ConnectorId::from_raw(connector.0 as _));
        match data {
            Some(d) => Ok(d),
            _ => Err(CphError::OutputDoesNotExist(connector)),
        }
    }

    fn get_output_node(&self, connector: Connector) -> Result<Rc<OutputNode>, CphError> {
        let data = self.get_output(connector)?;
        match data.node.clone() {
            Some(d) => Ok(d),
            _ => Err(CphError::OutputIsNotDesktop(connector)),
        }
    }

    fn get_drm_device(&self, dev: DrmDevice) -> Result<Rc<DrmDevData>, CphError> {
        match self.state.drm_devs.get(&DrmDeviceId::from_raw(dev.0 as _)) {
            Some(dev) => Ok(dev),
            _ => Err(CphError::DrmDeviceDoesNotExist(dev)),
        }
    }

    fn get_seat(&self, seat: Seat) -> Result<Rc<WlSeatGlobal>, CphError> {
        let seats = self.state.globals.seats.lock();
        for seat_global in seats.values() {
            if seat_global.id().raw() == seat.0 as u32 {
                return Ok(seat_global.clone());
            }
        }
        Err(CphError::SeatDoesNotExist(seat))
    }

    fn get_kb(&self, kb: InputDevice) -> Result<Rc<dyn backend::InputDevice>, CphError> {
        let kbs = self.state.input_device_handlers.borrow_mut();
        match kbs.get(&(InputDeviceId::from_raw(kb.0 as _))) {
            None => Err(CphError::KeyboardDoesNotExist(kb)),
            Some(kb) => Ok(kb.data.device.clone()),
        }
    }

    fn get_keymap(&self, keymap: Keymap) -> Result<Rc<KbvmMap>, CphError> {
        match self.keymaps.get(&keymap) {
            Some(k) => Ok(k),
            None => Err(CphError::KeymapDoesNotExist(keymap)),
        }
    }

    fn handle_set_seat(&self, device: InputDevice, seat: Seat) -> Result<(), CphError> {
        let seat = if seat.is_invalid() {
            None
        } else {
            Some(self.get_seat(seat)?)
        };
        let dev = self.get_device_handler_data(device)?;
        dev.set_seat(&self.state, seat);
        Ok(())
    }

    fn handle_set_left_handed(
        &self,
        device: InputDevice,
        left_handed: bool,
    ) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        dev.set_left_handed(&self.state, left_handed);
        Ok(())
    }

    fn handle_set_accel_profile(
        &self,
        device: InputDevice,
        accel_profile: AccelProfile,
    ) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        let profile = match accel_profile {
            ACCEL_PROFILE_FLAT => InputDeviceAccelProfile::Flat,
            ACCEL_PROFILE_ADAPTIVE => InputDeviceAccelProfile::Adaptive,
            _ => return Err(CphError::UnknownAccelProfile(accel_profile)),
        };
        dev.set_accel_profile(&self.state, profile);
        Ok(())
    }

    fn handle_set_accel_speed(&self, device: InputDevice, speed: f64) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        dev.set_accel_speed(&self.state, speed);
        Ok(())
    }

    fn handle_set_px_per_wheel_scroll(&self, device: InputDevice, px: f64) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        dev.set_px_per_scroll_wheel(&self.state, px);
        Ok(())
    }

    fn handle_set_tap_enabled(&self, device: InputDevice, enabled: bool) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        dev.set_tap_enabled(&self.state, enabled);
        Ok(())
    }

    fn handle_set_drag_enabled(&self, device: InputDevice, enabled: bool) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        dev.set_drag_enabled(&self.state, enabled);
        Ok(())
    }

    fn handle_set_natural_scrolling_enabled(
        &self,
        device: InputDevice,
        enabled: bool,
    ) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        dev.set_natural_scrolling_enabled(&self.state, enabled);
        Ok(())
    }

    fn handle_set_drag_lock_enabled(
        &self,
        device: InputDevice,
        enabled: bool,
    ) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        dev.set_drag_lock_enabled(&self.state, enabled);
        Ok(())
    }

    fn handle_set_transform_matrix(
        &self,
        device: InputDevice,
        matrix: [[f64; 2]; 2],
    ) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        dev.set_transform_matrix(&self.state, matrix);
        Ok(())
    }

    fn handle_set_calibration_matrix(
        &self,
        device: InputDevice,
        matrix: [[f32; 3]; 2],
    ) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        dev.set_calibration_matrix(&self.state, matrix);
        Ok(())
    }

    fn handle_set_click_method(
        &self,
        device: InputDevice,
        click_method: ClickMethod,
    ) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        let method = match click_method {
            CLICK_METHOD_NONE => InputDeviceClickMethod::None,
            CLICK_METHOD_BUTTON_AREAS => InputDeviceClickMethod::ButtonAreas,
            CLICK_METHOD_CLICKFINGER => InputDeviceClickMethod::Clickfinger,
            _ => return Err(CphError::UnknownClickMethod(click_method)),
        };
        dev.set_click_method(&self.state, method);
        Ok(())
    }

    fn handle_set_middle_button_emulation_enabled(
        &self,
        device: InputDevice,
        enabled: bool,
    ) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        dev.set_middle_button_emulation_enabled(&self.state, enabled);
        Ok(())
    }

    fn handle_set_ei_socket_enabled(&self, enabled: bool) {
        self.state.set_ei_socket_enabled(enabled);
    }

    fn handle_get_workspace(&self, name: &str) {
        self.respond(Response::GetWorkspace {
            workspace: self.get_workspace_by_name(&name.to_owned()),
        });
    }

    fn handle_get_workspace_capture(&self, workspace: Workspace) -> Result<(), CphError> {
        let ws = self.get_existing_workspace(workspace)?;
        let capture = match ws {
            Some(ws) => ws.may_capture.get(),
            None => self.state.default_workspace_capture.get(),
        };
        self.respond(Response::GetWorkspaceCapture { capture });
        Ok(())
    }

    fn handle_set_workspace_capture(
        &self,
        workspace: Workspace,
        capture: bool,
    ) -> Result<(), CphError> {
        if let Some(ws) = self.get_existing_workspace(workspace)? {
            ws.may_capture.set(capture);
            ws.update_has_captures();
        }
        Ok(())
    }

    fn handle_set_gfx_api(&self, device: Option<DrmDevice>, api: GfxApi) -> Result<(), CphError> {
        let Ok(api) = api.try_into() else {
            return Err(CphError::UnknownGfxApi(api));
        };
        match device {
            Some(dev) => self.get_drm_device(dev)?.dev.set_gfx_api(api),
            _ => self.state.default_gfx_api.set(api),
        }
        Ok(())
    }

    fn handle_set_flip_margin(&self, device: DrmDevice, margin: Duration) -> Result<(), CphError> {
        self.get_drm_device(device)?.set_flip_margin(
            &self.state,
            margin.as_nanos().try_into().unwrap_or(u64::MAX),
        );
        Ok(())
    }

    fn handle_set_x_scaling_mode(&self, mode: XScalingMode) -> Result<(), CphError> {
        let use_wire_scale = match mode {
            XScalingMode::DEFAULT => false,
            XScalingMode::DOWNSCALED => true,
            _ => return Err(CphError::UnknownXScalingMode(mode)),
        };
        self.state.set_xwayland_use_wire_scale(use_wire_scale);
        Ok(())
    }

    fn handle_set_x_wayland_enabled(&self, enabled: bool) -> Result<(), CphError> {
        self.state.set_xwayland_enabled(enabled);
        Ok(())
    }

    fn handle_set_ui_drag_enabled(&self, enabled: bool) {
        self.state.set_ui_drag_enabled(enabled);
    }

    fn handle_set_ui_drag_threshold(&self, threshold: i32) {
        self.state.set_ui_drag_threshold(threshold.max(1));
    }

    fn handle_set_direct_scanout_enabled(
        &self,
        device: Option<DrmDevice>,
        enabled: bool,
    ) -> Result<(), CphError> {
        match device {
            Some(dev) => self
                .get_drm_device(dev)?
                .set_direct_scanout_enabled(&self.state, enabled),
            _ => self.state.direct_scanout_enabled.set(enabled),
        }
        Ok(())
    }

    fn handle_get_default_workspace_capture(&self) {
        self.respond(Response::GetDefaultWorkspaceCapture {
            capture: self.state.default_workspace_capture.get(),
        });
    }

    fn handle_set_default_workspace_capture(&self, capture: bool) {
        self.state.default_workspace_capture.set(capture);
    }

    fn handle_set_double_click_interval_usec(&self, usec: u64) {
        self.state.double_click_interval_usec.set(usec);
    }

    fn handle_set_double_click_distance(&self, dist: i32) {
        self.state.double_click_distance.set(dist);
    }

    fn handle_get_seat_cursor_workspace(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        let output = seat.get_cursor_output();
        let mut workspace = Workspace(0);
        if !output.is_dummy
            && let Some(ws) = output.workspace.get()
        {
            workspace = self.get_workspace_by_name(&ws.name);
        }
        self.respond(Response::GetSeatCursorWorkspace { workspace });
        Ok(())
    }

    fn handle_get_seat_keyboard_workspace(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        let mut workspace = Workspace(0);
        if let Some(output) = seat.get_keyboard_output()
            && !output.is_dummy
            && let Some(ws) = output.workspace.get()
        {
            workspace = self.get_workspace_by_name(&ws.name);
        }
        self.respond(Response::GetSeatKeyboardWorkspace { workspace });
        Ok(())
    }

    fn handle_show_workspace(
        &self,
        seat: Seat,
        ws: Workspace,
        output: Option<Connector>,
    ) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        let name = self.get_workspace(ws)?;
        let output = output.map(|o| self.get_output_node(o)).transpose()?;
        self.state.show_workspace(&seat, &name, output);
        Ok(())
    }

    fn handle_set_seat_workspace(&self, seat: Seat, ws: Workspace) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        let name = self.get_workspace(ws)?;
        let workspace = match self.state.workspaces.get(name.deref()) {
            Some(ws) => ws,
            _ => seat.get_fallback_output().create_workspace(name.deref()),
        };
        seat.set_workspace(&workspace);
        Ok(())
    }

    fn handle_set_window_workspace(&self, window: Window, ws: Workspace) -> Result<(), CphError> {
        let window = self.get_window(window)?;
        let name = self.get_workspace(ws)?;
        let workspace = match self.state.workspaces.get(name.deref()) {
            Some(ws) => ws,
            _ => match window.node_output() {
                Some(o) => o.create_workspace(name.deref()),
                _ => return Ok(()),
            },
        };
        toplevel_set_workspace(&self.state, window, &workspace);
        Ok(())
    }

    fn handle_get_device_name(&self, device: InputDevice) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        let name = dev.device.name();
        self.respond(Response::GetDeviceName {
            name: name.to_string(),
        });
        Ok(())
    }

    fn handle_get_input_device_syspath(&self, device: InputDevice) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        self.respond(Response::GetInputDeviceSyspath {
            syspath: dev.syspath.clone().unwrap_or_default(),
        });
        Ok(())
    }

    fn handle_get_input_device_devnode(&self, device: InputDevice) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        self.respond(Response::GetInputDeviceDevnode {
            devnode: dev.devnode.clone().unwrap_or_default(),
        });
        Ok(())
    }

    fn handle_move_to_output(
        &self,
        workspace: WorkspaceSource,
        connector: Connector,
    ) -> Result<(), CphError> {
        let output = self.get_output_node(connector)?;
        let ws = match workspace {
            WorkspaceSource::Explicit(ws) => match self.get_existing_workspace(ws)? {
                Some(ws) => ws,
                _ => return Ok(()),
            },
            WorkspaceSource::Seat(s) => {
                match self.get_seat(s)?.get_fallback_output().workspace.get() {
                    Some(ws) => ws,
                    _ => return Ok(()),
                }
            }
        };
        self.state.move_ws_to_output(&ws, &output);
        Ok(())
    }

    fn handle_set_idle(&self, timeout: Duration) {
        self.state.idle.set_timeout(&self.state, timeout);
    }

    fn handle_set_idle_grace_period(&self, period: Duration) {
        self.state.idle.set_grace_period(&self.state, period);
    }

    fn handle_set_explicit_sync_enabled(&self, enabled: bool) {
        self.state.set_explicit_sync_enabled(enabled);
    }

    fn handle_set_color_management_enabled(&self, enabled: bool) {
        self.state.set_color_management_enabled(enabled);
    }

    fn handle_get_socket_path(&self) {
        match self.state.acceptor.get() {
            Some(a) => {
                self.respond(Response::GetSocketPath {
                    path: a.socket_name().to_string(),
                });
            }
            _ => {
                log::warn!("There is no acceptor");
            }
        }
    }

    fn handle_connector_connected(&self, connector: Connector) -> Result<(), CphError> {
        let connector = self.get_connector(connector)?;
        self.respond(Response::ConnectorConnected {
            connected: connector.connected.get(),
        });
        Ok(())
    }

    fn handle_connector_type(&self, connector: Connector) -> Result<(), CphError> {
        let connector = self.get_connector(connector)?;
        self.respond(Response::ConnectorType {
            ty: connector.connector.kernel_id().ty.to_config(),
        });
        Ok(())
    }

    fn handle_connector_mode(&self, connector: Connector) -> Result<(), CphError> {
        let connector = self.get_output_node(connector)?;
        let mode = connector.global.mode.get();
        self.respond(Response::ConnectorMode {
            width: mode.width,
            height: mode.height,
            refresh_millihz: mode.refresh_rate_millihz,
        });
        Ok(())
    }

    fn handle_connector_set_mode(
        &self,
        connector: Connector,
        mode: WireMode,
    ) -> Result<(), CphError> {
        let connector = self.get_connector(connector)?;
        connector
            .modify_state(&self.state, |s| {
                s.mode = backend::Mode {
                    width: mode.width,
                    height: mode.height,
                    refresh_rate_millihz: mode.refresh_millihz,
                };
            })
            .map_err(CphError::ModifyConnectorState)?;
        Ok(())
    }

    fn handle_connector_modes(&self, connector: Connector) -> Result<(), CphError> {
        let connector = self.get_output_node(connector)?;
        self.respond(Response::ConnectorModes {
            modes: connector
                .global
                .modes
                .iter()
                .flatten()
                .map(|m| WireMode {
                    width: m.width,
                    height: m.height,
                    refresh_millihz: m.refresh_rate_millihz,
                })
                .collect(),
        });
        Ok(())
    }

    fn handle_connector_supports_arbitrary_modes(
        &self,
        connector: Connector,
    ) -> Result<(), CphError> {
        let connector = self.get_output_node(connector)?;
        self.respond(Response::ConnectorSupportsArbitraryModes {
            supports_arbitrary_modes: connector.global.modes.is_none(),
        });
        Ok(())
    }

    fn handle_connector_name(&self, connector: Connector) -> Result<(), CphError> {
        let connector = self.get_connector(connector)?;
        self.respond(Response::GetConnectorName {
            name: connector.name.deref().clone(),
        });
        Ok(())
    }

    fn handle_connector_model(&self, connector: Connector) -> Result<(), CphError> {
        let connector = self.get_output(connector)?;
        self.respond(Response::GetConnectorModel {
            model: connector.monitor_info.output_id.model.clone(),
        });
        Ok(())
    }

    fn handle_connector_manufacturer(&self, connector: Connector) -> Result<(), CphError> {
        let connector = self.get_output(connector)?;
        self.respond(Response::GetConnectorManufacturer {
            manufacturer: connector.monitor_info.output_id.manufacturer.clone(),
        });
        Ok(())
    }

    fn handle_connector_serial_number(&self, connector: Connector) -> Result<(), CphError> {
        let connector = self.get_output(connector)?;
        self.respond(Response::GetConnectorSerialNumber {
            serial_number: connector.monitor_info.output_id.serial_number.clone(),
        });
        Ok(())
    }

    fn handle_set_cursor_size(&self, seat: Seat, size: i32) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        if size < 0 {
            return Err(CphError::NegativeCursorSize);
        }
        seat.cursor_group().set_cursor_size(size as _);
        Ok(())
    }

    fn handle_disable_pointer_constraint(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.disable_pointer_constraint();
        Ok(())
    }

    fn handle_set_use_hardware_cursor(
        &self,
        seat: Seat,
        use_hardware_cursor: bool,
    ) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.cursor_group().set_hardware_cursor(use_hardware_cursor);
        self.state.refresh_hardware_cursors();
        Ok(())
    }

    fn handle_connector_size(&self, connector: Connector) -> Result<(), CphError> {
        let connector = self.get_output_node(connector)?;
        let pos = connector.global.pos.get();
        self.respond(Response::ConnectorSize {
            width: pos.width(),
            height: pos.height(),
        });
        Ok(())
    }

    fn handle_connector_get_scale(&self, connector: Connector) -> Result<(), CphError> {
        let connector = self.get_output_node(connector)?;
        self.respond(Response::ConnectorGetScale {
            scale: connector.global.persistent.scale.get().to_f64(),
        });
        Ok(())
    }

    fn handle_connector_set_scale(&self, connector: Connector, scale: f64) -> Result<(), CphError> {
        if scale < 0.1 {
            return Err(CphError::ScaleTooSmall(scale));
        }
        if scale > 1000.0 {
            return Err(CphError::ScaleTooLarge(scale));
        }
        let scale = Scale::from_f64(scale);
        let connector = self.get_output_node(connector)?;
        connector.set_preferred_scale(scale);
        Ok(())
    }

    fn handle_connector_set_format(
        &self,
        connector: Connector,
        format: ConfigFormat,
    ) -> Result<(), CphError> {
        let Some(&format) = config_formats().get(&format) else {
            return Err(CphError::UnknownFormat(format));
        };
        let connector = self.get_connector(connector)?;
        connector
            .modify_state(&self.state, |s| s.format = format)
            .map_err(CphError::ModifyConnectorState)?;
        Ok(())
    }

    fn handle_connector_set_colors(
        &self,
        connector: Connector,
        color_space: ColorSpace,
        eotf: ConfigEotf,
    ) -> Result<(), CphError> {
        let bcs = match color_space {
            ColorSpace::DEFAULT => BackendColorSpace::Default,
            ColorSpace::BT2020 => BackendColorSpace::Bt2020,
            _ => return Err(CphError::UnknownColorSpace(color_space)),
        };
        let btf = match eotf {
            ConfigEotf::DEFAULT => BackendEotfs::Default,
            ConfigEotf::PQ => BackendEotfs::Pq,
            _ => return Err(CphError::UnknownEotf(eotf)),
        };
        let connector = self.get_connector(connector)?;
        connector
            .modify_state(&self.state, |s| {
                s.color_space = bcs;
                s.eotf = btf;
            })
            .map_err(CphError::ModifyConnectorState)?;
        Ok(())
    }

    fn handle_connector_set_blend_space(
        &self,
        connector: Connector,
        blend_space: ConfigBlendSpace,
    ) -> Result<(), CphError> {
        let blend_space = match blend_space {
            ConfigBlendSpace::SRGB => BlendSpace::Srgb,
            ConfigBlendSpace::LINEAR => BlendSpace::Linear,
            _ => return Err(CphError::UnknownBlendSpace(blend_space)),
        };
        let connector = self.get_output_node(connector)?;
        connector.set_blend_space(blend_space);
        Ok(())
    }

    fn handle_connector_set_brightness(
        &self,
        connector: Connector,
        brightness: Option<f64>,
    ) -> Result<(), CphError> {
        let connector = self.get_output_node(connector)?;
        connector.set_brightness(brightness);
        Ok(())
    }

    fn handle_connector_set_use_native_gamut(
        &self,
        connector: Connector,
        use_native_gamut: bool,
    ) -> Result<(), CphError> {
        let connector = self.get_output_node(connector)?;
        connector.set_use_native_gamut(use_native_gamut);
        Ok(())
    }

    fn handle_set_float_above_fullscreen(&self, above: bool) {
        self.state.set_float_above_fullscreen(above);
    }

    fn handle_get_float_above_fullscreen(&self) {
        self.respond(Response::GetFloatAboveFullscreen {
            above: self.state.float_above_fullscreen.get(),
        });
    }

    fn handle_set_show_bar(&self, show: bool) {
        self.state.set_show_bar(show);
    }

    fn handle_get_show_bar(&self) {
        self.respond(Response::GetShowBar {
            show: self.state.show_bar.get(),
        });
    }

    fn handle_set_show_titles(&self, _show: bool) {
        // no-op: titles have been removed
    }

    fn handle_get_show_titles(&self) {
        self.respond(Response::GetShowTitles { show: false });
    }

    fn handle_set_floating_titles(&self, _floating: bool) {
        // no-op: titles have been removed
    }

    fn handle_get_floating_titles(&self) {
        self.respond(Response::GetFloatingTitles { floating: false });
    }

    fn handle_set_bar_position(&self, position: BarPosition) -> Result<(), CphError> {
        let Ok(position) = position.try_into() else {
            return Err(CphError::UnknownBarPosition(position));
        };
        self.state.set_bar_position(position);
        Ok(())
    }

    fn handle_get_bar_position(&self) {
        self.respond(Response::GetBarPosition {
            position: self.state.theme.bar_position.get().into(),
        });
    }

    fn handle_set_corner_radius(&self, radius: f32) {
        use crate::theme::CornerRadius;
        let radius = radius.max(0.0).min(1000.0);
        self.state
            .theme
            .corner_radius
            .set(CornerRadius::from(radius));
        self.state.damage(self.state.root.extents.get());
    }

    fn handle_get_corner_radius(&self) {
        self.respond(Response::GetCornerRadius {
            radius: self.state.theme.corner_radius.get().top_left,
        });
    }

    fn handle_set_show_float_pin_icon(&self, _show: bool) {
        // no-op: titles have been removed, pin icon was in title bar
    }

    fn handle_set_workspace_display_order(&self, order: WorkspaceDisplayOrder) {
        self.state.set_workspace_display_order(order.into());
    }

    fn handle_get_seat_float_pinned(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        self.respond(Response::GetFloatPinned {
            pinned: seat.pinned(),
        });
        Ok(())
    }

    fn handle_set_seat_float_pinned(&self, seat: Seat, pinned: bool) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.set_pinned(pinned);
        Ok(())
    }

    fn handle_get_window_float_pinned(&self, window: Window) -> Result<(), CphError> {
        let window = self.get_window(window)?;
        self.respond(Response::GetWindowFloatPinned {
            pinned: window.tl_pinned(),
        });
        Ok(())
    }

    fn handle_set_window_float_pinned(&self, window: Window, pinned: bool) -> Result<(), CphError> {
        let window = self.get_window(window)?;
        window.tl_set_pinned(true, pinned);
        Ok(())
    }

    fn handle_set_vrr_mode(
        &self,
        connector: Option<Connector>,
        mode: ConfigVrrMode,
    ) -> Result<(), CphError> {
        let Some(mode) = VrrMode::from_config(mode) else {
            return Err(CphError::UnknownVrrMode(mode));
        };
        match connector {
            Some(c) => {
                let connector = self.get_output_node(c)?;
                connector.set_vrr_mode(mode);
            }
            _ => self.state.default_vrr_mode.set(*mode),
        }
        Ok(())
    }

    fn handle_set_vrr_cursor_hz(
        &self,
        connector: Option<Connector>,
        hz: f64,
    ) -> Result<(), CphError> {
        match connector {
            Some(c) => {
                let connector = self.get_output_node(c)?;
                connector.schedule.set_cursor_hz(&self.state, hz);
            }
            _ => {
                let Some((hz, _)) = map_cursor_hz(hz) else {
                    return Err(CphError::InvalidCursorHz(hz));
                };
                self.state.default_vrr_cursor_hz.set(hz)
            }
        }
        Ok(())
    }

    fn handle_set_tearing_mode(
        &self,
        connector: Option<Connector>,
        mode: ConfigTearingMode,
    ) -> Result<(), CphError> {
        let Some(mode) = TearingMode::from_config(mode) else {
            return Err(CphError::UnknownTearingMode(mode));
        };
        match connector {
            Some(c) => {
                let connector = self.get_output_node(c)?;
                connector.set_tearing_mode(mode);
            }
            _ => self.state.default_tearing_mode.set(*mode),
        }
        Ok(())
    }

    fn handle_connector_set_transform(
        &self,
        connector: Connector,
        transform: Transform,
    ) -> Result<(), CphError> {
        let connector = self.get_output_node(connector)?;
        connector.update_transform(transform.into());
        Ok(())
    }

    fn handle_connector_set_position(
        &self,
        connector: Connector,
        x: i32,
        y: i32,
    ) -> Result<(), CphError> {
        let connector = self.get_output_node(connector)?;
        if x < 0 || y < 0 || x > MAX_EXTENTS || y > MAX_EXTENTS {
            return Err(CphError::InvalidConnectorPosition(x, y));
        }
        connector.set_position(x, y);
        Ok(())
    }

    fn handle_connector_get_position(&self, connector: Connector) -> Result<(), CphError> {
        let connector = self.get_output_node(connector)?;
        let (x, y) = connector.global.pos.get().position();
        self.respond(Response::ConnectorGetPosition { x, y });
        Ok(())
    }

    fn handle_connector_set_enabled(
        &self,
        connector: Connector,
        enabled: bool,
    ) -> Result<(), CphError> {
        let connector = self.get_connector(connector)?;
        connector
            .modify_state(&self.state, |s| {
                s.enabled = enabled;
            })
            .map_err(CphError::ModifyConnectorState)?;
        Ok(())
    }

    fn handle_get_connector(
        &self,
        ty: jay_config::video::connector_type::ConnectorType,
        idx: u32,
    ) -> Result<(), CphError> {
        let connectors = self.state.connectors.lock();
        let connector = 'get_connector: {
            for connector in connectors.values() {
                let kid = connector.connector.kernel_id();
                if ty == kid.ty.to_config() && idx == kid.idx {
                    break 'get_connector Connector(connector.connector.id().raw() as _);
                }
            }
            Connector(0)
        };
        self.respond(Response::GetConnector { connector });
        Ok(())
    }

    fn handle_get_connector_by_name(&self, name: &str) {
        let connector = self
            .state
            .connectors
            .lock()
            .values()
            .find(|c| *c.name == name)
            .map(|c| c.connector.id().raw() as _)
            .map(Connector)
            .unwrap_or(Connector(0));
        self.respond(Response::GetConnector { connector });
    }

    fn handle_create_virtual_output(&self, name: &str) {
        self.state.virtual_outputs.get_or_create(&self.state, name);
    }

    fn handle_remove_virtual_output(&self, name: &str) {
        self.state.virtual_outputs.remove_output(&self.state, name);
    }

    fn handle_get_connector_active_workspace(&self, connector: Connector) -> Result<(), CphError> {
        let output = self.get_output_node(connector)?;
        let workspace = output
            .workspace
            .get()
            .map_or(Workspace(0), |ws| self.get_workspace_by_name(&ws.name));
        self.respond(Response::GetConnectorActiveWorkspace { workspace });
        Ok(())
    }

    fn handle_get_connector_workspaces(&self, connector: Connector) -> Result<(), CphError> {
        let output = self.get_output_node(connector)?;
        let workspaces = output
            .workspaces
            .iter()
            .map(|ws| self.get_workspace_by_name(&ws.name))
            .collect::<Vec<_>>();
        self.respond(Response::GetConnectorWorkspaces { workspaces });
        Ok(())
    }

    fn handle_get_workspace_connector(&self, workspace: Workspace) -> Result<(), CphError> {
        let connector = self
            .get_existing_workspace(workspace)?
            .map(|ws| ws.output.get())
            .filter(|o| !o.is_dummy)
            .map(|o| Connector(o.global.connector.id.raw() as _))
            .unwrap_or(Connector(0));
        self.respond(Response::GetWorkspaceConnector { connector });
        Ok(())
    }

    fn handle_get_connector_in_direction(
        &self,
        connector: Connector,
        direction: Direction,
    ) -> Result<(), CphError> {
        let source_output = self.get_output_node(connector)?;
        let connector = self
            .state
            .find_output_in_direction(&source_output, direction.into())
            .map(|o| Connector(o.global.connector.id.raw() as u64))
            .unwrap_or(Connector(0));
        self.respond(Response::GetConnectorInDirection { connector });
        Ok(())
    }

    fn handle_has_capability(&self, device: InputDevice, cap: Capability) -> Result<(), CphError> {
        let dev = self.get_device_handler_data(device)?;
        let mut is_unknown = false;
        let has_cap = 'has_cap: {
            let cap = match cap {
                CAP_KEYBOARD => InputDeviceCapability::Keyboard,
                CAP_POINTER => InputDeviceCapability::Pointer,
                CAP_TOUCH => InputDeviceCapability::Touch,
                CAP_TABLET_TOOL => InputDeviceCapability::TabletTool,
                CAP_TABLET_PAD => InputDeviceCapability::TabletPad,
                CAP_GESTURE => InputDeviceCapability::Gesture,
                CAP_SWITCH => InputDeviceCapability::Switch,
                _ => {
                    is_unknown = true;
                    break 'has_cap false;
                }
            };
            dev.device.has_capability(cap)
        };
        self.respond(Response::HasCapability { has: has_cap });
        if is_unknown {
            Err(CphError::UnknownCapability(cap))
        } else {
            Ok(())
        }
    }

    fn handle_get_seat_mono(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        self.respond(Response::GetMono {
            mono: seat.get_mono().unwrap_or(false),
        });
        Ok(())
    }

    fn handle_set_seat_mono(&self, seat: Seat, mono: bool) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.set_mono(mono);
        Ok(())
    }

    fn handle_get_window_mono(&self, window: Window) -> Result<(), CphError> {
        let window = self.get_window(window)?;
        self.respond(Response::GetWindowMono {
            mono: toplevel_parent_container(&*window)
                .map(|c| c.mono_child.is_some())
                .unwrap_or(false),
        });
        Ok(())
    }

    fn handle_set_window_mono(&self, window: Window, mono: bool) -> Result<(), CphError> {
        let window = self.get_window(window)?;
        if let Some(c) = toplevel_parent_container(&*window) {
            c.set_mono(mono.then_some(window.as_ref()));
        }
        Ok(())
    }

    fn handle_get_seat_split(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        self.respond(Response::GetSplit {
            axis: seat
                .get_split()
                .unwrap_or(ContainerSplit::Horizontal)
                .into(),
        });
        Ok(())
    }

    fn handle_set_seat_split(&self, seat: Seat, axis: Axis) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.set_split(axis.into());
        Ok(())
    }

    fn handle_seat_toggle_tab(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.toggle_tab();
        Ok(())
    }

    fn handle_seat_make_group(
        &self,
        seat: Seat,
        axis: Axis,
        ephemeral: bool,
    ) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.make_group(axis.into(), ephemeral);
        Ok(())
    }

    fn handle_seat_change_group_opposite(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.change_group_opposite();
        Ok(())
    }

    fn handle_seat_equalize(&self, seat: Seat, recursive: bool) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.equalize(recursive);
        Ok(())
    }

    fn handle_seat_move_tab(&self, seat: Seat, right: bool) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.move_tab(right);
        Ok(())
    }

    fn handle_get_window_split(&self, window: Window) -> Result<(), CphError> {
        let window = self.get_window(window)?;
        self.respond(Response::GetWindowSplit {
            axis: toplevel_parent_container(&*window)
                .map(|c| c.split.get())
                .unwrap_or(ContainerSplit::Horizontal)
                .into(),
        });
        Ok(())
    }

    fn handle_set_window_split(&self, window: Window, axis: Axis) -> Result<(), CphError> {
        let window = self.get_window(window)?;
        if let Some(c) = toplevel_parent_container(&*window) {
            c.set_split(axis.into());
        }
        Ok(())
    }

    fn handle_add_shortcut(
        &self,
        seat: Seat,
        mod_mask: Modifiers,
        mods: Modifiers,
        sym: KeySym,
    ) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.add_shortcut(mod_mask, mods, sym);
        Ok(())
    }

    fn handle_remove_shortcut(
        &self,
        seat: Seat,
        mods: Modifiers,
        sym: KeySym,
    ) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.remove_shortcut(mods, sym);
        Ok(())
    }

    fn handle_get_input_devices(&self, seat: Option<Seat>) {
        let id = seat.map(|s| SeatId::from_raw(s.0 as _));
        let matches = |dhd: &DeviceHandlerData| {
            let id = match id {
                Some(id) => id,
                _ => return true,
            };
            if let Some(seat) = dhd.seat.get() {
                return seat.id() == id;
            }
            false
        };
        let mut res = vec![];
        {
            let devs = self.state.input_device_handlers.borrow_mut();
            for dev in devs.values() {
                if matches(&dev.data) {
                    res.push(InputDevice(dev.id.raw() as _));
                }
            }
        }
        self.respond(Response::GetInputDevices { devices: res });
    }

    fn handle_get_seats(&self) {
        let seats = {
            let seats = self.state.globals.seats.lock();
            seats
                .values()
                .map(|seat| Seat::from_raw(seat.id().raw() as _))
                .collect()
        };
        self.respond(Response::GetSeats { seats });
    }

    fn handle_run(
        &self,
        prog: &str,
        args: Vec<String>,
        mut env: Vec<(String, String)>,
        fds: Vec<(i32, i32)>,
        tag: Option<&str>,
    ) -> Result<(), CphError> {
        if let Some(tag) = tag {
            let display = self
                .state
                .tagged_acceptors
                .get(&self.state, tag)
                .map_err(CphError::CreateTaggedAcceptor)?;
            env.push((WAYLAND_DISPLAY.to_string(), display.to_string()));
        }
        let fds: Vec<_> = fds
            .into_iter()
            .map(|(a, b)| (a, Rc::new(OwnedFd::new(b))))
            .collect();
        let forker = match self.state.forker.get() {
            Some(f) => f,
            _ => return Err(CphError::NoForker),
        };
        let env = env.into_iter().map(|(k, v)| (k, Some(v))).collect();
        forker.spawn(prog.to_string(), args, env, fds);
        Ok(())
    }

    fn handle_set_log_level(&self, level: ConfigLogLevel) {
        self.state.set_log_level(level.into());
    }

    fn handle_clean_logs_older_than(&self, time: SystemTime) {
        self.state.clean_logs_older_than.set(Some(time));
    }

    fn handle_grab(&self, kb: InputDevice, grab: bool) -> Result<(), CphError> {
        let kb = self.get_kb(kb)?;
        kb.grab(grab);
        Ok(())
    }

    fn handle_create_seat_split(&self, seat: Seat, axis: Axis) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.create_split(axis.into());
        Ok(())
    }

    fn handle_create_window_split(&self, window: Window, axis: Axis) -> Result<(), CphError> {
        let window = self.get_window(window)?;
        toplevel_create_split(&self.state, window, axis.into());
        Ok(())
    }

    fn handle_focus_seat_parent(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.focus_parent();
        Ok(())
    }

    fn handle_quit(&self) {
        log::info!("Quitting");
        self.state.ring.stop();
    }

    fn handle_switch_to(&self, vtnr: u32) {
        self.state.backend.get().switch_to(vtnr);
    }

    fn handle_get_seat_floating(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        self.respond(Response::GetFloating {
            floating: seat.get_floating().unwrap_or(false),
        });
        Ok(())
    }

    fn handle_set_seat_floating(&self, seat: Seat, floating: bool) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.set_floating(floating);
        Ok(())
    }

    fn handle_get_window_floating(&self, window: Window) -> Result<(), CphError> {
        let window = self.get_window(window)?;
        self.respond(Response::GetWindowFloating {
            floating: window.tl_data().parent_is_float.get(),
        });
        Ok(())
    }

    fn handle_set_window_floating(&self, window: Window, floating: bool) -> Result<(), CphError> {
        let window = self.get_window(window)?;
        toplevel_set_floating(&self.state, window, floating);
        Ok(())
    }

    fn handle_add_pollable(self: &Rc<Self>, fd: i32) -> Result<(), CphError> {
        let fd = match fcntl_dupfd_cloexec(fd, 0).to_os_error() {
            Ok(fd) => Rc::new(fd),
            Err(e) => {
                let err = format!("Could not invoke F_DUPFD_CLOEXEC: {}", ErrorFmt(e));
                log::error!("{}", err);
                self.respond(Response::AddPollable { id: Err(err) });
                return Ok(());
            }
        };
        let id = self.pollable_id.fetch_add(1);
        let id = PollableId(id);
        let create = |writable: bool, events: c::c_short| {
            let event = Rc::new(AsyncEvent::default());
            let slf = self.clone();
            let trigger = event.clone();
            let fd = fd.clone();
            let future = self.state.eng.spawn("config fd poller", async move {
                loop {
                    trigger.triggered().await;
                    let res = slf.state.ring.poll(&fd, events).await.merge();
                    if let Err(e) = &res {
                        log::warn!("Could not poll fd: {}", ErrorFmt(e));
                    }
                    let res = res.map_err(|e| ErrorFmt(e).to_string()).map(drop);
                    slf.send(&ServerMessage::InterestReady { id, writable, res });
                }
            });
            (event, future)
        };
        let (read_trigger, _read_future) = create(false, c::POLLIN);
        let (write_trigger, _write_future) = create(true, c::POLLOUT);
        self.pollables.set(
            id,
            Rc::new(Pollable {
                write_trigger,
                _write_future,
                read_trigger,
                _read_future,
            }),
        );
        self.respond(Response::AddPollable { id: Ok(id) });
        Ok(())
    }

    fn handle_remove_pollable(self: &Rc<Self>, id: PollableId) {
        self.pollables.remove(&id);
    }

    fn handle_add_interest(
        self: &Rc<Self>,
        id: PollableId,
        writable: bool,
    ) -> Result<(), CphError> {
        let Some(pollable) = self.pollables.get(&id) else {
            return Err(CphError::PollableDoesNotExist);
        };
        let trigger = match writable {
            true => &pollable.write_trigger,
            false => &pollable.read_trigger,
        };
        trigger.trigger();
        Ok(())
    }

    fn tl_to_window(&self, tl: &dyn ToplevelNode) -> Window {
        self.tl_id_to_window(tl.tl_data().identifier.get())
    }

    fn tl_id_to_window(&self, tl: ToplevelIdentifier) -> Window {
        if let Some(win) = self.windows_from_tl_id.get(&tl) {
            return win;
        }
        let id = Window(self.window_ids.fetch_add(1));
        self.windows_from_tl_id.set(tl, id);
        self.windows_to_tl_id.set(id, tl);
        id
    }

    fn get_window(&self, window: Window) -> Result<Rc<dyn ToplevelNode>, CphError> {
        self.windows_to_tl_id
            .get(&window)
            .and_then(|id| self.state.toplevels.get(&id))
            .and_then(|tl| tl.upgrade())
            .ok_or(CphError::WindowDoesNotExist(window))
    }

    fn get_client_matcher(
        &self,
        matcher: ClientMatcher,
    ) -> Result<Rc<CachedCriterion<ClientCriterionIpc, Rc<Client>>>, CphError> {
        self.client_matchers
            .get(&matcher)
            .ok_or(CphError::ClientMatcherDoesNotExist(matcher))
    }

    fn sort_generic_matcher<T, K>(
        &self,
        generic: &mut GenericCriterionIpc<T>,
        key: impl FnMut(&T) -> K,
    ) where
        K: Ord,
    {
        match generic {
            GenericCriterionIpc::List { list, .. } | GenericCriterionIpc::Exactly { list, .. } => {
                list.sort_by_key(key)
            }
            GenericCriterionIpc::Matcher(_) | GenericCriterionIpc::Not(_) => {}
        }
    }

    fn create_generic_matcher<Crit, Matcher, Mgr>(
        &self,
        mgr: &Mgr,
        generic: &GenericCriterionIpc<Matcher>,
        upstream: &mut Vec<Rc<CachedCriterion<Crit, Mgr::Target>>>,
        get_matcher: impl Fn(&Matcher) -> Result<Rc<CachedCriterion<Crit, Mgr::Target>>, CphError>,
    ) -> Result<Rc<dyn CritUpstreamNode<Mgr::Target>>, CphError>
    where
        Crit: Clone + Hash + Eq,
        Mgr: CritMgrExt,
    {
        let mut get_upstream = |m: &Matcher| -> Result<_, CphError> {
            let m = get_matcher(m)?;
            let node = m.node.clone();
            upstream.push(m);
            Ok(node)
        };
        let node = match generic {
            GenericCriterionIpc::Matcher(m) => get_matcher(m)?.node.clone(),
            GenericCriterionIpc::Not(m) => mgr.not(&get_upstream(m)?),
            GenericCriterionIpc::List { list, all } => {
                let mut m = Vec::with_capacity(list.len());
                for c in list {
                    m.push(get_upstream(c)?);
                }
                mgr.list(&m, *all)
            }
            GenericCriterionIpc::Exactly { list, num } => {
                let mut m = Vec::with_capacity(list.len());
                for c in list {
                    m.push(get_upstream(c)?);
                }
                mgr.exactly(&m, *num)
            }
        };
        Ok(node)
    }

    fn handle_create_client_matcher(
        &self,
        mut criterion: ClientCriterionIpc,
    ) -> Result<(), CphError> {
        if let ClientCriterionIpc::Generic(generic) = &mut criterion {
            self.sort_generic_matcher(generic, |m| m.0);
        }
        let id = ClientMatcher(self.client_matcher_ids.fetch_add(1));
        let cache = &self.client_matcher_cache;
        if let Some(matcher) = cache.get(&criterion)
            && let Some(matcher) = matcher.upgrade()
        {
            self.client_matchers.set(id, matcher);
            self.respond(Response::CreateClientMatcher { matcher: id });
            return Ok(());
        }
        let mgr = &self.state.cl_matcher_manager;
        let mut upstream = vec![];
        let matcher = match &criterion {
            ClientCriterionIpc::Generic(m) => {
                self.create_generic_matcher(mgr, m, &mut upstream, |m| self.get_client_matcher(*m))?
            }
            ClientCriterionIpc::String {
                string,
                field,
                regex,
            } => {
                let needle = match *regex {
                    true => {
                        let regex = Regex::new(string).map_err(CphError::InvalidRegex)?;
                        CritLiteralOrRegex::Regex(regex)
                    }
                    false => CritLiteralOrRegex::Literal(string.to_string()),
                };
                match *field {
                    ClientCriterionStringField::SandboxEngine => mgr.sandbox_engine(needle),
                    ClientCriterionStringField::SandboxAppId => mgr.sandbox_app_id(needle),
                    ClientCriterionStringField::SandboxInstanceId => {
                        mgr.sandbox_instance_id(needle)
                    }
                    ClientCriterionStringField::Comm => mgr.comm(needle),
                    ClientCriterionStringField::Exe => mgr.exe(needle),
                    ClientCriterionStringField::Tag => mgr.tag(needle),
                }
            }
            ClientCriterionIpc::Sandboxed => mgr.sandboxed(),
            ClientCriterionIpc::Uid(p) => mgr.uid(*p),
            ClientCriterionIpc::Pid(p) => mgr.pid(*p),
            ClientCriterionIpc::IsXwayland => mgr.is_xwayland(),
        };
        let cached = Rc::new(CachedCriterion {
            crit: criterion.clone(),
            cache: cache.clone(),
            upstream,
            node: matcher.clone(),
        });
        cache.set(criterion, Rc::downgrade(&cached));
        self.client_matchers.set(id, cached);
        self.respond(Response::CreateClientMatcher { matcher: id });
        Ok(())
    }

    fn handle_destroy_client_matcher(&self, matcher: ClientMatcher) {
        self.client_matchers.remove(&matcher);
        self.client_matcher_leafs.remove(&matcher);
        self.client_matcher_capabilities.remove(&matcher);
        self.client_matcher_bounding_capabilities.remove(&matcher);
    }

    fn handle_enable_client_matcher_events(
        self: &Rc<Self>,
        matcher: ClientMatcher,
    ) -> Result<(), CphError> {
        if self.client_matcher_leafs.contains(&matcher) {
            return Ok(());
        }
        let upstream = self.get_client_matcher(matcher)?;
        let slf = self.clone();
        let leaf = self
            .state
            .cl_matcher_manager
            .leaf(&upstream.node, move |id| {
                let client = ConfigClient(id.raw());
                slf.send(&ServerMessage::ClientMatcherMatched { matcher, client });
                let slf = slf.clone();
                Box::new(move || {
                    slf.send(&ServerMessage::ClientMatcherUnmatched { matcher, client });
                })
            });
        self.client_matcher_leafs.set(matcher, leaf);
        self.state.cl_matcher_manager.rematch_all(&self.state);
        Ok(())
    }

    fn get_window_matcher(
        &self,
        matcher: WindowMatcher,
    ) -> Result<Rc<CachedCriterion<WindowCriterionIpc, ToplevelData>>, CphError> {
        self.window_matchers
            .get(&matcher)
            .ok_or(CphError::WindowMatcherDoesNotExist(matcher))
    }

    fn handle_create_window_matcher(
        &self,
        mut criterion: WindowCriterionIpc,
    ) -> Result<(), CphError> {
        if let WindowCriterionIpc::Generic(generic) = &mut criterion {
            self.sort_generic_matcher(generic, |m| m.0);
        }
        let id = WindowMatcher(self.window_matcher_ids.fetch_add(1));
        let cache = &self.window_matcher_cache;
        if let Some(matcher) = cache.get(&criterion)
            && let Some(matcher) = matcher.upgrade()
        {
            self.window_matchers.set(id, matcher);
            self.respond(Response::CreateWindowMatcher { matcher: id });
            return Ok(());
        }
        let mgr = &self.state.tl_matcher_manager;
        let mut upstream = vec![];
        let matcher = match &criterion {
            WindowCriterionIpc::Generic(m) => {
                self.create_generic_matcher(mgr, m, &mut upstream, |m| self.get_window_matcher(*m))?
            }
            WindowCriterionIpc::String {
                string,
                field,
                regex,
            } => {
                let needle = match *regex {
                    true => {
                        let regex = Regex::new(string).map_err(CphError::InvalidRegex)?;
                        CritLiteralOrRegex::Regex(regex)
                    }
                    false => CritLiteralOrRegex::Literal(string.to_string()),
                };
                match *field {
                    WindowCriterionStringField::Title => mgr.title(needle),
                    WindowCriterionStringField::AppId => mgr.app_id(needle),
                    WindowCriterionStringField::Tag => mgr.tag(needle),
                    WindowCriterionStringField::XClass => mgr.class(needle),
                    WindowCriterionStringField::XInstance => mgr.instance(needle),
                    WindowCriterionStringField::XRole => mgr.role(needle),
                    WindowCriterionStringField::Workspace => mgr.workspace(needle),
                }
            }
            WindowCriterionIpc::Types(t) => mgr.kind(*t),
            WindowCriterionIpc::Client(c) => {
                self.state.cl_matcher_manager.rematch_all(&self.state);
                mgr.client(&self.state, &self.get_client_matcher(*c)?.node)
            }
            WindowCriterionIpc::Floating => mgr.floating(),
            WindowCriterionIpc::Visible => mgr.visible(),
            WindowCriterionIpc::Urgent => mgr.urgent(),
            WindowCriterionIpc::SeatFocus(seat) => mgr.seat_focus(&*self.get_seat(*seat)?),
            WindowCriterionIpc::Fullscreen => mgr.fullscreen(),
            WindowCriterionIpc::JustMapped => mgr.just_mapped(),
            WindowCriterionIpc::Workspace(w) => mgr.workspace(CritLiteralOrRegex::Literal(
                self.get_workspace(*w)?.to_string(),
            )),
            WindowCriterionIpc::ContentTypes(t) => mgr.content_type(*t),
        };
        let cached = Rc::new(CachedCriterion {
            crit: criterion.clone(),
            cache: cache.clone(),
            upstream,
            node: matcher.clone(),
        });
        cache.set(criterion, Rc::downgrade(&cached));
        self.window_matchers.set(id, cached);
        self.respond(Response::CreateWindowMatcher { matcher: id });
        Ok(())
    }

    fn handle_destroy_window_matcher(&self, matcher: WindowMatcher) {
        self.window_matchers.remove(&matcher);
        self.window_matcher_leafs.remove(&matcher);
        self.window_matcher_no_auto_focus.remove(&matcher);
        self.window_matcher_initial_tile_state.remove(&matcher);
    }

    fn handle_enable_window_matcher_events(
        self: &Rc<Self>,
        matcher: WindowMatcher,
    ) -> Result<(), CphError> {
        if self.window_matcher_leafs.contains(&matcher) {
            return Ok(());
        }
        let upstream = self.get_window_matcher(matcher)?;
        let mut node = upstream.node.clone();
        if !upstream.any(&|crit| matches!(crit, WindowCriterionIpc::Types(_))) {
            let list = [self.window_matcher_std_kinds.clone(), node];
            node = self.state.tl_matcher_manager.list(&list, true);
        }
        let slf = self.clone();
        let leaf = self.state.tl_matcher_manager.leaf(&node, move |tl| {
            let window = slf.tl_id_to_window(tl);
            slf.send(&ServerMessage::WindowMatcherMatched { matcher, window });
            let slf = slf.clone();
            Box::new(move || {
                slf.send(&ServerMessage::WindowMatcherUnmatched { matcher, window });
            })
        });
        self.window_matcher_leafs.set(matcher, leaf);
        self.state.tl_matcher_manager.rematch_all(&self.state);
        Ok(())
    }

    fn handle_set_window_matcher_auto_focus(
        &self,
        matcher: WindowMatcher,
        auto_focus: bool,
    ) -> Result<(), CphError> {
        if auto_focus {
            self.window_matcher_no_auto_focus.remove(&matcher);
        } else {
            let m = self.get_window_matcher(matcher)?;
            self.window_matcher_no_auto_focus.set(matcher, m);
        }
        Ok(())
    }

    fn handle_set_window_matcher_initial_tile_state(
        &self,
        matcher: WindowMatcher,
        tile_state: ConfigTileState,
    ) -> Result<(), CphError> {
        let Ok(tile_state) = tile_state.try_into() else {
            return Err(CphError::UnknownTileState(tile_state));
        };
        let m = self.get_window_matcher(matcher)?;
        self.window_matcher_initial_tile_state
            .set(matcher, (m, tile_state));
        Ok(())
    }

    fn handle_set_pointer_revert_key(&self, seat: Seat, key: KeySym) -> Result<(), CphError> {
        self.get_seat(seat)?.set_pointer_revert_key(key);
        Ok(())
    }

    fn handle_seat_focus_history(&self, seat: Seat, timeline: Timeline) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        match timeline {
            Timeline::Older => seat.focus_prev(),
            Timeline::Newer => seat.focus_next(),
        }
        Ok(())
    }

    fn handle_seat_focus_history_set_only_visible(
        &self,
        seat: Seat,
        visible: bool,
    ) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.focus_history_set_visible(visible);
        Ok(())
    }

    fn handle_seat_focus_history_set_same_workspace(
        &self,
        seat: Seat,
        same_workspace: bool,
    ) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.focus_history_set_same_workspace(same_workspace);
        Ok(())
    }

    fn handle_seat_focus_layer_rel(
        &self,
        seat: Seat,
        direction: LayerDirection,
    ) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        match direction {
            LayerDirection::Below => seat.focus_layer_below(),
            LayerDirection::Above => seat.focus_layer_above(),
        }
        Ok(())
    }

    fn handle_seat_focus_tiles(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.focus_tiles();
        Ok(())
    }

    fn handle_seat_focus_floats(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.focus_floats();
        Ok(())
    }

    fn handle_seat_toggle_focus_float_tiled(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.toggle_focus_float_tiled();
        Ok(())
    }

    fn handle_set_middle_click_paste_enabled(&self, enabled: bool) {
        self.state.set_primary_selection_enabled(enabled);
    }

    fn handle_seat_create_mark(&self, seat: Seat, kc: Option<u32>) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        if let Some(kc) = kc {
            seat.create_mark(Keycode::from_evdev(kc));
        } else {
            seat.create_mark_interactive();
        }
        Ok(())
    }

    fn handle_seat_jump_to_mark(&self, seat: Seat, kc: Option<u32>) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        if let Some(kc) = kc {
            seat.jump_to_mark(Keycode::from_evdev(kc));
        } else {
            seat.jump_to_mark_interactive();
        }
        Ok(())
    }

    fn handle_seat_copy_mark(&self, seat: Seat, src: u32, dst: u32) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.copy_mark(Keycode::from_evdev(src), Keycode::from_evdev(dst));
        Ok(())
    }

    fn handle_seat_set_simple_im_enabled(&self, seat: Seat, enabled: bool) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.set_simple_im_enabled(enabled);
        Ok(())
    }

    fn handle_seat_get_simple_im_enabled(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        self.respond(Response::SeatGetSimpleImEnabled {
            enabled: seat.simple_im_enabled(),
        });
        Ok(())
    }

    fn handle_seat_reload_simple_im(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.reload_simple_im();
        Ok(())
    }

    fn handle_seat_enable_unicode_input(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.enable_unicode_input();
        Ok(())
    }

    fn handle_seat_warp_mouse_to_focus(&self, seat: Seat) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.schedule_warp_mouse_to_focus();
        Ok(())
    }

    fn handle_seat_set_mouse_follows_focus(
        &self,
        seat: Seat,
        enabled: bool,
    ) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        seat.set_mouse_follows_focus(enabled);
        Ok(())
    }

    fn get_sized(&self, sized: Resizable) -> Result<ThemeSized, CphError> {
        use jay_config::theme::sized::*;
        let sized = match sized {
            TITLE_HEIGHT => ThemeSized::title_height,
            BORDER_WIDTH => ThemeSized::border_width,
            BAR_HEIGHT => ThemeSized::bar_height,
            BAR_SEPARATOR_WIDTH => ThemeSized::bar_separator_width,
            GAP => ThemeSized::gap,
            TITLE_GAP => ThemeSized::title_gap,
            TAB_BAR_HEIGHT => ThemeSized::tab_bar_height,
            TAB_BAR_PADDING => ThemeSized::tab_bar_padding,
            TAB_BAR_RADIUS => ThemeSized::tab_bar_radius,
            TAB_BAR_BORDER_WIDTH => ThemeSized::tab_bar_border_width,
            TAB_BAR_TEXT_PADDING => ThemeSized::tab_bar_text_padding,
            TAB_BAR_GAP => ThemeSized::tab_bar_gap,
            _ => return Err(CphError::UnknownSized(sized.0)),
        };
        Ok(sized)
    }

    fn handle_get_size(&self, sized: Resizable) -> Result<(), CphError> {
        let sized = self.get_sized(sized)?;
        let size = sized.field(&self.state.theme).val.get();
        self.respond(Response::GetSize { size });
        Ok(())
    }

    fn handle_set_size(&self, sized: Resizable, size: i32) -> Result<(), CphError> {
        let sized = self.get_sized(sized)?;
        if size < sized.min() {
            return Err(CphError::InvalidSize(size, sized));
        }
        if size > sized.max() {
            return Err(CphError::InvalidSize(size, sized));
        }
        self.state.set_size(sized, size);
        Ok(())
    }

    fn handle_reset_colors(&self) {
        self.state.reset_colors();
    }

    fn handle_reset_sizes(&self) {
        self.state.reset_sizes();
    }

    fn handle_reset_font(&self) {
        self.state.reset_fonts();
    }

    fn handle_set_font(&self, font: &str) {
        self.state.set_font(font);
    }

    fn handle_set_bar_font(&self, font: &str) {
        self.state.set_bar_font(Some(font));
    }

    fn handle_set_title_font(&self, _font: &str) {
        // no-op: titles have been removed
    }

    fn handle_get_font(&self) {
        let font = self.state.theme.font.get().to_string();
        self.respond(Response::GetFont { font });
    }

    fn get_color(&self, colorable: Colorable) -> Result<ThemeColor, CphError> {
        use jay_config::theme::colors::*;
        let colorable = match colorable {
            UNFOCUSED_TITLE_BACKGROUND_COLOR => ThemeColor::unfocused_title_background,
            FOCUSED_TITLE_BACKGROUND_COLOR => ThemeColor::focused_title_background,
            CAPTURED_UNFOCUSED_TITLE_BACKGROUND_COLOR => {
                ThemeColor::captured_unfocused_title_background
            }
            CAPTURED_FOCUSED_TITLE_BACKGROUND_COLOR => {
                ThemeColor::captured_focused_title_background
            }
            FOCUSED_INACTIVE_TITLE_BACKGROUND_COLOR => {
                ThemeColor::focused_inactive_title_background
            }
            BACKGROUND_COLOR => ThemeColor::background,
            BAR_BACKGROUND_COLOR => ThemeColor::bar_background,
            SEPARATOR_COLOR => ThemeColor::separator,
            BORDER_COLOR => ThemeColor::border,
            ACTIVE_BORDER_COLOR => ThemeColor::active_border,
            UNFOCUSED_TITLE_TEXT_COLOR => ThemeColor::unfocused_title_text,
            FOCUSED_TITLE_TEXT_COLOR => ThemeColor::focused_title_text,
            FOCUSED_INACTIVE_TITLE_TEXT_COLOR => ThemeColor::focused_inactive_title_text,
            BAR_STATUS_TEXT_COLOR => ThemeColor::bar_text,
            ATTENTION_REQUESTED_BACKGROUND_COLOR => ThemeColor::attention_requested_background,
            HIGHLIGHT_COLOR => ThemeColor::highlight,
            TAB_ACTIVE_BACKGROUND_COLOR => ThemeColor::tab_active_background,
            TAB_ACTIVE_BORDER_COLOR => ThemeColor::tab_active_border,
            TAB_INACTIVE_BACKGROUND_COLOR => ThemeColor::tab_inactive_background,
            TAB_INACTIVE_BORDER_COLOR => ThemeColor::tab_inactive_border,
            TAB_ACTIVE_TEXT_COLOR => ThemeColor::tab_active_text,
            TAB_INACTIVE_TEXT_COLOR => ThemeColor::tab_inactive_text,
            TAB_BAR_BACKGROUND_COLOR => ThemeColor::tab_bar_background,
            TAB_ATTENTION_BACKGROUND_COLOR => ThemeColor::tab_attention_background,
            _ => return Err(CphError::UnknownColor(colorable.0)),
        };
        Ok(colorable)
    }

    fn handle_get_color(&self, colorable: Colorable) -> Result<(), CphError> {
        let color = self.get_color(colorable)?.field(&self.state.theme).get();
        let [r, g, b, a] = color.to_array(Eotf::Gamma22);
        let color = jay_config::theme::Color::new_f32_premultiplied(r, g, b, a);
        self.respond(Response::GetColor { color });
        Ok(())
    }

    fn handle_set_color(
        &self,
        colorable: Colorable,
        color: jay_config::theme::Color,
    ) -> Result<(), CphError> {
        self.state
            .set_color(self.get_color(colorable)?, color.into());
        Ok(())
    }

    fn handle_destroy_keymap(&self, keymap: Keymap) {
        self.keymaps.remove(&keymap);
    }

    fn get_client(&self, client: ConfigClient) -> Result<Rc<Client>, CphError> {
        self.state
            .clients
            .get(ClientId::from_raw(client.0))
            .ok()
            .ok_or(CphError::ClientDoesNotExist(client))
    }

    fn handle_get_clients(&self) {
        let mut clients = vec![];
        for client in self.state.clients.clients.borrow().values() {
            clients.push(ConfigClient(client.data.id.raw()));
        }
        self.respond(Response::GetClients { clients });
    }

    fn handle_client_exists(&self, client: ConfigClient) {
        self.respond(Response::ClientExists {
            exists: self.get_client(client).is_ok(),
        });
    }

    fn handle_client_is_xwayland(&self, client: ConfigClient) -> Result<(), CphError> {
        self.respond(Response::ClientIsXwayland {
            is_xwayland: self.get_client(client)?.is_xwayland,
        });
        Ok(())
    }

    fn handle_client_kill(&self, client: ConfigClient) {
        self.state.clients.kill(ClientId::from_raw(client.0));
    }

    fn handle_get_workspace_window(&self, ws: Workspace) -> Result<(), CphError> {
        let window = self
            .get_existing_workspace(ws)?
            .and_then(|ws| ws.container.get())
            .map(|c| self.tl_to_window(&*c))
            .unwrap_or(Window(0));
        self.respond(Response::GetWorkspaceWindow { window });
        Ok(())
    }

    fn handle_get_seat_keyboard_window(&self, seat: Seat) -> Result<(), CphError> {
        let window = self
            .get_seat(seat)?
            .get_keyboard_node()
            .node_toplevel()
            .map(|tl| self.tl_to_window(&*tl))
            .unwrap_or(Window(0));
        self.respond(Response::GetSeatKeyboardWindow { window });
        Ok(())
    }

    fn handle_seat_focus_window(&self, seat: Seat, window_id: Window) -> Result<(), CphError> {
        let seat = self.get_seat(seat)?;
        let window = self.get_window(window_id)?;
        if !window.node_visible() {
            return Err(CphError::WindowNotVisible(window_id));
        }
        seat.focus_toplevel(window);
        seat.maybe_schedule_warp_mouse_to_focus();
        Ok(())
    }

    fn handle_get_window_title(&self, window: Window) -> Result<(), CphError> {
        let title = self.get_window(window)?.tl_data().title.borrow().clone();
        self.respond(Response::GetWindowTitle { title });
        Ok(())
    }

    fn handle_get_window_type(&self, window: Window) -> Result<(), CphError> {
        let kind = self.get_window(window)?.tl_data().kind.to_window_type();
        self.respond(Response::GetWindowType { kind });
        Ok(())
    }

    fn handle_get_content_type(&self, window: Window) -> Result<(), CphError> {
        let kind = self
            .get_window(window)?
            .tl_data()
            .content_type
            .get()
            .to_config();
        self.respond(Response::GetContentType { kind });
        Ok(())
    }

    fn handle_window_resize(
        &self,
        window: Window,
        dx1: i32,
        dy1: i32,
        dx2: i32,
        dy2: i32,
    ) -> Result<(), CphError> {
        self.get_window(window)?.tl_resize(dx1, dy1, dx2, dy2);
        Ok(())
    }

    fn handle_window_exists(&self, window: Window) {
        self.respond(Response::WindowExists {
            exists: self.get_window(window).is_ok(),
        });
    }

    fn handle_get_window_id(&self, window: Window) -> Result<(), CphError> {
        let id = self
            .get_window(window)?
            .tl_data()
            .identifier
            .get()
            .to_string();
        self.respond(Response::GetWindowId { id: id.to_string() });
        Ok(())
    }

    fn handle_get_window_is_visible(&self, window: Window) -> Result<(), CphError> {
        let window = self.get_window(window)?;
        self.respond(Response::GetWindowIsVisible {
            visible: window.node_visible(),
        });
        Ok(())
    }

    fn handle_get_window_client(&self, window: Window) -> Result<(), CphError> {
        let window = self.get_window(window)?;
        self.respond(Response::GetWindowClient {
            client: window
                .tl_data()
                .client
                .as_ref()
                .map(|c| ConfigClient(c.id.raw()))
                .unwrap_or(ConfigClient(0)),
        });
        Ok(())
    }

    fn handle_get_window_parent(&self, window: Window) -> Result<(), CphError> {
        let window = self
            .get_window(window)?
            .tl_data()
            .parent
            .get()
            .and_then(|tl| tl.node_into_toplevel())
            .map(|tl| self.tl_to_window(&*tl))
            .unwrap_or(Window(0));
        self.respond(Response::GetWindowParent { window });
        Ok(())
    }

    fn handle_get_window_workspace(&self, window: Window) -> Result<(), CphError> {
        let workspace = self
            .get_window(window)?
            .tl_data()
            .workspace
            .get()
            .map(|ws| self.get_workspace_by_name(&ws.name))
            .unwrap_or(Workspace(0));
        self.respond(Response::GetWindowWorkspace { workspace });
        Ok(())
    }

    fn handle_get_window_children(&self, window: Window) -> Result<(), CphError> {
        let mut windows = vec![];
        if let Some(c) = self.get_window(window)?.node_into_container() {
            for c in c.children.iter() {
                windows.push(self.tl_to_window(&*c.node));
            }
        }
        self.respond(Response::GetWindowChildren { windows });
        Ok(())
    }

    fn handle_set_client_matcher_capabilities(
        &self,
        matcher: ClientMatcher,
        caps: ClientCapabilities,
    ) -> Result<(), CphError> {
        let m = self.get_client_matcher(matcher)?;
        self.client_matcher_capabilities
            .set(matcher, (m, caps.to_client_caps()));
        Ok(())
    }

    fn handle_set_client_matcher_bounding_capabilities(
        &self,
        matcher: ClientMatcher,
        caps: ClientCapabilities,
    ) -> Result<(), CphError> {
        let m = self.get_client_matcher(matcher)?;
        self.client_matcher_bounding_capabilities
            .set(matcher, (m, caps.to_client_caps()));
        Ok(())
    }

    pub fn handle_request(self: &Rc<Self>, msg: &[u8]) {
        if let Err(e) = self.handle_request_(msg) {
            log::error!("Could not handle client request: {}", ErrorFmt(e));
        }
    }

    fn handle_request_(self: &Rc<Self>, msg: &[u8]) -> Result<(), CphError> {
        let request = match bincode_ops().deserialize::<ClientMessage>(msg) {
            Ok(msg) => msg,
            Err(e) => return Err(CphError::ParsingFailed(e)),
        };
        match request {
            ClientMessage::Log {
                level,
                msg,
                file,
                line,
            } => self.handle_log_request(level, msg, file, line),
            ClientMessage::GetSeat { name } => self.handle_get_seat(name),
            ClientMessage::ParseKeymap { keymap } => {
                self.handle_parse_keymap(keymap).wrn("parse_keymap")?
            }
            ClientMessage::SeatSetKeymap { seat, keymap } => {
                self.handle_set_keymap(seat, keymap).wrn("set_keymap")?
            }
            ClientMessage::SeatGetRepeatRate { seat } => {
                self.handle_get_repeat_rate(seat).wrn("get_repeat_rate")?
            }
            ClientMessage::SeatSetRepeatRate { seat, rate, delay } => self
                .handle_set_repeat_rate(seat, rate, delay)
                .wrn("set_repeat_rate")?,
            ClientMessage::SetSeat { device, seat } => {
                self.handle_set_seat(device, seat).wrn("set_seat")?
            }
            ClientMessage::GetSeatMono { seat } => {
                self.handle_get_seat_mono(seat).wrn("get_seat_mono")?
            }
            ClientMessage::SetSeatMono { seat, mono } => {
                self.handle_set_seat_mono(seat, mono).wrn("set_seat_mono")?
            }
            ClientMessage::GetSeatSplit { seat } => {
                self.handle_get_seat_split(seat).wrn("get_seat_split")?
            }
            ClientMessage::SetSeatSplit { seat, axis } => self
                .handle_set_seat_split(seat, axis)
                .wrn("set_seat_split")?,
            ClientMessage::AddShortcut { seat, mods, sym } => self
                .handle_add_shortcut(seat, Modifiers(!0), mods, sym)
                .wrn("add_shortcut")?,
            ClientMessage::RemoveShortcut { seat, mods, sym } => self
                .handle_remove_shortcut(seat, mods, sym)
                .wrn("remove_shortcut")?,
            ClientMessage::SeatFocus { seat, direction } => {
                self.handle_seat_focus(seat, direction).wrn("seat_focus")?
            }
            ClientMessage::SeatMove { seat, direction } => {
                self.handle_seat_move(seat, direction).wrn("seat_move")?
            }
            ClientMessage::GetInputDevices { seat } => self.handle_get_input_devices(seat),
            ClientMessage::GetSeats => self.handle_get_seats(),
            ClientMessage::RemoveSeat { .. } => {}
            ClientMessage::Run { prog, args, env } => {
                self.handle_run(prog, args, env, vec![], None).wrn("run")?
            }
            ClientMessage::GrabKb { kb, grab } => self.handle_grab(kb, grab).wrn("grab")?,
            ClientMessage::SetColor { colorable, color } => {
                self.handle_set_color(colorable, color).wrn("set_color")?
            }
            ClientMessage::GetColor { colorable } => {
                self.handle_get_color(colorable).wrn("get_color")?
            }
            ClientMessage::CreateSeatSplit { seat, axis } => self
                .handle_create_seat_split(seat, axis)
                .wrn("create_seat_split")?,
            ClientMessage::FocusSeatParent { seat } => self
                .handle_focus_seat_parent(seat)
                .wrn("focus_seat_parent")?,
            ClientMessage::GetSeatFloating { seat } => self
                .handle_get_seat_floating(seat)
                .wrn("get_seat_floating")?,
            ClientMessage::SetSeatFloating { seat, floating } => self
                .handle_set_seat_floating(seat, floating)
                .wrn("set_seat_floating")?,
            ClientMessage::Quit => self.handle_quit(),
            ClientMessage::SwitchTo { vtnr } => self.handle_switch_to(vtnr),
            ClientMessage::HasCapability { device, cap } => self
                .handle_has_capability(device, cap)
                .wrn("has_capability")?,
            ClientMessage::SetLeftHanded {
                device,
                left_handed,
            } => self
                .handle_set_left_handed(device, left_handed)
                .wrn("set_left_handed")?,
            ClientMessage::SetAccelProfile { device, profile } => self
                .handle_set_accel_profile(device, profile)
                .wrn("set_accel_profile")?,
            ClientMessage::SetAccelSpeed { device, speed } => self
                .handle_set_accel_speed(device, speed)
                .wrn("set_accel_speed")?,
            ClientMessage::SetTransformMatrix { device, matrix } => self
                .handle_set_transform_matrix(device, matrix)
                .wrn("set_transform_matrix")?,
            ClientMessage::GetDeviceName { device } => {
                self.handle_get_device_name(device).wrn("get_device_name")?
            }
            ClientMessage::GetWorkspace { name } => self.handle_get_workspace(name),
            ClientMessage::ShowWorkspace { seat, workspace } => self
                .handle_show_workspace(seat, workspace, None)
                .wrn("show_workspace")?,
            ClientMessage::SetSeatWorkspace { seat, workspace } => self
                .handle_set_seat_workspace(seat, workspace)
                .wrn("set_seat_workspace")?,
            ClientMessage::GetConnector { ty, idx } => {
                self.handle_get_connector(ty, idx).wrn("get_connector")?
            }
            ClientMessage::ConnectorConnected { connector } => self
                .handle_connector_connected(connector)
                .wrn("connector_connected")?,
            ClientMessage::ConnectorType { connector } => self
                .handle_connector_type(connector)
                .wrn("connector_type")?,
            ClientMessage::ConnectorMode { connector } => self
                .handle_connector_mode(connector)
                .wrn("connector_mode")?,
            ClientMessage::ConnectorSetPosition { connector, x, y } => self
                .handle_connector_set_position(connector, x, y)
                .wrn("connector_set_position")?,
            ClientMessage::ConnectorSetEnabled { connector, enabled } => self
                .handle_connector_set_enabled(connector, enabled)
                .wrn("connector_set_enabled")?,
            ClientMessage::SeatClose { seat } => self.handle_seat_close(seat).wrn("seat_close")?,
            ClientMessage::SetStatus { status } => self.handle_set_status(status),
            ClientMessage::GetTimer { name } => self.handle_get_timer(name).wrn("get_timer")?,
            ClientMessage::RemoveTimer { timer } => {
                self.handle_remove_timer(timer).wrn("remove_timer")?
            }
            ClientMessage::ProgramTimer {
                timer,
                initial,
                periodic,
            } => self
                .handle_program_timer(timer, initial, periodic)
                .wrn("program_timer")?,
            ClientMessage::SetEnv { key, val } => self.handle_set_env(key, val),
            ClientMessage::SetSeatFullscreen { seat, fullscreen } => self
                .handle_set_seat_fullscreen(seat, fullscreen)
                .wrn("set_seat_fullscreen")?,
            ClientMessage::GetSeatFullscreen { seat } => self
                .handle_get_seat_fullscreen(seat)
                .wrn("get_seat_fullscreen")?,
            ClientMessage::Reload => self.handle_reload(),
            ClientMessage::GetDeviceConnectors { device } => self
                .handle_get_connectors(Some(device), false)
                .wrn("get_device_connectors")?,
            ClientMessage::GetDrmDeviceSyspath { device } => self
                .handle_get_drm_device_syspath(device)
                .wrn("get_drm_device_syspath")?,
            ClientMessage::GetDrmDeviceVendor { device } => self
                .handle_get_drm_device_vendor(device)
                .wrn("get_drm_device_vendor")?,
            ClientMessage::GetDrmDeviceModel { device } => self
                .handle_get_drm_device_model(device)
                .wrn("get_drm_device_model")?,
            ClientMessage::GetDrmDevices => self.handle_get_drm_devices(),
            ClientMessage::GetDrmDevicePciId { device } => self
                .handle_get_drm_device_pci_id(device)
                .wrn("get_drm_device_pci_id")?,
            ClientMessage::ResetColors => self.handle_reset_colors(),
            ClientMessage::ResetSizes => self.handle_reset_sizes(),
            ClientMessage::GetSize { sized } => self.handle_get_size(sized).wrn("get_size")?,
            ClientMessage::SetSize { sized, size } => {
                self.handle_set_size(sized, size).wrn("set_size")?
            }
            ClientMessage::ResetFont => self.handle_reset_font(),
            ClientMessage::GetFont => self.handle_get_font(),
            ClientMessage::SetFont { font } => self.handle_set_font(font),
            ClientMessage::SetPxPerWheelScroll { device, px } => self
                .handle_set_px_per_wheel_scroll(device, px)
                .wrn("set_px_per_wheel_scroll")?,
            ClientMessage::ConnectorSetScale { connector, scale } => self
                .handle_connector_set_scale(connector, scale)
                .wrn("connector_set_scale")?,
            ClientMessage::ConnectorGetScale { connector } => self
                .handle_connector_get_scale(connector)
                .wrn("connector_get_scale")?,
            ClientMessage::ConnectorSize { connector } => self
                .handle_connector_size(connector)
                .wrn("connector_size")?,
            ClientMessage::SetCursorSize { seat, size } => self
                .handle_set_cursor_size(seat, size)
                .wrn("set_cursor_size")?,
            ClientMessage::SetTapEnabled { device, enabled } => self
                .handle_set_tap_enabled(device, enabled)
                .wrn("set_tap_enabled")?,
            ClientMessage::SetDragEnabled { device, enabled } => self
                .handle_set_drag_enabled(device, enabled)
                .wrn("set_drag_enabled")?,
            ClientMessage::SetDragLockEnabled { device, enabled } => self
                .handle_set_drag_lock_enabled(device, enabled)
                .wrn("set_drag_lock_enabled")?,
            ClientMessage::SetUseHardwareCursor {
                seat,
                use_hardware_cursor,
            } => self
                .handle_set_use_hardware_cursor(seat, use_hardware_cursor)
                .wrn("set_use_hardware_cursor")?,
            ClientMessage::DisablePointerConstraint { seat } => self
                .handle_disable_pointer_constraint(seat)
                .wrn("disable_pointer_constraint")?,
            ClientMessage::MakeRenderDevice { device } => self
                .handle_make_render_device(device)
                .wrn("make_render_device")?,
            ClientMessage::GetSeatCursorWorkspace { seat } => self
                .handle_get_seat_cursor_workspace(seat)
                .wrn("get_seat_cursor_workspace")?,
            ClientMessage::GetSeatKeyboardWorkspace { seat } => self
                .handle_get_seat_keyboard_workspace(seat)
                .wrn("get_seat_keyboard_workspace")?,
            ClientMessage::SetDefaultWorkspaceCapture { capture } => {
                self.handle_set_default_workspace_capture(capture)
            }
            ClientMessage::GetDefaultWorkspaceCapture => {
                self.handle_get_default_workspace_capture()
            }
            ClientMessage::SetWorkspaceCapture { workspace, capture } => self
                .handle_set_workspace_capture(workspace, capture)
                .wrn("set_workspace_capture")?,
            ClientMessage::GetWorkspaceCapture { workspace } => self
                .handle_get_workspace_capture(workspace)
                .wrn("get_workspace_capture")?,
            ClientMessage::SetNaturalScrollingEnabled { device, enabled } => self
                .handle_set_natural_scrolling_enabled(device, enabled)
                .wrn("set_natural_scrolling_enabled")?,
            ClientMessage::SetGfxApi { device, api } => {
                self.handle_set_gfx_api(device, api).wrn("set_gfx_api")?
            }
            ClientMessage::SetDirectScanoutEnabled { device, enabled } => self
                .handle_set_direct_scanout_enabled(device, enabled)
                .wrn("set_direct_scanout_enabled")?,
            ClientMessage::ConnectorSetTransform {
                connector,
                transform,
            } => self
                .handle_connector_set_transform(connector, transform)
                .wrn("connector_set_transform")?,
            ClientMessage::SetDoubleClickIntervalUsec { usec } => {
                self.handle_set_double_click_interval_usec(usec)
            }
            ClientMessage::SetDoubleClickDistance { dist } => {
                self.handle_set_double_click_distance(dist)
            }
            ClientMessage::ConnectorModes { connector } => self
                .handle_connector_modes(connector)
                .wrn("connector_modes")?,
            ClientMessage::ConnectorSetMode { connector, mode } => self
                .handle_connector_set_mode(connector, mode)
                .wrn("connector_set_mode")?,
            ClientMessage::AddPollable { fd } => {
                self.handle_add_pollable(fd).wrn("add_pollable")?
            }
            ClientMessage::RemovePollable { id } => self.handle_remove_pollable(id),
            ClientMessage::AddInterest { pollable, writable } => self
                .handle_add_interest(pollable, writable)
                .wrn("add_interest")?,
            ClientMessage::Run2 {
                prog,
                args,
                env,
                fds,
            } => self.handle_run(prog, args, env, fds, None).wrn("run")?,
            ClientMessage::DisableDefaultSeat => self.state.create_default_seat.set(false),
            ClientMessage::DestroyKeymap { keymap } => self.handle_destroy_keymap(keymap),
            ClientMessage::GetConnectorName { connector } => self
                .handle_connector_name(connector)
                .wrn("connector_name")?,
            ClientMessage::GetConnectorModel { connector } => self
                .handle_connector_model(connector)
                .wrn("connector_model")?,
            ClientMessage::GetConnectorManufacturer { connector } => self
                .handle_connector_manufacturer(connector)
                .wrn("connector_manufacturer")?,
            ClientMessage::GetConnectorSerialNumber { connector } => self
                .handle_connector_serial_number(connector)
                .wrn("connector_serial_number")?,
            ClientMessage::GetConnectors {
                device,
                connected_only,
            } => self
                .handle_get_connectors(device, connected_only)
                .wrn("get_connectors")?,
            ClientMessage::ConnectorGetPosition { connector } => self
                .handle_connector_get_position(connector)
                .wrn("connector_get_position")?,
            ClientMessage::GetConfigDir => self.handle_get_config_dir(),
            ClientMessage::GetWorkspaces => self.handle_get_workspaces(),
            ClientMessage::UnsetEnv { key } => self.handle_unset_env(key),
            ClientMessage::SetLogLevel { level } => self.handle_set_log_level(level),
            ClientMessage::GetDrmDeviceDevnode { device } => self
                .handle_get_drm_device_devnode(device)
                .wrn("get_drm_device_devnode")?,
            ClientMessage::GetInputDeviceSyspath { device } => self
                .handle_get_input_device_syspath(device)
                .wrn("get_input_device_syspath")?,
            ClientMessage::GetInputDeviceDevnode { device } => self
                .handle_get_input_device_devnode(device)
                .wrn("get_input_device_devnode")?,
            ClientMessage::SetIdle { timeout } => self.handle_set_idle(timeout),
            ClientMessage::MoveToOutput {
                workspace,
                connector,
            } => self
                .handle_move_to_output(workspace, connector)
                .wrn("move_to_output")?,
            ClientMessage::SetExplicitSyncEnabled { enabled } => {
                self.handle_set_explicit_sync_enabled(enabled)
            }
            ClientMessage::GetSocketPath => self.handle_get_socket_path(),
            ClientMessage::DeviceSetKeymap { device, keymap } => self
                .handle_set_device_keymap(device, keymap)
                .wrn("set_device_keymap")?,
            ClientMessage::SetForward { seat, forward } => {
                self.handle_set_forward(seat, forward).wrn("set_forward")?
            }
            ClientMessage::AddShortcut2 {
                seat,
                mod_mask,
                mods,
                sym,
            } => self
                .handle_add_shortcut(seat, mod_mask, mods, sym)
                .wrn("add_shortcut")?,
            ClientMessage::SetFocusFollowsMouseMode { seat, mode } => self
                .handle_set_focus_follows_mouse_mode(seat, mode)
                .wrn("set_focus_follows_mouse_mode")?,
            ClientMessage::SetInputDeviceConnector {
                input_device,
                connector,
            } => self
                .handle_set_input_device_connector(input_device, connector)
                .wrn("set_input_device_connector")?,
            ClientMessage::RemoveInputMapping { input_device } => self
                .handle_remove_input_mapping(input_device)
                .wrn("remove_input_mapping")?,
            ClientMessage::SetWindowManagementEnabled { seat, enabled } => self
                .handle_set_window_management_enabled(seat, enabled)
                .wrn("set_window_management_enabled")?,
            ClientMessage::SetVrrMode { connector, mode } => self
                .handle_set_vrr_mode(connector, mode)
                .wrn("set_vrr_mode")?,
            ClientMessage::SetVrrCursorHz { connector, hz } => self
                .handle_set_vrr_cursor_hz(connector, hz)
                .wrn("set_vrr_cursor_hz")?,
            ClientMessage::SetTearingMode { connector, mode } => self
                .handle_set_tearing_mode(connector, mode)
                .wrn("set_tearing_mode")?,
            ClientMessage::SetCalibrationMatrix { device, matrix } => self
                .handle_set_calibration_matrix(device, matrix)
                .wrn("set_calibration_matrix")?,
            ClientMessage::SetEiSocketEnabled { enabled } => {
                self.handle_set_ei_socket_enabled(enabled)
            }
            ClientMessage::ConnectorSetFormat { connector, format } => self
                .handle_connector_set_format(connector, format)
                .wrn("connector_set_format")?,
            ClientMessage::SetFlipMargin { device, margin } => self
                .handle_set_flip_margin(device, margin)
                .wrn("set_flip_margin")?,
            ClientMessage::SetUiDragEnabled { enabled } => self.handle_set_ui_drag_enabled(enabled),
            ClientMessage::SetUiDragThreshold { threshold } => {
                self.handle_set_ui_drag_threshold(threshold)
            }
            ClientMessage::SetXScalingMode { mode } => self
                .handle_set_x_scaling_mode(mode)
                .wrn("set_x_scaling_mode")?,
            ClientMessage::SetIdleGracePeriod { period } => {
                self.handle_set_idle_grace_period(period)
            }
            ClientMessage::SetColorManagementEnabled { enabled } => {
                self.handle_set_color_management_enabled(enabled)
            }
            ClientMessage::ConnectorSetColors {
                connector,
                color_space,
                eotf,
            } => self
                .handle_connector_set_colors(connector, color_space, eotf)
                .wrn("connector_set_colors")?,
            ClientMessage::ConnectorSetBrightness {
                connector,
                brightness,
            } => self
                .handle_connector_set_brightness(connector, brightness)
                .wrn("connector_set_brightness")?,
            ClientMessage::SetFloatAboveFullscreen { above } => {
                self.handle_set_float_above_fullscreen(above)
            }
            ClientMessage::GetFloatAboveFullscreen => self.handle_get_float_above_fullscreen(),
            ClientMessage::GetSeatFloatPinned { seat } => self
                .handle_get_seat_float_pinned(seat)
                .wrn("get_seat_float_pinned")?,
            ClientMessage::SetSeatFloatPinned { seat, pinned } => self
                .handle_set_seat_float_pinned(seat, pinned)
                .wrn("set_seat_float_pinned")?,
            ClientMessage::SetShowFloatPinIcon { show } => {
                self.handle_set_show_float_pin_icon(show)
            }
            ClientMessage::GetConnectorActiveWorkspace { connector } => self
                .handle_get_connector_active_workspace(connector)
                .wrn("get_connector_active_workspace")?,
            ClientMessage::GetConnectorWorkspaces { connector } => self
                .handle_get_connector_workspaces(connector)
                .wrn("get_connector_workspaces")?,
            ClientMessage::GetWorkspaceConnector { workspace } => self
                .handle_get_workspace_connector(workspace)
                .wrn("get_workspace_connector")?,
            ClientMessage::GetConnectorInDirection {
                connector,
                direction,
            } => self
                .handle_get_connector_in_direction(connector, direction)
                .wrn("get_connector_in_direction")?,
            ClientMessage::GetClients => self.handle_get_clients(),
            ClientMessage::ClientExists { client } => self.handle_client_exists(client),
            ClientMessage::ClientIsXwayland { client } => self
                .handle_client_is_xwayland(client)
                .wrn("client_is_xwayland")?,
            ClientMessage::ClientKill { client } => self.handle_client_kill(client),
            ClientMessage::WindowExists { window } => self.handle_window_exists(window),
            ClientMessage::GetWorkspaceWindow { workspace } => self
                .handle_get_workspace_window(workspace)
                .wrn("get_workspace_window")?,
            ClientMessage::GetSeatKeyboardWindow { seat } => self
                .handle_get_seat_keyboard_window(seat)
                .wrn("get_seat_keyboard_window")?,
            ClientMessage::SeatFocusWindow { seat, window } => self
                .handle_seat_focus_window(seat, window)
                .wrn("seat_focus_window")?,
            ClientMessage::GetWindowTitle { window } => self
                .handle_get_window_title(window)
                .wrn("get_window_title")?,
            ClientMessage::GetWindowType { window } => {
                self.handle_get_window_type(window).wrn("get_window_type")?
            }
            ClientMessage::GetWindowId { window } => {
                self.handle_get_window_id(window).wrn("get_window_id")?
            }
            ClientMessage::GetWindowParent { window } => self
                .handle_get_window_parent(window)
                .wrn("get_window_parent")?,
            ClientMessage::GetWindowWorkspace { window } => self
                .handle_get_window_workspace(window)
                .wrn("get_window_workspace")?,
            ClientMessage::GetWindowChildren { window } => self
                .handle_get_window_children(window)
                .wrn("get_window_children")?,
            ClientMessage::GetWindowSplit { window } => self
                .handle_get_window_split(window)
                .wrn("get_window_split")?,
            ClientMessage::SetWindowSplit { window, axis } => self
                .handle_set_window_split(window, axis)
                .wrn("set_window_split")?,
            ClientMessage::GetWindowMono { window } => {
                self.handle_get_window_mono(window).wrn("get_window_mono")?
            }
            ClientMessage::SetWindowMono { window, mono } => self
                .handle_set_window_mono(window, mono)
                .wrn("set_window_mono")?,
            ClientMessage::WindowMove { window, direction } => self
                .handle_window_move(window, direction)
                .wrn("window_move")?,
            ClientMessage::CreateWindowSplit { window, axis } => self
                .handle_create_window_split(window, axis)
                .wrn("create_window_split")?,
            ClientMessage::WindowClose { window } => {
                self.handle_window_close(window).wrn("close_window")?
            }
            ClientMessage::GetWindowFloating { window } => self
                .handle_get_window_floating(window)
                .wrn("get_window_floating")?,
            ClientMessage::SetWindowFloating { window, floating } => self
                .handle_set_window_floating(window, floating)
                .wrn("set_window_floating")?,
            ClientMessage::SetWindowWorkspace { window, workspace } => self
                .handle_set_window_workspace(window, workspace)
                .wrn("set_window_workspace")?,
            ClientMessage::SetWindowFullscreen { window, fullscreen } => self
                .handle_set_window_fullscreen(window, fullscreen)
                .wrn("set_window_fullscreen")?,
            ClientMessage::GetWindowFullscreen { window } => self
                .handle_get_window_fullscreen(window)
                .wrn("get_window_fullscreen")?,
            ClientMessage::GetWindowFloatPinned { window } => self
                .handle_get_window_float_pinned(window)
                .wrn("get_window_float_pinned")?,
            ClientMessage::SetWindowFloatPinned { window, pinned } => self
                .handle_set_window_float_pinned(window, pinned)
                .wrn("set_window_float_pinned")?,
            ClientMessage::GetWindowIsVisible { window } => self
                .handle_get_window_is_visible(window)
                .wrn("get_window_is_visible")?,
            ClientMessage::GetWindowClient { window } => self
                .handle_get_window_client(window)
                .wrn("get_window_client")?,
            ClientMessage::CreateClientMatcher { criterion } => self
                .handle_create_client_matcher(criterion)
                .wrn("create_window_matcher")?,
            ClientMessage::DestroyClientMatcher { matcher } => {
                self.handle_destroy_client_matcher(matcher)
            }
            ClientMessage::EnableClientMatcherEvents { matcher } => self
                .handle_enable_client_matcher_events(matcher)
                .wrn("enable_window_matcher_events")?,
            ClientMessage::CreateWindowMatcher { criterion } => self
                .handle_create_window_matcher(criterion)
                .wrn("create_window_matcher")?,
            ClientMessage::DestroyWindowMatcher { matcher } => {
                self.handle_destroy_window_matcher(matcher)
            }
            ClientMessage::EnableWindowMatcherEvents { matcher } => self
                .handle_enable_window_matcher_events(matcher)
                .wrn("enable_window_matcher_events")?,
            ClientMessage::SetWindowMatcherAutoFocus {
                matcher,
                auto_focus,
            } => self
                .handle_set_window_matcher_auto_focus(matcher, auto_focus)
                .wrn("set_window_matcher_auto_focus")?,
            ClientMessage::SetWindowMatcherInitialTileState {
                matcher,
                tile_state,
            } => self
                .handle_set_window_matcher_initial_tile_state(matcher, tile_state)
                .wrn("set_window_matcher_initial_tile_state")?,
            ClientMessage::SetPointerRevertKey { seat, key } => self
                .handle_set_pointer_revert_key(seat, key)
                .wrn("set_pointer_revert_key")?,
            ClientMessage::SetClickMethod { device, method } => self
                .handle_set_click_method(device, method)
                .wrn("set_click_method")?,
            ClientMessage::SetMiddleButtonEmulationEnabled { device, enabled } => self
                .handle_set_middle_button_emulation_enabled(device, enabled)
                .wrn("set_middle_button_emulation_enabled")?,
            ClientMessage::GetContentType { window } => self
                .handle_get_content_type(window)
                .wrn("get_content_type")?,
            ClientMessage::SetShowBar { show } => self.handle_set_show_bar(show),
            ClientMessage::GetShowBar => self.handle_get_show_bar(),
            ClientMessage::SetShowTitles { show } => self.handle_set_show_titles(show),
            ClientMessage::GetShowTitles => self.handle_get_show_titles(),
            ClientMessage::SetFloatingTitles { floating } => {
                self.handle_set_floating_titles(floating)
            }
            ClientMessage::GetFloatingTitles => self.handle_get_floating_titles(),
            ClientMessage::SetBarPosition { position } => self
                .handle_set_bar_position(position)
                .wrn("set_bar_position")?,
            ClientMessage::GetBarPosition => self.handle_get_bar_position(),
            ClientMessage::SetCornerRadius { radius } => self.handle_set_corner_radius(radius),
            ClientMessage::GetCornerRadius => self.handle_get_corner_radius(),
            ClientMessage::SeatFocusHistory { seat, timeline } => self
                .handle_seat_focus_history(seat, timeline)
                .wrn("seat_focus_history")?,
            ClientMessage::SeatFocusHistorySetOnlyVisible { seat, only_visible } => self
                .handle_seat_focus_history_set_only_visible(seat, only_visible)
                .wrn("seat_focus_history_set_only_visible")?,
            ClientMessage::SeatFocusHistorySetSameWorkspace {
                seat,
                same_workspace,
            } => self
                .handle_seat_focus_history_set_same_workspace(seat, same_workspace)
                .wrn("seat_focus_history_set_same_workspace")?,
            ClientMessage::SeatFocusLayerRel { seat, direction } => self
                .handle_seat_focus_layer_rel(seat, direction)
                .wrn("seat_focus_layer_rel")?,
            ClientMessage::SeatFocusTiles { seat } => {
                self.handle_seat_focus_tiles(seat).wrn("seat_focus_tiles")?
            }
            ClientMessage::SeatFocusFloats { seat } => self
                .handle_seat_focus_floats(seat)
                .wrn("seat_focus_floats")?,
            ClientMessage::SeatToggleFocusFloatTiled { seat } => self
                .handle_seat_toggle_focus_float_tiled(seat)
                .wrn("seat_toggle_focus_float_tiled")?,
            ClientMessage::SetMiddleClickPasteEnabled { enabled } => {
                self.handle_set_middle_click_paste_enabled(enabled)
            }
            ClientMessage::SetWorkspaceDisplayOrder { order } => {
                self.handle_set_workspace_display_order(order)
            }
            ClientMessage::SeatCreateMark { seat, kc } => self
                .handle_seat_create_mark(seat, kc)
                .wrn("seat_create_mark")?,
            ClientMessage::SeatJumpToMark { seat, kc } => self
                .handle_seat_jump_to_mark(seat, kc)
                .wrn("seat_jump_to_mark")?,
            ClientMessage::SeatCopyMark { seat, src, dst } => self
                .handle_seat_copy_mark(seat, src, dst)
                .wrn("seat_copy_mark")?,
            ClientMessage::ConnectorSetBlendSpace {
                connector,
                blend_space,
            } => self
                .handle_connector_set_blend_space(connector, blend_space)
                .wrn("connector_set_blend_space")?,
            ClientMessage::SetBarFont { font } => self.handle_set_bar_font(font),
            ClientMessage::SetTitleFont { font } => self.handle_set_title_font(font),
            ClientMessage::SetClientMatcherCapabilities { matcher, caps } => self
                .handle_set_client_matcher_capabilities(matcher, caps)
                .wrn("set_client_matcher_capabilities")?,
            ClientMessage::SetClientMatcherBoundingCapabilities { matcher, caps } => self
                .handle_set_client_matcher_bounding_capabilities(matcher, caps)
                .wrn("set_client_matcher_bounding_capabilities")?,
            ClientMessage::ShowWorkspaceOn {
                seat,
                workspace,
                connector,
            } => self
                .handle_show_workspace(seat, workspace, Some(connector))
                .wrn("show_workspace_on")?,
            ClientMessage::SeatSetSimpleImEnabled { seat, enabled } => self
                .handle_seat_set_simple_im_enabled(seat, enabled)
                .wrn("seat_set_simple_im_enabled")?,
            ClientMessage::SeatGetSimpleImEnabled { seat } => self
                .handle_seat_get_simple_im_enabled(seat)
                .wrn("seat_get_simple_im_enabled")?,
            ClientMessage::SeatReloadSimpleIm { seat } => self
                .handle_seat_reload_simple_im(seat)
                .wrn("seat_reload_simple_im")?,
            ClientMessage::SeatEnableUnicodeInput { seat } => self
                .handle_seat_enable_unicode_input(seat)
                .wrn("seat_enable_unicode_input")?,
            ClientMessage::SeatWarpMouseToFocus { seat } => self
                .handle_seat_warp_mouse_to_focus(seat)
                .wrn("seat_warp_mouse_to_focus")?,
            ClientMessage::SeatSetMouseFollowsFocus { seat, enabled } => self
                .handle_seat_set_mouse_follows_focus(seat, enabled)
                .wrn("seat_set_mouse_follows_focus")?,
            ClientMessage::ConnectorSetUseNativeGamut {
                connector,
                use_native_gamut,
            } => self
                .handle_connector_set_use_native_gamut(connector, use_native_gamut)
                .wrn("connector_set_use_native_gamut")?,
            ClientMessage::KeymapFromNames {
                rules,
                model,
                groups,
                options,
            } => self
                .handle_keymap_from_names(rules, model, groups, options)
                .wrn("keymap_from_names")?,
            ClientMessage::SetFallbackOutputMode { seat, mode } => self
                .handle_set_fallback_output_mode(seat, mode)
                .wrn("set_fallback_output_mode")?,
            ClientMessage::SetXWaylandEnabled { enabled } => self
                .handle_set_x_wayland_enabled(enabled)
                .wrn("set_x_wayland_enabled")?,
            ClientMessage::Run3 {
                prog,
                args,
                env,
                fds,
                tag,
            } => self.handle_run(prog, args, env, fds, tag).wrn("run")?,
            ClientMessage::ConnectorSupportsArbitraryModes { connector } => self
                .handle_connector_supports_arbitrary_modes(connector)
                .wrn("connector_supports_arbitrary_modes")?,
            ClientMessage::GetConnectorByName { name } => self.handle_get_connector_by_name(name),
            ClientMessage::CreateVirtualOutput { name } => self.handle_create_virtual_output(name),
            ClientMessage::RemoveVirtualOutput { name } => self.handle_remove_virtual_output(name),
            ClientMessage::CleanLogsOlderThan { time } => self.handle_clean_logs_older_than(time),
            ClientMessage::WindowResize {
                window,
                dx1,
                dy1,
                dx2,
                dy2,
            } => self
                .handle_window_resize(window, dx1, dy1, dx2, dy2)
                .wrn("window_resize")?,
            ClientMessage::SeatToggleTab { seat } => {
                self.handle_seat_toggle_tab(seat).wrn("seat_toggle_tab")?
            }
            ClientMessage::SeatMakeGroup {
                seat,
                axis,
                ephemeral,
            } => self
                .handle_seat_make_group(seat, axis, ephemeral)
                .wrn("seat_make_group")?,
            ClientMessage::SeatChangeGroupOpposite { seat } => self
                .handle_seat_change_group_opposite(seat)
                .wrn("seat_change_group_opposite")?,
            ClientMessage::SeatEqualize { seat, recursive } => self
                .handle_seat_equalize(seat, recursive)
                .wrn("seat_equalize")?,
            ClientMessage::SetAutotile { enabled } => {
                self.state.theme.autotile_enabled.set(enabled);
            }
            ClientMessage::SeatToggleExpand { .. } => {
                // Removed feature; kept for binary protocol compatibility.
            }
            ClientMessage::SetTabTitleAlign { align } => {
                use crate::theme::TabTitleAlign;
                let val = match align {
                    1 => TabTitleAlign::Center,
                    2 => TabTitleAlign::End,
                    _ => TabTitleAlign::Start,
                };
                self.state.theme.tab_title_align.set(val);
            }
            ClientMessage::SeatMoveTab { seat, right } => self
                .handle_seat_move_tab(seat, right)
                .wrn("seat_move_tab")?,
        }
        Ok(())
    }

    pub fn auto_focus(&self, data: &ToplevelData) -> bool {
        for matcher in self.window_matcher_no_auto_focus.lock().values() {
            if matcher.node.pull(data) {
                return false;
            }
        }
        true
    }

    pub fn initial_tile_state(&self, data: &ToplevelData) -> Option<TileState> {
        for (matcher, state) in self.window_matcher_initial_tile_state.lock().values() {
            if matcher.node.pull(data) {
                return Some(*state);
            }
        }
        None
    }

    pub fn update_capabilities(
        &self,
        data: &Rc<Client>,
        bounding_caps: ClientCaps,
        set_bounding_caps: bool,
    ) {
        let mut have_caps = false;
        let mut have_bounding_caps = false;
        let mut caps = ClientCaps::none();
        let mut new_bounding_caps = ClientCaps::none();
        for (matcher, state) in self.client_matcher_capabilities.lock().values() {
            if matcher.node.pull(data) {
                have_caps = true;
                caps |= *state;
            }
        }
        for (matcher, state) in self.client_matcher_bounding_capabilities.lock().values() {
            if matcher.node.pull(data) {
                have_bounding_caps = true;
                new_bounding_caps |= *state;
            }
        }
        if have_caps {
            caps &= bounding_caps;
            data.effective_caps.set(caps);
        }
        if !have_bounding_caps && set_bounding_caps {
            have_bounding_caps = true;
            new_bounding_caps = data.effective_caps.get();
        }
        if have_bounding_caps {
            new_bounding_caps &= bounding_caps;
            data.bounding_caps_for_children.set(new_bounding_caps);
        }
    }
}

#[derive(Debug, Error)]
enum CphError {
    #[error("Tried to set an unknown accel profile: {}", (.0).0)]
    UnknownAccelProfile(AccelProfile),
    #[error("Queried unknown capability: {}", (.0).0)]
    UnknownCapability(Capability),
    #[error("Tried to set an unknown click method: {}", (.0).0)]
    UnknownClickMethod(ClickMethod),
    #[error("The sized {} is outside the valid range [{}, {}] for component {}", .0, .1.min(), .1.max(), .1.name())]
    InvalidSize(i32, ThemeSized),
    #[error("The ol' forker is not available")]
    NoForker,
    #[error("Repeat rate is negative")]
    NegativeRepeatRate,
    #[error("Repeat delay is negative")]
    NegativeRepeatDelay,
    #[error("Parsing failed")]
    ParseKeymapError(#[from] KbvmError),
    #[error("Device {0:?} does not exist")]
    DeviceDoesNotExist(InputDevice),
    #[error("Connector {0:?} does not exist")]
    ConnectorDoesNotExist(Connector),
    #[error("Timer {0:?} does not exist")]
    TimerDoesNotExist(JayTimer),
    #[error("Connector {0:?} does not exist or is not connected")]
    OutputDoesNotExist(Connector),
    #[error("Output {0:?} is not a desktop output")]
    OutputIsNotDesktop(Connector),
    #[error("{0}x{1} is not a valid connector position")]
    InvalidConnectorPosition(i32, i32),
    #[error("Keymap {0:?} does not exist")]
    KeymapDoesNotExist(Keymap),
    #[error("Seat {0:?} does not exist")]
    SeatDoesNotExist(Seat),
    #[error("DRM device {0:?} does not exist")]
    DrmDeviceDoesNotExist(DrmDevice),
    #[error("Workspace {0:?} does not exist")]
    WorkspaceDoesNotExist(Workspace),
    #[error("Keyboard {0:?} does not exist")]
    KeyboardDoesNotExist(InputDevice),
    #[error("Colorable element {0} is not known")]
    UnknownColor(u32),
    #[error("Sized element {0} is not known")]
    UnknownSized(u32),
    #[error("Could not parse the message")]
    ParsingFailed(#[source] bincode::Error),
    #[error("Could not process a `{0}` request")]
    FailedRequest(&'static str, #[source] Box<Self>),
    #[error(transparent)]
    TimerError(#[from] TimerError),
    #[error("The requested monitor scale {0} is too small")]
    ScaleTooSmall(f64),
    #[error("The requested monitor scale {0} is too large")]
    ScaleTooLarge(f64),
    #[error("Tried to set a negative cursor size")]
    NegativeCursorSize,
    #[error("Config referred to a pollable that does not exist")]
    PollableDoesNotExist,
    #[error("Unknown VRR mode {0:?}")]
    UnknownVrrMode(ConfigVrrMode),
    #[error("Invalid cursor hz {0}")]
    InvalidCursorHz(f64),
    #[error("Unknown tearing mode {0:?}")]
    UnknownTearingMode(ConfigTearingMode),
    #[error("The format {0:?} is unknown")]
    UnknownFormat(ConfigFormat),
    #[error("Unknown x scaling mode {0:?}")]
    UnknownXScalingMode(XScalingMode),
    #[error("Unknown color space {0:?}")]
    UnknownColorSpace(ColorSpace),
    #[error("Unknown EOTF {0:?}")]
    UnknownEotf(ConfigEotf),
    #[error("Client {0:?} does not exist")]
    ClientDoesNotExist(ConfigClient),
    #[error("Window {0:?} does not exist")]
    WindowDoesNotExist(Window),
    #[error("Window {0:?} is not visible")]
    WindowNotVisible(Window),
    #[error("Client matcher {0:?} does not exist")]
    ClientMatcherDoesNotExist(ClientMatcher),
    #[error("Could not parse regex")]
    InvalidRegex(#[source] regex::Error),
    #[error("Window matcher {0:?} does not exist")]
    WindowMatcherDoesNotExist(WindowMatcher),
    #[error("Could not modify the connector state")]
    ModifyConnectorState(#[source] BackendConnectorTransactionError),
    #[error("Unknown blend space {0:?}")]
    UnknownBlendSpace(ConfigBlendSpace),
    #[error("Unknown bar position {0:?}")]
    UnknownBarPosition(BarPosition),
    #[error("Unknown gfx API {0:?}")]
    UnknownGfxApi(GfxApi),
    #[error("Unknown fallback output mode {0:?}")]
    UnknownFallbackOutputMode(FallbackOutputMode),
    #[error("Unknown tile state {0:?}")]
    UnknownTileState(ConfigTileState),
    #[error("Could not create a tagged acceptor")]
    CreateTaggedAcceptor(#[source] TaggedAcceptorError),
}

trait WithRequestName {
    fn wrn(self, request: &'static str) -> Result<(), CphError>;
}

impl WithRequestName for Result<(), CphError> {
    fn wrn(self, request: &'static str) -> Result<(), CphError> {
        self.map_err(move |e| CphError::FailedRequest(request, Box::new(e)))
    }
}

trait ClientCapabilitiesExt {
    fn to_client_caps(self) -> ClientCaps;
}

impl ClientCapabilitiesExt for ClientCapabilities {
    fn to_client_caps(self) -> ClientCaps {
        ClientCaps(self.0 as u32) & !CAP_JAY_COMPOSITOR & ClientCaps::all()
    }
}
