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`
- `X11Surface::buffer` now additionally returns the age of the buffer
- `X11Surface` now has an explicit `submit` function
- `X11Surface` is now multi-window capable.
### Additions

View File

@ -12,7 +12,7 @@ use smithay::{
backend::{
egl::{EGLContext, EGLDisplay},
renderer::{gles2::Gles2Renderer, Bind, ImportEgl, Renderer, Transform, Unbind},
x11::{X11Backend, X11Event, X11Surface},
x11::{WindowBuilder, X11Backend, X11Event, X11Surface},
SwapBuffersError,
},
reexports::{
@ -59,20 +59,32 @@ pub fn run_x11(log: Logger) {
let mut event_loop = EventLoop::try_new().unwrap();
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.
let drm_node = backend
let drm_node = handle
.drm_node()
.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");
// 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 device = Arc::new(Mutex::new(device));
// Create the OpenGL context
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,
context
.dmabuf_render_formats()
@ -104,8 +116,6 @@ pub fn run_x11(log: Logger) {
}
}
let window = backend.window();
let size = {
let s = window.size();

View File

@ -25,10 +25,17 @@ pub enum X11Error {
#[error("Creating the window failed")]
CreateWindow(CreateWindowError),
/// An X11 surface already exists for this backend.
#[error("An X11 surface already exists for this backend")]
/// An X11 surface already exists for this window.
#[error("An X11 surface already exists for this window")]
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.
#[error("The X server is not capable of direct rendering")]
CannotDirectRender,

View File

@ -12,8 +12,8 @@
//! ## Example usage
//!
//! ```rust,no_run
//! # use std::error::Error;
//! # use smithay::backend::x11::{X11Backend, X11Surface};
//! # use std::{sync::{Arc, Mutex}, error::Error};
//! # use smithay::backend::x11::{X11Backend, X11Surface, WindowBuilder};
//! use smithay::backend::egl::{EGLDisplay, EGLContext};
//! use smithay::reexports::gbm;
//! use std::collections::HashSet;
@ -24,14 +24,20 @@
//! logger: slog::Logger
//! ) -> Result<(), Box<dyn Error>> {
//! // Create the backend, also yielding a surface that may be used to render to the window.
//! let mut backend = X11Backend::new(logger.clone())?;
//! // You can get a handle to the window the backend has created for later use.
//! let window = backend.window();
//! let backend = X11Backend::new(logger.clone())?;
//!
//! // 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.
//! let drm_node = backend.drm_node()?;
//! let drm_node = x_handle.drm_node()?;
//! // Create the gbm device for allocating buffers
//! let device = gbm::Device::new(drm_node)?;
//! // Initialize EGL to retrieve the support modifier list
@ -39,9 +45,11 @@
//! let context = EGLContext::new(&egl, logger).expect("Failed to create EGLContext");
//! 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
//! // 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.
//! handle.insert_source(backend, |event, _window, state| {
@ -95,11 +103,12 @@ use nix::{
};
use slog::{error, info, o, Logger};
use std::{
collections::HashMap,
io, mem,
os::unix::prelude::AsRawFd,
sync::{
atomic::{AtomicU32, Ordering},
mpsc::{self, Receiver, Sender},
mpsc::{self, Receiver},
Arc, Mutex, MutexGuard, Weak,
},
};
@ -146,14 +155,7 @@ pub struct X11Backend {
log: Logger,
connection: Arc<RustConnection>,
source: X11Source,
screen_number: usize,
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,
inner: Arc<Mutex<X11Inner>>,
}
atom_manager! {
@ -167,46 +169,8 @@ atom_manager! {
}
impl X11Backend {
/// Initializes the X11 backend.
///
/// This connects to the X server and configures the window using the default options.
/// Initializes the X11 backend by connecting to the X server.
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
L: Into<Option<slog::Logger>>,
{
@ -252,90 +216,41 @@ impl X11Backend {
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(
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,
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 {
log: logger,
source,
connection,
window,
key_counter: Arc::new(AtomicU32::new(0)),
screen_number,
resize: None,
window_format: format,
source,
inner: Arc::new(Mutex::new(inner)),
})
}
/// Returns the default screen number of the X server.
pub fn screen(&self) -> usize {
self.screen_number
/// Returns a handle to the X11 backend.
pub fn handle(&self) -> X11Handle {
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),
}
fn egl_init(_backend: &X11Backend) -> Result<DrmNode, EGLInitError> {
fn egl_init(_: &X11Inner) -> Result<DrmNode, EGLInitError> {
let display = EGLDisplay::new(&X11DefaultDisplay, None)?;
let device = EGLDevice::device_for_display(&display)?;
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)
}
fn dri3_init(backend: &X11Backend) -> Result<DrmNode, X11Error> {
let connection = &backend.connection;
fn dri3_init(x11: &X11Inner) -> Result<DrmNode, X11Error> {
let connection = &x11.connection;
// 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.
let dri3 = match connection.dri3_open(screen.root, x11rb::NONE)?.reply() {
Ok(reply) => reply,
@ -422,46 +337,119 @@ fn dri3_init(backend: &X11Backend) -> Result<DrmNode, X11Error> {
.map_err(CreateDrmNodeError::Io)
.and_then(DrmNode::from_fd)
.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
}));
}
}
slog::warn!(
&backend.log,
&x11.log,
"DRM Device does not have a render node, falling back to primary node"
);
Ok(drm_node)
}
impl X11Surface {
/// Creates a surface that allocates and presents buffers to the window managed by the backend.
/// A handle to the X11 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.
pub fn new(
backend: &mut X11Backend,
/// 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.
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>>>,
modifiers: impl Iterator<Item = DrmModifier>,
) -> 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);
}
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 window = backend.window();
let format = window.format().unwrap();
let format = window.format;
let size = window.size();
let swapchain = Swapchain::new(device, size.w as u32, size.h as u32, format, modifiers);
let (sender, recv) = mpsc::channel();
backend.resize = Some(sender);
{
let mut resize = window.resize.lock().unwrap();
*resize = Some(sender);
}
Ok(X11Surface {
connection: Arc::downgrade(&backend.connection),
window,
connection: Arc::downgrade(&inner_guard.connection),
window: window.into(),
swapchain,
format,
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.
pub fn device(&self) -> MutexGuard<'_, gbm::Device<DrmNode>> {
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.
#[derive(Debug)]
pub struct Window(Weak<WindowInner>);
@ -646,20 +707,63 @@ impl EventSource for X11Backend {
) -> io::Result<PostAction>
where
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;
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 {
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
// 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
@ -704,7 +808,7 @@ impl EventSource for X11Backend {
},
},
}),
&mut event_window,
&mut Window(Arc::downgrade(window)),
)
} else {
callback(
@ -715,14 +819,18 @@ impl EventSource for X11Backend {
state: ButtonState::Pressed,
},
}),
&mut event_window,
&mut Window(Arc::downgrade(window)),
)
}
}
}
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
// tick for scrolling and the backend will dispatch release event automatically during
// the press event.
@ -738,13 +846,15 @@ impl EventSource for X11Backend {
state: ButtonState::Released,
},
}),
&mut event_window,
&mut Window(Arc::downgrade(window)),
);
}
}
x11::Event::KeyPress(key_press) => {
if key_press.event == window.id {
if !self.windows.contains_key(&key_press.event) {
return;
}
callback(
Input(InputEvent::Keyboard {
event: X11KeyboardInputEvent {
@ -755,21 +865,23 @@ impl EventSource for X11Backend {
//
// https://github.com/freedesktop/xorg-xf86-input-libinput/blob/master/src/xf86libinput.c#L54
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,
},
}),
&mut event_window,
&mut Window(Arc::downgrade(self.windows.get(&key_press.event).unwrap())),
)
}
}
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.
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.store(key_counter_val, Ordering::SeqCst);
self.key_counter.store(key_counter_val, Ordering::SeqCst);
callback(
Input(InputEvent::Keyboard {
@ -785,33 +897,43 @@ impl EventSource for X11Backend {
state: KeyState::Released,
},
}),
&mut event_window,
&mut Window(Arc::downgrade(self.windows.get(&key_release.event).unwrap())),
);
}
}
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.
let x = motion_notify.event_x as f64;
let y = motion_notify.event_y as f64;
let window_size = { *window.size.lock().unwrap() };
callback(
Input(InputEvent::PointerMotionAbsolute {
event: X11MouseMovedEvent {
time: motion_notify.time,
x,
y,
size: window.size(),
size: window_size,
},
}),
&mut event_window,
&mut Window(Arc::downgrade(window)),
)
}
}
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() };
// Did the size of the window change?
@ -826,48 +948,73 @@ impl EventSource for X11Backend {
*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);
}
}
}
}
x11::Event::EnterNotify(enter_notify) => {
if enter_notify.event == window.id {
window.cursor_enter();
if !self.windows.contains_key(&enter_notify.event) {
return;
}
self.windows.get(&enter_notify.event).unwrap().cursor_enter();
}
x11::Event::LeaveNotify(leave_notify) => {
if leave_notify.event == window.id {
window.cursor_leave();
if !self.windows.contains_key(&leave_notify.event) {
return;
}
self.windows.get(&leave_notify.event).unwrap().cursor_leave();
}
x11::Event::ClientMessage(client_message) => {
if client_message.data.as_data32()[0] == window.atoms.WM_DELETE_WINDOW // Destroy the window?
&& client_message.window == window.id
// Same window
if !self.windows.contains_key(&client_message.window) {
return;
}
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) => {
if expose.window == window.id && expose.count == 0 {
(callback)(X11Event::Refresh, &mut event_window);
if !self.windows.contains_key(&expose.window) {
return;
}
if expose.count == 0 {
(callback)(
X11Event::Refresh,
&mut Window(Arc::downgrade(self.windows.get(&expose.window).unwrap())),
);
}
}
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);
(callback)(X11Event::PresentCompleted, &mut event_window);
}
(callback)(
X11Event::PresentCompleted,
&mut Window(Arc::downgrade(self.windows.get(&complete_notify.window).unwrap())),
);
}
x11::Event::PresentIdleNotify(_) => {
@ -875,26 +1022,10 @@ impl EventSource for X11Backend {
}
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 std::sync::{
atomic::{AtomicU32, AtomicU64},
mpsc::Sender,
Arc, Mutex, Weak,
};
use x11rb::{
@ -60,6 +61,10 @@ pub(crate) struct WindowInner {
pub atoms: Atoms,
pub cursor_state: Arc<Mutex<CursorState>>,
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 last_msc: Arc<AtomicU64>,
pub format: DrmFourcc,
@ -154,6 +159,7 @@ impl WindowInner {
format,
depth,
extensions,
resize: Mutex::new(None),
};
// Enable WM_DELETE_WINDOW so our client is not disconnected upon our toplevel window being destroyed.