mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-27 07:03:15 -04:00
Compare commits
30 Commits
madsmtm/ch
...
v0.28.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ebbfab6a4 | ||
|
|
55f60a35b1 | ||
|
|
640c51fe6f | ||
|
|
8e4ef92669 | ||
|
|
5278441547 | ||
|
|
340202f842 | ||
|
|
870e6d1cb2 | ||
|
|
b455f88f40 | ||
|
|
30678cbcef | ||
|
|
bb26b0355c | ||
|
|
6f97ff174b | ||
|
|
0881a28302 | ||
|
|
d37dac8f39 | ||
|
|
64a97c16c7 | ||
|
|
dec45bb09f | ||
|
|
d102c21792 | ||
|
|
68ed564def | ||
|
|
339d57b646 | ||
|
|
2e4dafc9fe | ||
|
|
0fbba02318 | ||
|
|
41e524f12c | ||
|
|
644c47a6f8 | ||
|
|
0be08e574d | ||
|
|
811cc5cdb7 | ||
|
|
230b37df56 | ||
|
|
09bca59cf3 | ||
|
|
9fb8aaa6f4 | ||
|
|
546ab7575e | ||
|
|
3e258a377f | ||
|
|
e5260da95b |
41
CHANGELOG.md
41
CHANGELOG.md
@@ -8,6 +8,47 @@ And please only add new entries to the top of this list, right below the `# Unre
|
||||
|
||||
# Unreleased
|
||||
|
||||
# 0.28.7
|
||||
|
||||
- Fix window size sometimes being invalid when resizing on macOS 14 Sonoma.
|
||||
|
||||
# 0.28.6
|
||||
|
||||
- On macOS, fixed memory leak when getting monitor handle.
|
||||
- On macOS, fix `Backspace` being emitted when clearing preedit with it.
|
||||
|
||||
# 0.28.5
|
||||
|
||||
- On macOS, fix `key_up` beind ignored when `Ime` is disabled.
|
||||
|
||||
# 0.28.4
|
||||
|
||||
- On macOS, fix empty marked text blocking regular input.
|
||||
- On macOS, fixed potential panic when getting refresh rate.
|
||||
- On macOS, fix crash when calling `Window::set_ime_position` from another thread.
|
||||
- On macOS, fixed memory leak when getting monitor handle.
|
||||
|
||||
# 0.28.3
|
||||
|
||||
- Fix macOS memory leaks.
|
||||
|
||||
# 0.28.2
|
||||
|
||||
- Implement `HasRawDisplayHandle` for `EventLoop`.
|
||||
- On macOS, set resize increments only for live resizes.
|
||||
- On Wayland, fix rare crash on DPI change
|
||||
- Web: Added support for `Window::theme`.
|
||||
- On Wayland, fix rounding issues when doing resize.
|
||||
- On macOS, fix wrong focused state on startup.
|
||||
- On Windows, fix crash on setting taskbar when using Visual Studio debugger.
|
||||
- On macOS, resize simple fullscreen windows on windowDidChangeScreen events.
|
||||
|
||||
# 0.28.1
|
||||
|
||||
- On Wayland, fix crash when dropping a window in multi-window setup.
|
||||
|
||||
# 0.28.0
|
||||
|
||||
- On macOS, fixed `Ime::Commit` persisting for all input after interacting with `Ime`.
|
||||
- On macOS, added `WindowExtMacOS::option_as_alt` and `WindowExtMacOS::set_option_as_alt`.
|
||||
- On Windows, fix window size for maximized, undecorated windows.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.27.5"
|
||||
version = "0.28.7"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
edition = "2021"
|
||||
@@ -68,7 +68,7 @@ ndk = "0.7.0"
|
||||
|
||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||
core-foundation = "0.9.3"
|
||||
objc2 = "=0.3.0-beta.3"
|
||||
objc2 = ">=0.3.0-beta.3, <0.3.0-beta.4" # Allow `0.3.0-beta.3.patch-leaks`
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-graphics = "0.22.3"
|
||||
|
||||
@@ -179,7 +179,7 @@ Legend:
|
||||
|Providing pointer to init Vulkan |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |**N/A**|**N/A** |
|
||||
|Window decorations |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|✔️ |
|
||||
|Window decorations toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Window resizing |✔️ |▢[#219]|✔️ |▢[#306] |**N/A**|**N/A**|✔️ |✔️ |
|
||||
|Window resizing |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ |
|
||||
|Window resize increments |❌ |✔️ |✔️ |❌ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A |✔️ |
|
||||
|Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
@@ -210,7 +210,7 @@ Legend:
|
||||
|Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |✔️ |**N/A** |
|
||||
|Multitouch |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ |**N/A** |
|
||||
|Keyboard events |✔️ |✔️ |✔️ |✔️ |✔️ |❌ |✔️ |✔️ |
|
||||
|Drag & Drop |▢[#720] |▢[#720] |▢[#720] |❌[#306] |**N/A**|**N/A**|❓ |**N/A** |
|
||||
|Drag & Drop |▢[#720] |▢[#720] |▢[#720] |▢[#720] |**N/A**|**N/A**|❓ |**N/A** |
|
||||
|Raw Device Events |▢[#750] |▢[#750] |▢[#750] |❌ |❌ |❌ |❓ |**N/A** |
|
||||
|Gamepad/Joystick events |❌[#804] |❌ |❌ |❌ |❌ |❌ |❓ |**N/A** |
|
||||
|Device movement events |❓ |❓ |❓ |❓ |❌ |❌ |❓ |**N/A** |
|
||||
@@ -223,7 +223,7 @@ Changes in the API that have been agreed upon but aren't implemented across all
|
||||
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |Redox OS|
|
||||
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
||||
|New API for HiDPI ([#315] [#319]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |❓ |
|
||||
|Event Loop 2.0 ([#459]) |✔️ |✔️ |❌ |✔️ |✔️ |✔️ |❓ |❓ |
|
||||
|Event Loop 2.0 ([#459]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |❓ |
|
||||
|Keyboard Input ([#812]) |❌ |❌ |❌ |❌ |❌ |❌ |❓ |❓ |
|
||||
|
||||
### Completed API Reworks
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.27.5"
|
||||
winit = "0.28.7"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
|
||||
@@ -5,6 +5,9 @@ use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEve
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::window::{Fullscreen, WindowBuilder};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use winit::platform::macos::WindowExtMacOS;
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
@@ -32,6 +35,8 @@ fn main() {
|
||||
println!("- Esc\tExit");
|
||||
println!("- F\tToggle exclusive fullscreen mode");
|
||||
println!("- B\tToggle borderless mode");
|
||||
#[cfg(target_os = "macos")]
|
||||
println!("- C\tToggle simple fullscreen mode");
|
||||
println!("- S\tNext screen");
|
||||
println!("- M\tNext mode for this screen");
|
||||
println!("- D\tToggle window decorations");
|
||||
@@ -67,6 +72,10 @@ fn main() {
|
||||
println!("Setting mode: {fullscreen:?}");
|
||||
window.set_fullscreen(fullscreen);
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
VirtualKeyCode::C => {
|
||||
window.set_simple_fullscreen(!window.simple_fullscreen());
|
||||
}
|
||||
VirtualKeyCode::S => {
|
||||
monitor_index += 1;
|
||||
if let Some(mon) = elwt.available_monitors().nth(monitor_index) {
|
||||
|
||||
@@ -313,6 +313,13 @@ impl<T> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> HasRawDisplayHandle for EventLoop<T> {
|
||||
/// Returns a [`raw_window_handle::RawDisplayHandle`] for the event loop.
|
||||
fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
self.event_loop.window_target().p.raw_display_handle()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for EventLoop<T> {
|
||||
type Target = EventLoopWindowTarget<T>;
|
||||
fn deref(&self) -> &EventLoopWindowTarget<T> {
|
||||
|
||||
@@ -19,6 +19,7 @@ use sctk::environment::Environment;
|
||||
use sctk::seat::pointer::{ThemeManager, ThemeSpec};
|
||||
use sctk::WaylandSource;
|
||||
|
||||
use crate::dpi::{LogicalSize, PhysicalSize};
|
||||
use crate::event::{Event, StartCause, WindowEvent};
|
||||
use crate::event_loop::{ControlFlow, EventLoopWindowTarget as RootEventLoopWindowTarget};
|
||||
use crate::platform_impl::platform::sticky_exit_callback;
|
||||
@@ -385,7 +386,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
let window_size = window_compositor_update.size.unwrap_or(*size);
|
||||
*size = window_size;
|
||||
|
||||
window_size.to_physical(scale_factor)
|
||||
logical_to_physical_rounded(window_size, scale_factor)
|
||||
});
|
||||
|
||||
sticky_exit_callback(
|
||||
@@ -430,7 +431,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
} else {
|
||||
*window_size = size;
|
||||
let scale_factor = window_handle.scale_factor();
|
||||
let physical_size = size.to_physical(scale_factor);
|
||||
let physical_size = logical_to_physical_rounded(size, scale_factor);
|
||||
Some(physical_size)
|
||||
};
|
||||
|
||||
@@ -599,3 +600,10 @@ impl<T: 'static> EventLoop<T> {
|
||||
.map_err(|error| error.into())
|
||||
}
|
||||
}
|
||||
|
||||
// The default routine does floor, but we need round on Wayland.
|
||||
fn logical_to_physical_rounded(size: LogicalSize<u32>, scale_factor: f64) -> PhysicalSize<u32> {
|
||||
let width = size.width as f64 * scale_factor;
|
||||
let height = size.height as f64 * scale_factor;
|
||||
(width.round(), height.round()).into()
|
||||
}
|
||||
|
||||
@@ -24,7 +24,10 @@ pub(super) fn handle_keyboard(
|
||||
KeyboardEvent::Enter { surface, .. } => {
|
||||
let window_id = wayland::make_wid(&surface);
|
||||
|
||||
let window_handle = winit_state.window_map.get_mut(&window_id).unwrap();
|
||||
let window_handle = match winit_state.window_map.get_mut(&window_id) {
|
||||
Some(window_handle) => window_handle,
|
||||
None => return,
|
||||
};
|
||||
window_handle.has_focus.store(true, Ordering::Relaxed);
|
||||
|
||||
// Window gained focus.
|
||||
@@ -39,7 +42,14 @@ pub(super) fn handle_keyboard(
|
||||
inner.target_window_id = Some(window_id);
|
||||
}
|
||||
KeyboardEvent::Leave { surface, .. } => {
|
||||
// Reset the id.
|
||||
inner.target_window_id = None;
|
||||
|
||||
let window_id = wayland::make_wid(&surface);
|
||||
let window_handle = match winit_state.window_map.get_mut(&window_id) {
|
||||
Some(window_handle) => window_handle,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// Notify that no modifiers are being pressed.
|
||||
if !inner.modifiers_state.borrow().is_empty() {
|
||||
@@ -49,14 +59,10 @@ pub(super) fn handle_keyboard(
|
||||
);
|
||||
}
|
||||
|
||||
let window_handle = winit_state.window_map.get_mut(&window_id).unwrap();
|
||||
window_handle.has_focus.store(false, Ordering::Relaxed);
|
||||
|
||||
// Window lost focus.
|
||||
event_sink.push_window_event(WindowEvent::Focused(false), window_id);
|
||||
|
||||
// Reset the id.
|
||||
inner.target_window_id = None;
|
||||
}
|
||||
KeyboardEvent::Key {
|
||||
rawkey,
|
||||
|
||||
@@ -45,9 +45,6 @@ pub(super) fn handle_pointer(
|
||||
pointer_data.latest_enter_serial.replace(serial);
|
||||
|
||||
let window_id = wayland::make_wid(&surface);
|
||||
if !winit_state.window_map.contains_key(&window_id) {
|
||||
return;
|
||||
}
|
||||
let window_handle = match winit_state.window_map.get_mut(&window_id) {
|
||||
Some(window_handle) => window_handle,
|
||||
None => return,
|
||||
|
||||
@@ -128,6 +128,12 @@ impl Window {
|
||||
let surface = event_loop_window_target
|
||||
.env
|
||||
.create_surface_with_scale_callback(move |scale, surface, mut dispatch_data| {
|
||||
// While I'm not sure how this could happen, we can safely ignore it
|
||||
// for now as a quickfix.
|
||||
if !surface.as_ref().is_alive() {
|
||||
return;
|
||||
}
|
||||
|
||||
let winit_state = dispatch_data.get::<WinitState>().unwrap();
|
||||
|
||||
// Get the window that received the event.
|
||||
@@ -164,12 +170,12 @@ impl Window {
|
||||
use sctk::window::{Event, State};
|
||||
|
||||
let winit_state = dispatch_data.get::<WinitState>().unwrap();
|
||||
let mut window_compositor_update = winit_state
|
||||
let window_compositor_update = winit_state
|
||||
.window_compositor_updates
|
||||
.get_mut(&window_id)
|
||||
.unwrap();
|
||||
|
||||
let mut window_user_requests = winit_state
|
||||
let window_user_requests = winit_state
|
||||
.window_user_requests
|
||||
.get_mut(&window_id)
|
||||
.unwrap();
|
||||
|
||||
@@ -384,7 +384,7 @@ impl UnownedWindow {
|
||||
}
|
||||
}
|
||||
|
||||
let mut shared_state = window.shared_state.get_mut().unwrap();
|
||||
let shared_state = window.shared_state.get_mut().unwrap();
|
||||
shared_state.min_inner_size = min_inner_size.map(Into::into);
|
||||
shared_state.max_inner_size = max_inner_size.map(Into::into);
|
||||
shared_state.resize_increments = window_attrs.resize_increments;
|
||||
|
||||
@@ -39,21 +39,25 @@ extern_methods!(
|
||||
}
|
||||
|
||||
pub fn display_id(&self) -> u32 {
|
||||
let device_description = self.deviceDescription();
|
||||
let key = ns_string!("NSScreenNumber");
|
||||
|
||||
// Retrieve the CGDirectDisplayID associated with this screen
|
||||
//
|
||||
// SAFETY: The value from @"NSScreenNumber" in deviceDescription is guaranteed
|
||||
// to be an NSNumber. See documentation for `deviceDescription` for details:
|
||||
// <https://developer.apple.com/documentation/appkit/nsscreen/1388360-devicedescription?language=objc>
|
||||
let obj = device_description
|
||||
.get(ns_string!("NSScreenNumber"))
|
||||
.expect("failed getting screen display id from device description");
|
||||
let obj: *const Object = obj;
|
||||
let obj: *const NSNumber = obj.cast();
|
||||
let obj: &NSNumber = unsafe { &*obj };
|
||||
objc2::rc::autoreleasepool(|_| {
|
||||
let device_description = self.deviceDescription();
|
||||
|
||||
obj.as_u32()
|
||||
// Retrieve the CGDirectDisplayID associated with this screen
|
||||
//
|
||||
// SAFETY: The value from @"NSScreenNumber" in deviceDescription is guaranteed
|
||||
// to be an NSNumber. See documentation for `deviceDescription` for details:
|
||||
// <https://developer.apple.com/documentation/appkit/nsscreen/1388360-devicedescription?language=objc>
|
||||
let obj = device_description
|
||||
.get(key)
|
||||
.expect("failed getting screen display id from device description");
|
||||
let obj: *const Object = obj;
|
||||
let obj: *const NSNumber = obj.cast();
|
||||
let obj: &NSNumber = unsafe { &*obj };
|
||||
|
||||
obj.as_u32()
|
||||
})
|
||||
}
|
||||
|
||||
#[sel(backingScaleFactor)]
|
||||
|
||||
@@ -44,9 +44,6 @@ extern_methods!(
|
||||
unsafe { msg_send_id![self, inputContext] }
|
||||
}
|
||||
|
||||
#[sel(visibleRect)]
|
||||
pub fn visibleRect(&self) -> NSRect;
|
||||
|
||||
#[sel(hasMarkedText)]
|
||||
pub fn hasMarkedText(&self) -> bool;
|
||||
|
||||
|
||||
@@ -230,7 +230,9 @@ impl MonitorHandle {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some((time.time_scale as i64 / time.time_value * 1000) as u32)
|
||||
(time.time_scale as i64)
|
||||
.checked_div(time.time_value)
|
||||
.map(|v| (v * 1000) as u32)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@ use std::ops::Deref;
|
||||
|
||||
use dispatch::Queue;
|
||||
use objc2::foundation::{is_main_thread, CGFloat, NSPoint, NSSize, NSString};
|
||||
use objc2::rc::autoreleasepool;
|
||||
use objc2::rc::{autoreleasepool, Id};
|
||||
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
dpi::{LogicalPosition, LogicalSize},
|
||||
platform_impl::platform::{
|
||||
appkit::{NSScreen, NSWindow, NSWindowLevel, NSWindowStyleMask},
|
||||
ffi,
|
||||
@@ -201,3 +201,11 @@ pub(crate) fn close_sync(window: &NSWindow) {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn set_ime_position_sync(window: &WinitWindow, logical_spot: LogicalPosition<f64>) {
|
||||
let window = MainThreadSafe(window);
|
||||
run_on_main(move || {
|
||||
// TODO(madsmtm): Remove the need for this
|
||||
unsafe { Id::from_shared(window.view()) }.set_ime_position(logical_spot);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use objc2::foundation::{
|
||||
NSArray, NSAttributedString, NSAttributedStringKey, NSCopying, NSMutableAttributedString,
|
||||
NSObject, NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger,
|
||||
};
|
||||
use objc2::rc::{Id, Owned, Shared};
|
||||
use objc2::rc::{Id, Owned, Shared, WeakId};
|
||||
use objc2::runtime::{Object, Sel};
|
||||
use objc2::{class, declare_class, msg_send, msg_send_id, sel, ClassType};
|
||||
|
||||
@@ -50,13 +50,14 @@ impl Default for CursorState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
enum ImeState {
|
||||
/// The IME events are disabled, so only `ReceivedCharacter` is being sent to the user.
|
||||
Disabled,
|
||||
|
||||
/// The IME events are enabled.
|
||||
Enabled,
|
||||
/// The ground state of enabled IME input. It means that both Preedit and regular keyboard
|
||||
/// input could be start from it.
|
||||
Ground,
|
||||
|
||||
/// The IME is in preedit.
|
||||
Preedit,
|
||||
@@ -136,7 +137,8 @@ declare_class!(
|
||||
#[derive(Debug)]
|
||||
#[allow(non_snake_case)]
|
||||
pub(super) struct WinitView {
|
||||
_ns_window: IvarDrop<Id<WinitWindow, Shared>>,
|
||||
// Weak reference because the window keeps a strong reference to the view
|
||||
_ns_window: IvarDrop<Box<WeakId<WinitWindow>>>,
|
||||
pub(super) state: IvarDrop<Box<ViewState>>,
|
||||
marked_text: IvarDrop<Id<NSMutableAttributedString, Owned>>,
|
||||
accepts_first_mouse: bool,
|
||||
@@ -167,7 +169,10 @@ declare_class!(
|
||||
forward_key_to_app: false,
|
||||
};
|
||||
|
||||
Ivar::write(&mut this._ns_window, window.retain());
|
||||
Ivar::write(
|
||||
&mut this._ns_window,
|
||||
Box::new(WeakId::new(&window.retain())),
|
||||
);
|
||||
Ivar::write(&mut this.state, Box::new(state));
|
||||
Ivar::write(&mut this.marked_text, NSMutableAttributedString::new());
|
||||
Ivar::write(&mut this.accepts_first_mouse, accepts_first_mouse);
|
||||
@@ -204,7 +209,7 @@ declare_class!(
|
||||
self.removeTrackingRect(tracking_rect);
|
||||
}
|
||||
|
||||
let rect = self.visibleRect();
|
||||
let rect = self.frame();
|
||||
let tracking_rect = self.add_tracking_rect(rect, false);
|
||||
self.state.tracking_rect = Some(tracking_rect);
|
||||
}
|
||||
@@ -216,7 +221,7 @@ declare_class!(
|
||||
self.removeTrackingRect(tracking_rect);
|
||||
}
|
||||
|
||||
let rect = self.visibleRect();
|
||||
let rect = self.frame();
|
||||
let tracking_rect = self.add_tracking_rect(rect, false);
|
||||
self.state.tracking_rect = Some(tracking_rect);
|
||||
|
||||
@@ -321,7 +326,7 @@ declare_class!(
|
||||
)
|
||||
};
|
||||
|
||||
// Update marked text
|
||||
// Update marked text.
|
||||
*self.marked_text = marked_text;
|
||||
|
||||
// Notify IME is active if application still doesn't know it.
|
||||
@@ -330,10 +335,11 @@ declare_class!(
|
||||
self.queue_event(WindowEvent::Ime(Ime::Enabled));
|
||||
}
|
||||
|
||||
// Don't update self.state to preedit when we've just commited a string, since the following
|
||||
// preedit string will be None anyway.
|
||||
if self.state.ime_state != ImeState::Commited {
|
||||
if self.hasMarkedText() {
|
||||
self.state.ime_state = ImeState::Preedit;
|
||||
} else {
|
||||
// In case the preedit was cleared, set IME into the Ground state.
|
||||
self.state.ime_state = ImeState::Ground;
|
||||
}
|
||||
|
||||
// Empty string basically means that there's no preedit, so indicate that by sending
|
||||
@@ -359,7 +365,7 @@ declare_class!(
|
||||
self.queue_event(WindowEvent::Ime(Ime::Preedit(String::new(), None)));
|
||||
if self.is_ime_enabled() {
|
||||
// Leave the Preedit self.state
|
||||
self.state.ime_state = ImeState::Enabled;
|
||||
self.state.ime_state = ImeState::Ground;
|
||||
} else {
|
||||
warn!("Expected to have IME enabled when receiving unmarkText");
|
||||
}
|
||||
@@ -447,8 +453,8 @@ declare_class!(
|
||||
self.state.forward_key_to_app = true;
|
||||
|
||||
if self.hasMarkedText() && self.state.ime_state == ImeState::Preedit {
|
||||
// Leave preedit so that we also report the keyup for this key
|
||||
self.state.ime_state = ImeState::Enabled;
|
||||
// Leave preedit so that we also report the key-up for this key.
|
||||
self.state.ime_state = ImeState::Ground;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,7 +469,6 @@ declare_class!(
|
||||
self.state.input_source = input_source;
|
||||
self.queue_event(WindowEvent::Ime(Ime::Disabled));
|
||||
}
|
||||
let was_in_preedit = self.state.ime_state == ImeState::Preedit;
|
||||
|
||||
// Get the characters from the event.
|
||||
let ev_mods = event_mods(event);
|
||||
@@ -476,6 +481,7 @@ declare_class!(
|
||||
&& !ev_mods.logo();
|
||||
|
||||
let characters = get_characters(event, ignore_alt_characters);
|
||||
let old_ime_state = self.state.ime_state;
|
||||
self.state.forward_key_to_app = false;
|
||||
|
||||
// The `interpretKeyEvents` function might call
|
||||
@@ -484,7 +490,6 @@ declare_class!(
|
||||
// we must send the `KeyboardInput` event during IME if it triggered
|
||||
// `doCommandBySelector`. (doCommandBySelector means that the keyboard input
|
||||
// is not handled by IME and should be handled by the application)
|
||||
let mut text_commited = false;
|
||||
if self.state.ime_allowed {
|
||||
let new_event = if ignore_alt_characters {
|
||||
replace_event_chars(event, &characters)
|
||||
@@ -499,21 +504,26 @@ declare_class!(
|
||||
if self.state.ime_state == ImeState::Commited {
|
||||
// Remove any marked text, so normal input can continue.
|
||||
*self.marked_text = NSMutableAttributedString::new();
|
||||
self.state.ime_state = ImeState::Enabled;
|
||||
text_commited = true;
|
||||
}
|
||||
}
|
||||
|
||||
let now_in_preedit = self.state.ime_state == ImeState::Preedit;
|
||||
|
||||
let scancode = event.scancode() as u32;
|
||||
let virtual_keycode = retrieve_keycode(event);
|
||||
|
||||
self.update_potentially_stale_modifiers(event);
|
||||
|
||||
let ime_related = was_in_preedit || now_in_preedit || text_commited;
|
||||
let had_ime_input = match self.state.ime_state {
|
||||
ImeState::Commited => {
|
||||
// Allow normal input after the commit.
|
||||
self.state.ime_state = ImeState::Ground;
|
||||
true
|
||||
}
|
||||
ImeState::Preedit => true,
|
||||
// `key_down` could result in preedit clear, so compare old and current state.
|
||||
_ => old_ime_state != self.state.ime_state,
|
||||
};
|
||||
|
||||
if !ime_related || self.state.forward_key_to_app || !self.state.ime_allowed {
|
||||
if !had_ime_input || self.state.forward_key_to_app {
|
||||
#[allow(deprecated)]
|
||||
self.queue_event(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
@@ -540,8 +550,8 @@ declare_class!(
|
||||
|
||||
self.update_potentially_stale_modifiers(event);
|
||||
|
||||
// We want to send keyboard input when we are not currently in preedit
|
||||
if self.state.ime_state != ImeState::Preedit {
|
||||
// We want to send keyboard input when we are currently in the ground state.
|
||||
if matches!(self.state.ime_state, ImeState::Ground | ImeState::Disabled) {
|
||||
#[allow(deprecated)]
|
||||
self.queue_event(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
@@ -873,11 +883,11 @@ impl WinitView {
|
||||
// (which is incompatible with `frameDidChange:`)
|
||||
//
|
||||
// unsafe { msg_send_id![self, window] }
|
||||
(*self._ns_window).clone()
|
||||
self._ns_window.load().expect("view to have a window")
|
||||
}
|
||||
|
||||
fn window_id(&self) -> WindowId {
|
||||
WindowId(self._ns_window.id())
|
||||
WindowId(self.window().id())
|
||||
}
|
||||
|
||||
fn queue_event(&self, event: WindowEvent<'static>) {
|
||||
|
||||
@@ -150,7 +150,7 @@ pub struct SharedState {
|
||||
pub(crate) target_fullscreen: Option<Option<Fullscreen>>,
|
||||
pub maximized: bool,
|
||||
pub standard_frame: Option<NSRect>,
|
||||
is_simple_fullscreen: bool,
|
||||
pub(crate) is_simple_fullscreen: bool,
|
||||
pub saved_style: Option<NSWindowStyleMask>,
|
||||
/// Presentation options saved before entering `set_simple_fullscreen`, and
|
||||
/// restored upon exiting it. Also used when transitioning from Borderless to
|
||||
@@ -160,6 +160,9 @@ pub struct SharedState {
|
||||
save_presentation_opts: Option<NSApplicationPresentationOptions>,
|
||||
pub current_theme: Option<Theme>,
|
||||
|
||||
/// The current resize incerments for the window content.
|
||||
pub(crate) resize_increments: NSSize,
|
||||
|
||||
/// The state of the `Option` as `Alt`.
|
||||
pub(crate) option_as_alt: OptionAsAlt,
|
||||
}
|
||||
@@ -301,6 +304,16 @@ impl WinitWindow {
|
||||
};
|
||||
|
||||
this.map(|mut this| {
|
||||
let resize_increments = match attrs
|
||||
.resize_increments
|
||||
.map(|i| i.to_logical::<f64>(this.scale_factor()))
|
||||
{
|
||||
Some(LogicalSize { width, height }) if width >= 1. && height >= 1. => {
|
||||
NSSize::new(width, height)
|
||||
}
|
||||
_ => NSSize::new(1., 1.),
|
||||
};
|
||||
|
||||
// Properly initialize the window's variables
|
||||
//
|
||||
// Ideally this should be done in an `init` method,
|
||||
@@ -308,6 +321,7 @@ impl WinitWindow {
|
||||
let state = SharedState {
|
||||
resizable: attrs.resizable,
|
||||
maximized: attrs.maximized,
|
||||
resize_increments,
|
||||
..Default::default()
|
||||
};
|
||||
Ivar::write(&mut this.shared_state, Box::new(Mutex::new(state)));
|
||||
@@ -353,19 +367,6 @@ impl WinitWindow {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(increments) = attrs.resize_increments {
|
||||
let increments = increments.to_logical(this.scale_factor());
|
||||
let (w, h) = (increments.width, increments.height);
|
||||
if w >= 1.0 && h >= 1.0 {
|
||||
let size = NSSize::new(w, h);
|
||||
// It was concluded (#2411) that there is never a use-case for
|
||||
// "outer" resize increments, hence we set "inner" ones here.
|
||||
// ("outer" in macOS being just resizeIncrements, and "inner" - contentResizeIncrements)
|
||||
// This is consistent with X11 size hints behavior
|
||||
this.setContentResizeIncrements(size);
|
||||
}
|
||||
}
|
||||
|
||||
if !pl_attrs.has_shadow {
|
||||
this.setHasShadow(false);
|
||||
}
|
||||
@@ -465,6 +466,10 @@ impl WinitWindow {
|
||||
|
||||
let delegate = WinitWindowDelegate::new(&this, attrs.fullscreen.is_some());
|
||||
|
||||
// XXX Send `Focused(false)` right after creating the window delegate, so we won't
|
||||
// obscure the real focused events on the startup.
|
||||
delegate.queue_event(WindowEvent::Focused(false));
|
||||
|
||||
// Set fullscreen mode after we setup everything
|
||||
this.set_fullscreen(attrs.fullscreen.map(Into::into));
|
||||
|
||||
@@ -484,8 +489,6 @@ impl WinitWindow {
|
||||
this.set_maximized(attrs.maximized);
|
||||
}
|
||||
|
||||
delegate.queue_event(WindowEvent::Focused(false));
|
||||
|
||||
Ok((this, delegate))
|
||||
}
|
||||
|
||||
@@ -648,7 +651,9 @@ impl WinitWindow {
|
||||
}
|
||||
|
||||
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
let increments = self.contentResizeIncrements();
|
||||
let increments = self
|
||||
.lock_shared_state("set_resize_increments")
|
||||
.resize_increments;
|
||||
let (w, h) = (increments.width, increments.height);
|
||||
if w > 1.0 || h > 1.0 {
|
||||
Some(LogicalSize::new(w, h).to_physical(self.scale_factor()))
|
||||
@@ -658,12 +663,21 @@ impl WinitWindow {
|
||||
}
|
||||
|
||||
pub fn set_resize_increments(&self, increments: Option<Size>) {
|
||||
let size = increments
|
||||
// XXX the resize increments are only used during live resizes.
|
||||
let mut shared_state_lock = self.lock_shared_state("set_resize_increments");
|
||||
shared_state_lock.resize_increments = increments
|
||||
.map(|increments| {
|
||||
let logical = increments.to_logical::<f64>(self.scale_factor());
|
||||
NSSize::new(logical.width.max(1.0), logical.height.max(1.0))
|
||||
})
|
||||
.unwrap_or_else(|| NSSize::new(1.0, 1.0));
|
||||
}
|
||||
|
||||
pub(crate) fn set_resize_increments_inner(&self, size: NSSize) {
|
||||
// It was concluded (#2411) that there is never a use-case for
|
||||
// "outer" resize increments, hence we set "inner" ones here.
|
||||
// ("outer" in macOS being just resizeIncrements, and "inner" - contentResizeIncrements)
|
||||
// This is consistent with X11 size hints behavior
|
||||
self.setContentResizeIncrements(size);
|
||||
}
|
||||
|
||||
@@ -1151,8 +1165,7 @@ impl WinitWindow {
|
||||
pub fn set_ime_position(&self, spot: Position) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let logical_spot = spot.to_logical(scale_factor);
|
||||
// TODO(madsmtm): Remove the need for this
|
||||
unsafe { Id::from_shared(self.view()) }.set_ime_position(logical_spot);
|
||||
util::set_ime_position_sync(self, logical_spot);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1303,6 +1316,10 @@ impl WindowExtMacOS for WinitWindow {
|
||||
// Tell our window's state that we're in fullscreen
|
||||
shared_state_lock.is_simple_fullscreen = true;
|
||||
|
||||
// Drop shared state lock before calling app.setPresentationOptions, because
|
||||
// it will call our windowDidChangeScreen listener which reacquires the lock
|
||||
drop(shared_state_lock);
|
||||
|
||||
// Simulate pre-Lion fullscreen by hiding the dock and menu bar
|
||||
let presentation_options =
|
||||
NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock
|
||||
@@ -1327,11 +1344,17 @@ impl WindowExtMacOS for WinitWindow {
|
||||
self.set_style_mask_sync(new_mask);
|
||||
shared_state_lock.is_simple_fullscreen = false;
|
||||
|
||||
if let Some(presentation_opts) = shared_state_lock.save_presentation_opts {
|
||||
let save_presentation_opts = shared_state_lock.save_presentation_opts;
|
||||
let frame = shared_state_lock.saved_standard_frame();
|
||||
|
||||
// Drop shared state lock before calling app.setPresentationOptions, because
|
||||
// it will call our windowDidChangeScreen listener which reacquires the lock
|
||||
drop(shared_state_lock);
|
||||
|
||||
if let Some(presentation_opts) = save_presentation_opts {
|
||||
app.setPresentationOptions(presentation_opts);
|
||||
}
|
||||
|
||||
let frame = shared_state_lock.saved_standard_frame();
|
||||
self.setFrame_display(frame, true);
|
||||
self.setMovable(true);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use std::ptr;
|
||||
|
||||
use objc2::declare::{Ivar, IvarDrop};
|
||||
use objc2::foundation::{NSArray, NSObject, NSString};
|
||||
use objc2::foundation::{NSArray, NSObject, NSSize, NSString};
|
||||
use objc2::rc::{autoreleasepool, Id, Shared};
|
||||
use objc2::runtime::Object;
|
||||
use objc2::{class, declare_class, msg_send, msg_send_id, sel, ClassType};
|
||||
@@ -117,6 +117,23 @@ declare_class!(
|
||||
self.emit_move_event();
|
||||
}
|
||||
|
||||
#[sel(windowWillStartLiveResize:)]
|
||||
fn window_will_start_live_resize(&mut self, _: Option<&Object>) {
|
||||
trace_scope!("windowWillStartLiveResize:");
|
||||
|
||||
let increments = self
|
||||
.window
|
||||
.lock_shared_state("window_will_enter_fullscreen")
|
||||
.resize_increments;
|
||||
self.window.set_resize_increments_inner(increments);
|
||||
}
|
||||
|
||||
#[sel(windowDidEndLiveResize:)]
|
||||
fn window_did_end_live_resize(&mut self, _: Option<&Object>) {
|
||||
trace_scope!("windowDidEndLiveResize:");
|
||||
self.window.set_resize_increments_inner(NSSize::new(1., 1.));
|
||||
}
|
||||
|
||||
// This won't be triggered if the move was part of a resize.
|
||||
#[sel(windowDidMove:)]
|
||||
fn window_did_move(&mut self, _: Option<&Object>) {
|
||||
@@ -392,6 +409,20 @@ declare_class!(
|
||||
self.queue_event(WindowEvent::ThemeChanged(theme));
|
||||
}
|
||||
}
|
||||
|
||||
#[sel(windowDidChangeScreen:)]
|
||||
fn window_did_change_screen(&self, _: Option<&Object>) {
|
||||
trace_scope!("windowDidChangeScreen:");
|
||||
let is_simple_fullscreen = self
|
||||
.window
|
||||
.lock_shared_state("window_did_change_screen")
|
||||
.is_simple_fullscreen;
|
||||
if is_simple_fullscreen {
|
||||
if let Some(screen) = self.window.screen() {
|
||||
self.window.setFrame_display(screen.frame(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -409,7 +409,20 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn theme(&self) -> Option<Theme> {
|
||||
None
|
||||
web_sys::window()
|
||||
.and_then(|window| {
|
||||
window
|
||||
.match_media("(prefers-color-scheme: dark)")
|
||||
.ok()
|
||||
.flatten()
|
||||
})
|
||||
.map(|media_query_list| {
|
||||
if media_query_list.matches() {
|
||||
Theme::Dark
|
||||
} else {
|
||||
Theme::Light
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -10,6 +10,7 @@ use std::{
|
||||
mem, panic, ptr,
|
||||
rc::Rc,
|
||||
sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
mpsc::{self, Receiver, Sender},
|
||||
Arc, Mutex, MutexGuard,
|
||||
},
|
||||
@@ -204,7 +205,10 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
let thread_msg_target = create_event_target_window::<T>();
|
||||
|
||||
thread::spawn(move || wait_thread(thread_id, thread_msg_target));
|
||||
thread::Builder::new()
|
||||
.name("winit wait thread".to_string())
|
||||
.spawn(move || wait_thread(thread_id, thread_msg_target))
|
||||
.expect("Failed to spawn winit wait thread");
|
||||
let wait_thread_id = get_wait_thread_id();
|
||||
|
||||
let runner_shared = Rc::new(EventLoopRunner::new(thread_msg_target, wait_thread_id));
|
||||
@@ -382,11 +386,12 @@ fn get_wait_thread_id() -> u32 {
|
||||
let result = GetMessageW(
|
||||
&mut msg,
|
||||
-1,
|
||||
*SEND_WAIT_THREAD_ID_MSG_ID,
|
||||
*SEND_WAIT_THREAD_ID_MSG_ID,
|
||||
SEND_WAIT_THREAD_ID_MSG_ID.get(),
|
||||
SEND_WAIT_THREAD_ID_MSG_ID.get(),
|
||||
);
|
||||
assert_eq!(
|
||||
msg.message, *SEND_WAIT_THREAD_ID_MSG_ID,
|
||||
msg.message,
|
||||
SEND_WAIT_THREAD_ID_MSG_ID.get(),
|
||||
"this shouldn't be possible. please open an issue with Winit. error code: {result}"
|
||||
);
|
||||
msg.lParam as u32
|
||||
@@ -412,7 +417,7 @@ fn wait_thread(parent_thread_id: u32, msg_window_id: HWND) {
|
||||
let cur_thread_id = GetCurrentThreadId();
|
||||
PostThreadMessageW(
|
||||
parent_thread_id,
|
||||
*SEND_WAIT_THREAD_ID_MSG_ID,
|
||||
SEND_WAIT_THREAD_ID_MSG_ID.get(),
|
||||
0,
|
||||
cur_thread_id as LPARAM,
|
||||
);
|
||||
@@ -436,9 +441,9 @@ fn wait_thread(parent_thread_id: u32, msg_window_id: HWND) {
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
|
||||
if msg.message == *WAIT_UNTIL_MSG_ID {
|
||||
if msg.message == WAIT_UNTIL_MSG_ID.get() {
|
||||
wait_until_opt = Some(*WaitUntilInstantBox::from_raw(msg.lParam as *mut _));
|
||||
} else if msg.message == *CANCEL_WAIT_UNTIL_MSG_ID {
|
||||
} else if msg.message == CANCEL_WAIT_UNTIL_MSG_ID.get() {
|
||||
wait_until_opt = None;
|
||||
}
|
||||
|
||||
@@ -466,11 +471,11 @@ fn wait_thread(parent_thread_id: u32, msg_window_id: HWND) {
|
||||
timeEndPeriod(period);
|
||||
}
|
||||
if resume_reason == WAIT_TIMEOUT {
|
||||
PostMessageW(msg_window_id, *PROCESS_NEW_EVENTS_MSG_ID, 0, 0);
|
||||
PostMessageW(msg_window_id, PROCESS_NEW_EVENTS_MSG_ID.get(), 0, 0);
|
||||
wait_until_opt = None;
|
||||
}
|
||||
} else {
|
||||
PostMessageW(msg_window_id, *PROCESS_NEW_EVENTS_MSG_ID, 0, 0);
|
||||
PostMessageW(msg_window_id, PROCESS_NEW_EVENTS_MSG_ID.get(), 0, 0);
|
||||
wait_until_opt = None;
|
||||
}
|
||||
}
|
||||
@@ -556,7 +561,7 @@ impl EventLoopThreadExecutor {
|
||||
|
||||
let raw = Box::into_raw(boxed2);
|
||||
|
||||
let res = PostMessageW(self.target_window, *EXEC_MSG_ID, raw as usize, 0);
|
||||
let res = PostMessageW(self.target_window, EXEC_MSG_ID.get(), raw as usize, 0);
|
||||
assert!(
|
||||
res != false.into(),
|
||||
"PostMessage failed; is the messages queue full?"
|
||||
@@ -586,7 +591,7 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
unsafe {
|
||||
if PostMessageW(self.target_window, *USER_EVENT_MSG_ID, 0, 0) != false.into() {
|
||||
if PostMessageW(self.target_window, USER_EVENT_MSG_ID.get(), 0, 0) != false.into() {
|
||||
self.event_send.send(event).ok();
|
||||
Ok(())
|
||||
} else {
|
||||
@@ -598,40 +603,84 @@ impl<T: 'static> EventLoopProxy<T> {
|
||||
|
||||
type WaitUntilInstantBox = Box<Instant>;
|
||||
|
||||
/// A lazily-initialized window message ID.
|
||||
pub struct LazyMessageId {
|
||||
/// The ID.
|
||||
id: AtomicU32,
|
||||
|
||||
/// The name of the message.
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
/// An invalid custom window ID.
|
||||
const INVALID_ID: u32 = 0x0;
|
||||
|
||||
impl LazyMessageId {
|
||||
/// Create a new `LazyId`.
|
||||
const fn new(name: &'static str) -> Self {
|
||||
Self {
|
||||
id: AtomicU32::new(INVALID_ID),
|
||||
name,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the message ID.
|
||||
pub fn get(&self) -> u32 {
|
||||
// Load the ID.
|
||||
let id = self.id.load(Ordering::Relaxed);
|
||||
|
||||
if id != INVALID_ID {
|
||||
return id;
|
||||
}
|
||||
|
||||
// Register the message.
|
||||
// SAFETY: We are sure that the pointer is a valid C string ending with '\0'.
|
||||
assert!(self.name.ends_with('\0'));
|
||||
let new_id = unsafe { RegisterWindowMessageA(self.name.as_ptr()) };
|
||||
|
||||
assert_ne!(
|
||||
new_id,
|
||||
0,
|
||||
"RegisterWindowMessageA returned zero for '{}': {}",
|
||||
self.name,
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
|
||||
// Store the new ID. Since `RegisterWindowMessageA` returns the same value for any given string,
|
||||
// the target value will always either be a). `INVALID_ID` or b). the correct ID. Therefore a
|
||||
// compare-and-swap operation here (or really any consideration) is never necessary.
|
||||
self.id.store(new_id, Ordering::Relaxed);
|
||||
|
||||
new_id
|
||||
}
|
||||
}
|
||||
|
||||
// Message sent by the `EventLoopProxy` when we want to wake up the thread.
|
||||
// WPARAM and LPARAM are unused.
|
||||
static USER_EVENT_MSG_ID: Lazy<u32> =
|
||||
Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::WakeupMsg\0".as_ptr()) });
|
||||
static USER_EVENT_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::WakeupMsg\0");
|
||||
// Message sent when we want to execute a closure in the thread.
|
||||
// WPARAM contains a Box<Box<dyn FnMut()>> that must be retrieved with `Box::from_raw`,
|
||||
// and LPARAM is unused.
|
||||
static EXEC_MSG_ID: Lazy<u32> =
|
||||
Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr()) });
|
||||
static PROCESS_NEW_EVENTS_MSG_ID: Lazy<u32> =
|
||||
Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::ProcessNewEvents\0".as_ptr()) });
|
||||
static EXEC_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::ExecMsg\0");
|
||||
static PROCESS_NEW_EVENTS_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::ProcessNewEvents\0");
|
||||
/// lparam is the wait thread's message id.
|
||||
static SEND_WAIT_THREAD_ID_MSG_ID: Lazy<u32> =
|
||||
Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::SendWaitThreadId\0".as_ptr()) });
|
||||
static SEND_WAIT_THREAD_ID_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::SendWaitThreadId\0");
|
||||
/// lparam points to a `Box<Instant>` signifying the time `PROCESS_NEW_EVENTS_MSG_ID` should
|
||||
/// be sent.
|
||||
static WAIT_UNTIL_MSG_ID: Lazy<u32> =
|
||||
Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::WaitUntil\0".as_ptr()) });
|
||||
static CANCEL_WAIT_UNTIL_MSG_ID: Lazy<u32> =
|
||||
Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::CancelWaitUntil\0".as_ptr()) });
|
||||
static WAIT_UNTIL_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::WaitUntil\0");
|
||||
static CANCEL_WAIT_UNTIL_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::CancelWaitUntil\0");
|
||||
// Message sent by a `Window` when it wants to be destroyed by the main thread.
|
||||
// WPARAM and LPARAM are unused.
|
||||
pub static DESTROY_MSG_ID: Lazy<u32> =
|
||||
Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::DestroyMsg\0".as_ptr()) });
|
||||
pub static DESTROY_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::DestroyMsg\0");
|
||||
// WPARAM is a bool specifying the `WindowFlags::MARKER_RETAIN_STATE_ON_SIZE` flag. See the
|
||||
// documentation in the `window_state` module for more information.
|
||||
pub static SET_RETAIN_STATE_ON_SIZE_MSG_ID: Lazy<u32> =
|
||||
Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::SetRetainMaximized\0".as_ptr()) });
|
||||
pub static SET_RETAIN_STATE_ON_SIZE_MSG_ID: LazyMessageId =
|
||||
LazyMessageId::new("Winit::SetRetainMaximized\0");
|
||||
static THREAD_EVENT_TARGET_WINDOW_CLASS: Lazy<Vec<u16>> =
|
||||
Lazy::new(|| util::encode_wide("Winit Thread Event Target"));
|
||||
/// When the taskbar is created, it registers a message with the "TaskbarCreated" string and then broadcasts this message to all top-level windows
|
||||
/// <https://docs.microsoft.com/en-us/windows/win32/shell/taskbar#taskbar-creation-notification>
|
||||
pub static TASKBAR_CREATED: Lazy<u32> =
|
||||
Lazy::new(|| unsafe { RegisterWindowMessageA("TaskbarCreated\0".as_ptr()) });
|
||||
pub static TASKBAR_CREATED: LazyMessageId = LazyMessageId::new("TaskbarCreated\0");
|
||||
|
||||
fn create_event_target_window<T: 'static>() -> HWND {
|
||||
use windows_sys::Win32::UI::WindowsAndMessaging::CS_HREDRAW;
|
||||
@@ -783,13 +832,18 @@ unsafe fn flush_paint_messages<T: 'static>(
|
||||
unsafe fn process_control_flow<T: 'static>(runner: &EventLoopRunner<T>) {
|
||||
match runner.control_flow() {
|
||||
ControlFlow::Poll => {
|
||||
PostMessageW(runner.thread_msg_target(), *PROCESS_NEW_EVENTS_MSG_ID, 0, 0);
|
||||
PostMessageW(
|
||||
runner.thread_msg_target(),
|
||||
PROCESS_NEW_EVENTS_MSG_ID.get(),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
}
|
||||
ControlFlow::Wait => (),
|
||||
ControlFlow::WaitUntil(until) => {
|
||||
PostThreadMessageW(
|
||||
runner.wait_thread_id(),
|
||||
*WAIT_UNTIL_MSG_ID,
|
||||
WAIT_UNTIL_MSG_ID.get(),
|
||||
0,
|
||||
Box::into_raw(WaitUntilInstantBox::new(until)) as isize,
|
||||
);
|
||||
@@ -2244,16 +2298,16 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
||||
}
|
||||
|
||||
_ => {
|
||||
if msg == *DESTROY_MSG_ID {
|
||||
if msg == DESTROY_MSG_ID.get() {
|
||||
DestroyWindow(window);
|
||||
0
|
||||
} else if msg == *SET_RETAIN_STATE_ON_SIZE_MSG_ID {
|
||||
} else if msg == SET_RETAIN_STATE_ON_SIZE_MSG_ID.get() {
|
||||
let mut window_state = userdata.window_state_lock();
|
||||
window_state.set_window_flags_in_place(|f| {
|
||||
f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam != 0)
|
||||
});
|
||||
0
|
||||
} else if msg == *TASKBAR_CREATED {
|
||||
} else if msg == TASKBAR_CREATED.get() {
|
||||
let window_state = userdata.window_state_lock();
|
||||
set_skip_taskbar(window, window_state.skip_taskbar);
|
||||
DefWindowProcW(window, msg, wparam, lparam)
|
||||
@@ -2442,21 +2496,21 @@ unsafe extern "system" fn thread_event_target_callback<T: 'static>(
|
||||
DefWindowProcW(window, msg, wparam, lparam)
|
||||
}
|
||||
|
||||
_ if msg == *USER_EVENT_MSG_ID => {
|
||||
_ if msg == USER_EVENT_MSG_ID.get() => {
|
||||
if let Ok(event) = userdata.user_event_receiver.recv() {
|
||||
userdata.send_event(Event::UserEvent(event));
|
||||
}
|
||||
0
|
||||
}
|
||||
_ if msg == *EXEC_MSG_ID => {
|
||||
_ if msg == EXEC_MSG_ID.get() => {
|
||||
let mut function: ThreadExecFn = Box::from_raw(wparam as *mut _);
|
||||
function();
|
||||
0
|
||||
}
|
||||
_ if msg == *PROCESS_NEW_EVENTS_MSG_ID => {
|
||||
_ if msg == PROCESS_NEW_EVENTS_MSG_ID.get() => {
|
||||
PostThreadMessageW(
|
||||
userdata.event_loop_runner.wait_thread_id(),
|
||||
*CANCEL_WAIT_UNTIL_MSG_ID,
|
||||
CANCEL_WAIT_UNTIL_MSG_ID.get(),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
@@ -841,7 +841,7 @@ impl Drop for Window {
|
||||
unsafe {
|
||||
// The window must be destroyed from the same thread that created it, so we send a
|
||||
// custom message to be handled by our callback to do the actual work.
|
||||
PostMessageW(self.hwnd(), *DESTROY_MSG_ID, 0, 0);
|
||||
PostMessageW(self.hwnd(), DESTROY_MSG_ID.get(), 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1218,10 +1218,13 @@ unsafe fn taskbar_mark_fullscreen(handle: HWND, fullscreen: bool) {
|
||||
&IID_ITaskbarList2,
|
||||
&mut task_bar_list2 as *mut _ as *mut _,
|
||||
);
|
||||
if hr != S_OK {
|
||||
// In visual studio retrieving the taskbar list fails
|
||||
return;
|
||||
}
|
||||
|
||||
let hr_init = (*(*task_bar_list2).lpVtbl).parent.HrInit;
|
||||
|
||||
if hr != S_OK || hr_init(task_bar_list2.cast()) != S_OK {
|
||||
if hr_init(task_bar_list2.cast()) != S_OK {
|
||||
// In some old windows, the taskbar object could not be created, we just ignore it
|
||||
return;
|
||||
}
|
||||
@@ -1247,10 +1250,13 @@ pub(crate) unsafe fn set_skip_taskbar(hwnd: HWND, skip: bool) {
|
||||
&IID_ITaskbarList,
|
||||
&mut task_bar_list as *mut _ as *mut _,
|
||||
);
|
||||
if hr != S_OK {
|
||||
// In visual studio retrieving the taskbar list fails
|
||||
return;
|
||||
}
|
||||
|
||||
let hr_init = (*(*task_bar_list).lpVtbl).HrInit;
|
||||
|
||||
if hr != S_OK || hr_init(task_bar_list.cast()) != S_OK {
|
||||
if hr_init(task_bar_list.cast()) != S_OK {
|
||||
// In some old windows, the taskbar object could not be created, we just ignore it
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -391,7 +391,12 @@ impl WindowFlags {
|
||||
let (style, style_ex) = new.to_window_styles();
|
||||
|
||||
unsafe {
|
||||
SendMessageW(window, *event_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID, 1, 0);
|
||||
SendMessageW(
|
||||
window,
|
||||
event_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID.get(),
|
||||
1,
|
||||
0,
|
||||
);
|
||||
|
||||
// This condition is necessary to avoid having an unrestorable window
|
||||
if !new.contains(WindowFlags::MINIMIZED) {
|
||||
@@ -412,7 +417,12 @@ impl WindowFlags {
|
||||
|
||||
// Refresh the window frame
|
||||
SetWindowPos(window, 0, 0, 0, 0, 0, flags);
|
||||
SendMessageW(window, *event_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID, 0, 0);
|
||||
SendMessageW(
|
||||
window,
|
||||
event_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID.get(),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1126,7 +1126,7 @@ impl Window {
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **macOS:** This is an app-wide setting.
|
||||
/// - **iOS / Android / Web / Wayland / x11 / Orbital:** Unsupported.
|
||||
/// - **iOS / Android / Wayland / x11 / Orbital:** Unsupported.
|
||||
#[inline]
|
||||
pub fn theme(&self) -> Option<Theme> {
|
||||
self.window.theme()
|
||||
|
||||
Reference in New Issue
Block a user