<NME> udev.rs
<BEF> use std::{
    borrow::Cow,
    cell::RefCell,
    collections::hash_map::{Entry, HashMap},
    os::unix::io::{AsRawFd, RawFd},
    path::PathBuf,
    rc::Rc,
    sync::{atomic::Ordering, Arc, Mutex},
    time::Duration,
};

use slog::Logger;

use crate::{
    drawing::*,
    render::*,
    state::{AnvilState, Backend, CalloopData},
};
#[cfg(feature = "debug")]
use smithay::backend::renderer::ImportMem;
#[cfg(feature = "egl")]
use smithay::{
    backend::{
        allocator::dmabuf::Dmabuf,
        renderer::{ImportDma, ImportEgl},
    },
    delegate_dmabuf,
    wayland::dmabuf::{DmabufGlobal, DmabufHandler, DmabufState, ImportError},
};
use smithay::{
    backend::{
        drm::{DrmDevice, DrmError, DrmEvent, DrmNode, GbmBufferedSurface, NodeType},
        egl::{EGLContext, EGLDevice, EGLDisplay},
        libinput::{LibinputInputBackend, LibinputSessionInterface},
        renderer::{
            damage::{DamageTrackedRenderer, DamageTrackedRendererError},
            element::{texture::TextureBuffer, AsRenderElements, RenderElementStates},
            gles2::{Gles2Renderbuffer, Gles2Renderer},
            multigpu::{egl::EglGlesBackend, GpuManager, MultiRenderer, MultiTexture},
            Bind, Frame, Renderer,
        },
        session::{auto::AutoSession, Session, Signal as SessionSignal},
        udev::{all_gpus, primary_gpu, UdevBackend, UdevEvent},
        SwapBuffersError,
    },
    desktop::{
        space::{Space, SurfaceTree},
        Window,
    },
    input::pointer::{CursorImageAttributes, CursorImageStatus},
    output::{Mode, Output, PhysicalProperties, Subpixel},
    reexports::{
        calloop::{
            timer::{TimeoutAction, Timer},
            Dispatcher, EventLoop, LoopHandle, RegistrationToken,
        },
        drm::{
            self,
            control::{
                connector::{Info as ConnectorInfo, State as ConnectorState},
                crtc,
                encoder::Info as EncoderInfo,
                Device as ControlDevice,
            },
        },
        gbm::Device as GbmDevice,
        input::Libinput,
        nix::{fcntl::OFlag, sys::stat::dev_t},
        wayland_server::{backend::GlobalId, protocol::wl_surface, Display, DisplayHandle},
    },
    utils::{
        signaling::{Linkable, SignalToken, Signaler},
        IsAlive, Logical, Point, Rectangle, Scale, Transform,
    },
    wayland::{
        compositor,
        input_method::{InputMethodHandle, InputMethodSeat},
    backends: HashMap<dev_t, BackendData>,
    signaler: Signaler<SessionSignal>,
    pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
}

impl Backend for UdevData {
pub struct SessionFd(RawFd);
impl AsRawFd for SessionFd {
    fn as_raw_fd(&self) -> RawFd {
        self.0
    }
}

#[derive(Debug, PartialEq)]
struct UdevOutputId {
    device_id: DrmNode,
    crtc: crtc::Handle,
}

pub struct UdevData {
    pub session: AutoSession,
    dh: DisplayHandle,
    #[cfg(feature = "egl")]
    dmabuf_state: Option<(DmabufState, DmabufGlobal)>,
    primary_gpu: DrmNode,
    gpus: GpuManager<EglGlesBackend<Gles2Renderer>>,
    backends: HashMap<DrmNode, BackendData>,
    pointer_images: Vec<(xcursor::parser::Image, TextureBuffer<MultiTexture>)>,
    pointer_element: PointerElement<MultiTexture>,
    #[cfg(feature = "debug")]
    fps_texture: MultiTexture,
    signaler: Signaler<SessionSignal>,
    pointer_image: crate::cursor::Cursor,
    logger: slog::Logger,
}

#[cfg(feature = "egl")]
impl DmabufHandler for AnvilState<UdevData> {
     */
    let pointer_bytes = include_bytes!("../resources/cursor2.rgba");
    let primary_gpu = primary_gpu(&session.seat()).unwrap_or_default();
    let data = UdevData {
        session,
        output_map: output_map.clone(),
            .map_err(|_| ImportError::Failed)
        backends: HashMap::new(),
        signaler: session_signal.clone(),
        pointer_image: ImageBuffer::from_raw(64, 64, pointer_bytes.to_vec()).unwrap(),
    };
    let mut state = AnvilState::init(
        display.clone(),
        self.session.seat()
    }

    fn reset_buffers(&mut self, output: &Output) {
        log.clone(),
    );

    /*
     * Initialize the udev backend
     */
        {
            warn!(self.logger, "Early buffer import failed: {}", err);
        }
    }
}

pub fn run_udev(log: Logger) {
    let mut event_loop = EventLoop::try_new().unwrap();
    let mut display = Display::new().unwrap();

    /*
     * Initialize session
     */
    let (session, notifier) = match AutoSession::new(log.clone()) {
        Some(ret) => ret,
        None => {
            crit!(log, "Could not initialize a session");
            return;
        }
    };
    let session_signal = notifier.signaler();

    /*
     * Initialize the compositor
     */
    let primary_gpu = if let Ok(var) = std::env::var("ANVIL_DRM_DEVICE") {
        DrmNode::from_path(var).expect("Invalid drm device path")
    } else {
        primary_gpu(&session.seat())
            .unwrap()
            .and_then(|x| DrmNode::from_path(x).ok()?.node_with_type(NodeType::Render)?.ok())
            .unwrap_or_else(|| {
                all_gpus(&session.seat())
                    .unwrap()
                    .into_iter()
                    .find_map(|x| DrmNode::from_path(x).ok())
                    .expect("No GPU!")
            })
    };
    info!(log, "Using {} as primary gpu.", primary_gpu);

    #[cfg_attr(not(feature = "egl"), allow(unused_mut))]
    let mut gpus = GpuManager::new(EglGlesBackend::default(), log.clone()).unwrap();
    #[cfg_attr(not(feature = "egl"), allow(unused_mut))]
    #[cfg(any(feature = "egl", feature = "debug"))]
    let mut renderer = gpus
        .renderer::<Gles2Renderbuffer>(&primary_gpu, &primary_gpu)
        .unwrap();

    #[cfg(feature = "debug")]
    let fps_image =
        image::io::Reader::with_format(std::io::Cursor::new(FPS_NUMBERS_PNG), image::ImageFormat::Png)
            .decode()
            .unwrap();
    #[cfg(feature = "debug")]
    let fps_texture = renderer
        .import_memory(
            &fps_image.to_rgba8(),
            (fps_image.width() as i32, fps_image.height() as i32).into(),
            false,
        )
        .expect("Unable to upload FPS texture");

    // init dmabuf support with format list from our primary gpu
    // TODO: This does not necessarily depend on egl, but mesa makes no use of it without wl_drm right now
    #[cfg(feature = "egl")]
    let dmabuf_state = {
        info!(
            log,
            "Trying to initialize EGL Hardware Acceleration via {:?}", primary_gpu
        );

        if renderer.bind_wl_display(&display.handle()).is_ok() {
            info!(log, "EGL hardware-acceleration enabled");
            let dmabuf_formats = renderer.dmabuf_formats().cloned().collect::<Vec<_>>();
            let mut state = DmabufState::new();
            let global = state.create_global::<AnvilState<UdevData>, _>(
                &display.handle(),
                dmabuf_formats,
                log.clone(),
            );
            Some((state, global))
        } else {
            None
        }
    };

    let data = UdevData {
        dh: display.handle(),
        #[cfg(feature = "egl")]
        dmabuf_state,
        session,
        primary_gpu,
        gpus,
        backends: HashMap::new(),
        signaler: session_signal.clone(),
        pointer_image: crate::cursor::Cursor::load(&log),
        pointer_images: Vec::new(),
        pointer_element: PointerElement::default(),
        #[cfg(feature = "debug")]
        fps_texture,
        logger: log.clone(),
    };
    let mut state = AnvilState::init(&mut display, event_loop.handle(), data, log.clone(), true);

    /*
     * Initialize the udev backend
     */
    let udev_backend = match UdevBackend::new(&state.seat_name, log.clone()) {
        Ok(ret) => ret,
        Err(err) => {
            crit!(log, "Failed to initialize udev backend"; "error" => err);
            return;
        }
    };

    /*
     * Initialize a fake output (we render one screen to every device in this example)
     */

    /*
     * Initialize libinput backend
     */
    let mut libinput_context = Libinput::new_with_udev::<LibinputSessionInterface<AutoSession>>(
        state.backend_data.session.clone().into(),
    );
    libinput_context.udev_assign_seat(&state.seat_name).unwrap();
    let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone());
    libinput_backend.link(session_signal);

    /*
     * Bind all our objects that get driven by the event loop
     */
    event_loop
        .handle()
        .insert_source(libinput_backend, move |event, _, data| {
            let dh = data.state.backend_data.dh.clone();
            data.state.process_input_event(&dh, event)
    gbm: GbmDevice<SessionFd>,
    registration_token: RegistrationToken,
    event_dispatcher: Dispatcher<'static, DrmDevice<SessionFd>, AnvilState<UdevData>>,
}

pub fn scan_connectors(
    }

    event_loop
        .handle()
        .insert_source(udev_backend, move |event, _, data| match event {
            UdevEvent::Added { device_id, path } => {
                data.state.device_added(&mut data.display, device_id, path)
            }
            UdevEvent::Changed { device_id } => data.state.device_changed(&mut data.display, device_id),
            UdevEvent::Removed { device_id } => data.state.device_removed(device_id),
        })
        .unwrap();

    /*
     * Start XWayland if supported
     */
    #[cfg(feature = "xwayland")]
    state.start_xwayland();

    /*
     * And run our loop
     */

    while state.running.load(Ordering::SeqCst) {
        let mut calloop_data = CalloopData { state, display };
        let result = event_loop.dispatch(Some(Duration::from_millis(16)), &mut calloop_data);
        CalloopData { state, display } = calloop_data;

        if result.is_err() {
            state.running.store(false, Ordering::SeqCst);
        } else {
            state.space.refresh();
            state.popups.cleanup();
            display.flush_clients().unwrap();
        }
    }
}

pub type RenderSurface = GbmBufferedSurface<Rc<RefCell<GbmDevice<SessionFd>>>, SessionFd>;

struct SurfaceData {
    dh: DisplayHandle,
    device_id: DrmNode,
    render_node: DrmNode,
    surface: RenderSurface,
    global: Option<GlobalId>,
    damage_tracked_renderer: DamageTrackedRenderer,
    #[cfg(feature = "debug")]
    fps: fps_ticker::Fps,
    #[cfg(feature = "debug")]
    fps_element: FpsElement<MultiTexture>,
}

impl Drop for SurfaceData {
    fn drop(&mut self) {
        if let Some(global) = self.global.take() {
            self.dh.remove_global::<AnvilState<UdevBackend>>(global);
        }
    }
}

struct BackendData {
    _restart_token: SignalToken,
    surfaces: Rc<RefCell<HashMap<crtc::Handle, Rc<RefCell<SurfaceData>>>>>,
    gbm: Rc<RefCell<GbmDevice<SessionFd>>>,
    registration_token: RegistrationToken,
    event_dispatcher: Dispatcher<'static, DrmDevice<SessionFd>, CalloopData<UdevData>>,
}

#[allow(clippy::too_many_arguments)]
fn scan_connectors(
    device_id: DrmNode,
    device: &DrmDevice<SessionFd>,
    gbm: &Rc<RefCell<GbmDevice<SessionFd>>>,
    display: &mut Display<AnvilState<UdevData>>,
    space: &mut Space<Window>,
    #[cfg(feature = "debug")] fps_texture: &MultiTexture,
    signaler: &Signaler<SessionSignal>,
    logger: &::slog::Logger,
) -> HashMap<crtc::Handle, Rc<RefCell<SurfaceData>>> {
    // Get a set of all modesetting resource handles (excluding planes):
    let res_handles = device.resource_handles().unwrap();

    // Find all connected output ports.
    let connector_infos: Vec<ConnectorInfo> = res_handles
        .connectors()
        .iter()
        .map(|conn| device.get_connector(*conn, true).unwrap())
        .filter(|conn| conn.state() == ConnectorState::Connected)
        .inspect(|conn| info!(logger, "Connected: {:?}", conn.interface()))
        .collect();

    let mut backends = HashMap::new();

    let (render_node, formats) = {
        let display = unsafe { EGLDisplay::new(&*gbm.borrow(), logger.clone()).unwrap() };
        let node = match EGLDevice::device_for_display(&display)
            .ok()
            .and_then(|x| x.try_get_render_node().ok().flatten())
        {
            Some(node) => node,
            None => return HashMap::new(),
        };
        let context = EGLContext::new(&display, logger.clone()).unwrap();
        (node, context.dmabuf_render_formats().clone())
    };

    // very naive way of finding good crtc/encoder/connector combinations. This problem is np-complete
    for connector_info in connector_infos {
        let encoder_infos = connector_info
            .encoders()
            .iter()
            .flat_map(|encoder_handle| device.get_encoder(*encoder_handle))
            .collect::<Vec<EncoderInfo>>();

        let crtcs = encoder_infos
            .iter()
            .flat_map(|encoder_info| res_handles.filter_crtcs(encoder_info.possible_crtcs()));

        for crtc in crtcs {
            // Skip CRTCs used by previous connectors.
            let entry = match backends.entry(crtc) {
                Entry::Vacant(entry) => entry,
                Entry::Occupied(_) => continue,
            };

            info!(
                logger,
                "Trying to setup connector {:?}-{} with crtc {:?}",
                connector_info.interface(),
                connector_info.interface_id(),
                crtc,
            );

            let mode = connector_info.modes()[0];
            let mut surface = match device.create_surface(crtc, mode, &[connector_info.handle()]) {
                Ok(surface) => surface,
                Err(err) => {
                    warn!(logger, "Failed to create drm surface: {}", err);
                    continue;
                }
            };
            surface.link(signaler.clone());

            let gbm_surface =
                match GbmBufferedSurface::new(surface, gbm.clone(), formats.clone(), logger.clone()) {
                    Ok(renderer) => renderer,
                    Err(err) => {
                        warn!(logger, "Failed to create rendering surface: {}", err);
                        continue;
                    }
                };

            let size = mode.size();
            let mode = Mode {
                size: (size.0 as i32, size.1 as i32).into(),
                refresh: mode.vrefresh() as i32 * 1000,
            };

            let interface_short_name = match connector_info.interface() {
                drm::control::connector::Interface::DVII => Cow::Borrowed("DVI-I"),
                drm::control::connector::Interface::DVID => Cow::Borrowed("DVI-D"),
                drm::control::connector::Interface::DVIA => Cow::Borrowed("DVI-A"),
                drm::control::connector::Interface::SVideo => Cow::Borrowed("S-VIDEO"),
                drm::control::connector::Interface::DisplayPort => Cow::Borrowed("DP"),
                drm::control::connector::Interface::HDMIA => Cow::Borrowed("HDMI-A"),
                drm::control::connector::Interface::HDMIB => Cow::Borrowed("HDMI-B"),
                drm::control::connector::Interface::EmbeddedDisplayPort => Cow::Borrowed("eDP"),
                other => Cow::Owned(format!("{:?}", other)),
            };

            let output_name = format!("{}-{}", interface_short_name, connector_info.interface_id());

            let (phys_w, phys_h) = connector_info.size().unwrap_or((0, 0));
            let output = Output::new(
                output_name,
                PhysicalProperties {
                    size: (phys_w as i32, phys_h as i32).into(),
                    subpixel: Subpixel::Unknown,
                    make: "Smithay".into(),
                    model: "Generic DRM".into(),
                },
                None,
                    .expect("Failed to load pointer")
            };

            // Set the handler.
            // Note: if you replicate this (very simple) structure, it is rather easy
            // to introduce reference cycles with Rc. Be sure about your drop order
            let renderer = Rc::new(DrmRenderer {
                device_id,
                #[cfg(feature = "egl")]
                egl_buffer_reader: if is_primary {
                    self.egl_reader.borrow().clone()
                } else {
                    None
                },
                compositor_token: self.ctoken,
                backends: backends.clone(),
                window_map: self.window_map.clone(),
                output_map: self.backend_data.output_map.clone(),
                pointer_location: self.pointer_location.clone(),
                pointer_image,
                cursor_status: self.cursor_status.clone(),
                dnd_icon: self.dnd_icon.clone(),
                logger: self.log.clone(),
                start_time: std::time::Instant::now(),
            });
            let mut listener = DrmRendererSessionListener {
                renderer: renderer.clone(),
                loop_handle: self.handle.clone(),
            };
            let restart_token = self.backend_data.signaler.register(move |signal| match signal {
                SessionSignal::ActivateSession | SessionSignal::ActivateDevice { .. } => listener.activate(),
                _ => {}
            });
            let mut drm_handler = DrmHandlerImpl {
                renderer,
                loop_handle: self.handle.clone(),
            };

            device.link(self.backend_data.signaler.clone());
            let dev_id = device.device_id();
            let event_dispatcher = Dispatcher::new(device, move |event, _, _| match event {
                DrmEvent::VBlank(crtc) => drm_handler.vblank(crtc),
                DrmEvent::Error(error) => {
                    error!(drm_handler.renderer.logger, "{:?}", error);
                }
            });
            let registration_token = self.handle.register_dispatcher(event_dispatcher.clone()).unwrap();

            trace!(self.log, "Backends: {:?}", backends.borrow().keys());
                surface: gbm_surface,
                global: Some(global),
                damage_tracked_renderer,
                #[cfg(feature = "debug")]
                fps: fps_ticker::Fps::default(),
                #[cfg(feature = "debug")]
                fps_element,
            })));

            break;
        }
    }

                    egl,
                    context,
                    gbm,
                },
            );
        }
        let devices = device_fd
            .map(SessionFd)
            .map(|fd| (DrmDevice::new(fd, true, self.log.clone()), GbmDevice::new(fd)));

        // Report device open failures.
        let (mut device, gbm) = match devices {
            Some((Ok(drm), Ok(gbm))) => (drm, gbm),
            Some((Err(err), _)) => {
                warn!(
                    self.log,
                    "Skipping device {:?}, because of drm error: {}", device_id, err
                );
                return;
            }
            Some((_, Err(err))) => {
                // TODO try DumbBuffer allocator in this case
                warn!(
                    self.log,
                    "Skipping device {:?}, because of gbm error: {}", device_id, err
                );
                return;
            }
            None => return,
        };

        let gbm = Rc::new(RefCell::new(gbm));
        let node = match DrmNode::from_dev_id(device_id) {
            Ok(node) => node,
            Err(err) => {
                warn!(self.log, "Failed to access drm node for {}: {}", device_id, err);
                return;
            }
        };
        let backends = Rc::new(RefCell::new(scan_connectors(
            node,
            &device,
            &gbm,
            display,
            &mut self.space,
            #[cfg(feature = "debug")]
            &self.backend_data.fps_texture,
            &self.backend_data.signaler,
            &self.log,
        )));

        let handle = self.handle.clone();
        let restart_token = self.backend_data.signaler.register(move |signal| match signal {
            SessionSignal::ActivateSession | SessionSignal::ActivateDevice { .. } => {
                handle.insert_idle(move |data| data.state.render(node, None));
            }
            _ => {}
        });

        device.link(self.backend_data.signaler.clone());
        let event_dispatcher =
            Dispatcher::new(device, move |event, _, data: &mut CalloopData<_>| match event {
                DrmEvent::VBlank(crtc) => data.state.render(node, Some(crtc)),
            debug!(self.log, "Dropping device");
        }
    }
}

pub struct DrmHandlerImpl<Data: 'static> {
    renderer: Rc<DrmRenderer>,
    loop_handle: LoopHandle<'static, Data>,
}

impl<Data: 'static> DrmHandlerImpl<Data> {
    fn vblank(&mut self, crtc: crtc::Handle) {
        self.renderer.clone().render(crtc, None, Some(&self.loop_handle))
    }
}

pub struct DrmRendererSessionListener<Data: 'static> {
    renderer: Rc<DrmRenderer>,
    loop_handle: LoopHandle<'static, Data>,
}

impl<Data: 'static> DrmRendererSessionListener<Data> {
    fn activate(&mut self) {
        // we want to be called, after all session handling is done (TODO this is not so nice)
        let renderer = self.renderer.clone();
        let handle = self.loop_handle.clone();
        self.loop_handle
            .insert_idle(move |_| renderer.render_all(Some(&handle)));
    }
}

pub struct DrmRenderer {
    device_id: dev_t,
    #[cfg(feature = "egl")]
    egl_buffer_reader: Option<EGLBufferReader>,
    compositor_token: CompositorToken<Roles>,
    backends: Rc<RefCell<HashMap<crtc::Handle, Rc<RefCell<RenderSurface>>>>>,
    window_map: Rc<RefCell<MyWindowMap>>,
    output_map: Rc<RefCell<Vec<MyOutput>>>,
    pointer_location: Rc<RefCell<(f64, f64)>>,
    pointer_image: Gles2Texture,
    cursor_status: Arc<Mutex<CursorImageStatus>>,
    dnd_icon: Arc<Mutex<Option<wl_surface::WlSurface>>>,
    logger: ::slog::Logger,
    start_time: std::time::Instant,
}

impl DrmRenderer {
    fn render_all<Data: 'static>(self: Rc<Self>, evt_handle: Option<&LoopHandle<'static, Data>>) {
        for crtc in self.backends.borrow().keys() {
            self.clone().render(*crtc, None, evt_handle);
        }
    }
    fn render<Data: 'static>(
        self: Rc<Self>,
        crtc: crtc::Handle,
        timer: Option<TimerHandle<(std::rc::Weak<DrmRenderer>, crtc::Handle)>>,
        evt_handle: Option<&LoopHandle<'static, Data>>,
    ) {
        if let Some(surface) = self.backends.borrow().get(&crtc) {
            let result = DrmRenderer::render_surface(
                &mut *surface.borrow_mut(),
                #[cfg(feature = "egl")]
                self.egl_buffer_reader.as_ref(),
                self.device_id,
                crtc,
                &mut *self.window_map.borrow_mut(),
                &mut *self.output_map.borrow_mut(),
                &self.compositor_token,
                &*self.pointer_location.borrow(),
                &self.pointer_image,
                &*self.dnd_icon.lock().unwrap(),
                &mut *self.cursor_status.lock().unwrap(),
                &self.logger,
            );
            if let Err(err) = result {
                warn!(self.logger, "Error during rendering: {:?}", err);
                let reschedule = match err {
                    SwapBuffersError::AlreadySwapped => false,
                    SwapBuffersError::TemporaryFailure(err) => !matches!(
                })
                .cloned()
                .collect::<Vec<_>>()
                .into_iter()
            {
                self.space.unmap_output(&output);
            }

                };

                if reschedule {
                    debug!(self.logger, "Rescheduling");
                    match (timer, evt_handle) {
                        (Some(handle), _) => {
                            let _ = handle.add_timeout(
                                Duration::from_millis(1000 /*a seconds*/ / 60 /*refresh rate*/),
                                (Rc::downgrade(&self), crtc),
                            );
                        }
                        (None, Some(evt_handle)) => {
                            let timer = Timer::new().unwrap();
                            let handle = timer.handle();
                            let _ = handle.add_timeout(
                                Duration::from_millis(1000 /*a seconds*/ / 60 /*refresh rate*/),
                                (Rc::downgrade(&self), crtc),
                            );
                            evt_handle
                                .insert_source(timer, |(renderer, crtc), handle, _data| {
                                    if let Some(renderer) = renderer.upgrade() {
                                        renderer.render(
                                            crtc,
                                            Some(handle.clone()),
                                            Option::<&LoopHandle<'static, Data>>::None,
                                        );
                                    }
                                })
                                .unwrap();
                        }
                        _ => unreachable!(),
                    }
                }
            } else {
                // TODO: only send drawn windows the frames callback
                &logger,
            );

            // fixup window coordinates
            }
        }
    }

    #[allow(clippy::too_many_arguments)]
    fn render_surface(
        surface: &mut RenderSurface,
        #[cfg(feature = "egl")] egl_buffer_reader: Option<&EGLBufferReader>,
        device_id: dev_t,
        crtc: crtc::Handle,
        window_map: &mut MyWindowMap,
        output_map: &mut Vec<MyOutput>,
        compositor_token: &CompositorToken<Roles>,
        pointer_location: &(f64, f64),
        pointer_image: &Gles2Texture,
        dnd_icon: &Option<wl_surface::WlSurface>,
        cursor_status: &mut CursorImageStatus,
        logger: &slog::Logger,
    ) -> Result<(), SwapBuffersError> {
        #[cfg(not(feature = "egl"))]
        let egl_buffer_reader = None;

        surface.frame_submitted()?;

        // get output coordinates
        let (x, y) = output_map
            .iter()
            .take_while(|output| output.device_id != device_id || output.crtc != crtc)
            .fold((0u32, 0u32), |pos, output| (pos.0 + output.size.0, pos.1));
        let (width, height) = output_map
            .iter()
            .find(|output| output.device_id == device_id && output.crtc == crtc)
            .map(|output| output.size)
            .unwrap_or((0, 0)); // in this case the output will be removed.

        // and draw in sync with our monitor
        surface
            .render(|renderer, frame| {
                frame.clear([0.8, 0.8, 0.9, 1.0])?;
                // draw the surfaces
                draw_windows(
                    renderer,
                    frame,
                    egl_buffer_reader,
                    window_map,
                    Some(Rectangle {
                        x: x as i32,
                        y: y as i32,
                        width: width as i32,
                        height: height as i32,
                    }),
                    *compositor_token,
                    logger,
                )?;

                // get pointer coordinates
                let (ptr_x, ptr_y) = *pointer_location;
                let ptr_x = ptr_x.trunc().abs() as i32 - x as i32;
                let ptr_y = ptr_y.trunc().abs() as i32 - y as i32;

                // set cursor
                if ptr_x >= 0 && ptr_x < width as i32 && ptr_y >= 0 && ptr_y < height as i32 {
                    // draw the dnd icon if applicable
                    {
                        if let Some(ref wl_surface) = dnd_icon.as_ref() {
                            if wl_surface.as_ref().is_alive() {
                                draw_dnd_icon(
                                    renderer,
                                    frame,
                                    wl_surface,
                                    egl_buffer_reader,
                                    (ptr_x, ptr_y),
                                    *compositor_token,
                                    logger,
                                )?;
                            }
                        }
                    }
                    // draw the cursor as relevant
                    {
                        // reset the cursor if the surface is no longer alive
                        let mut reset = false;
                        if let CursorImageStatus::Image(ref surface) = *cursor_status {
                            reset = !surface.as_ref().is_alive();
                        }
                        if reset {
                            *cursor_status = CursorImageStatus::Default;
                        }

                        if let CursorImageStatus::Image(ref wl_surface) = *cursor_status {
                            draw_cursor(
                                renderer,
                                frame,
                                wl_surface,
        for (&crtc, surface) in to_render_iter {
            // TODO get scale from the rendersurface when supporting HiDPI
                                *compositor_token,
                                logger,
                            )?;
                        } else {
                            frame.render_texture_at(pointer_image, (ptr_x, ptr_y), Transform::Normal, 1.0)?;
                        }
                    }
                }

                Ok(())
            })
            .map_err(Into::<SwapBuffersError>::into)
            .and_then(|x| x)
            .map_err(Into::<SwapBuffersError>::into)
    }
}

fn schedule_initial_render<Data: 'static>(
            }) {
                output.clone()
            } else {
                // somehow we got called with an invalid output
                continue;
            };

            let result = render_surface(
                &mut *surface.borrow_mut(),
                &mut renderer,
                &mut self.space,
                &output,
                self.seat.input_method().unwrap(),
                self.pointer_location,
                &pointer_image,
                &mut self.backend_data.pointer_element,
                &self.dnd_icon,
                &mut *self.cursor_status.lock().unwrap(),
                &self.log,
            );
            let reschedule = match &result {
                Ok((has_rendered, _)) => !has_rendered,
                Err(err) => {
                    warn!(self.log, "Error during rendering: {:?}", err);
                    match err {
                        SwapBuffersError::AlreadySwapped => false,
                        SwapBuffersError::TemporaryFailure(err) => !matches!(
                            err.downcast_ref::<DrmError>(),
                            Some(&DrmError::DeviceInactive)
                                | Some(&DrmError::Access {
                                    source: drm::SystemError::PermissionDenied,
                                    ..
                                })
                        ),
                        SwapBuffersError::ContextLost(err) => panic!("Rendering loop lost: {}", err),
                    }
                }
            };

            if reschedule {
                let timer = Timer::from_duration(Duration::from_millis(
                    1000 /*a seconds*/ / 60, /*refresh rate*/
                ));
                self.handle
                    .insert_source(timer, move |_, _, data| {
                        data.state.render(dev_id, Some(crtc));
                        TimeoutAction::Drop
                    })
                    .expect("failed to schedule frame timer");
            }

            if let Ok((_, states)) = result {
                outputs.push((output.clone(), states));
            }
        }

        std::mem::drop(surfaces);
        for (output, states) in outputs {
            // Send frame events so that client start drawing their next frame
            self.send_frames(&output, &states);
        }
    }
}

#[allow(clippy::too_many_arguments)]
fn render_surface<'a>(
    surface: &'a mut SurfaceData,
    renderer: &mut UdevRenderer<'a>,
    space: &mut Space<Window>,
    output: &Output,
    input_method: &InputMethodHandle,
    pointer_location: Point<f64, Logical>,
    pointer_image: &TextureBuffer<MultiTexture>,
    pointer_element: &mut PointerElement<MultiTexture>,
    dnd_icon: &Option<wl_surface::WlSurface>,
    cursor_status: &mut CursorImageStatus,
    logger: &slog::Logger,
) -> Result<(bool, RenderElementStates), SwapBuffersError> {
    surface.surface.frame_submitted()?;

    let output_geometry = space.output_geometry(output).unwrap();
    let scale = Scale::from(output.current_scale().fractional_scale());

    let (dmabuf, age) = surface.surface.next_buffer()?;
    renderer.bind(dmabuf)?;

    let mut elements: Vec<CustomRenderElements<_>> = Vec::new();
    // draw input method surface if any
    let rectangle = input_method.coordinates();
    let position = Point::from((
        rectangle.loc.x + rectangle.size.w,
        rectangle.loc.y + rectangle.size.h,
    ));
    input_method.with_surface(|surface| {
        elements.extend(AsRenderElements::<UdevRenderer<'a>>::render_elements(
            &SurfaceTree::from_surface(surface),
            position.to_physical_precise_round(scale),
            scale,
        ));
    });

    if output_geometry.to_f64().contains(pointer_location) {
        let cursor_hotspot = if let CursorImageStatus::Surface(ref surface) = cursor_status {
            compositor::with_states(surface, |states| {
                states
                    .data_map
                    .get::<Mutex<CursorImageAttributes>>()
                    .unwrap()
                    .lock()
                    .unwrap()
                    .hotspot
            })
        } else {
            (0, 0).into()
        };
        let cursor_pos = pointer_location - output_geometry.loc.to_f64() - cursor_hotspot.to_f64();
        let cursor_pos_scaled = cursor_pos.to_physical(scale).to_i32_round();

        // set cursor
        pointer_element.set_texture(pointer_image.clone());

        // draw the cursor as relevant
        {
            // reset the cursor if the surface is no longer alive
            let mut reset = false;
            if let CursorImageStatus::Surface(ref surface) = *cursor_status {
                reset = !surface.alive();
            }
            if reset {
                *cursor_status = CursorImageStatus::Default;
            }

            pointer_element.set_status(cursor_status.clone());
        }

        elements.extend(pointer_element.render_elements(cursor_pos_scaled, scale));

        // draw the dnd icon if applicable
        {
            if let Some(wl_surface) = dnd_icon.as_ref() {
                if wl_surface.alive() {
                    elements.extend(AsRenderElements::<UdevRenderer<'a>>::render_elements(
                        &SurfaceTree::from_surface(wl_surface),
                        cursor_pos_scaled,
                        scale,
                    ));
                }
            }
        }
    }

    #[cfg(feature = "debug")]
    {
        surface.fps_element.update_fps(surface.fps.avg().round() as u32);
        surface.fps.tick();
        elements.push(CustomRenderElements::Fps(surface.fps_element.clone()));
    }

    // and draw to our buffer
    let render_res = render_output(
        output,
        space,
        &*elements,
        renderer,
        &mut surface.damage_tracked_renderer,
        age.into(),
        logger,
    )
    .map(|(damage, states)| (damage.is_some(), states));

    match render_res.map_err(|err| match err {
        DamageTrackedRendererError::Rendering(err) => err.into(),
        _ => unreachable!(),
    }) {
        Ok((true, states)) => {
            surface
                .surface
                .queue_buffer()
                .map_err(Into::<SwapBuffersError>::into)?;
            Ok((true, states))
        }
        x => x,
    }
}

fn schedule_initial_render(
    gpus: &mut GpuManager<EglGlesBackend<Gles2Renderer>>,
    surface: Rc<RefCell<SurfaceData>>,
    evt_handle: &LoopHandle<'static, CalloopData<UdevData>>,
    logger: ::slog::Logger,
) {
    let node = surface.borrow().render_node;
    let result = {
        let mut renderer = gpus.renderer::<Gles2Renderbuffer>(&node, &node).unwrap();
        let mut surface = surface.borrow_mut();
        initial_render(&mut surface.surface, &mut renderer)
    };
    if let Err(err) = result {
        match err {
            SwapBuffersError::AlreadySwapped => {}
            SwapBuffersError::TemporaryFailure(err) => {
                // TODO dont reschedule after 3(?) retries
                warn!(logger, "Failed to submit page_flip: {}", err);
                let handle = evt_handle.clone();
                evt_handle.insert_idle(move |data| {
                    schedule_initial_render(&mut data.state.backend_data.gpus, surface, &handle, logger)
                });
            }
            SwapBuffersError::ContextLost(err) => panic!("Rendering loop lost: {}", err),
        }
    }
}

fn initial_render(
    surface: &mut RenderSurface,
    renderer: &mut UdevRenderer<'_>,
) -> Result<(), SwapBuffersError> {
    let (dmabuf, _age) = surface.next_buffer()?;
    renderer.bind(dmabuf)?;
    // Does not matter if we render an empty frame
    renderer
        .render((1, 1).into(), Transform::Normal, |_, frame| {
            frame
                .clear(CLEAR_COLOR, &[Rectangle::from_loc_and_size((0, 0), (1, 1))])
                .map_err(Into::<SwapBuffersError>::into)
        })
        .map_err(Into::<SwapBuffersError>::into)
        .and_then(|x| x.map_err(Into::<SwapBuffersError>::into))?;
    surface.queue_buffer()?;
    surface.reset_buffers();
    Ok(())
}

<MSG> anvil: flatten DrmRenderer into AnvilState

<DFF> @@ -5,7 +5,7 @@ use std::{
     os::unix::io::{AsRawFd, RawFd},
     path::PathBuf,
     rc::Rc,
-    sync::{atomic::Ordering, Arc, Mutex},
+    sync::atomic::Ordering,
     time::Duration,
 };
 
@@ -78,6 +78,7 @@ pub struct UdevData {
     backends: HashMap<dev_t, BackendData>,
     signaler: Signaler<SessionSignal>,
     pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
+    render_timer: TimerHandle<(u64, crtc::Handle)>,
 }
 
 impl Backend for UdevData {
@@ -116,6 +117,10 @@ pub fn run_udev(
      */
     let pointer_bytes = include_bytes!("../resources/cursor2.rgba");
     let primary_gpu = primary_gpu(&session.seat()).unwrap_or_default();
+
+    // setup the timer
+    let timer = Timer::new().unwrap();
+
     let data = UdevData {
         session,
         output_map: output_map.clone(),
@@ -123,6 +128,7 @@ pub fn run_udev(
         backends: HashMap::new(),
         signaler: session_signal.clone(),
         pointer_image: ImageBuffer::from_raw(64, 64, pointer_bytes.to_vec()).unwrap(),
+        render_timer: timer.handle(),
     };
     let mut state = AnvilState::init(
         display.clone(),
@@ -133,6 +139,14 @@ pub fn run_udev(
         log.clone(),
     );
 
+    // re-render timer
+    event_loop
+        .handle()
+        .insert_source(timer, |(dev_id, crtc), _, anvil_state| {
+            anvil_state.render(dev_id, Some(crtc))
+        })
+        .unwrap();
+
     /*
      * Initialize the udev backend
      */
@@ -277,6 +291,8 @@ struct BackendData {
     gbm: GbmDevice<SessionFd>,
     registration_token: RegistrationToken,
     event_dispatcher: Dispatcher<'static, DrmDevice<SessionFd>, AnvilState<UdevData>>,
+    dev_id: u64,
+    pointer_image: Gles2Texture,
 }
 
 pub fn scan_connectors(
@@ -466,49 +482,25 @@ impl AnvilState<UdevData> {
                     .expect("Failed to load pointer")
             };
 
-            // Set the handler.
-            // Note: if you replicate this (very simple) structure, it is rather easy
-            // to introduce reference cycles with Rc. Be sure about your drop order
-            let renderer = Rc::new(DrmRenderer {
-                device_id,
-                #[cfg(feature = "egl")]
-                egl_buffer_reader: if is_primary {
-                    self.egl_reader.borrow().clone()
-                } else {
-                    None
-                },
-                compositor_token: self.ctoken,
-                backends: backends.clone(),
-                window_map: self.window_map.clone(),
-                output_map: self.backend_data.output_map.clone(),
-                pointer_location: self.pointer_location.clone(),
-                pointer_image,
-                cursor_status: self.cursor_status.clone(),
-                dnd_icon: self.dnd_icon.clone(),
-                logger: self.log.clone(),
-                start_time: std::time::Instant::now(),
-            });
-            let mut listener = DrmRendererSessionListener {
-                renderer: renderer.clone(),
-                loop_handle: self.handle.clone(),
-            };
+            let dev_id = device.device_id();
+            let handle = self.handle.clone();
             let restart_token = self.backend_data.signaler.register(move |signal| match signal {
-                SessionSignal::ActivateSession | SessionSignal::ActivateDevice { .. } => listener.activate(),
+                SessionSignal::ActivateSession | SessionSignal::ActivateDevice { .. } => {
+                    handle.insert_idle(move |anvil_state| anvil_state.render(dev_id, None));
+                }
                 _ => {}
             });
-            let mut drm_handler = DrmHandlerImpl {
-                renderer,
-                loop_handle: self.handle.clone(),
-            };
 
             device.link(self.backend_data.signaler.clone());
-            let dev_id = device.device_id();
-            let event_dispatcher = Dispatcher::new(device, move |event, _, _| match event {
-                DrmEvent::VBlank(crtc) => drm_handler.vblank(crtc),
-                DrmEvent::Error(error) => {
-                    error!(drm_handler.renderer.logger, "{:?}", error);
-                }
-            });
+            let event_dispatcher = Dispatcher::new(
+                device,
+                move |event, _, anvil_state: &mut AnvilState<_>| match event {
+                    DrmEvent::VBlank(crtc) => anvil_state.render(dev_id, Some(crtc)),
+                    DrmEvent::Error(error) => {
+                        error!(anvil_state.log, "{:?}", error);
+                    }
+                },
+            );
             let registration_token = self.handle.register_dispatcher(event_dispatcher.clone()).unwrap();
 
             trace!(self.log, "Backends: {:?}", backends.borrow().keys());
@@ -528,6 +520,8 @@ impl AnvilState<UdevData> {
                     egl,
                     context,
                     gbm,
+                    pointer_image,
+                    dev_id,
                 },
             );
         }
@@ -591,80 +585,50 @@ impl AnvilState<UdevData> {
             debug!(self.log, "Dropping device");
         }
     }
-}
-
-pub struct DrmHandlerImpl<Data: 'static> {
-    renderer: Rc<DrmRenderer>,
-    loop_handle: LoopHandle<'static, Data>,
-}
-
-impl<Data: 'static> DrmHandlerImpl<Data> {
-    fn vblank(&mut self, crtc: crtc::Handle) {
-        self.renderer.clone().render(crtc, None, Some(&self.loop_handle))
-    }
-}
 
-pub struct DrmRendererSessionListener<Data: 'static> {
-    renderer: Rc<DrmRenderer>,
-    loop_handle: LoopHandle<'static, Data>,
-}
-
-impl<Data: 'static> DrmRendererSessionListener<Data> {
-    fn activate(&mut self) {
-        // we want to be called, after all session handling is done (TODO this is not so nice)
-        let renderer = self.renderer.clone();
-        let handle = self.loop_handle.clone();
-        self.loop_handle
-            .insert_idle(move |_| renderer.render_all(Some(&handle)));
-    }
-}
+    // If crtc is `Some()`, render it, else render all crtcs
+    fn render(&mut self, dev_id: u64, crtc: Option<crtc::Handle>) {
+        let device_backend = match self.backend_data.backends.get_mut(&dev_id) {
+            Some(backend) => backend,
+            None => {
+                error!(self.log, "Trying to render on non-existent backend {}", dev_id);
+                return;
+            }
+        };
+        // setup two iterators on the stack, one over all surfaces for this backend, and
+        // one containing only the one given as argument.
+        // They make a trait-object to dynamically choose between the two
+        let surfaces = device_backend.surfaces.borrow();
+        let mut surfaces_iter = surfaces.iter();
+        let mut option_iter = crtc
+            .iter()
+            .flat_map(|crtc| surfaces.get(&crtc).map(|surface| (crtc, surface)));
 
-pub struct DrmRenderer {
-    device_id: dev_t,
-    #[cfg(feature = "egl")]
-    egl_buffer_reader: Option<EGLBufferReader>,
-    compositor_token: CompositorToken<Roles>,
-    backends: Rc<RefCell<HashMap<crtc::Handle, Rc<RefCell<RenderSurface>>>>>,
-    window_map: Rc<RefCell<MyWindowMap>>,
-    output_map: Rc<RefCell<Vec<MyOutput>>>,
-    pointer_location: Rc<RefCell<(f64, f64)>>,
-    pointer_image: Gles2Texture,
-    cursor_status: Arc<Mutex<CursorImageStatus>>,
-    dnd_icon: Arc<Mutex<Option<wl_surface::WlSurface>>>,
-    logger: ::slog::Logger,
-    start_time: std::time::Instant,
-}
+        let to_render_iter: &mut dyn Iterator<Item = (&crtc::Handle, &Rc<RefCell<RenderSurface>>)> =
+            if crtc.is_some() {
+                &mut option_iter
+            } else {
+                &mut surfaces_iter
+            };
 
-impl DrmRenderer {
-    fn render_all<Data: 'static>(self: Rc<Self>, evt_handle: Option<&LoopHandle<'static, Data>>) {
-        for crtc in self.backends.borrow().keys() {
-            self.clone().render(*crtc, None, evt_handle);
-        }
-    }
-    fn render<Data: 'static>(
-        self: Rc<Self>,
-        crtc: crtc::Handle,
-        timer: Option<TimerHandle<(std::rc::Weak<DrmRenderer>, crtc::Handle)>>,
-        evt_handle: Option<&LoopHandle<'static, Data>>,
-    ) {
-        if let Some(surface) = self.backends.borrow().get(&crtc) {
-            let result = DrmRenderer::render_surface(
+        for (&crtc, surface) in to_render_iter {
+            let result = render_surface(
                 &mut *surface.borrow_mut(),
                 #[cfg(feature = "egl")]
-                self.egl_buffer_reader.as_ref(),
-                self.device_id,
+                self.egl_reader.borrow().as_ref(),
+                device_backend.dev_id,
                 crtc,
                 &mut *self.window_map.borrow_mut(),
-                &mut *self.output_map.borrow_mut(),
-                &self.compositor_token,
+                &mut *self.backend_data.output_map.borrow_mut(),
+                &self.ctoken,
                 &*self.pointer_location.borrow(),
-                &self.pointer_image,
+                &device_backend.pointer_image,
                 &*self.dnd_icon.lock().unwrap(),
                 &mut *self.cursor_status.lock().unwrap(),
-                &self.logger,
+                &self.log,
             );
             if let Err(err) = result {
-                warn!(self.logger, "Error during rendering: {:?}", err);
+                warn!(self.log, "Error during rendering: {:?}", err);
                 let reschedule = match err {
                     SwapBuffersError::AlreadySwapped => false,
                     SwapBuffersError::TemporaryFailure(err) => !matches!(
@@ -679,35 +643,11 @@ impl DrmRenderer {
                 };
 
                 if reschedule {
-                    debug!(self.logger, "Rescheduling");
-                    match (timer, evt_handle) {
-                        (Some(handle), _) => {
-                            let _ = handle.add_timeout(
-                                Duration::from_millis(1000 /*a seconds*/ / 60 /*refresh rate*/),
-                                (Rc::downgrade(&self), crtc),
-                            );
-                        }
-                        (None, Some(evt_handle)) => {
-                            let timer = Timer::new().unwrap();
-                            let handle = timer.handle();
-                            let _ = handle.add_timeout(
-                                Duration::from_millis(1000 /*a seconds*/ / 60 /*refresh rate*/),
-                                (Rc::downgrade(&self), crtc),
-                            );
-                            evt_handle
-                                .insert_source(timer, |(renderer, crtc), handle, _data| {
-                                    if let Some(renderer) = renderer.upgrade() {
-                                        renderer.render(
-                                            crtc,
-                                            Some(handle.clone()),
-                                            Option::<&LoopHandle<'static, Data>>::None,
-                                        );
-                                    }
-                                })
-                                .unwrap();
-                        }
-                        _ => unreachable!(),
-                    }
+                    debug!(self.log, "Rescheduling");
+                    self.backend_data.render_timer.add_timeout(
+                        Duration::from_millis(1000 /*a seconds*/ / 60 /*refresh rate*/),
+                        (device_backend.dev_id, crtc),
+                    );
                 }
             } else {
                 // TODO: only send drawn windows the frames callback
@@ -718,94 +658,71 @@ impl DrmRenderer {
             }
         }
     }
+}
 
-    #[allow(clippy::too_many_arguments)]
-    fn render_surface(
-        surface: &mut RenderSurface,
-        #[cfg(feature = "egl")] egl_buffer_reader: Option<&EGLBufferReader>,
-        device_id: dev_t,
-        crtc: crtc::Handle,
-        window_map: &mut MyWindowMap,
-        output_map: &mut Vec<MyOutput>,
-        compositor_token: &CompositorToken<Roles>,
-        pointer_location: &(f64, f64),
-        pointer_image: &Gles2Texture,
-        dnd_icon: &Option<wl_surface::WlSurface>,
-        cursor_status: &mut CursorImageStatus,
-        logger: &slog::Logger,
-    ) -> Result<(), SwapBuffersError> {
-        #[cfg(not(feature = "egl"))]
-        let egl_buffer_reader = None;
-
-        surface.frame_submitted()?;
-
-        // get output coordinates
-        let (x, y) = output_map
-            .iter()
-            .take_while(|output| output.device_id != device_id || output.crtc != crtc)
-            .fold((0u32, 0u32), |pos, output| (pos.0 + output.size.0, pos.1));
-        let (width, height) = output_map
-            .iter()
-            .find(|output| output.device_id == device_id && output.crtc == crtc)
-            .map(|output| output.size)
-            .unwrap_or((0, 0)); // in this case the output will be removed.
-
-        // and draw in sync with our monitor
-        surface
-            .render(|renderer, frame| {
-                frame.clear([0.8, 0.8, 0.9, 1.0])?;
-                // draw the surfaces
-                draw_windows(
-                    renderer,
-                    frame,
-                    egl_buffer_reader,
-                    window_map,
-                    Some(Rectangle {
-                        x: x as i32,
-                        y: y as i32,
-                        width: width as i32,
-                        height: height as i32,
-                    }),
-                    *compositor_token,
-                    logger,
-                )?;
-
-                // get pointer coordinates
-                let (ptr_x, ptr_y) = *pointer_location;
-                let ptr_x = ptr_x.trunc().abs() as i32 - x as i32;
-                let ptr_y = ptr_y.trunc().abs() as i32 - y as i32;
-
-                // set cursor
-                if ptr_x >= 0 && ptr_x < width as i32 && ptr_y >= 0 && ptr_y < height as i32 {
-                    // draw the dnd icon if applicable
-                    {
-                        if let Some(ref wl_surface) = dnd_icon.as_ref() {
-                            if wl_surface.as_ref().is_alive() {
-                                draw_dnd_icon(
-                                    renderer,
-                                    frame,
-                                    wl_surface,
-                                    egl_buffer_reader,
-                                    (ptr_x, ptr_y),
-                                    *compositor_token,
-                                    logger,
-                                )?;
-                            }
-                        }
-                    }
-                    // draw the cursor as relevant
-                    {
-                        // reset the cursor if the surface is no longer alive
-                        let mut reset = false;
-                        if let CursorImageStatus::Image(ref surface) = *cursor_status {
-                            reset = !surface.as_ref().is_alive();
-                        }
-                        if reset {
-                            *cursor_status = CursorImageStatus::Default;
-                        }
-
-                        if let CursorImageStatus::Image(ref wl_surface) = *cursor_status {
-                            draw_cursor(
+#[allow(clippy::too_many_arguments)]
+fn render_surface(
+    surface: &mut RenderSurface,
+    #[cfg(feature = "egl")] egl_buffer_reader: Option<&EGLBufferReader>,
+    device_id: dev_t,
+    crtc: crtc::Handle,
+    window_map: &mut MyWindowMap,
+    output_map: &mut Vec<MyOutput>,
+    compositor_token: &CompositorToken<Roles>,
+    pointer_location: &(f64, f64),
+    pointer_image: &Gles2Texture,
+    dnd_icon: &Option<wl_surface::WlSurface>,
+    cursor_status: &mut CursorImageStatus,
+    logger: &slog::Logger,
+) -> Result<(), SwapBuffersError> {
+    #[cfg(not(feature = "egl"))]
+    let egl_buffer_reader = None;
+
+    surface.frame_submitted()?;
+
+    // get output coordinates
+    let (x, y) = output_map
+        .iter()
+        .take_while(|output| output.device_id != device_id || output.crtc != crtc)
+        .fold((0u32, 0u32), |pos, output| (pos.0 + output.size.0, pos.1));
+    let (width, height) = output_map
+        .iter()
+        .find(|output| output.device_id == device_id && output.crtc == crtc)
+        .map(|output| output.size)
+        .unwrap_or((0, 0)); // in this case the output will be removed.
+
+    // and draw in sync with our monitor
+    surface
+        .render(|renderer, frame| {
+            frame.clear([0.8, 0.8, 0.9, 1.0])?;
+            // draw the surfaces
+            draw_windows(
+                renderer,
+                frame,
+                egl_buffer_reader,
+                window_map,
+                Some(Rectangle {
+                    x: x as i32,
+                    y: y as i32,
+                    width: width as i32,
+                    height: height as i32,
+                }),
+                *compositor_token,
+                logger,
+            )?;
+
+            // get pointer coordinates
+            let (ptr_x, ptr_y) = *pointer_location;
+            let ptr_x = ptr_x.trunc().abs() as i32 - x as i32;
+            let ptr_y = ptr_y.trunc().abs() as i32 - y as i32;
+
+            // set cursor
+            if ptr_x >= 0 && ptr_x < width as i32 && ptr_y >= 0 && ptr_y < height as i32 {
+                // draw the dnd icon if applicable
+                {
+                    if let Some(ref wl_surface) = dnd_icon.as_ref() {
+                        if wl_surface.as_ref().is_alive() {
+                            draw_dnd_icon(
                                 renderer,
                                 frame,
                                 wl_surface,
@@ -814,18 +731,41 @@ impl DrmRenderer {
                                 *compositor_token,
                                 logger,
                             )?;
-                        } else {
-                            frame.render_texture_at(pointer_image, (ptr_x, ptr_y), Transform::Normal, 1.0)?;
                         }
                     }
                 }
+                // draw the cursor as relevant
+                {
+                    // reset the cursor if the surface is no longer alive
+                    let mut reset = false;
+                    if let CursorImageStatus::Image(ref surface) = *cursor_status {
+                        reset = !surface.as_ref().is_alive();
+                    }
+                    if reset {
+                        *cursor_status = CursorImageStatus::Default;
+                    }
 
-                Ok(())
-            })
-            .map_err(Into::<SwapBuffersError>::into)
-            .and_then(|x| x)
-            .map_err(Into::<SwapBuffersError>::into)
-    }
+                    if let CursorImageStatus::Image(ref wl_surface) = *cursor_status {
+                        draw_cursor(
+                            renderer,
+                            frame,
+                            wl_surface,
+                            egl_buffer_reader,
+                            (ptr_x, ptr_y),
+                            *compositor_token,
+                            logger,
+                        )?;
+                    } else {
+                        frame.render_texture_at(pointer_image, (ptr_x, ptr_y), Transform::Normal, 1.0)?;
+                    }
+                }
+            }
+
+            Ok(())
+        })
+        .map_err(Into::<SwapBuffersError>::into)
+        .and_then(|x| x)
+        .map_err(Into::<SwapBuffersError>::into)
 }
 
 fn schedule_initial_render<Data: 'static>(

anvil: flatten DrmRenderer into AnvilState