mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1dad450ee | ||
|
|
8d66df7f6f | ||
|
|
1c5fcf3309 | ||
|
|
58c89c1ffc | ||
|
|
2dae807c4f |
@@ -11,6 +11,15 @@ Unreleased` header.
|
||||
|
||||
# Unreleased
|
||||
|
||||
# 0.29.13
|
||||
|
||||
- On Web, fix possible crash with `ControlFlow::Wait` and `ControlFlow::WaitUntil`.
|
||||
|
||||
# 0.29.12
|
||||
|
||||
- On X11, fix use after free during xinput2 handling.
|
||||
- On X11, filter close to zero values in mouse device events
|
||||
|
||||
# 0.29.11
|
||||
|
||||
- On Wayland, fix DeviceEvent::Motion not being sent
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.29.11"
|
||||
version = "0.29.13"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
edition = "2021"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.29.11"
|
||||
winit = "0.29.13"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
@@ -156,7 +156,7 @@ For more details, refer to these `android-activity` [example applications](https
|
||||
|
||||
If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk`, then the minimal changes would be:
|
||||
1. Remove `ndk-glue` from your `Cargo.toml`
|
||||
2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.11", features = [ "android-native-activity" ] }`
|
||||
2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.13", features = [ "android-native-activity" ] }`
|
||||
3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize logging as above).
|
||||
4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above).
|
||||
|
||||
|
||||
@@ -33,9 +33,10 @@ use crate::platform_impl::platform::common::xkb::Context;
|
||||
use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventReceiver, ImeRequest};
|
||||
use crate::platform_impl::platform::x11::EventLoopWindowTarget;
|
||||
use crate::platform_impl::platform::EventLoopWindowTarget as PlatformEventLoopWindowTarget;
|
||||
use crate::platform_impl::x11::util::cookie::GenericEventCookie;
|
||||
use crate::platform_impl::x11::{
|
||||
atoms::*, mkdid, mkwid, util, CookieResultExt, Device, DeviceId, DeviceInfo, Dnd, DndState,
|
||||
GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId,
|
||||
ImeReceiver, ScrollOrientation, UnownedWindow, WindowId,
|
||||
};
|
||||
|
||||
/// The maximum amount of X modifiers to replay.
|
||||
@@ -184,14 +185,15 @@ impl<T: 'static> EventProcessor<T> {
|
||||
}
|
||||
xlib::GenericEvent => {
|
||||
let wt = Self::window_target(&self.target);
|
||||
let xev = match GenericEventCookie::from_event(&wt.xconn, *xev) {
|
||||
Some(xev) if xev.cookie.extension as u8 == self.xi2ext.major_opcode => {
|
||||
xev.cookie
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
let xev: GenericEventCookie =
|
||||
match GenericEventCookie::from_event(wt.xconn.clone(), *xev) {
|
||||
Some(xev) if xev.extension() == self.xi2ext.major_opcode => xev,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
match xev.evtype {
|
||||
let evtype = xev.evtype();
|
||||
|
||||
match evtype {
|
||||
ty @ xinput2::XI_ButtonPress | ty @ xinput2::XI_ButtonRelease => {
|
||||
let state = if ty == xinput2::XI_ButtonPress {
|
||||
ElementState::Pressed
|
||||
@@ -199,7 +201,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
ElementState::Released
|
||||
};
|
||||
|
||||
let xev: &XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
||||
let xev: &XIDeviceEvent = unsafe { xev.as_event() };
|
||||
self.update_mods_from_xinput2_event(
|
||||
&xev.mods,
|
||||
&xev.group,
|
||||
@@ -209,7 +211,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
self.xinput2_button_input(xev, state, &mut callback);
|
||||
}
|
||||
xinput2::XI_Motion => {
|
||||
let xev: &XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
||||
let xev: &XIDeviceEvent = unsafe { xev.as_event() };
|
||||
self.update_mods_from_xinput2_event(
|
||||
&xev.mods,
|
||||
&xev.group,
|
||||
@@ -219,11 +221,11 @@ impl<T: 'static> EventProcessor<T> {
|
||||
self.xinput2_mouse_motion(xev, &mut callback);
|
||||
}
|
||||
xinput2::XI_Enter => {
|
||||
let xev: &XIEnterEvent = unsafe { &*(xev.data as *const _) };
|
||||
let xev: &XIEnterEvent = unsafe { xev.as_event() };
|
||||
self.xinput2_mouse_enter(xev, &mut callback);
|
||||
}
|
||||
xinput2::XI_Leave => {
|
||||
let xev: &XILeaveEvent = unsafe { &*(xev.data as *const _) };
|
||||
let xev: &XILeaveEvent = unsafe { xev.as_event() };
|
||||
self.update_mods_from_xinput2_event(
|
||||
&xev.mods,
|
||||
&xev.group,
|
||||
@@ -233,51 +235,51 @@ impl<T: 'static> EventProcessor<T> {
|
||||
self.xinput2_mouse_left(xev, &mut callback);
|
||||
}
|
||||
xinput2::XI_FocusIn => {
|
||||
let xev: &XIFocusInEvent = unsafe { &*(xev.data as *const _) };
|
||||
let xev: &XIFocusInEvent = unsafe { xev.as_event() };
|
||||
self.xinput2_focused(xev, &mut callback);
|
||||
}
|
||||
xinput2::XI_FocusOut => {
|
||||
let xev: &XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
|
||||
let xev: &XIFocusOutEvent = unsafe { xev.as_event() };
|
||||
self.xinput2_unfocused(xev, &mut callback);
|
||||
}
|
||||
xinput2::XI_TouchBegin | xinput2::XI_TouchUpdate | xinput2::XI_TouchEnd => {
|
||||
let phase = match xev.evtype {
|
||||
let phase = match evtype {
|
||||
xinput2::XI_TouchBegin => TouchPhase::Started,
|
||||
xinput2::XI_TouchUpdate => TouchPhase::Moved,
|
||||
xinput2::XI_TouchEnd => TouchPhase::Ended,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let xev: &XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
||||
let xev: &XIDeviceEvent = unsafe { xev.as_event() };
|
||||
self.xinput2_touch(xev, phase, &mut callback);
|
||||
}
|
||||
xinput2::XI_RawButtonPress | xinput2::XI_RawButtonRelease => {
|
||||
let state = match xev.evtype {
|
||||
let state = match evtype {
|
||||
xinput2::XI_RawButtonPress => ElementState::Pressed,
|
||||
xinput2::XI_RawButtonRelease => ElementState::Released,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let xev: &XIRawEvent = unsafe { &*(xev.data as *const _) };
|
||||
let xev: &XIRawEvent = unsafe { xev.as_event() };
|
||||
self.xinput2_raw_button_input(xev, state, &mut callback);
|
||||
}
|
||||
xinput2::XI_RawMotion => {
|
||||
let xev: &XIRawEvent = unsafe { &*(xev.data as *const _) };
|
||||
let xev: &XIRawEvent = unsafe { xev.as_event() };
|
||||
self.xinput2_raw_mouse_motion(xev, &mut callback);
|
||||
}
|
||||
xinput2::XI_RawKeyPress | xinput2::XI_RawKeyRelease => {
|
||||
let state = match xev.evtype {
|
||||
let state = match evtype {
|
||||
xinput2::XI_RawKeyPress => ElementState::Pressed,
|
||||
xinput2::XI_RawKeyRelease => ElementState::Released,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let xev: &xinput2::XIRawEvent = unsafe { &*(xev.data as *const _) };
|
||||
let xev: &xinput2::XIRawEvent = unsafe { xev.as_event() };
|
||||
self.xinput2_raw_key_input(xev, state, &mut callback);
|
||||
}
|
||||
|
||||
xinput2::XI_HierarchyChanged => {
|
||||
let xev: &XIHierarchyEvent = unsafe { &*(xev.data as *const _) };
|
||||
let xev: &XIHierarchyEvent = unsafe { xev.as_event() };
|
||||
self.xinput2_hierarchy_changed(xev, &mut callback);
|
||||
}
|
||||
_ => {}
|
||||
@@ -1502,8 +1504,8 @@ impl<T: 'static> EventProcessor<T> {
|
||||
let mask =
|
||||
unsafe { slice::from_raw_parts(xev.valuators.mask, xev.valuators.mask_len as usize) };
|
||||
let mut value = xev.raw_values;
|
||||
let mut mouse_delta = (0.0, 0.0);
|
||||
let mut scroll_delta = (0.0, 0.0);
|
||||
let mut mouse_delta = util::Delta::default();
|
||||
let mut scroll_delta = util::Delta::default();
|
||||
for i in 0..xev.valuators.mask_len * 8 {
|
||||
if !xinput2::XIMaskIsSet(mask, i) {
|
||||
continue;
|
||||
@@ -1513,10 +1515,10 @@ impl<T: 'static> EventProcessor<T> {
|
||||
// We assume that every XInput2 device with analog axes is a pointing device emitting
|
||||
// relative coordinates.
|
||||
match i {
|
||||
0 => mouse_delta.0 = x,
|
||||
1 => mouse_delta.1 = x,
|
||||
2 => scroll_delta.0 = x as f32,
|
||||
3 => scroll_delta.1 = x as f32,
|
||||
0 => mouse_delta.set_x(x),
|
||||
1 => mouse_delta.set_y(x),
|
||||
2 => scroll_delta.set_x(x as f32),
|
||||
3 => scroll_delta.set_y(x as f32),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -1532,7 +1534,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
value = unsafe { value.offset(1) };
|
||||
}
|
||||
|
||||
if mouse_delta != (0.0, 0.0) {
|
||||
if let Some(mouse_delta) = mouse_delta.consume() {
|
||||
let event = Event::DeviceEvent {
|
||||
device_id: did,
|
||||
event: DeviceEvent::MouseMotion { delta: mouse_delta },
|
||||
@@ -1540,7 +1542,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
callback(&self.target, event);
|
||||
}
|
||||
|
||||
if scroll_delta != (0.0, 0.0) {
|
||||
if let Some(scroll_delta) = scroll_delta.consume() {
|
||||
let event = Event::DeviceEvent {
|
||||
device_id: did,
|
||||
event: DeviceEvent::MouseWheel {
|
||||
|
||||
@@ -69,6 +69,9 @@ const ALL_DEVICES: u16 = 0;
|
||||
const ALL_MASTER_DEVICES: u16 = 1;
|
||||
const ICONIC_STATE: u32 = 3;
|
||||
|
||||
/// The underlying x11rb connection that we are using.
|
||||
type X11rbConnection = x11rb::xcb_ffi::XCBConnection;
|
||||
|
||||
type X11Source = Generic<BorrowedFd<'static>>;
|
||||
|
||||
struct WakeSender<T> {
|
||||
@@ -989,9 +992,6 @@ impl From<xsettings::ParserError> for X11Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// The underlying x11rb connection that we are using.
|
||||
type X11rbConnection = x11rb::xcb_ffi::XCBConnection;
|
||||
|
||||
/// Type alias for a void cookie.
|
||||
type VoidCookie<'a> = x11rb::cookie::VoidCookie<'a, X11rbConnection>;
|
||||
|
||||
@@ -1007,34 +1007,6 @@ impl<'a, E: fmt::Debug> CookieResultExt for Result<VoidCookie<'a>, E> {
|
||||
}
|
||||
}
|
||||
|
||||
/// XEvents of type GenericEvent store their actual data in an XGenericEventCookie data structure. This is a wrapper to
|
||||
/// extract the cookie from a GenericEvent XEvent and release the cookie data once it has been processed
|
||||
struct GenericEventCookie<'a> {
|
||||
xconn: &'a XConnection,
|
||||
cookie: ffi::XGenericEventCookie,
|
||||
}
|
||||
|
||||
impl<'a> GenericEventCookie<'a> {
|
||||
fn from_event(xconn: &XConnection, event: ffi::XEvent) -> Option<GenericEventCookie<'_>> {
|
||||
unsafe {
|
||||
let mut cookie: ffi::XGenericEventCookie = From::from(event);
|
||||
if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == ffi::True {
|
||||
Some(GenericEventCookie { xconn, cookie })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for GenericEventCookie<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XFreeEventData)(self.xconn.display, &mut self.cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mkwid(w: xproto::Window) -> crate::window::WindowId {
|
||||
crate::window::WindowId(crate::platform_impl::platform::WindowId(w as _))
|
||||
}
|
||||
|
||||
55
src/platform_impl/linux/x11/util/cookie.rs
Normal file
55
src/platform_impl/linux/x11/util/cookie.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use std::ffi::c_int;
|
||||
use std::sync::Arc;
|
||||
|
||||
use x11_dl::xlib::{self, XEvent, XGenericEventCookie};
|
||||
|
||||
use crate::platform_impl::x11::XConnection;
|
||||
|
||||
/// XEvents of type GenericEvent store their actual data in an XGenericEventCookie data structure.
|
||||
/// This is a wrapper to extract the cookie from a GenericEvent XEvent and release the cookie data
|
||||
/// once it has been processed
|
||||
pub struct GenericEventCookie {
|
||||
cookie: XGenericEventCookie,
|
||||
xconn: Arc<XConnection>,
|
||||
}
|
||||
|
||||
impl GenericEventCookie {
|
||||
pub fn from_event(xconn: Arc<XConnection>, event: XEvent) -> Option<GenericEventCookie> {
|
||||
unsafe {
|
||||
let mut cookie: XGenericEventCookie = From::from(event);
|
||||
if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == xlib::True {
|
||||
Some(GenericEventCookie { cookie, xconn })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn extension(&self) -> u8 {
|
||||
self.cookie.extension as u8
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn evtype(&self) -> c_int {
|
||||
self.cookie.evtype
|
||||
}
|
||||
|
||||
/// Borrow inner event data as `&T`.
|
||||
///
|
||||
/// ## SAFETY
|
||||
///
|
||||
/// The caller must ensure that the event has the `T` inside of it.
|
||||
#[inline]
|
||||
pub unsafe fn as_event<T>(&self) -> &T {
|
||||
unsafe { &*(self.cookie.data as *const _) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GenericEventCookie {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XFreeEventData)(self.xconn.display, &mut self.cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ use std::{
|
||||
};
|
||||
|
||||
mod client_msg;
|
||||
pub mod cookie;
|
||||
mod cursor;
|
||||
mod geometry;
|
||||
mod hint;
|
||||
@@ -15,13 +16,14 @@ mod icon;
|
||||
mod input;
|
||||
pub mod keys;
|
||||
pub(crate) mod memory;
|
||||
mod mouse;
|
||||
mod randr;
|
||||
mod window_property;
|
||||
mod wm;
|
||||
mod xmodmap;
|
||||
|
||||
pub use self::{
|
||||
geometry::*, hint::*, input::*, window_property::*, wm::*, xmodmap::ModifierKeymap,
|
||||
geometry::*, hint::*, input::*, mouse::*, window_property::*, wm::*, xmodmap::ModifierKeymap,
|
||||
};
|
||||
|
||||
use super::{atoms::*, ffi, VoidCookie, X11Error, XConnection, XError};
|
||||
|
||||
52
src/platform_impl/linux/x11/util/mouse.rs
Normal file
52
src/platform_impl/linux/x11/util/mouse.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
//! Utilities for handling mouse events.
|
||||
|
||||
/// Recorded mouse delta designed to filter out noise.
|
||||
pub struct Delta<T> {
|
||||
x: T,
|
||||
y: T,
|
||||
}
|
||||
|
||||
impl<T: Default> Default for Delta<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
x: Default::default(),
|
||||
y: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default> Delta<T> {
|
||||
pub(crate) fn set_x(&mut self, x: T) {
|
||||
self.x = x;
|
||||
}
|
||||
|
||||
pub(crate) fn set_y(&mut self, y: T) {
|
||||
self.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! consume {
|
||||
($this:expr, $ty:ty) => {{
|
||||
let this = $this;
|
||||
let (x, y) = match (this.x.abs() < <$ty>::EPSILON, this.y.abs() < <$ty>::EPSILON) {
|
||||
(true, true) => return None,
|
||||
(true, false) => (this.x, 0.0),
|
||||
(false, true) => (0.0, this.y),
|
||||
(false, false) => (this.x, this.y),
|
||||
};
|
||||
|
||||
Some((x, y))
|
||||
}};
|
||||
}
|
||||
|
||||
impl Delta<f32> {
|
||||
pub(crate) fn consume(self) -> Option<(f32, f32)> {
|
||||
consume!(self, f32)
|
||||
}
|
||||
}
|
||||
|
||||
impl Delta<f64> {
|
||||
pub(crate) fn consume(self) -> Option<(f64, f64)> {
|
||||
consume!(self, f64)
|
||||
}
|
||||
}
|
||||
@@ -117,9 +117,7 @@ impl Schedule {
|
||||
let channel = MessageChannel::new().unwrap();
|
||||
let closure = Closure::new(f);
|
||||
let port_1 = channel.port1();
|
||||
port_1
|
||||
.add_event_listener_with_callback("message", closure.as_ref().unchecked_ref())
|
||||
.expect("Failed to set message handler");
|
||||
port_1.set_onmessage(Some(closure.as_ref().unchecked_ref()));
|
||||
port_1.start();
|
||||
|
||||
let port_2 = channel.port2();
|
||||
@@ -178,6 +176,7 @@ impl Drop for Schedule {
|
||||
} => {
|
||||
window.clear_timeout_with_handle(*handle);
|
||||
port.close();
|
||||
port.set_onmessage(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user