Compare commits

..

12 Commits

Author SHA1 Message Date
Kirill Chibisov
9135eb4024 Winit version 0.29.7 2023-12-27 10:10:59 +04:00
John Nunley
23b3c127fd bugfix: Change value sent to X server during minimize
Closes #3327

Signed-off-by: John Nunley <dev@notgull.net>
2023-12-27 10:10:59 +04:00
John Nunley
b343f45500 bugfix: Reload Xft database on DPI change
Closes #1228
2023-12-27 10:10:59 +04:00
Kirill Chibisov
572d61f9ba Winit version 0.29.6 2023-12-24 23:55:50 +04:00
Uli Schlachter
87fc19826b On X11, simplify available_monitors() impl
This code confused me. I tried to understand it. I tried to simplify it
while keeping the functional style. But in the end, this just seems too
complicated for its own good. Just doing the exact same thing with a
match statement and the question mark operator makes it sooo much more
obvious what is happening.

Signed-off-by: Uli Schlachter <psychon@znc.in>
2023-12-24 23:55:50 +04:00
Kirill Chibisov
11d1b7a980 Fix run_on_demand exiting on consequent call
Fixes #3284.
2023-12-24 23:55:50 +04:00
Kirill Chibisov
5ca810ba8f On Wayland, fix WindowEvent::Destroyed delivery 2023-12-24 23:55:50 +04:00
Alex Butler
2d1607b3f7 bugfix(rwh): Bump rwh_05 min version to 0.5.2
Correct min version to support "std" feature
2023-12-24 23:55:50 +04:00
Markus Siglreithmaier
a32e232020 On Windows, remove internal WindowWrapper (#3294)
HWND in windows-sys doesn't require a newtype wrapper for Send/Sync.
2023-12-24 23:55:50 +04:00
daxpedda
9b03bb7276 Fix some doc nits (#3274) 2023-12-24 23:55:50 +04:00
Markus Siglreithmaier
e39596151c On Windows, refactor dynamic function definitions and raw input keyboard handling (#3286) 2023-12-24 23:55:50 +04:00
daxpedda
5289b4f206 On Web, fix context menu not being disabled 2023-12-24 23:55:50 +04:00
26 changed files with 477 additions and 384 deletions

View File

@@ -11,6 +11,17 @@ Unreleased` header.
# Unreleased
# 0.29.7
- On X11, fix `Xft.dpi` reload during runtime.
- On X11, fix window minimize.
# 0.29.6
- On Web, fix context menu not being disabled by `with_prevent_default(true)`.
- On Wayland, fix `WindowEvent::Destroyed` not being delivered after destroying window.
- Fix `EventLoopExtRunOnDemand::run_on_demand` not working for consequent invocation
# 0.29.5
- On macOS, remove spurious error logging when handling `Fn`.

View File

@@ -1,6 +1,6 @@
[package]
name = "winit"
version = "0.29.5"
version = "0.29.7"
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library."
edition = "2021"
@@ -66,7 +66,7 @@ log = "0.4"
mint = { version = "0.5.6", optional = true }
once_cell = "1.12"
rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true }
rwh_05 = { package = "raw-window-handle", version = "0.5", features = ["std"], optional = true }
rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = ["std"], optional = true }
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true }
serde = { version = "1", optional = true, features = ["serde_derive"] }
smol_str = "0.2.0"

View File

@@ -6,7 +6,7 @@
```toml
[dependencies]
winit = "0.29.5"
winit = "0.29.7"
```
## [Documentation](https://docs.rs/winit)
@@ -156,7 +156,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.5", features = [ "android-native-activity" ] }`
2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.7", 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).

View File

@@ -7,17 +7,30 @@
//! The `softbuffer` crate is used, largely because of its ease of use. `glutin` or `wgpu` could
//! also be used to fill the window buffer, but they are more complicated to use.
use winit::window::Window;
#[allow(unused_imports)]
pub use platform::cleanup_window;
pub use platform::fill_window;
#[cfg(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios"))))]
pub(super) fn fill_window(window: &Window) {
use softbuffer::{Context, Surface};
mod platform {
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem::ManuallyDrop;
use std::num::NonZeroU32;
use softbuffer::{Context, Surface};
use winit::window::Window;
use winit::window::WindowId;
thread_local! {
// NOTE: You should never do things like that, create context and drop it before
// you drop the event loop. We do this for brevity to not blow up examples. We use
// ManuallyDrop to prevent destructors from running.
//
// A static, thread-local map of graphics contexts to open windows.
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = ManuallyDrop::new(RefCell::new(None));
}
/// The graphics context used to draw to a window.
struct GraphicsContext {
/// The global softbuffer context.
@@ -35,55 +48,69 @@ pub(super) fn fill_window(window: &Window) {
}
}
fn surface(&mut self, w: &Window) -> &mut Surface {
self.surfaces.entry(w.id()).or_insert_with(|| {
unsafe { Surface::new(&self.context, w) }
fn create_surface(&mut self, window: &Window) -> &mut Surface {
self.surfaces.entry(window.id()).or_insert_with(|| {
unsafe { Surface::new(&self.context, window) }
.expect("Failed to create a softbuffer surface")
})
}
fn destroy_surface(&mut self, window: &Window) {
self.surfaces.remove(&window.id());
}
}
thread_local! {
// NOTE: You should never do things like that, create context and drop it before
// you drop the event loop. We do this for brevity to not blow up examples. We use
// ManuallyDrop to prevent destructors from running.
//
// A static, thread-local map of graphics contexts to open windows.
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = ManuallyDrop::new(RefCell::new(None));
pub 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
.get_or_insert_with(|| GraphicsContext::new(window))
.create_surface(window);
// Fill a buffer with a solid color.
const DARK_GRAY: u32 = 0xFF181818;
surface
.resize(width, height)
.expect("Failed to resize the softbuffer surface");
let mut buffer = surface
.buffer_mut()
.expect("Failed to get the softbuffer buffer");
buffer.fill(DARK_GRAY);
buffer
.present()
.expect("Failed to present the softbuffer buffer");
})
}
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
.get_or_insert_with(|| GraphicsContext::new(window))
.surface(window);
// Fill a buffer with a solid color.
const DARK_GRAY: u32 = 0xFF181818;
surface
.resize(width, height)
.expect("Failed to resize the softbuffer surface");
let mut buffer = surface
.buffer_mut()
.expect("Failed to get the softbuffer buffer");
buffer.fill(DARK_GRAY);
buffer
.present()
.expect("Failed to present the softbuffer buffer");
})
#[allow(dead_code)]
pub fn cleanup_window(window: &Window) {
GC.with(|gc| {
let mut gc = gc.borrow_mut();
if let Some(context) = gc.as_mut() {
context.destroy_surface(window);
}
});
}
}
#[cfg(not(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios")))))]
pub(super) fn fill_window(_window: &Window) {
// No-op on mobile platforms.
mod platform {
pub fn fill_window(_window: &winit::window::Window) {
// No-op on mobile platforms.
}
#[allow(dead_code)]
pub fn cleanup_window(_window: &winit::window::Window) {
// No-op on mobile platforms.
}
}

View File

@@ -40,6 +40,7 @@ fn main() -> Result<(), impl std::error::Error> {
window_id,
} if window.id() == window_id => {
println!("--------------------------------------------------------- Window {idx} CloseRequested");
fill::cleanup_window(window);
app.window = None;
}
Event::AboutToWait => window.request_redraw(),

View File

@@ -76,6 +76,14 @@ impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>),
{
self.event_loop.window_target().clear_exit();
self.event_loop.run_on_demand(event_handler)
}
}
impl<T> EventLoopWindowTarget<T> {
/// Clear exit status.
pub(crate) fn clear_exit(&self) {
self.p.clear_exit()
}
}

View File

@@ -713,6 +713,10 @@ impl<T: 'static> EventLoopWindowTarget<T> {
self.exit.set(true)
}
pub(crate) fn clear_exit(&self) {
self.exit.set(false)
}
pub(crate) fn exiting(&self) -> bool {
self.exit.get()
}

View File

@@ -918,6 +918,10 @@ impl<T> EventLoopWindowTarget<T> {
x11_or_wayland!(match self; Self(evlp) => evlp.control_flow())
}
pub(crate) fn clear_exit(&self) {
x11_or_wayland!(match self; Self(evlp) => evlp.clear_exit())
}
pub(crate) fn exit(&self) {
x11_or_wayland!(match self; Self(evlp) => evlp.exit())
}

View File

@@ -467,44 +467,44 @@ impl<T: 'static> EventLoop<T> {
});
for window_id in window_ids.drain(..) {
let request_redraw = self.with_state(|state| {
let event = self.with_state(|state| {
let window_requests = state.window_requests.get_mut();
if window_requests.get(&window_id).unwrap().take_closed() {
mem::drop(window_requests.remove(&window_id));
mem::drop(state.windows.get_mut().remove(&window_id));
false
} else {
let mut window = state
.windows
.get_mut()
.get_mut(&window_id)
.unwrap()
.lock()
.unwrap();
if window.frame_callback_state() == FrameCallbackState::Requested {
false
} else {
// Reset the frame callbacks state.
window.frame_callback_reset();
let mut redraw_requested = window_requests
.get(&window_id)
.unwrap()
.take_redraw_requested();
// Redraw the frame while at it.
redraw_requested |= window.refresh_frame();
redraw_requested
}
return Some(WindowEvent::Destroyed);
}
let mut window = state
.windows
.get_mut()
.get_mut(&window_id)
.unwrap()
.lock()
.unwrap();
if window.frame_callback_state() == FrameCallbackState::Requested {
return None;
}
// Reset the frame callbacks state.
window.frame_callback_reset();
let mut redraw_requested = window_requests
.get(&window_id)
.unwrap()
.take_redraw_requested();
// Redraw the frame while at it.
redraw_requested |= window.refresh_frame();
redraw_requested.then_some(WindowEvent::RedrawRequested)
});
if request_redraw {
if let Some(event) = event {
callback(
Event::WindowEvent {
window_id: crate::window::WindowId(window_id),
event: WindowEvent::RedrawRequested,
event,
},
&self.window_target,
);
@@ -629,6 +629,34 @@ pub struct EventLoopWindowTarget<T> {
}
impl<T> EventLoopWindowTarget<T> {
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
self.control_flow.set(control_flow)
}
pub(crate) fn control_flow(&self) -> ControlFlow {
self.control_flow.get()
}
pub(crate) fn exit(&self) {
self.exit.set(Some(0))
}
pub(crate) fn clear_exit(&self) {
self.exit.set(None)
}
pub(crate) fn exiting(&self) -> bool {
self.exit.get().is_some()
}
pub(crate) fn set_exit_code(&self, code: i32) {
self.exit.set(Some(code))
}
pub(crate) fn exit_code(&self) -> Option<i32> {
self.exit.get()
}
#[inline]
pub fn listen_device_events(&self, _allowed: DeviceEvents) {}

View File

@@ -4,7 +4,6 @@ use sctk::reexports::client::Proxy;
use sctk::output::OutputData;
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use crate::event_loop::ControlFlow;
use crate::platform_impl::platform::VideoMode as PlatformVideoMode;
use super::event_loop::EventLoopWindowTarget;
@@ -24,30 +23,6 @@ impl<T> EventLoopWindowTarget<T> {
// There's no primary monitor on Wayland.
None
}
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
self.control_flow.set(control_flow)
}
pub(crate) fn control_flow(&self) -> ControlFlow {
self.control_flow.get()
}
pub(crate) fn exit(&self) {
self.exit.set(Some(0))
}
pub(crate) fn exiting(&self) -> bool {
self.exit.get().is_some()
}
pub(crate) fn set_exit_code(&self, code: i32) {
self.exit.set(Some(code))
}
pub(crate) fn exit_code(&self) -> Option<i32> {
self.exit.get()
}
}
#[derive(Clone, Debug)]

View File

@@ -1413,6 +1413,9 @@ impl<T: 'static> EventProcessor<T> {
F: FnMut(Event<T>),
{
let wt = get_xtarget(&self.target);
wt.xconn
.reload_database()
.expect("failed to reload Xft database");
// In the future, it would be quite easy to emit monitor hotplug events.
let prev_list = {

View File

@@ -752,6 +752,10 @@ impl<T> EventLoopWindowTarget<T> {
self.exit.set(Some(0))
}
pub(crate) fn clear_exit(&self) {
self.exit.set(None)
}
pub(crate) fn exiting(&self) -> bool {
self.exit.get().is_some()
}

View File

@@ -284,22 +284,16 @@ impl XConnection {
pub fn available_monitors(&self) -> Result<Vec<MonitorHandle>, X11Error> {
let mut monitors_lock = self.monitor_handles.lock().unwrap();
(*monitors_lock)
.as_ref()
.cloned()
.map(Ok)
.or_else(|| {
self.query_monitor_list()
.map(|mon_list| {
let monitors = Some(mon_list);
if !DISABLE_MONITOR_LIST_CACHING {
(*monitors_lock) = monitors.clone();
}
monitors
})
.transpose()
})
.unwrap()
match *monitors_lock {
Some(ref monitors) => Ok(monitors.clone()),
None => {
let monitors = self.query_monitor_list()?;
if !DISABLE_MONITOR_LIST_CACHING {
*monitors_lock = Some(monitors.clone());
}
Ok(monitors)
}
}
}
#[inline]

View File

@@ -9,7 +9,7 @@ use std::{
use x11rb::{
connection::Connection,
properties::{WmHints, WmHintsState, WmSizeHints, WmSizeHintsSpecification},
properties::{WmHints, WmSizeHints, WmSizeHintsSpecification},
protocol::{
randr,
shape::SK,
@@ -987,7 +987,7 @@ impl UnownedWindow {
xproto::EventMask::SUBSTRUCTURE_REDIRECT
| xproto::EventMask::SUBSTRUCTURE_NOTIFY,
),
[WmHintsState::Iconic as u32, 0, 0, 0, 0],
[3u32, 0, 0, 0, 0],
)
} else {
self.xconn.send_client_msg(

View File

@@ -4,7 +4,7 @@ use std::{
fmt, ptr,
sync::{
atomic::{AtomicU32, Ordering},
Arc, Mutex,
Arc, Mutex, RwLock, RwLockReadGuard,
},
};
@@ -45,7 +45,7 @@ pub(crate) struct XConnection {
pub monitor_handles: Mutex<Option<Vec<MonitorHandle>>>,
/// The resource database.
database: resource_manager::Database,
database: RwLock<resource_manager::Database>,
pub latest_error: Mutex<Option<XError>>,
pub cursor_cache: Mutex<HashMap<Option<CursorIcon>, ffi::Cursor>>,
@@ -115,7 +115,7 @@ impl XConnection {
timestamp: AtomicU32::new(0),
latest_error: Mutex::new(None),
monitor_handles: Mutex::new(None),
database,
database: RwLock::new(database),
cursor_cache: Default::default(),
})
}
@@ -159,8 +159,16 @@ impl XConnection {
/// Get the resource database.
#[inline]
pub fn database(&self) -> &resource_manager::Database {
&self.database
pub fn database(&self) -> RwLockReadGuard<'_, resource_manager::Database> {
self.database.read().unwrap_or_else(|e| e.into_inner())
}
/// Reload the resource database.
#[inline]
pub fn reload_database(&self) -> Result<(), super::X11Error> {
let database = resource_manager::new_from_default(self.xcb_connection())?;
*self.database.write().unwrap_or_else(|e| e.into_inner()) = database;
Ok(())
}
/// Get the latest timestamp.

View File

@@ -209,6 +209,10 @@ impl Handler {
self.exit.store(true, Ordering::Relaxed)
}
pub fn clear_exit(&self) {
self.exit.store(false, Ordering::Relaxed)
}
pub fn exiting(&self) -> bool {
self.exit.load(Ordering::Relaxed)
}
@@ -434,6 +438,10 @@ impl AppState {
HANDLER.exit()
}
pub fn clear_exit() {
HANDLER.clear_exit()
}
pub fn exiting() -> bool {
HANDLER.exiting()
}

View File

@@ -116,6 +116,10 @@ impl<T: 'static> EventLoopWindowTarget<T> {
AppState::exit()
}
pub(crate) fn clear_exit(&self) {
AppState::clear_exit()
}
pub(crate) fn exiting(&self) -> bool {
AppState::exiting()
}

View File

@@ -649,6 +649,8 @@ impl<T> EventLoopWindowTarget<T> {
canvas.on_animation_frame(move || runner.request_redraw(RootWindowId(id)));
canvas.on_touch_end();
canvas.on_context_menu(prevent_default);
}
pub fn available_monitors(&self) -> VecDequeIter<MonitorHandle> {

View File

@@ -5,7 +5,8 @@ use std::sync::{Arc, Mutex};
use smol_str::SmolStr;
use wasm_bindgen::{closure::Closure, JsCast};
use web_sys::{
CssStyleDeclaration, Document, Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, WheelEvent,
CssStyleDeclaration, Document, Event, FocusEvent, HtmlCanvasElement, KeyboardEvent,
PointerEvent, WheelEvent,
};
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
@@ -42,6 +43,7 @@ pub struct Canvas {
on_intersect: Option<IntersectionObserverHandle>,
animation_frame_handler: AnimationFrameHandler,
on_touch_end: Option<EventListenerHandle<dyn FnMut(Event)>>,
on_context_menu: Option<EventListenerHandle<dyn FnMut(PointerEvent)>>,
}
pub struct Common {
@@ -152,6 +154,7 @@ impl Canvas {
on_intersect: None,
animation_frame_handler: AnimationFrameHandler::new(window),
on_touch_end: None,
on_context_menu: None,
})
}
@@ -446,6 +449,17 @@ impl Canvas {
self.on_touch_end = Some(self.common.add_transient_event("touchend", |_| {}));
}
pub(crate) fn on_context_menu(&mut self, prevent_default: bool) {
self.on_context_menu = Some(self.common.add_event(
"contextmenu",
move |event: PointerEvent| {
if prevent_default {
event.prevent_default();
}
},
));
}
pub fn request_fullscreen(&self) {
self.common.fullscreen_handler.request_fullscreen()
}
@@ -524,6 +538,7 @@ impl Canvas {
self.animation_frame_handler.cancel();
self.on_touch_end = None;
self.common.fullscreen_handler.cancel();
self.on_context_menu = None;
}
}

View File

@@ -9,8 +9,8 @@ use windows_sys::Win32::{
},
UI::{
HiDpi::{
DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, MDT_EFFECTIVE_DPI,
PROCESS_PER_MONITOR_DPI_AWARE,
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2,
MDT_EFFECTIVE_DPI, PROCESS_PER_MONITOR_DPI_AWARE,
},
WindowsAndMessaging::IsProcessDPIAware,
},
@@ -21,8 +21,6 @@ use crate::platform_impl::platform::util::{
SET_PROCESS_DPI_AWARENESS, SET_PROCESS_DPI_AWARENESS_CONTEXT,
};
const DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2: DPI_AWARENESS_CONTEXT = -4;
pub fn become_dpi_aware() {
static ENABLE_DPI_AWARENESS: Once = Once::new();
ENABLE_DPI_AWARENESS.call_once(|| {

View File

@@ -21,7 +21,7 @@ use once_cell::sync::Lazy;
use windows_sys::Win32::{
Devices::HumanInterfaceDevice::MOUSE_MOVE_RELATIVE,
Foundation::{BOOL, HANDLE, HWND, LPARAM, LRESULT, POINT, RECT, WPARAM},
Foundation::{HWND, LPARAM, LRESULT, POINT, RECT, WPARAM},
Graphics::Gdi::{
GetMonitorInfoW, MonitorFromRect, MonitorFromWindow, RedrawWindow, ScreenToClient,
ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL, RDW_INTERNALPAINT, SC_SCREENSAVE,
@@ -35,13 +35,9 @@ use windows_sys::Win32::{
Input::{
Ime::{GCS_COMPSTR, GCS_RESULTSTR, ISC_SHOWUICOMPOSITIONWINDOW},
KeyboardAndMouse::{
MapVirtualKeyW, ReleaseCapture, SetCapture, TrackMouseEvent, MAPVK_VK_TO_VSC_EX,
TME_LEAVE, TRACKMOUSEEVENT, VK_NUMLOCK, VK_SHIFT,
},
Pointer::{
POINTER_FLAG_DOWN, POINTER_FLAG_UP, POINTER_FLAG_UPDATE, POINTER_INFO,
POINTER_PEN_INFO, POINTER_TOUCH_INFO,
ReleaseCapture, SetCapture, TrackMouseEvent, TME_LEAVE, TRACKMOUSEEVENT,
},
Pointer::{POINTER_FLAG_DOWN, POINTER_FLAG_UP, POINTER_FLAG_UPDATE},
Touch::{
CloseTouchInputHandle, GetTouchInputInfo, TOUCHEVENTF_DOWN, TOUCHEVENTF_MOVE,
TOUCHEVENTF_UP, TOUCHINPUT,
@@ -54,20 +50,19 @@ use windows_sys::Win32::{
RegisterClassExW, RegisterWindowMessageA, SetCursor, SetTimer, SetWindowPos,
TranslateMessage, CREATESTRUCTW, GIDC_ARRIVAL, GIDC_REMOVAL, GWL_STYLE, GWL_USERDATA,
HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, NCCALCSIZE_PARAMS, PM_REMOVE, PT_PEN,
PT_TOUCH, RI_KEY_E0, RI_KEY_E1, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE,
SC_RESTORE, SIZE_MAXIMIZED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER,
WHEEL_DELTA, WINDOWPOS, WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY,
WM_DPICHANGED, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION,
WM_IME_ENDCOMPOSITION, WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT,
WM_INPUT_DEVICE_CHANGE, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN,
WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE,
WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY,
WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN, WM_POINTERUP, WM_POINTERUPDATE,
WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE,
WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH, WM_WINDOWPOSCHANGED,
WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSEXW, WS_EX_LAYERED,
WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP,
WS_VISIBLE,
PT_TOUCH, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED,
SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS,
WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERSIZEMOVE,
WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION,
WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_INPUT_DEVICE_CHANGE,
WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN,
WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE,
WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY, WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN,
WM_POINTERUP, WM_POINTERUPDATE, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR,
WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP,
WM_TOUCH, WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP,
WNDCLASSEXW, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT,
WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
},
},
};
@@ -80,8 +75,8 @@ use crate::{
WindowEvent,
},
event_loop::{ControlFlow, DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW},
keyboard::{KeyCode, ModifiersState, PhysicalKey},
platform::{pump_events::PumpStatus, scancode::PhysicalKeyExtScancode},
keyboard::ModifiersState,
platform::pump_events::PumpStatus,
platform_impl::platform::{
dark_mode::try_theme,
dpi::{become_dpi_aware, dpi_to_scale_factor},
@@ -103,37 +98,6 @@ use self::runner::RunnerState;
use super::window::set_skip_taskbar;
type GetPointerFrameInfoHistory = unsafe extern "system" fn(
pointerId: u32,
entriesCount: *mut u32,
pointerCount: *mut u32,
pointerInfo: *mut POINTER_INFO,
) -> BOOL;
type SkipPointerFrameMessages = unsafe extern "system" fn(pointerId: u32) -> BOOL;
type GetPointerDeviceRects = unsafe extern "system" fn(
device: HANDLE,
pointerDeviceRect: *mut RECT,
displayRect: *mut RECT,
) -> BOOL;
type GetPointerTouchInfo =
unsafe extern "system" fn(pointerId: u32, touchInfo: *mut POINTER_TOUCH_INFO) -> BOOL;
type GetPointerPenInfo =
unsafe extern "system" fn(pointId: u32, penInfo: *mut POINTER_PEN_INFO) -> BOOL;
static GET_POINTER_FRAME_INFO_HISTORY: Lazy<Option<GetPointerFrameInfoHistory>> =
Lazy::new(|| get_function!("user32.dll", GetPointerFrameInfoHistory));
static SKIP_POINTER_FRAME_MESSAGES: Lazy<Option<SkipPointerFrameMessages>> =
Lazy::new(|| get_function!("user32.dll", SkipPointerFrameMessages));
static GET_POINTER_DEVICE_RECTS: Lazy<Option<GetPointerDeviceRects>> =
Lazy::new(|| get_function!("user32.dll", GetPointerDeviceRects));
static GET_POINTER_TOUCH_INFO: Lazy<Option<GetPointerTouchInfo>> =
Lazy::new(|| get_function!("user32.dll", GetPointerTouchInfo));
static GET_POINTER_PEN_INFO: Lazy<Option<GetPointerPenInfo>> =
Lazy::new(|| get_function!("user32.dll", GetPointerPenInfo));
pub(crate) struct WindowData<T: 'static> {
pub window_state: Arc<Mutex<WindowState>>,
pub event_loop_runner: EventLoopRunnerShared<T>,
@@ -566,6 +530,10 @@ impl<T> EventLoopWindowTarget<T> {
self.runner_shared.exit_code().is_some()
}
pub(crate) fn clear_exit(&self) {
self.runner_shared.clear_exit();
}
fn exit_code(&self) -> Option<i32> {
self.runner_shared.exit_code()
}
@@ -1831,9 +1799,9 @@ unsafe fn public_window_callback_inner<T: 'static>(
Some(SkipPointerFrameMessages),
Some(GetPointerDeviceRects),
) = (
*GET_POINTER_FRAME_INFO_HISTORY,
*SKIP_POINTER_FRAME_MESSAGES,
*GET_POINTER_DEVICE_RECTS,
*util::GET_POINTER_FRAME_INFO_HISTORY,
*util::SKIP_POINTER_FRAME_MESSAGES,
*util::GET_POINTER_DEVICE_RECTS,
) {
let pointer_id = super::loword(wparam as u32) as u32;
let mut entries_count = 0u32;
@@ -1915,7 +1883,7 @@ unsafe fn public_window_callback_inner<T: 'static>(
let force = match pointer_info.pointerType {
PT_TOUCH => {
let mut touch_info = mem::MaybeUninit::uninit();
GET_POINTER_TOUCH_INFO.and_then(|GetPointerTouchInfo| {
util::GET_POINTER_TOUCH_INFO.and_then(|GetPointerTouchInfo| {
match unsafe {
GetPointerTouchInfo(
pointer_info.pointerId,
@@ -1931,7 +1899,7 @@ unsafe fn public_window_callback_inner<T: 'static>(
}
PT_PEN => {
let mut pen_info = mem::MaybeUninit::uninit();
GET_POINTER_PEN_INFO.and_then(|GetPointerPenInfo| {
util::GET_POINTER_PEN_INFO.and_then(|GetPointerPenInfo| {
match unsafe {
GetPointerPenInfo(pointer_info.pointerId, pen_info.as_mut_ptr())
} {
@@ -2493,105 +2461,17 @@ unsafe fn handle_raw_input<T: 'static>(userdata: &ThreadMsgTargetData<T>, data:
return;
}
let state = if pressed { Pressed } else { Released };
let extension = {
if util::has_flag(keyboard.Flags, RI_KEY_E0 as _) {
0xE000
} else if util::has_flag(keyboard.Flags, RI_KEY_E1 as _) {
0xE100
} else {
0x0000
}
};
let scancode = if keyboard.MakeCode == 0 {
// In some cases (often with media keys) the device reports a scancode of 0 but a
// valid virtual key. In these cases we obtain the scancode from the virtual key.
unsafe { MapVirtualKeyW(keyboard.VKey as u32, MAPVK_VK_TO_VSC_EX) as u16 }
} else {
keyboard.MakeCode | extension
};
if scancode == 0xE11D || scancode == 0xE02A {
// At the hardware (or driver?) level, pressing the Pause key is equivalent to pressing
// Ctrl+NumLock.
// This equvalence means that if the user presses Pause, the keyboard will emit two
// subsequent keypresses:
// 1, 0xE11D - Which is a left Ctrl (0x1D) with an extension flag (0xE100)
// 2, 0x0045 - Which on its own can be interpreted as Pause
//
// There's another combination which isn't quite an equivalence:
// PrtSc used to be Shift+Asterisk. This means that on some keyboards, presssing
// PrtSc (print screen) produces the following sequence:
// 1, 0xE02A - Which is a left shift (0x2A) with an extension flag (0xE000)
// 2, 0xE037 - Which is a numpad multiply (0x37) with an exteion flag (0xE000). This on
// its own it can be interpreted as PrtSc
//
// For this reason, if we encounter the first keypress, we simply ignore it, trusting
// that there's going to be another event coming, from which we can extract the
// appropriate key.
// For more on this, read the article by Raymond Chen, titled:
// "Why does Ctrl+ScrollLock cancel dialogs?"
// https://devblogs.microsoft.com/oldnewthing/20080211-00/?p=23503
return;
if let Some(physical_key) = raw_input::get_keyboard_physical_key(keyboard) {
let state = if pressed { Pressed } else { Released };
userdata.send_event(Event::DeviceEvent {
device_id,
event: Key(RawKeyEvent {
physical_key,
state,
}),
});
}
let physical_key = if keyboard.VKey == VK_NUMLOCK {
// Historically, the NumLock and the Pause key were one and the same physical key.
// The user could trigger Pause by pressing Ctrl+NumLock.
// Now these are often physically separate and the two keys can be differentiated by
// checking the extension flag of the scancode. NumLock is 0xE045, Pause is 0x0045.
//
// However in this event, both keys are reported as 0x0045 even on modern hardware.
// Therefore we use the virtual key instead to determine whether it's a NumLock and
// set the KeyCode accordingly.
//
// For more on this, read the article by Raymond Chen, titled:
// "Why does Ctrl+ScrollLock cancel dialogs?"
// https://devblogs.microsoft.com/oldnewthing/20080211-00/?p=23503
PhysicalKey::Code(KeyCode::NumLock)
} else {
PhysicalKey::from_scancode(scancode as u32)
};
if keyboard.VKey == VK_SHIFT {
if let PhysicalKey::Code(code) = physical_key {
match code {
KeyCode::NumpadDecimal
| KeyCode::Numpad0
| KeyCode::Numpad1
| KeyCode::Numpad2
| KeyCode::Numpad3
| KeyCode::Numpad4
| KeyCode::Numpad5
| KeyCode::Numpad6
| KeyCode::Numpad7
| KeyCode::Numpad8
| KeyCode::Numpad9 => {
// On Windows, holding the Shift key makes numpad keys behave as if NumLock
// wasn't active. The way this is exposed to applications by the system is that
// the application receives a fake key release event for the shift key at the
// moment when the numpad key is pressed, just before receiving the numpad key
// as well.
//
// The issue is that in the raw device event (here), the fake shift release
// event reports the numpad key as the scancode. Unfortunately, the event doesn't
// have any information to tell whether it's the left shift or the right shift
// that needs to get the fake release (or press) event so we don't forward this
// event to the application at all.
//
// For more on this, read the article by Raymond Chen, titled:
// "The shift key overrides NumLock"
// https://devblogs.microsoft.com/oldnewthing/20040906-00/?p=37953
return;
}
_ => (),
}
}
}
userdata.send_event(Event::DeviceEvent {
device_id,
event: Key(RawKeyEvent {
physical_key,
state,
}),
});
}
}

View File

@@ -163,6 +163,10 @@ impl<T> EventLoopRunner<T> {
self.exit.get()
}
pub fn clear_exit(&self) {
self.exit.set(None);
}
pub fn should_buffer(&self) -> bool {
let handler = self.event_handler.take();
let should_buffer = handler.is_none();

View File

@@ -11,21 +11,29 @@ use windows_sys::Win32::{
UI::{
Input::{
GetRawInputData, GetRawInputDeviceInfoW, GetRawInputDeviceList,
KeyboardAndMouse::{MapVirtualKeyW, MAPVK_VK_TO_VSC_EX, VK_NUMLOCK, VK_SHIFT},
RegisterRawInputDevices, HRAWINPUT, RAWINPUT, RAWINPUTDEVICE, RAWINPUTDEVICELIST,
RAWINPUTHEADER, RIDEV_DEVNOTIFY, RIDEV_INPUTSINK, RIDEV_REMOVE, RIDI_DEVICEINFO,
RIDI_DEVICENAME, RID_DEVICE_INFO, RID_DEVICE_INFO_HID, RID_DEVICE_INFO_KEYBOARD,
RID_DEVICE_INFO_MOUSE, RID_INPUT, RIM_TYPEHID, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE,
RAWINPUTHEADER, RAWKEYBOARD, RIDEV_DEVNOTIFY, RIDEV_INPUTSINK, RIDEV_REMOVE,
RIDI_DEVICEINFO, RIDI_DEVICENAME, RID_DEVICE_INFO, RID_DEVICE_INFO_HID,
RID_DEVICE_INFO_KEYBOARD, RID_DEVICE_INFO_MOUSE, RID_INPUT, RIM_TYPEHID,
RIM_TYPEKEYBOARD, RIM_TYPEMOUSE,
},
WindowsAndMessaging::{
RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP, RI_MOUSE_BUTTON_2_DOWN,
RI_MOUSE_BUTTON_2_UP, RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP,
RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP, RI_MOUSE_BUTTON_5_DOWN,
RI_MOUSE_BUTTON_5_UP,
RI_KEY_E0, RI_KEY_E1, RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP,
RI_MOUSE_BUTTON_2_DOWN, RI_MOUSE_BUTTON_2_UP, RI_MOUSE_BUTTON_3_DOWN,
RI_MOUSE_BUTTON_3_UP, RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP,
RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP,
},
},
};
use crate::{event::ElementState, event_loop::DeviceEvents, platform_impl::platform::util};
use crate::{
event::ElementState,
event_loop::DeviceEvents,
keyboard::{KeyCode, PhysicalKey},
platform::scancode::PhysicalKeyExtScancode,
platform_impl::platform::util,
};
#[allow(dead_code)]
pub fn get_raw_input_device_list() -> Option<Vec<RAWINPUTDEVICELIST>> {
@@ -220,3 +228,99 @@ pub fn get_raw_mouse_button_state(button_flags: u32) -> [Option<ElementState>; 5
button_flags_to_element_state(button_flags, RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP),
]
}
pub fn get_keyboard_physical_key(keyboard: RAWKEYBOARD) -> Option<PhysicalKey> {
let extension = {
if util::has_flag(keyboard.Flags, RI_KEY_E0 as _) {
0xE000
} else if util::has_flag(keyboard.Flags, RI_KEY_E1 as _) {
0xE100
} else {
0x0000
}
};
let scancode = if keyboard.MakeCode == 0 {
// In some cases (often with media keys) the device reports a scancode of 0 but a
// valid virtual key. In these cases we obtain the scancode from the virtual key.
unsafe { MapVirtualKeyW(keyboard.VKey as u32, MAPVK_VK_TO_VSC_EX) as u16 }
} else {
keyboard.MakeCode | extension
};
if scancode == 0xE11D || scancode == 0xE02A {
// At the hardware (or driver?) level, pressing the Pause key is equivalent to pressing
// Ctrl+NumLock.
// This equvalence means that if the user presses Pause, the keyboard will emit two
// subsequent keypresses:
// 1, 0xE11D - Which is a left Ctrl (0x1D) with an extension flag (0xE100)
// 2, 0x0045 - Which on its own can be interpreted as Pause
//
// There's another combination which isn't quite an equivalence:
// PrtSc used to be Shift+Asterisk. This means that on some keyboards, presssing
// PrtSc (print screen) produces the following sequence:
// 1, 0xE02A - Which is a left shift (0x2A) with an extension flag (0xE000)
// 2, 0xE037 - Which is a numpad multiply (0x37) with an exteion flag (0xE000). This on
// its own it can be interpreted as PrtSc
//
// For this reason, if we encounter the first keypress, we simply ignore it, trusting
// that there's going to be another event coming, from which we can extract the
// appropriate key.
// For more on this, read the article by Raymond Chen, titled:
// "Why does Ctrl+ScrollLock cancel dialogs?"
// https://devblogs.microsoft.com/oldnewthing/20080211-00/?p=23503
return None;
}
let physical_key = if keyboard.VKey == VK_NUMLOCK {
// Historically, the NumLock and the Pause key were one and the same physical key.
// The user could trigger Pause by pressing Ctrl+NumLock.
// Now these are often physically separate and the two keys can be differentiated by
// checking the extension flag of the scancode. NumLock is 0xE045, Pause is 0x0045.
//
// However in this event, both keys are reported as 0x0045 even on modern hardware.
// Therefore we use the virtual key instead to determine whether it's a NumLock and
// set the KeyCode accordingly.
//
// For more on this, read the article by Raymond Chen, titled:
// "Why does Ctrl+ScrollLock cancel dialogs?"
// https://devblogs.microsoft.com/oldnewthing/20080211-00/?p=23503
PhysicalKey::Code(KeyCode::NumLock)
} else {
PhysicalKey::from_scancode(scancode as u32)
};
if keyboard.VKey == VK_SHIFT {
if let PhysicalKey::Code(code) = physical_key {
match code {
KeyCode::NumpadDecimal
| KeyCode::Numpad0
| KeyCode::Numpad1
| KeyCode::Numpad2
| KeyCode::Numpad3
| KeyCode::Numpad4
| KeyCode::Numpad5
| KeyCode::Numpad6
| KeyCode::Numpad7
| KeyCode::Numpad8
| KeyCode::Numpad9 => {
// On Windows, holding the Shift key makes numpad keys behave as if NumLock
// wasn't active. The way this is exposed to applications by the system is that
// the application receives a fake key release event for the shift key at the
// moment when the numpad key is pressed, just before receiving the numpad key
// as well.
//
// The issue is that in the raw device event (here), the fake shift release
// event reports the numpad key as the scancode. Unfortunately, the event doesn't
// have any information to tell whether it's the left shift or the right shift
// that needs to get the fake release (or press) event so we don't forward this
// event to the application at all.
//
// For more on this, read the article by Raymond Chen, titled:
// "The shift key overrides NumLock"
// https://devblogs.microsoft.com/oldnewthing/20040906-00/?p=37953
return None;
}
_ => (),
}
}
}
Some(physical_key)
}

View File

@@ -13,7 +13,7 @@ use once_cell::sync::Lazy;
use windows_sys::{
core::{HRESULT, PCWSTR},
Win32::{
Foundation::{BOOL, HMODULE, HWND, RECT},
Foundation::{BOOL, HANDLE, HMODULE, HWND, RECT},
Graphics::Gdi::{ClientToScreen, HMONITOR},
System::{
LibraryLoader::{GetProcAddress, LoadLibraryA},
@@ -21,7 +21,10 @@ use windows_sys::{
},
UI::{
HiDpi::{DPI_AWARENESS_CONTEXT, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS},
Input::KeyboardAndMouse::GetActiveWindow,
Input::{
KeyboardAndMouse::GetActiveWindow,
Pointer::{POINTER_INFO, POINTER_PEN_INFO, POINTER_TOUCH_INFO},
},
WindowsAndMessaging::{
ClipCursor, GetClientRect, GetClipCursor, GetSystemMetrics, GetWindowPlacement,
GetWindowRect, IsIconic, ShowCursor, IDC_APPSTARTING, IDC_ARROW, IDC_CROSS,
@@ -191,7 +194,9 @@ pub(crate) fn to_windows_cursor(cursor: CursorIcon) -> PCWSTR {
}
}
// Helper function to dynamically load function pointer.
// Helper function to dynamically load function pointer as some functions
// may not be available on all Windows platforms supported by winit.
//
// `library` and `function` must be zero-terminated.
pub(super) fn get_function_impl(library: &str, function: &str) -> Option<*const c_void> {
assert_eq!(library.chars().last(), Some('\0'));
@@ -237,6 +242,26 @@ pub type AdjustWindowRectExForDpi = unsafe extern "system" fn(
dpi: u32,
) -> BOOL;
pub type GetPointerFrameInfoHistory = unsafe extern "system" fn(
pointerId: u32,
entriesCount: *mut u32,
pointerCount: *mut u32,
pointerInfo: *mut POINTER_INFO,
) -> BOOL;
pub type SkipPointerFrameMessages = unsafe extern "system" fn(pointerId: u32) -> BOOL;
pub type GetPointerDeviceRects = unsafe extern "system" fn(
device: HANDLE,
pointerDeviceRect: *mut RECT,
displayRect: *mut RECT,
) -> BOOL;
pub type GetPointerTouchInfo =
unsafe extern "system" fn(pointerId: u32, touchInfo: *mut POINTER_TOUCH_INFO) -> BOOL;
pub type GetPointerPenInfo =
unsafe extern "system" fn(pointId: u32, penInfo: *mut POINTER_PEN_INFO) -> BOOL;
pub static GET_DPI_FOR_WINDOW: Lazy<Option<GetDpiForWindow>> =
Lazy::new(|| get_function!("user32.dll", GetDpiForWindow));
pub static ADJUST_WINDOW_RECT_EX_FOR_DPI: Lazy<Option<AdjustWindowRectExForDpi>> =
@@ -251,3 +276,13 @@ pub static SET_PROCESS_DPI_AWARENESS: Lazy<Option<SetProcessDpiAwareness>> =
Lazy::new(|| get_function!("shcore.dll", SetProcessDpiAwareness));
pub static SET_PROCESS_DPI_AWARE: Lazy<Option<SetProcessDPIAware>> =
Lazy::new(|| get_function!("user32.dll", SetProcessDPIAware));
pub static GET_POINTER_FRAME_INFO_HISTORY: Lazy<Option<GetPointerFrameInfoHistory>> =
Lazy::new(|| get_function!("user32.dll", GetPointerFrameInfoHistory));
pub static SKIP_POINTER_FRAME_MESSAGES: Lazy<Option<SkipPointerFrameMessages>> =
Lazy::new(|| get_function!("user32.dll", SkipPointerFrameMessages));
pub static GET_POINTER_DEVICE_RECTS: Lazy<Option<GetPointerDeviceRects>> =
Lazy::new(|| get_function!("user32.dll", GetPointerDeviceRects));
pub static GET_POINTER_TOUCH_INFO: Lazy<Option<GetPointerTouchInfo>> =
Lazy::new(|| get_function!("user32.dll", GetPointerTouchInfo));
pub static GET_POINTER_PEN_INFO: Lazy<Option<GetPointerPenInfo>> =
Lazy::new(|| get_function!("user32.dll", GetPointerPenInfo));

View File

@@ -83,7 +83,7 @@ use crate::{
/// The Win32 implementation of the main `Window` object.
pub(crate) struct Window {
/// Main handle for the window.
window: WindowWrapper,
window: HWND,
/// The current window state.
window_state: Arc<Mutex<WindowState>>,
@@ -127,11 +127,11 @@ impl Window {
}
pub fn set_transparent(&self, transparent: bool) {
let window = self.window.clone();
let window = self.window;
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
let _ = &window;
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
f.set(WindowFlags::TRANSPARENT, transparent)
});
});
@@ -141,11 +141,11 @@ impl Window {
#[inline]
pub fn set_visible(&self, visible: bool) {
let window = self.window.clone();
let window = self.window;
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
let _ = &window;
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
f.set(WindowFlags::VISIBLE, visible)
});
});
@@ -153,7 +153,7 @@ impl Window {
#[inline]
pub fn is_visible(&self) -> Option<bool> {
Some(unsafe { IsWindowVisible(self.window.0) == 1 })
Some(unsafe { IsWindowVisible(self.window) == 1 })
}
#[inline]
@@ -189,10 +189,10 @@ impl Window {
let (x, y): (i32, i32) = position.to_physical::<i32>(self.scale_factor()).into();
let window_state = Arc::clone(&self.window_state);
let window = self.window.clone();
let window = self.window;
self.thread_executor.execute_in_thread(move || {
let _ = &window;
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
f.set(WindowFlags::MAXIMIZED, false)
});
});
@@ -246,10 +246,10 @@ impl Window {
if physical_size != self.inner_size() {
let window_state = Arc::clone(&self.window_state);
let window = self.window.clone();
let window = self.window;
self.thread_executor.execute_in_thread(move || {
let _ = &window;
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
f.set(WindowFlags::MAXIMIZED, false)
});
});
@@ -284,12 +284,12 @@ impl Window {
#[inline]
pub fn set_resizable(&self, resizable: bool) {
let window = self.window.clone();
let window = self.window;
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
let _ = &window;
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
f.set(WindowFlags::RESIZABLE, resizable)
});
});
@@ -303,12 +303,12 @@ impl Window {
#[inline]
pub fn set_enabled_buttons(&self, buttons: WindowButtons) {
let window = self.window.clone();
let window = self.window;
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
let _ = &window;
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
f.set(
WindowFlags::MINIMIZABLE,
buttons.contains(WindowButtons::MINIMIZE),
@@ -342,14 +342,14 @@ impl Window {
/// Returns the `hwnd` of this window.
#[inline]
pub fn hwnd(&self) -> HWND {
self.window.0
self.window
}
#[cfg(feature = "rwh_04")]
#[inline]
pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
let mut window_handle = rwh_04::Win32Handle::empty();
window_handle.hwnd = self.window.0 as *mut _;
window_handle.hwnd = self.window as *mut _;
let hinstance = unsafe { super::get_window_long(self.hwnd(), GWLP_HINSTANCE) };
window_handle.hinstance = hinstance as *mut _;
rwh_04::RawWindowHandle::Win32(window_handle)
@@ -359,7 +359,7 @@ impl Window {
#[inline]
pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
let mut window_handle = rwh_05::Win32WindowHandle::empty();
window_handle.hwnd = self.window.0 as *mut _;
window_handle.hwnd = self.window as *mut _;
let hinstance = unsafe { super::get_window_long(self.hwnd(), GWLP_HINSTANCE) };
window_handle.hinstance = hinstance as *mut _;
rwh_05::RawWindowHandle::Win32(window_handle)
@@ -376,8 +376,7 @@ impl Window {
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
let mut window_handle = rwh_06::Win32WindowHandle::new(unsafe {
// SAFETY: Handle will never be zero.
let window = self.window.0;
std::num::NonZeroIsize::new_unchecked(window)
std::num::NonZeroIsize::new_unchecked(self.window)
});
let hinstance = unsafe { super::get_window_long(self.hwnd(), GWLP_HINSTANCE) };
window_handle.hinstance = std::num::NonZeroIsize::new(hinstance);
@@ -413,7 +412,7 @@ impl Window {
}
};
let window = self.window.clone();
let window = self.window;
let window_state = Arc::clone(&self.window_state);
let (tx, rx) = channel();
@@ -423,7 +422,7 @@ impl Window {
.lock()
.unwrap()
.mouse
.set_cursor_flags(window.0, |f| f.set(CursorFlags::GRABBED, confine))
.set_cursor_flags(window, |f| f.set(CursorFlags::GRABBED, confine))
.map_err(|e| ExternalError::Os(os_error!(e)));
let _ = tx.send(result);
});
@@ -432,7 +431,7 @@ impl Window {
#[inline]
pub fn set_cursor_visible(&self, visible: bool) {
let window = self.window.clone();
let window = self.window;
let window_state = Arc::clone(&self.window_state);
let (tx, rx) = channel();
@@ -442,7 +441,7 @@ impl Window {
.lock()
.unwrap()
.mouse
.set_cursor_flags(window.0, |f| f.set(CursorFlags::HIDDEN, !visible))
.set_cursor_flags(window, |f| f.set(CursorFlags::HIDDEN, !visible))
.map_err(|e| e.to_string());
let _ = tx.send(result);
});
@@ -472,7 +471,7 @@ impl Window {
}
unsafe fn handle_os_dragging(&self, wparam: WPARAM) {
let window = self.window.clone();
let window = self.window;
let window_state = self.window_state.clone();
self.thread_executor.execute_in_thread(move || {
@@ -500,7 +499,7 @@ impl Window {
unsafe {
PostMessageW(
window.0,
window,
WM_NCLBUTTONDOWN,
wparam,
&points as *const _ as LPARAM,
@@ -615,10 +614,10 @@ impl Window {
#[inline]
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
let window = self.window.clone();
let window = self.window;
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
f.set(WindowFlags::IGNORE_CURSOR_EVENT, !hittest)
});
});
@@ -633,7 +632,7 @@ impl Window {
#[inline]
pub fn set_minimized(&self, minimized: bool) {
let window = self.window.clone();
let window = self.window;
let window_state = Arc::clone(&self.window_state);
let is_minimized = util::is_minimized(self.hwnd());
@@ -643,7 +642,7 @@ impl Window {
WindowState::set_window_flags_in_place(&mut window_state.lock().unwrap(), |f| {
f.set(WindowFlags::MINIMIZED, is_minimized)
});
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
f.set(WindowFlags::MINIMIZED, minimized)
});
});
@@ -656,12 +655,12 @@ impl Window {
#[inline]
pub fn set_maximized(&self, maximized: bool) {
let window = self.window.clone();
let window = self.window;
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
let _ = &window;
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
f.set(WindowFlags::MAXIMIZED, maximized)
});
});
@@ -681,7 +680,7 @@ impl Window {
#[inline]
pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
let window = self.window.clone();
let window = self.window;
let window_state = Arc::clone(&self.window_state);
let mut window_state_lock = window_state.lock().unwrap();
@@ -692,7 +691,7 @@ impl Window {
_ if old_fullscreen == fullscreen => return,
// Return if saved Borderless(monitor) is the same as current monitor when requested fullscreen is Borderless(None)
(Some(Fullscreen::Borderless(Some(monitor))), Some(Fullscreen::Borderless(None)))
if *monitor == monitor::current_monitor(window.0) =>
if *monitor == monitor::current_monitor(window) =>
{
return
}
@@ -761,7 +760,7 @@ impl Window {
}
// Update window style
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
f.set(
WindowFlags::MARKER_EXCLUSIVE_FULLSCREEN,
matches!(fullscreen, Some(Fullscreen::Exclusive(_))),
@@ -777,7 +776,7 @@ impl Window {
// this needs to be called before the below fullscreen SetWindowPos as this itself
// will generate WM_SIZE messages of the old window size that can race with what we set below
unsafe {
taskbar_mark_fullscreen(window.0, fullscreen.is_some());
taskbar_mark_fullscreen(window, fullscreen.is_some());
}
// Update window bounds
@@ -786,7 +785,7 @@ impl Window {
// Save window bounds before entering fullscreen
let placement = unsafe {
let mut placement = mem::zeroed();
GetWindowPlacement(window.0, &mut placement);
GetWindowPlacement(window, &mut placement);
placement
};
@@ -795,7 +794,7 @@ impl Window {
let monitor = match &fullscreen {
Fullscreen::Exclusive(video_mode) => video_mode.monitor(),
Fullscreen::Borderless(Some(monitor)) => monitor.clone(),
Fullscreen::Borderless(None) => monitor::current_monitor(window.0),
Fullscreen::Borderless(None) => monitor::current_monitor(window),
};
let position: (i32, i32) = monitor.position().into();
@@ -803,7 +802,7 @@ impl Window {
unsafe {
SetWindowPos(
window.0,
window,
0,
position.0,
position.1,
@@ -811,7 +810,7 @@ impl Window {
size.1 as i32,
SWP_ASYNCWINDOWPOS | SWP_NOZORDER,
);
InvalidateRgn(window.0, 0, false.into());
InvalidateRgn(window, 0, false.into());
}
}
None => {
@@ -819,8 +818,8 @@ impl Window {
if let Some(SavedWindow { placement }) = window_state_lock.saved_window.take() {
drop(window_state_lock);
unsafe {
SetWindowPlacement(window.0, &placement);
InvalidateRgn(window.0, 0, false.into());
SetWindowPlacement(window, &placement);
InvalidateRgn(window, 0, false.into());
}
}
}
@@ -830,12 +829,12 @@ impl Window {
#[inline]
pub fn set_decorations(&self, decorations: bool) {
let window = self.window.clone();
let window = self.window;
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
let _ = &window;
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
f.set(WindowFlags::MARKER_DECORATIONS, decorations)
});
});
@@ -851,12 +850,12 @@ impl Window {
#[inline]
pub fn set_window_level(&self, level: WindowLevel) {
let window = self.window.clone();
let window = self.window;
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
let _ = &window;
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
f.set(
WindowFlags::ALWAYS_ON_TOP,
level == WindowLevel::AlwaysOnTop,
@@ -905,21 +904,21 @@ impl Window {
#[inline]
pub fn set_ime_cursor_area(&self, spot: Position, size: Size) {
let window = self.window.clone();
let window = self.window;
let state = self.window_state.clone();
self.thread_executor.execute_in_thread(move || unsafe {
let scale_factor = state.lock().unwrap().scale_factor;
ImeContext::current(window.0).set_ime_cursor_area(spot, size, scale_factor);
ImeContext::current(window).set_ime_cursor_area(spot, size, scale_factor);
});
}
#[inline]
pub fn set_ime_allowed(&self, allowed: bool) {
let window = self.window.clone();
let window = self.window;
let state = self.window_state.clone();
self.thread_executor.execute_in_thread(move || unsafe {
state.lock().unwrap().ime_allowed = allowed;
ImeContext::set_ime_allowed(window.0, allowed);
ImeContext::set_ime_allowed(window, allowed);
})
}
@@ -928,14 +927,13 @@ impl Window {
#[inline]
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
let window = self.window.clone();
let window = self.window;
let active_window_handle = unsafe { GetActiveWindow() };
if window.0 == active_window_handle {
if window == active_window_handle {
return;
}
self.thread_executor.execute_in_thread(move || unsafe {
let _ = &window;
let (flags, count) = request_type
.map(|ty| match ty {
UserAttentionType::Critical => (FLASHW_ALL | FLASHW_TIMERNOFG, u32::MAX),
@@ -945,7 +943,7 @@ impl Window {
let flash_info = FLASHWINFO {
cbSize: mem::size_of::<FLASHWINFO>() as u32,
hwnd: window.0,
hwnd: window,
dwFlags: flags,
uCount: count,
dwTimeout: 0,
@@ -956,7 +954,7 @@ impl Window {
#[inline]
pub fn set_theme(&self, theme: Option<Theme>) {
try_theme(self.window.0, theme);
try_theme(self.window, theme);
}
#[inline]
@@ -971,9 +969,9 @@ impl Window {
}
pub fn title(&self) -> String {
let len = unsafe { GetWindowTextLengthW(self.window.0) } + 1;
let len = unsafe { GetWindowTextLengthW(self.window) } + 1;
let mut buf = vec![0; len as usize];
unsafe { GetWindowTextW(self.window.0, buf.as_mut_ptr(), len) };
unsafe { GetWindowTextW(self.window, buf.as_mut_ptr(), len) };
util::decode_wide(&buf).to_string_lossy().to_string()
}
@@ -985,12 +983,12 @@ impl Window {
#[inline]
pub fn set_undecorated_shadow(&self, shadow: bool) {
let window = self.window.clone();
let window = self.window;
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
let _ = &window;
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
f.set(WindowFlags::MARKER_UNDECORATED_SHADOW, shadow)
});
});
@@ -998,15 +996,14 @@ impl Window {
#[inline]
pub fn focus_window(&self) {
let window = self.window.clone();
let window_flags = self.window_state_lock().window_flags();
let is_visible = window_flags.contains(WindowFlags::VISIBLE);
let is_minimized = util::is_minimized(self.hwnd());
let is_foreground = window.0 == unsafe { GetForegroundWindow() };
let is_foreground = self.window == unsafe { GetForegroundWindow() };
if is_visible && !is_minimized && !is_foreground {
unsafe { force_window_active(window.0) };
unsafe { force_window_active(self.window) };
}
}
@@ -1056,18 +1053,6 @@ impl Drop for Window {
}
}
/// A simple non-owning wrapper around a window.
#[doc(hidden)]
#[derive(Clone)]
pub struct WindowWrapper(HWND);
// Send and Sync are not implemented for HWND and HDC, we have to wrap it and implement them manually.
// For more info see:
// https://github.com/retep998/winapi-rs/issues/360
// https://github.com/retep998/winapi-rs/issues/396
unsafe impl Sync for WindowWrapper {}
unsafe impl Send for WindowWrapper {}
pub(super) struct InitData<'a, T: 'static> {
// inputs
pub event_loop: &'a EventLoopWindowTarget<T>,
@@ -1115,7 +1100,7 @@ impl<'a, T: 'static> InitData<'a, T> {
unsafe { ImeContext::set_ime_allowed(window, false) };
Window {
window: WindowWrapper(window),
window,
window_state,
thread_executor: self.event_loop.create_thread_executor(),
}
@@ -1138,7 +1123,7 @@ impl<'a, T: 'static> InitData<'a, T> {
let file_drop_runner = self.event_loop.runner_shared.clone();
let file_drop_handler = FileDropHandler::new(
win.window.0,
win.window,
Box::new(move |event| {
if let Ok(e) = event.map_nonuser_event() {
file_drop_runner.send_event(e)
@@ -1150,7 +1135,7 @@ impl<'a, T: 'static> InitData<'a, T> {
unsafe { &mut (*file_drop_handler.data).interface as *mut _ as *mut c_void };
assert_eq!(
unsafe { RegisterDragDrop(win.window.0, handler_interface_ptr) },
unsafe { RegisterDragDrop(win.window, handler_interface_ptr) },
S_OK
);
Some(file_drop_handler)
@@ -1227,7 +1212,7 @@ impl<'a, T: 'static> InitData<'a, T> {
if attributes.fullscreen.0.is_some() {
win.set_fullscreen(attributes.fullscreen.0.map(Into::into));
unsafe { force_window_active(win.window.0) };
unsafe { force_window_active(win.window) };
} else {
let size = attributes
.inner_size

View File

@@ -1475,10 +1475,6 @@ impl Window {
/// Returns the monitor on which the window currently resides.
///
/// Returns `None` if current monitor can't be detected.
///
/// ## Platform-specific
///
/// **iOS:** Can only be called on the main thread.
#[inline]
pub fn current_monitor(&self) -> Option<MonitorHandle> {
self.window
@@ -1489,10 +1485,6 @@ impl Window {
///
/// This is the same as [`EventLoopWindowTarget::available_monitors`], and is provided for convenience.
///
/// ## Platform-specific
///
/// **iOS:** Can only be called on the main thread.
///
/// [`EventLoopWindowTarget::available_monitors`]: crate::event_loop::EventLoopWindowTarget::available_monitors
#[inline]
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
@@ -1511,8 +1503,7 @@ impl Window {
///
/// ## Platform-specific
///
/// **iOS:** Can only be called on the main thread.
/// **Wayland:** Always returns `None`.
/// **Wayland / Web:** Always returns `None`.
///
/// [`EventLoopWindowTarget::primary_monitor`]: crate::event_loop::EventLoopWindowTarget::primary_monitor
#[inline]