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 | |
|---|---|---|---|
|
|
43f29f0481 | ||
|
|
266219f27f | ||
|
|
7449534ba2 | ||
|
|
7aa202b872 | ||
|
|
06cec065d4 | ||
|
|
f968e64ac8 | ||
|
|
a97309690e | ||
|
|
dec45ce0ff | ||
|
|
f709ac667f | ||
|
|
ecbe04caa7 | ||
|
|
7103514ae8 | ||
|
|
8b5aa33a88 | ||
|
|
7a3b486965 | ||
|
|
fc9c78cb56 |
11
CHANGELOG.md
11
CHANGELOG.md
@@ -11,6 +11,17 @@ Unreleased` header.
|
||||
|
||||
# Unreleased
|
||||
|
||||
# 0.29.4
|
||||
|
||||
- Fix crash when running iOS app on macOS.
|
||||
- On X11, check common alternative cursor names when loading cursor.
|
||||
- On X11, reload the DPI after a property change event.
|
||||
- On Windows, fix so `drag_window` and `drag_resize_window` can be called from another thread.
|
||||
- On Windows, fix `set_control_flow` in `AboutToWait` not being taken in account.
|
||||
- On macOS, send a `Resized` event after each `ScaleFactorChanged` event.
|
||||
- On Wayland, fix `wl_surface` being destroyed before associated objects.
|
||||
- On macOS, fix assertion when pressing `Fn` key.
|
||||
|
||||
# 0.29.3
|
||||
|
||||
- On Wayland, apply correct scale to `PhysicalSize` passed in `WindowBuilder::with_inner_size` when possible.
|
||||
|
||||
15
Cargo.toml
15
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.29.3"
|
||||
version = "0.29.4"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
edition = "2021"
|
||||
@@ -13,7 +13,14 @@ categories = ["gui"]
|
||||
rust-version = "1.65.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["rwh_04", "rwh_05", "rwh_06", "serde"]
|
||||
features = [
|
||||
"rwh_04",
|
||||
"rwh_05",
|
||||
"rwh_06",
|
||||
"serde",
|
||||
# Enabled to get docs to compile
|
||||
"android-native-activity",
|
||||
]
|
||||
default-target = "x86_64-unknown-linux-gnu"
|
||||
# These are all tested in CI
|
||||
targets = [
|
||||
@@ -54,7 +61,7 @@ cfg_aliases = "0.1.1"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "2"
|
||||
cursor-icon = "1.0.0"
|
||||
cursor-icon = "1.1.0"
|
||||
log = "0.4"
|
||||
mint = { version = "0.5.6", optional = true }
|
||||
once_cell = "1.12"
|
||||
@@ -74,7 +81,7 @@ softbuffer = "0.3.0"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android-activity = "0.5.0"
|
||||
ndk = "0.8.0"
|
||||
ndk = { version = "0.8.0", default-features = false }
|
||||
ndk-sys = "0.5.0"
|
||||
|
||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.29.3"
|
||||
winit = "0.29.4"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
@@ -157,7 +157,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.3", features = [ "android-native-activity" ] }`
|
||||
2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.4", 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).
|
||||
|
||||
|
||||
@@ -53,6 +53,13 @@ pub(super) fn fill_window(window: &Window) {
|
||||
}
|
||||
|
||||
GC.with(|gc| {
|
||||
let size = window.inner_size();
|
||||
let (Some(width), Some(height)) =
|
||||
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Either get the last context used or create a new one.
|
||||
let mut gc = gc.borrow_mut();
|
||||
let surface = gc
|
||||
@@ -61,13 +68,9 @@ pub(super) fn fill_window(window: &Window) {
|
||||
|
||||
// Fill a buffer with a solid color.
|
||||
const DARK_GRAY: u32 = 0xFF181818;
|
||||
let size = window.inner_size();
|
||||
|
||||
surface
|
||||
.resize(
|
||||
NonZeroU32::new(size.width).expect("Width must be greater than zero"),
|
||||
NonZeroU32::new(size.height).expect("Height must be greater than zero"),
|
||||
)
|
||||
.resize(width, height)
|
||||
.expect("Failed to resize the softbuffer surface");
|
||||
|
||||
let mut buffer = surface
|
||||
|
||||
@@ -49,11 +49,7 @@ impl fmt::Display for BadIcon {
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for BadIcon {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
impl Error for BadIcon {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct RgbaIcon {
|
||||
|
||||
@@ -84,5 +84,10 @@ impl<T> EventLoopBuilderExtAndroid for EventLoopBuilder<T> {
|
||||
/// use winit::platform::android::activity::AndroidApp;
|
||||
/// ```
|
||||
pub mod activity {
|
||||
// We enable the `"native-activity"` feature just so that we can build the
|
||||
// docs, but it'll be very confusing for users to see the docs with that
|
||||
// feature enabled, so we avoid inlining it so that they're forced to view
|
||||
// it on the crate's own docs.rs page.
|
||||
#[doc(no_inline)]
|
||||
pub use android_activity::*;
|
||||
}
|
||||
|
||||
@@ -200,6 +200,10 @@ impl AppState {
|
||||
)
|
||||
}
|
||||
|
||||
fn has_terminated(&self) -> bool {
|
||||
matches!(self.state(), AppStateImpl::Terminated)
|
||||
}
|
||||
|
||||
fn will_launch_transition(&mut self, queued_event_handler: Box<dyn EventHandler>) {
|
||||
let (queued_windows, queued_events, queued_gpu_redraws) = match self.take_state() {
|
||||
AppStateImpl::NotLaunched {
|
||||
@@ -243,7 +247,7 @@ impl AppState {
|
||||
fn wakeup_transition(&mut self) -> Option<EventWrapper> {
|
||||
// before `AppState::did_finish_launching` is called, pretend there is no running
|
||||
// event loop.
|
||||
if !self.has_launched() {
|
||||
if !self.has_launched() || self.has_terminated() {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -390,7 +394,7 @@ impl AppState {
|
||||
}
|
||||
|
||||
fn events_cleared_transition(&mut self) {
|
||||
if !self.has_launched() {
|
||||
if !self.has_launched() || self.has_terminated() {
|
||||
return;
|
||||
}
|
||||
let (waiting_event_handler, old) = match self.take_state() {
|
||||
@@ -586,6 +590,10 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||
events: I,
|
||||
) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
if this.has_terminated() {
|
||||
return;
|
||||
}
|
||||
|
||||
let (mut event_handler, active_control_flow, processing_redraws) =
|
||||
match this.try_user_callback_transition() {
|
||||
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
|
||||
@@ -737,7 +745,7 @@ fn handle_user_events(mtm: MainThreadMarker) {
|
||||
|
||||
pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
if !this.has_launched() {
|
||||
if !this.has_launched() || this.has_terminated() {
|
||||
return;
|
||||
}
|
||||
match this.state_mut() {
|
||||
|
||||
@@ -289,7 +289,7 @@ fn setup_control_flow_observers() {
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(mtm),
|
||||
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
|
||||
kCFRunLoopExit => {} // may happen when running on macOS
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -304,7 +304,7 @@ fn setup_control_flow_observers() {
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(mtm),
|
||||
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
|
||||
kCFRunLoopExit => {} // may happen when running on macOS
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
//! The state of the window, which is shared with the event-loop.
|
||||
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::num::NonZeroU32;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::time::Duration;
|
||||
@@ -55,9 +54,6 @@ pub struct WindowState {
|
||||
/// The connection to Wayland server.
|
||||
pub connection: Connection,
|
||||
|
||||
/// The underlying SCTK window.
|
||||
pub window: ManuallyDrop<Window>,
|
||||
|
||||
/// The window frame, which is created from the configure request.
|
||||
frame: Option<WinitFrame>,
|
||||
|
||||
@@ -149,6 +145,9 @@ pub struct WindowState {
|
||||
///
|
||||
/// The value is the serial of the event triggered moved.
|
||||
has_pending_move: Option<u32>,
|
||||
|
||||
/// The underlying SCTK window.
|
||||
pub window: Window,
|
||||
}
|
||||
|
||||
impl WindowState {
|
||||
@@ -206,7 +205,7 @@ impl WindowState {
|
||||
title: String::default(),
|
||||
transparent: false,
|
||||
viewport,
|
||||
window: ManuallyDrop::new(window),
|
||||
window,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +270,7 @@ impl WindowState {
|
||||
&& !self.csd_fails
|
||||
{
|
||||
match WinitFrame::new(
|
||||
&*self.window,
|
||||
&self.window,
|
||||
shm,
|
||||
subcompositor.clone(),
|
||||
self.queue_handle.clone(),
|
||||
@@ -1026,13 +1025,6 @@ impl WindowState {
|
||||
|
||||
impl Drop for WindowState {
|
||||
fn drop(&mut self) {
|
||||
let surface = self.window.wl_surface().clone();
|
||||
unsafe {
|
||||
ManuallyDrop::drop(&mut self.window);
|
||||
}
|
||||
|
||||
// Cleanup objects.
|
||||
|
||||
if let Some(blur) = self.blur.take() {
|
||||
blur.release();
|
||||
}
|
||||
@@ -1045,7 +1037,8 @@ impl Drop for WindowState {
|
||||
viewport.destroy();
|
||||
}
|
||||
|
||||
surface.destroy();
|
||||
// NOTE: the wl_surface used by the window is being cleaned up when
|
||||
// dropping SCTK `Window`.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -570,6 +570,14 @@ impl<T: 'static> EventProcessor<T> {
|
||||
event: WindowEvent::Destroyed,
|
||||
});
|
||||
}
|
||||
ffi::PropertyNotify => {
|
||||
let xev: &ffi::XPropertyEvent = xev.as_ref();
|
||||
let atom = xev.atom as xproto::Atom;
|
||||
|
||||
if atom == xproto::Atom::from(xproto::AtomEnum::RESOURCE_MANAGER) {
|
||||
self.process_dpi_change(&mut callback);
|
||||
}
|
||||
}
|
||||
|
||||
ffi::VisibilityNotify => {
|
||||
let xev: &ffi::XVisibilityEvent = xev.as_ref();
|
||||
@@ -1306,72 +1314,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
}
|
||||
}
|
||||
if event_type == self.randr_event_offset as c_int {
|
||||
// In the future, it would be quite easy to emit monitor hotplug events.
|
||||
let prev_list = wt.xconn.invalidate_cached_monitor_list();
|
||||
if let Some(prev_list) = prev_list {
|
||||
let new_list = wt
|
||||
.xconn
|
||||
.available_monitors()
|
||||
.expect("Failed to get monitor list");
|
||||
for new_monitor in new_list {
|
||||
// Previous list may be empty, in case of disconnecting and
|
||||
// reconnecting the only one monitor. We still need to emit events in
|
||||
// this case.
|
||||
let maybe_prev_scale_factor = prev_list
|
||||
.iter()
|
||||
.find(|prev_monitor| prev_monitor.name == new_monitor.name)
|
||||
.map(|prev_monitor| prev_monitor.scale_factor);
|
||||
if Some(new_monitor.scale_factor) != maybe_prev_scale_factor {
|
||||
for (window_id, window) in wt.windows.borrow().iter() {
|
||||
if let Some(window) = window.upgrade() {
|
||||
// Check if the window is on this monitor
|
||||
let monitor =
|
||||
window.shared_state_lock().last_monitor.clone();
|
||||
if monitor.name == new_monitor.name {
|
||||
let (width, height) = window.inner_size_physical();
|
||||
let (new_width, new_height) = window.adjust_for_dpi(
|
||||
// If we couldn't determine the previous scale
|
||||
// factor (e.g., because all monitors were closed
|
||||
// before), just pick whatever the current monitor
|
||||
// has set as a baseline.
|
||||
maybe_prev_scale_factor
|
||||
.unwrap_or(monitor.scale_factor),
|
||||
new_monitor.scale_factor,
|
||||
width,
|
||||
height,
|
||||
&window.shared_state_lock(),
|
||||
);
|
||||
|
||||
let window_id = crate::window::WindowId(*window_id);
|
||||
let old_inner_size = PhysicalSize::new(width, height);
|
||||
let inner_size = Arc::new(Mutex::new(
|
||||
PhysicalSize::new(new_width, new_height),
|
||||
));
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor: new_monitor.scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(
|
||||
Arc::downgrade(&inner_size),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
let new_inner_size = *inner_size.lock().unwrap();
|
||||
drop(inner_size);
|
||||
|
||||
if new_inner_size != old_inner_size {
|
||||
let (new_width, new_height) = new_inner_size.into();
|
||||
window.request_inner_size_physical(
|
||||
new_width, new_height,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.process_dpi_change(&mut callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1464,6 +1407,45 @@ impl<T: 'static> EventProcessor<T> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn process_dpi_change<F>(&self, callback: &mut F)
|
||||
where
|
||||
F: FnMut(Event<T>),
|
||||
{
|
||||
let wt = get_xtarget(&self.target);
|
||||
|
||||
// In the future, it would be quite easy to emit monitor hotplug events.
|
||||
let prev_list = {
|
||||
let prev_list = wt.xconn.invalidate_cached_monitor_list();
|
||||
match prev_list {
|
||||
Some(prev_list) => prev_list,
|
||||
None => return,
|
||||
}
|
||||
};
|
||||
|
||||
let new_list = wt
|
||||
.xconn
|
||||
.available_monitors()
|
||||
.expect("Failed to get monitor list");
|
||||
for new_monitor in new_list {
|
||||
// Previous list may be empty, in case of disconnecting and
|
||||
// reconnecting the only one monitor. We still need to emit events in
|
||||
// this case.
|
||||
let maybe_prev_scale_factor = prev_list
|
||||
.iter()
|
||||
.find(|prev_monitor| prev_monitor.name == new_monitor.name)
|
||||
.map(|prev_monitor| prev_monitor.scale_factor);
|
||||
if Some(new_monitor.scale_factor) != maybe_prev_scale_factor {
|
||||
for window in wt.windows.borrow().iter().filter_map(|(_, w)| w.upgrade()) {
|
||||
window.refresh_dpi_for_monitor(
|
||||
&new_monitor,
|
||||
maybe_prev_scale_factor,
|
||||
&mut *callback,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_first_touch(first: &mut Option<u64>, num: &mut u32, id: u64, phase: TouchPhase) -> bool {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::ffi::CString;
|
||||
use std::iter;
|
||||
|
||||
use x11rb::connection::Connection;
|
||||
|
||||
@@ -56,10 +57,22 @@ impl XConnection {
|
||||
None => return self.create_empty_cursor(),
|
||||
};
|
||||
|
||||
let name = CString::new(cursor.name()).unwrap();
|
||||
unsafe {
|
||||
(self.xcursor.XcursorLibraryLoadCursor)(self.display, name.as_ptr() as *const c_char)
|
||||
let mut xcursor = 0;
|
||||
for &name in iter::once(&cursor.name()).chain(cursor.alt_names().iter()) {
|
||||
let name = CString::new(name).unwrap();
|
||||
xcursor = unsafe {
|
||||
(self.xcursor.XcursorLibraryLoadCursor)(
|
||||
self.display,
|
||||
name.as_ptr() as *const c_char,
|
||||
)
|
||||
};
|
||||
|
||||
if xcursor != 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
xcursor
|
||||
}
|
||||
|
||||
fn update_cursor(&self, window: xproto::Window, cursor: ffi::Cursor) -> Result<(), X11Error> {
|
||||
|
||||
@@ -22,6 +22,7 @@ use x11rb::{
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
||||
event::{Event, InnerSizeWriter, WindowEvent},
|
||||
event_loop::AsyncRequestSerial,
|
||||
platform_impl::{
|
||||
x11::{atoms::*, MonitorHandle as X11MonitorHandle, WakeSender, X11Error},
|
||||
@@ -276,7 +277,8 @@ impl UnownedWindow {
|
||||
| EventMask::KEYMAP_STATE
|
||||
| EventMask::BUTTON_PRESS
|
||||
| EventMask::BUTTON_RELEASE
|
||||
| EventMask::POINTER_MOTION;
|
||||
| EventMask::POINTER_MOTION
|
||||
| EventMask::PROPERTY_CHANGE;
|
||||
|
||||
aux = aux.event_mask(event_mask).border_pixel(0);
|
||||
|
||||
@@ -923,6 +925,51 @@ impl UnownedWindow {
|
||||
})
|
||||
}
|
||||
|
||||
/// Refresh the API for the given monitor.
|
||||
#[inline]
|
||||
pub(super) fn refresh_dpi_for_monitor<T: 'static>(
|
||||
&self,
|
||||
new_monitor: &X11MonitorHandle,
|
||||
maybe_prev_scale_factor: Option<f64>,
|
||||
mut callback: impl FnMut(Event<T>),
|
||||
) {
|
||||
// Check if the self is on this monitor
|
||||
let monitor = self.shared_state_lock().last_monitor.clone();
|
||||
if monitor.name == new_monitor.name {
|
||||
let (width, height) = self.inner_size_physical();
|
||||
let (new_width, new_height) = self.adjust_for_dpi(
|
||||
// If we couldn't determine the previous scale
|
||||
// factor (e.g., because all monitors were closed
|
||||
// before), just pick whatever the current monitor
|
||||
// has set as a baseline.
|
||||
maybe_prev_scale_factor.unwrap_or(monitor.scale_factor),
|
||||
new_monitor.scale_factor,
|
||||
width,
|
||||
height,
|
||||
&self.shared_state_lock(),
|
||||
);
|
||||
|
||||
let window_id = crate::window::WindowId(self.id());
|
||||
let old_inner_size = PhysicalSize::new(width, height);
|
||||
let inner_size = Arc::new(Mutex::new(PhysicalSize::new(new_width, new_height)));
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor: new_monitor.scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&inner_size)),
|
||||
},
|
||||
});
|
||||
|
||||
let new_inner_size = *inner_size.lock().unwrap();
|
||||
drop(inner_size);
|
||||
|
||||
if new_inner_size != old_inner_size {
|
||||
let (new_width, new_height) = new_inner_size.into();
|
||||
self.request_inner_size_physical(new_width, new_height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_minimized_inner(&self, minimized: bool) -> Result<VoidCookie<'_>, X11Error> {
|
||||
let atoms = self.xconn.atoms();
|
||||
|
||||
|
||||
@@ -329,7 +329,7 @@ impl Handler {
|
||||
) {
|
||||
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
|
||||
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||
let event = Event::WindowEvent {
|
||||
let scale_factor_changed_event = Event::WindowEvent {
|
||||
window_id: WindowId(window.id()),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
@@ -337,13 +337,19 @@ impl Handler {
|
||||
},
|
||||
};
|
||||
|
||||
callback.handle_nonuser_event(event);
|
||||
callback.handle_nonuser_event(scale_factor_changed_event);
|
||||
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
let logical_size = physical_size.to_logical(scale_factor);
|
||||
let size = NSSize::new(logical_size.width, logical_size.height);
|
||||
window.setContentSize(size);
|
||||
|
||||
let resized_event = Event::WindowEvent {
|
||||
window_id: WindowId(window.id()),
|
||||
event: WindowEvent::Resized(physical_size),
|
||||
};
|
||||
callback.handle_nonuser_event(resized_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,13 +156,11 @@ pub(crate) fn create_key_event(
|
||||
// Also not checking if this is a release event because then this issue would
|
||||
// still affect the key release.
|
||||
Some(text) if !has_ctrl => Key::Character(text.clone()),
|
||||
_ => {
|
||||
let modifierless_chars = match key_without_modifiers.as_ref() {
|
||||
Key::Character(ch) => ch,
|
||||
_ => "",
|
||||
};
|
||||
get_logical_key_char(ns_event, modifierless_chars)
|
||||
}
|
||||
_ => match key_without_modifiers.as_ref() {
|
||||
Key::Character(ch) => get_logical_key_char(ns_event, ch),
|
||||
// Don't try to get text for events which likely don't have it.
|
||||
_ => key_without_modifiers.clone(),
|
||||
},
|
||||
};
|
||||
|
||||
(logical_key, key_without_modifiers)
|
||||
|
||||
@@ -74,8 +74,8 @@ enum ImeState {
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
struct ModLocationMask: u8 {
|
||||
const LEFT = 1;
|
||||
const RIGHT = 2;
|
||||
const LEFT = 0b0001;
|
||||
const RIGHT = 0b0010;
|
||||
}
|
||||
}
|
||||
impl ModLocationMask {
|
||||
@@ -88,13 +88,13 @@ impl ModLocationMask {
|
||||
}
|
||||
}
|
||||
|
||||
fn key_to_modifier(key: &Key) -> ModifiersState {
|
||||
fn key_to_modifier(key: &Key) -> Option<ModifiersState> {
|
||||
match key {
|
||||
Key::Named(NamedKey::Alt) => ModifiersState::ALT,
|
||||
Key::Named(NamedKey::Control) => ModifiersState::CONTROL,
|
||||
Key::Named(NamedKey::Super) => ModifiersState::SUPER,
|
||||
Key::Named(NamedKey::Shift) => ModifiersState::SHIFT,
|
||||
_ => unreachable!(),
|
||||
Key::Named(NamedKey::Alt) => Some(ModifiersState::ALT),
|
||||
Key::Named(NamedKey::Control) => Some(ModifiersState::CONTROL),
|
||||
Key::Named(NamedKey::Super) => Some(ModifiersState::SUPER),
|
||||
Key::Named(NamedKey::Shift) => Some(ModifiersState::SHIFT),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -924,91 +924,96 @@ impl WinitView {
|
||||
// event, thus we can't generate regular presses based on that. The `ModifiersChanged`
|
||||
// later will work though, since the flags are attached to the event and contain valid
|
||||
// information.
|
||||
if is_flags_changed_event && ns_event.key_code() != 0 {
|
||||
let scancode = ns_event.key_code();
|
||||
let physical_key = PhysicalKey::from_scancode(scancode as u32);
|
||||
'send_event: {
|
||||
if is_flags_changed_event && ns_event.key_code() != 0 {
|
||||
let scancode = ns_event.key_code();
|
||||
let physical_key = PhysicalKey::from_scancode(scancode as u32);
|
||||
|
||||
// We'll correct the `is_press` later.
|
||||
let mut event = create_key_event(ns_event, false, false, Some(physical_key));
|
||||
// We'll correct the `is_press` later.
|
||||
let mut event = create_key_event(ns_event, false, false, Some(physical_key));
|
||||
|
||||
let key = code_to_key(physical_key, scancode);
|
||||
let event_modifier = key_to_modifier(&key);
|
||||
event.physical_key = physical_key;
|
||||
event.logical_key = key.clone();
|
||||
event.location = code_to_location(physical_key);
|
||||
let location_mask = ModLocationMask::from_location(event.location);
|
||||
let key = code_to_key(physical_key, scancode);
|
||||
// Ignore processing of unkown modifiers because we can't determine whether
|
||||
// it was pressed or release reliably.
|
||||
let Some(event_modifier) = key_to_modifier(&key) else {
|
||||
break 'send_event;
|
||||
};
|
||||
event.physical_key = physical_key;
|
||||
event.logical_key = key.clone();
|
||||
event.location = code_to_location(physical_key);
|
||||
let location_mask = ModLocationMask::from_location(event.location);
|
||||
|
||||
let mut phys_mod_state = self.state.phys_modifiers.borrow_mut();
|
||||
let phys_mod = phys_mod_state
|
||||
.entry(key)
|
||||
.or_insert(ModLocationMask::empty());
|
||||
let mut phys_mod_state = self.state.phys_modifiers.borrow_mut();
|
||||
let phys_mod = phys_mod_state
|
||||
.entry(key)
|
||||
.or_insert(ModLocationMask::empty());
|
||||
|
||||
let is_active = current_modifiers.state().contains(event_modifier);
|
||||
let mut events = VecDeque::with_capacity(2);
|
||||
let is_active = current_modifiers.state().contains(event_modifier);
|
||||
let mut events = VecDeque::with_capacity(2);
|
||||
|
||||
// There is no API for getting whether the button was pressed or released
|
||||
// during this event. For this reason we have to do a bit of magic below
|
||||
// to come up with a good guess whether this key was pressed or released.
|
||||
// (This is not trivial because there are multiple buttons that may affect
|
||||
// the same modifier)
|
||||
if !is_active {
|
||||
event.state = Released;
|
||||
if phys_mod.contains(ModLocationMask::LEFT) {
|
||||
let mut event = event.clone();
|
||||
event.location = KeyLocation::Left;
|
||||
event.physical_key = get_left_modifier_code(&event.logical_key).into();
|
||||
events.push_back(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event,
|
||||
is_synthetic: false,
|
||||
});
|
||||
}
|
||||
if phys_mod.contains(ModLocationMask::RIGHT) {
|
||||
event.location = KeyLocation::Right;
|
||||
event.physical_key = get_right_modifier_code(&event.logical_key).into();
|
||||
events.push_back(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event,
|
||||
is_synthetic: false,
|
||||
});
|
||||
}
|
||||
*phys_mod = ModLocationMask::empty();
|
||||
} else {
|
||||
// is_active
|
||||
if *phys_mod == location_mask {
|
||||
// Here we hit a contradiction:
|
||||
// The modifier state was "changed" to active,
|
||||
// yet the only pressed modifier key was the one that we
|
||||
// just got a change event for.
|
||||
// This seemingly means that the only pressed modifier is now released,
|
||||
// but at the same time the modifier became active.
|
||||
//
|
||||
// But this scenario is possible if we released modifiers
|
||||
// while the application was not in focus. (Because we don't
|
||||
// get informed of modifier key events while the application
|
||||
// is not focused)
|
||||
|
||||
// In this case we prioritize the information
|
||||
// about the current modifier state which means
|
||||
// that the button was pressed.
|
||||
event.state = Pressed;
|
||||
// There is no API for getting whether the button was pressed or released
|
||||
// during this event. For this reason we have to do a bit of magic below
|
||||
// to come up with a good guess whether this key was pressed or released.
|
||||
// (This is not trivial because there are multiple buttons that may affect
|
||||
// the same modifier)
|
||||
if !is_active {
|
||||
event.state = Released;
|
||||
if phys_mod.contains(ModLocationMask::LEFT) {
|
||||
let mut event = event.clone();
|
||||
event.location = KeyLocation::Left;
|
||||
event.physical_key = get_left_modifier_code(&event.logical_key).into();
|
||||
events.push_back(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event,
|
||||
is_synthetic: false,
|
||||
});
|
||||
}
|
||||
if phys_mod.contains(ModLocationMask::RIGHT) {
|
||||
event.location = KeyLocation::Right;
|
||||
event.physical_key = get_right_modifier_code(&event.logical_key).into();
|
||||
events.push_back(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event,
|
||||
is_synthetic: false,
|
||||
});
|
||||
}
|
||||
*phys_mod = ModLocationMask::empty();
|
||||
} else {
|
||||
phys_mod.toggle(location_mask);
|
||||
let is_pressed = phys_mod.contains(location_mask);
|
||||
event.state = if is_pressed { Pressed } else { Released };
|
||||
if *phys_mod == location_mask {
|
||||
// Here we hit a contradiction:
|
||||
// The modifier state was "changed" to active,
|
||||
// yet the only pressed modifier key was the one that we
|
||||
// just got a change event for.
|
||||
// This seemingly means that the only pressed modifier is now released,
|
||||
// but at the same time the modifier became active.
|
||||
//
|
||||
// But this scenario is possible if we released modifiers
|
||||
// while the application was not in focus. (Because we don't
|
||||
// get informed of modifier key events while the application
|
||||
// is not focused)
|
||||
|
||||
// In this case we prioritize the information
|
||||
// about the current modifier state which means
|
||||
// that the button was pressed.
|
||||
event.state = Pressed;
|
||||
} else {
|
||||
phys_mod.toggle(location_mask);
|
||||
let is_pressed = phys_mod.contains(location_mask);
|
||||
event.state = if is_pressed { Pressed } else { Released };
|
||||
}
|
||||
|
||||
events.push_back(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event,
|
||||
is_synthetic: false,
|
||||
});
|
||||
}
|
||||
|
||||
events.push_back(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event,
|
||||
is_synthetic: false,
|
||||
});
|
||||
}
|
||||
drop(phys_mod_state);
|
||||
|
||||
drop(phys_mod_state);
|
||||
|
||||
for event in events {
|
||||
self.queue_event(event);
|
||||
for event in events {
|
||||
self.queue_event(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -356,19 +356,6 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
/// Wait for one message and dispatch it, optionally with a timeout
|
||||
fn wait_and_dispatch_message(&mut self, timeout: Option<Duration>) {
|
||||
let start = Instant::now();
|
||||
|
||||
let runner = &self.window_target.p.runner_shared;
|
||||
|
||||
let control_flow_timeout = match runner.control_flow() {
|
||||
ControlFlow::Wait => None,
|
||||
ControlFlow::Poll => Some(Duration::ZERO),
|
||||
ControlFlow::WaitUntil(wait_deadline) => {
|
||||
Some(wait_deadline.saturating_duration_since(start))
|
||||
}
|
||||
};
|
||||
let timeout = min_timeout(control_flow_timeout, timeout);
|
||||
|
||||
fn get_msg_with_timeout(msg: &mut MSG, timeout: Option<Duration>) -> PumpStatus {
|
||||
unsafe {
|
||||
// A timeout of None means wait indefinitely (so we don't need to call SetTimer)
|
||||
@@ -404,6 +391,8 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
let runner = &self.window_target.p.runner_shared;
|
||||
|
||||
// We aim to be consistent with the MacOS backend which has a RunLoop
|
||||
// observer that will dispatch AboutToWait when about to wait for
|
||||
// events, and NewEvents after the RunLoop wakes up.
|
||||
@@ -415,6 +404,16 @@ impl<T: 'static> EventLoop<T> {
|
||||
//
|
||||
runner.prepare_wait();
|
||||
|
||||
let control_flow_timeout = match runner.control_flow() {
|
||||
ControlFlow::Wait => None,
|
||||
ControlFlow::Poll => Some(Duration::ZERO),
|
||||
ControlFlow::WaitUntil(wait_deadline) => {
|
||||
let start = Instant::now();
|
||||
Some(wait_deadline.saturating_duration_since(start))
|
||||
}
|
||||
};
|
||||
let timeout = min_timeout(control_flow_timeout, timeout);
|
||||
|
||||
// # Safety
|
||||
// The Windows API has no documented requirement for bitwise
|
||||
// initializing a `MSG` struct (it can be uninitialized memory for the C
|
||||
|
||||
@@ -472,27 +472,41 @@ impl Window {
|
||||
}
|
||||
|
||||
unsafe fn handle_os_dragging(&self, wparam: WPARAM) {
|
||||
let points = {
|
||||
let mut pos = unsafe { mem::zeroed() };
|
||||
unsafe { GetCursorPos(&mut pos) };
|
||||
pos
|
||||
};
|
||||
let points = POINTS {
|
||||
x: points.x as i16,
|
||||
y: points.y as i16,
|
||||
};
|
||||
unsafe { ReleaseCapture() };
|
||||
let window = self.window.clone();
|
||||
let window_state = self.window_state.clone();
|
||||
|
||||
self.window_state_lock().dragging = true;
|
||||
self.thread_executor.execute_in_thread(move || {
|
||||
{
|
||||
let mut guard = window_state.lock().unwrap();
|
||||
if !guard.dragging {
|
||||
guard.dragging = true;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
PostMessageW(
|
||||
self.hwnd(),
|
||||
WM_NCLBUTTONDOWN,
|
||||
wparam,
|
||||
&points as *const _ as LPARAM,
|
||||
)
|
||||
};
|
||||
let points = {
|
||||
let mut pos = unsafe { mem::zeroed() };
|
||||
unsafe { GetCursorPos(&mut pos) };
|
||||
pos
|
||||
};
|
||||
let points = POINTS {
|
||||
x: points.x as i16,
|
||||
y: points.y as i16,
|
||||
};
|
||||
|
||||
// ReleaseCapture needs to execute on the main thread
|
||||
unsafe { ReleaseCapture() };
|
||||
|
||||
unsafe {
|
||||
PostMessageW(
|
||||
window.0,
|
||||
WM_NCLBUTTONDOWN,
|
||||
wparam,
|
||||
&points as *const _ as LPARAM,
|
||||
)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -600,11 +600,11 @@ impl Window {
|
||||
self.window.maybe_queue_on_main(|w| w.request_redraw())
|
||||
}
|
||||
|
||||
/// Notify the windowing system that you're before presenting to the window.
|
||||
/// Notify the windowing system before presenting to the window.
|
||||
///
|
||||
/// You should call this event after you've done drawing operations, but before you submit
|
||||
/// You should call this event after your drawing operations, but before you submit
|
||||
/// the buffer to the display or commit your drawings. Doing so will help winit to properly
|
||||
/// schedule and do assumptions about its internal state. For example, it could properly
|
||||
/// schedule and make assumptions about its internal state. For example, it could properly
|
||||
/// throttle [`WindowEvent::RedrawRequested`].
|
||||
///
|
||||
/// ## Example
|
||||
|
||||
Reference in New Issue
Block a user