druid-shell 0.8.0

Platform abstracting application shell used for Druid toolkit.
Documentation
// Copyright 2022 The Druid Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Multiplexing events.
//!
//! Calloop is a wrapper around `epoll` essentially allowing us to *select* multiple file
//! descriptors. We use it here to select events from a timer and from wayland.
//!
//! Based on `client-toolkit/src/event_loop.rs` in `smithay-client-toolkit` (MIT Licensed).

use calloop::{
    generic::{Fd, Generic},
    Dispatcher, EventSource, Interest, Mode,
};
use std::{cell::RefCell, io, rc::Rc};
use wayland_client::EventQueue;

use super::{application, window};

/// A wrapper around the wayland event queue that calloop knows how to select.
pub(crate) struct WaylandSource {
    appdata: std::sync::Arc<application::Data>,
    queue: Rc<RefCell<EventQueue>>,
    fd: Generic<Fd>,
}

impl WaylandSource {
    /// Wrap an `EventQueue` as a `WaylandSource`.
    pub fn new(appdata: std::sync::Arc<application::Data>) -> WaylandSource {
        let queue = appdata.wayland.queue.clone();
        let fd = queue.borrow().display().get_connection_fd();
        WaylandSource {
            appdata,
            queue,
            fd: Generic::from_fd(fd, Interest::READ, Mode::Level),
        }
    }

    /// Get a dispatcher that we can insert into our event loop.
    pub fn into_dispatcher(
        self,
    ) -> Dispatcher<
        Self,
        impl FnMut(
            window::WindowHandle,
            &mut Rc<RefCell<EventQueue>>,
            &mut std::sync::Arc<application::Data>,
        ) -> io::Result<u32>,
    > {
        Dispatcher::new(self, |_winhandle, queue, appdata| {
            queue
                .borrow_mut()
                .dispatch_pending(appdata, |event, object, _| {
                    tracing::error!(
                        "[druid-shell] Encountered an orphan event: {}@{} : {}",
                        event.interface,
                        object.as_ref().id(),
                        event.name
                    );
                    tracing::error!("all events should be handled: please raise an issue");
                })
        })
    }
}

impl EventSource for WaylandSource {
    type Event = window::WindowHandle;
    type Metadata = Rc<RefCell<EventQueue>>;
    type Ret = io::Result<u32>;

    fn process_events<F>(
        &mut self,
        ready: calloop::Readiness,
        token: calloop::Token,
        mut callback: F,
    ) -> std::io::Result<()>
    where
        F: FnMut(window::WindowHandle, &mut Rc<RefCell<EventQueue>>) -> Self::Ret,
    {
        tracing::trace!("processing events invoked {:?} {:?}", ready, token);

        self.appdata.display_flushed.replace(false);

        let winhandle = match self.appdata.acquire_current_window() {
            Some(winhandle) => winhandle,
            None => {
                tracing::error!("unable to acquire current window");
                return Ok(());
            }
        };

        // in case of readiness of the wayland socket we do the following in a loop, until nothing
        // more can be read:
        loop {
            // 1. read events from the socket if any are available
            if let Some(guard) = self.queue.borrow().prepare_read() {
                // might be None if some other thread read events before us, concurrently
                if let Err(e) = guard.read_events() {
                    if e.kind() != io::ErrorKind::WouldBlock {
                        return Err(e);
                    }
                }
            }
            tracing::trace!("processing events initiated");
            // 2. dispatch any pending event in the queue
            // propagate orphan events to the user
            let ret = callback(winhandle.clone(), &mut self.queue);
            tracing::trace!("processing events completed {:?}", ret);
            match ret {
                Ok(0) => {
                    // no events were dispatched even after reading the socket,
                    // nothing more to do, stop here
                    break;
                }
                Ok(_) => {}
                Err(e) => {
                    // in case of error, forward it and fast-exit
                    return Err(e);
                }
            }
        }

        tracing::trace!("dispatching completed, flushing");
        // 3. Once dispatching is finished, flush the responses to the compositor
        if let Err(e) = self.queue.borrow().display().flush() {
            if e.kind() != io::ErrorKind::WouldBlock {
                // in case of error, forward it and fast-exit
                return Err(e);
            }

            // WouldBlock error means the compositor could not process all our messages
            // quickly. Either it is slowed down or we are a spammer.
            // Should not really happen, if it does we do nothing and will flush again later.
            tracing::warn!("unable to flush display: {:?}", e);
        } else {
            self.appdata.display_flushed.replace(true);
        }

        tracing::trace!("event queue completed");
        Ok(())
    }

    fn register(&mut self, poll: &mut calloop::Poll, token: calloop::Token) -> std::io::Result<()> {
        self.fd.register(poll, token)
    }

    fn reregister(
        &mut self,
        poll: &mut calloop::Poll,
        token: calloop::Token,
    ) -> std::io::Result<()> {
        self.fd.reregister(poll, token)
    }

    fn unregister(&mut self, poll: &mut calloop::Poll) -> std::io::Result<()> {
        self.fd.unregister(poll)
    }
}