diff --git a/Cargo.toml b/Cargo.toml index 33d429f671..d6487655a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "winit" -version = "0.30.7" +version = "0.30.8" authors = [ "The winit contributors", "Pierre Krieger ", diff --git a/README.md b/README.md index 88d7403200..136e75de93 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ ```toml [dependencies] -winit = "0.30.7" +winit = "0.30.8" ``` ## [Documentation](https://docs.rs/winit) diff --git a/src/changelog/v0.30.md b/src/changelog/v0.30.md index 3ff54b508d..2a791ec0d0 100644 --- a/src/changelog/v0.30.md +++ b/src/changelog/v0.30.md @@ -1,3 +1,15 @@ +## 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 diff --git a/src/platform/android.rs b/src/platform/android.rs index dc3a111201..71c3364559 100644 --- a/src/platform/android.rs +++ b/src/platform/android.rs @@ -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.7", +//! 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 diff --git a/src/platform/startup_notify.rs b/src/platform/startup_notify.rs index c9047a03b2..6fab284e1a 100644 --- a/src/platform/startup_notify.rs +++ b/src/platform/startup_notify.rs @@ -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); } diff --git a/src/platform_impl/linux/wayland/types/xdg_activation.rs b/src/platform_impl/linux/wayland/types/xdg_activation.rs index 8bd21d0abb..9efc75da34 100644 --- a/src/platform_impl/linux/wayland/types/xdg_activation.rs +++ b/src/platform_impl/linux/wayland/types/xdg_activation.rs @@ -80,7 +80,7 @@ impl Dispatch 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, ); diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index 08e4504a83..83d6f80328 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -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. diff --git a/src/platform_impl/linux/x11/ime/input_method.rs b/src/platform_impl/linux/x11/ime/input_method.rs index b9d3ca7101..7f147bf1c4 100644 --- a/src/platform_impl/linux/x11/ime/input_method.rs +++ b/src/platform_impl/linux/x11/ime/input_method.rs @@ -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 }) } diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 8b9b690fb2..4042da0d09 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -531,7 +531,7 @@ impl EventLoop { 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) diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 9eff6dd028..ec1b0420b4 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -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() { diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 3fdf98d550..fa912f6ce0 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -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 { diff --git a/src/window.rs b/src/window.rs index 9bd002ab4c..f59273ed49 100644 --- a/src/window.rs +++ b/src/window.rs @@ -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 } }