x11: rework impl to allow multiple windows

This commit is contained in:
i509VCB 2021-11-29 17:04:46 -06:00
parent 4f26641f8c
commit e4e964c869
5 changed files with 568 additions and 413 deletions

View File

@ -30,6 +30,7 @@
- `Present` was merged into the `X11Surface` - `Present` was merged into the `X11Surface`
- `X11Surface::buffer` now additionally returns the age of the buffer - `X11Surface::buffer` now additionally returns the age of the buffer
- `X11Surface` now has an explicit `submit` function - `X11Surface` now has an explicit `submit` function
- `X11Surface` is now multi-window capable.
### Additions ### Additions

View File

@ -12,7 +12,7 @@ use smithay::{
backend::{ backend::{
egl::{EGLContext, EGLDisplay}, egl::{EGLContext, EGLDisplay},
renderer::{gles2::Gles2Renderer, Bind, ImportEgl, Renderer, Transform, Unbind}, renderer::{gles2::Gles2Renderer, Bind, ImportEgl, Renderer, Transform, Unbind},
x11::{X11Backend, X11Event, X11Surface}, x11::{WindowBuilder, X11Backend, X11Event, X11Surface},
SwapBuffersError, SwapBuffersError,
}, },
reexports::{ reexports::{
@ -59,20 +59,32 @@ pub fn run_x11(log: Logger) {
let mut event_loop = EventLoop::try_new().unwrap(); let mut event_loop = EventLoop::try_new().unwrap();
let display = Rc::new(RefCell::new(Display::new())); let display = Rc::new(RefCell::new(Display::new()));
let mut backend = X11Backend::with_title("Anvil", log.clone()).expect("Failed to initialize X11 backend"); let backend = X11Backend::new(log.clone()).expect("Failed to initilize X11 backend");
let handle = backend.handle();
// Obtain the DRM node the X server uses for direct rendering. // Obtain the DRM node the X server uses for direct rendering.
let drm_node = backend let drm_node = handle
.drm_node() .drm_node()
.expect("Could not get DRM node used by X server"); .expect("Could not get DRM node used by X server");
// Create the gbm device for buffer allocation and the X11 surface which presents to the window. // Create the gbm device for buffer allocation.
let device = gbm::Device::new(drm_node).expect("Failed to create gbm device"); let device = gbm::Device::new(drm_node).expect("Failed to create gbm device");
// Initialize EGL using the GBM device setup earlier. // Initialize EGL using the GBM device.
let egl = EGLDisplay::new(&device, log.clone()).expect("Failed to create EGLDisplay"); let egl = EGLDisplay::new(&device, log.clone()).expect("Failed to create EGLDisplay");
let device = Arc::new(Mutex::new(device)); // Create the OpenGL context
let context = EGLContext::new(&egl, log.clone()).expect("Failed to create EGLContext"); let context = EGLContext::new(&egl, log.clone()).expect("Failed to create EGLContext");
let surface = X11Surface::new(
&mut backend, let window = WindowBuilder::new()
.title("Anvil")
.build(&handle)
.expect("Failed to create first window");
let device = Arc::new(Mutex::new(device));
// Create the surface for the window.
let surface = handle
.create_surface(
&window,
device, device,
context context
.dmabuf_render_formats() .dmabuf_render_formats()
@ -104,8 +116,6 @@ pub fn run_x11(log: Logger) {
} }
} }
let window = backend.window();
let size = { let size = {
let s = window.size(); let s = window.size();

View File

@ -25,10 +25,17 @@ pub enum X11Error {
#[error("Creating the window failed")] #[error("Creating the window failed")]
CreateWindow(CreateWindowError), CreateWindow(CreateWindowError),
/// An X11 surface already exists for this backend. /// An X11 surface already exists for this window.
#[error("An X11 surface already exists for this backend")] #[error("An X11 surface already exists for this window")]
SurfaceExists, SurfaceExists,
/// An invalid window was used to create an X11 surface.
///
/// This error will be risen if the window was destroyed or the window does not belong to the [`X11Handle`](super::X11Handle)
/// in use.
#[error("An invalid window was used to create an X11 surface")]
InvalidWindow,
/// The X server is not capable of direct rendering. /// The X server is not capable of direct rendering.
#[error("The X server is not capable of direct rendering")] #[error("The X server is not capable of direct rendering")]
CannotDirectRender, CannotDirectRender,

View File

@ -12,8 +12,8 @@
//! ## Example usage //! ## Example usage
//! //!
//! ```rust,no_run //! ```rust,no_run
//! # use std::error::Error; //! # use std::{sync::{Arc, Mutex}, error::Error};
//! # use smithay::backend::x11::{X11Backend, X11Surface}; //! # use smithay::backend::x11::{X11Backend, X11Surface, WindowBuilder};
//! use smithay::backend::egl::{EGLDisplay, EGLContext}; //! use smithay::backend::egl::{EGLDisplay, EGLContext};
//! use smithay::reexports::gbm; //! use smithay::reexports::gbm;
//! use std::collections::HashSet; //! use std::collections::HashSet;
@ -24,14 +24,20 @@
//! logger: slog::Logger //! logger: slog::Logger
//! ) -> Result<(), Box<dyn Error>> { //! ) -> Result<(), Box<dyn Error>> {
//! // Create the backend, also yielding a surface that may be used to render to the window. //! // Create the backend, also yielding a surface that may be used to render to the window.
//! let mut backend = X11Backend::new(logger.clone())?; //! let backend = X11Backend::new(logger.clone())?;
//! // You can get a handle to the window the backend has created for later use.
//! let window = backend.window();
//! //!
//! // To render to the window the X11 backend creates, we need to create an X11 surface. //! // Get a handle from the backend to interface with the X server
//! let x_handle = backend.handle();
//! // Create a window
//! let window = WindowBuilder::new()
//! .title("Wayland inside X11")
//! .build(&x_handle)
//! .expect("Could not create window");
//!
//! // To render to a window, we need to create an X11 surface.
//! //!
//! // Get the DRM node used by the X server for direct rendering. //! // Get the DRM node used by the X server for direct rendering.
//! let drm_node = backend.drm_node()?; //! let drm_node = x_handle.drm_node()?;
//! // Create the gbm device for allocating buffers //! // Create the gbm device for allocating buffers
//! let device = gbm::Device::new(drm_node)?; //! let device = gbm::Device::new(drm_node)?;
//! // Initialize EGL to retrieve the support modifier list //! // Initialize EGL to retrieve the support modifier list
@ -39,9 +45,11 @@
//! let context = EGLContext::new(&egl, logger).expect("Failed to create EGLContext"); //! let context = EGLContext::new(&egl, logger).expect("Failed to create EGLContext");
//! let modifiers = context.dmabuf_render_formats().iter().map(|format| format.modifier).collect::<HashSet<_>>(); //! let modifiers = context.dmabuf_render_formats().iter().map(|format| format.modifier).collect::<HashSet<_>>();
//! //!
//! // Wrap up the device for sharing among the created X11 surfaces.
//! let device = Arc::new(Mutex::new(device));
//! // Finally create the X11 surface, you will use this to obtain buffers that will be presented to the //! // Finally create the X11 surface, you will use this to obtain buffers that will be presented to the
//! // window. //! // window.
//! let surface = X11Surface::new(&mut backend, device, modifiers.into_iter()); //! let surface = x_handle.create_surface(&window, device, modifiers.into_iter());
//! //!
//! // Insert the backend into the event loop to receive events. //! // Insert the backend into the event loop to receive events.
//! handle.insert_source(backend, |event, _window, state| { //! handle.insert_source(backend, |event, _window, state| {
@ -95,11 +103,12 @@ use nix::{
}; };
use slog::{error, info, o, Logger}; use slog::{error, info, o, Logger};
use std::{ use std::{
collections::HashMap,
io, mem, io, mem,
os::unix::prelude::AsRawFd, os::unix::prelude::AsRawFd,
sync::{ sync::{
atomic::{AtomicU32, Ordering}, atomic::{AtomicU32, Ordering},
mpsc::{self, Receiver, Sender}, mpsc::{self, Receiver},
Arc, Mutex, MutexGuard, Weak, Arc, Mutex, MutexGuard, Weak,
}, },
}; };
@ -146,14 +155,7 @@ pub struct X11Backend {
log: Logger, log: Logger,
connection: Arc<RustConnection>, connection: Arc<RustConnection>,
source: X11Source, source: X11Source,
screen_number: usize, inner: Arc<Mutex<X11Inner>>,
window: Arc<WindowInner>,
/// Channel used to send resize notifications to the surface.
///
/// This value will be [`None`] if no surface is bound to the window managed by the backend.
resize: Option<Sender<Size<u16, Logical>>>,
key_counter: Arc<AtomicU32>,
window_format: DrmFourcc,
} }
atom_manager! { atom_manager! {
@ -167,46 +169,8 @@ atom_manager! {
} }
impl X11Backend { impl X11Backend {
/// Initializes the X11 backend. /// Initializes the X11 backend by connecting to the X server.
///
/// This connects to the X server and configures the window using the default options.
pub fn new<L>(logger: L) -> Result<X11Backend, X11Error> pub fn new<L>(logger: L) -> Result<X11Backend, X11Error>
where
L: Into<Option<::slog::Logger>>,
{
Self::with_size_and_title((1280, 800).into(), "Smithay", logger)
}
/// Initializes the X11 backend.
///
/// This connects to the X server and configures the window using the default size and the
/// specified window title.
pub fn with_title<L>(title: &str, logger: L) -> Result<X11Backend, X11Error>
where
L: Into<Option<::slog::Logger>>,
{
Self::with_size_and_title((1280, 800).into(), title, logger)
}
/// Initializes the X11 backend.
///
/// This connects to the X server and configures the window using the default window title
/// and the specified window size.
pub fn with_size<L>(size: Size<u16, Logical>, logger: L) -> Result<X11Backend, X11Error>
where
L: Into<Option<::slog::Logger>>,
{
Self::with_size_and_title(size, "Smithay", logger)
}
/// Initializes the X11 backend.
///
/// This connects to the X server and configures the window using the specified window size and title.
pub fn with_size_and_title<L>(
size: Size<u16, Logical>,
title: &str,
logger: L,
) -> Result<X11Backend, X11Error>
where where
L: Into<Option<slog::Logger>>, L: Into<Option<slog::Logger>>,
{ {
@ -252,90 +216,41 @@ impl X11Backend {
let atoms = Atoms::new(&*connection)?.reply()?; let atoms = Atoms::new(&*connection)?.reply()?;
let window = Arc::new(WindowInner::new(
Arc::downgrade(&connection),
screen,
size,
title,
format,
atoms,
depth,
visual_id,
colormap,
extensions,
)?);
let source = X11Source::new( let source = X11Source::new(
connection.clone(), connection.clone(),
window.id, 0, // send the close request to something to ensure we can wake the reader thread for events.
atoms._SMITHAY_X11_BACKEND_CLOSE, atoms._SMITHAY_X11_BACKEND_CLOSE,
logger.clone(), logger.clone(),
); );
info!(logger, "Window created"); let inner = X11Inner {
log: logger.clone(),
connection: connection.clone(),
screen_number,
windows: HashMap::new(),
key_counter: Arc::new(AtomicU32::new(0)),
window_format: format,
extensions,
colormap,
atoms,
depth,
visual_id,
};
Ok(X11Backend { Ok(X11Backend {
log: logger, log: logger,
source,
connection, connection,
window, source,
key_counter: Arc::new(AtomicU32::new(0)), inner: Arc::new(Mutex::new(inner)),
screen_number,
resize: None,
window_format: format,
}) })
} }
/// Returns the default screen number of the X server. /// Returns a handle to the X11 backend.
pub fn screen(&self) -> usize { pub fn handle(&self) -> X11Handle {
self.screen_number X11Handle {
log: self.log.clone(),
inner: self.inner.clone(),
} }
/// Returns the underlying connection to the X server.
pub fn connection(&self) -> &RustConnection {
&*self.connection
}
/// Returns a handle to the X11 window created by the backend.
pub fn window(&self) -> Window {
self.window.clone().into()
}
/// Returns the format of the window.
pub fn format(&self) -> DrmFourcc {
self.window_format
}
/// Returns the DRM node the X server uses for direct rendering.
///
/// The DRM node may be used to create a [`gbm::Device`] to allocate buffers.
pub fn drm_node(&self) -> Result<DrmNode, X11Error> {
// Kernel documentation explains why we should prefer the node to be a render node:
// https://kernel.readthedocs.io/en/latest/gpu/drm-uapi.html
//
// > Render nodes solely serve render clients, that is, no modesetting or privileged ioctls
// > can be issued on render nodes. Only non-global rendering commands are allowed. If a
// > driver supports render nodes, it must advertise it via the DRIVER_RENDER DRM driver
// > capability. If not supported, the primary node must be used for render clients together
// > with the legacy drmAuth authentication procedure.
//
// Since giving the X11 backend the ability to do modesetting is a big nono, we try to only
// ever create a gbm device from a render node.
//
// Of course if the DRM device does not support render nodes, no DRIVER_RENDER capability, then
// fall back to the primary node.
// We cannot fallback on the egl_init method, because there is no way for us to authenticate a primary node.
// dri3 does not work for closed-source drivers, but *may* give us a authenticated fd as a fallback.
// As a result we try to use egl for a cleaner, better supported approach at first and only if that fails use dri3.
egl_init(self).or_else(|err| {
slog::warn!(
&self.log,
"Failed to init X11 surface via egl, falling back to dri3: {}",
err
);
dri3_init(self)
})
} }
} }
@ -360,7 +275,7 @@ enum EGLInitError {
IO(#[from] io::Error), IO(#[from] io::Error),
} }
fn egl_init(_backend: &X11Backend) -> Result<DrmNode, EGLInitError> { fn egl_init(_: &X11Inner) -> Result<DrmNode, EGLInitError> {
let display = EGLDisplay::new(&X11DefaultDisplay, None)?; let display = EGLDisplay::new(&X11DefaultDisplay, None)?;
let device = EGLDevice::device_for_display(&display)?; let device = EGLDevice::device_for_display(&display)?;
let path = path_to_type(device.drm_device_path()?, NodeType::Render)?; let path = path_to_type(device.drm_device_path()?, NodeType::Render)?;
@ -375,11 +290,11 @@ fn egl_init(_backend: &X11Backend) -> Result<DrmNode, EGLInitError> {
.map_err(EGLInitError::IO) .map_err(EGLInitError::IO)
} }
fn dri3_init(backend: &X11Backend) -> Result<DrmNode, X11Error> { fn dri3_init(x11: &X11Inner) -> Result<DrmNode, X11Error> {
let connection = &backend.connection; let connection = &x11.connection;
// Determine which drm-device the Display is using. // Determine which drm-device the Display is using.
let screen = &connection.setup().roots[backend.screen()]; let screen = &connection.setup().roots[x11.screen_number];
// provider being NONE tells the X server to use the RandR provider. // provider being NONE tells the X server to use the RandR provider.
let dri3 = match connection.dri3_open(screen.root, x11rb::NONE)?.reply() { let dri3 = match connection.dri3_open(screen.root, x11rb::NONE)?.reply() {
Ok(reply) => reply, Ok(reply) => reply,
@ -422,46 +337,119 @@ fn dri3_init(backend: &X11Backend) -> Result<DrmNode, X11Error> {
.map_err(CreateDrmNodeError::Io) .map_err(CreateDrmNodeError::Io)
.and_then(DrmNode::from_fd) .and_then(DrmNode::from_fd)
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
slog::warn!(&backend.log, "Could not create render node from existing DRM node ({}), falling back to primary node", err); slog::warn!(&x11.log, "Could not create render node from existing DRM node ({}), falling back to primary node", err);
drm_node drm_node
})); }));
} }
} }
slog::warn!( slog::warn!(
&backend.log, &x11.log,
"DRM Device does not have a render node, falling back to primary node" "DRM Device does not have a render node, falling back to primary node"
); );
Ok(drm_node) Ok(drm_node)
} }
impl X11Surface { /// A handle to the X11 backend.
/// Creates a surface that allocates and presents buffers to the window managed by the backend. ///
/// This is the primary object used to interface with the backend.
#[derive(Debug)]
pub struct X11Handle {
log: Logger,
inner: Arc<Mutex<X11Inner>>,
}
impl X11Handle {
/// Returns the default screen number of the X server.
pub fn screen(&self) -> usize {
self.inner.lock().unwrap().screen_number
}
/// Returns the underlying connection to the X server.
pub fn connection(&self) -> Arc<RustConnection> {
self.inner.lock().unwrap().connection.clone()
}
/// Returns the format of the window.
pub fn format(&self) -> DrmFourcc {
self.inner.lock().unwrap().window_format
}
/// Returns the DRM node the X server uses for direct rendering.
/// ///
/// This will fail if the backend has already been used to create a surface. /// The DRM node may be used to create a [`gbm::Device`] to allocate buffers.
pub fn new( pub fn drm_node(&self) -> Result<DrmNode, X11Error> {
backend: &mut X11Backend, // Kernel documentation explains why we should prefer the node to be a render node:
// https://kernel.readthedocs.io/en/latest/gpu/drm-uapi.html
//
// > Render nodes solely serve render clients, that is, no modesetting or privileged ioctls
// > can be issued on render nodes. Only non-global rendering commands are allowed. If a
// > driver supports render nodes, it must advertise it via the DRIVER_RENDER DRM driver
// > capability. If not supported, the primary node must be used for render clients together
// > with the legacy drmAuth authentication procedure.
//
// Since giving the X11 backend the ability to do modesetting is a big nono, we try to only
// ever create a gbm device from a render node.
//
// Of course if the DRM device does not support render nodes, no DRIVER_RENDER capability, then
// fall back to the primary node.
// We cannot fallback on the egl_init method, because there is no way for us to authenticate a primary node.
// dri3 does not work for closed-source drivers, but *may* give us a authenticated fd as a fallback.
// As a result we try to use egl for a cleaner, better supported approach at first and only if that fails use dri3.
let inner = self.inner.lock().unwrap();
egl_init(&*inner).or_else(|err| {
slog::warn!(
&self.log,
"Failed to init X11 surface via egl, falling back to dri3: {}",
err
);
dri3_init(&*inner)
})
}
/// Creates a surface that allocates and presents buffers to the window.
///
/// This will fail if the window has already been used to create a surface.
pub fn create_surface(
&self,
window: &Window,
device: Arc<Mutex<gbm::Device<DrmNode>>>, device: Arc<Mutex<gbm::Device<DrmNode>>>,
modifiers: impl Iterator<Item = DrmModifier>, modifiers: impl Iterator<Item = DrmModifier>,
) -> Result<X11Surface, X11Error> { ) -> Result<X11Surface, X11Error> {
if backend.resize.is_some() { match window.0.upgrade() {
Some(window) => {
let has_resize = { window.resize.lock().unwrap().is_some() };
if has_resize {
return Err(X11Error::SurfaceExists); return Err(X11Error::SurfaceExists);
} }
let inner = self.inner.clone();
let inner_guard = inner.lock().unwrap();
// Fail if the window is not managed by this backend or is destroyed
if !inner_guard.windows.contains_key(&window.id) {
return Err(X11Error::InvalidWindow);
}
let modifiers = modifiers.collect::<Vec<_>>(); let modifiers = modifiers.collect::<Vec<_>>();
let window = backend.window(); let format = window.format;
let format = window.format().unwrap();
let size = window.size(); let size = window.size();
let swapchain = Swapchain::new(device, size.w as u32, size.h as u32, format, modifiers); let swapchain = Swapchain::new(device, size.w as u32, size.h as u32, format, modifiers);
let (sender, recv) = mpsc::channel(); let (sender, recv) = mpsc::channel();
backend.resize = Some(sender); {
let mut resize = window.resize.lock().unwrap();
*resize = Some(sender);
}
Ok(X11Surface { Ok(X11Surface {
connection: Arc::downgrade(&backend.connection), connection: Arc::downgrade(&inner_guard.connection),
window, window: window.into(),
swapchain, swapchain,
format, format,
width: size.w, width: size.w,
@ -471,6 +459,17 @@ impl X11Surface {
}) })
} }
None => Err(X11Error::InvalidWindow),
}
}
}
impl X11Surface {
/// Returns the window the surface presents to.
pub fn window(&self) -> &Window {
&self.window
}
/// Returns a handle to the GBM device used to allocate buffers. /// Returns a handle to the GBM device used to allocate buffers.
pub fn device(&self) -> MutexGuard<'_, gbm::Device<DrmNode>> { pub fn device(&self) -> MutexGuard<'_, gbm::Device<DrmNode>> {
self.swapchain.allocator.lock().unwrap() self.swapchain.allocator.lock().unwrap()
@ -560,6 +559,68 @@ impl X11Surface {
} }
} }
/// Builder used to construct a window.
#[derive(Debug)]
pub struct WindowBuilder<'a> {
name: Option<&'a str>,
size: Option<Size<u16, Logical>>,
}
impl<'a> WindowBuilder<'a> {
#[allow(clippy::new_without_default)]
/// Returns a new builder.
pub fn new() -> WindowBuilder<'a> {
WindowBuilder {
name: None,
size: None,
}
}
/// Sets the title of the window that will be created by the builder.
pub fn title(self, name: &'a str) -> Self {
Self {
name: Some(name),
..self
}
}
/// Sets the size of the window that will be created.
///
/// There is no guarantee the size specified here will be the actual size of the window when it is
/// presented.
pub fn size(self, size: Size<u16, Logical>) -> Self {
Self {
size: Some(size),
..self
}
}
/// Creates a window using the options specified in the builder.
pub fn build(self, handle: &X11Handle) -> Result<Window, X11Error> {
let connection = handle.connection();
let inner = &mut *handle.inner.lock().unwrap();
let window = Arc::new(WindowInner::new(
Arc::downgrade(&connection),
&connection.setup().roots[inner.screen_number],
self.size.unwrap_or_else(|| (1280, 800).into()),
self.name.unwrap_or("Smithay"),
inner.window_format,
inner.atoms,
inner.depth.clone(),
inner.visual_id,
inner.colormap,
inner.extensions,
)?);
let downgrade = Arc::downgrade(&window);
inner.windows.insert(window.id, window);
Ok(Window(downgrade))
}
}
/// An X11 window. /// An X11 window.
#[derive(Debug)] #[derive(Debug)]
pub struct Window(Weak<WindowInner>); pub struct Window(Weak<WindowInner>);
@ -646,20 +707,63 @@ impl EventSource for X11Backend {
) -> io::Result<PostAction> ) -> io::Result<PostAction>
where where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret, F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
let connection = self.connection.clone();
let inner = self.inner.clone();
let mut inner = inner.lock().unwrap();
let post_action = self.source.process_events(readiness, token, |event, _| {
inner.process_event(event, &mut callback);
})?;
// Flush the connection so changes to the window state during callbacks can be emitted.
let _ = connection.flush();
Ok(post_action)
}
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> io::Result<()> {
self.source.register(poll, token_factory)
}
fn reregister(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> io::Result<()> {
self.source.reregister(poll, token_factory)
}
fn unregister(&mut self, poll: &mut Poll) -> io::Result<()> {
self.source.unregister(poll)
}
}
#[derive(Debug)]
pub(crate) struct X11Inner {
log: Logger,
connection: Arc<RustConnection>,
screen_number: usize,
windows: HashMap<u32, Arc<WindowInner>>,
key_counter: Arc<AtomicU32>,
window_format: DrmFourcc,
extensions: Extensions,
colormap: u32,
atoms: Atoms,
depth: x11::xproto::Depth,
visual_id: u32,
}
impl X11Inner {
pub fn process_event<F>(&mut self, event: x11::Event, callback: &mut F)
where
F: FnMut(X11Event, &mut Window),
{ {
use self::X11Event::Input; use self::X11Event::Input;
let connection = self.connection.clone();
let window = self.window.clone();
let key_counter = self.key_counter.clone();
let log = self.log.clone();
let mut event_window = window.clone().into();
let resize = &self.resize;
self.source.process_events(readiness, token, |event, _| {
match event { match event {
x11::Event::ButtonPress(button_press) => { x11::Event::ButtonPress(button_press) => {
if button_press.event == window.id { if !self.windows.contains_key(&button_press.event) {
return;
}
let window = self.windows.get(&button_press.event).unwrap();
// X11 decided to associate scroll wheel with a button, 4, 5, 6 and 7 for // X11 decided to associate scroll wheel with a button, 4, 5, 6 and 7 for
// up, down, right and left. For scrolling, a press event is emitted and a // up, down, right and left. For scrolling, a press event is emitted and a
// release is them immediately followed for scrolling. This means we can // release is them immediately followed for scrolling. This means we can
@ -704,7 +808,7 @@ impl EventSource for X11Backend {
}, },
}, },
}), }),
&mut event_window, &mut Window(Arc::downgrade(window)),
) )
} else { } else {
callback( callback(
@ -715,14 +819,18 @@ impl EventSource for X11Backend {
state: ButtonState::Pressed, state: ButtonState::Pressed,
}, },
}), }),
&mut event_window, &mut Window(Arc::downgrade(window)),
) )
} }
} }
}
x11::Event::ButtonRelease(button_release) => { x11::Event::ButtonRelease(button_release) => {
if button_release.event == window.id { if !self.windows.contains_key(&button_release.event) {
return;
}
let window = self.windows.get(&button_release.event).unwrap();
// Ignore release tick because this event is always sent immediately after the press // Ignore release tick because this event is always sent immediately after the press
// tick for scrolling and the backend will dispatch release event automatically during // tick for scrolling and the backend will dispatch release event automatically during
// the press event. // the press event.
@ -738,13 +846,15 @@ impl EventSource for X11Backend {
state: ButtonState::Released, state: ButtonState::Released,
}, },
}), }),
&mut event_window, &mut Window(Arc::downgrade(window)),
); );
} }
}
x11::Event::KeyPress(key_press) => { x11::Event::KeyPress(key_press) => {
if key_press.event == window.id { if !self.windows.contains_key(&key_press.event) {
return;
}
callback( callback(
Input(InputEvent::Keyboard { Input(InputEvent::Keyboard {
event: X11KeyboardInputEvent { event: X11KeyboardInputEvent {
@ -755,21 +865,23 @@ impl EventSource for X11Backend {
// //
// https://github.com/freedesktop/xorg-xf86-input-libinput/blob/master/src/xf86libinput.c#L54 // https://github.com/freedesktop/xorg-xf86-input-libinput/blob/master/src/xf86libinput.c#L54
key: key_press.detail as u32 - 8, key: key_press.detail as u32 - 8,
count: key_counter.fetch_add(1, Ordering::SeqCst) + 1, count: self.key_counter.fetch_add(1, Ordering::SeqCst) + 1,
state: KeyState::Pressed, state: KeyState::Pressed,
}, },
}), }),
&mut event_window, &mut Window(Arc::downgrade(self.windows.get(&key_press.event).unwrap())),
) )
} }
}
x11::Event::KeyRelease(key_release) => { x11::Event::KeyRelease(key_release) => {
if key_release.event == window.id { if !self.windows.contains_key(&key_release.event) {
return;
}
// atomic u32 has no checked_sub, so load and store to do the same. // atomic u32 has no checked_sub, so load and store to do the same.
let mut key_counter_val = key_counter.load(Ordering::SeqCst); let mut key_counter_val = self.key_counter.load(Ordering::SeqCst);
key_counter_val = key_counter_val.saturating_sub(1); key_counter_val = key_counter_val.saturating_sub(1);
key_counter.store(key_counter_val, Ordering::SeqCst); self.key_counter.store(key_counter_val, Ordering::SeqCst);
callback( callback(
Input(InputEvent::Keyboard { Input(InputEvent::Keyboard {
@ -785,33 +897,43 @@ impl EventSource for X11Backend {
state: KeyState::Released, state: KeyState::Released,
}, },
}), }),
&mut event_window, &mut Window(Arc::downgrade(self.windows.get(&key_release.event).unwrap())),
); );
} }
}
x11::Event::MotionNotify(motion_notify) => { x11::Event::MotionNotify(motion_notify) => {
if motion_notify.event == window.id { if !self.windows.contains_key(&motion_notify.event) {
return;
}
let window = self.windows.get(&motion_notify.event).unwrap();
// Use event_x/y since those are relative the the window receiving events. // Use event_x/y since those are relative the the window receiving events.
let x = motion_notify.event_x as f64; let x = motion_notify.event_x as f64;
let y = motion_notify.event_y as f64; let y = motion_notify.event_y as f64;
let window_size = { *window.size.lock().unwrap() };
callback( callback(
Input(InputEvent::PointerMotionAbsolute { Input(InputEvent::PointerMotionAbsolute {
event: X11MouseMovedEvent { event: X11MouseMovedEvent {
time: motion_notify.time, time: motion_notify.time,
x, x,
y, y,
size: window.size(), size: window_size,
}, },
}), }),
&mut event_window, &mut Window(Arc::downgrade(window)),
) )
} }
}
x11::Event::ConfigureNotify(configure_notify) => { x11::Event::ConfigureNotify(configure_notify) => {
if configure_notify.window == window.id { if !self.windows.contains_key(&configure_notify.window) {
return;
}
let window = self.windows.get(&configure_notify.window).unwrap();
let previous_size = { *window.size.lock().unwrap() }; let previous_size = { *window.size.lock().unwrap() };
// Did the size of the window change? // Did the size of the window change?
@ -826,48 +948,73 @@ impl EventSource for X11Backend {
*window.size.lock().unwrap() = configure_notify_size; *window.size.lock().unwrap() = configure_notify_size;
} }
(callback)(X11Event::Resized(configure_notify_size), &mut event_window); (callback)(
X11Event::Resized(configure_notify_size),
&mut Window(Arc::downgrade(window)),
);
if let Some(resize_sender) = resize { if let Some(resize_sender) = &*window.resize.lock().unwrap() {
let _ = resize_sender.send(configure_notify_size); let _ = resize_sender.send(configure_notify_size);
} }
} }
} }
}
x11::Event::EnterNotify(enter_notify) => { x11::Event::EnterNotify(enter_notify) => {
if enter_notify.event == window.id { if !self.windows.contains_key(&enter_notify.event) {
window.cursor_enter(); return;
} }
self.windows.get(&enter_notify.event).unwrap().cursor_enter();
} }
x11::Event::LeaveNotify(leave_notify) => { x11::Event::LeaveNotify(leave_notify) => {
if leave_notify.event == window.id { if !self.windows.contains_key(&leave_notify.event) {
window.cursor_leave(); return;
} }
self.windows.get(&leave_notify.event).unwrap().cursor_leave();
} }
x11::Event::ClientMessage(client_message) => { x11::Event::ClientMessage(client_message) => {
if client_message.data.as_data32()[0] == window.atoms.WM_DELETE_WINDOW // Destroy the window? if !self.windows.contains_key(&client_message.window) {
&& client_message.window == window.id return;
// Same window }
let window = self.windows.get(&client_message.window).unwrap();
if client_message.data.as_data32()[0] == window.atoms.WM_DELETE_WINDOW
// Destroy the window?
{ {
(callback)(X11Event::CloseRequested, &mut event_window); (callback)(X11Event::CloseRequested, &mut Window(Arc::downgrade(window)));
} }
} }
x11::Event::Expose(expose) => { x11::Event::Expose(expose) => {
if expose.window == window.id && expose.count == 0 { if !self.windows.contains_key(&expose.window) {
(callback)(X11Event::Refresh, &mut event_window); return;
}
if expose.count == 0 {
(callback)(
X11Event::Refresh,
&mut Window(Arc::downgrade(self.windows.get(&expose.window).unwrap())),
);
} }
} }
x11::Event::PresentCompleteNotify(complete_notify) => { x11::Event::PresentCompleteNotify(complete_notify) => {
if complete_notify.window == window.id { if !self.windows.contains_key(&complete_notify.window) {
return;
}
let window = self.windows.get(&complete_notify.window).unwrap();
window.last_msc.store(complete_notify.msc, Ordering::SeqCst); window.last_msc.store(complete_notify.msc, Ordering::SeqCst);
(callback)(X11Event::PresentCompleted, &mut event_window); (callback)(
} X11Event::PresentCompleted,
&mut Window(Arc::downgrade(self.windows.get(&complete_notify.window).unwrap())),
);
} }
x11::Event::PresentIdleNotify(_) => { x11::Event::PresentIdleNotify(_) => {
@ -875,26 +1022,10 @@ impl EventSource for X11Backend {
} }
x11::Event::Error(e) => { x11::Event::Error(e) => {
error!(log, "X11 protocol error: {:?}", e); error!(self.log, "X11 protocol error: {:?}", e);
} }
_ => (), _ => (),
} }
// Flush the connection so changes to the window state during callbacks can be emitted.
let _ = connection.flush();
})
}
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> io::Result<()> {
self.source.register(poll, token_factory)
}
fn reregister(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> io::Result<()> {
self.source.reregister(poll, token_factory)
}
fn unregister(&mut self, poll: &mut Poll) -> io::Result<()> {
self.source.unregister(poll)
} }
} }

View File

@ -15,6 +15,7 @@ use super::{extension::Extensions, Atoms, Window, X11Error};
use drm_fourcc::DrmFourcc; use drm_fourcc::DrmFourcc;
use std::sync::{ use std::sync::{
atomic::{AtomicU32, AtomicU64}, atomic::{AtomicU32, AtomicU64},
mpsc::Sender,
Arc, Mutex, Weak, Arc, Mutex, Weak,
}; };
use x11rb::{ use x11rb::{
@ -60,6 +61,10 @@ pub(crate) struct WindowInner {
pub atoms: Atoms, pub atoms: Atoms,
pub cursor_state: Arc<Mutex<CursorState>>, pub cursor_state: Arc<Mutex<CursorState>>,
pub size: Mutex<Size<u16, Logical>>, pub size: Mutex<Size<u16, Logical>>,
/// Channel used to send resize notifications to the surface that presents to this window.
///
/// This value will be [`None`] if no surface is bound to the window.
pub resize: Mutex<Option<Sender<Size<u16, Logical>>>>,
pub next_serial: AtomicU32, pub next_serial: AtomicU32,
pub last_msc: Arc<AtomicU64>, pub last_msc: Arc<AtomicU64>,
pub format: DrmFourcc, pub format: DrmFourcc,
@ -154,6 +159,7 @@ impl WindowInner {
format, format,
depth, depth,
extensions, extensions,
resize: Mutex::new(None),
}; };
// Enable WM_DELETE_WINDOW so our client is not disconnected upon our toplevel window being destroyed. // Enable WM_DELETE_WINDOW so our client is not disconnected upon our toplevel window being destroyed.