Compare commits

..

7 Commits

Author SHA1 Message Date
Kirill Chibisov
58402b58cf Winit version 0.30.8 2025-01-04 08:45:12 +03:00
Kirill Chibisov
d7710f7264 api: add ActivationToken::{from,into}_raw
This is needed when passing and getting token from the IPC to activate
the window.
2025-01-04 08:45:12 +03:00
Kirill Chibisov
61314cd50a x11: fix cursor grab mode tracking on error
Fixes #4064.
2025-01-04 08:45:12 +03:00
Kirill Chibisov
6b5cc165dd x11: add workaround for disabling IME on gnome
GNOME doesn't list that there's a _NONE_ style at all, but it still
works if you use it.
2025-01-04 08:45:12 +03:00
Matt Campbell
43c323ccc0 windows: fix the event loop not waking on accessibility requests
Fixes #4055.
2025-01-04 08:45:12 +03:00
Kirill Chibisov
9cbce055d3 Winit version 0.30.7 2024-12-22 23:00:41 +03:00
Kirill Chibisov
727583ffbf x11: fix KeyboardInput delivered twice with IME
The filtered events were still processed even though they shouldn't once
we know that they're filtered.

Fixes #4048.
2024-12-22 23:00:41 +03:00
13 changed files with 106 additions and 50 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "winit"
version = "0.30.6"
version = "0.30.8"
authors = [
"The winit contributors",
"Pierre Krieger <pierre.krieger1708@gmail.com>",

View File

@@ -8,7 +8,7 @@
```toml
[dependencies]
winit = "0.30.6"
winit = "0.30.8"
```
## [Documentation](https://docs.rs/winit)

View File

@@ -1,3 +1,21 @@
## 0.30.8
### Added
- `ActivationToken::from_raw` and `ActivationToken::into_raw`.
- On X11, add a workaround for disabling IME on GNOME.
### Fixed
- On Windows, fixed the event loop not waking on accessibility requests.
- On X11, fixed cursor grab mode state tracking on error.
## 0.30.7
### Fixed
- On X11, fixed KeyboardInput delivered twice when IME enabled.
## 0.30.6
### Added

View File

@@ -62,7 +62,7 @@
//! 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.30.6",
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.30.8",
//! 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

View File

@@ -64,7 +64,7 @@ impl EventLoopExtStartupNotify for ActiveEventLoop {
crate::platform_impl::ActiveEventLoop::X(_) => env::var(X11_VAR),
}
.ok()
.map(ActivationToken::_new)
.map(ActivationToken::from_raw)
}
}
@@ -94,6 +94,6 @@ pub fn reset_activation_token_env() {
///
/// This could be used before running daemon processes.
pub fn set_activation_token_env(token: ActivationToken) {
env::set_var(X11_VAR, &token._token);
env::set_var(WAYLAND_VAR, token._token);
env::set_var(X11_VAR, &token.token);
env::set_var(WAYLAND_VAR, token.token);
}

View File

@@ -80,7 +80,7 @@ impl Dispatch<XdgActivationTokenV1, XdgActivationTokenData, WinitState> for XdgA
state.events_sink.push_window_event(
crate::event::WindowEvent::ActivationTokenDone {
serial: *serial,
token: ActivationToken::_new(token),
token: ActivationToken::from_raw(token),
},
*window_id,
);

View File

@@ -168,7 +168,7 @@ impl Window {
if let (Some(xdg_activation), Some(token)) =
(xdg_activation.as_ref(), attributes.platform_specific.activation_token)
{
xdg_activation.activate(token._token, &surface);
xdg_activation.activate(token.token, &surface);
}
// XXX Do initial commit.

View File

@@ -129,6 +129,7 @@ impl EventProcessor {
/// Specifically, this involves all of the KeyPress events in compose/pre-edit sequences,
/// along with an extra copy of the KeyRelease events. This also prevents backspace and
/// arrow keys from being detected twice.
#[must_use]
fn filter_event(&mut self, xev: &mut XEvent) -> bool {
let wt = Self::window_target(&self.target);
unsafe {
@@ -149,7 +150,7 @@ impl EventProcessor {
// and forward back. This is not desired for e.g. games since some IMEs may delay the input
// and game can toggle IME back when e.g. typing into some field where latency won't really
// matter.
if event_type == xlib::KeyPress || event_type == xlib::KeyRelease {
let filtered = if event_type == xlib::KeyPress || event_type == xlib::KeyRelease {
let wt = Self::window_target(&self.target);
let ime = wt.ime.as_ref();
let window = self.active_window.map(|window| window as XWindow);
@@ -157,7 +158,8 @@ impl EventProcessor {
.and_then(|ime| window.map(|window| ime.borrow().is_ime_allowed(window)))
.unwrap_or(false);
if forward_to_ime && self.filter_event(xev) {
let filtered = forward_to_ime && self.filter_event(xev);
if filtered {
let xev: &XKeyEvent = xev.as_ref();
if self.xmodmap.is_modifier(xev.keycode as u8) {
// Don't grow the buffer past the `MAX_MOD_REPLAY_LEN`. This could happen
@@ -170,8 +172,15 @@ impl EventProcessor {
self.xfiltered_modifiers.push_front(xev.serial);
}
}
filtered
} else {
self.filter_event(xev);
self.filter_event(xev)
};
// Don't process event if it was filtered.
if filtered {
return;
}
match event_type {

View File

@@ -81,7 +81,9 @@ impl InputMethod {
}
let preedit_style = preedit_style.unwrap_or_else(|| none_style.unwrap());
let none_style = none_style.unwrap_or(preedit_style);
// Always initialize none style even when it's not advertised, since it seems to work
// regardless...
let none_style = none_style.unwrap_or(Style::None(XIM_NONE_STYLE));
Some(InputMethod { im, _name: name, preedit_style, none_style })
}

View File

@@ -531,7 +531,7 @@ impl<T: 'static> EventLoop<T> {
window_id: crate::window::WindowId(window_id),
event: WindowEvent::ActivationTokenDone {
serial,
token: crate::window::ActivationToken::_new(token),
token: crate::window::ActivationToken::from_raw(token),
},
};
callback(event, &self.event_processor.target)

View File

@@ -559,7 +559,7 @@ impl UnownedWindow {
// Remove the startup notification if we have one.
if let Some(startup) = window_attrs.platform_specific.activation_token.as_ref() {
leap!(xconn.remove_activation_token(xwindow, &startup._token));
leap!(xconn.remove_activation_token(xwindow, &startup.token));
}
// We never want to give the user a broken window, since by then, it's too late to handle.
@@ -1492,6 +1492,11 @@ impl UnownedWindow {
#[inline]
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
// We don't support the locked cursor yet, so ignore it early on.
if mode == CursorGrabMode::Locked {
return Err(ExternalError::NotSupported(NotSupportedError::new()));
}
let mut grabbed_lock = self.cursor_grabbed_mode.lock().unwrap();
if mode == *grabbed_lock {
return Ok(());
@@ -1503,40 +1508,40 @@ impl UnownedWindow {
.xcb_connection()
.ungrab_pointer(x11rb::CURRENT_TIME)
.expect_then_ignore_error("Failed to call `xcb_ungrab_pointer`");
*grabbed_lock = CursorGrabMode::None;
let result = match mode {
CursorGrabMode::None => self.xconn.flush_requests().map_err(|err| {
ExternalError::Os(os_error!(OsError::XError(X11Error::Xlib(err).into())))
}),
CursorGrabMode::Confined => {
let result = {
self.xconn
.xcb_connection()
.grab_pointer(
true as _,
self.xwindow,
xproto::EventMask::BUTTON_PRESS
| xproto::EventMask::BUTTON_RELEASE
| xproto::EventMask::ENTER_WINDOW
| xproto::EventMask::LEAVE_WINDOW
| xproto::EventMask::POINTER_MOTION
| xproto::EventMask::POINTER_MOTION_HINT
| xproto::EventMask::BUTTON1_MOTION
| xproto::EventMask::BUTTON2_MOTION
| xproto::EventMask::BUTTON3_MOTION
| xproto::EventMask::BUTTON4_MOTION
| xproto::EventMask::BUTTON5_MOTION
| xproto::EventMask::KEYMAP_STATE,
xproto::GrabMode::ASYNC,
xproto::GrabMode::ASYNC,
self.xwindow,
0u32,
x11rb::CURRENT_TIME,
)
.expect("Failed to call `grab_pointer`")
.reply()
.expect("Failed to receive reply from `grab_pointer`")
};
let result = self
.xconn
.xcb_connection()
.grab_pointer(
true as _,
self.xwindow,
xproto::EventMask::BUTTON_PRESS
| xproto::EventMask::BUTTON_RELEASE
| xproto::EventMask::ENTER_WINDOW
| xproto::EventMask::LEAVE_WINDOW
| xproto::EventMask::POINTER_MOTION
| xproto::EventMask::POINTER_MOTION_HINT
| xproto::EventMask::BUTTON1_MOTION
| xproto::EventMask::BUTTON2_MOTION
| xproto::EventMask::BUTTON3_MOTION
| xproto::EventMask::BUTTON4_MOTION
| xproto::EventMask::BUTTON5_MOTION
| xproto::EventMask::KEYMAP_STATE,
xproto::GrabMode::ASYNC,
xproto::GrabMode::ASYNC,
self.xwindow,
0u32,
x11rb::CURRENT_TIME,
)
.expect("Failed to call `grab_pointer`")
.reply()
.expect("Failed to receive reply from `grab_pointer`");
match result.status {
xproto::GrabStatus::SUCCESS => Ok(()),
@@ -1556,9 +1561,7 @@ impl UnownedWindow {
}
.map_err(|err| ExternalError::Os(os_error!(OsError::Misc(err))))
},
CursorGrabMode::Locked => {
return Err(ExternalError::NotSupported(NotSupportedError::new()));
},
CursorGrabMode::Locked => return Ok(()),
};
if result.is_ok() {

View File

@@ -48,7 +48,7 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
RegisterClassExW, RegisterWindowMessageA, SetCursor, SetWindowPos, TranslateMessage,
CREATESTRUCTW, GIDC_ARRIVAL, GIDC_REMOVAL, GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT,
MINMAXINFO, MNC_CLOSE, MSG, MWMO_INPUTAVAILABLE, NCCALCSIZE_PARAMS, PM_REMOVE, PT_PEN,
PT_TOUCH, QS_ALLEVENTS, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE,
PT_TOUCH, QS_ALLINPUT, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE,
SIZE_MAXIMIZED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS,
WMSZ_BOTTOM, WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT,
WMSZ_TOPRIGHT, WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED,
@@ -745,11 +745,12 @@ fn wait_for_messages_impl(
let (num_handles, raw_handles) =
if use_timer { (1, [high_resolution_timer.unwrap()]) } else { (0, [ptr::null_mut()]) };
// We must use `QS_ALLINPUT` to wake on accessibility messages.
let result = MsgWaitForMultipleObjectsEx(
num_handles,
raw_handles.as_ptr() as *const _,
wait_duration_ms,
QS_ALLEVENTS,
QS_ALLINPUT,
MWMO_INPUTAVAILABLE,
);
if result == WAIT_FAILED {

View File

@@ -1850,11 +1850,34 @@ impl Default for ImePurpose {
/// [`Window`]: crate::window::Window
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ActivationToken {
pub(crate) _token: String,
pub(crate) token: String,
}
impl ActivationToken {
pub(crate) fn _new(_token: String) -> Self {
Self { _token }
/// Make an [`ActivationToken`] from a string.
///
/// This method should be used to wrap tokens passed by side channels to your application, like
/// dbus.
///
/// The validity of the token is ensured by the windowing system. Using the invalid token will
/// only result in the side effect of the operation involving it being ignored (e.g. window
/// won't get focused automatically), but won't yield any errors.
///
/// To obtain a valid token, use
#[cfg_attr(any(x11_platform, wayland_platform, docsrs), doc = " [`request_activation_token`].")]
#[cfg_attr(
not(any(x11_platform, wayland_platform, docsrs)),
doc = " `request_activation_token`."
)]
///
#[rustfmt::skip]
/// [`request_activation_token`]: crate::platform::startup_notify::WindowExtStartupNotify::request_activation_token
pub fn from_raw(token: String) -> Self {
Self { token }
}
/// Convert the token to its string representation to later pass via IPC.
pub fn into_raw(self) -> String {
self.token
}
}