Compare commits

...

25 Commits

Author SHA1 Message Date
Christian Duerr
7a9c17a520 Bump version to 0.22.0 (#1500)
There are two PRs I'm aware of that should be relatively trivial to get
merged, which would fix some issues. Other than those, I don't think it
makes sense to wait on anything.

 - Fix Windows crash: https://github.com/rust-windowing/winit/pull/1459
 - Fix macOS mouse reports: https://github.com/rust-windowing/winit/pull/1490

While #1459 seems pretty essential to actually make winit run, #1490 is
much less important and can probably be ignored if there aren't any
resources to merge it.
2020-03-09 16:58:54 -04:00
Kirill Chibisov
b208daa271 Revert "on MacOS, Fix not sending ReceivedCharacter event for s… (#1501)
This reverts commit 9daa0738a9.

This commit introduced other bug #1453 with likely much more common bindings,
so reverting it for now.

Fixes #1453.

Co-authored-by: Osspial <osspial@gmail.com>
2020-03-09 16:57:04 -04:00
Imberflur
e85a80dd65 Fix freeze when pressing modifier keys on Windows (#1503) 2020-03-08 01:22:53 -05:00
Osspial
b1d8ce24e9 Use i32 instead of u32 for position type in WindowEvent::Moved (#1502)
* Use i32 instead of u32 for position type in WindowEvent::Moved

* Mark change as breaking
2020-03-08 00:21:04 -05:00
David Hewitt
098fd5d602 Add ability to create Icons from embedded resources on Windows (#1410)
* Add IconExtWindows trait

* Move changelog entries to unreleased category

Co-authored-by: Osspial <osspial@gmail.com>
2020-03-07 14:42:21 -05:00
Philippe Renon
2f27f64cdb On Windows, fix request_redraw() related panics (#1461)
* On Windows, fix request_redraw() related panics

These panics were introduced by 6a330a2894

Fixes https://github.com/rust-windowing/winit/issues/1391
Fixes https://github.com/rust-windowing/winit/issues/1400
Fixes https://github.com/rust-windowing/winit/issues/1466
Probably fixes other related issues

See https://github.com/rust-windowing/winit/issues/1429

* On Windows, replace all calls to UpdateWindow by calls to InvalidateRgn

This avoids directly sending a WM_PAINT message,
which might cause buffering of RedrawRequested events.

We don't want to buffer RedrawRequested events because:
- we wan't to handle RedrawRequested during processing of WM_PAINT messages
- state transitionning is broken when handling buffered RedrawRequested events

Fixes https://github.com/rust-windowing/winit/issues/1469

* On Windows, panic if we are trying to buffer a RedrawRequested event

* On Windows, move modal loop jumpstart to set_modal_loop() method

This fixes a panic.
Note that the WM_PAINT event is now sent to the modal_redraw_method
which is more correct and avoids an unecessary redraw of the window.

Relates to but does does not fix https://github.com/rust-windowing/winit/issues/1484

* On Window, filter by paint messages when draining paint messages

This seems to prevent PeekMessage from dispatching unrelated sent messages

* Change recently added panic/assert calls with warn calls

This makes the code less panicky...

And actually, winit's Windoww callbacks should not panic
because the panic will unwind into Windows code.

It is currently undefined behavior to unwind from Rust code into foreign code.
See https://doc.rust-lang.org/std/panic/fn.catch_unwind.html

* add comments to clarify WM_PAINT handling in non modal loop

* made redraw_events_cleared more explicit and more comments
2020-03-07 14:04:24 -05:00
Christian Duerr
cbb60d29a2 Remove assertions from Windows dark mode code (#1459)
* Remove assertions from Windows dark mode code

In general, winit should never assert on anything unless it means that
it is impossible to continue the execution of the program. There are
several assertions in the Windows dark mode code where this is not the
case.

Based on surface level inspection, all existing assertions could be
easily replaced with just simple conditional checks, allowing the
execution of the program to proceed with sane default values.

Fixes #1458.

* Add changelog entry

* Format code

* Pass dark mode by mutable reference

* Format code

* Return bool instead of mutable reference

* Fix dark mode success reply

Co-Authored-By: daxpedda <daxpedda@gmail.com>

* Fix dark mode success reply

* Replace magic integers with constants

Co-authored-by: daxpedda <daxpedda@gmail.com>
2020-03-07 13:56:33 -05:00
Murarth
e707052f66 Move ModifiersChanged variant to WindowEvent (#1381)
* Move `ModifiersChanged` variant to `WindowEvent`

* macos: Fix flags_changed for ModifiersChanged variant move

I haven't look too deep at what this does internally, but at least
cargo-check is fully happy now. :)

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>

* macos: Fire a ModifiersChanged event on window_did_resign_key

From debugging, I determined that macOS' emission of a flagsChanged
around window switching is inconsistent.  It is fair to assume, I think,
that when the user switches windows, they do not expect their former
modifiers state to remain effective; so I think it's best to clear that
state by sending a ModifiersChanged(ModifiersState::empty()).

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>

* windows: Fix build

I don't know enough about the code to implement the fix as it is done on
this branch, but this commit at least fixes the build.

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>

* windows: Send ModifiersChanged(ModifiersState::empty) on KILLFOCUS

Very similar to the changes made in [1], as focus is lost, send an event
to the window indicating that the modifiers have been released.

It's unclear to me (without a Windows device to test this on) whether
this is necessary, but it certainly ensures that unfocused windows will
have at least received this event, which is an improvement.

[1]: f79f21641a

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>

* macos: Add a hook to update stale modifiers

Sometimes, `ViewState` and `event` might have different values for their
stored `modifiers` flags.  These are internally stored as a bitmask in
the latter and an enum in the former.

We can check to see if they differ, and if they do, automatically
dispatch an event to update consumers of modifier state as well as the
stored `state.modifiers`.  That's what the hook does.

This hook is then called in the key_down, mouse_entered, mouse_exited,
mouse_click, scroll_wheel, and pressure_change_with_event callbacks,
which each will contain updated modifiers.

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>

* Only call event_mods once when determining whether to update state

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>

* flags_changed: Memoize window_id collection

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>

* window_did_resign_key: Remove synthetic ModifiersChanged event

We no longer need to emit this event, since we are checking the state of
our modifiers before emitting most other events.

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>

* mouse_motion: Add a call to update_potentially_stale_modifiers

Now, cover all events (that I can think of, at least) where stale
modifiers might affect how user programs behave.  Effectively, every
human-interface event (keypress, mouse click, keydown, etc.) will cause
a ModifiersChanged event to be fired if something has changed.

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>

* key_up: Add a call to update_potentially_stale_modifiers

We also want to make sure modifiers state is synchronized here, too.

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>

* mouse_motion: Remove update_potentially_stale_modifiers invocation

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>

* Retry CI

* ViewState: Promote visibility of modifiers to the macos impl

This is so that we can interact with the ViewState directly from the
WindowDelegate.

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>

* window_delegate: Synthetically set modifiers state to empty on resignKey

This logic is implemented similarly on other platforms, so we wish to
regain parity here.  Originally this behavior was implemented to always
fire an event with ModifiersState::empty(), but that was not the best as
it was not necessarily correct and could be a duplicate event.

This solution is perhaps the most elegant possible to implement the
desired behavior of sending a synthetic empty modifiers event when a
window loses focus, trading some safety for interoperation between the
NSWindowDelegate and the NSView (as the objc runtime must now be
consulted in order to acquire access to the ViewState which is "owned"
by the NSView).

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>

* Check for modifiers change in window events

* Fix modifier changed on macOS

Since the `mouse_entered` function was generating a mouse motion, which
updates the modifier state, a modifiers changed event was incorrectly
generated.

The updating of the modifier state has also been changed to make sure it
consistently happens before events that have a modifier state attached
to it, without happening on any other event.

This of course means that no `CursorMoved` event is generated anymore
when the user enters the window without it being focused, however I'd
say that is consistent with how winit should behave.

* Fix unused variable warning

* Move changelog entry into `Unreleased` section

Co-authored-by: Freya Gentz <zegentzy@protonmail.com>
Co-authored-by: Kristofer Rye <kristofer.rye@gmail.com>
Co-authored-by: Christian Duerr <contact@christianduerr.com>
2020-03-06 15:43:55 -07:00
Philippe Renon
71bd6e73ca windows: ignore spurious mouse move messages (#1435)
Fixes https://github.com/rust-windowing/winit/issues/1428
2020-03-06 16:15:49 -05:00
Philippe Renon
b8326f6452 In control_flow example, don't schedule a new WaitUntil if wait was cancelled (#1482) 2020-03-06 10:48:54 -07:00
HeroicKatora
ece2e70a53 Update image to 0.23 (#1485)
Also makes use of a few ergonomics improvements that were introduced or
optimized in the more recent version.
2020-03-03 16:13:53 -07:00
Murarth
2b14ec23d5 Fix GitHub Actions (#1479)
* Replaces `actions/checkout@v1` with `actions/checkout@v2` to get a bug fix
2020-02-25 09:10:31 -07:00
Murarth
9999f53329 X11: Fix deadlock when an error occurs during startup (#1475) 2020-02-19 10:38:59 -07:00
Philippe Renon
522a6e3298 fix issues in wait_until_time_or_msg function (#1423)
also removed unused return value
2020-02-18 19:27:47 -05:00
Kirill Chibisov
76d0dd7ec3 On Wayland, Hide CSD for fullscreen windows (#1473) 2020-02-18 16:58:48 -07:00
daxpedda
d1073dcecb Implement ThemeChanged for web target. (#1462)
* Implement ThemeChanged for web target.

* Add TODO upstream to stdweb.

Co-authored-by: Ryan G <ryanisaacg@users.noreply.github.com>
2020-02-17 14:25:27 -05:00
Héctor Ramón
e88e8bc194 Map UserEvent properly in Event::to_static (#1468) 2020-02-16 10:53:02 -07:00
Philippe Renon
bc29931434 Add an example that calls request_redraw() from a thread (#1467)
reproduces https://github.com/rust-windowing/winit/issues/1466
2020-02-15 11:38:29 -07:00
Philippe Renon
505f312d5f Add new example that demonstrates the different control flow schemes (#1460)
User can switch between Wait, WaitUntil and Poll modes with key '1', '2' and '3' respectivly.
User can toggle request_redraw calls with the 'R' key.

Helpful for testing all control flow modes and use of request_redraw.
2020-02-13 15:20:32 -07:00
Philippe Renon
f0093d3c54 rename dpi_factor to scale_factor where appropriate (#1463)
fixes https://github.com/rust-windowing/winit/issues/1457
2020-02-13 12:41:41 -07:00
Kirill Chibisov
83b60beba6 on Wayland, Add HiDPI cursor support (#1454)
Fixes #727.
2020-02-12 19:48:58 -07:00
hatoo
5f52d7c9d0 On macOS, Fix set_simple_screen to remember frame excluding title bar (#1430)
* On macOS, Fix `set_simple_screen` to remember frame excluding title bar

* Add CHANGELOG
2020-02-12 11:27:11 +03:00
Julien Sanchez
a1b65f7080 Ignore locale if unsupported by X11 backend (#1445)
This restores default portable 'C' locale when target locale is unsupported
by X11 backend (Xlib).

When target locale is unsupported by X11, some locale-dependent Xlib
functions like `XSetLocaleModifiers` fail or have no effect triggering
later failures and panics.

When target locale is not valid, `setLocale` should normally leave the
locale unchanged (`setLocale` returns 'C'). However, in some situations,
locale is accepted by `setLocale` (`setLocale` returns the new locale)
but the accepted locale is unsupported by Xlib (`XSupportsLocale` returns
`false`).

Fix #636
2020-02-09 22:37:06 -07:00
Kirill Chibisov
96df858961 On Wayland, fix color from close_button_icon_color not applying (#1444) 2020-02-08 19:36:44 -07:00
Kirill Chibisov
4eddd1e5bc On Wayland, fix coordinates in touch events when scale factor isn't 1 (#1439)
* On Wayland, fix coordinates in touch events when scale factor isn't 1

* Explicitly state that Wayland is using LogicalPosition internally

* Fix CHANGELOG
2020-02-08 01:25:08 -07:00
50 changed files with 1258 additions and 745 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -1,4 +1,23 @@
# Unreleased
# 0.22.0 (2020-03-07)
- 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)
@@ -20,7 +39,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.

View File

@@ -1,6 +1,6 @@
[package]
name = "winit"
version = "0.21.0"
version = "0.22.0"
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,7 +77,7 @@ features = [
wayland-client = { version = "0.23.0", features = [ "dlopen", "egl", "cursor", "eventloop"] }
mio = "0.6"
mio-extras = "2.0"
smithay-client-toolkit = "0.6"
smithay-client-toolkit = "^0.6.6"
x11-dl = "2.18.3"
percent-encoding = "2.0"
@@ -101,6 +101,8 @@ features = [
'HtmlCanvasElement',
'HtmlElement',
'KeyboardEvent',
'MediaQueryList',
'MediaQueryListEvent',
'MouseEvent',
'Node',
'PointerEvent',

View File

@@ -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)

View File

@@ -7,7 +7,7 @@
```toml
[dependencies]
winit = "0.21.0"
winit = "0.22.0"
```
## [Documentation](https://docs.rs/winit)

119
examples/control_flow.rs Normal file
View 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
}
};
}
_ => (),
}
});
}

View File

@@ -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,
_ => (),
},
_ => (),

View 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");
}
_ => (),
}
});
}

View File

@@ -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")

View File

@@ -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),
}
}
}

View File

@@ -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,
}

View File

@@ -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)?,
})
}
}

View File

@@ -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] {

View File

@@ -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")]

View File

@@ -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 })
}
}

View File

@@ -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))
}
}

View File

@@ -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,

View File

@@ -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];

View File

@@ -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];

View File

@@ -18,6 +18,8 @@ use crate::{
window::{CursorIcon, Fullscreen, WindowAttributes},
};
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
pub mod wayland;
pub mod x11;
@@ -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]

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}
},

View File

@@ -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,

View File

@@ -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),
);
}
}

View File

@@ -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() {

View File

@@ -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),
});
}
}
}
}

View File

@@ -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)(

View File

@@ -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

View File

@@ -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());

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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_());

View File

@@ -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:`");

View File

@@ -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),
});
});
}
}

View File

@@ -44,3 +44,5 @@ pub use self::window::{
Id as WindowId, PlatformSpecificBuilderAttributes as PlatformSpecificWindowBuilderAttributes,
Window,
};
pub(crate) use crate::icon::NoIcon as PlatformIcon;

View File

@@ -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,

View File

@@ -10,6 +10,7 @@ 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> {

View File

@@ -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,

View File

@@ -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> {

View File

@@ -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> {

View File

@@ -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(),
}),
});
}

View File

@@ -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
),
}
}

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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);
}
}

View File

@@ -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 {

View File

@@ -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);
}
}

View File

@@ -9,7 +9,7 @@ use crate::{
platform_impl,
};
pub use crate::icon::*;
pub use crate::icon::{BadIcon, Icon};
/// Represents a window.
///