Merge pull request #426 from i509VCB/x11/multi-window
This commit is contained in:
commit
1088e62a6f
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration};
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
rc::Rc,
|
||||||
|
sync::{atomic::Ordering, Arc, Mutex},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use slog::Logger;
|
use slog::Logger;
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
|
@ -7,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::{
|
||||||
|
@ -54,19 +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");
|
||||||
|
// 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()
|
||||||
|
@ -98,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();
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,12 @@ pub mod dumb;
|
||||||
pub mod gbm;
|
pub mod gbm;
|
||||||
|
|
||||||
mod swapchain;
|
mod swapchain;
|
||||||
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
rc::Rc,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::utils::{Buffer as BufferCoords, Size};
|
use crate::utils::{Buffer as BufferCoords, Size};
|
||||||
pub use swapchain::{Slot, Swapchain};
|
pub use swapchain::{Slot, Swapchain};
|
||||||
|
|
||||||
|
@ -60,3 +66,34 @@ pub trait Allocator<B: Buffer> {
|
||||||
modifiers: &[Modifier],
|
modifiers: &[Modifier],
|
||||||
) -> Result<B, Self::Error>;
|
) -> Result<B, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// General implementations for interior mutability.
|
||||||
|
|
||||||
|
impl<A: Allocator<B>, B: Buffer> Allocator<B> for Arc<Mutex<A>> {
|
||||||
|
type Error = A::Error;
|
||||||
|
|
||||||
|
fn create_buffer(
|
||||||
|
&mut self,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
fourcc: Fourcc,
|
||||||
|
modifiers: &[Modifier],
|
||||||
|
) -> Result<B, Self::Error> {
|
||||||
|
let mut guard = self.lock().unwrap();
|
||||||
|
guard.create_buffer(width, height, fourcc, modifiers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: Allocator<B>, B: Buffer> Allocator<B> for Rc<RefCell<A>> {
|
||||||
|
type Error = A::Error;
|
||||||
|
|
||||||
|
fn create_buffer(
|
||||||
|
&mut self,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
fourcc: Fourcc,
|
||||||
|
modifiers: &[Modifier],
|
||||||
|
) -> Result<B, Self::Error> {
|
||||||
|
self.borrow_mut().create_buffer(width, height, fourcc, modifiers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,12 +103,13 @@ 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, Weak,
|
Arc, Mutex, MutexGuard, Weak,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use x11rb::{
|
use x11rb::{
|
||||||
|
@ -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)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +260,7 @@ pub struct X11Surface {
|
||||||
connection: Weak<RustConnection>,
|
connection: Weak<RustConnection>,
|
||||||
window: Window,
|
window: Window,
|
||||||
resize: Receiver<Size<u16, Logical>>,
|
resize: Receiver<Size<u16, Logical>>,
|
||||||
swapchain: Swapchain<gbm::Device<DrmNode>, BufferObject<()>>,
|
swapchain: Swapchain<Arc<Mutex<gbm::Device<DrmNode>>>, BufferObject<()>>,
|
||||||
format: DrmFourcc,
|
format: DrmFourcc,
|
||||||
width: u16,
|
width: u16,
|
||||||
height: u16,
|
height: u16,
|
||||||
|
@ -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 will fail if the backend has already been used to create a surface.
|
/// This is the primary object used to interface with the backend.
|
||||||
pub fn new(
|
#[derive(Debug)]
|
||||||
backend: &mut X11Backend,
|
pub struct X11Handle {
|
||||||
device: gbm::Device<DrmNode>,
|
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.
|
||||||
|
///
|
||||||
|
/// 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>,
|
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,9 +459,20 @@ 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) -> &gbm::Device<DrmNode> {
|
pub fn device(&self) -> MutexGuard<'_, gbm::Device<DrmNode>> {
|
||||||
&self.swapchain.allocator
|
self.swapchain.allocator.lock().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the format of the buffers the surface accepts.
|
/// Returns the format of the buffers the surface accepts.
|
||||||
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue