<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, 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,
                encoder::Info as EncoderInfo,
            },
        },
        image::{ImageBuffer, Rgba},
        input::Libinput,
        nix::{fcntl::OFlag, sys::stat::dev_t},
        wayland_server::{
            protocol::{wl_output, wl_surface},
            Display,
        },
    },
    signaling::{Linkable, SignalToken, Signaler},
    wayland::{
        compositor::CompositorToken,
        output::{Mode, Output, PhysicalProperties},
                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},
    },
};

type UdevRenderer<'a> =
    MultiRenderer<'a, 'a, EglGlesBackend<Gles2Renderer>, EglGlesBackend<Gles2Renderer>, Gles2Renderbuffer>;

#[derive(Copy, Clone)]
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> {
    fn dmabuf_state(&mut self) -> &mut DmabufState {
        &mut self.backend_data.dmabuf_state.as_mut().unwrap().0
    }

    fn dmabuf_imported(&mut self, _global: &DmabufGlobal, dmabuf: Dmabuf) -> Result<(), ImportError> {
        self.backend_data
            .gpus
            .renderer::<Gles2Renderbuffer>(&self.backend_data.primary_gpu, &self.backend_data.primary_gpu)
            .and_then(|mut renderer| renderer.import_dmabuf(&dmabuf, None))
            .map(|_| ())
            .map_err(|_| ImportError::Failed)
    }
}
#[cfg(feature = "egl")]
delegate_dmabuf!(AnvilState<UdevData>);

impl Backend for UdevData {
    fn seat_name(&self) -> String {
        self.session.seat()
    }

    let bytes = include_bytes!("../resources/cursor2.rgba");
    let udev_backend = UdevBackend::new(state.seat_name.clone(), log.clone()).map_err(|_| ())?;

    let mut udev_handler = UdevHandlerImpl {
        compositor_token: state.ctoken,
        #[cfg(feature = "egl")]
        egl_buffer_reader,
        session: state.session.clone().unwrap(),
        backends: HashMap::new(),
        display: display.clone(),
        primary_gpu,
        window_map: state.window_map.clone(),
        {
            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 a fake output (we render one screen to every device in this example)
     */
    let (output, _output_global) = Output::new(
        &mut display.borrow_mut(),
        "Drm".into(),
        PhysicalProperties {
            width: 0,
            height: 0,
            subpixel: wl_output::Subpixel::Unknown,
            make: "Smithay".into(),
            model: "Generic DRM".into(),
        },
        log.clone(),
    );

    let (w, h) = (1920, 1080); // Hardcode full-hd res
    output.change_current_state(
        Some(Mode {
            width: w as i32,
            height: h as i32,
            refresh: 60_000,
        }),
        None,
        None,
    );
    output.set_preferred(Mode {
        width: w as i32,
        height: h as i32,
        refresh: 60_000,
    });

    /*
     * Initialize libinput backend
            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");
    Ok(())
}

struct BackendData {
    _restart_token: SignalToken,
    event_source: Source<Generic<RenderDevice>>,
            let dh = data.state.backend_data.dh.clone();
            data.state.process_input_event(&dh, event)
        })
        .unwrap();
    event_loop
        .handle()
        .insert_source(notifier, |(), &mut (), _data| {})
        .unwrap();
    for (dev, path) in udev_backend.device_list() {
    display: Rc<RefCell<Display>>,
    primary_gpu: Option<PathBuf>,
    window_map: Rc<RefCell<MyWindowMap>>,
    pointer_location: Rc<RefCell<(f64, f64)>>,
    pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
    cursor_status: Arc<Mutex<CursorImageStatus>>,
                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();

    /*
    pub fn scan_connectors(
        device: &mut RenderDevice,
        egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
        logger: &::slog::Logger,
    ) -> HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>> {
        // Get a set of all modesetting resource handles (excluding planes):

    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")]
                            egl_buffer_reader.clone(),
                            logger.clone(),
                        );

                        entry.insert(Rc::new(renderer));
                        break 'outer;
        }
    }
}

struct BackendData {
    _restart_token: SignalToken,
    surfaces: Rc<RefCell<HashMap<crtc::Handle, Rc<RefCell<SurfaceData>>>>>,
    gbm: Rc<RefCell<GbmDevice<SessionFd>>>,
    #[cfg(not(feature = "egl"))]
    pub fn scan_connectors(
        device: &mut RenderDevice,
        logger: &::slog::Logger,
    ) -> HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>> {
        // Get a set of all modesetting resource handles (excluding planes):
    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() };
                    if !backends.contains_key(&crtc) {
                        let renderer =
                            GliumDrawer::init(device.create_surface(crtc).unwrap(), logger.clone());

                        backends.insert(crtc, Rc::new(renderer));
                        break 'outer;
        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
}

impl<Data: 'static> UdevHandlerImpl<Data> {
    fn device_added(&mut self, _device: dev_t, path: PathBuf) {
        // Try to open the device
        if let Some(mut device) = self
            .session
            .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"),
            let backends = Rc::new(RefCell::new(UdevHandlerImpl::<Data>::scan_connectors(
                &mut device,
                self.egl_buffer_reader.clone(),
                &self.logger,
            )));

            #[cfg(not(feature = "egl"))]
            let backends = Rc::new(RefCell::new(UdevHandlerImpl::<Data>::scan_connectors(
                &mut device,
                &self.logger,
            )));

                    subpixel: Subpixel::Unknown,
            // 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 {
                compositor_token: self.compositor_token,
                backends: backends.clone(),
                window_map: self.window_map.clone(),
                pointer_location: self.pointer_location.clone(),
                cursor_status: self.cursor_status.clone(),
                dnd_icon: self.dnd_icon.clone(),
                logger: self.logger.clone(),
            output.change_current_state(Some(mode), None, None, Some(position));
            output.set_preferred(mode);
            space.map_output(&output, position);

            output
                .user_data()
                .insert_if_missing(|| UdevOutputId { crtc, device_id });

            let damage_tracked_renderer = DamageTrackedRenderer::from_output(&output);
            #[cfg(feature = "debug")]
            let fps_element = FpsElement::new(fps_texture.clone());

            entry.insert(Rc::new(RefCell::new(SurfaceData {
                dh: display.handle(),
                device_id,
                render_node,
                surface: gbm_surface,
                global: Some(global),
                .unwrap();

            for renderer in backends.borrow_mut().values() {
                // create cursor
                renderer
                    .borrow()
                    .set_cursor_representation(&self.pointer_image, (2, 2))
                    .unwrap();

                // render first frame
                schedule_initial_render(renderer.clone(), &self.loop_handle);
            }

            break;
        }
    }

    backends
}

impl AnvilState<UdevData> {
    fn device_added(&mut self, display: &mut Display<Self>, device_id: dev_t, path: PathBuf) {
        // Try to open the device
        let open_flags = OFlag::O_RDWR | OFlag::O_CLOEXEC | OFlag::O_NOCTTY | OFlag::O_NONBLOCK;
        let device_fd = self.backend_data.session.open(&path, open_flags).ok();
        //quick and dirty, just re-init all backends
        if let Some(ref mut backend_data) = self.backends.get_mut(&device) {
            let logger = &self.logger;
            let pointer_image = &self.pointer_image;
            let egl_buffer_reader = self.egl_buffer_reader.clone();
            let loop_handle = self.loop_handle.clone();
            self.loop_handle
                .with_source(&backend_data.event_source, |source| {
                    let mut backends = backend_data.surfaces.borrow_mut();
                    #[cfg(feature = "egl")]
                    let new_backends =
                        UdevHandlerImpl::<Data>::scan_connectors(&mut source.file, egl_buffer_reader, logger);
                    #[cfg(not(feature = "egl"))]
                    let new_backends = UdevHandlerImpl::<Data>::scan_connectors(&mut source.file, logger);
                    *backends = new_backends;

                    for renderer in backends.values() {
                        // create cursor
                        renderer
                            .borrow()
                            .set_cursor_representation(pointer_image, (2, 2))
                            .unwrap();

                        // render first frame
                        schedule_initial_render(renderer.clone(), &loop_handle);
                    }
            }
        };
        let backends = Rc::new(RefCell::new(scan_connectors(
            node,
            &device,
            &gbm,
            display,
            // drop surfaces
            backend_data.surfaces.borrow_mut().clear();
            debug!(self.logger, "Surfaces dropped");

            let device = self.loop_handle.remove(backend_data.event_source).unwrap();

        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)),
                DrmEvent::Error(error) => {
                    error!(data.state.log, "{:?}", error);
                }
            });
        let registration_token = self.handle.register_dispatcher(event_dispatcher.clone()).unwrap();

        for backend in backends.borrow_mut().values() {
            // render first frame
            trace!(self.log, "Scheduling frame");
            schedule_initial_render(
                &mut self.backend_data.gpus,
                backend.clone(),
                &self.handle,
                self.log.clone(),
            );
        }

        self.backend_data.backends.insert(
            node,
            BackendData {
                _restart_token: restart_token,
                registration_token,
                event_dispatcher,
                surfaces: backends,
                gbm,
            },
        );
    }

    fn device_changed(&mut self, display: &mut Display<Self>, device: dev_t) {
        let node = match DrmNode::from_dev_id(device).ok() {
}

pub struct DrmRenderer {
    compositor_token: CompositorToken<Roles>,
    backends: Rc<RefCell<HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>>>>,
    window_map: Rc<RefCell<MyWindowMap>>,
    pointer_location: Rc<RefCell<(f64, f64)>>,
    cursor_status: Arc<Mutex<CursorImageStatus>>,
    dnd_icon: Arc<Mutex<Option<wl_surface::WlSurface>>>,
    logger: ::slog::Logger,
                .outputs()
                .filter(|o| {
                    o.user_data()
                        .get::<UdevOutputId>()
                        .map(|id| id.device_id == node)
                        .unwrap_or(false)
                })
                .cloned()
                .collect::<Vec<_>>()
                .into_iter()
            {
                self.space.unmap_output(&output);
        evt_handle: Option<&LoopHandle<Data>>,
    ) {
        if let Some(drawer) = self.backends.borrow().get(&crtc) {
            {
                let (x, y) = *self.pointer_location.borrow();
                let _ = drawer
                    .borrow()
                    .set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32);
            }

            // and draw in sync with our monitor
            let mut frame = drawer.draw();
            frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
            crate::shell::fixup_positions(&mut self.space);
            drawer.draw_windows(
                &mut frame,
                &*self.window_map.borrow(),
                None,
                self.compositor_token,
            );
            let (x, y) = *self.pointer_location.borrow();
            // draw the dnd icon if applicable
            {
                let guard = self.dnd_icon.lock().unwrap();
                if let Some(ref surface) = *guard {
                    if surface.as_ref().is_alive() {
                        drawer.draw_dnd_icon(
                            &mut frame,
                            surface,
                            (x as i32, y as i32),
                            self.compositor_token,
                        );
                    }
                }
            }
            // draw the cursor as relevant
            {
                let mut guard = self.cursor_status.lock().unwrap();
                // reset the cursor if the surface is no longer alive
                let mut reset = false;
                if let CursorImageStatus::Image(ref surface) = *guard {
                    reset = !surface.as_ref().is_alive();
                }
                if reset {
                    *guard = CursorImageStatus::Default;
                }
                if let CursorImageStatus::Image(ref surface) = *guard {
                    drawer.draw_cursor(&mut frame, surface, (x as i32, y as i32), self.compositor_token);
                }
            }

            let result = frame.finish();
            if result.is_ok() {
                // Send frame events so that client start drawing their next frame
                self.window_map.borrow().send_frames(SCOUNTER.next_serial());
            }

            if let Err(err) = result {
                warn!(self.logger, "Error during rendering: {:?}", err);
                let reschedule = match err {
                    SwapBuffersError::AlreadySwapped => false,
                .backend_data
                .gpus
                .renderer::<Gles2Renderbuffer>(&primary_gpu, &surface.borrow().render_node)
                .unwrap();
            let pointer_images = &mut self.backend_data.pointer_images;
            let pointer_image = pointer_images
                .iter()
                .find_map(|(image, texture)| {
                    if image == &frame {
                        Some(texture.clone())
                    } else {
                        None
                    }
                })
                .unwrap_or_else(|| {
                    let texture = TextureBuffer::from_memory(
                        &mut renderer,
                        &frame.pixels_rgba,
                        (frame.width as i32, frame.height as i32),
                        false,
                        1,
                        Transform::Normal,
                        None,
                    )
                    .expect("Failed to import cursor bitmap");
                    pointer_images.push((frame, texture.clone()));
                    texture
                });

            let output = if let Some(output) = self.space.outputs().find(|o| {
                o.user_data().get::<UdevOutputId>()
                    == Some(&UdevOutputId {
                        device_id: surface.borrow().device_id,
                        crtc,
                    })
            }) {
                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,
                    }
                }
            } else {
                // Send frame events so that client start drawing their next frame
                self.window_map.borrow().send_frames(SCOUNTER.next_serial());
            }
                &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: create and render to multiple outputs independently

<DFF> @@ -45,15 +45,16 @@ use smithay::{
                 encoder::Info as EncoderInfo,
             },
         },
-        image::{ImageBuffer, Rgba},
+        image::{ImageBuffer, Rgba, Pixel},
         input::Libinput,
         nix::{fcntl::OFlag, sys::stat::dev_t},
         wayland_server::{
             protocol::{wl_output, wl_surface},
-            Display,
+            Display, Global,
         },
     },
     signaling::{Linkable, SignalToken, Signaler},
+    utils::Rectangle,
     wayland::{
         compositor::CompositorToken,
         output::{Mode, Output, PhysicalProperties},
@@ -137,12 +138,15 @@ pub fn run_udev(
     let bytes = include_bytes!("../resources/cursor2.rgba");
     let udev_backend = UdevBackend::new(state.seat_name.clone(), log.clone()).map_err(|_| ())?;
 
+    let output_map = Rc::new(RefCell::new(Vec::new()));
+
     let mut udev_handler = UdevHandlerImpl {
         compositor_token: state.ctoken,
         #[cfg(feature = "egl")]
         egl_buffer_reader,
         session: state.session.clone().unwrap(),
         backends: HashMap::new(),
+        output_map,
         display: display.clone(),
         primary_gpu,
         window_map: state.window_map.clone(),
@@ -158,34 +162,7 @@ pub fn run_udev(
     /*
      * Initialize a fake output (we render one screen to every device in this example)
      */
-    let (output, _output_global) = Output::new(
-        &mut display.borrow_mut(),
-        "Drm".into(),
-        PhysicalProperties {
-            width: 0,
-            height: 0,
-            subpixel: wl_output::Subpixel::Unknown,
-            make: "Smithay".into(),
-            model: "Generic DRM".into(),
-        },
-        log.clone(),
-    );
-
-    let (w, h) = (1920, 1080); // Hardcode full-hd res
-    output.change_current_state(
-        Some(Mode {
-            width: w as i32,
-            height: h as i32,
-            refresh: 60_000,
-        }),
-        None,
-        None,
-    );
-    output.set_preferred(Mode {
-        width: w as i32,
-        height: h as i32,
-        refresh: 60_000,
-    });
+    
 
     /*
      * Initialize libinput backend
@@ -250,6 +227,62 @@ pub fn run_udev(
     Ok(())
 }
 
+struct MyOutput {
+    device_id: dev_t,
+    crtc: crtc::Handle,
+    size: (u32, u32),
+    _wl: Output,
+    global: Option<Global<wl_output::WlOutput>>,
+}
+
+impl MyOutput {
+    fn new(display: &mut Display, device_id: dev_t, crtc: crtc::Handle, conn: ConnectorInfo, logger: ::slog::Logger) -> MyOutput {
+        let (output, global) = Output::new(
+            display,
+            format!("{:?}", conn.interface()),
+            PhysicalProperties {
+                width: conn.size().unwrap_or((0, 0)).0 as i32,
+                height: conn.size().unwrap_or((0, 0)).1 as i32,
+                subpixel: wl_output::Subpixel::Unknown,
+                make: "Smithay".into(),
+                model: "Generic DRM".into(),
+            },
+            logger,
+        );
+
+        let mode = conn.modes()[0];
+        let (w, h) = mode.size();
+        output.change_current_state(
+            Some(Mode {
+                width: w as i32,
+                height: h as i32,
+                refresh: (mode.vrefresh() * 1000) as i32,
+            }),
+            None,
+            None,
+        );
+        output.set_preferred(Mode {
+            width: w as i32,
+            height: h as i32,
+            refresh: (mode.vrefresh() * 1000) as i32,
+        });
+
+        MyOutput {
+            device_id,
+            crtc,
+            size: (w as u32, h as u32),
+            _wl: output,
+            global: Some(global),
+        }
+    }
+}
+
+impl Drop for MyOutput {
+    fn drop(&mut self) {
+        self.global.take().unwrap().destroy();
+    }
+}
+
 struct BackendData {
     _restart_token: SignalToken,
     event_source: Source<Generic<RenderDevice>>,
@@ -265,6 +298,7 @@ struct UdevHandlerImpl<Data: 'static> {
     display: Rc<RefCell<Display>>,
     primary_gpu: Option<PathBuf>,
     window_map: Rc<RefCell<MyWindowMap>>,
+    output_map: Rc<RefCell<Vec<MyOutput>>>,
     pointer_location: Rc<RefCell<(f64, f64)>>,
     pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
     cursor_status: Arc<Mutex<CursorImageStatus>>,
@@ -279,6 +313,8 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
     pub fn scan_connectors(
         device: &mut RenderDevice,
         egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
+        display: &mut Display,
+        output_map: &mut Vec<MyOutput>,
         logger: &::slog::Logger,
     ) -> HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>> {
         // Get a set of all modesetting resource handles (excluding planes):
@@ -313,6 +349,7 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
                             egl_buffer_reader.clone(),
                             logger.clone(),
                         );
+                        output_map.push(MyOutput::new(display, device.device_id(), crtc, connector_info, logger.clone()));
 
                         entry.insert(Rc::new(renderer));
                         break 'outer;
@@ -327,6 +364,8 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
     #[cfg(not(feature = "egl"))]
     pub fn scan_connectors(
         device: &mut RenderDevice,
+        display: &mut Display,
+        output_map: &mut Vec<MyOutput>,
         logger: &::slog::Logger,
     ) -> HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>> {
         // Get a set of all modesetting resource handles (excluding planes):
@@ -356,6 +395,7 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
                     if !backends.contains_key(&crtc) {
                         let renderer =
                             GliumDrawer::init(device.create_surface(crtc).unwrap(), logger.clone());
+                        output_map.push(MyOutput::new(display, device.device_id(), crtc, connector_info, logger.clone()));
 
                         backends.insert(crtc, Rc::new(renderer));
                         break 'outer;
@@ -369,7 +409,7 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
 }
 
 impl<Data: 'static> UdevHandlerImpl<Data> {
-    fn device_added(&mut self, _device: dev_t, path: PathBuf) {
+    fn device_added(&mut self, device_id: dev_t, path: PathBuf) {
         // Try to open the device
         if let Some(mut device) = self
             .session
@@ -421,12 +461,16 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
             let backends = Rc::new(RefCell::new(UdevHandlerImpl::<Data>::scan_connectors(
                 &mut device,
                 self.egl_buffer_reader.clone(),
+                &mut *self.display.borrow_mut(),
+                &mut *self.output_map.borrow_mut(),
                 &self.logger,
             )));
 
             #[cfg(not(feature = "egl"))]
             let backends = Rc::new(RefCell::new(UdevHandlerImpl::<Data>::scan_connectors(
                 &mut device,
+                &mut *self.display.borrow_mut(),
+                &mut *self.output_map.borrow_mut(),
                 &self.logger,
             )));
 
@@ -434,10 +478,13 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
             // 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,
                 compositor_token: self.compositor_token,
                 backends: backends.clone(),
                 window_map: self.window_map.clone(),
+                output_map: self.output_map.clone(),
                 pointer_location: self.pointer_location.clone(),
+                pointer_image: self.pointer_image.clone(),
                 cursor_status: self.cursor_status.clone(),
                 dnd_icon: self.dnd_icon.clone(),
                 logger: self.logger.clone(),
@@ -462,12 +509,6 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
                 .unwrap();
 
             for renderer in backends.borrow_mut().values() {
-                // create cursor
-                renderer
-                    .borrow()
-                    .set_cursor_representation(&self.pointer_image, (2, 2))
-                    .unwrap();
-
                 // render first frame
                 schedule_initial_render(renderer.clone(), &self.loop_handle);
             }
@@ -487,26 +528,31 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
         //quick and dirty, just re-init all backends
         if let Some(ref mut backend_data) = self.backends.get_mut(&device) {
             let logger = &self.logger;
-            let pointer_image = &self.pointer_image;
             let egl_buffer_reader = self.egl_buffer_reader.clone();
             let loop_handle = self.loop_handle.clone();
+            let mut display = self.display.borrow_mut();
+            let mut output_map = self.output_map.borrow_mut();
+            output_map.retain(|output| output.device_id != device);
             self.loop_handle
                 .with_source(&backend_data.event_source, |source| {
                     let mut backends = backend_data.surfaces.borrow_mut();
                     #[cfg(feature = "egl")]
                     let new_backends =
-                        UdevHandlerImpl::<Data>::scan_connectors(&mut source.file, egl_buffer_reader, logger);
+                        UdevHandlerImpl::<Data>::scan_connectors(
+                            &mut source.file,
+                            egl_buffer_reader,
+                            &mut *display,
+                            &mut *output_map,
+                            logger);
                     #[cfg(not(feature = "egl"))]
-                    let new_backends = UdevHandlerImpl::<Data>::scan_connectors(&mut source.file, logger);
+                    let new_backends = UdevHandlerImpl::<Data>::scan_connectors(
+                        &mut source.file,
+                        &mut *display,
+                        &mut *output_map,
+                        logger);
                     *backends = new_backends;
 
                     for renderer in backends.values() {
-                        // create cursor
-                        renderer
-                            .borrow()
-                            .set_cursor_representation(pointer_image, (2, 2))
-                            .unwrap();
-
                         // render first frame
                         schedule_initial_render(renderer.clone(), &loop_handle);
                     }
@@ -520,6 +566,8 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
             // drop surfaces
             backend_data.surfaces.borrow_mut().clear();
             debug!(self.logger, "Surfaces dropped");
+            // clear outputs
+            self.output_map.borrow_mut().retain(|output| output.device_id != device);
 
             let device = self.loop_handle.remove(backend_data.event_source).unwrap();
 
@@ -568,10 +616,13 @@ impl<Data: 'static> DrmRendererSessionListener<Data> {
 }
 
 pub struct DrmRenderer {
+    device_id: dev_t,
     compositor_token: CompositorToken<Roles>,
     backends: Rc<RefCell<HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>>>>,
     window_map: Rc<RefCell<MyWindowMap>>,
+    output_map: Rc<RefCell<Vec<MyOutput>>>,
     pointer_location: Rc<RefCell<(f64, f64)>>,
+    pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
     cursor_status: Arc<Mutex<CursorImageStatus>>,
     dnd_icon: Arc<Mutex<Option<wl_surface::WlSurface>>>,
     logger: ::slog::Logger,
@@ -590,13 +641,17 @@ impl DrmRenderer {
         evt_handle: Option<&LoopHandle<Data>>,
     ) {
         if let Some(drawer) = self.backends.borrow().get(&crtc) {
-            {
-                let (x, y) = *self.pointer_location.borrow();
-                let _ = drawer
-                    .borrow()
-                    .set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32);
-            }
-
+            // get output coordinates
+            let (x, y) = self.output_map.borrow()
+                .iter()
+                .take_while(|output| output.device_id != self.device_id || output.crtc != crtc)
+                .fold((0u32, 0u32), |pos, output| (pos.0 + output.size.0, pos.1));
+            let (width, height) = self.output_map.borrow()
+                .iter()
+                .find(|output| output.device_id == self.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
             let mut frame = drawer.draw();
             frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
@@ -604,47 +659,78 @@ impl DrmRenderer {
             drawer.draw_windows(
                 &mut frame,
                 &*self.window_map.borrow(),
-                None,
+                Some(Rectangle {
+                    x: x as i32, y: y as i32,
+                    width: width as i32,
+                    height: height as i32,
+                }),
                 self.compositor_token,
             );
-            let (x, y) = *self.pointer_location.borrow();
-            // draw the dnd icon if applicable
-            {
-                let guard = self.dnd_icon.lock().unwrap();
-                if let Some(ref surface) = *guard {
-                    if surface.as_ref().is_alive() {
-                        drawer.draw_dnd_icon(
-                            &mut frame,
-                            surface,
-                            (x as i32, y as i32),
-                            self.compositor_token,
-                        );
+
+            // get pointer coordinates
+            let (ptr_x, ptr_y) = *self.pointer_location.borrow();
+            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
+            // TODO hack, should be possible to clear the cursor
+            let empty = ImageBuffer::from_fn(self.pointer_image.width(), self.pointer_image.height(), |_, _| Rgba::from_channels(0, 0, 0, 0));
+            if ptr_x > 0 && ptr_x < width as i32 && ptr_y > 0 && ptr_y < height as i32 {
+                let _ = drawer
+                    .borrow()
+                    .set_cursor_position(ptr_x as u32, ptr_y as u32);
+                
+                let mut software_cursor = false;
+
+                // draw the dnd icon if applicable
+                {
+                    let guard = self.dnd_icon.lock().unwrap();
+                    if let Some(ref surface) = *guard {
+                        if surface.as_ref().is_alive() {
+                            drawer.draw_dnd_icon(
+                                &mut frame,
+                                surface,
+                                (ptr_x, ptr_y),
+                                self.compositor_token,
+                            );
+                            software_cursor = true;
+                        }
                     }
                 }
-            }
-            // draw the cursor as relevant
-            {
-                let mut guard = self.cursor_status.lock().unwrap();
-                // reset the cursor if the surface is no longer alive
-                let mut reset = false;
-                if let CursorImageStatus::Image(ref surface) = *guard {
-                    reset = !surface.as_ref().is_alive();
+                // draw the cursor as relevant
+                {
+                    let mut guard = self.cursor_status.lock().unwrap();
+                    // reset the cursor if the surface is no longer alive
+                    let mut reset = false;
+                    if let CursorImageStatus::Image(ref surface) = *guard {
+                        reset = !surface.as_ref().is_alive();
+                    }
+                    if reset {
+                        *guard = CursorImageStatus::Default;
+                    }
+                    if let CursorImageStatus::Image(ref surface) = *guard {
+                        drawer.draw_cursor(&mut frame, surface, (ptr_x, ptr_y), self.compositor_token);
+                        software_cursor = true;
+                    }
                 }
-                if reset {
-                    *guard = CursorImageStatus::Default;
+
+                // TODO: this is wasteful, only do this on change (cache previous software cursor state)
+                if software_cursor {
+                    if let Err(err) = drawer.borrow().set_cursor_representation(&empty, (2, 2)) {
+                        error!(self.logger, "Error setting cursor: {}", err);
+                    }
+                } else {
+                    if let Err(err) = drawer.borrow().set_cursor_representation(&self.pointer_image, (2, 2)) {
+                        error!(self.logger, "Error setting cursor: {}", err);
+                    }
                 }
-                if let CursorImageStatus::Image(ref surface) = *guard {
-                    drawer.draw_cursor(&mut frame, surface, (x as i32, y as i32), self.compositor_token);
+            } else {
+                if let Err(err) = drawer.borrow().set_cursor_representation(&empty, (2, 2)) {
+                    error!(self.logger, "Error setting cursor: {}", err);
                 }
             }
 
-            let result = frame.finish();
-            if result.is_ok() {
-                // Send frame events so that client start drawing their next frame
-                self.window_map.borrow().send_frames(SCOUNTER.next_serial());
-            }
-
-            if let Err(err) = result {
+            if let Err(err) = frame.finish() {
                 warn!(self.logger, "Error during rendering: {:?}", err);
                 let reschedule = match err {
                     SwapBuffersError::AlreadySwapped => false,
@@ -697,6 +783,7 @@ impl DrmRenderer {
                     }
                 }
             } else {
+                // TODO: only send drawn windows the frames callback
                 // Send frame events so that client start drawing their next frame
                 self.window_map.borrow().send_frames(SCOUNTER.next_serial());
             }

anvil: create and render to multiple outputs independently