use {
    crate::{
        allocator::{Allocator, AllocatorError, BO_USE_RENDERING, BufferObject},
        async_engine::SpawnedFuture,
        client::{Client, ClientCaps, ClientError},
        cursor::KnownCursor,
        egui_adapter::egui_vulkan::{
            EGV_FORMAT, EgvContext, EgvError, EgvFramebuffer, EgvRenderer,
        },
        fixed::Fixed,
        fontconfig::match_font,
        gfx_api::SyncFile,
        globals::{GlobalName, Singleton},
        ifs::wl_seat::{
            BTN_EXTRA, BTN_LEFT, BTN_MIDDLE, BTN_RIGHT, BTN_SIDE,
            wl_pointer::{self, HORIZONTAL_SCROLL, PendingScroll, VERTICAL_SCROLL},
        },
        object::Version,
        scale::Scale,
        security_context_acceptor::AcceptorMetadata,
        state::State,
        utils::{
            asyncevent::AsyncEvent,
            buf::Buf,
            clonecell::CloneCell,
            copyhashmap::CopyHashMap,
            double_buffered::DoubleBuffered,
            errorfmt::ErrorFmt,
            object_drop_queue::ObjectDropQueue,
            oserror::OsError,
            pipe::{Pipe, pipe},
            rc_eq::rc_eq,
        },
        video::{dmabuf::DMA_BUF_SYNC_WRITE, drm::DrmError},
        wire::{
            WlSurfaceId,
            wl_pointer::{Button, Enter, Leave, Motion},
            wp_fractional_scale_v1::PreferredScale,
        },
        wl_usr::{
            UsrCon, UsrConOwner,
            usr_ifs::{
                usr_jay_compositor::UsrJayCompositor,
                usr_jay_sync_file_release::UsrJaySyncFileReleaseOwner,
                usr_jay_sync_file_surface::UsrJaySyncFileSurface,
                usr_wl_buffer::UsrWlBuffer,
                usr_wl_callback::UsrWlCallbackOwner,
                usr_wl_compositor::UsrWlCompositor,
                usr_wl_data_device::UsrWlDataDevice,
                usr_wl_data_device_manager::UsrWlDataDeviceManager,
                usr_wl_data_source::{UsrWlDataSource, UsrWlDataSourceOwner},
                usr_wl_keyboard::{UsrWlKeyboard, UsrWlKeyboardOwner},
                usr_wl_pointer::{UsrWlPointer, UsrWlPointerOwner},
                usr_wl_registry::UsrWlRegistry,
                usr_wl_seat::UsrWlSeat,
                usr_wl_surface::UsrWlSurface,
                usr_wp_cursor_shape_device_v1::UsrWpCursorShapeDeviceV1,
                usr_wp_cursor_shape_manager_v1::UsrWpCursorShapeManagerV1,
                usr_wp_fractional_scale::{UsrWpFractionalScale, UsrWpFractionalScaleOwner},
                usr_wp_fractional_scale_manager::UsrWpFractionalScaleManager,
                usr_wp_viewport::UsrWpViewport,
                usr_wp_viewporter::UsrWpViewporter,
                usr_xdg_surface::{UsrXdgSurface, UsrXdgSurfaceOwner},
                usr_xdg_toplevel::{UsrXdgToplevel, UsrXdgToplevelOwner},
                usr_xdg_wm_base::UsrXdgWmBase,
                usr_zwp_linux_dmabuf_v1::UsrZwpLinuxDmabufV1,
                usr_zwp_primary_selection_device_manager::UsrZwpPrimarySelectionDeviceManagerV1,
            },
        },
    },
    egui::{
        CursorIcon, Event, FontData, FontDefinitions, FontFamily, FullOutput, Key, Modifiers,
        MouseWheelUnit, OutputCommand, PlatformOutput, PointerButton, Pos2, RawInput, Vec2,
        ViewportCommand, ViewportEvent, ViewportId, ViewportInfo, pos2, vec2,
    },
    futures_util::{FutureExt, select},
    isnt::std_1::primitive::{IsntCharExt, IsntSliceExt, IsntStrExt},
    kbvm::{Keysym, ModifierMask, lookup::Lookup},
    std::{
        cell::{Cell, RefCell},
        collections::btree_map::Entry,
        fs, mem,
        rc::{Rc, Weak},
        sync::Arc,
    },
    thiserror::Error,
    uapi::{OwnedFd, c},
};

#[derive(Debug, Error)]
pub enum EggError {
    #[error("Could not create a socket pair")]
    CreateSocketPair(#[source] OsError),
    #[error("Could not spawn a client")]
    SpawnClient(#[source] ClientError),
    #[error("Could not create a renderer")]
    CreateRenderer(#[source] EgvError),
    #[error("There is no render context")]
    NoRenderContext,
    #[error("Could not allocate a buffer")]
    AllocateBuffer(#[source] AllocatorError),
    #[error("Could not import a framebuffer")]
    ImportFramebuffer(#[source] EgvError),
    #[error("Could not render")]
    Render(#[source] EgvError),
    #[error("No viewport output")]
    NoViewportOutput,
    #[error("Could not export initial dmabuf sync file")]
    ExportBoSyncFile(#[source] DrmError),
}

pub mod icons {
    pub const ICON_ADD: &str = "\u{e145}";
    pub const ICON_CLOSE: &str = "\u{e5cd}";
    pub const ICON_DRAG_INDICATOR: &str = "\u{e945}";
    pub const ICON_INFO: &str = "\u{e88e}";
    pub const ICON_OPEN_IN_NEW: &str = "\u{e89e}";
    pub const ICON_PENDING: &str = "\u{ef64}";
    pub const ICON_REMOVE: &str = "\u{e15b}";
}

linear_ids!(EggContextIds, EggContextId, u64);

pub struct EggState {
    fonts: RefCell<EggFonts>,
    ctx: CloneCell<Option<Rc<EggContext>>>,
    context_ids: EggContextIds,
    cxts: CopyHashMap<EggContextId, Rc<EggContextInner>>,
}

#[derive(Default)]
struct EggFonts {
    definitions: Option<FontDefinitions>,
    proportional: Vec<String>,
    monospace: Vec<String>,
}

pub struct EggContext {
    inner: Rc<EggContextInner>,
}

struct EggContextInner {
    id: EggContextId,
    renderer: Rc<EgvRenderer>,
    allocator: Rc<dyn Allocator>,
    state: Rc<State>,
    _client: Rc<Client>,
    con: Rc<UsrCon>,
    jay_compositor: Rc<UsrJayCompositor>,
    wl_compositor: Rc<UsrWlCompositor>,
    xdg_wm_base: Rc<UsrXdgWmBase>,
    wl_data_device_manager: Rc<UsrWlDataDeviceManager>,
    _zwp_primary_selection_device_manager_v1: Rc<UsrZwpPrimarySelectionDeviceManagerV1>,
    wp_viewporter: Rc<UsrWpViewporter>,
    wp_cursor_shape_manager_v1: Rc<UsrWpCursorShapeManagerV1>,
    wp_fractional_scale_manager: Rc<UsrWpFractionalScaleManager>,
    zwp_linux_dmabuf_v1: Rc<UsrZwpLinuxDmabufV1>,
    registry: Rc<UsrWlRegistry>,
    windows: CopyHashMap<WlSurfaceId, Rc<EggWindowInner>>,
    seats: CopyHashMap<GlobalName, EggSeat>,
}

struct EggSeat {
    inner: Rc<EggSeatInner>,
}

pub struct EggSeatInner {
    ctx: Rc<EggContextInner>,
    global_name: GlobalName,
    wl_seat: Rc<UsrWlSeat>,
    wl_pointer: Rc<UsrWlPointer>,
    wl_data_device: Rc<UsrWlDataDevice>,
    pointer_window: CloneCell<Option<Rc<EggWindowInner>>>,
    pointer_enter_serial: Cell<u32>,
    pointer_serial: Cell<u32>,
    pointer_pos: Cell<Pos2>,
    kb_modifiers: Cell<Modifiers>,
    wp_cursor_shape_device_v1: Rc<UsrWpCursorShapeDeviceV1>,
    wl_keyboard: Rc<UsrWlKeyboard>,
    kb_window: CloneCell<Option<Rc<EggWindowInner>>>,
    kb_serial: Cell<u32>,
    serial: Cell<u32>,
    wl_data_source: CloneCell<Option<Rc<UsrWlDataSource>>>,
    copy_text: RefCell<Option<Buf>>,
    copy_task: Cell<Option<SpawnedFuture<()>>>,
    paste_task: Cell<Option<SpawnedFuture<()>>>,
}

pub struct EggWindow {
    _ctx: Rc<EggContext>,
    inner: Rc<EggWindowInner>,
    _render_task: SpawnedFuture<()>,
    _timer_task: SpawnedFuture<()>,
}

pub trait EggWindowOwner {
    fn close(&self);
    fn render(self: Rc<Self>, ctx: &egui::Context);
}

struct EggWindowInner {
    ctx: Rc<EggContextInner>,
    egv: Rc<EgvContext>,
    egui: egui::Context,
    wl_surface: Rc<UsrWlSurface>,
    wp_viewport: Rc<UsrWpViewport>,
    wp_fractional_scale: Rc<UsrWpFractionalScale>,
    xdg_surface: Rc<UsrXdgSurface>,
    xdg_toplevel: Rc<UsrXdgToplevel>,
    jay_sync_file_surface: Rc<UsrJaySyncFileSurface>,
    frame_task: AsyncEvent,
    want_frame: Cell<bool>,
    have_frame: Cell<bool>,
    initial_commit_pending: Cell<bool>,
    owner: CloneCell<Option<Rc<dyn EggWindowOwner>>>,
    active_seat: CloneCell<Option<Rc<EggSeatInner>>>,
    raw_input: RefCell<Option<RawInput>>,
    close: Cell<bool>,
    repaint_timeout: Cell<u64>,
    repaint_timeout_changed: AsyncEvent,
    fonts_changed: Cell<bool>,

    buffers: DoubleBuffered<CloneCell<Option<Rc<EggFramebuffer>>>>,

    surface_pending: RefCell<PendingWindowState>,
    logical_size: Cell<[i32; 2]>,
    physical_size: Cell<[i32; 2]>,
    scale: Cell<Scale>,
}

struct EggFramebuffer {
    client_acquire_fence: CloneCell<Option<Option<SyncFile>>>,
    size: Cell<[i32; 2]>,
    bo: Rc<dyn BufferObject>,
    egv: Rc<EgvFramebuffer>,
    wl_buffer: Rc<UsrWlBuffer>,
    window: Weak<EggWindowInner>,
    drop_queue: Rc<ObjectDropQueue<Rc<dyn BufferObject>>>,
}

#[derive(Default)]
struct PendingWindowState {
    size: Option<(i32, i32)>,
}

const PROPORTIONAL_FONTS: &[&str] = &["sans-serif", "Noto Sans", "Noto Color Emoji"];

const MONOSPACE_FONTS: &[&str] = &["monospace", "Noto Sans Mono", "Noto Color Emoji"];

impl Default for EggState {
    fn default() -> Self {
        let slf = Self {
            fonts: Default::default(),
            ctx: Default::default(),
            context_ids: Default::default(),
            cxts: Default::default(),
        };
        slf.reset_fonts();
        slf
    }
}

impl EggState {
    pub fn reset_fonts(&self) {
        self.set_proportional_fonts(PROPORTIONAL_FONTS);
        self.set_monospace_fonts(MONOSPACE_FONTS);
    }

    pub fn set_proportional_fonts(&self, fonts: &[&str]) {
        self.change_fonts(fonts, |f| &mut f.proportional)
    }

    pub fn set_monospace_fonts(&self, fonts: &[&str]) {
        self.change_fonts(fonts, |f| &mut f.monospace)
    }

    fn change_fonts(&self, fonts: &[&str], field: impl Fn(&mut EggFonts) -> &mut Vec<String>) {
        let f = &mut *self.fonts.borrow_mut();
        let field = field(f);
        if *field == fonts {
            return;
        }
        *field = fonts.iter().map(|s| s.to_string()).collect();
        f.definitions.take();
        for ctx in self.cxts.lock().values() {
            for window in ctx.windows.lock().values() {
                window.fonts_changed.set(true);
                window.want_frame();
            }
        }
    }

    pub fn clear(&self) {
        self.ctx.take();
    }

    fn font_definitions(&self) -> FontDefinitions {
        let f = &mut self.fonts.borrow_mut();
        if let Some(d) = &f.definitions {
            return d.clone();
        }
        let mut d = FontDefinitions::empty();
        for (ff, list) in [
            (FontFamily::Proportional, &f.proportional),
            (FontFamily::Monospace, &f.monospace),
        ] {
            for family in list {
                let font = match match_font(family) {
                    Ok(f) => f,
                    Err(e) => {
                        log::warn!("Could not find font family {family}: {}", ErrorFmt(e));
                        continue;
                    }
                };
                if let Entry::Vacant(e) = d.font_data.entry(font.fullname.clone()) {
                    let data = match fs::read(&font.file) {
                        Ok(f) => f,
                        Err(e) => {
                            log::error!("Could not read {}: {}", font.file.display(), ErrorFmt(e));
                            continue;
                        }
                    };
                    let data = Arc::new(FontData {
                        font: data.into(),
                        index: font.index.unwrap_or(0) as u32,
                        tweak: Default::default(),
                    });
                    e.insert(data);
                }
                let list = d.families.entry(ff.clone()).or_default();
                if list.not_contains(&font.fullname) {
                    list.push(font.fullname);
                }
            }
        }
        {
            let name = "material-icons";
            let list = d.families.entry(FontFamily::Proportional).or_default();
            if list.iter().all(|n| n != name) {
                if let Entry::Vacant(e) = d.font_data.entry(name.to_string()) {
                    let data = Arc::new(FontData {
                        font: include_bytes!("icons.ttf").into(),
                        index: 0,
                        tweak: Default::default(),
                    });
                    e.insert(data);
                }
                list.push(name.to_string());
            }
        }
        f.definitions = Some(d.clone());
        d
    }
}

impl State {
    pub fn get_egg_context(self: &Rc<Self>) -> Result<Rc<EggContext>, EggError> {
        if let Some(ctx) = self.egg_state.ctx.get() {
            return Ok(ctx);
        }
        let Some(ctx) = self.render_ctx.get() else {
            return Err(EggError::NoRenderContext);
        };
        let (client1, client2) = uapi::socketpair(c::AF_UNIX, c::SOCK_STREAM | c::SOCK_CLOEXEC, 0)
            .map_err(Into::into)
            .map_err(EggError::CreateSocketPair)?;
        let allocator = ctx.allocator();
        let dev = allocator.drm().map(|d| d.dev());
        let renderer = EgvRenderer::new(&self.eng, &self.ring, &self.eventfd_cache, dev)
            .map_err(EggError::CreateRenderer)?;
        let con = UsrCon::from_socket(
            &self.ring,
            &self.wheel,
            &self.eng,
            &self.dma_buf_ids,
            &Rc::new(client1),
            0,
        );
        let client = self
            .clients
            .spawn2(
                self.clients.id(),
                self,
                Rc::new(client2),
                uapi::getuid(),
                uapi::getpid(),
                ClientCaps::all(),
                true,
                false,
                &Rc::new(AcceptorMetadata::secure()),
            )
            .map_err(EggError::SpawnClient)?;
        let registry = con.get_registry();
        let jay_compositor = {
            let obj = Rc::new(UsrJayCompositor {
                id: con.id(),
                con: con.clone(),
                owner: Default::default(),
                caps: Default::default(),
                version: Version(27),
            });
            registry.bind(self.globals.singletons[Singleton::JayCompositor], &*obj);
            con.add_object(obj.clone());
            obj
        };
        macro_rules! add_singletons {
            ($($name:ident, $global:ident, $ty:ident, $version:expr;)*) => {
                $(
                    let $name = Rc::new($ty {
                        id: con.id(),
                        con: con.clone(),
                        version: Version($version),
                    });
                    registry.bind(self.globals.singletons[Singleton::$global], &*$name);
                    con.add_object($name.clone());
                )*
            };
        }
        add_singletons! {
            wl_compositor, WlCompositor, UsrWlCompositor, 6;
            xdg_wm_base, XdgWmBase, UsrXdgWmBase, 7;
            wl_data_device_manager, WlDataDeviceManager, UsrWlDataDeviceManager, 3;
            zwp_primary_selection_device_manager_v1, ZwpPrimarySelectionDeviceManagerV1, UsrZwpPrimarySelectionDeviceManagerV1, 1;
            wp_viewporter, WpViewporter, UsrWpViewporter, 1;
            wp_cursor_shape_manager_v1, WpCursorShapeManagerV1, UsrWpCursorShapeManagerV1, 2;
            wp_fractional_scale_manager, WpFractionalScaleManagerV1, UsrWpFractionalScaleManager, 1;
            zwp_linux_dmabuf_v1, ZwpLinuxDmabufV1, UsrZwpLinuxDmabufV1, 5;
        }
        let ctx = Rc::new(EggContext {
            inner: Rc::new(EggContextInner {
                id: self.egg_state.context_ids.next(),
                renderer,
                allocator,
                state: self.clone(),
                _client: client.clone(),
                con,
                jay_compositor,
                wl_compositor,
                xdg_wm_base,
                wl_data_device_manager,
                _zwp_primary_selection_device_manager_v1: zwp_primary_selection_device_manager_v1,
                wp_viewporter,
                wp_cursor_shape_manager_v1,
                wp_fractional_scale_manager,
                zwp_linux_dmabuf_v1,
                registry,
                windows: Default::default(),
                seats: Default::default(),
            }),
        });
        ctx.inner.con.owner.set(Some(ctx.inner.clone()));
        self.egg_state.cxts.set(ctx.inner.id, ctx.inner.clone());
        for &global_name in self.globals.seats.lock().keys() {
            ctx.inner.add_seat(global_name);
        }
        self.egg_state.ctx.set(Some(ctx.clone()));
        Ok(ctx)
    }
}

impl EggContext {
    pub fn create_window(self: &Rc<Self>, title: &str) -> Rc<EggWindow> {
        let i = &self.inner;
        let wl_surface = i.wl_compositor.create_surface();
        let jay_sync_file_surface = i.jay_compositor.get_sync_file_surface(&wl_surface);
        let xdg_surface = i.xdg_wm_base.get_xdg_surface(&wl_surface);
        let xdg_toplevel = xdg_surface.get_toplevel();
        xdg_toplevel.set_title(title);
        let wp_fractional_scale = i
            .wp_fractional_scale_manager
            .get_fractional_scale(&wl_surface);
        let wp_viewport = i.wp_viewporter.get_viewport(&wl_surface);
        wl_surface.commit();
        let window = Rc::new(EggWindowInner {
            ctx: self.inner.clone(),
            egv: i.renderer.create_context(),
            egui: egui::Context::default(),
            wl_surface,
            wp_viewport,
            wp_fractional_scale,
            xdg_surface,
            xdg_toplevel,
            jay_sync_file_surface,
            frame_task: Default::default(),
            want_frame: Default::default(),
            have_frame: Cell::new(true),
            initial_commit_pending: Cell::new(true),
            owner: Default::default(),
            active_seat: Default::default(),
            raw_input: RefCell::new(None),
            close: Default::default(),
            repaint_timeout: Cell::new(u64::MAX),
            repaint_timeout_changed: Default::default(),
            fonts_changed: Cell::new(true),
            buffers: Default::default(),
            surface_pending: Default::default(),
            logical_size: Cell::new([800, 600]),
            physical_size: Cell::new([800, 600]),
            scale: Default::default(),
        });
        window
            .egui
            .all_styles_mut(|s| s.spacing.item_spacing.y = 5.0);
        window.xdg_surface.owner.set(Some(window.clone()));
        window.xdg_toplevel.owner.set(Some(window.clone()));
        window.wp_fractional_scale.owner.set(Some(window.clone()));
        i.windows.set(window.wl_surface.id, window.clone());
        let eng = &i.state.eng;
        let render_task = eng.spawn("egui-render", window.clone().render_frames());
        let timer_task = eng.spawn("egui-timer", window.clone().handle_timer());
        let window = EggWindow {
            _ctx: self.clone(),
            inner: window,
            _render_task: render_task,
            _timer_task: timer_task,
        };
        Rc::new(window)
    }
}

impl EggContextInner {
    pub fn add_seat(self: &Rc<Self>, global_name: GlobalName) {
        let wl_seat = Rc::new(UsrWlSeat {
            id: self.con.id(),
            con: self.con.clone(),
            owner: Default::default(),
            version: Version(10),
        });
        self.registry.bind(global_name, &*wl_seat);
        self.con.add_object(wl_seat.clone());
        let wl_pointer = wl_seat.get_pointer();
        let wl_keyboard = wl_seat.get_keyboard();
        let wp_cursor_shape_device_v1 = self.wp_cursor_shape_manager_v1.get_pointer(&wl_pointer);
        let wl_data_device = self.wl_data_device_manager.get_data_device(&wl_seat);
        let seat = Rc::new(EggSeatInner {
            ctx: self.clone(),
            global_name,
            wl_seat,
            wl_pointer,
            wl_data_device,
            pointer_window: Default::default(),
            pointer_enter_serial: Default::default(),
            pointer_serial: Default::default(),
            pointer_pos: Default::default(),
            kb_modifiers: Default::default(),
            wp_cursor_shape_device_v1,
            wl_keyboard,
            kb_window: Default::default(),
            kb_serial: Default::default(),
            serial: Default::default(),
            wl_data_source: Default::default(),
            copy_text: Default::default(),
            copy_task: Default::default(),
            paste_task: Default::default(),
        });
        seat.wl_pointer.owner.set(Some(seat.clone()));
        seat.wl_keyboard.owner.set(Some(seat.clone()));
        let seat = EggSeat { inner: seat };
        self.seats.set(global_name, seat);
    }
}

const TEXT_PLAIN: &str = "text/plain;charset=utf-8";

impl EggSeatInner {
    pub fn request_paste(self: &Rc<Self>) {
        let Some(offer) = self.wl_data_device.selection.get() else {
            return;
        };
        if !offer.mime_types.borrow().contains(TEXT_PLAIN) {
            return;
        }
        let Some(window) = self.kb_window.get() else {
            return;
        };
        let Pipe { read, write } = match pipe() {
            Ok(p) => p.map_read(Rc::new).map_write(Rc::new),
            Err(e) => {
                log::error!("Could not create pipe: {}", ErrorFmt(e));
                return;
            }
        };
        offer.receive(TEXT_PLAIN, &write);
        let window = Rc::downgrade(&window);
        let ring = self.ctx.state.ring.clone();
        let mut buf = Buf::new(1024);
        let mut out = Vec::new();
        let task = self.ctx.state.eng.spawn("egui-paste", async move {
            loop {
                let n = match ring.read(&read, buf.clone()).await {
                    Ok(n) => n,
                    Err(e) => {
                        log::error!("Could not read from peer: {}", ErrorFmt(e));
                        return;
                    }
                };
                if n == 0 {
                    if let Some(window) = window.upgrade() {
                        let s = match String::from_utf8(out) {
                            Ok(s) => s,
                            Err(e) => {
                                log::error!("Peer did not send UTF-8: {}", ErrorFmt(e));
                                return;
                            }
                        };
                        window.event(Event::Paste(s));
                    }
                    return;
                }
                out.extend_from_slice(&buf[..n]);
                if out.len() >= 1024 * buf.len() {
                    log::error!("Paste buffer is too large");
                    return;
                }
            }
        });
        self.paste_task.set(Some(task));
    }
}

impl EggWindow {
    pub fn request_redraw(&self) {
        self.inner.want_frame();
    }

    pub fn set_owner(&self, owner: Option<Rc<dyn EggWindowOwner>>) {
        self.inner.owner.set(owner);
    }
}

impl EggWindowInner {
    fn update_physical_size(&self) {
        let size = self.logical_size.get();
        let scale = self.scale.get();
        let physical_size = scale.pixel_size(size);
        if self.physical_size.replace(physical_size) != physical_size {
            self.want_frame();
        }
    }

    fn want_frame(&self) {
        self.want_frame.set(true);
        self.maybe_trigger_frame();
    }

    fn maybe_trigger_frame(&self) {
        if self.want_frame.get() && self.have_frame.get() {
            self.frame_task.trigger();
        }
    }

    async fn handle_timer(self: Rc<Self>) {
        loop {
            let timeout = self.ctx.state.ring.timeout(self.repaint_timeout.get());
            let triggered = || self.repaint_timeout_changed.triggered();
            let timeout = select! {
                _ = timeout.fuse() => true,
                _ = triggered().fuse() => false,
            };
            if timeout {
                self.want_frame();
                triggered().await;
            }
        }
    }

    async fn render_frames(self: Rc<Self>) {
        loop {
            self.frame_task.triggered().await;
            if let Err(e) = self.render_frame() {
                log::error!("Could not render frame: {}", ErrorFmt(e));
                break;
            }
        }
    }

    fn render_frame(self: &Rc<Self>) -> Result<(), EggError> {
        if self.fonts_changed.take() {
            self.egui
                .set_fonts(self.ctx.state.egg_state.font_definitions());
        }
        if self.initial_commit_pending.get() {
            return Ok(());
        }
        if !self.have_frame.get() {
            return Ok(());
        }
        if !self.want_frame.get() {
            return Ok(());
        }
        let Some(owner) = self.owner.get() else {
            return Ok(());
        };
        let Some(render_ctx) = self.ctx.state.render_ctx.get() else {
            return Ok(());
        };
        let Some(format) = render_ctx.formats().get(&EGV_FORMAT.drm) else {
            return Ok(());
        };
        let logical_size = self.logical_size.get();
        let physical_size = self.physical_size.get();
        let mut fb_opt = self.buffers.back().get();
        'check: {
            if let Some(fb) = &fb_opt {
                if fb.size.get() != physical_size {
                    fb_opt = None;
                    break 'check;
                }
                if !format.read_modifiers.contains(&fb.bo.dmabuf().modifier) {
                    fb_opt = None;
                    break 'check;
                }
            }
        }
        let fb = match fb_opt {
            Some(fb) => fb,
            _ => {
                let modifiers: Vec<_> = self
                    .ctx
                    .renderer
                    .support()
                    .iter()
                    .filter(|s| {
                        s.max_width >= physical_size[0] as u32
                            && s.max_height >= physical_size[1] as u32
                            && format.read_modifiers.contains(&s.modifier)
                    })
                    .map(|s| s.modifier)
                    .collect();
                let bo = self
                    .ctx
                    .allocator
                    .create_bo(
                        &self.ctx.state.dma_buf_ids,
                        physical_size[0],
                        physical_size[1],
                        EGV_FORMAT,
                        &modifiers,
                        BO_USE_RENDERING,
                    )
                    .map_err(EggError::AllocateBuffer)?;
                let egv = self
                    .egv
                    .import_framebuffer(&bo)
                    .map_err(EggError::ImportFramebuffer)?;
                let dmabuf = bo.dmabuf();
                let sync_file = dmabuf
                    .export_sync_file(DMA_BUF_SYNC_WRITE)
                    .map_err(EggError::ExportBoSyncFile)?;
                let wl_buffer = self.ctx.zwp_linux_dmabuf_v1.create_buffer(dmabuf);
                let fb = Rc::new(EggFramebuffer {
                    client_acquire_fence: CloneCell::new(Some(sync_file)),
                    size: Cell::new(physical_size),
                    bo,
                    egv,
                    wl_buffer,
                    window: Rc::downgrade(self),
                    drop_queue: self.ctx.state.bo_drop_queue.clone(),
                });
                self.buffers.back().set(Some(fb.clone()));
                fb
            }
        };
        let Some(sync_file) = fb.client_acquire_fence.get() else {
            return Ok(());
        };
        let raw_input = self
            .raw_input
            .take()
            .unwrap_or_else(|| self.default_raw_input());
        let full_output = self.egui.run(raw_input, |ctx| {
            owner.clone().render(ctx);
        });
        let FullOutput {
            platform_output,
            textures_delta,
            shapes,
            pixels_per_point,
            viewport_output,
        } = full_output;
        let primitives = self.egui.tessellate(shapes, pixels_per_point);
        let sync = fb
            .egv
            .render(
                textures_delta,
                pixels_per_point,
                &primitives,
                (0.0, 0.0),
                sync_file.as_ref(),
            )
            .map_err(EggError::Render)?;
        let PlatformOutput {
            commands,
            cursor_icon,
            ..
        } = platform_output;
        if let Some(seat) = self.active_seat.get() {
            'set_icon: {
                let cursor = match cursor_icon {
                    CursorIcon::None => {
                        seat.wl_pointer
                            .set_cursor(seat.pointer_serial.get(), None, 0, 0);
                        break 'set_icon;
                    }
                    CursorIcon::Default => KnownCursor::Default,
                    CursorIcon::ContextMenu => KnownCursor::ContextMenu,
                    CursorIcon::Help => KnownCursor::Help,
                    CursorIcon::PointingHand => KnownCursor::Pointer,
                    CursorIcon::Progress => KnownCursor::Progress,
                    CursorIcon::Wait => KnownCursor::Wait,
                    CursorIcon::Cell => KnownCursor::Cell,
                    CursorIcon::Crosshair => KnownCursor::Crosshair,
                    CursorIcon::Text => KnownCursor::Text,
                    CursorIcon::VerticalText => KnownCursor::VerticalText,
                    CursorIcon::Alias => KnownCursor::Alias,
                    CursorIcon::Copy => KnownCursor::Copy,
                    CursorIcon::Move => KnownCursor::Move,
                    CursorIcon::NoDrop => KnownCursor::NoDrop,
                    CursorIcon::NotAllowed => KnownCursor::NotAllowed,
                    CursorIcon::Grab => KnownCursor::Grab,
                    CursorIcon::Grabbing => KnownCursor::Grabbing,
                    CursorIcon::AllScroll => KnownCursor::AllScroll,
                    CursorIcon::ResizeHorizontal => KnownCursor::EwResize,
                    CursorIcon::ResizeNeSw => KnownCursor::NeswResize,
                    CursorIcon::ResizeNwSe => KnownCursor::NwseResize,
                    CursorIcon::ResizeVertical => KnownCursor::NsResize,
                    CursorIcon::ResizeEast => KnownCursor::EResize,
                    CursorIcon::ResizeSouthEast => KnownCursor::SeResize,
                    CursorIcon::ResizeSouth => KnownCursor::SResize,
                    CursorIcon::ResizeSouthWest => KnownCursor::SwResize,
                    CursorIcon::ResizeWest => KnownCursor::WResize,
                    CursorIcon::ResizeNorthWest => KnownCursor::NwResize,
                    CursorIcon::ResizeNorth => KnownCursor::NResize,
                    CursorIcon::ResizeNorthEast => KnownCursor::NeResize,
                    CursorIcon::ResizeColumn => KnownCursor::ColResize,
                    CursorIcon::ResizeRow => KnownCursor::RowResize,
                    CursorIcon::ZoomIn => KnownCursor::ZoomIn,
                    CursorIcon::ZoomOut => KnownCursor::ZoomOut,
                };
                seat.wp_cursor_shape_device_v1
                    .set_shape(seat.pointer_serial.get(), cursor);
            }
        }
        for command in commands {
            match command {
                OutputCommand::CopyText(t) => {
                    if let Some(seat) = self.active_seat.get() {
                        let data_src = self.ctx.wl_data_device_manager.create_data_source();
                        data_src.offer(TEXT_PLAIN);
                        data_src.owner.set(Some(seat.clone()));
                        seat.wl_data_device
                            .set_selection(seat.serial.get(), &data_src);
                        if let Some(old) = seat.wl_data_source.set(Some(data_src)) {
                            old.con.remove_obj(&*old);
                        }
                        seat.copy_text.replace(Some(Buf::from_slice(t.as_bytes())));
                    }
                }
                OutputCommand::CopyImage(_) => {}
                OutputCommand::OpenUrl(url) => {
                    if let Some(forker) = self.ctx.state.forker.get() {
                        forker.spawn("xdg-open".to_string(), vec![url.url], vec![], vec![]);
                    }
                }
            }
        }
        let Some(viewport) = viewport_output.get(&ViewportId::ROOT) else {
            return Err(EggError::NoViewportOutput);
        };
        for command in &viewport.commands {
            match command {
                ViewportCommand::Close => self.close.set(true),
                ViewportCommand::CancelClose => self.close.set(false),
                ViewportCommand::Title(s) => self.xdg_toplevel.set_title(s),
                ViewportCommand::Fullscreen(b) => self.xdg_toplevel.set_fullscreen(*b),
                ViewportCommand::RequestPaste => {
                    if let Some(seat) = self.active_seat.get() {
                        seat.request_paste();
                    }
                }
                _ => {}
            }
        }
        let repaint_delay = u64::try_from(viewport.repaint_delay.as_nanos()).unwrap_or(u64::MAX);
        let repaint_timeout = self.ctx.state.now_nsec().saturating_add(repaint_delay);
        self.repaint_timeout.set(repaint_timeout);
        if repaint_timeout != u64::MAX {
            self.repaint_timeout_changed.trigger();
        }
        self.wl_surface.attach(&fb.wl_buffer);
        self.wl_surface.damage();
        self.jay_sync_file_surface.set_acquire(sync.as_ref());
        self.jay_sync_file_surface
            .get_release()
            .owner
            .set(Some(fb.clone()));
        self.wl_surface.frame().owner.set(Some(self.clone()));
        self.wp_viewport
            .set_destination(logical_size[0], logical_size[1]);
        self.wl_surface.commit();
        fb.client_acquire_fence.take();
        self.buffers.flip();
        self.have_frame.set(false);
        self.want_frame.set(false);
        if self.close.get() {
            owner.close();
        }
        Ok(())
    }
}

impl UsrXdgSurfaceOwner for EggWindowInner {
    fn configure(&self) {
        let pending = mem::take(&mut *self.surface_pending.borrow_mut());
        if let Some((mut w, mut h)) = pending.size {
            let [old_w, old_h] = self.logical_size.get();
            w = if w > 0 { w } else { old_w };
            h = if h > 0 { h } else { old_h };
            let size = [w, h];
            if self.logical_size.replace(size) != size {
                self.update_physical_size();
            }
        }
        if self.initial_commit_pending.take() {
            self.want_frame();
        }
    }
}

impl UsrXdgToplevelOwner for EggWindowInner {
    fn configure(&self, width: i32, height: i32) {
        self.surface_pending.borrow_mut().size = Some((width, height));
    }

    fn close(&self) {
        let raw_input = &mut *self.raw_input.borrow_mut();
        let raw_input = raw_input.get_or_insert_with(|| self.default_raw_input());
        raw_input
            .viewports
            .get_mut(&ViewportId::ROOT)
            .unwrap()
            .events
            .push(ViewportEvent::Close);
        self.close.set(true);
        self.want_frame();
    }
}

impl UsrWpFractionalScaleOwner for EggWindowInner {
    fn preferred_scale(self: Rc<Self>, ev: &PreferredScale) {
        let scale = Scale::from_wl(ev.scale);
        if self.scale.replace(scale) != scale {
            self.update_physical_size();
        }
    }
}

impl EggWindowInner {
    fn event(&self, event: Event) {
        let raw_input = &mut *self.raw_input.borrow_mut();
        let raw_input = raw_input.get_or_insert_with(|| self.default_raw_input());
        raw_input.events.push(event);
        self.want_frame();
    }

    fn default_raw_input(&self) -> RawInput {
        let viewport_info = ViewportInfo {
            native_pixels_per_point: Some(self.scale.get().to_f64() as _),
            ..Default::default()
        };
        let size = self.logical_size.get();
        let size =
            egui::Rect::from_min_size(Pos2::default(), Vec2::new(size[0] as f32, size[1] as f32));
        let mut modifiers = Modifiers::default();
        if let Some(seat) = self.active_seat.get() {
            modifiers = seat.kb_modifiers.get();
        }
        RawInput {
            viewport_id: ViewportId::ROOT,
            viewports: std::iter::once((ViewportId::ROOT, viewport_info)).collect(),
            screen_rect: Some(size),
            max_texture_side: Some(self.ctx.renderer.max_texture_side()),
            time: Some(self.ctx.state.now_nsec() as f64 / 1_000_000_000.0),
            modifiers,
            ..Default::default()
        }
    }
}

impl EggSeatInner {
    fn activate_pointer_window(self: &Rc<Self>) -> Option<Rc<EggWindowInner>> {
        let window = self.pointer_window.get()?;
        window.active_seat.set(Some(self.clone()));
        Some(window)
    }

    fn activate_kb_window(self: &Rc<Self>) -> Option<Rc<EggWindowInner>> {
        let window = self.kb_window.get()?;
        window.active_seat.set(Some(self.clone()));
        Some(window)
    }

    fn leave(self: &Rc<Self>) {
        if let Some(window) = self.pointer_window.take()
            && let Some(active_seat) = window.active_seat.get()
            && rc_eq(&active_seat, &self)
        {
            window.active_seat.take();
        }
    }

    fn unfocus(self: &Rc<Self>) {
        if let Some(window) = self.kb_window.take()
            && let Some(active_seat) = window.active_seat.get()
            && rc_eq(&active_seat, &self)
        {
            window.active_seat.take();
        }
    }

    fn motion(self: &Rc<Self>, surface_x: Fixed, surface_y: Fixed) {
        let Some(window) = self.activate_pointer_window() else {
            return;
        };
        let pos = pos2(surface_x.to_f32(), surface_y.to_f32());
        self.pointer_pos.set(pos);
        window.event(Event::PointerMoved(pos));
    }
}

impl UsrWlPointerOwner for EggSeatInner {
    fn enter(self: Rc<Self>, ev: &Enter) {
        let Some(window) = self.ctx.windows.get(&ev.surface) else {
            return;
        };
        self.pointer_window.set(Some(window.clone()));
        self.pointer_enter_serial.set(ev.serial);
        self.pointer_serial.set(ev.serial);
        self.serial.set(ev.serial);
        (&self).motion(ev.surface_x, ev.surface_y);
    }

    fn leave(self: Rc<Self>, _ev: &Leave) {
        (&self).leave();
    }

    fn motion(self: Rc<Self>, ev: &Motion) {
        (&self).motion(ev.surface_x, ev.surface_y);
    }

    fn button(self: Rc<Self>, ev: &Button) {
        let Some(window) = self.activate_pointer_window() else {
            return;
        };
        self.pointer_serial.set(ev.serial);
        self.serial.set(ev.serial);
        let button = match ev.button {
            BTN_LEFT => PointerButton::Primary,
            BTN_RIGHT => PointerButton::Secondary,
            BTN_MIDDLE => PointerButton::Middle,
            BTN_SIDE => PointerButton::Extra1,
            BTN_EXTRA => PointerButton::Extra2,
            _ => return,
        };
        window.event(Event::PointerButton {
            pos: self.pointer_pos.get(),
            button,
            pressed: ev.state == wl_pointer::PRESSED,
            modifiers: self.kb_modifiers.get(),
        });
    }

    fn scroll(self: Rc<Self>, ps: &PendingScroll) {
        let Some(window) = self.activate_pointer_window() else {
            return;
        };
        let v120_x = ps.v120[HORIZONTAL_SCROLL].get();
        let v120_y = ps.v120[VERTICAL_SCROLL].get();
        let px_x = ps.px[HORIZONTAL_SCROLL].get();
        let px_y = ps.px[VERTICAL_SCROLL].get();
        let unit;
        let delta;
        if v120_x.is_some() || v120_y.is_some() {
            unit = MouseWheelUnit::Line;
            delta = vec2(
                -v120_x.unwrap_or_default() as f32 / 120.0,
                -v120_y.unwrap_or_default() as f32 / 120.0,
            );
        } else if px_x.is_some() || px_y.is_some() {
            unit = MouseWheelUnit::Point;
            delta = vec2(
                -px_x.unwrap_or_default().to_f32(),
                -px_y.unwrap_or_default().to_f32(),
            );
        } else {
            return;
        }
        window.event(Event::MouseWheel {
            unit,
            delta,
            modifiers: self.kb_modifiers.get(),
        });
    }
}

impl EggSeatInner {
    fn handle_key(self: &Rc<Self>, lookup: Lookup<'_>, serial: u32, down: bool) {
        let Some(window) = self.activate_kb_window() else {
            return;
        };
        self.kb_serial.set(serial);
        self.serial.set(serial);
        if down {
            let mut text = String::new();
            for key in lookup {
                if let Some(c) = key.char()
                    && c.is_not_control()
                {
                    text.push(c);
                }
            }
            if text.is_not_empty() {
                window.event(Event::Text(text));
            }
        }
        for key in lookup {
            let mut modifiers = map_mods(lookup.remaining_mods());
            let Some(key) = map_key(key.keysym(), &mut modifiers) else {
                continue;
            };
            if down && modifiers.ctrl {
                match key {
                    Key::V => self.request_paste(),
                    Key::C => window.event(Event::Copy),
                    Key::X => window.event(Event::Cut),
                    _ => {}
                }
            }
            window.event(Event::Key {
                key,
                physical_key: None,
                pressed: down,
                repeat: false,
                modifiers,
            });
        }
    }
}

impl UsrWlKeyboardOwner for EggSeatInner {
    fn focus(self: Rc<Self>, surface: WlSurfaceId, serial: u32) {
        let Some(window) = self.ctx.windows.get(&surface) else {
            return;
        };
        self.kb_window.set(Some(window.clone()));
        self.kb_serial.set(serial);
        self.serial.set(serial);
        window.active_seat.set(Some(self.clone()));
    }

    fn unfocus(self: Rc<Self>) {
        (&self).unfocus();
    }

    fn modifiers(self: Rc<Self>, mods: ModifierMask) {
        self.kb_modifiers.set(map_mods(mods));
    }

    fn down(self: Rc<Self>, lookup: Lookup<'_>, serial: u32) {
        self.handle_key(lookup, serial, true);
    }

    fn repeat(self: Rc<Self>, lookup: Lookup<'_>, serial: u32) {
        self.handle_key(lookup, serial, true);
    }

    fn up(self: Rc<Self>, lookup: Lookup<'_>, serial: u32) {
        self.handle_key(lookup, serial, false);
    }
}

fn map_mods(mods: ModifierMask) -> Modifiers {
    Modifiers {
        alt: mods.contains(ModifierMask::ALT),
        ctrl: mods.contains(ModifierMask::CONTROL),
        shift: mods.contains(ModifierMask::SHIFT),
        mac_cmd: false,
        command: mods.contains(ModifierMask::CONTROL),
    }
}

impl UsrJaySyncFileReleaseOwner for EggFramebuffer {
    fn release(&self, sync_file: Option<SyncFile>) {
        self.client_acquire_fence.set(Some(sync_file));
        if let Some(window) = self.window.upgrade() {
            window.maybe_trigger_frame();
        }
    }
}

impl UsrWlCallbackOwner for EggWindowInner {
    fn done(self: Rc<Self>) {
        self.have_frame.set(true);
        self.maybe_trigger_frame();
    }
}

fn map_key(kc: Keysym, mods: &mut Modifiers) -> Option<Key> {
    use {Key as K, kbvm::syms as s};
    let mut with_shift = |k| {
        mods.shift = true;
        k
    };
    let key = match kc {
        s::Down | s::KP_Down => K::ArrowDown,
        s::Left | s::KP_Left => K::ArrowLeft,
        s::Right | s::KP_Right => K::ArrowRight,
        s::Up | s::KP_Up => K::ArrowUp,
        s::Escape => K::Escape,
        s::Tab | s::KP_Tab => K::Tab,
        s::ISO_Left_Tab => with_shift(K::Tab),
        s::BackSpace => K::Backspace,
        s::Return | s::KP_Enter => K::Enter,
        s::space | s::KP_Space => K::Space,
        s::Insert | s::KP_Insert => K::Insert,
        s::Delete | s::KP_Delete => K::Delete,
        s::Home | s::KP_Home | s::KP_Begin => K::Home,
        s::End | s::KP_End => K::End,
        s::Page_Up | s::KP_Page_Up => K::PageUp,
        s::Page_Down | s::KP_Page_Down => K::PageDown,
        s::XF86Copy => K::Copy,
        s::XF86Cut => K::Cut,
        s::XF86Paste => K::Paste,
        s::colon => K::Colon,
        s::comma => K::Comma,
        s::backslash => K::Backslash,
        s::slash | s::KP_Divide => K::Slash,
        s::bar => K::Pipe,
        s::question => K::Questionmark,
        s::exclam => K::Exclamationmark,
        s::bracketleft => K::OpenBracket,
        s::bracketright => K::CloseBracket,
        s::braceleft => K::OpenCurlyBracket,
        s::braceright => K::CloseCurlyBracket,
        s::grave => K::Backtick,
        s::minus | s::KP_Subtract => K::Minus,
        s::period | s::KP_Decimal => K::Period,
        s::plus | s::KP_Add => K::Plus,
        s::equal | s::KP_Equal => K::Equals,
        s::semicolon => K::Semicolon,
        s::quotedbl => K::Quote,
        s::KP_0 | s::_0 => K::Num0,
        s::KP_1 | s::_1 => K::Num1,
        s::KP_2 | s::_2 => K::Num2,
        s::KP_3 | s::_3 => K::Num3,
        s::KP_4 | s::_4 => K::Num4,
        s::KP_5 | s::_5 => K::Num5,
        s::KP_6 | s::_6 => K::Num6,
        s::KP_7 | s::_7 => K::Num7,
        s::KP_8 | s::_8 => K::Num8,
        s::KP_9 | s::_9 => K::Num9,
        s::a => K::A,
        s::b => K::B,
        s::c => K::C,
        s::d => K::D,
        s::e => K::E,
        s::f => K::F,
        s::g => K::G,
        s::h => K::H,
        s::i => K::I,
        s::j => K::J,
        s::k => K::K,
        s::l => K::L,
        s::m => K::M,
        s::n => K::N,
        s::o => K::O,
        s::p => K::P,
        s::q => K::Q,
        s::r => K::R,
        s::s => K::S,
        s::t => K::T,
        s::u => K::U,
        s::v => K::V,
        s::w => K::W,
        s::x => K::X,
        s::y => K::Y,
        s::z => K::Z,
        s::A => with_shift(K::A),
        s::B => with_shift(K::B),
        s::C => with_shift(K::C),
        s::D => with_shift(K::D),
        s::E => with_shift(K::E),
        s::F => with_shift(K::F),
        s::G => with_shift(K::G),
        s::H => with_shift(K::H),
        s::I => with_shift(K::I),
        s::J => with_shift(K::J),
        s::K => with_shift(K::K),
        s::L => with_shift(K::L),
        s::M => with_shift(K::M),
        s::N => with_shift(K::N),
        s::O => with_shift(K::O),
        s::P => with_shift(K::P),
        s::Q => with_shift(K::Q),
        s::R => with_shift(K::R),
        s::S => with_shift(K::S),
        s::T => with_shift(K::T),
        s::U => with_shift(K::U),
        s::V => with_shift(K::V),
        s::W => with_shift(K::W),
        s::X => with_shift(K::X),
        s::Y => with_shift(K::Y),
        s::Z => with_shift(K::Z),
        s::F1 | s::KP_F1 => K::F1,
        s::F2 | s::KP_F2 => K::F2,
        s::F3 | s::KP_F3 => K::F3,
        s::F4 | s::KP_F4 => K::F4,
        s::F5 => K::F5,
        s::F6 => K::F6,
        s::F7 => K::F7,
        s::F8 => K::F8,
        s::F9 => K::F9,
        s::F10 => K::F10,
        s::F11 => K::F11,
        s::F12 => K::F12,
        s::F13 => K::F13,
        s::F14 => K::F14,
        s::F15 => K::F15,
        s::F16 => K::F16,
        s::F17 => K::F17,
        s::F18 => K::F18,
        s::F19 => K::F19,
        s::F20 => K::F20,
        s::F21 => K::F21,
        s::F22 => K::F22,
        s::F23 => K::F23,
        s::F24 => K::F24,
        s::F25 => K::F25,
        s::F26 => K::F26,
        s::F27 => K::F27,
        s::F28 => K::F28,
        s::F29 => K::F29,
        s::F30 => K::F30,
        s::F31 => K::F31,
        s::F32 => K::F32,
        s::F33 => K::F33,
        s::F34 => K::F34,
        s::F35 => K::F35,
        s::XF86Back => K::BrowserBack,
        _ => return None,
    };
    Some(key)
}

impl Drop for EggSeat {
    fn drop(&mut self) {
        let s = &self.inner;
        s.copy_task.take();
        s.paste_task.take();
        s.leave();
        s.unfocus();
        if let Some(v) = s.wl_data_source.take() {
            s.ctx.con.remove_obj(&*v);
        }
        s.ctx.seats.remove(&s.global_name);
        s.ctx.con.remove_obj(&*s.wl_data_device);
        s.ctx.con.remove_obj(&*s.wl_keyboard);
        s.ctx.con.remove_obj(&*s.wp_cursor_shape_device_v1);
        s.ctx.con.remove_obj(&*s.wl_pointer);
        s.ctx.con.remove_obj(&*s.wl_seat);
    }
}

impl Drop for EggContext {
    fn drop(&mut self) {
        let i = &self.inner;
        i.state.egg_state.cxts.remove(&self.inner.id);
        i.seats.clear();
        i.windows.clear();
        i.con.owner.take();
        i.con.kill();
    }
}

impl Drop for EggWindow {
    fn drop(&mut self) {
        let i = &self.inner;
        i.ctx.windows.remove(&i.wl_surface.id);
        if let Some(seat) = i.active_seat.take() {
            for field in [&seat.kb_window, &seat.pointer_window] {
                if let Some(w) = field.get()
                    && rc_eq(&w, i)
                {
                    field.take();
                }
            }
        }
        i.owner.take();
        i.ctx.con.remove_obj(&*i.jay_sync_file_surface);
        i.ctx.con.remove_obj(&*i.xdg_toplevel);
        i.ctx.con.remove_obj(&*i.xdg_surface);
        i.ctx.con.remove_obj(&*i.wp_fractional_scale);
        i.ctx.con.remove_obj(&*i.wp_viewport);
        i.ctx.con.remove_obj(&*i.wl_surface);
    }
}

impl UsrWlDataSourceOwner for EggSeatInner {
    fn send(&self, _mime_type: &str, fd: Rc<OwnedFd>) {
        let Some(buf) = self.copy_text.borrow_mut().as_mut().map(|b| b.clone()) else {
            return;
        };
        let ring = self.ctx.state.ring.clone();
        let task = self.ctx.state.eng.spawn("egg-copy-text", async move {
            if let Err(e) = ring.write(&fd, buf, None).await {
                log::error!("Could not send text to client: {}", e);
            }
        });
        self.copy_task.set(Some(task));
    }
}

impl UsrConOwner for EggContextInner {
    fn killed(&self) {
        if let Some(ctx) = self.state.egg_state.ctx.get()
            && ctx.inner.id == self.id
        {
            self.state.egg_state.ctx.take();
        }
        for window in self.windows.clear().values() {
            if let Some(owner) = window.owner.take() {
                owner.close();
            }
        }
    }
}

impl Drop for EggFramebuffer {
    fn drop(&mut self) {
        if let Some(Some(fence)) = self.client_acquire_fence.take() {
            self.drop_queue.push(&fence.0, self.bo.clone());
        }
        self.wl_buffer.con.remove_obj(&*self.wl_buffer);
    }
}
