mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5609729cc | ||
|
|
1f24a09570 | ||
|
|
a8e777a5df | ||
|
|
0bc58f695b | ||
|
|
28023d9f5b | ||
|
|
c2aed1979d | ||
|
|
7e04273719 | ||
|
|
0683bdcd42 | ||
|
|
29ab0bb629 | ||
|
|
7a9c17a520 | ||
|
|
b208daa271 | ||
|
|
e85a80dd65 | ||
|
|
b1d8ce24e9 | ||
|
|
098fd5d602 | ||
|
|
2f27f64cdb | ||
|
|
cbb60d29a2 | ||
|
|
e707052f66 | ||
|
|
71bd6e73ca | ||
|
|
b8326f6452 | ||
|
|
ece2e70a53 | ||
|
|
2b14ec23d5 | ||
|
|
9999f53329 | ||
|
|
522a6e3298 | ||
|
|
76d0dd7ec3 | ||
|
|
d1073dcecb | ||
|
|
e88e8bc194 | ||
|
|
bc29931434 | ||
|
|
505f312d5f | ||
|
|
f0093d3c54 | ||
|
|
83b60beba6 | ||
|
|
5f52d7c9d0 | ||
|
|
a1b65f7080 | ||
|
|
96df858961 | ||
|
|
4eddd1e5bc |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
Check_Formatting:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- uses: hecrj/setup-rust-action@v1
|
||||
with:
|
||||
rust-version: stable
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
# Used to cache cargo-web
|
||||
- name: Cache cargo folder
|
||||
uses: actions/cache@v1
|
||||
|
||||
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
Publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- uses: hecrj/setup-rust-action@v1
|
||||
with:
|
||||
rust-version: stable
|
||||
|
||||
28
CHANGELOG.md
28
CHANGELOG.md
@@ -1,5 +1,32 @@
|
||||
# Unreleased
|
||||
|
||||
# 0.22.1 (2020-04-16)
|
||||
|
||||
- On X11, fix `ResumeTimeReached` being fired too early.
|
||||
- On Web, replaced zero timeout for `ControlFlow::Poll` with `requestAnimationFrame`
|
||||
- On Web, fix a possible panic during event handling
|
||||
|
||||
# 0.22.0 (2020-03-09)
|
||||
|
||||
- On Windows, fix minor timing issue in wait_until_time_or_msg
|
||||
- On Windows, rework handling of request_redraw() to address panics.
|
||||
- On macOS, fix `set_simple_screen` to remember frame excluding title bar.
|
||||
- On Wayland, fix coordinates in touch events when scale factor isn't 1.
|
||||
- On Wayland, fix color from `close_button_icon_color` not applying.
|
||||
- Ignore locale if unsupported by X11 backend
|
||||
- On Wayland, Add HiDPI cursor support
|
||||
- On Web, add the ability to query "Light" or "Dark" system theme send `ThemeChanged` on change.
|
||||
- Fix `Event::to_static` returning `None` for user events.
|
||||
- On Wayland, Hide CSD for fullscreen windows.
|
||||
- On Windows, ignore spurious mouse move messages.
|
||||
- **Breaking:** Move `ModifiersChanged` variant from `DeviceEvent` to `WindowEvent`.
|
||||
- On Windows, add `IconExtWindows` trait which exposes creating an `Icon` from an external file or embedded resource
|
||||
- Add `BadIcon::OsError` variant for when OS icon functionality fails
|
||||
- On Windows, fix crash at startup on systems that do not properly support Windows' Dark Mode
|
||||
- Revert On macOS, fix not sending ReceivedCharacter event for specific keys combinations.
|
||||
- on macOS, fix incorrect ReceivedCharacter events for some key combinations.
|
||||
- **Breaking:** Use `i32` instead of `u32` for position type in `WindowEvent::Moved`.
|
||||
|
||||
# 0.21.0 (2020-02-04)
|
||||
|
||||
- On Windows, fixed "error: linking with `link.exe` failed: exit code: 1120" error on older versions of windows.
|
||||
@@ -20,7 +47,6 @@
|
||||
# 0.20.0 (2020-01-05)
|
||||
|
||||
- On X11, fix `ModifiersChanged` emitting incorrect modifier change events
|
||||
|
||||
- **Breaking**: Overhaul how Winit handles DPI:
|
||||
+ Window functions and events now return `PhysicalSize` instead of `LogicalSize`.
|
||||
+ Functions that take `Size` or `Position` types can now take either `Logical` or `Physical` types.
|
||||
|
||||
10
Cargo.toml
10
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.21.0"
|
||||
version = "0.22.1"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
edition = "2018"
|
||||
@@ -28,7 +28,7 @@ raw-window-handle = "0.3"
|
||||
bitflags = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
image = "0.21"
|
||||
image = "0.23"
|
||||
simple_logger = "1"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies.android_glue]
|
||||
@@ -77,8 +77,8 @@ features = [
|
||||
wayland-client = { version = "0.23.0", features = [ "dlopen", "egl", "cursor", "eventloop"] }
|
||||
mio = "0.6"
|
||||
mio-extras = "2.0"
|
||||
smithay-client-toolkit = "0.6"
|
||||
x11-dl = "2.18.3"
|
||||
smithay-client-toolkit = "^0.6.6"
|
||||
x11-dl = "2.18.5"
|
||||
percent-encoding = "2.0"
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "windows"))'.dependencies.parking_lot]
|
||||
@@ -101,6 +101,8 @@ features = [
|
||||
'HtmlCanvasElement',
|
||||
'HtmlElement',
|
||||
'KeyboardEvent',
|
||||
'MediaQueryList',
|
||||
'MediaQueryListEvent',
|
||||
'MouseEvent',
|
||||
'Node',
|
||||
'PointerEvent',
|
||||
|
||||
@@ -149,6 +149,9 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
* Getting the device idiom
|
||||
* Getting the preferred video mode
|
||||
|
||||
### Web
|
||||
* Get if systems preferred color scheme is "dark"
|
||||
|
||||
## Usability
|
||||
* `serde`: Enables serialization/deserialization of certain types with Serde. (Maintainer: @Osspial)
|
||||
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
|
||||
[](https://crates.io/crates/winit)
|
||||
[](https://docs.rs/winit)
|
||||
[](https://travis-ci.org/rust-windowing/winit)
|
||||
[](https://ci.appveyor.com/project/Osspial/winit/branch/master)
|
||||
[](https://github.com/rust-windowing/winit/actions)
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.21.0"
|
||||
winit = "0.22.1"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
|
||||
119
examples/control_flow.rs
Normal file
119
examples/control_flow.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
use std::{thread, time};
|
||||
|
||||
use winit::{
|
||||
event::{Event, KeyboardInput, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Mode {
|
||||
Wait,
|
||||
WaitUntil,
|
||||
Poll,
|
||||
}
|
||||
|
||||
const WAIT_TIME: time::Duration = time::Duration::from_millis(100);
|
||||
const POLL_SLEEP_TIME: time::Duration = time::Duration::from_millis(100);
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
|
||||
println!("Press '1' to switch to Wait mode.");
|
||||
println!("Press '2' to switch to WaitUntil mode.");
|
||||
println!("Press '3' to switch to Poll mode.");
|
||||
println!("Press 'R' to toggle request_redraw() calls.");
|
||||
println!("Press 'Esc' to close the window.");
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut mode = Mode::Wait;
|
||||
let mut request_redraw = false;
|
||||
let mut wait_cancelled = false;
|
||||
let mut close_requested = false;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
use winit::event::{ElementState, StartCause, VirtualKeyCode};
|
||||
println!("{:?}", event);
|
||||
match event {
|
||||
Event::NewEvents(start_cause) => {
|
||||
wait_cancelled = mode == Mode::WaitUntil;
|
||||
match start_cause {
|
||||
StartCause::ResumeTimeReached {
|
||||
start: _,
|
||||
requested_resume: _,
|
||||
} => {
|
||||
wait_cancelled = false;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
close_requested = true;
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
virtual_keycode: Some(virtual_code),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match virtual_code {
|
||||
VirtualKeyCode::Key1 => {
|
||||
mode = Mode::Wait;
|
||||
println!("\nmode: {:?}\n", mode);
|
||||
}
|
||||
VirtualKeyCode::Key2 => {
|
||||
mode = Mode::WaitUntil;
|
||||
println!("\nmode: {:?}\n", mode);
|
||||
}
|
||||
VirtualKeyCode::Key3 => {
|
||||
mode = Mode::Poll;
|
||||
println!("\nmode: {:?}\n", mode);
|
||||
}
|
||||
VirtualKeyCode::R => {
|
||||
request_redraw = !request_redraw;
|
||||
println!("\nrequest_redraw: {}\n", request_redraw);
|
||||
}
|
||||
VirtualKeyCode::Escape => {
|
||||
close_requested = true;
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
Event::MainEventsCleared => {
|
||||
if request_redraw && !wait_cancelled && !close_requested {
|
||||
window.request_redraw();
|
||||
}
|
||||
if close_requested {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
}
|
||||
Event::RedrawRequested(_window_id) => {}
|
||||
Event::RedrawEventsCleared => {
|
||||
*control_flow = match mode {
|
||||
Mode::Wait => ControlFlow::Wait,
|
||||
Mode::WaitUntil => {
|
||||
if wait_cancelled {
|
||||
*control_flow
|
||||
} else {
|
||||
ControlFlow::WaitUntil(time::Instant::now() + WAIT_TIME)
|
||||
}
|
||||
}
|
||||
Mode::Poll => {
|
||||
thread::sleep(POLL_SLEEP_TIME);
|
||||
ControlFlow::Poll
|
||||
}
|
||||
};
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -38,6 +38,7 @@ fn main() {
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
WindowEvent::ModifiersChanged(m) => modifiers = m,
|
||||
_ => (),
|
||||
},
|
||||
Event::DeviceEvent { event, .. } => match event {
|
||||
@@ -46,7 +47,6 @@ fn main() {
|
||||
ElementState::Pressed => println!("mouse button {} pressed", button),
|
||||
ElementState::Released => println!("mouse button {} released", button),
|
||||
},
|
||||
DeviceEvent::ModifiersChanged(m) => modifiers = m,
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
|
||||
39
examples/request_redraw_threaded.rs
Normal file
39
examples/request_redraw_threaded.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use std::{thread, time};
|
||||
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
thread::spawn(move || loop {
|
||||
thread::sleep(time::Duration::from_secs(1));
|
||||
window.request_redraw();
|
||||
});
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
println!("{:?}", event);
|
||||
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
println!("\nredrawing!\n");
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -45,13 +45,11 @@ fn main() {
|
||||
|
||||
fn load_icon(path: &Path) -> Icon {
|
||||
let (icon_rgba, icon_width, icon_height) = {
|
||||
let image = image::open(path).expect("Failed to open icon path");
|
||||
use image::{GenericImageView, Pixel};
|
||||
let image = image::open(path)
|
||||
.expect("Failed to open icon path")
|
||||
.into_rgba();
|
||||
let (width, height) = image.dimensions();
|
||||
let mut rgba = Vec::with_capacity((width * height) as usize * 4);
|
||||
for (_, _, pixel) in image.pixels() {
|
||||
rgba.extend_from_slice(&pixel.to_rgba().data);
|
||||
}
|
||||
let rgba = image.into_raw();
|
||||
(rgba, width, height)
|
||||
};
|
||||
Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
|
||||
|
||||
71
src/dpi.rs
71
src/dpi.rs
@@ -151,8 +151,8 @@ impl Pixel for f64 {
|
||||
/// anywhere other than winit, it's recommended to validate them using this function before passing them to winit;
|
||||
/// otherwise, you risk panics.
|
||||
#[inline]
|
||||
pub fn validate_scale_factor(dpi_factor: f64) -> bool {
|
||||
dpi_factor.is_sign_positive() && dpi_factor.is_normal()
|
||||
pub fn validate_scale_factor(scale_factor: f64) -> bool {
|
||||
scale_factor.is_sign_positive() && scale_factor.is_normal()
|
||||
}
|
||||
|
||||
/// A position represented in logical pixels.
|
||||
@@ -178,16 +178,16 @@ impl<P: Pixel> LogicalPosition<P> {
|
||||
#[inline]
|
||||
pub fn from_physical<T: Into<PhysicalPosition<X>>, X: Pixel>(
|
||||
physical: T,
|
||||
dpi_factor: f64,
|
||||
scale_factor: f64,
|
||||
) -> Self {
|
||||
physical.into().to_logical(dpi_factor)
|
||||
physical.into().to_logical(scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_physical<X: Pixel>(&self, dpi_factor: f64) -> PhysicalPosition<X> {
|
||||
assert!(validate_scale_factor(dpi_factor));
|
||||
let x = self.x.into() * dpi_factor;
|
||||
let y = self.y.into() * dpi_factor;
|
||||
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalPosition<X> {
|
||||
assert!(validate_scale_factor(scale_factor));
|
||||
let x = self.x.into() * scale_factor;
|
||||
let y = self.y.into() * scale_factor;
|
||||
PhysicalPosition::new(x, y).cast()
|
||||
}
|
||||
|
||||
@@ -243,16 +243,16 @@ impl<P: Pixel> PhysicalPosition<P> {
|
||||
#[inline]
|
||||
pub fn from_logical<T: Into<LogicalPosition<X>>, X: Pixel>(
|
||||
logical: T,
|
||||
dpi_factor: f64,
|
||||
scale_factor: f64,
|
||||
) -> Self {
|
||||
logical.into().to_physical(dpi_factor)
|
||||
logical.into().to_physical(scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_logical<X: Pixel>(&self, dpi_factor: f64) -> LogicalPosition<X> {
|
||||
assert!(validate_scale_factor(dpi_factor));
|
||||
let x = self.x.into() / dpi_factor;
|
||||
let y = self.y.into() / dpi_factor;
|
||||
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalPosition<X> {
|
||||
assert!(validate_scale_factor(scale_factor));
|
||||
let x = self.x.into() / scale_factor;
|
||||
let y = self.y.into() / scale_factor;
|
||||
LogicalPosition::new(x, y).cast()
|
||||
}
|
||||
|
||||
@@ -306,15 +306,18 @@ impl<P> LogicalSize<P> {
|
||||
|
||||
impl<P: Pixel> LogicalSize<P> {
|
||||
#[inline]
|
||||
pub fn from_physical<T: Into<PhysicalSize<X>>, X: Pixel>(physical: T, dpi_factor: f64) -> Self {
|
||||
physical.into().to_logical(dpi_factor)
|
||||
pub fn from_physical<T: Into<PhysicalSize<X>>, X: Pixel>(
|
||||
physical: T,
|
||||
scale_factor: f64,
|
||||
) -> Self {
|
||||
physical.into().to_logical(scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_physical<X: Pixel>(&self, dpi_factor: f64) -> PhysicalSize<X> {
|
||||
assert!(validate_scale_factor(dpi_factor));
|
||||
let width = self.width.into() * dpi_factor;
|
||||
let height = self.height.into() * dpi_factor;
|
||||
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalSize<X> {
|
||||
assert!(validate_scale_factor(scale_factor));
|
||||
let width = self.width.into() * scale_factor;
|
||||
let height = self.height.into() * scale_factor;
|
||||
PhysicalSize::new(width, height).cast()
|
||||
}
|
||||
|
||||
@@ -368,15 +371,15 @@ impl<P> PhysicalSize<P> {
|
||||
|
||||
impl<P: Pixel> PhysicalSize<P> {
|
||||
#[inline]
|
||||
pub fn from_logical<T: Into<LogicalSize<X>>, X: Pixel>(logical: T, dpi_factor: f64) -> Self {
|
||||
logical.into().to_physical(dpi_factor)
|
||||
pub fn from_logical<T: Into<LogicalSize<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
|
||||
logical.into().to_physical(scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_logical<X: Pixel>(&self, dpi_factor: f64) -> LogicalSize<X> {
|
||||
assert!(validate_scale_factor(dpi_factor));
|
||||
let width = self.width.into() / dpi_factor;
|
||||
let height = self.height.into() / dpi_factor;
|
||||
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalSize<X> {
|
||||
assert!(validate_scale_factor(scale_factor));
|
||||
let width = self.width.into() / scale_factor;
|
||||
let height = self.height.into() / scale_factor;
|
||||
LogicalSize::new(width, height).cast()
|
||||
}
|
||||
|
||||
@@ -426,17 +429,17 @@ impl Size {
|
||||
size.into()
|
||||
}
|
||||
|
||||
pub fn to_logical<P: Pixel>(&self, dpi_factor: f64) -> LogicalSize<P> {
|
||||
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalSize<P> {
|
||||
match *self {
|
||||
Size::Physical(size) => size.to_logical(dpi_factor),
|
||||
Size::Physical(size) => size.to_logical(scale_factor),
|
||||
Size::Logical(size) => size.cast(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_physical<P: Pixel>(&self, dpi_factor: f64) -> PhysicalSize<P> {
|
||||
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalSize<P> {
|
||||
match *self {
|
||||
Size::Physical(size) => size.cast(),
|
||||
Size::Logical(size) => size.to_physical(dpi_factor),
|
||||
Size::Logical(size) => size.to_physical(scale_factor),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -468,17 +471,17 @@ impl Position {
|
||||
position.into()
|
||||
}
|
||||
|
||||
pub fn to_logical<P: Pixel>(&self, dpi_factor: f64) -> LogicalPosition<P> {
|
||||
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalPosition<P> {
|
||||
match *self {
|
||||
Position::Physical(position) => position.to_logical(dpi_factor),
|
||||
Position::Physical(position) => position.to_logical(scale_factor),
|
||||
Position::Logical(position) => position.cast(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_physical<P: Pixel>(&self, dpi_factor: f64) -> PhysicalPosition<P> {
|
||||
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalPosition<P> {
|
||||
match *self {
|
||||
Position::Physical(position) => position.cast(),
|
||||
Position::Logical(position) => position.to_physical(dpi_factor),
|
||||
Position::Logical(position) => position.to_physical(scale_factor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
30
src/event.rs
30
src/event.rs
@@ -139,7 +139,7 @@ impl<'a, T> Event<'a, T> {
|
||||
WindowEvent { window_id, event } => event
|
||||
.to_static()
|
||||
.map(|event| WindowEvent { window_id, event }),
|
||||
UserEvent(_) => None,
|
||||
UserEvent(event) => Some(UserEvent(event)),
|
||||
DeviceEvent { device_id, event } => Some(DeviceEvent { device_id, event }),
|
||||
NewEvents(cause) => Some(NewEvents(cause)),
|
||||
MainEventsCleared => Some(MainEventsCleared),
|
||||
@@ -185,7 +185,7 @@ pub enum WindowEvent<'a> {
|
||||
Resized(PhysicalSize<u32>),
|
||||
|
||||
/// The position of the window has changed. Contains the window's new position.
|
||||
Moved(PhysicalPosition<u32>),
|
||||
Moved(PhysicalPosition<i32>),
|
||||
|
||||
/// The window has been requested to close.
|
||||
CloseRequested,
|
||||
@@ -235,6 +235,13 @@ pub enum WindowEvent<'a> {
|
||||
is_synthetic: bool,
|
||||
},
|
||||
|
||||
/// The keyboard modifiers have changed.
|
||||
///
|
||||
/// Platform-specific behavior:
|
||||
/// - **Web**: This API is currently unimplemented on the web. This isn't by design - it's an
|
||||
/// issue, and it should get fixed - but it's the current state of the API.
|
||||
ModifiersChanged(ModifiersState),
|
||||
|
||||
/// The cursor has moved on the window.
|
||||
CursorMoved {
|
||||
device_id: DeviceId,
|
||||
@@ -243,7 +250,7 @@ pub enum WindowEvent<'a> {
|
||||
/// limited by the display area and it may have been transformed by the OS to implement effects such as cursor
|
||||
/// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control.
|
||||
position: PhysicalPosition<f64>,
|
||||
#[deprecated = "Deprecated in favor of DeviceEvent::ModifiersChanged"]
|
||||
#[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"]
|
||||
modifiers: ModifiersState,
|
||||
},
|
||||
|
||||
@@ -258,7 +265,7 @@ pub enum WindowEvent<'a> {
|
||||
device_id: DeviceId,
|
||||
delta: MouseScrollDelta,
|
||||
phase: TouchPhase,
|
||||
#[deprecated = "Deprecated in favor of DeviceEvent::ModifiersChanged"]
|
||||
#[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"]
|
||||
modifiers: ModifiersState,
|
||||
},
|
||||
|
||||
@@ -267,7 +274,7 @@ pub enum WindowEvent<'a> {
|
||||
device_id: DeviceId,
|
||||
state: ElementState,
|
||||
button: MouseButton,
|
||||
#[deprecated = "Deprecated in favor of DeviceEvent::ModifiersChanged"]
|
||||
#[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"]
|
||||
modifiers: ModifiersState,
|
||||
},
|
||||
|
||||
@@ -341,6 +348,7 @@ impl<'a> WindowEvent<'a> {
|
||||
input,
|
||||
is_synthetic,
|
||||
}),
|
||||
ModifiersChanged(modifiers) => Some(ModifiersChanged(modifiers)),
|
||||
#[allow(deprecated)]
|
||||
CursorMoved {
|
||||
device_id,
|
||||
@@ -464,16 +472,6 @@ pub enum DeviceEvent {
|
||||
|
||||
Key(KeyboardInput),
|
||||
|
||||
/// The keyboard modifiers have changed.
|
||||
///
|
||||
/// This is tracked internally to avoid tracking errors arising from modifier key state changes when events from
|
||||
/// this device are not being delivered to the application, e.g. due to keyboard focus being elsewhere.
|
||||
///
|
||||
/// Platform-specific behavior:
|
||||
/// - **Web**: This API is currently unimplemented on the web. This isn't by design - it's an
|
||||
/// issue, and it should get fixed - but it's the current state of the API.
|
||||
ModifiersChanged(ModifiersState),
|
||||
|
||||
Text {
|
||||
codepoint: char,
|
||||
},
|
||||
@@ -502,7 +500,7 @@ pub struct KeyboardInput {
|
||||
///
|
||||
/// This is tracked internally to avoid tracking errors arising from modifier key state changes when events from
|
||||
/// this device are not being delivered to the application, e.g. due to keyboard focus being elsewhere.
|
||||
#[deprecated = "Deprecated in favor of DeviceEvent::ModifiersChanged"]
|
||||
#[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"]
|
||||
pub modifiers: ModifiersState,
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,8 @@ impl<T> fmt::Debug for EventLoopWindowTarget<T> {
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ControlFlow {
|
||||
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
|
||||
/// whether or not new events are available to process.
|
||||
/// whether or not new events are available to process. For web, events are sent when
|
||||
/// `requestAnimationFrame` fires.
|
||||
Poll,
|
||||
/// When the current loop iteration finishes, suspend the thread until another event arrives.
|
||||
Wait,
|
||||
|
||||
92
src/icon.rs
92
src/icon.rs
@@ -1,4 +1,5 @@
|
||||
use std::{error::Error, fmt, mem};
|
||||
use crate::platform_impl::PlatformIcon;
|
||||
use std::{error::Error, fmt, io, mem};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
@@ -11,7 +12,7 @@ pub(crate) struct Pixel {
|
||||
|
||||
pub(crate) const PIXEL_SIZE: usize = mem::size_of::<Pixel>();
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug)]
|
||||
/// An error produced when using `Icon::from_rgba` with invalid arguments.
|
||||
pub enum BadIcon {
|
||||
/// Produced when the length of the `rgba` argument isn't divisible by 4, thus `rgba` can't be
|
||||
@@ -25,6 +26,8 @@ pub enum BadIcon {
|
||||
width_x_height: usize,
|
||||
pixel_count: usize,
|
||||
},
|
||||
/// Produced when underlying OS functionality failed to create the icon
|
||||
OsError(io::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for BadIcon {
|
||||
@@ -43,6 +46,7 @@ impl fmt::Display for BadIcon {
|
||||
"The specified dimensions ({:?}x{:?}) don't match the number of pixels supplied by the `rgba` argument ({:?}). For those dimensions, the expected pixel count is {:?}.",
|
||||
width, height, pixel_count, width_x_height,
|
||||
),
|
||||
BadIcon::OsError(e) => write!(f, "OS error when instantiating the icon: {:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,38 +58,78 @@ impl Error for BadIcon {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
/// An icon used for the window titlebar, taskbar, etc.
|
||||
pub struct Icon {
|
||||
pub(crate) struct RgbaIcon {
|
||||
pub(crate) rgba: Vec<u8>,
|
||||
pub(crate) width: u32,
|
||||
pub(crate) height: u32,
|
||||
}
|
||||
|
||||
/// For platforms which don't have window icons (e.g. web)
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct NoIcon;
|
||||
|
||||
#[allow(dead_code)] // These are not used on every platform
|
||||
mod constructors {
|
||||
use super::*;
|
||||
|
||||
impl RgbaIcon {
|
||||
/// Creates an `Icon` from 32bpp RGBA data.
|
||||
///
|
||||
/// The length of `rgba` must be divisible by 4, and `width * height` must equal
|
||||
/// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error.
|
||||
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
|
||||
if rgba.len() % PIXEL_SIZE != 0 {
|
||||
return Err(BadIcon::ByteCountNotDivisibleBy4 {
|
||||
byte_count: rgba.len(),
|
||||
});
|
||||
}
|
||||
let pixel_count = rgba.len() / PIXEL_SIZE;
|
||||
if pixel_count != (width * height) as usize {
|
||||
Err(BadIcon::DimensionsVsPixelCount {
|
||||
width,
|
||||
height,
|
||||
width_x_height: (width * height) as usize,
|
||||
pixel_count,
|
||||
})
|
||||
} else {
|
||||
Ok(RgbaIcon {
|
||||
rgba,
|
||||
width,
|
||||
height,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NoIcon {
|
||||
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
|
||||
// Create the rgba icon anyway to validate the input
|
||||
let _ = RgbaIcon::from_rgba(rgba, width, height)?;
|
||||
Ok(NoIcon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An icon used for the window titlebar, taskbar, etc.
|
||||
#[derive(Clone)]
|
||||
pub struct Icon {
|
||||
pub(crate) inner: PlatformIcon,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Icon {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
fmt::Debug::fmt(&self.inner, formatter)
|
||||
}
|
||||
}
|
||||
|
||||
impl Icon {
|
||||
/// Creates an `Icon` from 32bpp RGBA data.
|
||||
///
|
||||
/// The length of `rgba` must be divisible by 4, and `width * height` must equal
|
||||
/// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error.
|
||||
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
|
||||
if rgba.len() % PIXEL_SIZE != 0 {
|
||||
return Err(BadIcon::ByteCountNotDivisibleBy4 {
|
||||
byte_count: rgba.len(),
|
||||
});
|
||||
}
|
||||
let pixel_count = rgba.len() / PIXEL_SIZE;
|
||||
if pixel_count != (width * height) as usize {
|
||||
Err(BadIcon::DimensionsVsPixelCount {
|
||||
width,
|
||||
height,
|
||||
width_x_height: (width * height) as usize,
|
||||
pixel_count,
|
||||
})
|
||||
} else {
|
||||
Ok(Icon {
|
||||
rgba,
|
||||
width,
|
||||
height,
|
||||
})
|
||||
}
|
||||
Ok(Icon {
|
||||
inner: PlatformIcon::from_rgba(rgba, width, height)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,7 +446,8 @@ impl<T: Theme> SCTKTheme for WaylandTheme<T> {
|
||||
}
|
||||
|
||||
fn get_close_button_icon_color(&self, status: SCTKButtonState) -> [u8; 4] {
|
||||
self.0.close_button_color(ButtonState::from_sctk(status))
|
||||
self.0
|
||||
.close_button_icon_color(ButtonState::from_sctk(status))
|
||||
}
|
||||
|
||||
fn get_maximize_button_color(&self, status: SCTKButtonState) -> [u8; 4] {
|
||||
|
||||
@@ -14,6 +14,9 @@ use stdweb::web::html_element::CanvasElement;
|
||||
#[cfg(feature = "stdweb")]
|
||||
pub trait WindowExtStdweb {
|
||||
fn canvas(&self) -> CanvasElement;
|
||||
|
||||
/// Whether the browser reports the preferred color scheme to be "dark".
|
||||
fn is_dark_mode(&self) -> bool;
|
||||
}
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
@@ -22,6 +25,9 @@ use web_sys::HtmlCanvasElement;
|
||||
#[cfg(feature = "web-sys")]
|
||||
pub trait WindowExtWebSys {
|
||||
fn canvas(&self) -> HtmlCanvasElement;
|
||||
|
||||
/// Whether the browser reports the preferred color scheme to be "dark".
|
||||
fn is_dark_mode(&self) -> bool;
|
||||
}
|
||||
|
||||
#[cfg(feature = "stdweb")]
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use std::os::raw::c_void;
|
||||
use std::path::Path;
|
||||
|
||||
use libc;
|
||||
use winapi::shared::minwindef::WORD;
|
||||
use winapi::shared::windef::HWND;
|
||||
|
||||
use crate::{
|
||||
dpi::PhysicalSize,
|
||||
event::DeviceId,
|
||||
event_loop::EventLoop,
|
||||
monitor::MonitorHandle,
|
||||
platform_impl::EventLoop as WindowsEventLoop,
|
||||
window::{Icon, Window, WindowBuilder},
|
||||
platform_impl::{EventLoop as WindowsEventLoop, WinIcon},
|
||||
window::{BadIcon, Icon, Window, WindowBuilder},
|
||||
};
|
||||
|
||||
/// Additional methods on `EventLoop` that are specific to Windows.
|
||||
@@ -171,3 +174,40 @@ impl DeviceIdExtWindows for DeviceId {
|
||||
self.0.persistent_identifier()
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `Icon` that are specific to Windows.
|
||||
pub trait IconExtWindows: Sized {
|
||||
/// Create an icon from a file path.
|
||||
///
|
||||
/// Specify `size` to load a specific icon size from the file, or `None` to load the default
|
||||
/// icon size from the file.
|
||||
///
|
||||
/// In cases where the specified size does not exist in the file, Windows may perform scaling
|
||||
/// to get an icon of the desired size.
|
||||
fn from_path<P: AsRef<Path>>(path: P, size: Option<PhysicalSize<u32>>)
|
||||
-> Result<Self, BadIcon>;
|
||||
|
||||
/// Create an icon from a resource embedded in this executable or library.
|
||||
///
|
||||
/// Specify `size` to load a specific icon size from the file, or `None` to load the default
|
||||
/// icon size from the file.
|
||||
///
|
||||
/// In cases where the specified size does not exist in the file, Windows may perform scaling
|
||||
/// to get an icon of the desired size.
|
||||
fn from_resource(ordinal: WORD, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon>;
|
||||
}
|
||||
|
||||
impl IconExtWindows for Icon {
|
||||
fn from_path<P: AsRef<Path>>(
|
||||
path: P,
|
||||
size: Option<PhysicalSize<u32>>,
|
||||
) -> Result<Self, BadIcon> {
|
||||
let win_icon = WinIcon::from_path(path, size)?;
|
||||
Ok(Icon { inner: win_icon })
|
||||
}
|
||||
|
||||
fn from_resource(ordinal: WORD, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon> {
|
||||
let win_icon = WinIcon::from_resource(ordinal, size)?;
|
||||
Ok(Icon { inner: win_icon })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ use crate::{
|
||||
use raw_window_handle::{android::AndroidHandle, RawWindowHandle};
|
||||
use CreationError::OsError;
|
||||
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
|
||||
pub type OsError = std::io::Error;
|
||||
|
||||
pub struct EventLoop {
|
||||
@@ -61,10 +63,10 @@ impl EventLoop {
|
||||
while let Ok(event) = self.event_rx.try_recv() {
|
||||
let e = match event {
|
||||
android_glue::Event::EventMotion(motion) => {
|
||||
let dpi_factor = MonitorHandle.scale_factor();
|
||||
let scale_factor = MonitorHandle.scale_factor();
|
||||
let location = LogicalPosition::from_physical(
|
||||
(motion.x as f64, motion.y as f64),
|
||||
dpi_factor,
|
||||
scale_factor,
|
||||
);
|
||||
Some(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId),
|
||||
@@ -102,9 +104,9 @@ impl EventLoop {
|
||||
if native_window.is_null() {
|
||||
None
|
||||
} else {
|
||||
let dpi_factor = MonitorHandle.scale_factor();
|
||||
let scale_factor = MonitorHandle.scale_factor();
|
||||
let physical_size = MonitorHandle.size();
|
||||
let size = LogicalSize::from_physical(physical_size, dpi_factor);
|
||||
let size = LogicalSize::from_physical(physical_size, scale_factor);
|
||||
Some(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId),
|
||||
event: WindowEvent::Resized(size),
|
||||
@@ -319,9 +321,9 @@ impl Window {
|
||||
if self.native_window.is_null() {
|
||||
None
|
||||
} else {
|
||||
let dpi_factor = self.scale_factor();
|
||||
let scale_factor = self.scale_factor();
|
||||
let physical_size = self.current_monitor().size();
|
||||
Some(LogicalSize::from_physical(physical_size, dpi_factor))
|
||||
Some(LogicalSize::from_physical(physical_size, scale_factor))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,8 @@ pub use self::{
|
||||
window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId},
|
||||
};
|
||||
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId {
|
||||
uiscreen: ffi::id,
|
||||
|
||||
@@ -127,12 +127,12 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
|
||||
let screen_space: id = msg_send![screen, coordinateSpace];
|
||||
let screen_frame: CGRect =
|
||||
msg_send![object, convertRect:bounds toCoordinateSpace:screen_space];
|
||||
let dpi_factor: CGFloat = msg_send![screen, scale];
|
||||
let scale_factor: CGFloat = msg_send![screen, scale];
|
||||
let size = crate::dpi::LogicalSize {
|
||||
width: screen_frame.size.width as f64,
|
||||
height: screen_frame.size.height as f64,
|
||||
}
|
||||
.to_physical(dpi_factor.into());
|
||||
.to_physical(scale_factor.into());
|
||||
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: RootWindowId(window.into()),
|
||||
event: WindowEvent::Resized(size),
|
||||
@@ -162,15 +162,15 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
|
||||
// `setContentScaleFactor` may be called with a value of 0, which means "reset the
|
||||
// content scale factor to a device-specific default value", so we can't use the
|
||||
// parameter here. We can query the actual factor using the getter
|
||||
let dpi_factor: CGFloat = msg_send![object, contentScaleFactor];
|
||||
let scale_factor: CGFloat = msg_send![object, contentScaleFactor];
|
||||
assert!(
|
||||
!dpi_factor.is_nan()
|
||||
&& dpi_factor.is_finite()
|
||||
&& dpi_factor.is_sign_positive()
|
||||
&& dpi_factor > 0.0,
|
||||
!scale_factor.is_nan()
|
||||
&& scale_factor.is_finite()
|
||||
&& scale_factor.is_sign_positive()
|
||||
&& scale_factor > 0.0,
|
||||
"invalid scale_factor set on UIView",
|
||||
);
|
||||
let scale_factor: f64 = dpi_factor.into();
|
||||
let scale_factor: f64 = scale_factor.into();
|
||||
let bounds: CGRect = msg_send![object, bounds];
|
||||
let screen: id = msg_send![window, screen];
|
||||
let screen_space: id = msg_send![screen, coordinateSpace];
|
||||
|
||||
@@ -83,8 +83,8 @@ impl Inner {
|
||||
x: safe_area.origin.x as f64,
|
||||
y: safe_area.origin.y as f64,
|
||||
};
|
||||
let dpi_factor = self.scale_factor();
|
||||
Ok(position.to_physical(dpi_factor))
|
||||
let scale_factor = self.scale_factor();
|
||||
Ok(position.to_physical(scale_factor))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,15 +95,15 @@ impl Inner {
|
||||
x: screen_frame.origin.x as f64,
|
||||
y: screen_frame.origin.y as f64,
|
||||
};
|
||||
let dpi_factor = self.scale_factor();
|
||||
Ok(position.to_physical(dpi_factor))
|
||||
let scale_factor = self.scale_factor();
|
||||
Ok(position.to_physical(scale_factor))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_outer_position(&self, physical_position: Position) {
|
||||
unsafe {
|
||||
let dpi_factor = self.scale_factor();
|
||||
let position = physical_position.to_logical::<f64>(dpi_factor);
|
||||
let scale_factor = self.scale_factor();
|
||||
let position = physical_position.to_logical::<f64>(scale_factor);
|
||||
let screen_frame = self.screen_frame();
|
||||
let new_screen_frame = CGRect {
|
||||
origin: CGPoint {
|
||||
@@ -119,25 +119,25 @@ impl Inner {
|
||||
|
||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||
unsafe {
|
||||
let dpi_factor = self.scale_factor();
|
||||
let scale_factor = self.scale_factor();
|
||||
let safe_area = self.safe_area_screen_space();
|
||||
let size = LogicalSize {
|
||||
width: safe_area.size.width as f64,
|
||||
height: safe_area.size.height as f64,
|
||||
};
|
||||
size.to_physical(dpi_factor)
|
||||
size.to_physical(scale_factor)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
unsafe {
|
||||
let dpi_factor = self.scale_factor();
|
||||
let scale_factor = self.scale_factor();
|
||||
let screen_frame = self.screen_frame();
|
||||
let size = LogicalSize {
|
||||
width: screen_frame.size.width as f64,
|
||||
height: screen_frame.size.height as f64,
|
||||
};
|
||||
size.to_physical(dpi_factor)
|
||||
size.to_physical(scale_factor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,8 +355,8 @@ impl Window {
|
||||
|
||||
let frame = match window_attributes.inner_size {
|
||||
Some(dim) => {
|
||||
let dpi_factor = msg_send![screen, scale];
|
||||
let size = dim.to_logical::<f64>(dpi_factor);
|
||||
let scale_factor = msg_send![screen, scale];
|
||||
let size = dim.to_logical::<f64>(scale_factor);
|
||||
CGRect {
|
||||
origin: screen_bounds.origin,
|
||||
size: CGSize {
|
||||
@@ -400,8 +400,8 @@ impl Window {
|
||||
|
||||
// Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `Resized`
|
||||
// event on window creation if the DPI factor != 1.0
|
||||
let dpi_factor: CGFloat = msg_send![view, contentScaleFactor];
|
||||
let scale_factor: f64 = dpi_factor.into();
|
||||
let scale_factor: CGFloat = msg_send![view, contentScaleFactor];
|
||||
let scale_factor: f64 = scale_factor.into();
|
||||
if scale_factor != 1.0 {
|
||||
let bounds: CGRect = msg_send![view, bounds];
|
||||
let screen: id = msg_send![window, screen];
|
||||
|
||||
@@ -18,6 +18,8 @@ use crate::{
|
||||
window::{CursorIcon, Fullscreen, WindowAttributes},
|
||||
};
|
||||
|
||||
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
|
||||
|
||||
pub mod wayland;
|
||||
pub mod x11;
|
||||
|
||||
@@ -61,7 +63,7 @@ impl Default for PlatformSpecificWindowBuilderAttributes {
|
||||
|
||||
lazy_static! {
|
||||
pub static ref X11_BACKEND: Mutex<Result<Arc<XConnection>, XNotSupported>> =
|
||||
{ Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)) };
|
||||
Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new));
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -584,13 +586,12 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
|
||||
pub fn new_x11_any_thread() -> Result<EventLoop<T>, XNotSupported> {
|
||||
X11_BACKEND
|
||||
.lock()
|
||||
.as_ref()
|
||||
.map(Arc::clone)
|
||||
.map(x11::EventLoop::new)
|
||||
.map(EventLoop::X)
|
||||
.map_err(|err| err.clone())
|
||||
let xconn = match X11_BACKEND.lock().as_ref() {
|
||||
Ok(xconn) => xconn.clone(),
|
||||
Err(err) => return Err(err.clone()),
|
||||
};
|
||||
|
||||
Ok(EventLoop::X(x11::EventLoop::new(xconn)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -39,7 +39,10 @@ use crate::{
|
||||
window::{CursorIcon, WindowId as RootWindowId},
|
||||
};
|
||||
|
||||
use super::{window::WindowStore, DeviceId, WindowId};
|
||||
use super::{
|
||||
window::{DecorationsAction, WindowStore},
|
||||
DeviceId, WindowId,
|
||||
};
|
||||
|
||||
use smithay_client_toolkit::{
|
||||
output::OutputMgr,
|
||||
@@ -90,6 +93,7 @@ pub struct CursorManager {
|
||||
locked_pointers: Vec<ZwpLockedPointerV1>,
|
||||
cursor_visible: bool,
|
||||
current_cursor: CursorIcon,
|
||||
scale_factor: u32,
|
||||
}
|
||||
|
||||
impl CursorManager {
|
||||
@@ -101,6 +105,7 @@ impl CursorManager {
|
||||
locked_pointers: Vec::new(),
|
||||
cursor_visible: true,
|
||||
current_cursor: CursorIcon::default(),
|
||||
scale_factor: 1,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +150,11 @@ impl CursorManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_scale_factor(&mut self, scale: u32) {
|
||||
self.scale_factor = scale;
|
||||
self.reload_cursor_style();
|
||||
}
|
||||
|
||||
fn set_cursor_icon_impl(&mut self, cursor: CursorIcon) {
|
||||
let cursor = match cursor {
|
||||
CursorIcon::Alias => "link",
|
||||
@@ -193,7 +203,7 @@ impl CursorManager {
|
||||
for pointer in self.pointers.iter() {
|
||||
// Ignore erros, since we don't want to fail hard in case we can't find a proper cursor
|
||||
// in a given theme.
|
||||
let _ = pointer.set_cursor(cursor, None);
|
||||
let _ = pointer.set_cursor_with_scale(cursor, self.scale_factor, None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -706,6 +716,13 @@ impl<T> EventLoop<T> {
|
||||
crate::window::WindowId(crate::platform_impl::WindowId::Wayland(window.wid));
|
||||
if let Some(frame) = window.frame {
|
||||
if let Some((w, h)) = window.newsize {
|
||||
// Update decorations state
|
||||
match window.decorations_action {
|
||||
Some(DecorationsAction::Hide) => frame.set_decorate(false),
|
||||
Some(DecorationsAction::Show) => frame.set_decorate(true),
|
||||
None => (),
|
||||
}
|
||||
|
||||
// mutter (GNOME Wayland) relies on `set_geometry` to reposition window in case
|
||||
// it overlaps mutter's `bounding box`, so we can't avoid this resize call,
|
||||
// which calls `set_geometry` under the hood, for now.
|
||||
@@ -728,6 +745,13 @@ impl<T> EventLoop<T> {
|
||||
}
|
||||
|
||||
if let Some(dpi) = window.new_dpi {
|
||||
// Update cursor scale factor
|
||||
{
|
||||
self.cursor_manager
|
||||
.lock()
|
||||
.unwrap()
|
||||
.update_scale_factor(dpi as u32);
|
||||
};
|
||||
let dpi = dpi as f64;
|
||||
let logical_size = LogicalSize::<f64>::from(*window.size);
|
||||
let mut new_inner_size = logical_size.to_physical(dpi);
|
||||
@@ -742,6 +766,8 @@ impl<T> EventLoop<T> {
|
||||
|
||||
let (w, h) = new_inner_size.to_logical::<u32>(dpi).into();
|
||||
frame.resize(w, h);
|
||||
// Refresh frame to rescale decorations
|
||||
frame.refresh();
|
||||
*window.size = (w, h);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,7 @@ use smithay_client_toolkit::{
|
||||
reexports::client::protocol::{wl_keyboard, wl_seat},
|
||||
};
|
||||
|
||||
use crate::event::{
|
||||
DeviceEvent, ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent,
|
||||
};
|
||||
use crate::event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent};
|
||||
|
||||
pub fn init_keyboard(
|
||||
seat: &wl_seat::WlSeat,
|
||||
@@ -33,9 +31,24 @@ pub fn init_keyboard(
|
||||
let wid = make_wid(&surface);
|
||||
my_sink.send_window_event(WindowEvent::Focused(true), wid);
|
||||
*target.lock().unwrap() = Some(wid);
|
||||
|
||||
let modifiers = *modifiers_tracker.lock().unwrap();
|
||||
|
||||
if !modifiers.is_empty() {
|
||||
my_sink.send_window_event(WindowEvent::ModifiersChanged(modifiers), wid);
|
||||
}
|
||||
}
|
||||
KbEvent::Leave { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
let modifiers = *modifiers_tracker.lock().unwrap();
|
||||
|
||||
if !modifiers.is_empty() {
|
||||
my_sink.send_window_event(
|
||||
WindowEvent::ModifiersChanged(ModifiersState::empty()),
|
||||
wid,
|
||||
);
|
||||
}
|
||||
|
||||
my_sink.send_window_event(WindowEvent::Focused(false), wid);
|
||||
*target.lock().unwrap() = None;
|
||||
}
|
||||
@@ -88,7 +101,9 @@ pub fn init_keyboard(
|
||||
|
||||
*modifiers_tracker.lock().unwrap() = modifiers;
|
||||
|
||||
my_sink.send_device_event(DeviceEvent::ModifiersChanged(modifiers), DeviceId);
|
||||
if let Some(wid) = *target.lock().unwrap() {
|
||||
my_sink.send_window_event(WindowEvent::ModifiersChanged(modifiers), wid);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::dpi::LogicalPosition;
|
||||
use crate::event::{
|
||||
DeviceEvent, ElementState, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase,
|
||||
WindowEvent,
|
||||
@@ -76,13 +77,16 @@ pub fn implement_pointer(
|
||||
},
|
||||
wid,
|
||||
);
|
||||
|
||||
let position = LogicalPosition::new(surface_x, surface_y)
|
||||
.to_physical(scale_factor);
|
||||
|
||||
sink.send_window_event(
|
||||
WindowEvent::CursorMoved {
|
||||
device_id: crate::event::DeviceId(
|
||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
||||
),
|
||||
position: (surface_x * scale_factor, surface_y * scale_factor)
|
||||
.into(),
|
||||
position,
|
||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
||||
},
|
||||
wid,
|
||||
@@ -109,15 +113,18 @@ pub fn implement_pointer(
|
||||
..
|
||||
} => {
|
||||
if let Some(surface) = mouse_focus.as_ref() {
|
||||
let scale_factor = surface::get_dpi_factor(&surface) as f64;
|
||||
let wid = make_wid(surface);
|
||||
|
||||
let scale_factor = surface::get_dpi_factor(&surface) as f64;
|
||||
let position = LogicalPosition::new(surface_x, surface_y)
|
||||
.to_physical(scale_factor);
|
||||
|
||||
sink.send_window_event(
|
||||
WindowEvent::CursorMoved {
|
||||
device_id: crate::event::DeviceId(
|
||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
||||
),
|
||||
position: (surface_x * scale_factor, surface_y * scale_factor)
|
||||
.into(),
|
||||
position,
|
||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
||||
},
|
||||
wid,
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::dpi::LogicalPosition;
|
||||
use crate::event::{TouchPhase, WindowEvent};
|
||||
|
||||
use super::{event_loop::EventsSink, window::WindowStore, DeviceId, WindowId};
|
||||
use super::{event_loop::EventsSink, make_wid, window::WindowStore, DeviceId};
|
||||
|
||||
use smithay_client_toolkit::surface;
|
||||
|
||||
use smithay_client_toolkit::reexports::client::protocol::{
|
||||
wl_seat,
|
||||
wl_surface::WlSurface,
|
||||
wl_touch::{Event as TouchEvent, WlTouch},
|
||||
};
|
||||
|
||||
// location is in logical coordinates.
|
||||
struct TouchPoint {
|
||||
wid: WindowId,
|
||||
location: (f64, f64),
|
||||
surface: WlSurface,
|
||||
position: LogicalPosition<f64>,
|
||||
id: i32,
|
||||
}
|
||||
|
||||
@@ -31,21 +36,24 @@ pub(crate) fn implement_touch(
|
||||
} => {
|
||||
let wid = store.find_wid(&surface);
|
||||
if let Some(wid) = wid {
|
||||
let scale_factor = surface::get_dpi_factor(&surface) as f64;
|
||||
let position = LogicalPosition::new(x, y);
|
||||
|
||||
sink.send_window_event(
|
||||
WindowEvent::Touch(crate::event::Touch {
|
||||
device_id: crate::event::DeviceId(
|
||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
||||
),
|
||||
phase: TouchPhase::Started,
|
||||
location: (x, y).into(),
|
||||
location: position.to_physical(scale_factor),
|
||||
force: None, // TODO
|
||||
id: id as u64,
|
||||
}),
|
||||
wid,
|
||||
);
|
||||
pending_ids.push(TouchPoint {
|
||||
wid,
|
||||
location: (x, y),
|
||||
surface,
|
||||
position,
|
||||
id,
|
||||
});
|
||||
}
|
||||
@@ -54,52 +62,63 @@ pub(crate) fn implement_touch(
|
||||
let idx = pending_ids.iter().position(|p| p.id == id);
|
||||
if let Some(idx) = idx {
|
||||
let pt = pending_ids.remove(idx);
|
||||
|
||||
let scale_factor = surface::get_dpi_factor(&pt.surface) as f64;
|
||||
let location = pt.position.to_physical(scale_factor);
|
||||
|
||||
sink.send_window_event(
|
||||
WindowEvent::Touch(crate::event::Touch {
|
||||
device_id: crate::event::DeviceId(
|
||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
||||
),
|
||||
phase: TouchPhase::Ended,
|
||||
location: pt.location.into(),
|
||||
location,
|
||||
force: None, // TODO
|
||||
id: id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
make_wid(&pt.surface),
|
||||
);
|
||||
}
|
||||
}
|
||||
TouchEvent::Motion { id, x, y, .. } => {
|
||||
let pt = pending_ids.iter_mut().find(|p| p.id == id);
|
||||
if let Some(pt) = pt {
|
||||
pt.location = (x, y);
|
||||
pt.position = LogicalPosition::new(x, y);
|
||||
|
||||
let scale_factor = surface::get_dpi_factor(&pt.surface) as f64;
|
||||
let location = pt.position.to_physical(scale_factor);
|
||||
|
||||
sink.send_window_event(
|
||||
WindowEvent::Touch(crate::event::Touch {
|
||||
device_id: crate::event::DeviceId(
|
||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
||||
),
|
||||
phase: TouchPhase::Moved,
|
||||
location: (x, y).into(),
|
||||
location,
|
||||
force: None, // TODO
|
||||
id: id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
make_wid(&pt.surface),
|
||||
);
|
||||
}
|
||||
}
|
||||
TouchEvent::Frame => (),
|
||||
TouchEvent::Cancel => {
|
||||
for pt in pending_ids.drain(..) {
|
||||
let scale_factor = surface::get_dpi_factor(&pt.surface) as f64;
|
||||
let location = pt.position.to_physical(scale_factor);
|
||||
|
||||
sink.send_window_event(
|
||||
WindowEvent::Touch(crate::event::Touch {
|
||||
device_id: crate::event::DeviceId(
|
||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
||||
),
|
||||
phase: TouchPhase::Cancelled,
|
||||
location: pt.location.into(),
|
||||
location,
|
||||
force: None, // TODO
|
||||
id: pt.id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
make_wid(&pt.surface),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,13 @@ pub struct Window {
|
||||
need_refresh: Arc<Mutex<bool>>,
|
||||
fullscreen: Arc<Mutex<bool>>,
|
||||
cursor_grab_changed: Arc<Mutex<Option<bool>>>, // Update grab state
|
||||
decorated: Arc<Mutex<bool>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum DecorationsAction {
|
||||
Hide,
|
||||
Show,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
@@ -69,6 +76,9 @@ impl Window {
|
||||
|
||||
let window_store = evlp.store.clone();
|
||||
|
||||
let decorated = Arc::new(Mutex::new(attributes.decorations));
|
||||
let pending_decorations_action = Arc::new(Mutex::new(None));
|
||||
|
||||
let my_surface = surface.clone();
|
||||
let mut frame = SWindow::<ConceptFrame>::init_from_env(
|
||||
&evlp.env,
|
||||
@@ -83,7 +93,23 @@ impl Window {
|
||||
if window.surface.as_ref().equals(&my_surface.as_ref()) {
|
||||
window.newsize = new_size;
|
||||
*(window.need_refresh.lock().unwrap()) = true;
|
||||
*(window.fullscreen.lock().unwrap()) = is_fullscreen;
|
||||
{
|
||||
// Get whether we're in fullscreen
|
||||
let mut fullscreen = window.fullscreen.lock().unwrap();
|
||||
// Fullscreen state was changed, so update decorations
|
||||
if *fullscreen != is_fullscreen {
|
||||
let decorated = { *window.decorated.lock().unwrap() };
|
||||
if decorated {
|
||||
*window.pending_decorations_action.lock().unwrap() =
|
||||
if is_fullscreen {
|
||||
Some(DecorationsAction::Hide)
|
||||
} else {
|
||||
Some(DecorationsAction::Show)
|
||||
};
|
||||
}
|
||||
}
|
||||
*fullscreen = is_fullscreen;
|
||||
}
|
||||
*(window.need_frame_refresh.lock().unwrap()) = true;
|
||||
return;
|
||||
}
|
||||
@@ -174,6 +200,8 @@ impl Window {
|
||||
frame: Arc::downgrade(&frame),
|
||||
current_dpi: 1,
|
||||
new_dpi: None,
|
||||
decorated: decorated.clone(),
|
||||
pending_decorations_action: pending_decorations_action.clone(),
|
||||
});
|
||||
evlp.evq.borrow_mut().sync_roundtrip().unwrap();
|
||||
|
||||
@@ -189,6 +217,7 @@ impl Window {
|
||||
cursor_manager,
|
||||
fullscreen,
|
||||
cursor_grab_changed,
|
||||
decorated,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -277,6 +306,7 @@ impl Window {
|
||||
}
|
||||
|
||||
pub fn set_decorations(&self, decorate: bool) {
|
||||
*(self.decorated.lock().unwrap()) = decorate;
|
||||
self.frame.lock().unwrap().set_decorate(decorate);
|
||||
*(self.need_frame_refresh.lock().unwrap()) = true;
|
||||
}
|
||||
@@ -409,6 +439,8 @@ struct InternalWindow {
|
||||
frame: Weak<Mutex<SWindow<ConceptFrame>>>,
|
||||
current_dpi: i32,
|
||||
new_dpi: Option<i32>,
|
||||
decorated: Arc<Mutex<bool>>,
|
||||
pending_decorations_action: Arc<Mutex<Option<DecorationsAction>>>,
|
||||
}
|
||||
|
||||
pub struct WindowStore {
|
||||
@@ -425,6 +457,7 @@ pub struct WindowStoreForEach<'a> {
|
||||
pub surface: &'a wl_surface::WlSurface,
|
||||
pub wid: WindowId,
|
||||
pub frame: Option<&'a mut SWindow<ConceptFrame>>,
|
||||
pub decorations_action: Option<DecorationsAction>,
|
||||
}
|
||||
|
||||
impl WindowStore {
|
||||
@@ -482,6 +515,7 @@ impl WindowStore {
|
||||
let opt_arc = window.frame.upgrade();
|
||||
let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap());
|
||||
let mut size = { *window.size.lock().unwrap() };
|
||||
let decorations_action = { window.pending_decorations_action.lock().unwrap().take() };
|
||||
f(WindowStoreForEach {
|
||||
newsize: window.newsize.take(),
|
||||
size: &mut size,
|
||||
@@ -492,6 +526,7 @@ impl WindowStore {
|
||||
surface: &window.surface,
|
||||
wid: make_wid(&window.surface),
|
||||
frame: opt_mutex_lock.as_mut().map(|m| &mut **m),
|
||||
decorations_action,
|
||||
});
|
||||
*window.size.lock().unwrap() = size;
|
||||
if let Some(dpi) = window.new_dpi.take() {
|
||||
|
||||
@@ -32,6 +32,8 @@ pub(super) struct EventProcessor<T: 'static> {
|
||||
// Number of touch events currently in progress
|
||||
pub(super) num_touch: u32,
|
||||
pub(super) first_touch: Option<u64>,
|
||||
// Currently focused window belonging to this process
|
||||
pub(super) active_window: Option<ffi::Window>,
|
||||
}
|
||||
|
||||
impl<T: 'static> EventProcessor<T> {
|
||||
@@ -136,11 +138,12 @@ impl<T: 'static> EventProcessor<T> {
|
||||
if let Some(modifiers) =
|
||||
self.device_mod_state.update_state(&state, modifier)
|
||||
{
|
||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
|
||||
callback(Event::DeviceEvent {
|
||||
device_id,
|
||||
event: DeviceEvent::ModifiersChanged(modifiers),
|
||||
});
|
||||
if let Some(window_id) = self.active_window {
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(window_id),
|
||||
event: WindowEvent::ModifiersChanged(modifiers),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -872,44 +875,58 @@ impl<T: 'static> EventProcessor<T> {
|
||||
ffi::XI_FocusIn => {
|
||||
let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
|
||||
|
||||
let window_id = mkwid(xev.event);
|
||||
|
||||
wt.ime
|
||||
.borrow_mut()
|
||||
.focus(xev.event)
|
||||
.expect("Failed to focus input context");
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: Focused(true),
|
||||
});
|
||||
|
||||
let modifiers = ModifiersState::from_x11(&xev.mods);
|
||||
|
||||
update_modifiers!(modifiers, None);
|
||||
self.device_mod_state.update_state(&modifiers, None);
|
||||
|
||||
// The deviceid for this event is for a keyboard instead of a pointer,
|
||||
// so we have to do a little extra work.
|
||||
let pointer_id = self
|
||||
.devices
|
||||
.borrow()
|
||||
.get(&DeviceId(xev.deviceid))
|
||||
.map(|device| device.attachment)
|
||||
.unwrap_or(2);
|
||||
if self.active_window != Some(xev.event) {
|
||||
self.active_window = Some(xev.event);
|
||||
|
||||
let position = PhysicalPosition::new(xev.event_x, xev.event_y);
|
||||
let window_id = mkwid(xev.event);
|
||||
let position = PhysicalPosition::new(xev.event_x, xev.event_y);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: CursorMoved {
|
||||
device_id: mkdid(pointer_id),
|
||||
position,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: Focused(true),
|
||||
});
|
||||
|
||||
// Issue key press events for all pressed keys
|
||||
self.handle_pressed_keys(window_id, ElementState::Pressed, &mut callback);
|
||||
if !modifiers.is_empty() {
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ModifiersChanged(modifiers),
|
||||
});
|
||||
}
|
||||
|
||||
// The deviceid for this event is for a keyboard instead of a pointer,
|
||||
// so we have to do a little extra work.
|
||||
let pointer_id = self
|
||||
.devices
|
||||
.borrow()
|
||||
.get(&DeviceId(xev.deviceid))
|
||||
.map(|device| device.attachment)
|
||||
.unwrap_or(2);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: CursorMoved {
|
||||
device_id: mkdid(pointer_id),
|
||||
position,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
|
||||
// Issue key press events for all pressed keys
|
||||
self.handle_pressed_keys(
|
||||
window_id,
|
||||
ElementState::Pressed,
|
||||
&mut callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
ffi::XI_FocusOut => {
|
||||
let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
|
||||
@@ -921,15 +938,26 @@ impl<T: 'static> EventProcessor<T> {
|
||||
.unfocus(xev.event)
|
||||
.expect("Failed to unfocus input context");
|
||||
|
||||
let window_id = mkwid(xev.event);
|
||||
if self.active_window.take() == Some(xev.event) {
|
||||
let window_id = mkwid(xev.event);
|
||||
|
||||
// Issue key release events for all pressed keys
|
||||
self.handle_pressed_keys(window_id, ElementState::Released, &mut callback);
|
||||
// Issue key release events for all pressed keys
|
||||
self.handle_pressed_keys(
|
||||
window_id,
|
||||
ElementState::Released,
|
||||
&mut callback,
|
||||
);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: Focused(false),
|
||||
})
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ModifiersChanged(ModifiersState::empty()),
|
||||
});
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: Focused(false),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
|
||||
@@ -1084,10 +1112,12 @@ impl<T: 'static> EventProcessor<T> {
|
||||
let new_modifiers = self.device_mod_state.modifiers();
|
||||
|
||||
if modifiers != new_modifiers {
|
||||
callback(Event::DeviceEvent {
|
||||
device_id,
|
||||
event: DeviceEvent::ModifiersChanged(new_modifiers),
|
||||
});
|
||||
if let Some(window_id) = self.active_window {
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(window_id),
|
||||
event: WindowEvent::ModifiersChanged(new_modifiers),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ unsafe fn open_im(xconn: &Arc<XConnection>, locale_modifiers: &CStr) -> Option<f
|
||||
// XSetLocaleModifiers returns...
|
||||
// * The current locale modifiers if it's given a NULL pointer.
|
||||
// * The new locale modifiers if we succeeded in setting them.
|
||||
// * NULL if the locale modifiers string is malformed.
|
||||
// * NULL if the locale modifiers string is malformed or if the
|
||||
// current locale is not supported by Xlib.
|
||||
(xconn.xlib.XSetLocaleModifiers)(locale_modifiers.as_ptr());
|
||||
|
||||
let im = (xconn.xlib.XOpenIM)(
|
||||
|
||||
@@ -29,6 +29,7 @@ use std::{
|
||||
mem::{self, MaybeUninit},
|
||||
ops::Deref,
|
||||
os::raw::*,
|
||||
ptr,
|
||||
rc::Rc,
|
||||
slice,
|
||||
sync::{mpsc, Arc, Mutex, Weak},
|
||||
@@ -105,7 +106,25 @@ impl<T: 'static> EventLoop<T> {
|
||||
// Input methods will open successfully without setting the locale, but it won't be
|
||||
// possible to actually commit pre-edit sequences.
|
||||
unsafe {
|
||||
// Remember default locale to restore it if target locale is unsupported
|
||||
// by Xlib
|
||||
let default_locale = setlocale(LC_CTYPE, ptr::null());
|
||||
setlocale(LC_CTYPE, b"\0".as_ptr() as *const _);
|
||||
|
||||
// Check if set locale is supported by Xlib.
|
||||
// If not, calls to some Xlib functions like `XSetLocaleModifiers`
|
||||
// will fail.
|
||||
let locale_supported = (xconn.xlib.XSupportsLocale)() == 1;
|
||||
if !locale_supported {
|
||||
let unsupported_locale = setlocale(LC_CTYPE, ptr::null());
|
||||
warn!(
|
||||
"Unsupported locale \"{}\". Restoring default locale \"{}\".",
|
||||
CStr::from_ptr(unsupported_locale).to_string_lossy(),
|
||||
CStr::from_ptr(default_locale).to_string_lossy()
|
||||
);
|
||||
// Restore default locale
|
||||
setlocale(LC_CTYPE, default_locale);
|
||||
}
|
||||
}
|
||||
let ime = RefCell::new({
|
||||
let result = Ime::new(Arc::clone(&xconn));
|
||||
@@ -206,6 +225,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
device_mod_state: Default::default(),
|
||||
num_touch: 0,
|
||||
first_touch: None,
|
||||
active_window: None,
|
||||
};
|
||||
|
||||
// Register for device hotplug events
|
||||
@@ -248,14 +268,16 @@ impl<T: 'static> EventLoop<T> {
|
||||
{
|
||||
let mut control_flow = ControlFlow::default();
|
||||
let mut events = Events::with_capacity(8);
|
||||
|
||||
callback(
|
||||
crate::event::Event::NewEvents(crate::event::StartCause::Init),
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
);
|
||||
let mut cause = StartCause::Init;
|
||||
|
||||
loop {
|
||||
sticky_exit_callback(
|
||||
crate::event::Event::NewEvents(cause),
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
&mut callback,
|
||||
);
|
||||
|
||||
// Process all pending events
|
||||
self.drain_events(&mut callback, &mut control_flow);
|
||||
|
||||
@@ -306,7 +328,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
|
||||
let start = Instant::now();
|
||||
let (mut cause, deadline, timeout);
|
||||
let (deadline, timeout);
|
||||
|
||||
match control_flow {
|
||||
ControlFlow::Exit => break,
|
||||
@@ -337,38 +359,20 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
if self.event_processor.poll() {
|
||||
// If the XConnection already contains buffered events, we don't
|
||||
// need to wait for data on the socket.
|
||||
// However, we still need to check for user events.
|
||||
self.poll
|
||||
.poll(&mut events, Some(Duration::from_millis(0)))
|
||||
.unwrap();
|
||||
events.clear();
|
||||
|
||||
callback(
|
||||
crate::event::Event::NewEvents(cause),
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
);
|
||||
} else {
|
||||
// If the XConnection already contains buffered events, we don't
|
||||
// need to wait for data on the socket.
|
||||
if !self.event_processor.poll() {
|
||||
self.poll.poll(&mut events, timeout).unwrap();
|
||||
events.clear();
|
||||
}
|
||||
|
||||
let wait_cancelled = deadline.map_or(false, |deadline| Instant::now() < deadline);
|
||||
let wait_cancelled = deadline.map_or(false, |deadline| Instant::now() < deadline);
|
||||
|
||||
if wait_cancelled {
|
||||
cause = StartCause::WaitCancelled {
|
||||
start,
|
||||
requested_resume: deadline,
|
||||
};
|
||||
}
|
||||
|
||||
callback(
|
||||
crate::event::Event::NewEvents(cause),
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
);
|
||||
if wait_cancelled {
|
||||
cause = StartCause::WaitCancelled {
|
||||
start,
|
||||
requested_resume: deadline,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::*;
|
||||
use crate::window::{Icon, Pixel, PIXEL_SIZE};
|
||||
use crate::icon::{Icon, Pixel, PIXEL_SIZE};
|
||||
|
||||
impl Pixel {
|
||||
pub fn to_packed_argb(&self) -> Cardinal {
|
||||
@@ -18,13 +18,14 @@ impl Pixel {
|
||||
|
||||
impl Icon {
|
||||
pub(crate) fn to_cardinals(&self) -> Vec<Cardinal> {
|
||||
assert_eq!(self.rgba.len() % PIXEL_SIZE, 0);
|
||||
let pixel_count = self.rgba.len() / PIXEL_SIZE;
|
||||
assert_eq!(pixel_count, (self.width * self.height) as usize);
|
||||
let rgba_icon = &self.inner;
|
||||
assert_eq!(rgba_icon.rgba.len() % PIXEL_SIZE, 0);
|
||||
let pixel_count = rgba_icon.rgba.len() / PIXEL_SIZE;
|
||||
assert_eq!(pixel_count, (rgba_icon.width * rgba_icon.height) as usize);
|
||||
let mut data = Vec::with_capacity(pixel_count);
|
||||
data.push(self.width as Cardinal);
|
||||
data.push(self.height as Cardinal);
|
||||
let pixels = self.rgba.as_ptr() as *const Pixel;
|
||||
data.push(rgba_icon.width as Cardinal);
|
||||
data.push(rgba_icon.height as Cardinal);
|
||||
let pixels = rgba_icon.rgba.as_ptr() as *const Pixel;
|
||||
for pixel_index in 0..pixel_count {
|
||||
let pixel = unsafe { &*pixels.offset(pixel_index as isize) };
|
||||
data.push(pixel.to_packed_argb());
|
||||
|
||||
@@ -136,23 +136,23 @@ impl UnownedWindow {
|
||||
})
|
||||
.unwrap_or_else(|| monitors.swap_remove(0))
|
||||
};
|
||||
let dpi_factor = guessed_monitor.scale_factor();
|
||||
let scale_factor = guessed_monitor.scale_factor();
|
||||
|
||||
info!("Guessed window scale factor: {}", dpi_factor);
|
||||
info!("Guessed window scale factor: {}", scale_factor);
|
||||
|
||||
let max_inner_size: Option<(u32, u32)> = window_attrs
|
||||
.max_inner_size
|
||||
.map(|size| size.to_physical::<u32>(dpi_factor).into());
|
||||
.map(|size| size.to_physical::<u32>(scale_factor).into());
|
||||
let min_inner_size: Option<(u32, u32)> = window_attrs
|
||||
.min_inner_size
|
||||
.map(|size| size.to_physical::<u32>(dpi_factor).into());
|
||||
.map(|size| size.to_physical::<u32>(scale_factor).into());
|
||||
|
||||
let dimensions = {
|
||||
// x11 only applies constraints when the window is actively resized
|
||||
// by the user, so we have to manually apply the initial constraints
|
||||
let mut dimensions: (u32, u32) = window_attrs
|
||||
.inner_size
|
||||
.map(|size| size.to_physical::<u32>(dpi_factor))
|
||||
.map(|size| size.to_physical::<u32>(scale_factor))
|
||||
.or_else(|| Some((800, 600).into()))
|
||||
.map(Into::into)
|
||||
.unwrap();
|
||||
@@ -324,10 +324,10 @@ impl UnownedWindow {
|
||||
{
|
||||
let mut min_inner_size = window_attrs
|
||||
.min_inner_size
|
||||
.map(|size| size.to_physical::<u32>(dpi_factor));
|
||||
.map(|size| size.to_physical::<u32>(scale_factor));
|
||||
let mut max_inner_size = window_attrs
|
||||
.max_inner_size
|
||||
.map(|size| size.to_physical::<u32>(dpi_factor));
|
||||
.map(|size| size.to_physical::<u32>(scale_factor));
|
||||
|
||||
if !window_attrs.resizable {
|
||||
if util::wm_name_is_one_of(&["Xfwm4"]) {
|
||||
@@ -351,12 +351,12 @@ impl UnownedWindow {
|
||||
normal_hints.set_resize_increments(
|
||||
pl_attribs
|
||||
.resize_increments
|
||||
.map(|size| size.to_physical::<u32>(dpi_factor).into()),
|
||||
.map(|size| size.to_physical::<u32>(scale_factor).into()),
|
||||
);
|
||||
normal_hints.set_base_size(
|
||||
pl_attribs
|
||||
.base_size
|
||||
.map(|size| size.to_physical::<u32>(dpi_factor).into()),
|
||||
.map(|size| size.to_physical::<u32>(scale_factor).into()),
|
||||
);
|
||||
xconn.set_normal_hints(window.xwindow, normal_hints).queue();
|
||||
}
|
||||
@@ -1053,8 +1053,8 @@ impl UnownedWindow {
|
||||
|
||||
#[inline]
|
||||
pub fn set_inner_size(&self, size: Size) {
|
||||
let dpi_factor = self.scale_factor();
|
||||
let (width, height) = size.to_physical::<u32>(dpi_factor).into();
|
||||
let scale_factor = self.scale_factor();
|
||||
let (width, height) = size.to_physical::<u32>(scale_factor).into();
|
||||
self.set_inner_size_physical(width, height);
|
||||
}
|
||||
|
||||
@@ -1097,16 +1097,16 @@ impl UnownedWindow {
|
||||
|
||||
pub(crate) fn adjust_for_dpi(
|
||||
&self,
|
||||
old_dpi_factor: f64,
|
||||
new_dpi_factor: f64,
|
||||
old_scale_factor: f64,
|
||||
new_scale_factor: f64,
|
||||
width: u32,
|
||||
height: u32,
|
||||
shared_state: &SharedState,
|
||||
) -> (u32, u32) {
|
||||
let scale_factor = new_dpi_factor / old_dpi_factor;
|
||||
let scale_factor = new_scale_factor / old_scale_factor;
|
||||
self.update_normal_hints(|normal_hints| {
|
||||
let dpi_adjuster =
|
||||
|size: Size| -> (u32, u32) { size.to_physical::<u32>(new_dpi_factor).into() };
|
||||
|size: Size| -> (u32, u32) { size.to_physical::<u32>(new_scale_factor).into() };
|
||||
let max_size = shared_state.max_inner_size.map(&dpi_adjuster);
|
||||
let min_size = shared_state.min_inner_size.map(&dpi_adjuster);
|
||||
let resize_increments = shared_state.resize_increments.map(&dpi_adjuster);
|
||||
@@ -1146,12 +1146,12 @@ impl UnownedWindow {
|
||||
|
||||
self.set_maximizable_inner(resizable).queue();
|
||||
|
||||
let dpi_factor = self.scale_factor();
|
||||
let scale_factor = self.scale_factor();
|
||||
let min_inner_size = min_size
|
||||
.map(|size| size.to_physical::<u32>(dpi_factor))
|
||||
.map(|size| size.to_physical::<u32>(scale_factor))
|
||||
.map(Into::into);
|
||||
let max_inner_size = max_size
|
||||
.map(|size| size.to_physical::<u32>(dpi_factor))
|
||||
.map(|size| size.to_physical::<u32>(scale_factor))
|
||||
.map(Into::into);
|
||||
self.update_normal_hints(|normal_hints| {
|
||||
normal_hints.set_min_size(min_inner_size);
|
||||
|
||||
@@ -25,6 +25,8 @@ use crate::{
|
||||
error::OsError as RootOsError, event::DeviceId as RootDeviceId, window::WindowAttributes,
|
||||
};
|
||||
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
|
||||
|
||||
@@ -50,13 +50,13 @@ impl Default for CursorState {
|
||||
}
|
||||
}
|
||||
|
||||
struct ViewState {
|
||||
pub(super) struct ViewState {
|
||||
ns_window: id,
|
||||
pub cursor_state: Arc<Mutex<CursorState>>,
|
||||
ime_spot: Option<(f64, f64)>,
|
||||
raw_characters: Option<String>,
|
||||
is_key_down: bool,
|
||||
modifiers: ModifiersState,
|
||||
pub(super) modifiers: ModifiersState,
|
||||
tracking_rect: Option<NSInteger>,
|
||||
}
|
||||
|
||||
@@ -383,8 +383,13 @@ extern "C" fn reset_cursor_rects(this: &Object, _sel: Sel) {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn has_marked_text(_this: &Object, _sel: Sel) -> BOOL {
|
||||
YES
|
||||
extern "C" fn has_marked_text(this: &Object, _sel: Sel) -> BOOL {
|
||||
unsafe {
|
||||
trace!("Triggered `hasMarkedText`");
|
||||
let marked_text: id = *this.get_ivar("markedText");
|
||||
trace!("Completed `hasMarkedText`");
|
||||
(marked_text.length() > 0) as i8
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn marked_range(this: &Object, _sel: Sel) -> NSRange {
|
||||
@@ -618,6 +623,19 @@ fn retrieve_keycode(event: id) -> Option<VirtualKeyCode> {
|
||||
})
|
||||
}
|
||||
|
||||
// Update `state.modifiers` if `event` has something different
|
||||
fn update_potentially_stale_modifiers(state: &mut ViewState, event: id) {
|
||||
let event_modifiers = event_mods(event);
|
||||
if state.modifiers != event_modifiers {
|
||||
state.modifiers = event_modifiers;
|
||||
|
||||
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
event: WindowEvent::ModifiersChanged(state.modifiers),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn key_down(this: &Object, _sel: Sel, event: id) {
|
||||
trace!("Triggered `keyDown`");
|
||||
unsafe {
|
||||
@@ -633,6 +651,8 @@ extern "C" fn key_down(this: &Object, _sel: Sel, event: id) {
|
||||
|
||||
let is_repeat = msg_send![event, isARepeat];
|
||||
|
||||
update_potentially_stale_modifiers(state, event);
|
||||
|
||||
#[allow(deprecated)]
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id,
|
||||
@@ -686,6 +706,8 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) {
|
||||
let scancode = get_scancode(event) as u32;
|
||||
let virtual_keycode = retrieve_keycode(event);
|
||||
|
||||
update_potentially_stale_modifiers(state, event);
|
||||
|
||||
#[allow(deprecated)]
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
@@ -750,16 +772,18 @@ extern "C" fn flags_changed(this: &Object, _sel: Sel, event: id) {
|
||||
events.push_back(window_event);
|
||||
}
|
||||
|
||||
let window_id = WindowId(get_window_id(state.ns_window));
|
||||
|
||||
for event in events {
|
||||
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
window_id,
|
||||
event,
|
||||
}));
|
||||
}
|
||||
|
||||
AppState::queue_event(EventWrapper::StaticEvent(Event::DeviceEvent {
|
||||
device_id: DEVICE_ID,
|
||||
event: DeviceEvent::ModifiersChanged(state.modifiers),
|
||||
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ModifiersChanged(state.modifiers),
|
||||
}));
|
||||
}
|
||||
trace!("Completed `flagsChanged`");
|
||||
@@ -801,6 +825,8 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
|
||||
|
||||
let event: id = msg_send![NSApp(), currentEvent];
|
||||
|
||||
update_potentially_stale_modifiers(state, event);
|
||||
|
||||
#[allow(deprecated)]
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
@@ -826,6 +852,8 @@ fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: Elem
|
||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
update_potentially_stale_modifiers(state, event);
|
||||
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
event: WindowEvent::MouseInput {
|
||||
@@ -889,6 +917,8 @@ fn mouse_motion(this: &Object, event: id) {
|
||||
let y = view_rect.size.height as f64 - view_point.y as f64;
|
||||
let logical_position = LogicalPosition::new(x, y);
|
||||
|
||||
update_potentially_stale_modifiers(state, event);
|
||||
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
event: WindowEvent::CursorMoved {
|
||||
@@ -918,7 +948,7 @@ extern "C" fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_motion(this, event);
|
||||
}
|
||||
|
||||
extern "C" fn mouse_entered(this: &Object, _sel: Sel, event: id) {
|
||||
extern "C" fn mouse_entered(this: &Object, _sel: Sel, _event: id) {
|
||||
trace!("Triggered `mouseEntered`");
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||
@@ -932,7 +962,6 @@ extern "C" fn mouse_entered(this: &Object, _sel: Sel, event: id) {
|
||||
};
|
||||
|
||||
AppState::queue_event(EventWrapper::StaticEvent(enter_event));
|
||||
mouse_motion(this, event);
|
||||
}
|
||||
trace!("Completed `mouseEntered`");
|
||||
}
|
||||
@@ -982,6 +1011,8 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
update_potentially_stale_modifiers(state, event);
|
||||
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
event: WindowEvent::MouseWheel {
|
||||
|
||||
@@ -340,7 +340,7 @@ impl UnownedWindow {
|
||||
|
||||
let input_context = unsafe { util::create_input_context(*ns_view) };
|
||||
|
||||
let dpi_factor = unsafe { NSWindow::backingScaleFactor(*ns_window) as f64 };
|
||||
let scale_factor = unsafe { NSWindow::backingScaleFactor(*ns_window) as f64 };
|
||||
|
||||
unsafe {
|
||||
if win_attribs.transparent {
|
||||
@@ -350,11 +350,11 @@ impl UnownedWindow {
|
||||
|
||||
ns_app.activateIgnoringOtherApps_(YES);
|
||||
win_attribs.min_inner_size.map(|dim| {
|
||||
let logical_dim = dim.to_logical(dpi_factor);
|
||||
let logical_dim = dim.to_logical(scale_factor);
|
||||
set_min_inner_size(*ns_window, logical_dim)
|
||||
});
|
||||
win_attribs.max_inner_size.map(|dim| {
|
||||
let logical_dim = dim.to_logical(dpi_factor);
|
||||
let logical_dim = dim.to_logical(scale_factor);
|
||||
set_max_inner_size(*ns_window, logical_dim)
|
||||
});
|
||||
|
||||
@@ -378,7 +378,7 @@ impl UnownedWindow {
|
||||
let decorations = win_attribs.decorations;
|
||||
let inner_rect = win_attribs
|
||||
.inner_size
|
||||
.map(|size| size.to_physical(dpi_factor));
|
||||
.map(|size| size.to_physical(scale_factor));
|
||||
|
||||
let window = Arc::new(UnownedWindow {
|
||||
ns_view,
|
||||
@@ -450,8 +450,8 @@ impl UnownedWindow {
|
||||
frame_rect.origin.x as f64,
|
||||
util::bottom_left_to_top_left(frame_rect),
|
||||
);
|
||||
let dpi_factor = self.scale_factor();
|
||||
Ok(position.to_physical(dpi_factor))
|
||||
let scale_factor = self.scale_factor();
|
||||
Ok(position.to_physical(scale_factor))
|
||||
}
|
||||
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
@@ -462,13 +462,13 @@ impl UnownedWindow {
|
||||
content_rect.origin.x as f64,
|
||||
util::bottom_left_to_top_left(content_rect),
|
||||
);
|
||||
let dpi_factor = self.scale_factor();
|
||||
Ok(position.to_physical(dpi_factor))
|
||||
let scale_factor = self.scale_factor();
|
||||
Ok(position.to_physical(scale_factor))
|
||||
}
|
||||
|
||||
pub fn set_outer_position(&self, position: Position) {
|
||||
let dpi_factor = self.scale_factor();
|
||||
let position = position.to_logical(dpi_factor);
|
||||
let scale_factor = self.scale_factor();
|
||||
let position = position.to_logical(scale_factor);
|
||||
let dummy = NSRect::new(
|
||||
NSPoint::new(
|
||||
position.x,
|
||||
@@ -488,8 +488,8 @@ impl UnownedWindow {
|
||||
let view_frame = unsafe { NSView::frame(*self.ns_view) };
|
||||
let logical: LogicalSize<f64> =
|
||||
(view_frame.size.width as f64, view_frame.size.height as f64).into();
|
||||
let dpi_factor = self.scale_factor();
|
||||
logical.to_physical(dpi_factor)
|
||||
let scale_factor = self.scale_factor();
|
||||
logical.to_physical(scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -497,15 +497,15 @@ impl UnownedWindow {
|
||||
let view_frame = unsafe { NSWindow::frame(*self.ns_window) };
|
||||
let logical: LogicalSize<f64> =
|
||||
(view_frame.size.width as f64, view_frame.size.height as f64).into();
|
||||
let dpi_factor = self.scale_factor();
|
||||
logical.to_physical(dpi_factor)
|
||||
let scale_factor = self.scale_factor();
|
||||
logical.to_physical(scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_inner_size(&self, size: Size) {
|
||||
unsafe {
|
||||
let dpi_factor = self.scale_factor();
|
||||
util::set_content_size_async(*self.ns_window, size.to_logical(dpi_factor));
|
||||
let scale_factor = self.scale_factor();
|
||||
util::set_content_size_async(*self.ns_window, size.to_logical(scale_factor));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,8 +515,8 @@ impl UnownedWindow {
|
||||
width: 0.0,
|
||||
height: 0.0,
|
||||
}));
|
||||
let dpi_factor = self.scale_factor();
|
||||
set_min_inner_size(*self.ns_window, dimensions.to_logical(dpi_factor));
|
||||
let scale_factor = self.scale_factor();
|
||||
set_min_inner_size(*self.ns_window, dimensions.to_logical(scale_factor));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,8 +526,8 @@ impl UnownedWindow {
|
||||
width: std::f32::MAX as f64,
|
||||
height: std::f32::MAX as f64,
|
||||
}));
|
||||
let dpi_factor = self.scale_factor();
|
||||
set_max_inner_size(*self.ns_window, dimensions.to_logical(dpi_factor));
|
||||
let scale_factor = self.scale_factor();
|
||||
set_max_inner_size(*self.ns_window, dimensions.to_logical(scale_factor));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,9 +594,9 @@ impl UnownedWindow {
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, cursor_position: Position) -> Result<(), ExternalError> {
|
||||
let physical_window_position = self.inner_position().unwrap();
|
||||
let dpi_factor = self.scale_factor();
|
||||
let window_position = physical_window_position.to_logical::<CGFloat>(dpi_factor);
|
||||
let logical_cursor_position = cursor_position.to_logical::<CGFloat>(dpi_factor);
|
||||
let scale_factor = self.scale_factor();
|
||||
let window_position = physical_window_position.to_logical::<CGFloat>(scale_factor);
|
||||
let logical_cursor_position = cursor_position.to_logical::<CGFloat>(scale_factor);
|
||||
let point = appkit::CGPoint {
|
||||
x: logical_cursor_position.x + window_position.x,
|
||||
y: logical_cursor_position.y + window_position.y,
|
||||
@@ -933,8 +933,8 @@ impl UnownedWindow {
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_position(&self, spot: Position) {
|
||||
let dpi_factor = self.scale_factor();
|
||||
let logical_spot = spot.to_logical(dpi_factor);
|
||||
let scale_factor = self.scale_factor();
|
||||
let logical_spot = spot.to_logical(scale_factor);
|
||||
unsafe {
|
||||
view::set_ime_position(
|
||||
*self.ns_view,
|
||||
@@ -1028,7 +1028,11 @@ impl WindowExtMacOS for UnownedWindow {
|
||||
|
||||
if fullscreen {
|
||||
// Remember the original window's settings
|
||||
shared_state_lock.standard_frame = Some(NSWindow::frame(*self.ns_window));
|
||||
// Exclude title bar
|
||||
shared_state_lock.standard_frame = Some(NSWindow::contentRectForFrameRect_(
|
||||
*self.ns_window,
|
||||
NSWindow::frame(*self.ns_window),
|
||||
));
|
||||
shared_state_lock.saved_style = Some(self.ns_window.styleMask());
|
||||
shared_state_lock.save_presentation_opts = Some(app.presentationOptions_());
|
||||
|
||||
|
||||
@@ -16,11 +16,12 @@ use objc::{
|
||||
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
event::{Event, WindowEvent},
|
||||
event::{Event, ModifiersState, WindowEvent},
|
||||
platform_impl::platform::{
|
||||
app_state::AppState,
|
||||
event::{EventProxy, EventWrapper},
|
||||
util::{self, IdRef},
|
||||
view::ViewState,
|
||||
window::{get_window_id, UnownedWindow},
|
||||
},
|
||||
window::{Fullscreen, WindowId},
|
||||
@@ -43,7 +44,7 @@ pub struct WindowDelegateState {
|
||||
previous_position: Option<(f64, f64)>,
|
||||
|
||||
// Used to prevent redundant events.
|
||||
previous_dpi_factor: f64,
|
||||
previous_scale_factor: f64,
|
||||
}
|
||||
|
||||
impl WindowDelegateState {
|
||||
@@ -55,7 +56,7 @@ impl WindowDelegateState {
|
||||
window: Arc::downgrade(&window),
|
||||
initial_fullscreen,
|
||||
previous_position: None,
|
||||
previous_dpi_factor: scale_factor,
|
||||
previous_scale_factor: scale_factor,
|
||||
};
|
||||
|
||||
if scale_factor != 1.0 {
|
||||
@@ -82,11 +83,11 @@ impl WindowDelegateState {
|
||||
|
||||
pub fn emit_static_scale_factor_changed_event(&mut self) {
|
||||
let scale_factor = self.get_scale_factor();
|
||||
if scale_factor == self.previous_dpi_factor {
|
||||
if scale_factor == self.previous_scale_factor {
|
||||
return ();
|
||||
};
|
||||
|
||||
self.previous_dpi_factor = scale_factor;
|
||||
self.previous_scale_factor = scale_factor;
|
||||
let wrapper = EventWrapper::EventProxy(EventProxy::DpiChangedProxy {
|
||||
ns_window: IdRef::retain(*self.ns_window),
|
||||
suggested_size: self.view_size(),
|
||||
@@ -319,6 +320,29 @@ extern "C" fn window_did_become_key(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn window_did_resign_key(this: &Object, _: Sel, _: id) {
|
||||
trace!("Triggered `windowDidResignKey:`");
|
||||
with_state(this, |state| {
|
||||
// It happens rather often, e.g. when the user is Cmd+Tabbing, that the
|
||||
// NSWindowDelegate will receive a didResignKey event despite no event
|
||||
// being received when the modifiers are released. This is because
|
||||
// flagsChanged events are received by the NSView instead of the
|
||||
// NSWindowDelegate, and as a result a tracked modifiers state can quite
|
||||
// easily fall out of synchrony with reality. This requires us to emit
|
||||
// a synthetic ModifiersChanged event when we lose focus.
|
||||
//
|
||||
// Here we (very unsafely) acquire the winitState (a ViewState) from the
|
||||
// Object referenced by state.ns_view (an IdRef, which is dereferenced
|
||||
// to an id)
|
||||
let view_state: &mut ViewState = unsafe {
|
||||
let ns_view: &Object = (*state.ns_view).as_ref().expect("failed to deref");
|
||||
let state_ptr: *mut c_void = *ns_view.get_ivar("winitState");
|
||||
&mut *(state_ptr as *mut ViewState)
|
||||
};
|
||||
|
||||
// Both update the state and emit a ModifiersChanged event.
|
||||
if !view_state.modifiers.is_empty() {
|
||||
view_state.modifiers = ModifiersState::empty();
|
||||
state.emit_event(WindowEvent::ModifiersChanged(view_state.modifiers));
|
||||
}
|
||||
|
||||
state.emit_event(WindowEvent::Focused(false));
|
||||
});
|
||||
trace!("Completed `windowDidResignKey:`");
|
||||
|
||||
@@ -202,7 +202,6 @@ impl<T: 'static> Shared<T> {
|
||||
// It should only ever be called from send_event
|
||||
fn handle_event(&self, event: Event<'static, T>, control: &mut root::ControlFlow) {
|
||||
let is_closed = self.is_closed();
|
||||
|
||||
match *self.0.runner.borrow_mut() {
|
||||
Some(ref mut runner) => {
|
||||
// An event is being processed, so the runner should be marked busy
|
||||
@@ -227,7 +226,9 @@ impl<T: 'static> Shared<T> {
|
||||
// If the runner doesn't exist and this method recurses, it will recurse infinitely
|
||||
if !is_closed && self.0.runner.borrow().is_some() {
|
||||
// Take an event out of the queue and handle it
|
||||
if let Some(event) = self.0.events.borrow_mut().pop_front() {
|
||||
// Make sure not to let the borrow_mut live during the next handle_event
|
||||
let event = { self.0.events.borrow_mut().pop_front() };
|
||||
if let Some(event) = event {
|
||||
self.handle_event(event, control);
|
||||
}
|
||||
}
|
||||
@@ -240,7 +241,7 @@ impl<T: 'static> Shared<T> {
|
||||
root::ControlFlow::Poll => {
|
||||
let cloned = self.clone();
|
||||
State::Poll {
|
||||
timeout: backend::Timeout::new(move || cloned.poll(), Duration::from_millis(0)),
|
||||
request: backend::AnimationFrameRequest::new(move || cloned.poll()),
|
||||
}
|
||||
}
|
||||
root::ControlFlow::Wait => State::Wait {
|
||||
|
||||
@@ -15,7 +15,7 @@ pub enum State {
|
||||
start: Instant,
|
||||
},
|
||||
Poll {
|
||||
timeout: backend::Timeout,
|
||||
request: backend::AnimationFrameRequest,
|
||||
},
|
||||
Exit,
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::{backend, device, proxy::Proxy, runner, window};
|
||||
use crate::dpi::{PhysicalSize, Size};
|
||||
use crate::event::{DeviceId, ElementState, Event, KeyboardInput, TouchPhase, WindowEvent};
|
||||
use crate::event_loop::ControlFlow;
|
||||
use crate::window::WindowId;
|
||||
use crate::window::{Theme, WindowId};
|
||||
use std::clone::Clone;
|
||||
|
||||
pub struct WindowTarget<T: 'static> {
|
||||
@@ -199,5 +199,18 @@ impl<T> WindowTarget<T> {
|
||||
});
|
||||
runner.request_redraw(WindowId(id));
|
||||
});
|
||||
|
||||
let runner = self.runner.clone();
|
||||
canvas.on_dark_mode(move |is_dark_mode| {
|
||||
let theme = if is_dark_mode {
|
||||
Theme::Dark
|
||||
} else {
|
||||
Theme::Light
|
||||
};
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: WindowId(id),
|
||||
event: WindowEvent::ThemeChanged(theme),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,3 +44,5 @@ pub use self::window::{
|
||||
Id as WindowId, PlatformSpecificBuilderAttributes as PlatformSpecificWindowBuilderAttributes,
|
||||
Window,
|
||||
};
|
||||
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::platform_impl::{OsError, PlatformSpecificWindowBuilderAttributes};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use stdweb::js;
|
||||
use stdweb::traits::IPointerEvent;
|
||||
use stdweb::unstable::TryInto;
|
||||
use stdweb::web::event::{
|
||||
@@ -240,6 +241,22 @@ impl Canvas {
|
||||
self.on_fullscreen_change = Some(self.add_event(move |_: FullscreenChangeEvent| handler()));
|
||||
}
|
||||
|
||||
pub fn on_dark_mode<F>(&mut self, handler: F)
|
||||
where
|
||||
F: 'static + FnMut(bool),
|
||||
{
|
||||
// TODO: upstream to stdweb
|
||||
js! {
|
||||
var handler = @{handler};
|
||||
|
||||
if (window.matchMedia) {
|
||||
window.matchMedia("(prefers-color-scheme: dark)").addListener(function(e) {
|
||||
handler(event.matches)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_event<E, F>(&self, mut handler: F) -> EventListenerHandle
|
||||
where
|
||||
E: ConcreteEvent,
|
||||
|
||||
@@ -3,13 +3,14 @@ mod event;
|
||||
mod timeout;
|
||||
|
||||
pub use self::canvas::Canvas;
|
||||
pub use self::timeout::Timeout;
|
||||
pub use self::timeout::{AnimationFrameRequest, Timeout};
|
||||
|
||||
use crate::dpi::{LogicalSize, Size};
|
||||
use crate::platform::web::WindowExtStdweb;
|
||||
use crate::window::Window;
|
||||
|
||||
use stdweb::js;
|
||||
use stdweb::unstable::TryInto;
|
||||
use stdweb::web::event::BeforeUnloadEvent;
|
||||
use stdweb::web::window;
|
||||
use stdweb::web::IEventTarget;
|
||||
@@ -31,6 +32,15 @@ impl WindowExtStdweb for Window {
|
||||
fn canvas(&self) -> CanvasElement {
|
||||
self.window.canvas().raw().clone()
|
||||
}
|
||||
|
||||
fn is_dark_mode(&self) -> bool {
|
||||
// TODO: upstream to stdweb
|
||||
let is_dark_mode = js! {
|
||||
return (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches)
|
||||
};
|
||||
|
||||
is_dark_mode.try_into().expect("should return a bool")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn window_size() -> LogicalSize<f64> {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
use stdweb::web::{window, IWindowOrWorker, TimeoutHandle};
|
||||
use stdweb::web::{window, IWindowOrWorker, RequestAnimationFrameHandle, TimeoutHandle};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Timeout {
|
||||
@@ -23,3 +25,39 @@ impl Drop for Timeout {
|
||||
handle.clear();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AnimationFrameRequest {
|
||||
handle: Option<RequestAnimationFrameHandle>,
|
||||
// track callback state, because `cancelAnimationFrame` is slow
|
||||
fired: Rc<Cell<bool>>,
|
||||
}
|
||||
|
||||
impl AnimationFrameRequest {
|
||||
pub fn new<F>(mut f: F) -> AnimationFrameRequest
|
||||
where
|
||||
F: 'static + FnMut(),
|
||||
{
|
||||
let fired = Rc::new(Cell::new(false));
|
||||
let c_fired = fired.clone();
|
||||
let handle = window().request_animation_frame(move |_| {
|
||||
(*c_fired).set(true);
|
||||
f();
|
||||
});
|
||||
|
||||
AnimationFrameRequest {
|
||||
handle: Some(handle),
|
||||
fired,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AnimationFrameRequest {
|
||||
fn drop(&mut self) {
|
||||
if !(*self.fired).get() {
|
||||
if let Some(handle) = self.handle.take() {
|
||||
handle.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@ use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use wasm_bindgen::{closure::Closure, JsCast};
|
||||
use web_sys::{Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, PointerEvent, WheelEvent};
|
||||
use web_sys::{
|
||||
Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, MediaQueryListEvent, PointerEvent,
|
||||
WheelEvent,
|
||||
};
|
||||
|
||||
pub struct Canvas {
|
||||
/// Note: resizing the HTMLCanvasElement should go through `backend::set_canvas_size` to ensure the DPI factor is maintained.
|
||||
@@ -26,6 +29,7 @@ pub struct Canvas {
|
||||
on_mouse_wheel: Option<Closure<dyn FnMut(WheelEvent)>>,
|
||||
on_fullscreen_change: Option<Closure<dyn FnMut(Event)>>,
|
||||
wants_fullscreen: Rc<RefCell<bool>>,
|
||||
on_dark_mode: Option<Closure<dyn FnMut(MediaQueryListEvent)>>,
|
||||
}
|
||||
|
||||
impl Drop for Canvas {
|
||||
@@ -77,6 +81,7 @@ impl Canvas {
|
||||
on_mouse_wheel: None,
|
||||
on_fullscreen_change: None,
|
||||
wants_fullscreen: Rc::new(RefCell::new(false)),
|
||||
on_dark_mode: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -251,6 +256,28 @@ impl Canvas {
|
||||
Some(self.add_event("fullscreenchange", move |_: Event| handler()));
|
||||
}
|
||||
|
||||
pub fn on_dark_mode<F>(&mut self, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(bool),
|
||||
{
|
||||
let window = web_sys::window().expect("Failed to obtain window");
|
||||
|
||||
self.on_dark_mode = window
|
||||
.match_media("(prefers-color-scheme: dark)")
|
||||
.ok()
|
||||
.flatten()
|
||||
.and_then(|media| {
|
||||
let closure = Closure::wrap(Box::new(move |event: MediaQueryListEvent| {
|
||||
handler(event.matches())
|
||||
}) as Box<dyn FnMut(_)>);
|
||||
|
||||
media
|
||||
.add_listener_with_opt_callback(Some(&closure.as_ref().unchecked_ref()))
|
||||
.map(|_| closure)
|
||||
.ok()
|
||||
});
|
||||
}
|
||||
|
||||
fn add_event<E, F>(&self, event_name: &str, mut handler: F) -> Closure<dyn FnMut(E)>
|
||||
where
|
||||
E: 'static + AsRef<web_sys::Event> + wasm_bindgen::convert::FromWasmAbi,
|
||||
|
||||
@@ -3,7 +3,7 @@ mod event;
|
||||
mod timeout;
|
||||
|
||||
pub use self::canvas::Canvas;
|
||||
pub use self::timeout::Timeout;
|
||||
pub use self::timeout::{AnimationFrameRequest, Timeout};
|
||||
|
||||
use crate::dpi::{LogicalSize, Size};
|
||||
use crate::platform::web::WindowExtWebSys;
|
||||
@@ -38,6 +38,17 @@ impl WindowExtWebSys for Window {
|
||||
fn canvas(&self) -> HtmlCanvasElement {
|
||||
self.window.canvas().raw().clone()
|
||||
}
|
||||
|
||||
fn is_dark_mode(&self) -> bool {
|
||||
let window = web_sys::window().expect("Failed to obtain window");
|
||||
|
||||
window
|
||||
.match_media("(prefers-color-scheme: dark)")
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|media| media.matches())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn window_size() -> LogicalSize<f64> {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
use wasm_bindgen::closure::Closure;
|
||||
use wasm_bindgen::JsCast;
|
||||
@@ -38,3 +40,48 @@ impl Drop for Timeout {
|
||||
window.clear_timeout_with_handle(self.handle);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AnimationFrameRequest {
|
||||
handle: i32,
|
||||
// track callback state, because `cancelAnimationFrame` is slow
|
||||
fired: Rc<Cell<bool>>,
|
||||
_closure: Closure<dyn FnMut()>,
|
||||
}
|
||||
|
||||
impl AnimationFrameRequest {
|
||||
pub fn new<F>(mut f: F) -> AnimationFrameRequest
|
||||
where
|
||||
F: 'static + FnMut(),
|
||||
{
|
||||
let window = web_sys::window().expect("Failed to obtain window");
|
||||
|
||||
let fired = Rc::new(Cell::new(false));
|
||||
let c_fired = fired.clone();
|
||||
let closure = Closure::wrap(Box::new(move || {
|
||||
(*c_fired).set(true);
|
||||
f();
|
||||
}) as Box<dyn FnMut()>);
|
||||
|
||||
let handle = window
|
||||
.request_animation_frame(&closure.as_ref().unchecked_ref())
|
||||
.expect("Failed to request animation frame");
|
||||
|
||||
AnimationFrameRequest {
|
||||
handle,
|
||||
fired,
|
||||
_closure: closure,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AnimationFrameRequest {
|
||||
fn drop(&mut self) {
|
||||
if !(*self.fired).get() {
|
||||
let window = web_sys::window().expect("Failed to obtain window");
|
||||
window
|
||||
.cancel_animation_frame(self.handle)
|
||||
.expect("Failed to cancel animation frame");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,10 @@ use std::os::windows::ffi::OsStrExt;
|
||||
use winapi::{
|
||||
shared::{
|
||||
basetsd::SIZE_T,
|
||||
minwindef::{BOOL, DWORD, UINT, ULONG, WORD},
|
||||
minwindef::{BOOL, DWORD, FALSE, UINT, ULONG, WORD},
|
||||
ntdef::{LPSTR, NTSTATUS, NT_SUCCESS, PVOID, WCHAR},
|
||||
windef::HWND,
|
||||
winerror::S_OK,
|
||||
},
|
||||
um::{libloaderapi, uxtheme, winuser},
|
||||
};
|
||||
@@ -44,9 +45,8 @@ lazy_static! {
|
||||
};
|
||||
|
||||
let status = (rtl_get_version)(&mut vi as _);
|
||||
assert!(NT_SUCCESS(status));
|
||||
|
||||
if vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0 {
|
||||
if NT_SUCCESS(status) && vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0 {
|
||||
Some(vi.dwBuildNumber)
|
||||
} else {
|
||||
None
|
||||
@@ -82,22 +82,15 @@ pub fn try_dark_mode(hwnd: HWND) -> bool {
|
||||
LIGHT_THEME_NAME.as_ptr()
|
||||
};
|
||||
|
||||
unsafe {
|
||||
assert_eq!(
|
||||
0,
|
||||
uxtheme::SetWindowTheme(hwnd, theme_name as _, std::ptr::null())
|
||||
);
|
||||
let status = unsafe { uxtheme::SetWindowTheme(hwnd, theme_name as _, std::ptr::null()) };
|
||||
|
||||
set_dark_mode_for_window(hwnd, is_dark_mode)
|
||||
}
|
||||
|
||||
is_dark_mode
|
||||
status == S_OK && set_dark_mode_for_window(hwnd, is_dark_mode)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) {
|
||||
fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) -> bool {
|
||||
// Uses Windows undocumented API SetWindowCompositionAttribute,
|
||||
// as seen in win32-darkmode example linked at top of file.
|
||||
|
||||
@@ -132,11 +125,12 @@ fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) {
|
||||
cbData: std::mem::size_of_val(&is_dark_mode_bigbool) as _,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
1,
|
||||
set_window_composition_attribute(hwnd, &mut data as *mut _)
|
||||
);
|
||||
let status = set_window_composition_attribute(hwnd, &mut data as *mut _);
|
||||
|
||||
status != FALSE
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +199,7 @@ fn is_high_contrast() -> bool {
|
||||
)
|
||||
};
|
||||
|
||||
(ok > 0) && ((HCF_HIGHCONTRASTON & hc.dwFlags) == 1)
|
||||
ok != FALSE && (HCF_HIGHCONTRASTON & hc.dwFlags) == 1
|
||||
}
|
||||
|
||||
fn widestring(src: &'static str) -> Vec<u16> {
|
||||
|
||||
@@ -50,9 +50,7 @@ use crate::{
|
||||
dark_mode::try_dark_mode,
|
||||
dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling},
|
||||
drop_handler::FileDropHandler,
|
||||
event::{
|
||||
self, handle_extended_keys, process_key_params, vkey_to_winit_vkey, ModifiersStateSide,
|
||||
},
|
||||
event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey},
|
||||
monitor, raw_input, util,
|
||||
window_state::{CursorFlags, WindowFlags, WindowState},
|
||||
wrap_device_id, WindowId, DEVICE_ID,
|
||||
@@ -108,7 +106,6 @@ impl<T> SubclassInput<T> {
|
||||
struct ThreadMsgTargetSubclassInput<T: 'static> {
|
||||
event_loop_runner: EventLoopRunnerShared<T>,
|
||||
user_event_receiver: Receiver<T>,
|
||||
modifiers_state: ModifiersStateSide,
|
||||
}
|
||||
|
||||
impl<T> ThreadMsgTargetSubclassInput<T> {
|
||||
@@ -221,69 +218,68 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
|
||||
runner.new_events();
|
||||
loop {
|
||||
if !unread_message_exists {
|
||||
if 0 == winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if msg.message == winuser::WM_PAINT {
|
||||
unread_message_exists = true;
|
||||
break;
|
||||
}
|
||||
winuser::TranslateMessage(&mut msg);
|
||||
winuser::DispatchMessageW(&mut msg);
|
||||
|
||||
unread_message_exists = false;
|
||||
}
|
||||
runner.main_events_cleared();
|
||||
loop {
|
||||
if !unread_message_exists {
|
||||
if 0 == winuser::PeekMessageW(
|
||||
&mut msg,
|
||||
ptr::null_mut(),
|
||||
winuser::WM_PAINT,
|
||||
winuser::WM_PAINT,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
winuser::PM_REMOVE,
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
winuser::TranslateMessage(&mut msg);
|
||||
winuser::DispatchMessageW(&mut msg);
|
||||
|
||||
unread_message_exists = false;
|
||||
}
|
||||
if runner.redraw_events_cleared().events_buffered() {
|
||||
if runner.control_flow() == ControlFlow::Exit {
|
||||
break 'main;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if !unread_message_exists {
|
||||
let control_flow = runner.control_flow();
|
||||
match control_flow {
|
||||
ControlFlow::Exit => break 'main,
|
||||
ControlFlow::Wait => {
|
||||
if 0 == winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) {
|
||||
break 'main;
|
||||
}
|
||||
unread_message_exists = true;
|
||||
}
|
||||
ControlFlow::WaitUntil(resume_time) => {
|
||||
wait_until_time_or_msg(resume_time);
|
||||
}
|
||||
ControlFlow::Poll => (),
|
||||
if msg.message == winuser::WM_PAINT {
|
||||
// An "external" redraw was requested.
|
||||
// Note that the WM_PAINT has been dispatched and
|
||||
// has caused the event loop to emit the MainEventsCleared event.
|
||||
// See EventLoopRunner::process_event().
|
||||
// The call to main_events_cleared() below will do nothing.
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Make sure we emit the MainEventsCleared event if no WM_PAINT message was received.
|
||||
runner.main_events_cleared();
|
||||
// Drain eventual WM_PAINT messages sent if user called request_redraw()
|
||||
// during handling of MainEventsCleared.
|
||||
loop {
|
||||
if 0 == winuser::PeekMessageW(
|
||||
&mut msg,
|
||||
ptr::null_mut(),
|
||||
winuser::WM_PAINT,
|
||||
winuser::WM_PAINT,
|
||||
winuser::PM_QS_PAINT | winuser::PM_REMOVE,
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
||||
winuser::TranslateMessage(&mut msg);
|
||||
winuser::DispatchMessageW(&mut msg);
|
||||
}
|
||||
runner.redraw_events_cleared();
|
||||
match runner.control_flow() {
|
||||
ControlFlow::Exit => break 'main,
|
||||
ControlFlow::Wait => {
|
||||
if 0 == winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) {
|
||||
break 'main;
|
||||
}
|
||||
unread_message_exists = true;
|
||||
}
|
||||
ControlFlow::WaitUntil(resume_time) => {
|
||||
wait_until_time_or_msg(resume_time);
|
||||
}
|
||||
ControlFlow::Poll => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
runner.call_event_handler(Event::LoopDestroyed);
|
||||
}
|
||||
runner.destroy_loop();
|
||||
runner.destroy_runner();
|
||||
}
|
||||
|
||||
@@ -320,11 +316,9 @@ fn main_thread_id() -> DWORD {
|
||||
unsafe { MAIN_THREAD_ID }
|
||||
}
|
||||
|
||||
// Returns true if the wait time was reached, and false if a message must be processed.
|
||||
unsafe fn wait_until_time_or_msg(wait_until: Instant) -> bool {
|
||||
let mut msg = mem::zeroed();
|
||||
unsafe fn wait_until_time_or_msg(wait_until: Instant) {
|
||||
let now = Instant::now();
|
||||
if now <= wait_until {
|
||||
if now < wait_until {
|
||||
// MsgWaitForMultipleObjects tends to overshoot just a little bit. We subtract 1 millisecond
|
||||
// from the requested time and spinlock for the remainder to compensate for that.
|
||||
let resume_reason = winuser::MsgWaitForMultipleObjectsEx(
|
||||
@@ -336,16 +330,16 @@ unsafe fn wait_until_time_or_msg(wait_until: Instant) -> bool {
|
||||
);
|
||||
|
||||
if resume_reason == winerror::WAIT_TIMEOUT {
|
||||
let mut msg = mem::zeroed();
|
||||
while Instant::now() < wait_until {
|
||||
if 0 != winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0) {
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Implementation taken from https://github.com/rust-lang/rust/blob/db5476571d9b27c862b95c1e64764b0ac8980e23/src/libstd/sys/windows/mod.rs
|
||||
fn dur2timeout(dur: Duration) -> DWORD {
|
||||
// Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the
|
||||
@@ -555,7 +549,6 @@ fn thread_event_target_window<T>(event_loop_runner: EventLoopRunnerShared<T>) ->
|
||||
let subclass_input = ThreadMsgTargetSubclassInput {
|
||||
event_loop_runner,
|
||||
user_event_receiver: rx,
|
||||
modifiers_state: ModifiersStateSide::default(),
|
||||
};
|
||||
let input_ptr = Box::into_raw(Box::new(subclass_input));
|
||||
let subclass_result = commctrl::SetWindowSubclass(
|
||||
@@ -608,6 +601,27 @@ fn normalize_pointer_pressure(pressure: u32) -> Option<Force> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a `ModifiersChanged` event whenever modifiers have changed.
|
||||
fn update_modifiers<T>(window: HWND, subclass_input: &SubclassInput<T>) {
|
||||
use crate::event::WindowEvent::ModifiersChanged;
|
||||
|
||||
let modifiers = event::get_key_mods();
|
||||
let mut window_state = subclass_input.window_state.lock();
|
||||
if window_state.modifiers_state != modifiers {
|
||||
window_state.modifiers_state = modifiers;
|
||||
|
||||
// Drop lock
|
||||
drop(window_state);
|
||||
|
||||
unsafe {
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: ModifiersChanged(modifiers),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Any window whose callback is configured to this function will have its events propagated
|
||||
/// through the events loop of the thread the window was created in.
|
||||
//
|
||||
@@ -640,13 +654,6 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
}
|
||||
|
||||
winuser::WM_NCLBUTTONDOWN => {
|
||||
// jumpstart the modal loop
|
||||
winuser::RedrawWindow(
|
||||
window,
|
||||
ptr::null(),
|
||||
ptr::null_mut(),
|
||||
winuser::RDW_INTERNALPAINT,
|
||||
);
|
||||
if wparam == winuser::HTCAPTION as _ {
|
||||
winuser::PostMessageW(window, winuser::WM_MOUSEMOVE, 0, 0);
|
||||
}
|
||||
@@ -676,7 +683,6 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
}
|
||||
|
||||
winuser::WM_PAINT => {
|
||||
subclass_input.event_loop_runner.main_events_cleared();
|
||||
subclass_input.send_event(Event::RedrawRequested(RootWindowId(WindowId(window))));
|
||||
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
||||
}
|
||||
@@ -733,7 +739,7 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
let windowpos = lparam as *const winuser::WINDOWPOS;
|
||||
if (*windowpos).flags & winuser::SWP_NOMOVE != winuser::SWP_NOMOVE {
|
||||
let physical_position =
|
||||
PhysicalPosition::new((*windowpos).x as u32, (*windowpos).y as u32);
|
||||
PhysicalPosition::new((*windowpos).x as i32, (*windowpos).y as i32);
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: Moved(physical_position),
|
||||
@@ -858,15 +864,27 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
let x = windowsx::GET_X_LPARAM(lparam) as f64;
|
||||
let y = windowsx::GET_Y_LPARAM(lparam) as f64;
|
||||
let position = PhysicalPosition::new(x, y);
|
||||
let cursor_moved;
|
||||
{
|
||||
// handle spurious WM_MOUSEMOVE messages
|
||||
// see https://devblogs.microsoft.com/oldnewthing/20031001-00/?p=42343
|
||||
// and http://debugandconquer.blogspot.com/2015/08/the-cause-of-spurious-mouse-move.html
|
||||
let mut w = subclass_input.window_state.lock();
|
||||
cursor_moved = w.mouse.last_position != Some(position);
|
||||
w.mouse.last_position = Some(position);
|
||||
}
|
||||
if cursor_moved {
|
||||
update_modifiers(window, subclass_input);
|
||||
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: CursorMoved {
|
||||
device_id: DEVICE_ID,
|
||||
position,
|
||||
modifiers: event::get_key_mods(),
|
||||
},
|
||||
});
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: CursorMoved {
|
||||
device_id: DEVICE_ID,
|
||||
position,
|
||||
modifiers: event::get_key_mods(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
@@ -897,6 +915,8 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
let value = value as i32;
|
||||
let value = value as f32 / winuser::WHEEL_DELTA as f32;
|
||||
|
||||
update_modifiers(window, subclass_input);
|
||||
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: WindowEvent::MouseWheel {
|
||||
@@ -917,6 +937,8 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
let value = value as i32;
|
||||
let value = value as f32 / winuser::WHEEL_DELTA as f32;
|
||||
|
||||
update_modifiers(window, subclass_input);
|
||||
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: WindowEvent::MouseWheel {
|
||||
@@ -936,6 +958,8 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
||||
} else {
|
||||
if let Some((scancode, vkey)) = process_key_params(wparam, lparam) {
|
||||
update_modifiers(window, subclass_input);
|
||||
|
||||
#[allow(deprecated)]
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
@@ -966,6 +990,8 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
winuser::WM_KEYUP | winuser::WM_SYSKEYUP => {
|
||||
use crate::event::ElementState::Released;
|
||||
if let Some((scancode, vkey)) = process_key_params(wparam, lparam) {
|
||||
update_modifiers(window, subclass_input);
|
||||
|
||||
#[allow(deprecated)]
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
@@ -989,6 +1015,8 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
|
||||
capture_mouse(window, &mut *subclass_input.window_state.lock());
|
||||
|
||||
update_modifiers(window, subclass_input);
|
||||
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: MouseInput {
|
||||
@@ -1008,6 +1036,8 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
|
||||
release_mouse(&mut *subclass_input.window_state.lock());
|
||||
|
||||
update_modifiers(window, subclass_input);
|
||||
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: MouseInput {
|
||||
@@ -1027,6 +1057,8 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
|
||||
capture_mouse(window, &mut *subclass_input.window_state.lock());
|
||||
|
||||
update_modifiers(window, subclass_input);
|
||||
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: MouseInput {
|
||||
@@ -1046,6 +1078,8 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
|
||||
release_mouse(&mut *subclass_input.window_state.lock());
|
||||
|
||||
update_modifiers(window, subclass_input);
|
||||
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: MouseInput {
|
||||
@@ -1065,6 +1099,8 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
|
||||
capture_mouse(window, &mut *subclass_input.window_state.lock());
|
||||
|
||||
update_modifiers(window, subclass_input);
|
||||
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: MouseInput {
|
||||
@@ -1084,6 +1120,8 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
|
||||
release_mouse(&mut *subclass_input.window_state.lock());
|
||||
|
||||
update_modifiers(window, subclass_input);
|
||||
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: MouseInput {
|
||||
@@ -1104,6 +1142,8 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
|
||||
capture_mouse(window, &mut *subclass_input.window_state.lock());
|
||||
|
||||
update_modifiers(window, subclass_input);
|
||||
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: MouseInput {
|
||||
@@ -1124,6 +1164,8 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
|
||||
release_mouse(&mut *subclass_input.window_state.lock());
|
||||
|
||||
update_modifiers(window, subclass_input);
|
||||
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: MouseInput {
|
||||
@@ -1332,6 +1374,8 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
winuser::MapVirtualKeyA(windows_keycode as _, winuser::MAPVK_VK_TO_VSC);
|
||||
let virtual_keycode = event::vkey_to_winit_vkey(windows_keycode);
|
||||
|
||||
update_modifiers(window, subclass_input);
|
||||
|
||||
#[allow(deprecated)]
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
@@ -1357,7 +1401,11 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
}
|
||||
|
||||
winuser::WM_KILLFOCUS => {
|
||||
use crate::event::{ElementState::Released, WindowEvent::Focused};
|
||||
use crate::event::{
|
||||
ElementState::Released,
|
||||
ModifiersState,
|
||||
WindowEvent::{Focused, ModifiersChanged},
|
||||
};
|
||||
for windows_keycode in event::get_pressed_keys() {
|
||||
let scancode =
|
||||
winuser::MapVirtualKeyA(windows_keycode as _, winuser::MAPVK_VK_TO_VSC);
|
||||
@@ -1379,6 +1427,12 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
})
|
||||
}
|
||||
|
||||
subclass_input.window_state.lock().modifiers_state = ModifiersState::empty();
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: ModifiersChanged(ModifiersState::empty()),
|
||||
});
|
||||
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: Focused(false),
|
||||
@@ -1422,7 +1476,7 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
|
||||
if window_state.min_size.is_some() || window_state.max_size.is_some() {
|
||||
if let Some(min_size) = window_state.min_size {
|
||||
let min_size = min_size.to_physical(window_state.dpi_factor);
|
||||
let min_size = min_size.to_physical(window_state.scale_factor);
|
||||
let (width, height): (u32, u32) = util::adjust_size(window, min_size).into();
|
||||
(*mmi).ptMinTrackSize = POINT {
|
||||
x: width as i32,
|
||||
@@ -1430,7 +1484,7 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
};
|
||||
}
|
||||
if let Some(max_size) = window_state.max_size {
|
||||
let max_size = max_size.to_physical(window_state.dpi_factor);
|
||||
let max_size = max_size.to_physical(window_state.scale_factor);
|
||||
let (width, height): (u32, u32) = util::adjust_size(window, max_size).into();
|
||||
(*mmi).ptMaxTrackSize = POINT {
|
||||
x: width as i32,
|
||||
@@ -1452,15 +1506,15 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
// application since they are the same".
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/dn312083(v=vs.85).aspx
|
||||
let new_dpi_x = u32::from(LOWORD(wparam as DWORD));
|
||||
let new_dpi_factor = dpi_to_scale_factor(new_dpi_x);
|
||||
let old_dpi_factor: f64;
|
||||
let new_scale_factor = dpi_to_scale_factor(new_dpi_x);
|
||||
let old_scale_factor: f64;
|
||||
|
||||
let allow_resize = {
|
||||
let mut window_state = subclass_input.window_state.lock();
|
||||
old_dpi_factor = window_state.dpi_factor;
|
||||
window_state.dpi_factor = new_dpi_factor;
|
||||
old_scale_factor = window_state.scale_factor;
|
||||
window_state.scale_factor = new_scale_factor;
|
||||
|
||||
if new_dpi_factor == old_dpi_factor {
|
||||
if new_scale_factor == old_scale_factor {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1516,15 +1570,15 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
// We calculate our own size because the default suggested rect doesn't do a great job
|
||||
// of preserving the window's logical size.
|
||||
true => old_physical_inner_size
|
||||
.to_logical::<f64>(old_dpi_factor)
|
||||
.to_physical::<u32>(new_dpi_factor),
|
||||
.to_logical::<f64>(old_scale_factor)
|
||||
.to_physical::<u32>(new_scale_factor),
|
||||
false => old_physical_inner_size,
|
||||
};
|
||||
|
||||
let _ = subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: ScaleFactorChanged {
|
||||
scale_factor: new_dpi_factor,
|
||||
scale_factor: new_scale_factor,
|
||||
new_inner_size: &mut new_physical_inner_size,
|
||||
},
|
||||
});
|
||||
@@ -1720,50 +1774,39 @@ unsafe extern "system" fn thread_event_target_callback<T: 'static>(
|
||||
};
|
||||
let in_modal_loop = subclass_input.event_loop_runner.in_modal_loop();
|
||||
if in_modal_loop {
|
||||
let runner = &subclass_input.event_loop_runner;
|
||||
runner.main_events_cleared();
|
||||
// Drain eventual WM_PAINT messages sent if user called request_redraw()
|
||||
// during handling of MainEventsCleared.
|
||||
let mut msg = mem::zeroed();
|
||||
if 0 == winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0) {
|
||||
if msg.message != 0 && msg.message != winuser::WM_PAINT {
|
||||
queue_call_again();
|
||||
return 0;
|
||||
loop {
|
||||
if 0 == winuser::PeekMessageW(
|
||||
&mut msg,
|
||||
ptr::null_mut(),
|
||||
winuser::WM_PAINT,
|
||||
winuser::WM_PAINT,
|
||||
winuser::PM_QS_PAINT | winuser::PM_REMOVE,
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
||||
subclass_input.event_loop_runner.main_events_cleared();
|
||||
loop {
|
||||
if 0 == winuser::PeekMessageW(
|
||||
&mut msg,
|
||||
ptr::null_mut(),
|
||||
winuser::WM_PAINT,
|
||||
winuser::WM_PAINT,
|
||||
1,
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
||||
if msg.hwnd != window {
|
||||
winuser::TranslateMessage(&mut msg);
|
||||
winuser::DispatchMessageW(&mut msg);
|
||||
}
|
||||
if msg.hwnd != window {
|
||||
winuser::TranslateMessage(&mut msg);
|
||||
winuser::DispatchMessageW(&mut msg);
|
||||
}
|
||||
}
|
||||
|
||||
// we don't borrow until here because TODO SAFETY
|
||||
let runner = &subclass_input.event_loop_runner;
|
||||
if runner.redraw_events_cleared().events_buffered() {
|
||||
queue_call_again();
|
||||
runner.new_events();
|
||||
} else {
|
||||
match runner.control_flow() {
|
||||
// Waiting is handled by the modal loop.
|
||||
ControlFlow::Exit | ControlFlow::Wait => runner.new_events(),
|
||||
ControlFlow::WaitUntil(resume_time) => {
|
||||
wait_until_time_or_msg(resume_time);
|
||||
runner.new_events();
|
||||
queue_call_again();
|
||||
}
|
||||
ControlFlow::Poll => {
|
||||
runner.new_events();
|
||||
queue_call_again();
|
||||
}
|
||||
runner.redraw_events_cleared();
|
||||
match runner.control_flow() {
|
||||
// Waiting is handled by the modal loop.
|
||||
ControlFlow::Exit | ControlFlow::Wait => runner.new_events(),
|
||||
ControlFlow::WaitUntil(resume_time) => {
|
||||
wait_until_time_or_msg(resume_time);
|
||||
runner.new_events();
|
||||
queue_call_again();
|
||||
}
|
||||
ControlFlow::Poll => {
|
||||
runner.new_events();
|
||||
queue_call_again();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1787,10 +1830,9 @@ unsafe extern "system" fn thread_event_target_callback<T: 'static>(
|
||||
|
||||
winuser::WM_INPUT => {
|
||||
use crate::event::{
|
||||
DeviceEvent::{Button, Key, ModifiersChanged, Motion, MouseMotion, MouseWheel},
|
||||
DeviceEvent::{Button, Key, Motion, MouseMotion, MouseWheel},
|
||||
ElementState::{Pressed, Released},
|
||||
MouseScrollDelta::LineDelta,
|
||||
VirtualKeyCode,
|
||||
};
|
||||
|
||||
if let Some(data) = raw_input::get_raw_input_data(lparam as _) {
|
||||
@@ -1869,48 +1911,6 @@ unsafe extern "system" fn thread_event_target_callback<T: 'static>(
|
||||
{
|
||||
let virtual_keycode = vkey_to_winit_vkey(vkey);
|
||||
|
||||
// If we ever change the DeviceEvent API to only emit events when a
|
||||
// window is focused, we'll need to emit synthetic `ModifiersChanged`
|
||||
// events when Winit windows lose focus so that these don't drift out
|
||||
// of sync with the actual modifier state.
|
||||
let old_modifiers_state =
|
||||
subclass_input.modifiers_state.filter_out_altgr().into();
|
||||
match virtual_keycode {
|
||||
Some(VirtualKeyCode::LShift) => subclass_input
|
||||
.modifiers_state
|
||||
.set(ModifiersStateSide::LSHIFT, pressed),
|
||||
Some(VirtualKeyCode::RShift) => subclass_input
|
||||
.modifiers_state
|
||||
.set(ModifiersStateSide::RSHIFT, pressed),
|
||||
Some(VirtualKeyCode::LControl) => subclass_input
|
||||
.modifiers_state
|
||||
.set(ModifiersStateSide::LCTRL, pressed),
|
||||
Some(VirtualKeyCode::RControl) => subclass_input
|
||||
.modifiers_state
|
||||
.set(ModifiersStateSide::RCTRL, pressed),
|
||||
Some(VirtualKeyCode::LAlt) => subclass_input
|
||||
.modifiers_state
|
||||
.set(ModifiersStateSide::LALT, pressed),
|
||||
Some(VirtualKeyCode::RAlt) => subclass_input
|
||||
.modifiers_state
|
||||
.set(ModifiersStateSide::RALT, pressed),
|
||||
Some(VirtualKeyCode::LWin) => subclass_input
|
||||
.modifiers_state
|
||||
.set(ModifiersStateSide::LLOGO, pressed),
|
||||
Some(VirtualKeyCode::RWin) => subclass_input
|
||||
.modifiers_state
|
||||
.set(ModifiersStateSide::RLOGO, pressed),
|
||||
_ => (),
|
||||
}
|
||||
let new_modifiers_state =
|
||||
subclass_input.modifiers_state.filter_out_altgr().into();
|
||||
if new_modifiers_state != old_modifiers_state {
|
||||
subclass_input.send_event(Event::DeviceEvent {
|
||||
device_id,
|
||||
event: ModifiersChanged(new_modifiers_state),
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
subclass_input.send_event(Event::DeviceEvent {
|
||||
device_id,
|
||||
@@ -1918,7 +1918,7 @@ unsafe extern "system" fn thread_event_target_callback<T: 'static>(
|
||||
scancode,
|
||||
state,
|
||||
virtual_keycode,
|
||||
modifiers: new_modifiers_state,
|
||||
modifiers: event::get_key_mods(),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
dpi::PhysicalSize,
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event_loop::ControlFlow,
|
||||
platform_impl::platform::{event_loop::EventLoop, util},
|
||||
platform_impl::platform::event_loop::{util, EventLoop},
|
||||
window::WindowId,
|
||||
};
|
||||
|
||||
@@ -14,8 +14,8 @@ pub(crate) type EventLoopRunnerShared<T> = Rc<ELRShared<T>>;
|
||||
pub(crate) struct ELRShared<T: 'static> {
|
||||
runner: RefCell<Option<EventLoopRunner<T>>>,
|
||||
buffer: RefCell<VecDeque<BufferedEvent<T>>>,
|
||||
redraw_buffer: Rc<RefCell<VecDeque<WindowId>>>,
|
||||
}
|
||||
|
||||
struct EventLoopRunner<T: 'static> {
|
||||
control_flow: ControlFlow,
|
||||
runner_state: RunnerState,
|
||||
@@ -23,8 +23,8 @@ struct EventLoopRunner<T: 'static> {
|
||||
in_modal_loop: bool,
|
||||
event_handler: Box<dyn FnMut(Event<'_, T>, &mut ControlFlow)>,
|
||||
panic_error: Option<PanicError>,
|
||||
redraw_buffer: Rc<RefCell<VecDeque<WindowId>>>,
|
||||
}
|
||||
|
||||
pub type PanicError = Box<dyn Any + Send + 'static>;
|
||||
|
||||
pub enum BufferedEvent<T: 'static> {
|
||||
@@ -32,22 +32,6 @@ pub enum BufferedEvent<T: 'static> {
|
||||
ScaleFactorChanged(WindowId, f64, PhysicalSize<u32>),
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum AreEventsBuffered {
|
||||
EventsBuffered,
|
||||
ReadyToSleep,
|
||||
}
|
||||
|
||||
impl AreEventsBuffered {
|
||||
pub fn events_buffered(&self) -> bool {
|
||||
match self {
|
||||
Self::EventsBuffered => true,
|
||||
Self::ReadyToSleep => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> BufferedEvent<T> {
|
||||
pub fn from_event(event: Event<'_, T>) -> BufferedEvent<T> {
|
||||
match event {
|
||||
@@ -89,7 +73,6 @@ impl<T> ELRShared<T> {
|
||||
ELRShared {
|
||||
runner: RefCell::new(None),
|
||||
buffer: RefCell::new(VecDeque::new()),
|
||||
redraw_buffer: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,16 +80,11 @@ impl<T> ELRShared<T> {
|
||||
where
|
||||
F: FnMut(Event<'_, T>, &mut ControlFlow),
|
||||
{
|
||||
let mut runner = EventLoopRunner::new(event_loop, self.redraw_buffer.clone(), f);
|
||||
let mut runner = EventLoopRunner::new(event_loop, f);
|
||||
{
|
||||
let mut runner_ref = self.runner.borrow_mut();
|
||||
loop {
|
||||
let event = self.buffer.borrow_mut().pop_front();
|
||||
match event {
|
||||
Some(e) => e.dispatch_event(|e| runner.process_event(e)),
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
// Dispatch any events that were buffered during the creation of the window
|
||||
self.dispatch_buffered_events(&mut runner);
|
||||
*runner_ref = Some(runner);
|
||||
}
|
||||
}
|
||||
@@ -119,80 +97,46 @@ impl<T> ELRShared<T> {
|
||||
let mut runner_ref = self.runner.borrow_mut();
|
||||
if let Some(ref mut runner) = *runner_ref {
|
||||
runner.new_events();
|
||||
loop {
|
||||
let buffered_event_opt = self.buffer.borrow_mut().pop_front();
|
||||
match buffered_event_opt {
|
||||
Some(e) => e.dispatch_event(|e| runner.process_event(e)),
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
// Dispatch any events that were buffered during the call `new_events`
|
||||
self.dispatch_buffered_events(runner);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn send_event(&self, event: Event<'_, T>) {
|
||||
let handling_redraw = self
|
||||
.runner
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map(|r| RunnerState::HandlingRedraw == r.runner_state)
|
||||
.unwrap_or(false);
|
||||
let mut send = None;
|
||||
if handling_redraw {
|
||||
pub(crate) fn send_event(&self, event: Event<'_, T>) {
|
||||
if let Err(event) = self.send_event_unbuffered(event) {
|
||||
// If the runner is already borrowed, we're in the middle of an event loop invocation.
|
||||
// Add the event to a buffer to be processed later.
|
||||
if let Event::RedrawRequested(_) = event {
|
||||
send = Some(event);
|
||||
} else {
|
||||
self.buffer_event(event);
|
||||
}
|
||||
} else {
|
||||
send = Some(event);
|
||||
}
|
||||
if let Some(event) = send {
|
||||
if let Err(event) = self.send_event_unbuffered(event) {
|
||||
// If the runner is already borrowed, we're in the middle of an event loop invocation. Add
|
||||
// the event to a buffer to be processed later.
|
||||
self.buffer_event(event);
|
||||
panic!("buffering RedrawRequested event");
|
||||
}
|
||||
self.buffer
|
||||
.borrow_mut()
|
||||
.push_back(BufferedEvent::from_event(event));
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn send_event_unbuffered<'e>(&self, event: Event<'e, T>) -> Result<(), Event<'e, T>> {
|
||||
fn send_event_unbuffered<'e>(&self, event: Event<'e, T>) -> Result<(), Event<'e, T>> {
|
||||
if let Ok(mut runner_ref) = self.runner.try_borrow_mut() {
|
||||
if let Some(ref mut runner) = *runner_ref {
|
||||
runner.process_event(event);
|
||||
|
||||
let handling_redraw = if let RunnerState::HandlingRedraw = runner.runner_state {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if !handling_redraw {
|
||||
// Dispatch any events that were buffered during the call to `process_event`.
|
||||
loop {
|
||||
// We do this instead of using a `while let` loop because if we use a `while let`
|
||||
// loop the reference returned `borrow_mut()` doesn't get dropped until the end
|
||||
// of the loop's body and attempts to add events to the event buffer while in
|
||||
// `process_event` will fail.
|
||||
let buffered_event_opt = self.buffer.borrow_mut().pop_front();
|
||||
match buffered_event_opt {
|
||||
Some(e) => e.dispatch_event(|e| runner.process_event(e)),
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch any events that were buffered during the call to `process_event`.
|
||||
self.dispatch_buffered_events(runner);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Err(event)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn call_event_handler(&self, event: Event<'static, T>) {
|
||||
if let Ok(mut runner_ref) = self.runner.try_borrow_mut() {
|
||||
if let Some(ref mut runner) = *runner_ref {
|
||||
runner.call_event_handler(event);
|
||||
return;
|
||||
fn dispatch_buffered_events(&self, runner: &mut EventLoopRunner<T>) {
|
||||
// We do this instead of using a `while let` loop because if we use a `while let`
|
||||
// loop the reference returned `borrow_mut()` doesn't get dropped until the end
|
||||
// of the loop's body and attempts to add events to the event buffer while in
|
||||
// `process_event` will fail.
|
||||
loop {
|
||||
let buffered_event_opt = self.buffer.borrow_mut().pop_front();
|
||||
match buffered_event_opt {
|
||||
Some(e) => e.dispatch_event(|e| runner.process_event(e)),
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,17 +145,27 @@ impl<T> ELRShared<T> {
|
||||
let mut runner_ref = self.runner.borrow_mut();
|
||||
if let Some(ref mut runner) = *runner_ref {
|
||||
runner.main_events_cleared();
|
||||
if !self.buffer.borrow().is_empty() {
|
||||
warn!("Buffered events while dispatching MainEventsCleared");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn redraw_events_cleared(&self) -> AreEventsBuffered {
|
||||
pub(crate) fn redraw_events_cleared(&self) {
|
||||
let mut runner_ref = self.runner.borrow_mut();
|
||||
if let Some(ref mut runner) = *runner_ref {
|
||||
runner.redraw_events_cleared();
|
||||
if !self.buffer.borrow().is_empty() {
|
||||
warn!("Buffered events while dispatching RedrawEventsCleared");
|
||||
}
|
||||
}
|
||||
match self.buffer.borrow().len() {
|
||||
0 => AreEventsBuffered::ReadyToSleep,
|
||||
_ => AreEventsBuffered::EventsBuffered,
|
||||
}
|
||||
|
||||
pub(crate) fn destroy_loop(&self) {
|
||||
if let Ok(mut runner_ref) = self.runner.try_borrow_mut() {
|
||||
if let Some(ref mut runner) = *runner_ref {
|
||||
runner.call_event_handler(Event::LoopDestroyed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,6 +182,17 @@ impl<T> ELRShared<T> {
|
||||
let mut runner_ref = self.runner.borrow_mut();
|
||||
if let Some(ref mut runner) = *runner_ref {
|
||||
runner.in_modal_loop = in_modal_loop;
|
||||
if in_modal_loop {
|
||||
// jumpstart the modal loop
|
||||
unsafe {
|
||||
winuser::RedrawWindow(
|
||||
runner.modal_redraw_window,
|
||||
ptr::null(),
|
||||
ptr::null_mut(),
|
||||
winuser::RDW_INTERNALPAINT,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,18 +213,6 @@ impl<T> ELRShared<T> {
|
||||
ControlFlow::Exit
|
||||
}
|
||||
}
|
||||
|
||||
fn buffer_event(&self, event: Event<'_, T>) {
|
||||
match event {
|
||||
Event::RedrawRequested(window_id) => {
|
||||
self.redraw_buffer.borrow_mut().push_back(window_id)
|
||||
}
|
||||
_ => self
|
||||
.buffer
|
||||
.borrow_mut()
|
||||
.push_back(BufferedEvent::from_event(event)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -275,15 +228,13 @@ enum RunnerState {
|
||||
/// The event loop is handling the OS's events and sending them to the user's callback.
|
||||
/// `NewEvents` has been sent, and `MainEventsCleared` hasn't.
|
||||
HandlingEvents,
|
||||
/// The event loop is handling the redraw events and sending them to the user's callback.
|
||||
/// `MainEventsCleared` has been sent, and `RedrawEventsCleared` hasn't.
|
||||
HandlingRedraw,
|
||||
}
|
||||
|
||||
impl<T> EventLoopRunner<T> {
|
||||
unsafe fn new<F>(
|
||||
event_loop: &EventLoop<T>,
|
||||
redraw_buffer: Rc<RefCell<VecDeque<WindowId>>>,
|
||||
f: F,
|
||||
) -> EventLoopRunner<T>
|
||||
unsafe fn new<F>(event_loop: &EventLoop<T>, f: F) -> EventLoopRunner<T>
|
||||
where
|
||||
F: FnMut(Event<'_, T>, &mut ControlFlow),
|
||||
{
|
||||
@@ -297,7 +248,6 @@ impl<T> EventLoopRunner<T> {
|
||||
Box<dyn FnMut(Event<'_, T>, &mut ControlFlow)>,
|
||||
>(Box::new(f)),
|
||||
panic_error: None,
|
||||
redraw_buffer,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,6 +273,8 @@ impl<T> EventLoopRunner<T> {
|
||||
}
|
||||
|
||||
// When `NewEvents` gets sent after an idle depends on the control flow...
|
||||
// Some `NewEvents` are deferred because not all Windows messages trigger an event_loop event.
|
||||
// So we defer the `NewEvents` to when we actually process an event.
|
||||
RunnerState::Idle(wait_start) => {
|
||||
match self.control_flow {
|
||||
// If we're polling, send `NewEvents` and immediately move into event processing.
|
||||
@@ -411,24 +363,21 @@ impl<T> EventLoopRunner<T> {
|
||||
// that was sent after `MainEventsCleared`.
|
||||
ControlFlow::Poll => self.call_event_handler(Event::NewEvents(StartCause::Poll)),
|
||||
}
|
||||
self.runner_state = RunnerState::HandlingEvents;
|
||||
}
|
||||
|
||||
match (self.runner_state, &event) {
|
||||
(RunnerState::HandlingRedraw, Event::RedrawRequested(_)) => {
|
||||
self.call_event_handler(event)
|
||||
(RunnerState::HandlingEvents, Event::RedrawRequested(window_id)) => {
|
||||
self.call_event_handler(Event::MainEventsCleared);
|
||||
self.runner_state = RunnerState::HandlingRedraw;
|
||||
self.call_event_handler(Event::RedrawRequested(*window_id));
|
||||
}
|
||||
(RunnerState::New, Event::RedrawRequested(_))
|
||||
| (RunnerState::Idle(..), Event::RedrawRequested(_)) => {
|
||||
self.new_events();
|
||||
self.main_events_cleared();
|
||||
self.call_event_handler(event);
|
||||
}
|
||||
(_, Event::RedrawRequested(_)) => {
|
||||
panic!("redraw event in non-redraw phase");
|
||||
(RunnerState::HandlingRedraw, Event::RedrawRequested(window_id)) => {
|
||||
self.call_event_handler(Event::RedrawRequested(*window_id));
|
||||
}
|
||||
(RunnerState::HandlingRedraw, _) => {
|
||||
panic!(
|
||||
"Non-redraw event dispatched durning redraw phase: {:?}",
|
||||
warn!(
|
||||
"non-redraw event in redraw phase: {:?}",
|
||||
event.map_nonuser_event::<()>().ok()
|
||||
);
|
||||
}
|
||||
@@ -439,16 +388,6 @@ impl<T> EventLoopRunner<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_redraws(&mut self) {
|
||||
loop {
|
||||
let redraw_window_opt = self.redraw_buffer.borrow_mut().pop_front();
|
||||
match redraw_window_opt {
|
||||
Some(window_id) => self.process_event(Event::RedrawRequested(window_id)),
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main_events_cleared(&mut self) {
|
||||
match self.runner_state {
|
||||
// If we were handling events, send the MainEventsCleared message.
|
||||
@@ -457,7 +396,9 @@ impl<T> EventLoopRunner<T> {
|
||||
self.runner_state = RunnerState::HandlingRedraw;
|
||||
}
|
||||
|
||||
RunnerState::HandlingRedraw => (),
|
||||
// We already cleared the main events, we don't have to do anything.
|
||||
// This happens when process_events() processed a RedrawRequested event.
|
||||
RunnerState::HandlingRedraw => {}
|
||||
|
||||
// If we *weren't* handling events, we don't have to do anything.
|
||||
RunnerState::New | RunnerState::Idle(..) => (),
|
||||
@@ -469,6 +410,7 @@ impl<T> EventLoopRunner<T> {
|
||||
// If we had deferred a Poll, send the Poll NewEvents and MainEventsCleared.
|
||||
ControlFlow::Poll => {
|
||||
self.call_event_handler(Event::NewEvents(StartCause::Poll));
|
||||
self.runner_state = RunnerState::HandlingEvents;
|
||||
self.call_event_handler(Event::MainEventsCleared);
|
||||
self.runner_state = RunnerState::HandlingRedraw;
|
||||
}
|
||||
@@ -482,6 +424,7 @@ impl<T> EventLoopRunner<T> {
|
||||
requested_resume: resume_time,
|
||||
},
|
||||
));
|
||||
self.runner_state = RunnerState::HandlingEvents;
|
||||
self.call_event_handler(Event::MainEventsCleared);
|
||||
self.runner_state = RunnerState::HandlingRedraw;
|
||||
}
|
||||
@@ -496,57 +439,18 @@ impl<T> EventLoopRunner<T> {
|
||||
|
||||
fn redraw_events_cleared(&mut self) {
|
||||
match self.runner_state {
|
||||
// If we were handling events, send the MainEventsCleared message.
|
||||
RunnerState::HandlingEvents => {
|
||||
self.call_event_handler(Event::MainEventsCleared);
|
||||
self.runner_state = RunnerState::HandlingRedraw;
|
||||
self.flush_redraws();
|
||||
self.call_event_handler(Event::RedrawEventsCleared);
|
||||
self.runner_state = RunnerState::Idle(Instant::now());
|
||||
}
|
||||
|
||||
// If we were handling redraws, send the RedrawEventsCleared message.
|
||||
RunnerState::HandlingRedraw => {
|
||||
self.flush_redraws();
|
||||
self.call_event_handler(Event::RedrawEventsCleared);
|
||||
self.runner_state = RunnerState::Idle(Instant::now());
|
||||
}
|
||||
|
||||
// If we *weren't* handling events, we don't have to do anything.
|
||||
RunnerState::New | RunnerState::Idle(..) => (),
|
||||
|
||||
// Some control flows require a NewEvents call even if no events were received. This
|
||||
// branch handles those.
|
||||
RunnerState::DeferredNewEvents(wait_start) => {
|
||||
match self.control_flow {
|
||||
// If we had deferred a Poll, send the Poll NewEvents and MainEventsCleared.
|
||||
ControlFlow::Poll => {
|
||||
self.call_event_handler(Event::NewEvents(StartCause::Poll));
|
||||
self.call_event_handler(Event::MainEventsCleared);
|
||||
self.flush_redraws();
|
||||
self.call_event_handler(Event::RedrawEventsCleared);
|
||||
}
|
||||
// If we had deferred a WaitUntil and the resume time has since been reached,
|
||||
// send the resume notification and MainEventsCleared event.
|
||||
ControlFlow::WaitUntil(resume_time) => {
|
||||
if Instant::now() >= resume_time {
|
||||
self.call_event_handler(Event::NewEvents(
|
||||
StartCause::ResumeTimeReached {
|
||||
start: wait_start,
|
||||
requested_resume: resume_time,
|
||||
},
|
||||
));
|
||||
self.call_event_handler(Event::MainEventsCleared);
|
||||
self.flush_redraws();
|
||||
self.call_event_handler(Event::RedrawEventsCleared);
|
||||
}
|
||||
}
|
||||
// If we deferred a wait and no events were received, the user doesn't have to
|
||||
// get an event.
|
||||
ControlFlow::Wait | ControlFlow::Exit => (),
|
||||
}
|
||||
// Mark that we've entered an idle state.
|
||||
self.runner_state = RunnerState::Idle(wait_start)
|
||||
}
|
||||
// No event was processed, we don't have to do anything.
|
||||
RunnerState::DeferredNewEvents(_) => (),
|
||||
// Should not happen.
|
||||
_ => warn!(
|
||||
"unexpected state in redraw_events_cleared: {:?}",
|
||||
self.runner_state
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
use std::{io, mem, os::windows::ffi::OsStrExt, path::Path, ptr};
|
||||
use std::{fmt, io, iter::once, mem, os::windows::ffi::OsStrExt, path::Path, ptr, sync::Arc};
|
||||
|
||||
use winapi::{
|
||||
ctypes::{c_int, wchar_t},
|
||||
shared::{
|
||||
minwindef::{BYTE, LPARAM, WPARAM},
|
||||
minwindef::{BYTE, LPARAM, WORD, WPARAM},
|
||||
windef::{HICON, HWND},
|
||||
},
|
||||
um::libloaderapi,
|
||||
um::winuser,
|
||||
};
|
||||
|
||||
use crate::icon::{Icon, Pixel, PIXEL_SIZE};
|
||||
use crate::dpi::PhysicalSize;
|
||||
use crate::icon::*;
|
||||
|
||||
impl Pixel {
|
||||
fn to_bgra(&mut self) {
|
||||
@@ -17,92 +19,149 @@ impl Pixel {
|
||||
}
|
||||
}
|
||||
|
||||
impl RgbaIcon {
|
||||
fn into_windows_icon(self) -> Result<WinIcon, BadIcon> {
|
||||
let mut rgba = self.rgba;
|
||||
let pixel_count = rgba.len() / PIXEL_SIZE;
|
||||
let mut and_mask = Vec::with_capacity(pixel_count);
|
||||
let pixels =
|
||||
unsafe { std::slice::from_raw_parts_mut(rgba.as_mut_ptr() as *mut Pixel, pixel_count) };
|
||||
for pixel in pixels {
|
||||
and_mask.push(pixel.a.wrapping_sub(std::u8::MAX)); // invert alpha channel
|
||||
pixel.to_bgra();
|
||||
}
|
||||
assert_eq!(and_mask.len(), pixel_count);
|
||||
let handle = unsafe {
|
||||
winuser::CreateIcon(
|
||||
ptr::null_mut(),
|
||||
self.width as c_int,
|
||||
self.height as c_int,
|
||||
1,
|
||||
(PIXEL_SIZE * 8) as BYTE,
|
||||
and_mask.as_ptr() as *const BYTE,
|
||||
rgba.as_ptr() as *const BYTE,
|
||||
) as HICON
|
||||
};
|
||||
if !handle.is_null() {
|
||||
Ok(WinIcon::from_handle(handle))
|
||||
} else {
|
||||
Err(BadIcon::OsError(io::Error::last_os_error()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IconType {
|
||||
Small = winuser::ICON_SMALL as isize,
|
||||
Big = winuser::ICON_BIG as isize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
struct RaiiIcon {
|
||||
handle: HICON,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WinIcon {
|
||||
pub handle: HICON,
|
||||
inner: Arc<RaiiIcon>,
|
||||
}
|
||||
|
||||
unsafe impl Send for WinIcon {}
|
||||
|
||||
impl WinIcon {
|
||||
#[allow(dead_code)]
|
||||
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, io::Error> {
|
||||
let wide_path: Vec<u16> = path.as_ref().as_os_str().encode_wide().collect();
|
||||
pub fn as_raw_handle(&self) -> HICON {
|
||||
self.inner.handle
|
||||
}
|
||||
|
||||
pub fn from_path<P: AsRef<Path>>(
|
||||
path: P,
|
||||
size: Option<PhysicalSize<u32>>,
|
||||
) -> Result<Self, BadIcon> {
|
||||
let wide_path: Vec<u16> = path
|
||||
.as_ref()
|
||||
.as_os_str()
|
||||
.encode_wide()
|
||||
.chain(once(0))
|
||||
.collect();
|
||||
|
||||
// width / height of 0 along with LR_DEFAULTSIZE tells windows to load the default icon size
|
||||
let (width, height) = size.map(Into::into).unwrap_or((0, 0));
|
||||
|
||||
let handle = unsafe {
|
||||
winuser::LoadImageW(
|
||||
ptr::null_mut(),
|
||||
wide_path.as_ptr() as *const wchar_t,
|
||||
winuser::IMAGE_ICON,
|
||||
0, // 0 indicates that we want to use the actual width
|
||||
0, // and height
|
||||
winuser::LR_LOADFROMFILE,
|
||||
) as HICON
|
||||
};
|
||||
if !handle.is_null() {
|
||||
Ok(WinIcon { handle })
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_icon(icon: Icon) -> Result<Self, io::Error> {
|
||||
Self::from_rgba(icon.rgba, icon.width, icon.height)
|
||||
}
|
||||
|
||||
pub fn from_rgba(mut rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, io::Error> {
|
||||
assert_eq!(rgba.len() % PIXEL_SIZE, 0);
|
||||
let pixel_count = rgba.len() / PIXEL_SIZE;
|
||||
assert_eq!(pixel_count, (width * height) as usize);
|
||||
let mut and_mask = Vec::with_capacity(pixel_count);
|
||||
let pixels = rgba.as_mut_ptr() as *mut Pixel; // how not to write idiomatic Rust
|
||||
for pixel_index in 0..pixel_count {
|
||||
let pixel = unsafe { &mut *pixels.offset(pixel_index as isize) };
|
||||
and_mask.push(pixel.a.wrapping_sub(std::u8::MAX)); // invert alpha channel
|
||||
pixel.to_bgra();
|
||||
}
|
||||
assert_eq!(and_mask.len(), pixel_count);
|
||||
let handle = unsafe {
|
||||
winuser::CreateIcon(
|
||||
ptr::null_mut(),
|
||||
width as c_int,
|
||||
height as c_int,
|
||||
1,
|
||||
(PIXEL_SIZE * 8) as BYTE,
|
||||
and_mask.as_ptr() as *const BYTE,
|
||||
rgba.as_ptr() as *const BYTE,
|
||||
winuser::LR_DEFAULTSIZE | winuser::LR_LOADFROMFILE,
|
||||
) as HICON
|
||||
};
|
||||
if !handle.is_null() {
|
||||
Ok(WinIcon { handle })
|
||||
Ok(WinIcon::from_handle(handle))
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
Err(BadIcon::OsError(io::Error::last_os_error()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_resource(
|
||||
resource_id: WORD,
|
||||
size: Option<PhysicalSize<u32>>,
|
||||
) -> Result<Self, BadIcon> {
|
||||
// width / height of 0 along with LR_DEFAULTSIZE tells windows to load the default icon size
|
||||
let (width, height) = size.map(Into::into).unwrap_or((0, 0));
|
||||
let handle = unsafe {
|
||||
winuser::LoadImageW(
|
||||
libloaderapi::GetModuleHandleW(ptr::null_mut()),
|
||||
winuser::MAKEINTRESOURCEW(resource_id),
|
||||
winuser::IMAGE_ICON,
|
||||
width as c_int,
|
||||
height as c_int,
|
||||
winuser::LR_DEFAULTSIZE,
|
||||
) as HICON
|
||||
};
|
||||
if !handle.is_null() {
|
||||
Ok(WinIcon::from_handle(handle))
|
||||
} else {
|
||||
Err(BadIcon::OsError(io::Error::last_os_error()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
|
||||
let rgba_icon = RgbaIcon::from_rgba(rgba, width, height)?;
|
||||
rgba_icon.into_windows_icon()
|
||||
}
|
||||
|
||||
pub fn set_for_window(&self, hwnd: HWND, icon_type: IconType) {
|
||||
unsafe {
|
||||
winuser::SendMessageW(
|
||||
hwnd,
|
||||
winuser::WM_SETICON,
|
||||
icon_type as WPARAM,
|
||||
self.handle as LPARAM,
|
||||
self.as_raw_handle() as LPARAM,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn from_handle(handle: HICON) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RaiiIcon { handle }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WinIcon {
|
||||
impl Drop for RaiiIcon {
|
||||
fn drop(&mut self) {
|
||||
unsafe { winuser::DestroyIcon(self.handle) };
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for WinIcon {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
(*self.inner).fmt(formatter)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unset_for_window(hwnd: HWND, icon_type: IconType) {
|
||||
unsafe {
|
||||
winuser::SendMessageW(hwnd, winuser::WM_SETICON, icon_type as WPARAM, 0 as LPARAM);
|
||||
|
||||
@@ -4,11 +4,15 @@ use winapi::{self, shared::windef::HWND};
|
||||
|
||||
pub use self::{
|
||||
event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget},
|
||||
icon::WinIcon,
|
||||
monitor::{MonitorHandle, VideoMode},
|
||||
window::Window,
|
||||
};
|
||||
|
||||
use crate::{event::DeviceId as RootDeviceId, window::Icon};
|
||||
pub use self::icon::WinIcon as PlatformIcon;
|
||||
|
||||
use crate::event::DeviceId as RootDeviceId;
|
||||
use crate::icon::Icon;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
|
||||
@@ -116,7 +116,7 @@ pub(crate) fn set_inner_size_physical(window: HWND, x: u32, y: u32) {
|
||||
| winuser::SWP_NOMOVE
|
||||
| winuser::SWP_NOACTIVATE,
|
||||
);
|
||||
winuser::UpdateWindow(window);
|
||||
winuser::InvalidateRgn(window, ptr::null_mut(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,18 +32,19 @@ use winapi::{
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
||||
icon::Icon,
|
||||
monitor::MonitorHandle as RootMonitorHandle,
|
||||
platform_impl::platform::{
|
||||
dark_mode::try_dark_mode,
|
||||
dpi::{dpi_to_scale_factor, hwnd_dpi},
|
||||
drop_handler::FileDropHandler,
|
||||
event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID},
|
||||
icon::{self, IconType, WinIcon},
|
||||
icon::{self, IconType},
|
||||
monitor, util,
|
||||
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
|
||||
PlatformSpecificWindowBuilderAttributes, WindowId,
|
||||
},
|
||||
window::{CursorIcon, Fullscreen, Icon, WindowAttributes},
|
||||
window::{CursorIcon, Fullscreen, WindowAttributes},
|
||||
};
|
||||
|
||||
/// The Win32 implementation of the main `Window` object.
|
||||
@@ -187,7 +188,7 @@ impl Window {
|
||||
| winuser::SWP_NOSIZE
|
||||
| winuser::SWP_NOACTIVATE,
|
||||
);
|
||||
winuser::UpdateWindow(self.window.0);
|
||||
winuser::InvalidateRgn(self.window.0, ptr::null_mut(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,8 +218,8 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn set_inner_size(&self, size: Size) {
|
||||
let dpi_factor = self.scale_factor();
|
||||
let (width, height) = size.to_physical::<u32>(dpi_factor).into();
|
||||
let scale_factor = self.scale_factor();
|
||||
let (width, height) = size.to_physical::<u32>(scale_factor).into();
|
||||
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
let window = self.window.clone();
|
||||
@@ -325,13 +326,13 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
self.window_state.lock().dpi_factor
|
||||
self.window_state.lock().scale_factor
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
|
||||
let dpi_factor = self.scale_factor();
|
||||
let (x, y) = position.to_physical::<i32>(dpi_factor).into();
|
||||
let scale_factor = self.scale_factor();
|
||||
let (x, y) = position.to_physical::<i32>(scale_factor).into();
|
||||
|
||||
let mut point = POINT { x, y };
|
||||
unsafe {
|
||||
@@ -402,7 +403,7 @@ impl Window {
|
||||
let client_rect = util::get_client_rect(window.0).unwrap();
|
||||
window_state_lock.saved_window = Some(SavedWindow {
|
||||
client_rect,
|
||||
dpi_factor: window_state_lock.dpi_factor,
|
||||
scale_factor: window_state_lock.scale_factor,
|
||||
});
|
||||
}
|
||||
_ => (),
|
||||
@@ -506,17 +507,17 @@ impl Window {
|
||||
size.1 as i32,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER,
|
||||
);
|
||||
winuser::UpdateWindow(window.0);
|
||||
winuser::InvalidateRgn(window.0, ptr::null_mut(), 0);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let mut window_state_lock = window_state.lock();
|
||||
if let Some(SavedWindow {
|
||||
client_rect,
|
||||
dpi_factor,
|
||||
scale_factor,
|
||||
}) = window_state_lock.saved_window.take()
|
||||
{
|
||||
window_state_lock.dpi_factor = dpi_factor;
|
||||
window_state_lock.scale_factor = scale_factor;
|
||||
drop(window_state_lock);
|
||||
let client_rect = util::adjust_window_rect(window.0, client_rect).unwrap();
|
||||
|
||||
@@ -532,7 +533,7 @@ impl Window {
|
||||
| winuser::SWP_NOZORDER
|
||||
| winuser::SWP_NOACTIVATE,
|
||||
);
|
||||
winuser::UpdateWindow(window.0);
|
||||
winuser::InvalidateRgn(window.0, ptr::null_mut(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -576,12 +577,11 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, mut window_icon: Option<Icon>) {
|
||||
let window_icon = window_icon
|
||||
.take()
|
||||
.map(|icon| WinIcon::from_icon(icon).expect("Failed to create `ICON_SMALL`"));
|
||||
pub fn set_window_icon(&self, window_icon: Option<Icon>) {
|
||||
if let Some(ref window_icon) = window_icon {
|
||||
window_icon.set_for_window(self.window.0, IconType::Small);
|
||||
window_icon
|
||||
.inner
|
||||
.set_for_window(self.window.0, IconType::Small);
|
||||
} else {
|
||||
icon::unset_for_window(self.window.0, IconType::Small);
|
||||
}
|
||||
@@ -589,12 +589,11 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_taskbar_icon(&self, mut taskbar_icon: Option<Icon>) {
|
||||
let taskbar_icon = taskbar_icon
|
||||
.take()
|
||||
.map(|icon| WinIcon::from_icon(icon).expect("Failed to create `ICON_BIG`"));
|
||||
pub fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
|
||||
if let Some(ref taskbar_icon) = taskbar_icon {
|
||||
taskbar_icon.set_for_window(self.window.0, IconType::Big);
|
||||
taskbar_icon
|
||||
.inner
|
||||
.set_for_window(self.window.0, IconType::Big);
|
||||
} else {
|
||||
icon::unset_for_window(self.window.0, IconType::Big);
|
||||
}
|
||||
@@ -636,7 +635,7 @@ unsafe impl Sync for WindowWrapper {}
|
||||
unsafe impl Send for WindowWrapper {}
|
||||
|
||||
unsafe fn init<T: 'static>(
|
||||
mut attributes: WindowAttributes,
|
||||
attributes: WindowAttributes,
|
||||
pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||
event_loop: &EventLoopWindowTarget<T>,
|
||||
) -> Result<Window, RootOsError> {
|
||||
@@ -645,25 +644,8 @@ unsafe fn init<T: 'static>(
|
||||
.chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let window_icon = {
|
||||
let icon = attributes.window_icon.take().map(WinIcon::from_icon);
|
||||
if let Some(icon) = icon {
|
||||
Some(icon.map_err(|e| os_error!(e))?)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
let taskbar_icon = {
|
||||
let icon = attributes.window_icon.take().map(WinIcon::from_icon);
|
||||
if let Some(icon) = icon {
|
||||
Some(icon.map_err(|e| os_error!(e))?)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
// registering the window class
|
||||
let class_name = register_window_class(&window_icon, &taskbar_icon);
|
||||
let class_name = register_window_class(&attributes.window_icon, &pl_attribs.taskbar_icon);
|
||||
|
||||
let mut window_flags = WindowFlags::empty();
|
||||
window_flags.set(WindowFlags::DECORATIONS, attributes.decorations);
|
||||
@@ -712,7 +694,7 @@ unsafe fn init<T: 'static>(
|
||||
}
|
||||
|
||||
let dpi = hwnd_dpi(real_window.0);
|
||||
let dpi_factor = dpi_to_scale_factor(dpi);
|
||||
let scale_factor = dpi_to_scale_factor(dpi);
|
||||
|
||||
// making the window transparent
|
||||
if attributes.transparent && !pl_attribs.no_redirection_bitmap {
|
||||
@@ -756,9 +738,8 @@ unsafe fn init<T: 'static>(
|
||||
let window_state = {
|
||||
let window_state = WindowState::new(
|
||||
&attributes,
|
||||
window_icon,
|
||||
taskbar_icon,
|
||||
dpi_factor,
|
||||
pl_attribs.taskbar_icon,
|
||||
scale_factor,
|
||||
dark_mode,
|
||||
);
|
||||
let window_state = Arc::new(Mutex::new(window_state));
|
||||
@@ -787,8 +768,8 @@ unsafe fn init<T: 'static>(
|
||||
}
|
||||
|
||||
unsafe fn register_window_class(
|
||||
window_icon: &Option<WinIcon>,
|
||||
taskbar_icon: &Option<WinIcon>,
|
||||
window_icon: &Option<Icon>,
|
||||
taskbar_icon: &Option<Icon>,
|
||||
) -> Vec<u16> {
|
||||
let class_name: Vec<_> = OsStr::new("Window Class")
|
||||
.encode_wide()
|
||||
@@ -797,11 +778,11 @@ unsafe fn register_window_class(
|
||||
|
||||
let h_icon = taskbar_icon
|
||||
.as_ref()
|
||||
.map(|icon| icon.handle)
|
||||
.map(|icon| icon.inner.as_raw_handle())
|
||||
.unwrap_or(ptr::null_mut());
|
||||
let h_icon_small = window_icon
|
||||
.as_ref()
|
||||
.map(|icon| icon.handle)
|
||||
.map(|icon| icon.inner.as_raw_handle())
|
||||
.unwrap_or(ptr::null_mut());
|
||||
|
||||
let class = winuser::WNDCLASSEXW {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::{
|
||||
dpi::Size,
|
||||
platform_impl::platform::{event_loop, icon::WinIcon, util},
|
||||
dpi::{PhysicalPosition, Size},
|
||||
event::ModifiersState,
|
||||
icon::Icon,
|
||||
platform_impl::platform::{event_loop, util},
|
||||
window::{CursorIcon, Fullscreen, WindowAttributes},
|
||||
};
|
||||
use parking_lot::MutexGuard;
|
||||
@@ -14,7 +16,6 @@ use winapi::{
|
||||
};
|
||||
|
||||
/// Contains information about states and the window that the callback is going to use.
|
||||
#[derive(Clone)]
|
||||
pub struct WindowState {
|
||||
pub mouse: MouseProperties,
|
||||
|
||||
@@ -22,16 +23,14 @@ pub struct WindowState {
|
||||
pub min_size: Option<Size>,
|
||||
pub max_size: Option<Size>,
|
||||
|
||||
pub window_icon: Option<WinIcon>,
|
||||
pub taskbar_icon: Option<WinIcon>,
|
||||
pub window_icon: Option<Icon>,
|
||||
pub taskbar_icon: Option<Icon>,
|
||||
|
||||
pub saved_window: Option<SavedWindow>,
|
||||
pub dpi_factor: f64,
|
||||
pub scale_factor: f64,
|
||||
|
||||
pub modifiers_state: ModifiersState,
|
||||
pub fullscreen: Option<Fullscreen>,
|
||||
/// Used to supress duplicate redraw attempts when calling `request_redraw` multiple
|
||||
/// times in `MainEventsCleared`.
|
||||
pub queued_out_of_band_redraw: bool,
|
||||
pub is_dark_mode: bool,
|
||||
pub high_surrogate: Option<u16>,
|
||||
window_flags: WindowFlags,
|
||||
@@ -40,7 +39,7 @@ pub struct WindowState {
|
||||
#[derive(Clone)]
|
||||
pub struct SavedWindow {
|
||||
pub client_rect: RECT,
|
||||
pub dpi_factor: f64,
|
||||
pub scale_factor: f64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -48,6 +47,7 @@ pub struct MouseProperties {
|
||||
pub cursor: CursorIcon,
|
||||
pub buttons_down: u32,
|
||||
cursor_flags: CursorFlags,
|
||||
pub last_position: Option<PhysicalPosition<f64>>,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
@@ -96,9 +96,8 @@ bitflags! {
|
||||
impl WindowState {
|
||||
pub fn new(
|
||||
attributes: &WindowAttributes,
|
||||
window_icon: Option<WinIcon>,
|
||||
taskbar_icon: Option<WinIcon>,
|
||||
dpi_factor: f64,
|
||||
taskbar_icon: Option<Icon>,
|
||||
scale_factor: f64,
|
||||
is_dark_mode: bool,
|
||||
) -> WindowState {
|
||||
WindowState {
|
||||
@@ -106,19 +105,20 @@ impl WindowState {
|
||||
cursor: CursorIcon::default(),
|
||||
buttons_down: 0,
|
||||
cursor_flags: CursorFlags::empty(),
|
||||
last_position: None,
|
||||
},
|
||||
|
||||
min_size: attributes.min_inner_size,
|
||||
max_size: attributes.max_inner_size,
|
||||
|
||||
window_icon,
|
||||
window_icon: attributes.window_icon.clone(),
|
||||
taskbar_icon,
|
||||
|
||||
saved_window: None,
|
||||
dpi_factor,
|
||||
scale_factor,
|
||||
|
||||
modifiers_state: ModifiersState::default(),
|
||||
fullscreen: None,
|
||||
queued_out_of_band_redraw: false,
|
||||
is_dark_mode,
|
||||
high_surrogate: None,
|
||||
window_flags: WindowFlags::empty(),
|
||||
@@ -268,7 +268,7 @@ impl WindowFlags {
|
||||
| winuser::SWP_NOSIZE
|
||||
| winuser::SWP_NOACTIVATE,
|
||||
);
|
||||
winuser::UpdateWindow(window);
|
||||
winuser::InvalidateRgn(window, ptr::null_mut(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
platform_impl,
|
||||
};
|
||||
|
||||
pub use crate::icon::*;
|
||||
pub use crate::icon::{BadIcon, Icon};
|
||||
|
||||
/// Represents a window.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user