mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ae4f5cdea | ||
|
|
501d9b4a44 | ||
|
|
487137b867 | ||
|
|
b77ea7d218 | ||
|
|
3154c60ef4 | ||
|
|
abfe90bddb | ||
|
|
090498a4a6 | ||
|
|
58402b58cf | ||
|
|
d7710f7264 | ||
|
|
61314cd50a | ||
|
|
6b5cc165dd | ||
|
|
43c323ccc0 | ||
|
|
9cbce055d3 | ||
|
|
727583ffbf |
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@@ -107,7 +107,12 @@ jobs:
|
||||
|
||||
- name: Generate lockfile
|
||||
# Also updates the crates.io index
|
||||
run: cargo generate-lockfile && cargo update -p ahash --precise 0.8.7 && cargo update -p bumpalo --precise 3.14.0
|
||||
run: |
|
||||
cargo generate-lockfile
|
||||
cargo update -p ahash --precise 0.8.7
|
||||
cargo update -p bumpalo --precise 3.14.0
|
||||
cargo update -p objc2-encode --precise 4.0.3
|
||||
cargo update -p orbclient --precise 0.3.47
|
||||
|
||||
- name: Install GCC Multilib
|
||||
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
|
||||
|
||||
@@ -19,6 +19,11 @@ All patches have to be sent on Github as [pull requests][prs]. To simplify your
|
||||
life during review it's recommended to check the "give contributors write access
|
||||
to the branch" checkbox.
|
||||
|
||||
We use unstable Rustfmt options across the project, so please run
|
||||
`cargo +nightly fmt` before submitting your work. If you are unable to do so,
|
||||
the maintainers can do it for you before merging, just state so in your pull
|
||||
request description.
|
||||
|
||||
#### Handling review
|
||||
|
||||
During the review process certain events could require an action from your side,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.30.6"
|
||||
version = "0.30.9"
|
||||
authors = [
|
||||
"The winit contributors",
|
||||
"Pierre Krieger <pierre.krieger1708@gmail.com>",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.30.6"
|
||||
winit = "0.30.9"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
|
||||
@@ -1,3 +1,33 @@
|
||||
## 0.30.9
|
||||
|
||||
### Changed
|
||||
|
||||
- On Wayland, no longer send an explicit clearing `Ime::Preedit` just prior to a new `Ime::Preedit`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- On X11, fix crash with uim.
|
||||
- On X11, fix modifiers for keys that were sent by the same X11 request.
|
||||
- On iOS, fix high CPU usage even when using `ControlFlow::Wait`.
|
||||
|
||||
## 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
|
||||
|
||||
@@ -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.9",
|
||||
//! 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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -375,6 +375,7 @@ impl AppState {
|
||||
(ControlFlow::Wait, ControlFlow::Wait) => {
|
||||
let start = Instant::now();
|
||||
self.set_state(AppStateImpl::Waiting { waiting_handler, start });
|
||||
self.waker.stop()
|
||||
},
|
||||
(ControlFlow::WaitUntil(old_instant), ControlFlow::WaitUntil(new_instant))
|
||||
if old_instant == new_instant =>
|
||||
|
||||
@@ -121,11 +121,15 @@ impl Dispatch<ZwpTextInputV3, TextInputData, WinitState> for TextInputState {
|
||||
None => return,
|
||||
};
|
||||
|
||||
// Clear preedit at the start of `Done`.
|
||||
state.events_sink.push_window_event(
|
||||
WindowEvent::Ime(Ime::Preedit(String::new(), None)),
|
||||
window_id,
|
||||
);
|
||||
// Clear preedit, unless all we'll be doing next is sending a new preedit.
|
||||
if text_input_data.pending_commit.is_some()
|
||||
|| text_input_data.pending_preedit.is_none()
|
||||
{
|
||||
state.events_sink.push_window_event(
|
||||
WindowEvent::Ime(Ime::Preedit(String::new(), None)),
|
||||
window_id,
|
||||
);
|
||||
}
|
||||
|
||||
// Send `Commit`.
|
||||
if let Some(text) = text_input_data.pending_commit.take() {
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -66,7 +66,10 @@ pub struct EventProcessor {
|
||||
pub active_window: Option<xproto::Window>,
|
||||
/// Latest modifiers we've sent for the user to trigger change in event.
|
||||
pub modifiers: Cell<ModifiersState>,
|
||||
pub xfiltered_modifiers: VecDeque<c_ulong>,
|
||||
// Track modifiers based on keycodes. NOTE: that serials generally don't work for tracking
|
||||
// since they are not unique and could be duplicated in case of sequence of key events is
|
||||
// delivered at near the same time.
|
||||
pub xfiltered_modifiers: VecDeque<u8>,
|
||||
pub xmodmap: util::ModifierKeymap,
|
||||
pub is_composing: bool,
|
||||
}
|
||||
@@ -129,6 +132,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 +153,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,21 +161,27 @@ 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
|
||||
// when the modifiers are consumed entirely or serials are altered.
|
||||
//
|
||||
// Both cases shouldn't happen in well behaving clients.
|
||||
// when the modifiers are consumed entirely.
|
||||
if self.xfiltered_modifiers.len() == MAX_MOD_REPLAY_LEN {
|
||||
self.xfiltered_modifiers.pop_back();
|
||||
}
|
||||
self.xfiltered_modifiers.push_front(xev.serial);
|
||||
self.xfiltered_modifiers.push_front(xev.keycode as u8);
|
||||
}
|
||||
}
|
||||
|
||||
filtered
|
||||
} else {
|
||||
self.filter_event(xev);
|
||||
self.filter_event(xev)
|
||||
};
|
||||
|
||||
// Don't process event if it was filtered.
|
||||
if filtered {
|
||||
return;
|
||||
}
|
||||
|
||||
match event_type {
|
||||
@@ -941,7 +951,7 @@ impl EventProcessor {
|
||||
// itself are out of sync due to XkbState being delivered before XKeyEvent, since it's
|
||||
// being replayed by the XIM, thus we should replay ourselves.
|
||||
let replay = if let Some(position) =
|
||||
self.xfiltered_modifiers.iter().rev().position(|&s| s == xev.serial)
|
||||
self.xfiltered_modifiers.iter().rev().position(|&s| s == xev.keycode as u8)
|
||||
{
|
||||
// We don't have to replay modifiers pressed before the current event if some events
|
||||
// were not forwarded to us, since their state is irrelevant.
|
||||
|
||||
@@ -123,19 +123,15 @@ unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
|
||||
let is_allowed =
|
||||
old_context.as_ref().map(|old_context| old_context.is_allowed()).unwrap_or_default();
|
||||
|
||||
// We can't use the style from the old context here, since it may change on reload, so
|
||||
// pick style from the new XIM based on the old state.
|
||||
let style = if is_allowed { new_im.preedit_style } else { new_im.none_style };
|
||||
|
||||
let new_context = {
|
||||
let result = unsafe {
|
||||
ImeContext::new(
|
||||
xconn,
|
||||
new_im.im,
|
||||
style,
|
||||
&new_im,
|
||||
*window,
|
||||
spot,
|
||||
(*inner).event_sender.clone(),
|
||||
is_allowed,
|
||||
)
|
||||
};
|
||||
if result.is_err() {
|
||||
|
||||
@@ -5,10 +5,9 @@ use std::{mem, ptr};
|
||||
|
||||
use x11_dl::xlib::{XIMCallback, XIMPreeditCaretCallbackStruct, XIMPreeditDrawCallbackStruct};
|
||||
|
||||
use crate::platform_impl::platform::x11::ime::input_method::{Style, XIMStyle};
|
||||
use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventSender};
|
||||
|
||||
use super::{ffi, util, XConnection, XError};
|
||||
use crate::platform_impl::platform::x11::ime::input_method::{InputMethod, Style, XIMStyle};
|
||||
use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventSender};
|
||||
|
||||
/// IME creation error.
|
||||
#[derive(Debug)]
|
||||
@@ -184,7 +183,7 @@ struct ImeContextClientData {
|
||||
pub struct ImeContext {
|
||||
pub(crate) ic: ffi::XIC,
|
||||
pub(crate) ic_spot: ffi::XPoint,
|
||||
pub(crate) style: Style,
|
||||
pub(crate) allowed: bool,
|
||||
// Since the data is passed shared between X11 XIM callbacks, but couldn't be directly free
|
||||
// from there we keep the pointer to automatically deallocate it.
|
||||
_client_data: Box<ImeContextClientData>,
|
||||
@@ -193,11 +192,11 @@ pub struct ImeContext {
|
||||
impl ImeContext {
|
||||
pub(crate) unsafe fn new(
|
||||
xconn: &Arc<XConnection>,
|
||||
im: ffi::XIM,
|
||||
style: Style,
|
||||
im: &InputMethod,
|
||||
window: ffi::Window,
|
||||
ic_spot: Option<ffi::XPoint>,
|
||||
event_sender: ImeEventSender,
|
||||
allowed: bool,
|
||||
) -> Result<Self, ImeContextCreationError> {
|
||||
let client_data = Box::into_raw(Box::new(ImeContextClientData {
|
||||
window,
|
||||
@@ -206,20 +205,24 @@ impl ImeContext {
|
||||
cursor_pos: 0,
|
||||
}));
|
||||
|
||||
let style = if allowed { im.preedit_style } else { im.none_style };
|
||||
|
||||
let ic = match style as _ {
|
||||
Style::Preedit(style) => unsafe {
|
||||
ImeContext::create_preedit_ic(
|
||||
xconn,
|
||||
im,
|
||||
im.im,
|
||||
style,
|
||||
window,
|
||||
client_data as ffi::XPointer,
|
||||
)
|
||||
},
|
||||
Style::Nothing(style) => unsafe {
|
||||
ImeContext::create_nothing_ic(xconn, im, style, window)
|
||||
ImeContext::create_nothing_ic(xconn, im.im, style, window)
|
||||
},
|
||||
Style::None(style) => unsafe {
|
||||
ImeContext::create_none_ic(xconn, im.im, style, window)
|
||||
},
|
||||
Style::None(style) => unsafe { ImeContext::create_none_ic(xconn, im, style, window) },
|
||||
}
|
||||
.ok_or(ImeContextCreationError::Null)?;
|
||||
|
||||
@@ -228,7 +231,7 @@ impl ImeContext {
|
||||
let mut context = ImeContext {
|
||||
ic,
|
||||
ic_spot: ffi::XPoint { x: 0, y: 0 },
|
||||
style,
|
||||
allowed,
|
||||
_client_data: unsafe { Box::from_raw(client_data) },
|
||||
};
|
||||
|
||||
@@ -335,7 +338,7 @@ impl ImeContext {
|
||||
}
|
||||
|
||||
pub fn is_allowed(&self) -> bool {
|
||||
!matches!(self.style, Style::None(_))
|
||||
self.allowed
|
||||
}
|
||||
|
||||
// Set the spot for preedit text. Setting spot isn't working with libX11 when preedit callbacks
|
||||
|
||||
@@ -10,15 +10,13 @@ use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::debug;
|
||||
|
||||
use super::{ffi, util, XConnection, XError};
|
||||
|
||||
use self::callbacks::*;
|
||||
use self::context::ImeContext;
|
||||
pub use self::context::ImeContextCreationError;
|
||||
use self::inner::{close_im, ImeInner};
|
||||
use self::input_method::{PotentialInputMethods, Style};
|
||||
use self::input_method::PotentialInputMethods;
|
||||
use super::{ffi, util, XConnection, XError};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
@@ -114,39 +112,26 @@ impl Ime {
|
||||
pub fn create_context(
|
||||
&mut self,
|
||||
window: ffi::Window,
|
||||
with_preedit: bool,
|
||||
with_ime: bool,
|
||||
) -> Result<bool, ImeContextCreationError> {
|
||||
let context = if self.is_destroyed() {
|
||||
// Create empty entry in map, so that when IME is rebuilt, this window has a context.
|
||||
None
|
||||
} else {
|
||||
let im = self.inner.im.as_ref().unwrap();
|
||||
let style = if with_preedit { im.preedit_style } else { im.none_style };
|
||||
|
||||
let context = unsafe {
|
||||
ImeContext::new(
|
||||
&self.inner.xconn,
|
||||
im.im,
|
||||
style,
|
||||
im,
|
||||
window,
|
||||
None,
|
||||
self.inner.event_sender.clone(),
|
||||
with_ime,
|
||||
)?
|
||||
};
|
||||
|
||||
// Check the state on the context, since it could fail to enable or disable preedit.
|
||||
let event = if matches!(style, Style::None(_)) {
|
||||
if with_preedit {
|
||||
debug!("failed to create IME context with preedit support.")
|
||||
}
|
||||
ImeEvent::Disabled
|
||||
} else {
|
||||
if !with_preedit {
|
||||
debug!("failed to create IME context without preedit support.")
|
||||
}
|
||||
ImeEvent::Enabled
|
||||
};
|
||||
|
||||
let event = if context.is_allowed() { ImeEvent::Enabled } else { ImeEvent::Disabled };
|
||||
self.inner.event_sender.send((window, event)).expect("Failed to send enabled event");
|
||||
|
||||
Some(context)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -6,6 +6,7 @@ use std::fmt;
|
||||
use core_foundation::array::{CFArrayGetCount, CFArrayGetValueAtIndex};
|
||||
use core_foundation::base::{CFRelease, TCFType};
|
||||
use core_foundation::string::CFString;
|
||||
use core_foundation::uuid::{CFUUIDGetUUIDBytes, CFUUID};
|
||||
use core_graphics::display::{
|
||||
CGDirectDisplayID, CGDisplay, CGDisplayBounds, CGDisplayCopyDisplayMode,
|
||||
};
|
||||
@@ -100,15 +101,42 @@ impl VideoModeHandle {
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorHandle(CGDirectDisplayID);
|
||||
|
||||
type MonitorUuid = [u8; 16];
|
||||
|
||||
impl MonitorHandle {
|
||||
/// Internal comparisons of [`MonitorHandle`]s are done first requesting a UUID for the handle.
|
||||
fn uuid(&self) -> MonitorUuid {
|
||||
let cf_uuid = unsafe {
|
||||
CFUUID::wrap_under_create_rule(ffi::CGDisplayCreateUUIDFromDisplayID(self.0))
|
||||
};
|
||||
let uuid = unsafe { CFUUIDGetUUIDBytes(cf_uuid.as_concrete_TypeRef()) };
|
||||
MonitorUuid::from([
|
||||
uuid.byte0,
|
||||
uuid.byte1,
|
||||
uuid.byte2,
|
||||
uuid.byte3,
|
||||
uuid.byte4,
|
||||
uuid.byte5,
|
||||
uuid.byte6,
|
||||
uuid.byte7,
|
||||
uuid.byte8,
|
||||
uuid.byte9,
|
||||
uuid.byte10,
|
||||
uuid.byte11,
|
||||
uuid.byte12,
|
||||
uuid.byte13,
|
||||
uuid.byte14,
|
||||
uuid.byte15,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
// `CGDirectDisplayID` changes on video mode change, so we cannot rely on that
|
||||
// for comparisons, but we can use `CGDisplayCreateUUIDFromDisplayID` to get an
|
||||
// unique identifier that persists even across system reboots
|
||||
impl PartialEq for MonitorHandle {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
unsafe {
|
||||
ffi::CGDisplayCreateUUIDFromDisplayID(self.0)
|
||||
== ffi::CGDisplayCreateUUIDFromDisplayID(other.0)
|
||||
}
|
||||
self.uuid() == other.uuid()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,18 +150,13 @@ impl PartialOrd for MonitorHandle {
|
||||
|
||||
impl Ord for MonitorHandle {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
unsafe {
|
||||
ffi::CGDisplayCreateUUIDFromDisplayID(self.0)
|
||||
.cmp(&ffi::CGDisplayCreateUUIDFromDisplayID(other.0))
|
||||
}
|
||||
self.uuid().cmp(&other.uuid())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for MonitorHandle {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
unsafe {
|
||||
ffi::CGDisplayCreateUUIDFromDisplayID(self.0).hash(state);
|
||||
}
|
||||
self.uuid().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,13 +319,11 @@ impl MonitorHandle {
|
||||
}
|
||||
|
||||
pub(crate) fn ns_screen(&self, mtm: MainThreadMarker) -> Option<Retained<NSScreen>> {
|
||||
let uuid = unsafe { ffi::CGDisplayCreateUUIDFromDisplayID(self.0) };
|
||||
let uuid = self.uuid();
|
||||
NSScreen::screens(mtm).into_iter().find(|screen| {
|
||||
let other_native_id = get_display_id(screen);
|
||||
let other_uuid = unsafe {
|
||||
ffi::CGDisplayCreateUUIDFromDisplayID(other_native_id as CGDirectDisplayID)
|
||||
};
|
||||
uuid == other_uuid
|
||||
let other = MonitorHandle::new(other_native_id);
|
||||
uuid == other.uuid()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user