x11: Move window from Metadata into events

Some events (especially from xinput) cannot be cleanly mapped to a window.
Instead of requiring every event to come with a `Window` reference, move
the window into those events, that have one.
This commit is contained in:
Victor Brekenfeld 2021-12-21 20:09:31 +01:00
parent 0afebf5fdb
commit cc6225ce21
4 changed files with 155 additions and 61 deletions

View File

@ -166,13 +166,13 @@ pub fn run_x11(log: Logger) {
event_loop
.handle()
.insert_source(backend, |event, _window, state| match event {
X11Event::CloseRequested => {
.insert_source(backend, |event, _, state| match event {
X11Event::CloseRequested { .. } => {
state.running.store(false, Ordering::SeqCst);
}
X11Event::Resized(size) => {
let size = { (size.w as i32, size.h as i32).into() };
X11Event::Resized { new_size, .. } => {
let size = { (new_size.w as i32, new_size.h as i32).into() };
state.backend_data.mode = Mode {
size,
@ -193,7 +193,7 @@ pub fn run_x11(log: Logger) {
state.backend_data.render = true;
}
X11Event::PresentCompleted | X11Event::Refresh => {
X11Event::PresentCompleted { .. } | X11Event::Refresh { .. } => {
state.backend_data.render = true;
}

View File

@ -1,5 +1,6 @@
//! Input backend implementation for the X11 backend.
use super::{window_inner::WindowInner, Window, WindowTemporary};
use crate::{
backend::input::{
self, Axis, AxisSource, ButtonState, Device, DeviceCapability, InputBackend, KeyState,
@ -7,6 +8,7 @@ use crate::{
},
utils::{Logical, Size},
};
use std::sync::Weak;
/// Marker used to define the `InputBackend` types for the X11 backend.
#[derive(Debug)]
@ -43,12 +45,22 @@ impl Device for X11VirtualDevice {
/// X11-Backend internal event wrapping `X11`'s types into a [`KeyboardKeyEvent`].
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone)]
pub struct X11KeyboardInputEvent {
pub(crate) time: u32,
pub(crate) key: u32,
pub(crate) count: u32,
pub(crate) state: KeyState,
pub(crate) window: Weak<WindowInner>,
}
impl X11KeyboardInputEvent {
/// Returns a temporary reference to the window belonging to this event.
///
/// Returns None if the window is not alive anymore.
pub fn window(&self) -> Option<impl AsRef<Window> + '_> {
self.window.upgrade().map(Window).map(WindowTemporary)
}
}
impl input::Event<X11Input> for X11KeyboardInputEvent {
@ -77,11 +89,21 @@ impl KeyboardKeyEvent<X11Input> for X11KeyboardInputEvent {
/// X11-Backend internal event wrapping `X11`'s types into a [`PointerAxisEvent`]
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone)]
pub struct X11MouseWheelEvent {
pub(crate) time: u32,
pub(crate) axis: Axis,
pub(crate) amount: f64,
pub(crate) window: Weak<WindowInner>,
}
impl X11MouseWheelEvent {
/// Returns a temporary reference to the window belonging to this event.
///
/// Returns None if the window is not alive anymore.
pub fn window(&self) -> Option<impl AsRef<Window> + '_> {
self.window.upgrade().map(Window).map(WindowTemporary)
}
}
impl input::Event<X11Input> for X11MouseWheelEvent {
@ -115,11 +137,21 @@ impl PointerAxisEvent<X11Input> for X11MouseWheelEvent {
/// X11-Backend internal event wrapping `X11`'s types into a [`PointerButtonEvent`]
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone)]
pub struct X11MouseInputEvent {
pub(crate) time: u32,
pub(crate) raw: u32,
pub(crate) state: ButtonState,
pub(crate) window: Weak<WindowInner>,
}
impl X11MouseInputEvent {
/// Returns a temporary reference to the window belonging to this event.
///
/// Returns None if the window is not alive anymore.
pub fn window(&self) -> Option<impl AsRef<Window> + '_> {
self.window.upgrade().map(Window).map(WindowTemporary)
}
}
impl input::Event<X11Input> for X11MouseInputEvent {
@ -144,12 +176,22 @@ impl PointerButtonEvent<X11Input> for X11MouseInputEvent {
/// X11-Backend internal event wrapping `X11`'s types into a [`PointerMotionAbsoluteEvent`]
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone)]
pub struct X11MouseMovedEvent {
pub(crate) time: u32,
pub(crate) x: f64,
pub(crate) y: f64,
pub(crate) size: Size<u16, Logical>,
pub(crate) window: Weak<WindowInner>,
}
impl X11MouseMovedEvent {
/// Returns a temporary reference to the window belonging to this event.
///
/// Returns None if the window is not alive anymore.
pub fn window(&self) -> Option<impl AsRef<Window> + '_> {
self.window.upgrade().map(Window).map(WindowTemporary)
}
}
impl input::Event<X11Input> for X11MouseMovedEvent {

View File

@ -129,21 +129,35 @@ pub use self::surface::*;
#[derive(Debug)]
pub enum X11Event {
/// The X server has required the compositor to redraw the contents of window.
Refresh,
Refresh {
/// XID of the window
window_id: u32,
},
/// An input event occurred.
Input(InputEvent<X11Input>),
/// The window was resized.
Resized(Size<u16, Logical>),
Resized {
/// The new size of the window
new_size: Size<u16, Logical>,
/// XID of the window
window_id: u32,
},
/// The last buffer presented to the window has been displayed.
///
/// When this event is scheduled, the next frame may be rendered.
PresentCompleted,
PresentCompleted {
/// XID of the window
window_id: u32,
},
/// The window has received a request to be closed.
CloseRequested,
CloseRequested {
/// XID of the window
window_id: u32,
},
}
/// Represents an active connection to the X to manage events on the Window provided by the backend.
@ -241,6 +255,7 @@ impl X11Backend {
atoms,
depth,
visual_id,
devices: false,
};
Ok(X11Backend {
@ -376,6 +391,14 @@ impl X11Handle {
resize: recv,
})
}
/// Get a temporary reference to a window by its XID
pub fn window_ref_from_id(&self, id: u32) -> Option<impl AsRef<Window> + '_> {
X11Inner::window_ref_from_id(&self.inner, &id)
.and_then(|w| w.upgrade())
.map(Window)
.map(WindowTemporary)
}
}
/// Builder used to construct a window.
@ -498,11 +521,19 @@ impl PartialEq for Window {
}
}
struct WindowTemporary(Window);
impl AsRef<Window> for WindowTemporary {
fn as_ref(&self) -> &Window {
&self.0
}
}
impl EventSource for X11Backend {
type Event = X11Event;
/// The window the incoming events are applicable to.
type Metadata = Window;
type Metadata = ();
type Ret = ();
@ -565,30 +596,27 @@ pub(crate) struct X11Inner {
atoms: Atoms,
depth: x11::xproto::Depth,
visual_id: u32,
devices: bool,
}
impl X11Inner {
fn window_ref_from_id(inner: &Arc<Mutex<X11Inner>>, id: &u32) -> Option<Weak<WindowInner>> {
let mut inner = inner.lock().unwrap();
inner.windows.retain(|_, weak| weak.upgrade().is_some());
inner.windows.get(id).cloned()
}
fn process_event<F>(inner: &Arc<Mutex<X11Inner>>, log: &Logger, event: x11::Event, callback: &mut F)
where
F: FnMut(X11Event, &mut Window),
F: FnMut(X11Event, &mut ()),
{
use self::X11Event::Input;
fn window_from_id(inner: &Arc<Mutex<X11Inner>>, id: &u32) -> Option<Arc<WindowInner>> {
inner
.lock()
.unwrap()
.windows
.get(id)
.cloned()
.and_then(|weak| weak.upgrade())
}
// If X11 is deadlocking somewhere here, make sure you drop your mutex guards.
match event {
x11::Event::ButtonPress(button_press) => {
if let Some(window) = window_from_id(inner, &button_press.event) {
if let Some(window) = X11Inner::window_ref_from_id(inner, &button_press.event) {
// 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
@ -631,9 +659,10 @@ impl X11Inner {
_ => unreachable!(),
},
window,
},
}),
&mut Window(window),
&mut (),
)
} else {
callback(
@ -642,9 +671,10 @@ impl X11Inner {
time: button_press.time,
raw: button_press.detail as u32,
state: ButtonState::Pressed,
window,
},
}),
&mut Window(window),
&mut (),
)
}
}
@ -658,22 +688,23 @@ impl X11Inner {
return;
}
if let Some(window) = window_from_id(inner, &button_release.event) {
if let Some(window) = X11Inner::window_ref_from_id(inner, &button_release.event) {
callback(
Input(InputEvent::PointerButton {
event: X11MouseInputEvent {
time: button_release.time,
raw: button_release.detail as u32,
state: ButtonState::Released,
window,
},
}),
&mut Window(window),
&mut (),
);
}
}
x11::Event::KeyPress(key_press) => {
if let Some(window) = window_from_id(inner, &key_press.event) {
if let Some(window) = X11Inner::window_ref_from_id(inner, &key_press.event) {
// Do not hold the lock.
let count = { inner.lock().unwrap().key_counter.fetch_add(1, Ordering::SeqCst) + 1 };
@ -689,15 +720,16 @@ impl X11Inner {
key: key_press.detail as u32 - 8,
count,
state: KeyState::Pressed,
window,
},
}),
&mut Window(window),
&mut (),
)
}
}
x11::Event::KeyRelease(key_release) => {
if let Some(window) = window_from_id(inner, &key_release.event) {
if let Some(window) = X11Inner::window_ref_from_id(inner, &key_release.event) {
let count = {
let key_counter = inner.lock().unwrap().key_counter.clone();
@ -721,15 +753,18 @@ impl X11Inner {
key: key_release.detail as u32 - 8,
count,
state: KeyState::Released,
window,
},
}),
&mut Window(window),
&mut (),
);
}
}
x11::Event::MotionNotify(motion_notify) => {
if let Some(window) = window_from_id(inner, &motion_notify.event) {
if let Some(window) =
X11Inner::window_ref_from_id(inner, &motion_notify.event).and_then(|w| w.upgrade())
{
// 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;
@ -743,15 +778,18 @@ impl X11Inner {
x,
y,
size: window_size,
window: Arc::downgrade(&window),
},
}),
&mut Window(window),
&mut (),
)
}
}
x11::Event::ConfigureNotify(configure_notify) => {
if let Some(window) = window_from_id(inner, &configure_notify.window) {
if let Some(window) =
X11Inner::window_ref_from_id(inner, &configure_notify.window).and_then(|w| w.upgrade())
{
let previous_size = { *window.size.lock().unwrap() };
// Did the size of the window change?
@ -768,8 +806,11 @@ impl X11Inner {
}
(callback)(
X11Event::Resized(configure_notify_size),
&mut Window(window.clone()),
X11Event::Resized {
new_size: configure_notify_size,
window_id: configure_notify.window,
},
&mut (),
);
if let Some(resize_sender) = window.resize.lock().unwrap().as_ref() {
@ -780,40 +821,61 @@ impl X11Inner {
}
x11::Event::EnterNotify(enter_notify) => {
if let Some(window) = window_from_id(inner, &enter_notify.event) {
if let Some(window) =
X11Inner::window_ref_from_id(inner, &enter_notify.event).and_then(|w| w.upgrade())
{
window.cursor_enter();
}
}
x11::Event::LeaveNotify(leave_notify) => {
if let Some(window) = window_from_id(inner, &leave_notify.event) {
if let Some(window) =
X11Inner::window_ref_from_id(inner, &leave_notify.event).and_then(|w| w.upgrade())
{
window.cursor_leave();
}
}
x11::Event::ClientMessage(client_message) => {
if let Some(window) = window_from_id(inner, &client_message.window) {
if let Some(window) =
X11Inner::window_ref_from_id(inner, &client_message.window).and_then(|w| w.upgrade())
{
if client_message.data.as_data32()[0] == window.atoms.WM_DELETE_WINDOW
// Destroy the window?
{
(callback)(X11Event::CloseRequested, &mut Window(window));
(callback)(
X11Event::CloseRequested {
window_id: client_message.window,
},
&mut (),
);
}
}
}
x11::Event::Expose(expose) => {
if let Some(window) = window_from_id(inner, &expose.window) {
if expose.count == 0 {
(callback)(X11Event::Refresh, &mut Window(window));
}
if expose.count == 0 {
(callback)(
X11Event::Refresh {
window_id: expose.window,
},
&mut (),
);
}
}
x11::Event::PresentCompleteNotify(complete_notify) => {
if let Some(window) = window_from_id(inner, &complete_notify.window) {
if let Some(window) =
X11Inner::window_ref_from_id(inner, &complete_notify.window).and_then(|w| w.upgrade())
{
window.last_msc.store(complete_notify.msc, Ordering::SeqCst);
(callback)(X11Event::PresentCompleted, &mut Window(window));
(callback)(
X11Event::PresentCompleted {
window_id: complete_notify.window,
},
&mut (),
);
}
}

View File

@ -19,7 +19,7 @@ use crate::{
utils::{Logical, Size},
};
use super::X11Error;
use super::{WindowTemporary, X11Error};
/// An error that may occur when presenting.
#[derive(Debug, thiserror::Error)]
@ -55,17 +55,7 @@ impl X11Surface {
///
/// This will return [`None`] if the window has been destroyed.
pub fn window(&self) -> Option<impl AsRef<Window> + '_> {
let window = self.window.upgrade().map(Window).map(WindowTemporary);
struct WindowTemporary(Window);
impl AsRef<Window> for WindowTemporary {
fn as_ref(&self) -> &Window {
&self.0
}
}
window
self.window.upgrade().map(Window).map(WindowTemporary)
}
/// Returns a handle to the GBM device used to allocate buffers.