Compare commits

...

25 Commits

Author SHA1 Message Date
Osspial
627a127f1b we did it bois (#1352) 2020-01-05 17:11:25 -05:00
Murarth
ec1ae68cfc X11: Properly update window size constraints on DPI change (#1356)
* In `WindowBuilderExtUnix` methods, use `Size` instead of `LogicalSize`
2020-01-05 17:04:31 -05:00
Osspial
3aa3880e69 Add changelog entry 2020-01-05 16:57:32 -05:00
Osspial
a1b8d265d0 Refine DPI docs 2020-01-05 16:34:37 -05:00
Osspial
9b122c3804 Update the DPI module docs (#1349)
* Update the DPI module docs

* Fix HiDpiFactorChanged doc link

* Incorporate lokathor and icefox feedback

* Adjust documented desktop resolution range

* X11 is one of the reasons I use Windows

* Address DPI generics and float->int rounding

* Revise DPI value statement to better reflect best practices

* Address some of freya's feedback

* phrasing

* Rephrase X11 DPI stuff
2020-01-05 14:15:12 -05:00
Osspial
28b82fb9aa Try to fix iOS build 2020-01-05 14:15:12 -05:00
Murarth
7753bbba94 Fix examples 2020-01-05 14:15:12 -05:00
Murarth
ac69a9c0dc Silence warnings about use of deprecated fields 2020-01-05 14:15:12 -05:00
Osspial
d29f7f34aa Rename hidpi_factor to scale_factor (#1334)
* Rename hidpi_factor to scale_factor

* Deprecate WINIT_HIDPI_FACTOR environment variable in favor of WINIT_X11_SCALE_FACTOR

* Rename HiDpiFactorChanged to DpiChanged and update docs

I'm renaming it to DpiChanged instead of ScaleFactorChanged, since I'd
like Winit to expose the raw DPI value at some point in the near future,
and DpiChanged is a more apt name for that purpose.

* Format

* Fix macos and ios again

* Fix bad macos rebase
2020-01-05 14:15:12 -05:00
Osspial
85ea3f1d5d Use i32 in Position::Physical (#1350)
* Use i32 in Position::Physical

* Fix multithreaded example

* format
2020-01-05 14:15:12 -05:00
Osspial
55166da437 Remove Option from HiDpiFactorChanged in favor of a bare PhysicalSize (#1346)
* Remove Option from HiDpiFactorChanged in favor of a bare PhysicalSize

* Fix macos and ios builds
2020-01-05 14:15:12 -05:00
Michael Tang
777d9edeaa Implement hidpi for web platform (#1233)
* fix: use a 'static lifetime for the web backend's `Event` types

* implement hidpi for stdweb (web-sys wip?)

* fix: make all canvas resizes go through backend::set_canvas_size

* update Window docs for web, make `inner/outer_position` return the position in the viewport
2020-01-05 14:15:11 -05:00
Antonino Siena
28a20aec10 Dpi Type conversions into/from arrays (#1283)
* Added array conversion methods

* Cargo fmt

* Undo wrong fmt
2020-01-05 14:15:11 -05:00
Osspial
3a1e694c2f Make size/position types generic over pixel type (#1277)
* Begin implementing DPI generics

* Fix multithreaded example

* Format

* Fix serde test

* hopefully fix most of the errors

* Fix dpi module errors

* More error fixings

* Format

* fix macos errors

* Another error pass

* Replace bad type signatures

* more fixins
2020-01-05 14:15:11 -05:00
Bogaevsky
b16042a047 iOS: Dpi overhaul (#1223)
* WIP - Make EL2 DPI changes and implement on Windows (#895)

* Modify DPI API publicly and on Windows

* Add generic Position and make dpi creation functions const

* Make examples work

* Fix fullscreen windows not appearing

* Replace Logical coordinates in window events with Physical coordinates

* Update HiDpiFactorChanged

* Document to_static

* On Windows, make AdjustRect calls DPI-aware when possible (#1015)

* Use AdjustWidowRectExForDPI when available

* Prioritize presevering logical size when handling WM_DPICHANGED

* Format

* Add changelog entry

* macOS: Dpi overhaul (#997)

* WIP - Make EL2 DPI changes and implement on Windows (#895)

* Modify DPI API publicly and on Windows

* Add generic Position and make dpi creation functions const

* Make examples work

* Fix fullscreen windows not appearing

* Replace Logical coordinates in window events with Physical coordinates

* Update HiDpiFactorChanged

* Document to_static

* fix app_state errors

* fixes hidpi related errors in window_delegate

* fix bad merge

* dpi_factor edits in window_delegate

* fixes type and lifetime errors in window and window_delegate

* applies fmt

* complies with @aleksijuvani requested changes

* modifies Handler lifetimes

* fixes lifetime isues, adds propper handling for HiDpiChanged

* applies fmt

* restore original lifetimes

* solution is somewhere out there

* applies fmt

* pass as references

* resolves issue with HANDLER

* crate visible type error

* fixes visibility issues

* applies fmt

* deals with warnings

* simplifies new_inner_size setting algorthm

* moves proxy instead of referencing it and removes double deref from proxy.ns_window

* makes @Osspial tests (https://github.com/rust-windowing/winit/pull/997\#discussion_r301852354) pass

* complies with @aleksijuvani suggested changes

* makes max window size std::f32::MAX

* On Windows, fix new DPI API not setting window size properly (#1130)

* First attempt

* Second attempt

* Maintain cursor horizontal ratio

* Fix DPI change handling when maximized

* Revert window example

* Make new DPI code more understandable

* Format

* Implement DPI Usability Upgrades for X11 and Wayland (#1098)

* Fix compile errors

* Use `mio` for the X11 event loop

* Removes `calloop` from the X11 event loop, as the method of draining a
  source using a closure provided to the `calloop::EventLoop` instance
  conflicts with the need to deliver events directly to the callback
  provided to `EventLoop::run`, in order to respond to the value provided by
  `WindowEvent::HiDpiFactorChanged`.

* Implement interactive `HiDpiFactorChanged` event for X11

* Implement interactive `HiDpiFactorChanged` event for Wayland

* Run cargo fmt

* Fix Wayland not processing events from EventQueue

* Backport #981

* some lifetime tinkering

* finishes lifetime tinkering

* fixes all type errors

* adds support ffi functions

* adds wrappers for nonstatic events

* replaces events with event wrappers

* reimplementing hidpichanged event in app_state

* implements HiDpiFactorChanged for iOS

* applies formatter

* complies with @aleksijuvani requested changes

* resolves conflicts

* applies fmt

* removes merge blurp

* corrects state of CHANGELOG

* fix fmt check error

* fixes hidpi_factor for armv7-apple-ios
2020-01-05 14:15:11 -05:00
Osspial
cbf61e5cb9 Fix window rectangle change being in wrong changelog entry 2020-01-05 14:15:11 -05:00
Vladimir Bogaevsky
077ee4d851 macOS: Dpi overhaul (#997) (and rebase changes)
* WIP - Make EL2 DPI changes and implement on Windows (#895)

* Modify DPI API publicly and on Windows

* Add generic Position and make dpi creation functions const

* Make examples work

* Fix fullscreen windows not appearing

* Replace Logical coordinates in window events with Physical coordinates

* Update HiDpiFactorChanged

* Document to_static

* fix app_state errors

* fixes hidpi related errors in window_delegate

* fix bad merge

* dpi_factor edits in window_delegate

* fixes type and lifetime errors in window and window_delegate

* applies fmt

* complies with @aleksijuvani requested changes

* modifies Handler lifetimes

* fixes lifetime isues, adds propper handling for HiDpiChanged

* applies fmt

* restore original lifetimes

* solution is somewhere out there

* applies fmt

* pass as references

* resolves issue with HANDLER

* crate visible type error

* fixes visibility issues

* applies fmt

* deals with warnings

* simplifies new_inner_size setting algorthm

* moves proxy instead of referencing it and removes double deref from proxy.ns_window

* makes @Osspial tests (https://github.com/rust-windowing/winit/pull/997\#discussion_r301852354) pass

* complies with @aleksijuvani suggested changes

* makes max window size std::f32::MAX

Changes from rebasing:

* fixes compile errors

* applies fmt

* reimplements HiDpiFactorChanged after #1173 merge

* uses EventWrappers
2020-01-05 14:15:11 -05:00
Murarth
7b43b0bc94 Implement DPI Usability Upgrades for X11 and Wayland (#1098)
* Fix compile errors

* Use `mio` for the X11 event loop

* Removes `calloop` from the X11 event loop, as the method of draining a
  source using a closure provided to the `calloop::EventLoop` instance
  conflicts with the need to deliver events directly to the callback
  provided to `EventLoop::run`, in order to respond to the value provided by
  `WindowEvent::HiDpiFactorChanged`.

* Implement interactive `HiDpiFactorChanged` event for X11

* Implement interactive `HiDpiFactorChanged` event for Wayland

* Run cargo fmt

* Fix Wayland not processing events from EventQueue

* Backport #981
2020-01-05 14:15:11 -05:00
Osspial
6bb7db7c11 On Windows, fix new DPI API not setting window size properly (#1130)
* First attempt

* Second attempt

* Maintain cursor horizontal ratio

* Fix DPI change handling when maximized

* Revert window example

* Make new DPI code more understandable

* Format
2020-01-05 14:15:11 -05:00
Osspial
6ffd78767f On Windows, make AdjustRect calls DPI-aware when possible (#1015)
* Use AdjustWidowRectExForDPI when available

* Prioritize presevering logical size when handling WM_DPICHANGED

* Format

* Add changelog entry
2020-01-05 14:15:11 -05:00
Osspial
f379d069b9 WIP - Make EL2 DPI changes and implement on Windows (#895)
* Modify DPI API publicly and on Windows

* Add generic Position and make dpi creation functions const

* Make examples work

* Fix fullscreen windows not appearing

* Replace Logical coordinates in window events with Physical coordinates

* Update HiDpiFactorChanged

* Document to_static
2020-01-05 14:15:11 -05:00
Osspial
2da24089de Replace Appveyor and Travis with Github Actions (#1309)
* Experiment with github actions

* Only use stable for testing simplicity

* Disable fail-fast

* Never fail when rustup target add fails

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Update rust.yml

* Split formatting check into separate job

* Update and rename rust.yml to ci.yml

* Attempt to add web support

* Fix things

* Fixings

* The "I'm not familiar with YAML" update

* The empty string update

* Fix target interpolation

* Add gcc-multilib on linux x86

* Update ci.yml

* Update ci.yml

* Update ci.yml

* Update ci.yml

* Update ci.yml

* Update ci.yml

* Use correct host on Windows GNU

* Update ci.yml

* Update ci.yml

* in my defense it was like 2 AM when I wrote this

* Update ci.yml

* Update ci.yml

* Update ci.yml

* Update ci.yml

* try caching

* Update ci.yml

* Update ci.yml

* Update on

* Update ci.yml

* Update ci.yml

* Remove travis and appveyor testing

* Cache entire cargo folder

* Make cargo cache key more appropriately named

* Reduce key collisions

* Make key work

* Add publish workflow and path qualifiers

* Remove -f in cargo install cargo-web

* continue-on-error for cargo web

* ping

* Try to shorten matrix

* attempt two

* attempt three

* attempt four

* Use bash

* web feature formatting

* ping
2020-01-05 14:13:05 -05:00
icefoxen
57f29aa6d7 Added some "how" and "why" docs to event handling. (#1032)
* Added some "how" and "why" docs to event handling.

Basically I had these questions when I started exploring the new
event API's, and as I figured out the answers I put down more info
about how everything works.  This is not final, and suggestions
are welcome -- the code example in the `event` module docs is
particularly dubious, but it's how I'm used to thinking abou things
so it only made sense to me once I wrote that.

Note that my bias is towards using winit for writing games, so that's
the sort of things I was interested in.  This may not be valid for
more general use cases.

* cargo fmt

* Fix minor typos

* Revise event documentation

* Update lib.rs docs

* Update root docs

Co-authored-by: Osspial <osspial@gmail.com>
2020-01-05 11:02:41 -05:00
Osspial
028d3ec16d Make examples set control_flow in a more realistic way (#1363)
* Make examples set control_flow in a more realistic way

* Format
2020-01-05 02:12:03 -05:00
Christian Duerr
d1c6506865 Fix ModifiersChanged event on X11 (#1358) 2020-01-03 22:11:00 -07:00
83 changed files with 2790 additions and 2099 deletions

107
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,107 @@
name: CI
on:
pull_request:
paths:
- '**.rs'
- '**.toml'
- '.github/workflows/ci.yml'
push:
branches: [master]
paths:
- '**.rs'
- '**.toml'
- '.github/workflows/ci.yml'
jobs:
Check_Formatting:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: hecrj/setup-rust-action@v1
with:
rust-version: stable
components: rustfmt
- name: Check Formatting
run: cargo +stable fmt --all -- --check
Tests:
strategy:
fail-fast: false
matrix:
rust_version: [stable, nightly]
platform:
- { target: x86_64-pc-windows-msvc, os: windows-latest, }
- { target: i686-pc-windows-msvc, os: windows-latest, }
- { target: x86_64-pc-windows-gnu, os: windows-latest, host: -x86_64-pc-windows-gnu }
- { target: i686-pc-windows-gnu, os: windows-latest, host: -i686-pc-windows-gnu }
- { target: i686-unknown-linux-gnu, os: ubuntu-latest, }
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, }
- { target: x86_64-apple-darwin, os: macos-latest, }
- { target: x86_64-apple-ios, os: macos-latest, }
- { target: armv7-apple-ios, os: macos-latest, }
- { target: aarch64-apple-ios, os: macos-latest, }
# We're using Windows rather than Ubuntu to run the wasm tests because caching cargo-web
# doesn't currently work on Linux.
- { target: wasm32-unknown-unknown, os: windows-latest, features: stdweb, web: web }
- { target: wasm32-unknown-unknown, os: windows-latest, features: web-sys, web: web }
env:
RUST_BACKTRACE: 1
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-C debuginfo=0"
FEATURES: ${{ format(',{0}', matrix.platform.features ) }}
WEB: ${{ matrix.platform.web }}
runs-on: ${{ matrix.platform.os }}
steps:
- uses: actions/checkout@v1
# Used to cache cargo-web
- name: Cache cargo folder
uses: actions/cache@v1
with:
path: ~/.cargo
key: ${{ matrix.platform.target }}-cargo-${{ matrix.rust_version }}
- uses: hecrj/setup-rust-action@v1
with:
rust-version: ${{ matrix.rust_version }}${{ matrix.platform.host }}
targets: ${{ matrix.platform.target }}
- name: Install GCC Multilib
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
run: sudo apt-get update && sudo apt-get install gcc-multilib
- name: Install cargo-web
continue-on-error: true
if: contains(matrix.platform.target, 'wasm32')
run: cargo install cargo-web
- name: Check documentation
shell: bash
if: matrix.platform.target != 'wasm32-unknown-unknown'
run: cargo doc --no-deps --target ${{ matrix.platform.target }} --features $FEATURES
- name: Build
shell: bash
run: cargo $WEB build --verbose --target ${{ matrix.platform.target }} --features $FEATURES
- name: Build tests
shell: bash
run: cargo $WEB test --no-run --verbose --target ${{ matrix.platform.target }} --features $FEATURES
- name: Run tests
shell: bash
if: (!contains(matrix.platform.target, 'ios') && !contains(matrix.platform.target, 'wasm32'))
run: cargo $WEB test --verbose --target ${{ matrix.platform.target }} --features $FEATURES
- name: Build with serde enabled
shell: bash
run: cargo $WEB build --verbose --target ${{ matrix.platform.target }} --features serde,$FEATURES
- name: Build tests with serde enabled
shell: bash
run: cargo $WEB test --no-run --verbose --target ${{ matrix.platform.target }} --features serde,$FEATURES
- name: Run tests with serde enabled
shell: bash
if: (!contains(matrix.platform.target, 'ios') && !contains(matrix.platform.target, 'wasm32'))
run: cargo $WEB test --verbose --target ${{ matrix.platform.target }} --features serde,$FEATURES

18
.github/workflows/publish.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: Publish
on:
push:
branches: [master]
paths: "Cargo.toml"
jobs:
Publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: hecrj/setup-rust-action@v1
with:
rust-version: stable
components: rustfmt
- name: Publish to crates.io
run: cargo publish --token ${{ secrets.cratesio_token }}

View File

@@ -1,104 +0,0 @@
language: rust
matrix:
include:
# Linux 32bit
- env: TARGET=i686-unknown-linux-gnu
os: linux
rust: nightly
addons:
apt:
# Cross compiler and cross compiled C libraries
packages: &i686_packages
- gcc-multilib
- env: TARGET=i686-unknown-linux-gnu
os: linux
rust: stable
addons:
apt:
packages: *i686_packages
# Linux 64bit
- env: TARGET=x86_64-unknown-linux-gnu
os: linux
rust: nightly
- env: TARGET=x86_64-unknown-linux-gnu
os: linux
rust: stable
# macOS
- env: TARGET=x86_64-apple-darwin
os: osx
rust: nightly
- env: TARGET=x86_64-apple-darwin
os: osx
rust: stable
# iOS x86_64
- env: TARGET=x86_64-apple-ios
os: osx
rust: nightly
- env: TARGET=x86_64-apple-ios
os: osx
rust: stable
# iOS armv7
- env: TARGET=armv7-apple-ios
os: osx
rust: nightly
- env: TARGET=armv7-apple-ios
os: osx
rust: stable
# iOS arm64
- env: TARGET=aarch64-apple-ios
os: osx
rust: nightly
- env: TARGET=aarch64-apple-ios
os: osx
rust: stable
# wasm stdweb
- env: TARGET=wasm32-unknown-unknown WEB=web FEATURES=stdweb
os: linux
rust: stable
- env: TARGET=wasm32-unknown-unknown WEB=web FEATURES=stdweb
os: linux
rust: nightly
# wasm web-sys
- env: TARGET=wasm32-unknown-unknown FEATURES=web-sys
os: linux
rust: stable
- env: TARGET=wasm32-unknown-unknown FEATURES=web-sys
os: linux
rust: nightly
install:
- rustup self update
- rustup target add $TARGET; true
- rustup toolchain install stable
- rustup component add rustfmt --toolchain stable
script:
- cargo +stable fmt --all -- --check
# Ensure that the documentation builds properly.
- cargo doc --no-deps
# Install cargo-web to build stdweb
- if [[ $WEB = "web" ]]; then cargo install -f cargo-web; fi
# Build without serde then with serde
- if [[ -z "$FEATURES" ]]; then
cargo $WEB build --target $TARGET --verbose;
else
cargo $WEB build --target $TARGET --features $FEATURES --verbose;
fi
- cargo $WEB build --target $TARGET --features serde,$FEATURES --verbose
# Running iOS apps on macOS requires the Simulator so we skip that for now
# The web targets also don't support running tests
- if [[ $TARGET != *-apple-ios && $TARGET != wasm32-* ]]; then cargo test --target $TARGET --verbose; fi
- if [[ $TARGET != *-apple-ios && $TARGET != wasm32-* ]]; then cargo test --target $TARGET --features serde --verbose; fi
after_success:
- |
[ $TRAVIS_BRANCH = master ] &&
[ $TRAVIS_PULL_REQUEST = false ] &&
cargo publish --token ${CRATESIO_TOKEN}

View File

@@ -1,5 +1,17 @@
# Unreleased
# 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.
+ `hidpi_factor` has been renamed to `scale_factor`.
+ `HiDpiFactorChanged` has been renamed to `ScaleFactorChanged`, and lets you control how the OS
resizes the window in response to the change.
+ On X11, deprecate `WINIT_HIDPI_FACTOR` environment variable in favor of `WINIT_X11_SCALE_FACTOR`.
+ `Size` and `Position` types are now generic over their exact pixel type.
# 0.20.0 Alpha 6 (2020-01-03)
- On macOS, fix `set_cursor_visible` hides cursor outside of window.
@@ -89,6 +101,7 @@
reduces the potential for cross-platform compatibility gotchyas.
- On Windows and Linux X11/Wayland, add platform-specific functions for creating an `EventLoop` outside the main thread.
- On Wayland, drop resize events identical to the current window size.
- On Windows, fix window rectangle not getting set correctly on high-DPI systems.
# 0.20.0 Alpha 3 (2019-08-14)

View File

@@ -1,6 +1,6 @@
[package]
name = "winit"
version = "0.20.0-alpha6"
version = "0.20.0"
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library."
edition = "2018"
@@ -42,7 +42,7 @@ cocoa = "0.19.1"
core-foundation = "0.6"
core-graphics = "0.17.3"
dispatch = "0.1.4"
objc = "0.2.3"
objc = "0.2.6"
[target.'cfg(target_os = "macos")'.dependencies.core-video-sys]
version = "0.1.3"
@@ -75,7 +75,8 @@ features = [
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
wayland-client = { version = "0.23.0", features = [ "dlopen", "egl", "cursor", "eventloop"] }
calloop = "0.4.2"
mio = "0.6"
mio-extras = "2.0"
smithay-client-toolkit = "0.6"
x11-dl = "2.18.3"
percent-encoding = "2.0"
@@ -89,6 +90,7 @@ version = "0.3.22"
optional = true
features = [
'console',
'CssStyleDeclaration',
'BeforeUnloadEvent',
'Document',
'DomRect',

View File

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

View File

@@ -1,35 +0,0 @@
environment:
matrix:
- TARGET: x86_64-pc-windows-msvc
CHANNEL: stable
- TARGET: i686-pc-windows-msvc
CHANNEL: stable
- TARGET: x86_64-pc-windows-gnu
CHANNEL: stable
- TARGET: i686-pc-windows-gnu
CHANNEL: stable
- TARGET: x86_64-pc-windows-msvc
CHANNEL: nightly
- TARGET: i686-pc-windows-msvc
CHANNEL: nightly
- TARGET: x86_64-pc-windows-gnu
CHANNEL: nightly
- TARGET: i686-pc-windows-gnu
CHANNEL: nightly
matrix:
allow_failures:
- CHANNEL: nightly
install:
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
- rustup-init -yv --default-toolchain %CHANNEL% --default-host %TARGET%
- SET PATH=%PATH%;%USERPROFILE%\.cargo\bin
- SET PATH=%PATH%;C:\MinGW\bin
- rustc -V
- cargo -V
build: false
test_script:
- cargo test --verbose
- cargo test --features serde --verbose
- cargo doc --no-deps

View File

@@ -12,35 +12,39 @@ fn main() {
let mut cursor_idx = 0;
event_loop.run(move |event, _, control_flow| match event {
Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
..
},
..
},
..
} => {
println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]);
window.set_cursor_icon(CURSORS[cursor_idx]);
if cursor_idx < CURSORS.len() - 1 {
cursor_idx += 1;
} else {
cursor_idx = 0;
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
..
},
..
},
..
} => {
println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]);
window.set_cursor_icon(CURSORS[cursor_idx]);
if cursor_idx < CURSORS.len() - 1 {
cursor_idx += 1;
} else {
cursor_idx = 0;
}
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
*control_flow = ControlFlow::Exit;
return;
}
_ => (),
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
*control_flow = ControlFlow::Exit;
return;
}
_ => (),
});
}

View File

@@ -16,6 +16,7 @@ fn main() {
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,

View File

@@ -31,13 +31,17 @@ fn main() {
}
});
event_loop.run(move |event, _, control_flow| match event {
Event::UserEvent(event) => println!("user event: {:?}", event),
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => *control_flow = ControlFlow::Wait,
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::UserEvent(event) => println!("user event: {:?}", event),
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => (),
}
});
}

View File

@@ -14,6 +14,7 @@ fn main() {
window.set_max_inner_size(Some(LogicalSize::new(800.0, 400.0)));
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
println!("{:?}", event);
match event {
@@ -21,7 +22,7 @@ fn main() {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => *control_flow = ControlFlow::Wait,
_ => (),
}
});
}

View File

@@ -12,24 +12,28 @@ fn main() {
.build(&event_loop)
.unwrap();
event_loop.run(move |event, _, control_flow| match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
// Keyboard input event to handle minimize via a hotkey
Event::WindowEvent {
event: WindowEvent::KeyboardInput { input, .. },
window_id,
} => {
if window_id == window.id() {
// Pressing the 'M' key will minimize the window
if input.virtual_keycode == Some(VirtualKeyCode::M) {
window.set_minimized(true);
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
// Keyboard input event to handle minimize via a hotkey
Event::WindowEvent {
event: WindowEvent::KeyboardInput { input, .. },
window_id,
} => {
if window_id == window.id() {
// Pressing the 'M' key will minimize the window
if input.virtual_keycode == Some(VirtualKeyCode::M) {
window.set_minimized(true);
}
}
}
_ => (),
}
_ => *control_flow = ControlFlow::Wait,
});
}

View File

@@ -5,20 +5,21 @@ fn main() {
use std::{collections::HashMap, sync::mpsc, thread, time::Duration};
use winit::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::{CursorIcon, Fullscreen, WindowBuilder},
};
const WINDOW_COUNT: usize = 3;
const WINDOW_SIZE: (u32, u32) = (600, 400);
const WINDOW_SIZE: PhysicalSize<u32> = PhysicalSize::new(600, 400);
env_logger::init();
let event_loop = EventLoop::new();
let mut window_senders = HashMap::with_capacity(WINDOW_COUNT);
for _ in 0..WINDOW_COUNT {
let window = WindowBuilder::new()
.with_inner_size(WINDOW_SIZE.into())
.with_inner_size(WINDOW_SIZE)
.build(&event_loop)
.unwrap();
@@ -49,6 +50,7 @@ fn main() {
);
}
}
#[allow(deprecated)]
WindowEvent::KeyboardInput {
input:
KeyboardInput {
@@ -101,31 +103,38 @@ fn main() {
println!("-> fullscreen : {:?}", window.fullscreen());
}
L => window.set_min_inner_size(match state {
true => Some(WINDOW_SIZE.into()),
true => Some(WINDOW_SIZE),
false => None,
}),
M => window.set_maximized(state),
P => window.set_outer_position({
let mut position = window.outer_position().unwrap();
let sign = if state { 1.0 } else { -1.0 };
position.x += 10.0 * sign;
position.y += 10.0 * sign;
let sign = if state { 1 } else { -1 };
position.x += 10 * sign;
position.y += 10 * sign;
position
}),
Q => window.request_redraw(),
R => window.set_resizable(state),
S => window.set_inner_size(
match state {
true => (WINDOW_SIZE.0 + 100, WINDOW_SIZE.1 + 100),
false => WINDOW_SIZE,
S => window.set_inner_size(match state {
true => PhysicalSize::new(
WINDOW_SIZE.width + 100,
WINDOW_SIZE.height + 100,
),
false => WINDOW_SIZE,
}),
W => {
if let Size::Physical(size) = WINDOW_SIZE.into() {
window
.set_cursor_position(Position::Physical(
PhysicalPosition::new(
size.width as i32 / 2,
size.height as i32 / 2,
),
))
.unwrap()
}
.into(),
),
W => window
.set_cursor_position(
(WINDOW_SIZE.0 as i32 / 2, WINDOW_SIZE.1 as i32 / 2).into(),
)
.unwrap(),
}
Z => {
window.set_visible(false);
thread::sleep(Duration::from_secs(1));
@@ -161,7 +170,9 @@ fn main() {
}
_ => {
if let Some(tx) = window_senders.get(&window_id) {
tx.send(event).unwrap();
if let Some(event) = event.to_static() {
tx.send(event).unwrap();
}
}
}
},

View File

@@ -16,6 +16,7 @@ fn main() {
event_loop.run(move |event, event_loop, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent { event, window_id } => {
match event {

View File

@@ -1,8 +1,5 @@
use instant::Instant;
use std::time::Duration;
use winit::{
event::{Event, WindowEvent},
event::{ElementState, Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
@@ -15,18 +12,26 @@ fn main() {
.build(&event_loop)
.unwrap();
event_loop.run(move |event, _, control_flow| match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
Event::MainEventsCleared => {
window.request_redraw();
*control_flow = ControlFlow::WaitUntil(Instant::now() + Duration::new(1, 0))
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,
WindowEvent::MouseInput {
state: ElementState::Released,
..
} => {
window.request_redraw();
}
_ => (),
},
Event::RedrawRequested(_) => {
println!("\nredrawing!\n");
}
_ => (),
}
Event::RedrawRequested(_) => {
println!("{:?}", event);
}
_ => (),
});
}

View File

@@ -1,4 +1,5 @@
use winit::{
dpi::LogicalSize,
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
@@ -11,13 +12,14 @@ fn main() {
let window = WindowBuilder::new()
.with_title("Hit space to toggle resizability.")
.with_inner_size((400, 200).into())
.with_inner_size(LogicalSize::new(400.0, 200.0))
.with_resizable(resizable)
.build(&event_loop)
.unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,

View File

@@ -16,6 +16,7 @@ fn main() {
window.set_title("A fantastic window!");
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
println!("{:?}", event);
match event {
@@ -23,7 +24,7 @@ fn main() {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => *control_flow = ControlFlow::Wait,
_ => (),
}
});
}

View File

@@ -40,6 +40,8 @@ pub fn main() {
}
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
#[cfg(feature = "web-sys")]
log::debug!("{:?}", event);
@@ -54,7 +56,7 @@ pub fn main() {
Event::MainEventsCleared => {
window.request_redraw();
}
_ => *control_flow = ControlFlow::Wait,
_ => (),
}
});
}

View File

@@ -9,10 +9,12 @@ fn main() {
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.build(&event_loop)
.unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
println!("{:?}", event);
match event {
@@ -23,7 +25,7 @@ fn main() {
Event::MainEventsCleared => {
window.request_redraw();
}
_ => *control_flow = ControlFlow::Poll,
_ => (),
}
});
}

View File

@@ -12,20 +12,18 @@ fn main() {
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.with_inner_size(LogicalSize::from((100, 100)))
.with_inner_size(LogicalSize::new(100.0, 100.0))
.build(&event_loop)
.unwrap();
eprintln!("debugging keys:");
eprintln!(" (E) Enter exclusive fullscreen");
eprintln!(" (F) Toggle borderless fullscreen");
#[cfg(waiting_for_set_minimized)]
eprintln!(" (M) Toggle minimized");
eprintln!(" (Q) Quit event loop");
eprintln!(" (V) Toggle visibility");
eprintln!(" (X) Toggle maximized");
#[cfg(waiting_for_set_minimized)]
let mut minimized = false;
let mut maximized = false;
let mut visible = true;
@@ -43,7 +41,6 @@ fn main() {
}),
..
} => match key {
#[cfg(waiting_for_set_minimized)]
VirtualKeyCode::M => {
if minimized {
minimized = !minimized;
@@ -68,16 +65,15 @@ fn main() {
..
} => match key {
VirtualKeyCode::E => {
fn area(size: PhysicalSize) -> f64 {
fn area(size: PhysicalSize<u32>) -> u32 {
size.width * size.height
}
let monitor = window.current_monitor();
if let Some(mode) = monitor.video_modes().max_by(|a, b| {
area(a.size())
.partial_cmp(&area(b.size()))
.expect("NaN in video mode size")
}) {
if let Some(mode) = monitor
.video_modes()
.max_by(|a, b| area(a.size()).cmp(&area(b.size())))
{
window.set_fullscreen(Some(Fullscreen::Exclusive(mode)));
} else {
eprintln!("no video modes available");
@@ -91,7 +87,6 @@ fn main() {
window.set_fullscreen(Some(Fullscreen::Borderless(monitor)));
}
}
#[cfg(waiting_for_set_minimized)]
VirtualKeyCode::M => {
minimized = !minimized;
window.set_minimized(minimized);

View File

@@ -27,6 +27,7 @@ fn main() {
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
if let Event::WindowEvent { event, .. } = event {
use winit::event::WindowEvent::*;
match event {

View File

@@ -38,7 +38,6 @@ fn main() {
..
} => {
quit = true;
*control_flow = ControlFlow::Exit;
}
Event::MainEventsCleared => {
*control_flow = ControlFlow::Exit;
@@ -48,6 +47,7 @@ fn main() {
});
// Sleep for 1/60 second to simulate rendering
println!("rendering");
sleep(Duration::from_millis(16));
}
}

View File

@@ -1,331 +1,498 @@
//! DPI is important, so read the docs for this module if you don't want to be confused.
//! UI scaling is important, so read the docs for this module if you don't want to be confused.
//!
//! Originally, `winit` dealt entirely in physical pixels (excluding unintentional inconsistencies), but now all
//! window-related functions both produce and consume logical pixels. Monitor-related functions still use physical
//! pixels, as do any context-related functions in `glutin`.
//! ## Why should I care about UI scaling?
//!
//! If you've never heard of these terms before, then you're not alone, and this documentation will explain the
//! concepts.
//! Modern computer screens don't have a consistent relationship between resolution and size.
//! 1920x1080 is a common resolution for both desktop and mobile screens, despite mobile screens
//! normally being less than a quarter the size of their desktop counterparts. What's more, neither
//! desktop nor mobile screens are consistent resolutions within their own size classes - common
//! mobile screens range from below 720p to above 1440p, and desktop screens range from 720p to 5K
//! and beyond.
//!
//! Modern screens have a defined physical resolution, most commonly 1920x1080. Indepedent of that is the amount of
//! space the screen occupies, which is to say, the height and width in millimeters. The relationship between these two
//! measurements is the *pixel density*. Mobile screens require a high pixel density, as they're held close to the
//! eyes. Larger displays also require a higher pixel density, hence the growing presence of 1440p and 4K displays.
//! Given that, it's a mistake to assume that 2D content will only be displayed on screens with
//! a consistent pixel density. If you were to render a 96-pixel-square image on a 1080p screen,
//! then render the same image on a similarly-sized 4K screen, the 4K rendition would only take up
//! about a quarter of the physical space as it did on the 1080p screen. That issue is especially
//! problematic with text rendering, where quarter-sized text becomes a significant legibility
//! problem.
//!
//! So, this presents a problem. Let's say we want to render a square 100px button. It will occupy 100x100 of the
//! screen's pixels, which in many cases, seems perfectly fine. However, because this size doesn't account for the
//! screen's dimensions or pixel density, the button's size can vary quite a bit. On a 4K display, it would be unusably
//! small.
//! Failure to account for the scale factor can create a significantly degraded user experience.
//! Most notably, it can make users feel like they have bad eyesight, which will potentially cause
//! them to think about growing elderly, resulting in them having an existential crisis. Once users
//! enter that state, they will no longer be focused on your application.
//!
//! That's a description of what happens when the button is 100x100 *physical* pixels. Instead, let's try using 100x100
//! *logical* pixels. To map logical pixels to physical pixels, we simply multiply by the DPI (dots per inch) factor.
//! On a "typical" desktop display, the DPI factor will be 1.0, so 100x100 logical pixels equates to 100x100 physical
//! pixels. However, a 1440p display may have a DPI factor of 1.25, so the button is rendered as 125x125 physical pixels.
//! Ideally, the button now has approximately the same perceived size across varying displays.
//! ## How should I handle it?
//!
//! Failure to account for the DPI factor can create a badly degraded user experience. Most notably, it can make users
//! feel like they have bad eyesight, which will potentially cause them to think about growing elderly, resulting in
//! them entering an existential panic. Once users enter that state, they will no longer be focused on your application.
//! The solution to this problem is to account for the device's *scale factor*. The scale factor is
//! the factor UI elements should be scaled by to be consistent with the rest of the user's system -
//! for example, a button that's normally 50 pixels across would be 100 pixels across on a device
//! with a scale factor of `2.0`, or 75 pixels across with a scale factor of `1.5`.
//!
//! There are two ways to get the DPI factor:
//! - You can track the [`HiDpiFactorChanged`](crate::event::WindowEvent::HiDpiFactorChanged) event of your
//! windows. This event is sent any time the DPI factor changes, either because the window moved to another monitor,
//! or because the user changed the configuration of their screen.
//! - You can also retrieve the DPI factor of a monitor by calling
//! [`MonitorHandle::hidpi_factor`](crate::monitor::MonitorHandle::hidpi_factor), or the
//! current DPI factor applied to a window by calling
//! [`Window::hidpi_factor`](crate::window::Window::hidpi_factor), which is roughly equivalent
//! to `window.current_monitor().hidpi_factor()`.
//! Many UI systems, such as CSS, expose DPI-dependent units like [points] or [picas]. That's
//! usually a mistake, since there's no consistent mapping between the scale factor and the screen's
//! actual DPI. Unless you're printing to a physical medium, you should work in scaled pixels rather
//! than any DPI-dependent units.
//!
//! Depending on the platform, the window's actual DPI factor may only be known after
//! the event loop has started and your window has been drawn once. To properly handle these cases,
//! the most robust way is to monitor the [`HiDpiFactorChanged`](crate::event::WindowEvent::HiDpiFactorChanged)
//! event and dynamically adapt your drawing logic to follow the DPI factor.
//! ### Position and Size types
//!
//! Here's an overview of what sort of DPI factors you can expect, and where they come from:
//! - **Windows:** On Windows 8 and 10, per-monitor scaling is readily configured by users from the display settings.
//! While users are free to select any option they want, they're only given a selection of "nice" DPI factors, i.e.
//! 1.0, 1.25, 1.5... on Windows 7, the DPI factor is global and changing it requires logging out.
//! - **macOS:** The buzzword is "retina displays", which have a DPI factor of 2.0. Otherwise, the DPI factor is 1.0.
//! Intermediate DPI factors are never used, thus 1440p displays/etc. aren't properly supported. It's possible for any
//! display to use that 2.0 DPI factor, given the use of the command line.
//! - **X11:** On X11, we calculate the DPI factor based on the millimeter dimensions provided by XRandR. This can
//! result in a wide range of possible values, including some interesting ones like 1.0833333333333333. This can be
//! overridden using the `WINIT_HIDPI_FACTOR` environment variable, though that's not recommended.
//! - **Wayland:** On Wayland, DPI factors are set per-screen by the server, and are always integers (most often 1 or 2).
//! - **iOS:** DPI factors are both constant and device-specific on iOS.
//! - **Android:** This feature isn't yet implemented on Android, so the DPI factor will always be returned as 1.0.
//! - **Web:** DPI factors are handled by the browser and will always be 1.0 for your application.
//! Winit's `Physical(Position|Size)` types correspond with the actual pixels on the device, and the
//! `Logical(Position|Size)` types correspond to the physical pixels divided by the scale factor.
//! All of Winit's functions return physical types, but can take either logical or physical
//! coordinates as input, allowing you to use the most convenient coordinate system for your
//! particular application.
//!
//! The window's logical size is conserved across DPI changes, resulting in the physical size changing instead. This
//! may be surprising on X11, but is quite standard elsewhere. Physical size changes always produce a
//! [`Resized`](crate::event::WindowEvent::Resized) event, even on platforms where no resize actually occurs,
//! such as macOS and Wayland. As a result, it's not necessary to separately handle
//! [`HiDpiFactorChanged`](crate::event::WindowEvent::HiDpiFactorChanged) if you're only listening for size.
//! Winit's position and size types types are generic over their exact pixel type, `P`, to allow the
//! API to have integer precision where appropriate (e.g. most window manipulation functions) and
//! floating precision when necessary (e.g. logical sizes for fractional scale factors and touch
//! input). If `P` is a floating-point type, please do not cast the values with `as {int}`. Doing so
//! will truncate the fractional part of the float, rather than properly round to the nearest
//! integer. Use the provided `cast` function or `From`/`Into` conversions, which handle the
//! rounding properly. Note that precision loss will still occur when rounding from a float to an
//! int, although rounding lessens the problem.
//!
//! Your GPU has no awareness of the concept of logical pixels, and unless you like wasting pixel density, your
//! framebuffer's size should be in physical pixels.
//! ### Events
//!
//! `winit` will send [`Resized`](crate::event::WindowEvent::Resized) events whenever a window's logical size
//! changes, and [`HiDpiFactorChanged`](crate::event::WindowEvent::HiDpiFactorChanged) events
//! whenever the DPI factor changes. Receiving either of these events means that the physical size of your window has
//! changed, and you should recompute it using the latest values you received for each. If the logical size and the
//! DPI factor change simultaneously, `winit` will send both events together; thus, it's recommended to buffer
//! these events and process them at the end of the queue.
//! Winit will dispatch a [`ScaleFactorChanged`](crate::event::WindowEvent::ScaleFactorChanged)
//! event whenever a window's scale factor has changed. This can happen if the user drags their
//! window from a standard-resolution monitor to a high-DPI monitor, or if the user changes their
//! DPI settings. This gives you a chance to rescale your application's UI elements and adjust how
//! the platform changes the window's size to reflect the new scale factor. If a window hasn't
//! received a [`ScaleFactorChanged`](crate::event::WindowEvent::ScaleFactorChanged) event,
//! then its scale factor is `1.0`.
//!
//! If you never received any [`HiDpiFactorChanged`](crate::event::WindowEvent::HiDpiFactorChanged) events,
//! then your window's DPI factor is 1.
//! ## How is the scale factor calculated?
//!
//! Scale factor is calculated differently on different platforms:
//!
//! - **Windows:** On Windows 8 and 10, per-monitor scaling is readily configured by users from the
//! display settings. While users are free to select any option they want, they're only given a
//! selection of "nice" scale factors, i.e. 1.0, 1.25, 1.5... on Windows 7, the scale factor is
//! global and changing it requires logging out. See [this article][windows_1] for technical
//! details.
//! - **macOS:** "retina displays" have a scale factor of 2.0. Otherwise, the scale factor is 1.0.
//! Intermediate scale factors are never used. It's possible for any display to use that 2.0 scale
//! factor, given the use of the command line.
//! - **X11:** Many man-hours have been spent trying to figure out how to handle DPI in X11. Winit
//! currently uses a three-pronged approach:
//! + Use the value in the `WINIT_X11_SCALE_FACTOR` environment variable, if present.
//! + If not present, use the value set in `Xft.dpi` in Xresources.
//! + Otherwise, calcuate the scale factor based on the millimeter monitor dimensions provided by XRandR.
//!
//! If `WINIT_X11_SCALE_FACTOR` is set to `randr`, it'll ignore the `Xft.dpi` field and use the
//! XRandR scaling method. Generally speaking, you should try to configure the standard system
//! variables to do what you want before resorting to `WINIT_X11_SCALE_FACTOR`.
//! - **Wayland:** On Wayland, scale factors are set per-screen by the server, and are always
//! integers (most often 1 or 2).
//! - **iOS:** Scale factors are set by Apple to the value that best suits the device, and range
//! from `1.0` to `3.0`. See [this article][apple_1] and [this article][apple_2] for more
//! information.
//! - **Android:** Scale factors are set by the manufacturer to the value that best suits the
//! device, and range from `1.0` to `4.0`. See [this article][android_1] for more information.
//! - **Web:** The scale factor is the ratio between CSS pixels and the physical device pixels.
//!
//! [points]: https://en.wikipedia.org/wiki/Point_(typography)
//! [picas]: https://en.wikipedia.org/wiki/Pica_(typography)
//! [windows_1]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows
//! [apple_1]: https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html
//! [apple_2]: https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/image-size-and-resolution/
//! [android_1]: https://developer.android.com/training/multiscreen/screendensities
/// Checks that the DPI factor is a normal positive `f64`.
pub trait Pixel: Copy + Into<f64> {
fn from_f64(f: f64) -> Self;
fn cast<P: Pixel>(self) -> P {
P::from_f64(self.into())
}
}
impl Pixel for u8 {
fn from_f64(f: f64) -> Self {
f.round() as u8
}
}
impl Pixel for u16 {
fn from_f64(f: f64) -> Self {
f.round() as u16
}
}
impl Pixel for u32 {
fn from_f64(f: f64) -> Self {
f.round() as u32
}
}
impl Pixel for i8 {
fn from_f64(f: f64) -> Self {
f.round() as i8
}
}
impl Pixel for i16 {
fn from_f64(f: f64) -> Self {
f.round() as i16
}
}
impl Pixel for i32 {
fn from_f64(f: f64) -> Self {
f.round() as i32
}
}
impl Pixel for f32 {
fn from_f64(f: f64) -> Self {
f as f32
}
}
impl Pixel for f64 {
fn from_f64(f: f64) -> Self {
f
}
}
/// Checks that the scale factor is a normal positive `f64`.
///
/// All functions that take a DPI factor assert that this will return `true`. If you're sourcing DPI factors from
/// All functions that take a scale factor assert that this will return `true`. If you're sourcing scale factors from
/// 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_hidpi_factor(dpi_factor: f64) -> bool {
pub fn validate_scale_factor(dpi_factor: f64) -> bool {
dpi_factor.is_sign_positive() && dpi_factor.is_normal()
}
/// A position represented in logical pixels.
///
/// The position is stored as floats, so please be careful. Casting floats to integers truncates the fractional part,
/// which can cause noticable issues. To help with that, an `Into<(i32, i32)>` implementation is provided which
/// does the rounding for you.
/// The position is stored as floats, so please be careful. Casting floats to integers truncates the
/// fractional part, which can cause noticable issues. To help with that, an `Into<(i32, i32)>`
/// implementation is provided which does the rounding for you.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LogicalPosition {
pub x: f64,
pub y: f64,
pub struct LogicalPosition<P> {
pub x: P,
pub y: P,
}
impl LogicalPosition {
impl<P> LogicalPosition<P> {
#[inline]
pub fn new(x: f64, y: f64) -> Self {
pub const fn new(x: P, y: P) -> Self {
LogicalPosition { x, y }
}
}
impl<P: Pixel> LogicalPosition<P> {
#[inline]
pub fn from_physical<T: Into<PhysicalPosition>>(physical: T, dpi_factor: f64) -> Self {
pub fn from_physical<T: Into<PhysicalPosition<X>>, X: Pixel>(
physical: T,
dpi_factor: f64,
) -> Self {
physical.into().to_logical(dpi_factor)
}
#[inline]
pub fn to_physical(&self, dpi_factor: f64) -> PhysicalPosition {
assert!(validate_hidpi_factor(dpi_factor));
let x = self.x * dpi_factor;
let y = self.y * dpi_factor;
PhysicalPosition::new(x, y)
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;
PhysicalPosition::new(x, y).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> LogicalPosition<X> {
LogicalPosition {
x: self.x.cast(),
y: self.y.cast(),
}
}
}
impl From<(f64, f64)> for LogicalPosition {
#[inline]
fn from((x, y): (f64, f64)) -> Self {
Self::new(x, y)
impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalPosition<P> {
fn from((x, y): (X, X)) -> LogicalPosition<P> {
LogicalPosition::new(x.cast(), y.cast())
}
}
impl From<(i32, i32)> for LogicalPosition {
#[inline]
fn from((x, y): (i32, i32)) -> Self {
Self::new(x as f64, y as f64)
impl<P: Pixel, X: Pixel> Into<(X, X)> for LogicalPosition<P> {
fn into(self: Self) -> (X, X) {
(self.x.cast(), self.y.cast())
}
}
impl Into<(f64, f64)> for LogicalPosition {
#[inline]
fn into(self) -> (f64, f64) {
(self.x, self.y)
impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalPosition<P> {
fn from([x, y]: [X; 2]) -> LogicalPosition<P> {
LogicalPosition::new(x.cast(), y.cast())
}
}
impl Into<(i32, i32)> for LogicalPosition {
/// Note that this rounds instead of truncating.
#[inline]
fn into(self) -> (i32, i32) {
(self.x.round() as _, self.y.round() as _)
impl<P: Pixel, X: Pixel> Into<[X; 2]> for LogicalPosition<P> {
fn into(self: Self) -> [X; 2] {
[self.x.cast(), self.y.cast()]
}
}
/// A position represented in physical pixels.
///
/// The position is stored as floats, so please be careful. Casting floats to integers truncates the fractional part,
/// which can cause noticable issues. To help with that, an `Into<(i32, i32)>` implementation is provided which
/// does the rounding for you.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PhysicalPosition {
pub x: f64,
pub y: f64,
pub struct PhysicalPosition<P> {
pub x: P,
pub y: P,
}
impl PhysicalPosition {
impl<P> PhysicalPosition<P> {
#[inline]
pub fn new(x: f64, y: f64) -> Self {
pub const fn new(x: P, y: P) -> Self {
PhysicalPosition { x, y }
}
}
impl<P: Pixel> PhysicalPosition<P> {
#[inline]
pub fn from_logical<T: Into<LogicalPosition>>(logical: T, dpi_factor: f64) -> Self {
pub fn from_logical<T: Into<LogicalPosition<X>>, X: Pixel>(
logical: T,
dpi_factor: f64,
) -> Self {
logical.into().to_physical(dpi_factor)
}
#[inline]
pub fn to_logical(&self, dpi_factor: f64) -> LogicalPosition {
assert!(validate_hidpi_factor(dpi_factor));
let x = self.x / dpi_factor;
let y = self.y / dpi_factor;
LogicalPosition::new(x, y)
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;
LogicalPosition::new(x, y).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalPosition<X> {
PhysicalPosition {
x: self.x.cast(),
y: self.y.cast(),
}
}
}
impl From<(f64, f64)> for PhysicalPosition {
#[inline]
fn from((x, y): (f64, f64)) -> Self {
Self::new(x, y)
impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalPosition<P> {
fn from((x, y): (X, X)) -> PhysicalPosition<P> {
PhysicalPosition::new(x.cast(), y.cast())
}
}
impl From<(i32, i32)> for PhysicalPosition {
#[inline]
fn from((x, y): (i32, i32)) -> Self {
Self::new(x as f64, y as f64)
impl<P: Pixel, X: Pixel> Into<(X, X)> for PhysicalPosition<P> {
fn into(self: Self) -> (X, X) {
(self.x.cast(), self.y.cast())
}
}
impl Into<(f64, f64)> for PhysicalPosition {
#[inline]
fn into(self) -> (f64, f64) {
(self.x, self.y)
impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalPosition<P> {
fn from([x, y]: [X; 2]) -> PhysicalPosition<P> {
PhysicalPosition::new(x.cast(), y.cast())
}
}
impl Into<(i32, i32)> for PhysicalPosition {
/// Note that this rounds instead of truncating.
#[inline]
fn into(self) -> (i32, i32) {
(self.x.round() as _, self.y.round() as _)
impl<P: Pixel, X: Pixel> Into<[X; 2]> for PhysicalPosition<P> {
fn into(self: Self) -> [X; 2] {
[self.x.cast(), self.y.cast()]
}
}
/// A size represented in logical pixels.
///
/// The size is stored as floats, so please be careful. Casting floats to integers truncates the fractional part,
/// which can cause noticable issues. To help with that, an `Into<(u32, u32)>` implementation is provided which
/// does the rounding for you.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LogicalSize {
pub width: f64,
pub height: f64,
pub struct LogicalSize<P> {
pub width: P,
pub height: P,
}
impl LogicalSize {
impl<P> LogicalSize<P> {
#[inline]
pub fn new(width: f64, height: f64) -> Self {
pub const fn new(width: P, height: P) -> Self {
LogicalSize { width, height }
}
}
impl<P: Pixel> LogicalSize<P> {
#[inline]
pub fn from_physical<T: Into<PhysicalSize>>(physical: T, dpi_factor: f64) -> Self {
pub fn from_physical<T: Into<PhysicalSize<X>>, X: Pixel>(physical: T, dpi_factor: f64) -> Self {
physical.into().to_logical(dpi_factor)
}
#[inline]
pub fn to_physical(&self, dpi_factor: f64) -> PhysicalSize {
assert!(validate_hidpi_factor(dpi_factor));
let width = self.width * dpi_factor;
let height = self.height * dpi_factor;
PhysicalSize::new(width, height)
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;
PhysicalSize::new(width, height).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> LogicalSize<X> {
LogicalSize {
width: self.width.cast(),
height: self.height.cast(),
}
}
}
impl From<(f64, f64)> for LogicalSize {
#[inline]
fn from((width, height): (f64, f64)) -> Self {
Self::new(width, height)
impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalSize<P> {
fn from((x, y): (X, X)) -> LogicalSize<P> {
LogicalSize::new(x.cast(), y.cast())
}
}
impl From<(u32, u32)> for LogicalSize {
#[inline]
fn from((width, height): (u32, u32)) -> Self {
Self::new(width as f64, height as f64)
impl<P: Pixel, X: Pixel> Into<(X, X)> for LogicalSize<P> {
fn into(self: LogicalSize<P>) -> (X, X) {
(self.width.cast(), self.height.cast())
}
}
impl Into<(f64, f64)> for LogicalSize {
#[inline]
fn into(self) -> (f64, f64) {
(self.width, self.height)
impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalSize<P> {
fn from([x, y]: [X; 2]) -> LogicalSize<P> {
LogicalSize::new(x.cast(), y.cast())
}
}
impl Into<(u32, u32)> for LogicalSize {
/// Note that this rounds instead of truncating.
#[inline]
fn into(self) -> (u32, u32) {
(self.width.round() as _, self.height.round() as _)
impl<P: Pixel, X: Pixel> Into<[X; 2]> for LogicalSize<P> {
fn into(self: Self) -> [X; 2] {
[self.width.cast(), self.height.cast()]
}
}
/// A size represented in physical pixels.
///
/// The size is stored as floats, so please be careful. Casting floats to integers truncates the fractional part,
/// which can cause noticable issues. To help with that, an `Into<(u32, u32)>` implementation is provided which
/// does the rounding for you.
#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PhysicalSize {
pub width: f64,
pub height: f64,
pub struct PhysicalSize<P> {
pub width: P,
pub height: P,
}
impl PhysicalSize {
impl<P> PhysicalSize<P> {
#[inline]
pub fn new(width: f64, height: f64) -> Self {
pub const fn new(width: P, height: P) -> Self {
PhysicalSize { width, height }
}
}
impl<P: Pixel> PhysicalSize<P> {
#[inline]
pub fn from_logical<T: Into<LogicalSize>>(logical: T, dpi_factor: f64) -> Self {
pub fn from_logical<T: Into<LogicalSize<X>>, X: Pixel>(logical: T, dpi_factor: f64) -> Self {
logical.into().to_physical(dpi_factor)
}
#[inline]
pub fn to_logical(&self, dpi_factor: f64) -> LogicalSize {
assert!(validate_hidpi_factor(dpi_factor));
let width = self.width / dpi_factor;
let height = self.height / dpi_factor;
LogicalSize::new(width, height)
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;
LogicalSize::new(width, height).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalSize<X> {
PhysicalSize {
width: self.width.cast(),
height: self.height.cast(),
}
}
}
impl From<(f64, f64)> for PhysicalSize {
#[inline]
fn from((width, height): (f64, f64)) -> Self {
Self::new(width, height)
impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalSize<P> {
fn from((x, y): (X, X)) -> PhysicalSize<P> {
PhysicalSize::new(x.cast(), y.cast())
}
}
impl From<(u32, u32)> for PhysicalSize {
#[inline]
fn from((width, height): (u32, u32)) -> Self {
Self::new(width as f64, height as f64)
impl<P: Pixel, X: Pixel> Into<(X, X)> for PhysicalSize<P> {
fn into(self: Self) -> (X, X) {
(self.width.cast(), self.height.cast())
}
}
impl Into<(f64, f64)> for PhysicalSize {
#[inline]
fn into(self) -> (f64, f64) {
(self.width, self.height)
impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalSize<P> {
fn from([x, y]: [X; 2]) -> PhysicalSize<P> {
PhysicalSize::new(x.cast(), y.cast())
}
}
impl Into<(u32, u32)> for PhysicalSize {
/// Note that this rounds instead of truncating.
#[inline]
fn into(self) -> (u32, u32) {
(self.width.round() as _, self.height.round() as _)
impl<P: Pixel, X: Pixel> Into<[X; 2]> for PhysicalSize<P> {
fn into(self: Self) -> [X; 2] {
[self.width.cast(), self.height.cast()]
}
}
/// A size that's either physical or logical.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Size {
Physical(PhysicalSize<u32>),
Logical(LogicalSize<f64>),
}
impl Size {
pub fn new<S: Into<Size>>(size: S) -> Size {
size.into()
}
pub fn to_logical<P: Pixel>(&self, dpi_factor: f64) -> LogicalSize<P> {
match *self {
Size::Physical(size) => size.to_logical(dpi_factor),
Size::Logical(size) => size.cast(),
}
}
pub fn to_physical<P: Pixel>(&self, dpi_factor: f64) -> PhysicalSize<P> {
match *self {
Size::Physical(size) => size.cast(),
Size::Logical(size) => size.to_physical(dpi_factor),
}
}
}
impl<P: Pixel> From<PhysicalSize<P>> for Size {
#[inline]
fn from(size: PhysicalSize<P>) -> Size {
Size::Physical(size.cast())
}
}
impl<P: Pixel> From<LogicalSize<P>> for Size {
#[inline]
fn from(size: LogicalSize<P>) -> Size {
Size::Logical(size.cast())
}
}
/// A position that's either physical or logical.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Position {
Physical(PhysicalPosition<i32>),
Logical(LogicalPosition<f64>),
}
impl Position {
pub fn new<S: Into<Position>>(position: S) -> Position {
position.into()
}
pub fn to_logical<P: Pixel>(&self, dpi_factor: f64) -> LogicalPosition<P> {
match *self {
Position::Physical(position) => position.to_logical(dpi_factor),
Position::Logical(position) => position.cast(),
}
}
pub fn to_physical<P: Pixel>(&self, dpi_factor: f64) -> PhysicalPosition<P> {
match *self {
Position::Physical(position) => position.cast(),
Position::Logical(position) => position.to_physical(dpi_factor),
}
}
}
impl<P: Pixel> From<PhysicalPosition<P>> for Position {
#[inline]
fn from(position: PhysicalPosition<P>) -> Position {
Position::Physical(position.cast())
}
}
impl<P: Pixel> From<LogicalPosition<P>> for Position {
#[inline]
fn from(position: LogicalPosition<P>) -> Position {
Position::Logical(position.cast())
}
}

View File

@@ -3,63 +3,119 @@
//! These are sent to the closure given to [`EventLoop::run(...)`][event_loop_run], where they get
//! processed and used to modify the program state. For more details, see the root-level documentation.
//!
//! Some of these events represent different "parts" of a traditional event-handling loop. You could
//! approximate the basic ordering loop of [`EventLoop::run(...)`][event_loop_run] like this:
//!
//! ```rust,ignore
//! let mut control_flow = ControlFlow::Poll;
//! let mut start_cause = StartCause::Init;
//!
//! while control_flow != ControlFlow::Exit {
//! event_handler(NewEvents(start_cause), ..., &mut control_flow);
//!
//! for e in (window events, user events, device events) {
//! event_handler(e, ..., &mut control_flow);
//! }
//! event_handler(MainEventsCleared, ..., &mut control_flow);
//!
//! for w in (redraw windows) {
//! event_handler(RedrawRequested(w), ..., &mut control_flow);
//! }
//! event_handler(RedrawEventsCleared, ..., &mut control_flow);
//!
//! start_cause = wait_if_necessary(control_flow);
//! }
//!
//! event_handler(LoopDestroyed, ..., &mut control_flow);
//! ```
//!
//! This leaves out timing details like `ControlFlow::WaitUntil` but hopefully
//! describes what happens in what order.
//!
//! [event_loop_run]: crate::event_loop::EventLoop::run
use instant::Instant;
use std::path::PathBuf;
use crate::{
dpi::{LogicalPosition, LogicalSize},
dpi::{LogicalPosition, PhysicalPosition, PhysicalSize},
platform_impl,
window::{Theme, WindowId},
};
/// Describes a generic event.
#[derive(Clone, Debug, PartialEq)]
pub enum Event<T> {
///
/// See the module-level docs for more information on the event loop manages each event.
#[derive(Debug, PartialEq)]
pub enum Event<'a, T: 'static> {
/// Emitted when new events arrive from the OS to be processed.
///
/// This event type is useful as a place to put code that should be done before you start
/// processing events, such as updating frame timing information for benchmarking or checking
/// the [`StartCause`][crate::event::StartCause] to see if a timer set by
/// [`ControlFlow::WaitUntil`](crate::event_loop::ControlFlow::WaitUntil) has elapsed.
NewEvents(StartCause),
/// Emitted when the OS sends an event to a winit window.
WindowEvent {
window_id: WindowId,
event: WindowEvent,
event: WindowEvent<'a>,
},
/// Emitted when the OS sends an event to a device.
DeviceEvent {
device_id: DeviceId,
event: DeviceEvent,
},
/// Emitted when an event is sent from [`EventLoopProxy::send_event`](crate::event_loop::EventLoopProxy::send_event)
UserEvent(T),
/// Emitted when new events arrive from the OS to be processed.
NewEvents(StartCause),
/// Emitted when all events (except for `RedrawRequested`) have been reported.
///
/// This event is followed by zero or more instances of `RedrawRequested`
/// and, finally, `RedrawEventsCleared`.
MainEventsCleared,
/// The OS or application has requested that a window be redrawn.
///
/// Emitted only after `MainEventsCleared`.
RedrawRequested(WindowId),
/// Emitted after any `RedrawRequested` events.
///
/// If there are no `RedrawRequested` events, it is reported immediately after
/// `MainEventsCleared`.
RedrawEventsCleared,
/// Emitted when the event loop is being shut down. This is irreversable - if this event is
/// emitted, it is guaranteed to be the last event emitted.
LoopDestroyed,
/// Emitted when the application has been suspended.
Suspended,
/// Emitted when the application has been resumed.
Resumed,
/// Emitted when all of the event loop's input events have been processed and redraw processing
/// is about to begin.
///
/// This event is useful as a place to put your code that should be run after all
/// state-changing events have been handled and you want to do stuff (updating state, performing
/// calculations, etc) that happens as the "main body" of your event loop. If your program draws
/// graphics, it's usually better to do it in response to
/// [`Event::RedrawRequested`](crate::event::Event::RedrawRequested), which gets emitted
/// immediately after this event.
MainEventsCleared,
/// Emitted after `MainEventsCleared` when a window should be redrawn.
///
/// This gets triggered in two scenarios:
/// - The OS has performed an operation that's invalidated the window's contents (such as
/// resizing the window).
/// - The application has explicitly requested a redraw via
/// [`Window::request_redraw`](crate::window::Window::request_redraw).
///
/// During each iteration of the event loop, Winit will aggregate duplicate redraw requests
/// into a single event, to help avoid duplicating rendering work.
RedrawRequested(WindowId),
/// Emitted after all `RedrawRequested` events have been processed and control flow is about to
/// be taken away from the program. If there are no `RedrawRequested` events, it is emitted
/// immediately after `MainEventsCleared`.
///
/// This event is useful for doing any cleanup or bookkeeping work after all the rendering
/// tasks have been completed.
RedrawEventsCleared,
/// Emitted when the event loop is being shut down.
///
/// This is irreversable - if this event is emitted, it is guaranteed to be the last event that
/// gets emitted. You generally want to treat this as an "do on quit" event.
LoopDestroyed,
}
impl<T> Event<T> {
pub fn map_nonuser_event<U>(self) -> Result<Event<U>, Event<T>> {
impl<'a, T> Event<'a, T> {
pub fn map_nonuser_event<U>(self) -> Result<Event<'a, U>, Event<'a, T>> {
use self::Event::*;
match self {
UserEvent(_) => Err(self),
@@ -74,6 +130,26 @@ impl<T> Event<T> {
Resumed => Ok(Resumed),
}
}
/// If the event doesn't contain a reference, turn it into an event with a `'static` lifetime.
/// Otherwise, return `None`.
pub fn to_static(self) -> Option<Event<'static, T>> {
use self::Event::*;
match self {
WindowEvent { window_id, event } => event
.to_static()
.map(|event| WindowEvent { window_id, event }),
UserEvent(_) => None,
DeviceEvent { device_id, event } => Some(DeviceEvent { device_id, event }),
NewEvents(cause) => Some(NewEvents(cause)),
MainEventsCleared => Some(MainEventsCleared),
RedrawRequested(wid) => Some(RedrawRequested(wid)),
RedrawEventsCleared => Some(RedrawEventsCleared),
LoopDestroyed => Some(LoopDestroyed),
Suspended => Some(Suspended),
Resumed => Some(Resumed),
}
}
}
/// Describes the reason the event loop is resuming.
@@ -103,13 +179,13 @@ pub enum StartCause {
}
/// Describes an event from a `Window`.
#[derive(Clone, Debug, PartialEq)]
pub enum WindowEvent {
#[derive(Debug, PartialEq)]
pub enum WindowEvent<'a> {
/// The size of the window has changed. Contains the client area's new dimensions.
Resized(LogicalSize),
Resized(PhysicalSize<u32>),
/// The position of the window has changed. Contains the window's new position.
Moved(LogicalPosition),
Moved(PhysicalPosition<u32>),
/// The window has been requested to close.
CloseRequested,
@@ -166,7 +242,7 @@ pub enum WindowEvent {
/// (x,y) coords in pixels relative to the top-left corner of the window. Because the range of this data is
/// 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: LogicalPosition,
position: PhysicalPosition<i32>,
#[deprecated = "Deprecated in favor of DeviceEvent::ModifiersChanged"]
modifiers: ModifiersState,
},
@@ -216,16 +292,23 @@ pub enum WindowEvent {
/// Touch event has been received
Touch(Touch),
/// The DPI factor of the window has changed.
/// The window's scale factor has changed.
///
/// The following user actions can cause DPI changes:
///
/// * Changing the display's resolution.
/// * Changing the display's DPI factor (e.g. in Control Panel on Windows).
/// * Moving the window to a display with a different DPI factor.
/// * Changing the display's scale factor (e.g. in Control Panel on Windows).
/// * Moving the window to a display with a different scale factor.
///
/// After this event callback has been processed, the window will be resized to whatever value
/// is pointed to by the `new_inner_size` reference. By default, this will contain the size suggested
/// by the OS, but it can be changed to any value.
///
/// For more information about DPI in general, see the [`dpi`](crate::dpi) module.
HiDpiFactorChanged(f64),
ScaleFactorChanged {
scale_factor: f64,
new_inner_size: &'a mut PhysicalSize<u32>,
},
/// The system window theme has changed.
///
@@ -236,6 +319,89 @@ pub enum WindowEvent {
ThemeChanged(Theme),
}
impl<'a> WindowEvent<'a> {
pub fn to_static(self) -> Option<WindowEvent<'static>> {
use self::WindowEvent::*;
match self {
Resized(size) => Some(Resized(size)),
Moved(position) => Some(Moved(position)),
CloseRequested => Some(CloseRequested),
Destroyed => Some(Destroyed),
DroppedFile(file) => Some(DroppedFile(file)),
HoveredFile(file) => Some(HoveredFile(file)),
HoveredFileCancelled => Some(HoveredFileCancelled),
ReceivedCharacter(c) => Some(ReceivedCharacter(c)),
Focused(focused) => Some(Focused(focused)),
KeyboardInput {
device_id,
input,
is_synthetic,
} => Some(KeyboardInput {
device_id,
input,
is_synthetic,
}),
#[allow(deprecated)]
CursorMoved {
device_id,
position,
modifiers,
} => Some(CursorMoved {
device_id,
position,
modifiers,
}),
CursorEntered { device_id } => Some(CursorEntered { device_id }),
CursorLeft { device_id } => Some(CursorLeft { device_id }),
#[allow(deprecated)]
MouseWheel {
device_id,
delta,
phase,
modifiers,
} => Some(MouseWheel {
device_id,
delta,
phase,
modifiers,
}),
#[allow(deprecated)]
MouseInput {
device_id,
state,
button,
modifiers,
} => Some(MouseInput {
device_id,
state,
button,
modifiers,
}),
TouchpadPressure {
device_id,
pressure,
stage,
} => Some(TouchpadPressure {
device_id,
pressure,
stage,
}),
AxisMotion {
device_id,
axis,
value,
} => Some(AxisMotion {
device_id,
axis,
value,
}),
Touch(touch) => Some(Touch(touch)),
ThemeChanged(theme) => Some(ThemeChanged(theme)),
ScaleFactorChanged { .. } => None,
}
}
}
/// Identifier of an input device.
///
/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which
@@ -370,7 +536,7 @@ pub enum TouchPhase {
pub struct Touch {
pub device_id: DeviceId,
pub phase: TouchPhase,
pub location: LogicalPosition,
pub location: PhysicalPosition<f64>,
/// Describes how hard the screen was pressed. May be `None` if the platform
/// does not support pressure sensitivity.
///
@@ -481,7 +647,7 @@ pub enum MouseScrollDelta {
/// Scroll events are expressed as a PixelDelta if
/// supported by the device (eg. a touchpad) and
/// platform.
PixelDelta(LogicalPosition),
PixelDelta(LogicalPosition<f64>),
}
/// Symbolic name for a keyboard key.

View File

@@ -143,7 +143,7 @@ impl<T> EventLoop<T> {
#[inline]
pub fn run<F>(self, event_handler: F) -> !
where
F: 'static + FnMut(Event<T>, &EventLoopWindowTarget<T>, &mut ControlFlow),
F: 'static + FnMut(Event<'_, T>, &EventLoopWindowTarget<T>, &mut ControlFlow),
{
self.event_loop.run(event_handler)
}

View File

@@ -1,6 +1,6 @@
//! Winit allows you to build a window on as many platforms as possible.
//! Winit is a cross-platform window creation and event loop management library.
//!
//! # Building a window
//! # Building windows
//!
//! Before you can build a [`Window`], you first need to build an [`EventLoop`]. This is done with the
//! [`EventLoop::new()`] function.
@@ -15,26 +15,31 @@
//! - Calling [`Window::new(&event_loop)`][window_new].
//! - Calling [`let builder = WindowBuilder::new()`][window_builder_new] then [`builder.build(&event_loop)`][window_builder_build].
//!
//! The first way is the simplest way and will give you default values for everything.
//!
//! The second way allows you to customize the way your [`Window`] will look and behave by modifying
//! the fields of the [`WindowBuilder`] object before you create the [`Window`].
//! The first method is the simplest, and will give you default values for everything. The second
//! method allows you to customize the way your [`Window`] will look and behave by modifying the
//! fields of the [`WindowBuilder`] object before you create the [`Window`].
//!
//! # Event handling
//!
//! Once a [`Window`] has been created, it will generate different *events*. A [`Window`] object can
//! generate a [`WindowEvent`] when certain things happen, like whenever the user moves their mouse
//! or presses a key inside the [`Window`]. Devices can generate a [`DeviceEvent`] directly as well,
//! which contains unfiltered event data that isn't specific to a certain window. Some user
//! activity, like mouse movement, can generate both a [`WindowEvent`] *and* a [`DeviceEvent`]. You
//! can also create and handle your own custom [`UserEvent`]s, if desired.
//! generate [`WindowEvent`]s when certain input events occur, such as a cursor moving over the
//! window or a key getting pressed while the window is focused. Devices can generate
//! [`DeviceEvent`]s, which contain unfiltered event data that isn't specific to a certain window.
//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a
//! [`DeviceEvent`]. You can also create and handle your own custom [`UserEvent`]s, if desired.
//!
//! Events can be retreived by using an [`EventLoop`]. A [`Window`] will send its events to the
//! [`EventLoop`] object it was created with.
//! You can retreive events by calling [`EventLoop::run`][event_loop_run]. This function will
//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and
//! will run until the `control_flow` argument given to the closure is set to
//! [`ControlFlow`]`::`[`Exit`], at which point [`Event`]`::`[`LoopDestroyed`] is emitted and the
//! entire program terminates.
//!
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
//! model, since that can't be implemented properly on web and mobile platforms and works poorly on
//! most desktop platforms. However, this model can be re-implemented to an extent on desktops with
//! [`EventLoopExtDesktop::run_return`]. See that method's documentation for more reasons about why
//! it's discouraged, beyond mobile/web compatibility reasons.
//!
//! You do this by calling [`event_loop.run(...)`][event_loop_run]. This function will run forever
//! unless `control_flow` is set to [`ControlFlow`]`::`[`Exit`], at which point [`Event`]`::`[`LoopDestroyed`]
//! is emitted and the entire program terminates.
//!
//! ```no_run
//! use winit::{
@@ -47,7 +52,23 @@
//! let window = WindowBuilder::new().build(&event_loop).unwrap();
//!
//! event_loop.run(move |event, _, control_flow| {
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
//! // dispatched any events. This is ideal for games and similar applications.
//! *control_flow = ControlFlow::Poll;
//!
//! // ControlFlow::Wait pauses the event loop if no events are available to process.
//! // This is ideal for non-game applications that only update in response to user
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
//! *control_flow = ControlFlow::Wait;
//!
//! match event {
//! Event::WindowEvent {
//! event: WindowEvent::CloseRequested,
//! ..
//! } => {
//! println!("The close button was pressed; stopping");
//! *control_flow = ControlFlow::Exit
//! },
//! Event::MainEventsCleared => {
//! // Application update code.
//!
@@ -61,40 +82,29 @@
//! // rendering in here allows the program to gracefully handle redraws requested
//! // by the OS.
//! },
//! Event::WindowEvent {
//! event: WindowEvent::CloseRequested,
//! ..
//! } => {
//! println!("The close button was pressed; stopping");
//! *control_flow = ControlFlow::Exit
//! },
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
//! // dispatched any events. This is ideal for games and similar applications.
//! _ => *control_flow = ControlFlow::Poll,
//! // ControlFlow::Wait pauses the event loop if no events are available to process.
//! // This is ideal for non-game applications that only update in response to user
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
//! // _ => *control_flow = ControlFlow::Wait,
//! _ => ()
//! }
//! });
//! ```
//!
//! If you use multiple [`Window`]s, [`Event`]`::`[`WindowEvent`] has a member named `window_id`. You can
//! compare it with the value returned by the [`id()`][window_id_fn] method of [`Window`] in order to know which
//! [`Window`] has received the event.
//! [`Event`]`::`[`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be
//! compared to the value returned by [`Window::id()`][window_id_fn] to determine which [`Window`]
//! dispatched the event.
//!
//! # Drawing on the window
//!
//! Winit doesn't provide any function that allows drawing on a [`Window`]. However it allows you to
//! Winit doesn't directly provide any methods for drawing on a [`Window`]. However it allows you to
//! retrieve the raw handle of the window (see the [`platform`] module), which in turn allows you
//! to create an OpenGL/Vulkan/DirectX/Metal/etc. context that will draw on the [`Window`].
//! to create an OpenGL/Vulkan/DirectX/Metal/etc. context that can be used to render graphics.
//!
//! [`EventLoop`]: event_loop::EventLoop
//! [`EventLoopExtDesktop::run_return`]: ./platform/desktop/trait.EventLoopExtDesktop.html#tymethod.run_return
//! [`EventLoop::new()`]: event_loop::EventLoop::new
//! [event_loop_run]: event_loop::EventLoop::run
//! [`ControlFlow`]: event_loop::ControlFlow
//! [`Exit`]: event_loop::ControlFlow::Exit
//! [`Window`]: window::Window
//! [`WindowId`]: window::WindowId
//! [`WindowBuilder`]: window::WindowBuilder
//! [window_new]: window::Window::new
//! [window_builder_new]: window::WindowBuilder::new

View File

@@ -58,7 +58,7 @@ impl Ord for VideoMode {
impl VideoMode {
/// Returns the resolution of this video mode.
#[inline]
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
self.video_mode.size()
}
@@ -133,7 +133,7 @@ impl MonitorHandle {
///
/// - **Web:** Always returns (0,0)
#[inline]
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
self.inner.size()
}
@@ -144,22 +144,22 @@ impl MonitorHandle {
///
/// - **Web:** Always returns (0,0)
#[inline]
pub fn position(&self) -> PhysicalPosition {
pub fn position(&self) -> PhysicalPosition<i32> {
self.inner.position()
}
/// Returns the DPI factor that can be used to map logical pixels to physical pixels, and vice versa.
/// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
///
/// See the [`dpi`](crate::dpi) module for more information.
///
/// ## Platform-specific
///
/// - **X11:** Can be overridden using the `WINIT_HIDPI_FACTOR` environment variable.
/// - **X11:** Can be overridden using the `WINIT_X11_SCALE_FACTOR` environment variable.
/// - **Android:** Always returns 1.0.
/// - **Web:** Always returns 1.0
#[inline]
pub fn hidpi_factor(&self) -> f64 {
self.inner.hidpi_factor()
pub fn scale_factor(&self) -> f64 {
self.inner.scale_factor()
}
/// Returns all fullscreen video modes supported by this monitor.

View File

@@ -30,7 +30,11 @@ pub trait EventLoopExtDesktop {
/// You are strongly encouraged to use `run`, unless the use of this is absolutely necessary.
fn run_return<F>(&mut self, event_handler: F)
where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow);
F: FnMut(
Event<'_, Self::UserEvent>,
&EventLoopWindowTarget<Self::UserEvent>,
&mut ControlFlow,
);
}
impl<T> EventLoopExtDesktop for EventLoop<T> {
@@ -38,7 +42,11 @@ impl<T> EventLoopExtDesktop for EventLoop<T> {
fn run_return<F>(&mut self, event_handler: F)
where
F: FnMut(Event<T>, &EventLoopWindowTarget<T>, &mut ControlFlow),
F: FnMut(
Event<'_, Self::UserEvent>,
&EventLoopWindowTarget<Self::UserEvent>,
&mut ControlFlow,
),
{
self.event_loop.run_return(event_handler)
}

View File

@@ -43,14 +43,14 @@ pub trait WindowExtIOS {
/// [`UIView`]: https://developer.apple.com/documentation/uikit/uiview?language=objc
fn ui_view(&self) -> *mut c_void;
/// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `hidpi_factor`.
/// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`.
///
/// The default value is device dependent, and it's recommended GLES or Metal applications set
/// this to [`MonitorHandle::hidpi_factor()`].
/// this to [`MonitorHandle::scale_factor()`].
///
/// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc
/// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc
fn set_hidpi_factor(&self, hidpi_factor: f64);
fn set_scale_factor(&self, scale_factor: f64);
/// Sets the valid orientations for the [`Window`].
///
@@ -113,8 +113,8 @@ impl WindowExtIOS for Window {
}
#[inline]
fn set_hidpi_factor(&self, hidpi_factor: f64) {
self.window.set_hidpi_factor(hidpi_factor)
fn set_scale_factor(&self, scale_factor: f64) {
self.window.set_scale_factor(scale_factor)
}
#[inline]
@@ -148,14 +148,14 @@ pub trait WindowBuilderExtIOS {
/// [`UIView`]: https://developer.apple.com/documentation/uikit/uiview?language=objc
fn with_root_view_class(self, root_view_class: *const c_void) -> WindowBuilder;
/// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `hidpi_factor`.
/// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`.
///
/// The default value is device dependent, and it's recommended GLES or Metal applications set
/// this to [`MonitorHandle::hidpi_factor()`].
/// this to [`MonitorHandle::scale_factor()`].
///
/// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc
/// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc
fn with_hidpi_factor(self, hidpi_factor: f64) -> WindowBuilder;
fn with_scale_factor(self, scale_factor: f64) -> WindowBuilder;
/// Sets the valid orientations for the [`Window`].
///
@@ -204,8 +204,8 @@ impl WindowBuilderExtIOS for WindowBuilder {
}
#[inline]
fn with_hidpi_factor(mut self, hidpi_factor: f64) -> WindowBuilder {
self.platform_specific.hidpi_factor = Some(hidpi_factor);
fn with_scale_factor(mut self, scale_factor: f64) -> WindowBuilder {
self.platform_specific.scale_factor = Some(scale_factor);
self
}

View File

@@ -128,7 +128,7 @@ pub trait WindowBuilderExtMacOS {
/// Makes the window content appear behind the titlebar.
fn with_fullsize_content_view(self, fullsize_content_view: bool) -> WindowBuilder;
/// Build window with `resizeIncrements` property. Values must not be 0.
fn with_resize_increments(self, increments: LogicalSize) -> WindowBuilder;
fn with_resize_increments(self, increments: LogicalSize<f64>) -> WindowBuilder;
fn with_disallow_hidpi(self, disallow_hidpi: bool) -> WindowBuilder;
}
@@ -179,7 +179,7 @@ impl WindowBuilderExtMacOS for WindowBuilder {
}
#[inline]
fn with_resize_increments(mut self, increments: LogicalSize) -> WindowBuilder {
fn with_resize_increments(mut self, increments: LogicalSize<f64>) -> WindowBuilder {
self.platform_specific.resize_increments = Some(increments.into());
self
}

View File

@@ -5,7 +5,7 @@ use std::{os::raw, ptr, sync::Arc};
use smithay_client_toolkit::window::{ButtonState, Theme};
use crate::{
dpi::LogicalSize,
dpi::Size,
event_loop::{EventLoop, EventLoopWindowTarget},
monitor::MonitorHandle,
window::{Window, WindowBuilder},
@@ -380,9 +380,9 @@ pub trait WindowBuilderExtUnix {
/// Build window with `_GTK_THEME_VARIANT` hint set to the specified value. Currently only relevant on X11.
fn with_gtk_theme_variant(self, variant: String) -> Self;
/// Build window with resize increment hint. Only implemented on X11.
fn with_resize_increments(self, increments: LogicalSize) -> Self;
fn with_resize_increments<S: Into<Size>>(self, increments: S) -> Self;
/// Build window with base size hint. Only implemented on X11.
fn with_base_size(self, base_size: LogicalSize) -> Self;
fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self;
/// Build window with a given application ID. It should match the `.desktop` file distributed with
/// your program. Only relevant on Wayland.
@@ -431,13 +431,13 @@ impl WindowBuilderExtUnix for WindowBuilder {
}
#[inline]
fn with_resize_increments(mut self, increments: LogicalSize) -> Self {
fn with_resize_increments<S: Into<Size>>(mut self, increments: S) -> Self {
self.platform_specific.resize_increments = Some(increments.into());
self
}
#[inline]
fn with_base_size(mut self, base_size: LogicalSize) -> Self {
fn with_base_size<S: Into<Size>>(mut self, base_size: S) -> Self {
self.platform_specific.base_size = Some(base_size.into());
self
}

View File

@@ -61,7 +61,7 @@ impl EventLoop {
while let Ok(event) = self.event_rx.try_recv() {
let e = match event {
android_glue::Event::EventMotion(motion) => {
let dpi_factor = MonitorHandle.hidpi_factor();
let dpi_factor = MonitorHandle.scale_factor();
let location = LogicalPosition::from_physical(
(motion.x as f64, motion.y as f64),
dpi_factor,
@@ -102,7 +102,7 @@ impl EventLoop {
if native_window.is_null() {
None
} else {
let dpi_factor = MonitorHandle.hidpi_factor();
let dpi_factor = MonitorHandle.scale_factor();
let physical_size = MonitorHandle.size();
let size = LogicalSize::from_physical(physical_size, dpi_factor);
Some(Event::WindowEvent {
@@ -193,16 +193,16 @@ impl fmt::Debug for MonitorHandle {
#[derive(Debug)]
struct MonitorHandle {
name: Option<String>,
dimensions: PhysicalSize,
position: PhysicalPosition,
hidpi_factor: f64,
dimensions: PhysicalSize<u32>,
position: PhysicalPosition<i32>,
scale_factor: f64,
}
let monitor_id_proxy = MonitorHandle {
name: self.name(),
dimensions: self.size(),
position: self.outer_position(),
hidpi_factor: self.hidpi_factor(),
scale_factor: self.scale_factor(),
};
monitor_id_proxy.fmt(f)
@@ -216,7 +216,7 @@ impl MonitorHandle {
}
#[inline]
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
unsafe {
let window = android_glue::native_window();
(
@@ -228,13 +228,13 @@ impl MonitorHandle {
}
#[inline]
pub fn outer_position(&self) -> PhysicalPosition {
pub fn outer_position(&self) -> PhysicalPosition<i32> {
// Android assumes single screen
(0, 0).into()
}
#[inline]
pub fn hidpi_factor(&self) -> f64 {
pub fn scale_factor(&self) -> f64 {
1.0
}
}
@@ -283,29 +283,29 @@ impl Window {
}
#[inline]
pub fn outer_position(&self) -> Option<LogicalPosition> {
pub fn outer_position(&self) -> Option<LogicalPosition<f64>> {
// N/A
None
}
#[inline]
pub fn inner_position(&self) -> Option<LogicalPosition> {
pub fn inner_position(&self) -> Option<LogicalPosition<f64>> {
// N/A
None
}
#[inline]
pub fn set_outer_position(&self, _position: LogicalPosition) {
pub fn set_outer_position(&self, _position: LogicalPosition<f64>) {
// N/A
}
#[inline]
pub fn set_min_inner_size(&self, _dimensions: Option<LogicalSize>) {
pub fn set_min_inner_size(&self, _dimensions: Option<LogicalSize<f64>>) {
// N/A
}
#[inline]
pub fn set_max_inner_size(&self, _dimensions: Option<LogicalSize>) {
pub fn set_max_inner_size(&self, _dimensions: Option<LogicalSize<f64>>) {
// N/A
}
@@ -315,29 +315,29 @@ impl Window {
}
#[inline]
pub fn inner_size(&self) -> Option<LogicalSize> {
pub fn inner_size(&self) -> Option<LogicalSize<f64>> {
if self.native_window.is_null() {
None
} else {
let dpi_factor = self.hidpi_factor();
let dpi_factor = self.scale_factor();
let physical_size = self.current_monitor().size();
Some(LogicalSize::from_physical(physical_size, dpi_factor))
}
}
#[inline]
pub fn outer_size(&self) -> Option<LogicalSize> {
pub fn outer_size(&self) -> Option<LogicalSize<f64>> {
self.inner_size()
}
#[inline]
pub fn set_inner_size(&self, _size: LogicalSize) {
pub fn set_inner_size(&self, _size: LogicalSize<f64>) {
// N/A
}
#[inline]
pub fn hidpi_factor(&self) -> f64 {
self.current_monitor().hidpi_factor()
pub fn scale_factor(&self) -> f64 {
self.current_monitor().scale_factor()
}
#[inline]
@@ -356,7 +356,10 @@ impl Window {
}
#[inline]
pub fn set_cursor_position(&self, _position: LogicalPosition) -> Result<(), ExternalError> {
pub fn set_cursor_position(
&self,
_position: LogicalPosition<f64>,
) -> Result<(), ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
@@ -400,7 +403,7 @@ impl Window {
}
#[inline]
pub fn set_ime_position(&self, _spot: LogicalPosition) {
pub fn set_ime_position(&self, _spot: LogicalPosition<f64>) {
// N/A
}

View File

@@ -12,15 +12,16 @@ use std::{
use objc::runtime::{BOOL, YES};
use crate::{
event::{Event, StartCause},
dpi::LogicalSize,
event::{Event, StartCause, WindowEvent},
event_loop::ControlFlow,
platform_impl::platform::{
event_loop::{EventHandler, Never},
event_loop::{EventHandler, EventProxy, EventWrapper, Never},
ffi::{
id, kCFRunLoopCommonModes, CFAbsoluteTimeGetCurrent, CFRelease, CFRunLoopAddTimer,
CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate, CFRunLoopTimerInvalidate,
CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate, NSInteger, NSOperatingSystemVersion,
NSUInteger,
CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate, CGRect, CGSize, NSInteger,
NSOperatingSystemVersion, NSUInteger,
},
},
window::WindowId as RootWindowId,
@@ -45,11 +46,11 @@ enum UserCallbackTransitionResult<'a> {
processing_redraws: bool,
},
ReentrancyPrevented {
queued_events: &'a mut Vec<Event<Never>>,
queued_events: &'a mut Vec<EventWrapper>,
},
}
impl Event<Never> {
impl Event<'static, Never> {
fn is_redraw(&self) -> bool {
if let Event::RedrawRequested(_) = self {
true
@@ -65,12 +66,12 @@ impl Event<Never> {
enum AppStateImpl {
NotLaunched {
queued_windows: Vec<id>,
queued_events: Vec<Event<Never>>,
queued_events: Vec<EventWrapper>,
queued_gpu_redraws: HashSet<id>,
},
Launching {
queued_windows: Vec<id>,
queued_events: Vec<Event<Never>>,
queued_events: Vec<EventWrapper>,
queued_event_handler: Box<dyn EventHandler>,
queued_gpu_redraws: HashSet<id>,
},
@@ -81,7 +82,7 @@ enum AppStateImpl {
},
// special state to deal with reentrancy and prevent mutable aliasing.
InUserCallback {
queued_events: Vec<Event<Never>>,
queued_events: Vec<EventWrapper>,
queued_gpu_redraws: HashSet<id>,
},
ProcessingRedraws {
@@ -222,7 +223,7 @@ impl AppState {
});
}
fn did_finish_launching_transition(&mut self) -> (Vec<id>, Vec<Event<Never>>) {
fn did_finish_launching_transition(&mut self) -> (Vec<id>, Vec<EventWrapper>) {
let (windows, events, event_handler, queued_gpu_redraws) = match self.take_state() {
AppStateImpl::Launching {
queued_windows,
@@ -245,7 +246,7 @@ impl AppState {
(windows, events)
}
fn wakeup_transition(&mut self) -> Option<Event<Never>> {
fn wakeup_transition(&mut self) -> Option<EventWrapper> {
// before `AppState::did_finish_launching` is called, pretend there is no running
// event loop.
if !self.has_launched() {
@@ -258,7 +259,10 @@ impl AppState {
AppStateImpl::PollFinished {
waiting_event_handler,
},
) => (waiting_event_handler, Event::NewEvents(StartCause::Poll)),
) => (
waiting_event_handler,
EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll)),
),
(
ControlFlow::Wait,
AppStateImpl::Waiting {
@@ -267,10 +271,10 @@ impl AppState {
},
) => (
waiting_event_handler,
Event::NewEvents(StartCause::WaitCancelled {
EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled {
start,
requested_resume: None,
}),
})),
),
(
ControlFlow::WaitUntil(requested_resume),
@@ -280,15 +284,15 @@ impl AppState {
},
) => {
let event = if Instant::now() >= requested_resume {
Event::NewEvents(StartCause::ResumeTimeReached {
EventWrapper::StaticEvent(Event::NewEvents(StartCause::ResumeTimeReached {
start,
requested_resume,
})
}))
} else {
Event::NewEvents(StartCause::WaitCancelled {
EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled {
start,
requested_resume: Some(requested_resume),
})
}))
};
(waiting_event_handler, event)
}
@@ -587,7 +591,10 @@ pub unsafe fn did_finish_launching() {
let (windows, events) = AppState::get_mut().did_finish_launching_transition();
let events = std::iter::once(Event::NewEvents(StartCause::Init)).chain(events);
let events = std::iter::once(EventWrapper::StaticEvent(Event::NewEvents(
StartCause::Init,
)))
.chain(events);
handle_nonuser_events(events);
// the above window dance hack, could possibly trigger new windows to be created.
@@ -616,12 +623,12 @@ pub unsafe fn handle_wakeup_transition() {
}
// requires main thread
pub unsafe fn handle_nonuser_event(event: Event<Never>) {
pub unsafe fn handle_nonuser_event(event: EventWrapper) {
handle_nonuser_events(std::iter::once(event))
}
// requires main thread
pub unsafe fn handle_nonuser_events<I: IntoIterator<Item = Event<Never>>>(events: I) {
pub unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(events: I) {
let mut this = AppState::get_mut();
let (mut event_handler, active_control_flow, processing_redraws) =
match this.try_user_callback_transition() {
@@ -638,16 +645,23 @@ pub unsafe fn handle_nonuser_events<I: IntoIterator<Item = Event<Never>>>(events
let mut control_flow = this.control_flow;
drop(this);
for event in events {
if !processing_redraws && event.is_redraw() {
log::info!("processing `RedrawRequested` during the main event loop");
} else if processing_redraws && !event.is_redraw() {
log::warn!(
"processing non `RedrawRequested` event after the main event loop: {:#?}",
event
);
for wrapper in events {
match wrapper {
EventWrapper::StaticEvent(event) => {
if !processing_redraws && event.is_redraw() {
log::info!("processing `RedrawRequested` during the main event loop");
} else if processing_redraws && !event.is_redraw() {
log::warn!(
"processing non `RedrawRequested` event after the main event loop: {:#?}",
event
);
}
event_handler.handle_nonuser_event(event, &mut control_flow)
}
EventWrapper::EventProxy(proxy) => {
handle_event_proxy(&mut event_handler, control_flow, proxy)
}
}
event_handler.handle_nonuser_event(event, &mut control_flow)
}
loop {
@@ -688,16 +702,23 @@ pub unsafe fn handle_nonuser_events<I: IntoIterator<Item = Event<Never>>>(events
}
drop(this);
for event in queued_events {
if !processing_redraws && event.is_redraw() {
log::info!("processing `RedrawRequested` during the main event loop");
} else if processing_redraws && !event.is_redraw() {
log::warn!(
"processing non-`RedrawRequested` event after the main event loop: {:#?}",
event
);
for wrapper in queued_events {
match wrapper {
EventWrapper::StaticEvent(event) => {
if !processing_redraws && event.is_redraw() {
log::info!("processing `RedrawRequested` during the main event loop");
} else if processing_redraws && !event.is_redraw() {
log::warn!(
"processing non-`RedrawRequested` event after the main event loop: {:#?}",
event
);
}
event_handler.handle_nonuser_event(event, &mut control_flow)
}
EventWrapper::EventProxy(proxy) => {
handle_event_proxy(&mut event_handler, control_flow, proxy)
}
}
event_handler.handle_nonuser_event(event, &mut control_flow)
}
}
}
@@ -751,8 +772,15 @@ unsafe fn handle_user_events() {
}
drop(this);
for event in queued_events {
event_handler.handle_nonuser_event(event, &mut control_flow)
for wrapper in queued_events {
match wrapper {
EventWrapper::StaticEvent(event) => {
event_handler.handle_nonuser_event(event, &mut control_flow)
}
EventWrapper::EventProxy(proxy) => {
handle_event_proxy(&mut event_handler, control_flow, proxy)
}
}
}
event_handler.handle_user_events(&mut control_flow);
}
@@ -772,17 +800,19 @@ pub unsafe fn handle_main_events_cleared() {
// User events are always sent out at the end of the "MainEventLoop"
handle_user_events();
handle_nonuser_event(Event::MainEventsCleared);
handle_nonuser_event(EventWrapper::StaticEvent(Event::MainEventsCleared));
let mut this = AppState::get_mut();
let mut redraw_events: Vec<Event<Never>> = this
let mut redraw_events: Vec<EventWrapper> = this
.main_events_cleared_transition()
.into_iter()
.map(|window| Event::RedrawRequested(RootWindowId(window.into())))
.map(|window| {
EventWrapper::StaticEvent(Event::RedrawRequested(RootWindowId(window.into())))
})
.collect();
if !redraw_events.is_empty() {
redraw_events.push(Event::RedrawEventsCleared);
redraw_events.push(EventWrapper::StaticEvent(Event::RedrawEventsCleared));
}
drop(this);
@@ -804,6 +834,66 @@ pub unsafe fn terminated() {
event_handler.handle_nonuser_event(Event::LoopDestroyed, &mut control_flow)
}
fn handle_event_proxy(
event_handler: &mut Box<dyn EventHandler>,
control_flow: ControlFlow,
proxy: EventProxy,
) {
match proxy {
EventProxy::DpiChangedProxy {
suggested_size,
scale_factor,
window_id,
} => handle_hidpi_proxy(
event_handler,
control_flow,
suggested_size,
scale_factor,
window_id,
),
}
}
fn handle_hidpi_proxy(
event_handler: &mut Box<dyn EventHandler>,
mut control_flow: ControlFlow,
suggested_size: LogicalSize<f64>,
scale_factor: f64,
window_id: id,
) {
let mut size = suggested_size.to_physical(scale_factor);
let new_inner_size = &mut size;
let event = Event::WindowEvent {
window_id: RootWindowId(window_id.into()),
event: WindowEvent::ScaleFactorChanged {
scale_factor,
new_inner_size,
},
};
event_handler.handle_nonuser_event(event, &mut control_flow);
let (view, screen_frame) = get_view_and_screen_frame(window_id);
let physical_size = *new_inner_size;
let logical_size = physical_size.to_logical(scale_factor);
let size = CGSize::new(logical_size);
let new_frame: CGRect = CGRect::new(screen_frame.origin, size);
unsafe {
let () = msg_send![view, setFrame: new_frame];
}
}
fn get_view_and_screen_frame(window_id: id) -> (id, CGRect) {
unsafe {
let view_controller: id = msg_send![window_id, rootViewController];
let view: id = msg_send![view_controller, view];
let bounds: CGRect = msg_send![window_id, bounds];
let screen: id = msg_send![window_id, screen];
let screen_space: id = msg_send![screen, coordinateSpace];
let screen_frame: CGRect =
msg_send![window_id, convertRect:bounds toCoordinateSpace:screen_space];
(view, screen_frame)
}
}
struct EventLoopWaker {
timer: CFRunLoopTimerRef,
}

View File

@@ -8,6 +8,7 @@ use std::{
};
use crate::{
dpi::LogicalSize,
event::Event,
event_loop::{
ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootEventLoopWindowTarget,
@@ -28,6 +29,21 @@ use crate::platform_impl::platform::{
monitor, view, MonitorHandle,
};
#[derive(Debug)]
pub enum EventWrapper {
StaticEvent(Event<'static, Never>),
EventProxy(EventProxy),
}
#[derive(Debug, PartialEq)]
pub enum EventProxy {
DpiChangedProxy {
window_id: id,
suggested_size: LogicalSize<f64>,
scale_factor: f64,
},
}
pub struct EventLoopWindowTarget<T: 'static> {
receiver: Receiver<T>,
sender_to_clone: Sender<T>,
@@ -69,7 +85,7 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(self, event_handler: F) -> !
where
F: 'static + FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
{
unsafe {
let application: *mut c_void = msg_send![class!(UIApplication), sharedApplication];
@@ -277,7 +293,7 @@ fn setup_control_flow_observers() {
pub enum Never {}
pub trait EventHandler: Debug {
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow);
fn handle_nonuser_event(&mut self, event: Event<'_, Never>, control_flow: &mut ControlFlow);
fn handle_user_events(&mut self, control_flow: &mut ControlFlow);
}
@@ -296,10 +312,10 @@ impl<F, T: 'static> Debug for EventLoopHandler<F, T> {
impl<F, T> EventHandler for EventLoopHandler<F, T>
where
F: 'static + FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
T: 'static,
{
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow) {
fn handle_nonuser_event(&mut self, event: Event<'_, Never>, control_flow: &mut ControlFlow) {
(self.f)(
event.map_nonuser_event().unwrap(),
&self.event_loop,

View File

@@ -4,7 +4,10 @@ use std::{convert::TryInto, ffi::CString, ops::BitOr, os::raw::*};
use objc::{runtime::Object, Encode, Encoding};
use crate::platform::ios::{Idiom, ScreenEdge, ValidOrientations};
use crate::{
dpi::LogicalSize,
platform::ios::{Idiom, ScreenEdge, ValidOrientations},
};
pub type id = *mut Object;
pub const nil: id = 0 as id;
@@ -39,6 +42,15 @@ pub struct CGSize {
pub height: CGFloat,
}
impl CGSize {
pub fn new(size: LogicalSize<f64>) -> CGSize {
CGSize {
width: size.width as _,
height: size.height as _,
}
}
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct CGRect {
@@ -46,6 +58,12 @@ pub struct CGRect {
pub size: CGSize,
}
impl CGRect {
pub fn new(origin: CGPoint, size: CGSize) -> CGRect {
CGRect { origin, size }
}
}
unsafe impl Encode for CGRect {
fn encode() -> Encoding {
unsafe {

View File

@@ -88,7 +88,7 @@ impl VideoMode {
}
}
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
self.size.into()
}
@@ -171,16 +171,16 @@ impl fmt::Debug for MonitorHandle {
#[derive(Debug)]
struct MonitorHandle {
name: Option<String>,
size: PhysicalSize,
position: PhysicalPosition,
hidpi_factor: f64,
size: PhysicalSize<u32>,
position: PhysicalPosition<i32>,
scale_factor: f64,
}
let monitor_id_proxy = MonitorHandle {
name: self.name(),
size: self.size(),
position: self.position(),
hidpi_factor: self.hidpi_factor(),
scale_factor: self.scale_factor(),
};
monitor_id_proxy.fmt(f)
@@ -216,21 +216,21 @@ impl Inner {
}
}
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
unsafe {
let bounds: CGRect = msg_send![self.ui_screen(), nativeBounds];
(bounds.size.width as f64, bounds.size.height as f64).into()
PhysicalSize::new(bounds.size.width as u32, bounds.size.height as u32)
}
}
pub fn position(&self) -> PhysicalPosition {
pub fn position(&self) -> PhysicalPosition<i32> {
unsafe {
let bounds: CGRect = msg_send![self.ui_screen(), nativeBounds];
(bounds.origin.x as f64, bounds.origin.y as f64).into()
}
}
pub fn hidpi_factor(&self) -> f64 {
pub fn scale_factor(&self) -> f64 {
unsafe {
let scale: CGFloat = msg_send![self.ui_screen(), nativeScale];
scale as f64

View File

@@ -10,7 +10,7 @@ use crate::{
platform::ios::MonitorHandleExtIOS,
platform_impl::platform::{
app_state::{self, OSCapabilities},
event_loop,
event_loop::{self, EventProxy, EventWrapper},
ffi::{
id, nil, CGFloat, CGPoint, CGRect, UIForceTouchCapability, UIInterfaceOrientationMask,
UIRectEdge, UITouchPhase, UITouchType,
@@ -103,8 +103,12 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
let window: id = msg_send![object, window];
assert!(!window.is_null());
app_state::handle_nonuser_events(
std::iter::once(Event::RedrawRequested(RootWindowId(window.into())))
.chain(std::iter::once(Event::RedrawEventsCleared)),
std::iter::once(EventWrapper::StaticEvent(Event::RedrawRequested(
RootWindowId(window.into()),
)))
.chain(std::iter::once(EventWrapper::StaticEvent(
Event::RedrawEventsCleared,
))),
);
let superclass: &'static Class = msg_send![object, superclass];
let () = msg_send![super(object, superclass), drawRect: rect];
@@ -123,27 +127,29 @@ 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 size = crate::dpi::LogicalSize {
width: screen_frame.size.width as _,
height: screen_frame.size.height as _,
};
app_state::handle_nonuser_event(Event::WindowEvent {
width: screen_frame.size.width as f64,
height: screen_frame.size.height as f64,
}
.to_physical(dpi_factor.into());
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::Resized(size),
});
}));
}
}
extern "C" fn set_content_scale_factor(
object: &mut Object,
_: Sel,
untrusted_hidpi_factor: CGFloat,
untrusted_scale_factor: CGFloat,
) {
unsafe {
let superclass: &'static Class = msg_send![object, superclass];
let () = msg_send![
super(object, superclass),
setContentScaleFactor: untrusted_hidpi_factor
setContentScaleFactor: untrusted_scale_factor
];
let window: id = msg_send![object, window];
@@ -156,14 +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 hidpi_factor: CGFloat = msg_send![object, contentScaleFactor];
let dpi_factor: CGFloat = msg_send![object, contentScaleFactor];
assert!(
!hidpi_factor.is_nan()
&& hidpi_factor.is_finite()
&& hidpi_factor.is_sign_positive()
&& hidpi_factor > 0.0,
"invalid hidpi_factor set on UIView",
!dpi_factor.is_nan()
&& dpi_factor.is_finite()
&& dpi_factor.is_sign_positive()
&& dpi_factor > 0.0,
"invalid scale_factor set on UIView",
);
let scale_factor: f64 = dpi_factor.into();
let bounds: CGRect = msg_send![object, bounds];
let screen: id = msg_send![window, screen];
let screen_space: id = msg_send![screen, coordinateSpace];
@@ -174,14 +181,17 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
height: screen_frame.size.height as _,
};
app_state::handle_nonuser_events(
std::iter::once(Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::HiDpiFactorChanged(hidpi_factor as _),
})
.chain(std::iter::once(Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::Resized(size),
})),
std::iter::once(EventWrapper::EventProxy(EventProxy::DpiChangedProxy {
window_id: window,
scale_factor,
suggested_size: size,
}))
.chain(std::iter::once(EventWrapper::StaticEvent(
Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::Resized(size.to_physical(scale_factor)),
},
))),
);
}
}
@@ -238,7 +248,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
_ => panic!("unexpected touch phase: {:?}", phase as i32),
};
touch_events.push(Event::WindowEvent {
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::Touch(Touch {
device_id: RootDeviceId(DeviceId { uiscreen }),
@@ -247,7 +257,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
force,
phase,
}),
});
}));
}
app_state::handle_nonuser_events(touch_events);
}
@@ -367,20 +377,20 @@ unsafe fn get_window_class() -> &'static Class {
extern "C" fn become_key_window(object: &Object, _: Sel) {
unsafe {
app_state::handle_nonuser_event(Event::WindowEvent {
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(object.into()),
event: WindowEvent::Focused(true),
});
}));
let () = msg_send![super(object, class!(UIWindow)), becomeKeyWindow];
}
}
extern "C" fn resign_key_window(object: &Object, _: Sel) {
unsafe {
app_state::handle_nonuser_event(Event::WindowEvent {
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(object.into()),
event: WindowEvent::Focused(false),
});
}));
let () = msg_send![super(object, class!(UIWindow)), resignKeyWindow];
}
}
@@ -414,8 +424,8 @@ pub unsafe fn create_view(
let view: id = msg_send![view, initWithFrame: frame];
assert!(!view.is_null(), "Failed to initialize `UIView` instance");
let () = msg_send![view, setMultipleTouchEnabled: YES];
if let Some(hidpi_factor) = platform_attributes.hidpi_factor {
let () = msg_send![view, setContentScaleFactor: hidpi_factor as CGFloat];
if let Some(scale_factor) = platform_attributes.scale_factor {
let () = msg_send![view, setContentScaleFactor: scale_factor as CGFloat];
}
view
@@ -518,11 +528,11 @@ pub fn create_delegate_class() {
}
extern "C" fn did_become_active(_: &Object, _: Sel, _: id) {
unsafe { app_state::handle_nonuser_event(Event::Resumed) }
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Resumed)) }
}
extern "C" fn will_resign_active(_: &Object, _: Sel, _: id) {
unsafe { app_state::handle_nonuser_event(Event::Suspended) }
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Suspended)) }
}
extern "C" fn will_enter_foreground(_: &Object, _: Sel, _: id) {}
@@ -541,10 +551,10 @@ pub fn create_delegate_class() {
}
let is_winit_window: BOOL = msg_send![window, isKindOfClass: class!(WinitUIWindow)];
if is_winit_window == YES {
events.push(Event::WindowEvent {
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::Destroyed,
});
}));
}
}
app_state::handle_nonuser_events(events);

View File

@@ -7,14 +7,15 @@ use std::{
use objc::runtime::{Class, Object, BOOL, NO, YES};
use crate::{
dpi::{self, LogicalPosition, LogicalSize},
dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
event::{Event, WindowEvent},
icon::Icon,
monitor::MonitorHandle as RootMonitorHandle,
platform::ios::{MonitorHandleExtIOS, ScreenEdge, ValidOrientations},
platform_impl::platform::{
app_state, event_loop,
app_state,
event_loop::{self, EventProxy, EventWrapper},
ffi::{
id, CGFloat, CGPoint, CGRect, CGSize, UIEdgeInsets, UIInterfaceOrientationMask,
UIRectEdge, UIScreenOverscanCompensation,
@@ -75,28 +76,34 @@ impl Inner {
}
}
pub fn inner_position(&self) -> Result<LogicalPosition, NotSupportedError> {
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
unsafe {
let safe_area = self.safe_area_screen_space();
Ok(LogicalPosition {
x: safe_area.origin.x as _,
y: safe_area.origin.y as _,
})
let position = LogicalPosition {
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))
}
}
pub fn outer_position(&self) -> Result<LogicalPosition, NotSupportedError> {
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
unsafe {
let screen_frame = self.screen_frame();
Ok(LogicalPosition {
x: screen_frame.origin.x as _,
y: screen_frame.origin.y as _,
})
let position = LogicalPosition {
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))
}
}
pub fn set_outer_position(&self, position: LogicalPosition) {
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 screen_frame = self.screen_frame();
let new_screen_frame = CGRect {
origin: CGPoint {
@@ -110,35 +117,39 @@ impl Inner {
}
}
pub fn inner_size(&self) -> LogicalSize {
pub fn inner_size(&self) -> PhysicalSize<u32> {
unsafe {
let dpi_factor = self.scale_factor();
let safe_area = self.safe_area_screen_space();
LogicalSize {
width: safe_area.size.width as _,
height: safe_area.size.height as _,
}
let size = LogicalSize {
width: safe_area.size.width as f64,
height: safe_area.size.height as f64,
};
size.to_physical(dpi_factor)
}
}
pub fn outer_size(&self) -> LogicalSize {
pub fn outer_size(&self) -> PhysicalSize<u32> {
unsafe {
let dpi_factor = self.scale_factor();
let screen_frame = self.screen_frame();
LogicalSize {
width: screen_frame.size.width as _,
height: screen_frame.size.height as _,
}
let size = LogicalSize {
width: screen_frame.size.width as f64,
height: screen_frame.size.height as f64,
};
size.to_physical(dpi_factor)
}
}
pub fn set_inner_size(&self, _size: LogicalSize) {
pub fn set_inner_size(&self, _size: Size) {
unimplemented!("not clear what `Window::set_inner_size` means on iOS");
}
pub fn set_min_inner_size(&self, _dimensions: Option<LogicalSize>) {
pub fn set_min_inner_size(&self, _dimensions: Option<Size>) {
warn!("`Window::set_min_inner_size` is ignored on iOS")
}
pub fn set_max_inner_size(&self, _dimensions: Option<LogicalSize>) {
pub fn set_max_inner_size(&self, _dimensions: Option<Size>) {
warn!("`Window::set_max_inner_size` is ignored on iOS")
}
@@ -146,7 +157,7 @@ impl Inner {
warn!("`Window::set_resizable` is ignored on iOS")
}
pub fn hidpi_factor(&self) -> f64 {
pub fn scale_factor(&self) -> f64 {
unsafe {
let hidpi: CGFloat = msg_send![self.view, contentScaleFactor];
hidpi as _
@@ -157,7 +168,7 @@ impl Inner {
debug!("`Window::set_cursor_icon` ignored on iOS")
}
pub fn set_cursor_position(&self, _position: LogicalPosition) -> Result<(), ExternalError> {
pub fn set_cursor_position(&self, _position: Position) -> Result<(), ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
@@ -243,7 +254,7 @@ impl Inner {
warn!("`Window::set_window_icon` is ignored on iOS")
}
pub fn set_ime_position(&self, _position: LogicalPosition) {
pub fn set_ime_position(&self, _position: Position) {
warn!("`Window::set_ime_position` is ignored on iOS")
}
@@ -343,13 +354,17 @@ impl Window {
let screen_bounds: CGRect = msg_send![screen, bounds];
let frame = match window_attributes.inner_size {
Some(dim) => CGRect {
origin: screen_bounds.origin,
size: CGSize {
width: dim.width as _,
height: dim.height as _,
},
},
Some(dim) => {
let dpi_factor = msg_send![screen, scale];
let size = dim.to_logical::<f64>(dpi_factor);
CGRect {
origin: screen_bounds.origin,
size: CGSize {
width: size.width as _,
height: size.height as _,
},
}
}
None => screen_bounds,
};
@@ -383,10 +398,11 @@ impl Window {
};
app_state::set_key_window(window);
// Like the Windows and macOS backends, we send a `HiDpiFactorChanged` and `Resized`
// Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `Resized`
// event on window creation if the DPI factor != 1.0
let hidpi_factor: CGFloat = msg_send![view, contentScaleFactor];
if hidpi_factor != 1.0 {
let dpi_factor: CGFloat = msg_send![view, contentScaleFactor];
let scale_factor: f64 = dpi_factor.into();
if scale_factor != 1.0 {
let bounds: CGRect = msg_send![view, bounds];
let screen: id = msg_send![window, screen];
let screen_space: id = msg_send![screen, coordinateSpace];
@@ -397,14 +413,17 @@ impl Window {
height: screen_frame.size.height as _,
};
app_state::handle_nonuser_events(
std::iter::once(Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::HiDpiFactorChanged(hidpi_factor as _),
})
.chain(std::iter::once(Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::Resized(size),
})),
std::iter::once(EventWrapper::EventProxy(EventProxy::DpiChangedProxy {
window_id: window,
scale_factor,
suggested_size: size,
}))
.chain(std::iter::once(EventWrapper::StaticEvent(
Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::Resized(size.to_physical(scale_factor)),
},
))),
);
}
@@ -425,14 +444,14 @@ impl Inner {
self.view
}
pub fn set_hidpi_factor(&self, hidpi_factor: f64) {
pub fn set_scale_factor(&self, scale_factor: f64) {
unsafe {
assert!(
dpi::validate_hidpi_factor(hidpi_factor),
"`WindowExtIOS::set_hidpi_factor` received an invalid hidpi factor"
dpi::validate_scale_factor(scale_factor),
"`WindowExtIOS::set_scale_factor` received an invalid hidpi factor"
);
let hidpi_factor = hidpi_factor as CGFloat;
let () = msg_send![self.view, setContentScaleFactor: hidpi_factor];
let scale_factor = scale_factor as CGFloat;
let () = msg_send![self.view, setContentScaleFactor: scale_factor];
}
}
@@ -598,7 +617,7 @@ impl From<id> for WindowId {
#[derive(Clone)]
pub struct PlatformSpecificWindowBuilderAttributes {
pub root_view_class: &'static Class,
pub hidpi_factor: Option<f64>,
pub scale_factor: Option<f64>,
pub valid_orientations: ValidOrientations,
pub prefers_home_indicator_hidden: bool,
pub prefers_status_bar_hidden: bool,
@@ -609,7 +628,7 @@ impl Default for PlatformSpecificWindowBuilderAttributes {
fn default() -> PlatformSpecificWindowBuilderAttributes {
PlatformSpecificWindowBuilderAttributes {
root_view_class: class!(UIView),
hidpi_factor: None,
scale_factor: None,
valid_orientations: Default::default(),
prefers_home_indicator_hidden: false,
prefers_status_bar_hidden: false,

View File

@@ -7,11 +7,9 @@ use raw_window_handle::RawWindowHandle;
use smithay_client_toolkit::reexports::client::ConnectError;
pub use self::x11::XNotSupported;
use self::x11::{
ffi::XVisualInfo, get_xtarget, util::WindowType as XWindowType, XConnection, XError,
};
use self::x11::{ffi::XVisualInfo, util::WindowType as XWindowType, XConnection, XError};
use crate::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize},
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
event::Event,
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
@@ -36,8 +34,8 @@ const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND";
pub struct PlatformSpecificWindowBuilderAttributes {
pub visual_infos: Option<XVisualInfo>,
pub screen_id: Option<i32>,
pub resize_increments: Option<(u32, u32)>,
pub base_size: Option<(u32, u32)>,
pub resize_increments: Option<Size>,
pub base_size: Option<Size>,
pub class: Option<(String, String)>,
pub override_redirect: bool,
pub x11_window_types: Vec<XWindowType>,
@@ -134,7 +132,7 @@ impl MonitorHandle {
}
#[inline]
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
match self {
&MonitorHandle::X(ref m) => m.size(),
&MonitorHandle::Wayland(ref m) => m.size(),
@@ -142,7 +140,7 @@ impl MonitorHandle {
}
#[inline]
pub fn position(&self) -> PhysicalPosition {
pub fn position(&self) -> PhysicalPosition<i32> {
match self {
&MonitorHandle::X(ref m) => m.position(),
&MonitorHandle::Wayland(ref m) => m.position(),
@@ -150,10 +148,10 @@ impl MonitorHandle {
}
#[inline]
pub fn hidpi_factor(&self) -> f64 {
pub fn scale_factor(&self) -> f64 {
match self {
&MonitorHandle::X(ref m) => m.hidpi_factor(),
&MonitorHandle::Wayland(ref m) => m.hidpi_factor() as f64,
&MonitorHandle::X(ref m) => m.scale_factor(),
&MonitorHandle::Wayland(ref m) => m.scale_factor() as f64,
}
}
@@ -174,7 +172,7 @@ pub enum VideoMode {
impl VideoMode {
#[inline]
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
match self {
&VideoMode::X(ref m) => m.size(),
&VideoMode::Wayland(ref m) => m.size(),
@@ -248,7 +246,7 @@ impl Window {
}
#[inline]
pub fn outer_position(&self) -> Result<LogicalPosition, NotSupportedError> {
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
match self {
&Window::X(ref w) => w.outer_position(),
&Window::Wayland(ref w) => w.outer_position(),
@@ -256,7 +254,7 @@ impl Window {
}
#[inline]
pub fn inner_position(&self) -> Result<LogicalPosition, NotSupportedError> {
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
match self {
&Window::X(ref m) => m.inner_position(),
&Window::Wayland(ref m) => m.inner_position(),
@@ -264,7 +262,7 @@ impl Window {
}
#[inline]
pub fn set_outer_position(&self, position: LogicalPosition) {
pub fn set_outer_position(&self, position: Position) {
match self {
&Window::X(ref w) => w.set_outer_position(position),
&Window::Wayland(ref w) => w.set_outer_position(position),
@@ -272,7 +270,7 @@ impl Window {
}
#[inline]
pub fn inner_size(&self) -> LogicalSize {
pub fn inner_size(&self) -> PhysicalSize<u32> {
match self {
&Window::X(ref w) => w.inner_size(),
&Window::Wayland(ref w) => w.inner_size(),
@@ -280,7 +278,7 @@ impl Window {
}
#[inline]
pub fn outer_size(&self) -> LogicalSize {
pub fn outer_size(&self) -> PhysicalSize<u32> {
match self {
&Window::X(ref w) => w.outer_size(),
&Window::Wayland(ref w) => w.outer_size(),
@@ -288,7 +286,7 @@ impl Window {
}
#[inline]
pub fn set_inner_size(&self, size: LogicalSize) {
pub fn set_inner_size(&self, size: Size) {
match self {
&Window::X(ref w) => w.set_inner_size(size),
&Window::Wayland(ref w) => w.set_inner_size(size),
@@ -296,7 +294,7 @@ impl Window {
}
#[inline]
pub fn set_min_inner_size(&self, dimensions: Option<LogicalSize>) {
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
match self {
&Window::X(ref w) => w.set_min_inner_size(dimensions),
&Window::Wayland(ref w) => w.set_min_inner_size(dimensions),
@@ -304,7 +302,7 @@ impl Window {
}
#[inline]
pub fn set_max_inner_size(&self, dimensions: Option<LogicalSize>) {
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
match self {
&Window::X(ref w) => w.set_max_inner_size(dimensions),
&Window::Wayland(ref w) => w.set_max_inner_size(dimensions),
@@ -344,15 +342,15 @@ impl Window {
}
#[inline]
pub fn hidpi_factor(&self) -> f64 {
pub fn scale_factor(&self) -> f64 {
match self {
&Window::X(ref w) => w.hidpi_factor(),
&Window::Wayland(ref w) => w.hidpi_factor() as f64,
&Window::X(ref w) => w.scale_factor(),
&Window::Wayland(ref w) => w.scale_factor() as f64,
}
}
#[inline]
pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), ExternalError> {
pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
match self {
&Window::X(ref w) => w.set_cursor_position(position),
&Window::Wayland(ref w) => w.set_cursor_position(position),
@@ -416,7 +414,7 @@ impl Window {
}
#[inline]
pub fn set_ime_position(&self, position: LogicalPosition) {
pub fn set_ime_position(&self, position: Position) {
match self {
&Window::X(ref w) => w.set_ime_position(position),
&Window::Wayland(_) => (),
@@ -603,7 +601,7 @@ impl<T: 'static> EventLoop<T> {
.into_iter()
.map(MonitorHandle::Wayland)
.collect(),
EventLoop::X(ref evlp) => get_xtarget(&evlp.target)
EventLoop::X(ref evlp) => evlp
.x_connection()
.available_monitors()
.into_iter()
@@ -616,9 +614,7 @@ impl<T: 'static> EventLoop<T> {
pub fn primary_monitor(&self) -> MonitorHandle {
match *self {
EventLoop::Wayland(ref evlp) => MonitorHandle::Wayland(evlp.primary_monitor()),
EventLoop::X(ref evlp) => {
MonitorHandle::X(get_xtarget(&evlp.target).x_connection().primary_monitor())
}
EventLoop::X(ref evlp) => MonitorHandle::X(evlp.x_connection().primary_monitor()),
}
}
@@ -631,7 +627,7 @@ impl<T: 'static> EventLoop<T> {
pub fn run_return<F>(&mut self, callback: F)
where
F: FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
F: FnMut(crate::event::Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
match *self {
EventLoop::Wayland(ref mut evlp) => evlp.run_return(callback),
@@ -641,7 +637,7 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(self, callback: F) -> !
where
F: 'static + FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
F: 'static + FnMut(crate::event::Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
match self {
EventLoop::Wayland(evlp) => evlp.run(callback),
@@ -682,12 +678,12 @@ impl<T> EventLoopWindowTarget<T> {
}
fn sticky_exit_callback<T, F>(
evt: Event<T>,
evt: Event<'_, T>,
target: &RootELW<T>,
control_flow: &mut ControlFlow,
callback: &mut F,
) where
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
// make ControlFlow::Exit sticky by providing a dummy
// control flow reference if it is already Exit.

View File

@@ -2,11 +2,16 @@ use std::{
cell::RefCell,
collections::VecDeque,
fmt,
io::ErrorKind,
rc::Rc,
sync::{Arc, Mutex},
time::Instant,
time::{Duration, Instant},
};
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio_extras::channel::{channel, Receiver, SendError, Sender};
use smithay_client_toolkit::reexports::protocols::unstable::pointer_constraints::v1::client::{
zwp_locked_pointer_v1::ZwpLockedPointerV1, zwp_pointer_constraints_v1::ZwpPointerConstraintsV1,
};
@@ -21,15 +26,17 @@ use smithay_client_toolkit::reexports::client::protocol::{
};
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
event::ModifiersState,
dpi::{LogicalSize, PhysicalPosition, PhysicalSize},
event::{
DeviceEvent, DeviceId as RootDeviceId, Event, ModifiersState, StartCause, WindowEvent,
},
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
platform_impl::platform::{
sticky_exit_callback, MonitorHandle as PlatformMonitorHandle,
VideoMode as PlatformVideoMode,
sticky_exit_callback, DeviceId as PlatformDeviceId, MonitorHandle as PlatformMonitorHandle,
VideoMode as PlatformVideoMode, WindowId as PlatformWindowId,
},
window::CursorIcon,
window::{CursorIcon, WindowId as RootWindowId},
};
use super::{window::WindowStore, DeviceId, WindowId};
@@ -43,43 +50,37 @@ use smithay_client_toolkit::{
Environment,
};
pub struct WindowEventsSink<T> {
buffer: VecDeque<crate::event::Event<T>>,
const KBD_TOKEN: Token = Token(0);
const USER_TOKEN: Token = Token(1);
const EVQ_TOKEN: Token = Token(2);
#[derive(Clone)]
pub struct EventsSink {
sender: Sender<Event<'static, ()>>,
}
impl<T> WindowEventsSink<T> {
pub fn new() -> WindowEventsSink<T> {
WindowEventsSink {
buffer: VecDeque::new(),
}
impl EventsSink {
pub fn new(sender: Sender<Event<'static, ()>>) -> EventsSink {
EventsSink { sender }
}
pub fn send_event(&mut self, evt: crate::event::Event<T>) {
self.buffer.push_back(evt);
pub fn send_event(&self, event: Event<'static, ()>) {
self.sender.send(event).unwrap()
}
pub fn send_window_event(&mut self, evt: crate::event::WindowEvent, wid: WindowId) {
self.buffer.push_back(crate::event::Event::WindowEvent {
event: evt,
window_id: crate::window::WindowId(crate::platform_impl::WindowId::Wayland(wid)),
pub fn send_device_event(&self, event: DeviceEvent, device_id: DeviceId) {
self.send_event(Event::DeviceEvent {
event,
device_id: RootDeviceId(PlatformDeviceId::Wayland(device_id)),
});
}
pub fn send_device_event(&mut self, evt: crate::event::DeviceEvent, dev_id: DeviceId) {
self.buffer.push_back(crate::event::Event::DeviceEvent {
event: evt,
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(dev_id)),
pub fn send_window_event(&self, event: WindowEvent<'static>, window_id: WindowId) {
self.send_event(Event::WindowEvent {
event,
window_id: RootWindowId(PlatformWindowId::Wayland(window_id)),
});
}
fn empty_with<F>(&mut self, mut callback: F)
where
F: FnMut(crate::event::Event<T>),
{
for evt in self.buffer.drain(..) {
callback(evt)
}
}
}
pub struct CursorManager {
@@ -230,21 +231,17 @@ impl CursorManager {
}
pub struct EventLoop<T: 'static> {
// The loop
inner_loop: ::calloop::EventLoop<()>,
// Poll instance
poll: Poll,
// The wayland display
pub display: Arc<Display>,
// The output manager
pub outputs: OutputMgr,
// Our sink, shared with some handlers, buffering the events
sink: Arc<Mutex<WindowEventsSink<T>>>,
pending_user_events: Rc<RefCell<VecDeque<T>>>,
// The cursor manager
cursor_manager: Arc<Mutex<CursorManager>>,
// Utility for grabbing the cursor and changing visibility
_user_source: ::calloop::Source<::calloop::channel::Channel<T>>,
user_sender: ::calloop::channel::Sender<T>,
_kbd_source: ::calloop::Source<::calloop::channel::Channel<crate::event::Event<()>>>,
kbd_channel: Receiver<Event<'static, ()>>,
user_channel: Receiver<T>,
user_sender: Sender<T>,
window_target: RootELW<T>,
}
@@ -252,12 +249,12 @@ pub struct EventLoop<T: 'static> {
//
// We should only try and wake up the `EventLoop` if it still exists, so we hold Weak ptrs.
pub struct EventLoopProxy<T: 'static> {
user_sender: calloop::channel::Sender<T>,
user_sender: Sender<T>,
}
pub struct EventLoopWindowTarget<T> {
// The event queue
pub evq: RefCell<::calloop::Source<EventQueue>>,
// the event queue
pub evq: RefCell<EventQueue>,
// The window store
pub store: Arc<Mutex<WindowStore>>,
// The cursor manager
@@ -284,7 +281,7 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
impl<T: 'static> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.user_sender.send(event).map_err(|e| {
EventLoopClosed(if let ::calloop::channel::SendError::Disconnected(x) = e {
EventLoopClosed(if let SendError::Disconnected(x) = e {
x
} else {
unreachable!()
@@ -298,33 +295,26 @@ impl<T: 'static> EventLoop<T> {
let (display, mut event_queue) = Display::connect_to_env()?;
let display = Arc::new(display);
let sink = Arc::new(Mutex::new(WindowEventsSink::new()));
let store = Arc::new(Mutex::new(WindowStore::new()));
let seats = Arc::new(Mutex::new(Vec::new()));
let inner_loop = ::calloop::EventLoop::new().unwrap();
let poll = Poll::new().unwrap();
let (kbd_sender, kbd_channel) = ::calloop::channel::channel::<crate::event::Event<()>>();
let kbd_sink = sink.clone();
let kbd_source = inner_loop
.handle()
.insert_source(kbd_channel, move |evt, &mut ()| {
if let ::calloop::channel::Event::Msg(evt) = evt {
let evt = evt.map_nonuser_event().ok().unwrap();
kbd_sink.lock().unwrap().send_event(evt);
}
})
let (kbd_sender, kbd_channel) = channel();
let sink = EventsSink::new(kbd_sender);
poll.register(&kbd_channel, KBD_TOKEN, Ready::readable(), PollOpt::level())
.unwrap();
let pointer_constraints_proxy = Arc::new(Mutex::new(None));
let mut seat_manager = SeatManager {
sink: sink.clone(),
relative_pointer_manager_proxy: Rc::new(RefCell::new(None)),
pointer_constraints_proxy: pointer_constraints_proxy.clone(),
sink,
store: store.clone(),
seats: seats.clone(),
kbd_sender,
relative_pointer_manager_proxy: Rc::new(RefCell::new(None)),
pointer_constraints_proxy: pointer_constraints_proxy.clone(),
cursor_manager: Arc::new(Mutex::new(CursorManager::new(pointer_constraints_proxy))),
};
@@ -404,39 +394,31 @@ impl<T: 'static> EventLoop<T> {
)
.unwrap();
let source = inner_loop
.handle()
.insert_source(event_queue, |(), &mut ()| {})
poll.register(&event_queue, EVQ_TOKEN, Ready::readable(), PollOpt::level())
.unwrap();
let pending_user_events = Rc::new(RefCell::new(VecDeque::new()));
let pending_user_events2 = pending_user_events.clone();
let (user_sender, user_channel) = channel();
let (user_sender, user_channel) = ::calloop::channel::channel();
let user_source = inner_loop
.handle()
.insert_source(user_channel, move |evt, &mut ()| {
if let ::calloop::channel::Event::Msg(msg) = evt {
pending_user_events2.borrow_mut().push_back(msg);
}
})
.unwrap();
poll.register(
&user_channel,
USER_TOKEN,
Ready::readable(),
PollOpt::level(),
)
.unwrap();
let cursor_manager_clone = cursor_manager.clone();
Ok(EventLoop {
inner_loop,
sink,
pending_user_events,
poll,
display: display.clone(),
outputs: env.outputs.clone(),
_user_source: user_source,
user_sender,
user_channel,
kbd_channel,
cursor_manager,
_kbd_source: kbd_source,
window_target: RootELW {
p: crate::platform_impl::EventLoopWindowTarget::Wayland(EventLoopWindowTarget {
evq: RefCell::new(source),
evq: RefCell::new(event_queue),
store,
env,
cursor_manager: cursor_manager_clone,
@@ -458,7 +440,7 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(mut self, callback: F) -> !
where
F: 'static + FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
F: 'static + FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
self.run_return(callback);
std::process::exit(0);
@@ -466,53 +448,60 @@ impl<T: 'static> EventLoop<T> {
pub fn run_return<F>(&mut self, mut callback: F)
where
F: FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
// send pending events to the server
self.display.flush().expect("Wayland connection lost.");
let mut control_flow = ControlFlow::default();
let sink = self.sink.clone();
let user_events = self.pending_user_events.clone();
let mut events = Events::with_capacity(8);
callback(
crate::event::Event::NewEvents(crate::event::StartCause::Init),
Event::NewEvents(StartCause::Init),
&self.window_target,
&mut control_flow,
);
loop {
self.post_dispatch_triggers();
// Read events from the event queue
{
let mut evq = get_target(&self.window_target).evq.borrow_mut();
// empty buffer of events
{
let mut guard = sink.lock().unwrap();
guard.empty_with(|evt| {
sticky_exit_callback(
evt,
&self.window_target,
&mut control_flow,
&mut callback,
);
});
}
// empty user events
{
let mut guard = user_events.borrow_mut();
for evt in guard.drain(..) {
sticky_exit_callback(
crate::event::Event::UserEvent(evt),
&self.window_target,
&mut control_flow,
&mut callback,
);
evq.dispatch_pending()
.expect("failed to dispatch wayland events");
if let Some(read) = evq.prepare_read() {
if let Err(e) = read.read_events() {
if e.kind() != ErrorKind::WouldBlock {
panic!("failed to read wayland events: {}", e);
}
}
evq.dispatch_pending()
.expect("failed to dispatch wayland events");
}
}
self.post_dispatch_triggers(&mut callback, &mut control_flow);
while let Ok(event) = self.kbd_channel.try_recv() {
let event = event.map_nonuser_event().unwrap();
sticky_exit_callback(event, &self.window_target, &mut control_flow, &mut callback);
}
while let Ok(event) = self.user_channel.try_recv() {
sticky_exit_callback(
Event::UserEvent(event),
&self.window_target,
&mut control_flow,
&mut callback,
);
}
// send Events cleared
{
sticky_exit_callback(
crate::event::Event::MainEventsCleared,
Event::MainEventsCleared,
&self.window_target,
&mut control_flow,
&mut callback,
@@ -523,7 +512,7 @@ impl<T: 'static> EventLoop<T> {
{
self.redraw_triggers(|wid, window_target| {
sticky_exit_callback(
crate::event::Event::RedrawRequested(crate::window::WindowId(
Event::RedrawRequested(crate::window::WindowId(
crate::platform_impl::WindowId::Wayland(wid),
)),
window_target,
@@ -536,7 +525,7 @@ impl<T: 'static> EventLoop<T> {
// send RedrawEventsCleared
{
sticky_exit_callback(
crate::event::Event::RedrawEventsCleared,
Event::RedrawEventsCleared,
&self.window_target,
&mut control_flow,
&mut callback,
@@ -569,24 +558,25 @@ impl<T: 'static> EventLoop<T> {
ControlFlow::Exit => break,
ControlFlow::Poll => {
// non-blocking dispatch
self.inner_loop
.dispatch(Some(::std::time::Duration::from_millis(0)), &mut ())
self.poll
.poll(&mut events, Some(Duration::from_millis(0)))
.unwrap();
events.clear();
callback(
crate::event::Event::NewEvents(crate::event::StartCause::Poll),
Event::NewEvents(StartCause::Poll),
&self.window_target,
&mut control_flow,
);
}
ControlFlow::Wait => {
let timeout = if instant_wakeup {
Some(::std::time::Duration::from_millis(0))
} else {
None
};
self.inner_loop.dispatch(timeout, &mut ()).unwrap();
if !instant_wakeup {
self.poll.poll(&mut events, None).unwrap();
events.clear();
}
callback(
crate::event::Event::NewEvents(crate::event::StartCause::WaitCancelled {
Event::NewEvents(StartCause::WaitCancelled {
start: Instant::now(),
requested_resume: None,
}),
@@ -600,29 +590,27 @@ impl<T: 'static> EventLoop<T> {
let duration = if deadline > start && !instant_wakeup {
deadline - start
} else {
::std::time::Duration::from_millis(0)
Duration::from_millis(0)
};
self.inner_loop.dispatch(Some(duration), &mut ()).unwrap();
self.poll.poll(&mut events, Some(duration)).unwrap();
events.clear();
let now = Instant::now();
if now < deadline {
callback(
crate::event::Event::NewEvents(
crate::event::StartCause::WaitCancelled {
start,
requested_resume: Some(deadline),
},
),
Event::NewEvents(StartCause::WaitCancelled {
start,
requested_resume: Some(deadline),
}),
&self.window_target,
&mut control_flow,
);
} else {
callback(
crate::event::Event::NewEvents(
crate::event::StartCause::ResumeTimeReached {
start,
requested_resume: deadline,
},
),
Event::NewEvents(StartCause::ResumeTimeReached {
start,
requested_resume: deadline,
}),
&self.window_target,
&mut control_flow,
);
@@ -631,11 +619,7 @@ impl<T: 'static> EventLoop<T> {
}
}
callback(
crate::event::Event::LoopDestroyed,
&self.window_target,
&mut control_flow,
);
callback(Event::LoopDestroyed, &self.window_target, &mut control_flow);
}
pub fn primary_monitor(&self) -> MonitorHandle {
@@ -687,12 +671,19 @@ impl<T> EventLoop<T> {
)
}
fn post_dispatch_triggers(&mut self) {
let mut sink = self.sink.lock().unwrap();
fn post_dispatch_triggers<F>(&mut self, mut callback: F, control_flow: &mut ControlFlow)
where
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
let window_target = match self.window_target.p {
crate::platform_impl::EventLoopWindowTarget::Wayland(ref wt) => wt,
_ => unreachable!(),
};
let mut callback = |event: Event<'_, T>| {
sticky_exit_callback(event, &self.window_target, control_flow, &mut callback);
};
// prune possible dead windows
{
let mut cleanup_needed = window_target.cleanup_needed.lock().unwrap();
@@ -700,41 +691,65 @@ impl<T> EventLoop<T> {
let pruned = window_target.store.lock().unwrap().cleanup();
*cleanup_needed = false;
for wid in pruned {
sink.send_window_event(crate::event::WindowEvent::Destroyed, wid);
callback(Event::WindowEvent {
window_id: crate::window::WindowId(
crate::platform_impl::WindowId::Wayland(wid),
),
event: WindowEvent::Destroyed,
});
}
}
}
// process pending resize/refresh
window_target.store.lock().unwrap().for_each(|window| {
let window_id =
crate::window::WindowId(crate::platform_impl::WindowId::Wayland(window.wid));
if let Some(frame) = window.frame {
if let Some(newsize) = window.newsize {
let (w, h) = newsize;
if let Some((w, h)) = window.newsize {
// 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.
frame.resize(w, h);
frame.refresh();
// Don't send resize event downstream if the new size is identical to the
// current one.
if newsize != *window.size {
if (w, h) != *window.size {
let logical_size = crate::dpi::LogicalSize::new(w as f64, h as f64);
sink.send_window_event(
crate::event::WindowEvent::Resized(logical_size),
window.wid,
);
let physical_size = logical_size
.to_physical(window.new_dpi.unwrap_or(window.prev_dpi) as f64);
callback(Event::WindowEvent {
window_id,
event: WindowEvent::Resized(physical_size),
});
*window.size = (w, h);
}
}
}
if let Some(dpi) = window.new_dpi {
sink.send_window_event(
crate::event::WindowEvent::HiDpiFactorChanged(dpi as f64),
window.wid,
);
if let Some(dpi) = window.new_dpi {
let dpi = dpi as f64;
let logical_size = LogicalSize::<f64>::from(*window.size);
let mut new_inner_size = logical_size.to_physical(dpi);
callback(Event::WindowEvent {
window_id,
event: WindowEvent::ScaleFactorChanged {
scale_factor: dpi,
new_inner_size: &mut new_inner_size,
},
});
let (w, h) = new_inner_size.to_logical::<u32>(dpi).into();
frame.resize(w, h);
*window.size = (w, h);
}
}
if window.closed {
sink.send_window_event(crate::event::WindowEvent::CloseRequested, window.wid);
callback(Event::WindowEvent {
window_id,
event: WindowEvent::CloseRequested,
});
}
if let Some(grab_cursor) = window.grab_cursor {
@@ -749,21 +764,27 @@ impl<T> EventLoop<T> {
}
}
fn get_target<T>(target: &RootELW<T>) -> &EventLoopWindowTarget<T> {
match target.p {
crate::platform_impl::EventLoopWindowTarget::Wayland(ref wt) => wt,
_ => unreachable!(),
}
}
/*
* Wayland protocol implementations
*/
struct SeatManager<T: 'static> {
sink: Arc<Mutex<WindowEventsSink<T>>>,
struct SeatManager {
sink: EventsSink,
store: Arc<Mutex<WindowStore>>,
seats: Arc<Mutex<Vec<(u32, wl_seat::WlSeat)>>>,
kbd_sender: ::calloop::channel::Sender<crate::event::Event<()>>,
relative_pointer_manager_proxy: Rc<RefCell<Option<ZwpRelativePointerManagerV1>>>,
pointer_constraints_proxy: Arc<Mutex<Option<ZwpPointerConstraintsV1>>>,
cursor_manager: Arc<Mutex<CursorManager>>,
}
impl<T: 'static> SeatManager<T> {
impl SeatManager {
fn add_seat(&mut self, id: u32, version: u32, registry: wl_registry::WlRegistry) {
use std::cmp::min;
@@ -775,7 +796,6 @@ impl<T: 'static> SeatManager<T> {
relative_pointer_manager_proxy: self.relative_pointer_manager_proxy.clone(),
keyboard: None,
touch: None,
kbd_sender: self.kbd_sender.clone(),
modifiers_tracker: Arc::new(Mutex::new(ModifiersState::default())),
cursor_manager: self.cursor_manager.clone(),
};
@@ -799,10 +819,9 @@ impl<T: 'static> SeatManager<T> {
}
}
struct SeatData<T> {
sink: Arc<Mutex<WindowEventsSink<T>>>,
struct SeatData {
sink: EventsSink,
store: Arc<Mutex<WindowStore>>,
kbd_sender: ::calloop::channel::Sender<crate::event::Event<()>>,
pointer: Option<wl_pointer::WlPointer>,
relative_pointer: Option<ZwpRelativePointerV1>,
relative_pointer_manager_proxy: Rc<RefCell<Option<ZwpRelativePointerManagerV1>>>,
@@ -812,7 +831,7 @@ struct SeatData<T> {
cursor_manager: Arc<Mutex<CursorManager>>,
}
impl<T: 'static> SeatData<T> {
impl SeatData {
fn receive(&mut self, evt: wl_seat::Event, seat: wl_seat::WlSeat) {
match evt {
wl_seat::Event::Name { .. } => (),
@@ -858,7 +877,7 @@ impl<T: 'static> SeatData<T> {
if capabilities.contains(wl_seat::Capability::Keyboard) && self.keyboard.is_none() {
self.keyboard = Some(super::keyboard::init_keyboard(
&seat,
self.kbd_sender.clone(),
self.sink.clone(),
self.modifiers_tracker.clone(),
))
}
@@ -892,7 +911,7 @@ impl<T: 'static> SeatData<T> {
}
}
impl<T> Drop for SeatData<T> {
impl Drop for SeatData {
fn drop(&mut self) {
if let Some(pointer) = self.pointer.take() {
if pointer.as_ref().version() >= 3 {
@@ -926,7 +945,7 @@ pub struct VideoMode {
impl VideoMode {
#[inline]
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
self.size.into()
}
@@ -986,9 +1005,9 @@ impl fmt::Debug for MonitorHandle {
struct MonitorHandle {
name: Option<String>,
native_identifier: u32,
size: PhysicalSize,
position: PhysicalPosition,
hidpi_factor: i32,
size: PhysicalSize<u32>,
position: PhysicalPosition<i32>,
scale_factor: i32,
}
let monitor_id_proxy = MonitorHandle {
@@ -996,7 +1015,7 @@ impl fmt::Debug for MonitorHandle {
native_identifier: self.native_identifier(),
size: self.size(),
position: self.position(),
hidpi_factor: self.hidpi_factor(),
scale_factor: self.scale_factor(),
};
monitor_id_proxy.fmt(f)
@@ -1015,7 +1034,7 @@ impl MonitorHandle {
self.mgr.with_info(&self.proxy, |id, _| id).unwrap_or(0)
}
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
match self.mgr.with_info(&self.proxy, |_, info| {
info.modes
.iter()
@@ -1028,7 +1047,7 @@ impl MonitorHandle {
.into()
}
pub fn position(&self) -> PhysicalPosition {
pub fn position(&self) -> PhysicalPosition<i32> {
self.mgr
.with_info(&self.proxy, |_, info| info.location)
.unwrap_or((0, 0))
@@ -1036,7 +1055,7 @@ impl MonitorHandle {
}
#[inline]
pub fn hidpi_factor(&self) -> i32 {
pub fn scale_factor(&self) -> i32 {
self.mgr
.with_info(&self.proxy, |_, info| info.scale_factor)
.unwrap_or(1)

View File

@@ -1,6 +1,6 @@
use std::sync::{Arc, Mutex};
use super::{make_wid, DeviceId};
use super::{event_loop::EventsSink, make_wid, DeviceId};
use smithay_client_toolkit::{
keyboard::{
self, map_keyboard_auto_with_repeat, Event as KbEvent, KeyRepeatEvent, KeyRepeatKind,
@@ -9,12 +9,12 @@ use smithay_client_toolkit::{
};
use crate::event::{
DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent,
DeviceEvent, ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent,
};
pub fn init_keyboard(
seat: &wl_seat::WlSeat,
sink: ::calloop::channel::Sender<crate::event::Event<()>>,
sink: EventsSink,
modifiers_tracker: Arc<Mutex<ModifiersState>>,
) -> wl_keyboard::WlKeyboard {
// { variables to be captured by the closures
@@ -31,22 +31,12 @@ pub fn init_keyboard(
match evt {
KbEvent::Enter { surface, .. } => {
let wid = make_wid(&surface);
my_sink
.send(Event::WindowEvent {
window_id: mk_root_wid(wid),
event: WindowEvent::Focused(true),
})
.unwrap();
my_sink.send_window_event(WindowEvent::Focused(true), wid);
*target.lock().unwrap() = Some(wid);
}
KbEvent::Leave { surface, .. } => {
let wid = make_wid(&surface);
my_sink
.send(Event::WindowEvent {
window_id: mk_root_wid(wid),
event: WindowEvent::Focused(false),
})
.unwrap();
my_sink.send_window_event(WindowEvent::Focused(false), wid);
*target.lock().unwrap() = None;
}
KbEvent::Key {
@@ -63,33 +53,29 @@ pub fn init_keyboard(
_ => unreachable!(),
};
let vkcode = key_to_vkey(rawkey, keysym);
my_sink
.send(Event::WindowEvent {
window_id: mk_root_wid(wid),
event: WindowEvent::KeyboardInput {
device_id: device_id(),
input: KeyboardInput {
state,
scancode: rawkey,
virtual_keycode: vkcode,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
is_synthetic: false,
my_sink.send_window_event(
#[allow(deprecated)]
WindowEvent::KeyboardInput {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
input: KeyboardInput {
state,
scancode: rawkey,
virtual_keycode: vkcode,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
})
.unwrap();
is_synthetic: false,
},
wid,
);
// send char event only on key press, not release
if let ElementState::Released = state {
return;
}
if let Some(txt) = utf8 {
for chr in txt.chars() {
my_sink
.send(Event::WindowEvent {
window_id: mk_root_wid(wid),
event: WindowEvent::ReceivedCharacter(chr),
})
.unwrap();
my_sink.send_window_event(WindowEvent::ReceivedCharacter(chr), wid);
}
}
}
@@ -102,12 +88,7 @@ pub fn init_keyboard(
*modifiers_tracker.lock().unwrap() = modifiers;
my_sink
.send(Event::DeviceEvent {
device_id: device_id(),
event: DeviceEvent::ModifiersChanged(modifiers),
})
.unwrap();
my_sink.send_device_event(DeviceEvent::ModifiersChanged(modifiers), DeviceId);
}
}
},
@@ -115,29 +96,25 @@ pub fn init_keyboard(
if let Some(wid) = *repeat_target.lock().unwrap() {
let state = ElementState::Pressed;
let vkcode = key_to_vkey(repeat_event.rawkey, repeat_event.keysym);
repeat_sink
.send(Event::WindowEvent {
window_id: mk_root_wid(wid),
event: WindowEvent::KeyboardInput {
device_id: device_id(),
input: KeyboardInput {
state,
scancode: repeat_event.rawkey,
virtual_keycode: vkcode,
modifiers: my_modifiers.lock().unwrap().clone(),
},
is_synthetic: false,
repeat_sink.send_window_event(
#[allow(deprecated)]
WindowEvent::KeyboardInput {
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
DeviceId,
)),
input: KeyboardInput {
state,
scancode: repeat_event.rawkey,
virtual_keycode: vkcode,
modifiers: my_modifiers.lock().unwrap().clone(),
},
})
.unwrap();
is_synthetic: false,
},
wid,
);
if let Some(txt) = repeat_event.utf8 {
for chr in txt.chars() {
repeat_sink
.send(Event::WindowEvent {
window_id: mk_root_wid(wid),
event: WindowEvent::ReceivedCharacter(chr),
})
.unwrap();
repeat_sink.send_window_event(WindowEvent::ReceivedCharacter(chr), wid);
}
}
}
@@ -164,22 +141,12 @@ pub fn init_keyboard(
move |evt, _| match evt {
wl_keyboard::Event::Enter { surface, .. } => {
let wid = make_wid(&surface);
my_sink
.send(Event::WindowEvent {
window_id: mk_root_wid(wid),
event: WindowEvent::Focused(true),
})
.unwrap();
my_sink.send_window_event(WindowEvent::Focused(true), wid);
target = Some(wid);
}
wl_keyboard::Event::Leave { surface, .. } => {
let wid = make_wid(&surface);
my_sink
.send(Event::WindowEvent {
window_id: mk_root_wid(wid),
event: WindowEvent::Focused(false),
})
.unwrap();
my_sink.send_window_event(WindowEvent::Focused(false), wid);
target = None;
}
wl_keyboard::Event::Key { key, state, .. } => {
@@ -189,21 +156,22 @@ pub fn init_keyboard(
wl_keyboard::KeyState::Released => ElementState::Released,
_ => unreachable!(),
};
my_sink
.send(Event::WindowEvent {
window_id: mk_root_wid(wid),
event: WindowEvent::KeyboardInput {
device_id: device_id(),
input: KeyboardInput {
state,
scancode: key,
virtual_keycode: None,
modifiers: ModifiersState::default(),
},
is_synthetic: false,
my_sink.send_window_event(
#[allow(deprecated)]
WindowEvent::KeyboardInput {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
input: KeyboardInput {
state,
scancode: key,
virtual_keycode: None,
modifiers: ModifiersState::default(),
},
})
.unwrap();
is_synthetic: false,
},
wid,
);
}
}
_ => (),
@@ -412,11 +380,3 @@ impl ModifiersState {
m
}
}
fn device_id() -> crate::event::DeviceId {
crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId))
}
fn mk_root_wid(wid: crate::platform_impl::wayland::WindowId) -> crate::window::WindowId {
crate::window::WindowId(crate::platform_impl::WindowId::Wayland(wid))
}

View File

@@ -2,10 +2,7 @@
target_os = "netbsd", target_os = "openbsd"))]
pub use self::{
event_loop::{
EventLoop, EventLoopProxy, EventLoopWindowTarget, MonitorHandle, VideoMode,
WindowEventsSink,
},
event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget, MonitorHandle, VideoMode},
window::Window,
};

View File

@@ -6,7 +6,7 @@ use crate::event::{
};
use super::{
event_loop::{CursorManager, WindowEventsSink},
event_loop::{CursorManager, EventsSink},
window::WindowStore,
DeviceId,
};
@@ -28,9 +28,9 @@ use smithay_client_toolkit::reexports::protocols::unstable::pointer_constraints:
use smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface;
pub fn implement_pointer<T: 'static>(
pub fn implement_pointer(
seat: &wl_seat::WlSeat,
sink: Arc<Mutex<WindowEventsSink<T>>>,
sink: EventsSink,
store: Arc<Mutex<WindowStore>>,
modifiers_tracker: Arc<Mutex<ModifiersState>>,
cursor_manager: Arc<Mutex<CursorManager>>,
@@ -43,7 +43,6 @@ pub fn implement_pointer<T: 'static>(
pointer.implement_closure(
move |evt, pointer| {
let mut sink = sink.lock().unwrap();
let store = store.lock().unwrap();
let mut cursor_manager = cursor_manager.lock().unwrap();
match evt {
@@ -242,20 +241,18 @@ pub fn implement_pointer<T: 'static>(
.unwrap()
}
pub fn implement_relative_pointer<T: 'static>(
sink: Arc<Mutex<WindowEventsSink<T>>>,
pub fn implement_relative_pointer(
sink: EventsSink,
pointer: &WlPointer,
manager: &ZwpRelativePointerManagerV1,
) -> Result<ZwpRelativePointerV1, ()> {
manager.get_relative_pointer(pointer, |rel_pointer| {
rel_pointer.implement_closure(
move |evt, _rel_pointer| {
let mut sink = sink.lock().unwrap();
match evt {
Event::RelativeMotion { dx, dy, .. } => sink
.send_device_event(DeviceEvent::MouseMotion { delta: (dx, dy) }, DeviceId),
_ => unreachable!(),
move |evt, _rel_pointer| match evt {
Event::RelativeMotion { dx, dy, .. } => {
sink.send_device_event(DeviceEvent::MouseMotion { delta: (dx, dy) }, DeviceId)
}
_ => unreachable!(),
},
(),
)

View File

@@ -2,7 +2,7 @@ use std::sync::{Arc, Mutex};
use crate::event::{TouchPhase, WindowEvent};
use super::{event_loop::WindowEventsSink, window::WindowStore, DeviceId, WindowId};
use super::{event_loop::EventsSink, window::WindowStore, DeviceId, WindowId};
use smithay_client_toolkit::reexports::client::protocol::{
wl_seat,
@@ -15,16 +15,15 @@ struct TouchPoint {
id: i32,
}
pub(crate) fn implement_touch<T: 'static>(
pub(crate) fn implement_touch(
seat: &wl_seat::WlSeat,
sink: Arc<Mutex<WindowEventsSink<T>>>,
sink: EventsSink,
store: Arc<Mutex<WindowStore>>,
) -> WlTouch {
let mut pending_ids = Vec::new();
seat.get_touch(|touch| {
touch.implement_closure(
move |evt, _| {
let mut sink = sink.lock().unwrap();
let store = store.lock().unwrap();
match evt {
TouchEvent::Down {

View File

@@ -6,7 +6,7 @@ use std::{
};
use crate::{
dpi::{LogicalPosition, LogicalSize},
dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
monitor::MonitorHandle as RootMonitorHandle,
platform_impl::{
@@ -49,11 +49,7 @@ impl Window {
attributes: WindowAttributes,
pl_attribs: PlAttributes,
) -> Result<Window, RootOsError> {
let (width, height) = attributes.inner_size.map(Into::into).unwrap_or((800, 600));
// Create the window
let size = Arc::new(Mutex::new((width, height)));
let fullscreen = Arc::new(Mutex::new(false));
// Create the surface first to get initial DPI
let window_store = evlp.store.clone();
let cursor_manager = evlp.cursor_manager.clone();
let surface = evlp.env.create_surface(move |dpi, surface| {
@@ -61,7 +57,18 @@ impl Window {
surface.set_buffer_scale(dpi);
});
let dpi = get_dpi_factor(&surface) as f64;
let (width, height) = attributes
.inner_size
.map(|size| size.to_logical::<f64>(dpi).into())
.unwrap_or((800, 600));
// Create the window
let size = Arc::new(Mutex::new((width, height)));
let fullscreen = Arc::new(Mutex::new(false));
let window_store = evlp.store.clone();
let my_surface = surface.clone();
let mut frame = SWindow::<ConceptFrame>::init_from_env(
&evlp.env,
@@ -137,8 +144,16 @@ impl Window {
frame.set_title(attributes.title);
// min-max dimensions
frame.set_min_size(attributes.min_inner_size.map(Into::into));
frame.set_max_size(attributes.max_inner_size.map(Into::into));
frame.set_min_size(
attributes
.min_inner_size
.map(|size| size.to_logical::<f64>(dpi).into()),
);
frame.set_max_size(
attributes
.max_inner_size
.map(|size| size.to_logical::<f64>(dpi).into()),
);
let kill_switch = Arc::new(Mutex::new(false));
let need_frame_refresh = Arc::new(Mutex::new(true));
@@ -191,22 +206,24 @@ impl Window {
}
#[inline]
pub fn outer_position(&self) -> Result<LogicalPosition, NotSupportedError> {
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
Err(NotSupportedError::new())
}
#[inline]
pub fn inner_position(&self) -> Result<LogicalPosition, NotSupportedError> {
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
Err(NotSupportedError::new())
}
#[inline]
pub fn set_outer_position(&self, _pos: LogicalPosition) {
pub fn set_outer_position(&self, _pos: Position) {
// Not possible with wayland
}
pub fn inner_size(&self) -> LogicalSize {
self.size.lock().unwrap().clone().into()
pub fn inner_size(&self) -> PhysicalSize<u32> {
let dpi = self.scale_factor() as f64;
let size = LogicalSize::<f64>::from(*self.size.lock().unwrap());
size.to_physical(dpi)
}
pub fn request_redraw(&self) {
@@ -214,34 +231,39 @@ impl Window {
}
#[inline]
pub fn outer_size(&self) -> LogicalSize {
pub fn outer_size(&self) -> PhysicalSize<u32> {
let dpi = self.scale_factor() as f64;
let (w, h) = self.size.lock().unwrap().clone();
// let (w, h) = super::wayland_window::add_borders(w as i32, h as i32);
(w, h).into()
let size = LogicalSize::<f64>::from((w, h));
size.to_physical(dpi)
}
#[inline]
// NOTE: This will only resize the borders, the contents must be updated by the user
pub fn set_inner_size(&self, size: LogicalSize) {
let (w, h) = size.into();
pub fn set_inner_size(&self, size: Size) {
let dpi = self.scale_factor() as f64;
let (w, h) = size.to_logical::<u32>(dpi).into();
self.frame.lock().unwrap().resize(w, h);
*(self.size.lock().unwrap()) = (w, h);
}
#[inline]
pub fn set_min_inner_size(&self, dimensions: Option<LogicalSize>) {
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
let dpi = self.scale_factor() as f64;
self.frame
.lock()
.unwrap()
.set_min_size(dimensions.map(Into::into));
.set_min_size(dimensions.map(|dim| dim.to_logical::<f64>(dpi).into()));
}
#[inline]
pub fn set_max_inner_size(&self, dimensions: Option<LogicalSize>) {
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
let dpi = self.scale_factor() as f64;
self.frame
.lock()
.unwrap()
.set_max_size(dimensions.map(Into::into));
.set_max_size(dimensions.map(|dim| dim.to_logical::<f64>(dpi).into()));
}
#[inline]
@@ -250,7 +272,7 @@ impl Window {
}
#[inline]
pub fn hidpi_factor(&self) -> i32 {
pub fn scale_factor(&self) -> i32 {
get_dpi_factor(&self.surface)
}
@@ -325,7 +347,7 @@ impl Window {
}
#[inline]
pub fn set_cursor_position(&self, _pos: LogicalPosition) -> Result<(), ExternalError> {
pub fn set_cursor_position(&self, _pos: Position) -> Result<(), ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
@@ -375,6 +397,7 @@ impl Drop for Window {
struct InternalWindow {
surface: wl_surface::WlSurface,
// TODO: CONVERT TO LogicalSize<u32>s
newsize: Option<(u32, u32)>,
size: Arc<Mutex<(u32, u32)>>,
need_refresh: Arc<Mutex<bool>>,
@@ -395,6 +418,7 @@ pub struct WindowStore {
pub struct WindowStoreForEach<'a> {
pub newsize: Option<(u32, u32)>,
pub size: &'a mut (u32, u32),
pub prev_dpi: i32,
pub new_dpi: Option<i32>,
pub closed: bool,
pub grab_cursor: Option<bool>,
@@ -460,6 +484,7 @@ impl WindowStore {
f(WindowStoreForEach {
newsize: window.newsize.take(),
size: &mut *(window.size.lock().unwrap()),
prev_dpi: window.current_dpi,
new_dpi: window.new_dpi,
closed: window.closed,
grab_cursor: window.cursor_grab_changed.lock().unwrap().take(),

View File

@@ -1,4 +1,4 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc, slice};
use std::{cell::RefCell, collections::HashMap, rc::Rc, slice, sync::Arc};
use libc::{c_char, c_int, c_long, c_uint, c_ulong};
@@ -11,7 +11,7 @@ use super::{
use util::modifiers::{ModifierKeyState, ModifierKeymap};
use crate::{
dpi::{LogicalPosition, LogicalSize},
dpi::{PhysicalPosition, PhysicalSize},
event::{
DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, TouchPhase, WindowEvent,
},
@@ -45,7 +45,7 @@ impl<T: 'static> EventProcessor<T> {
fn with_window<F, Ret>(&self, window_id: ffi::Window, callback: F) -> Option<Ret>
where
F: Fn(&UnownedWindow) -> Ret,
F: Fn(&Arc<UnownedWindow>) -> Ret,
{
let mut deleted = false;
let window_id = WindowId(window_id);
@@ -59,7 +59,7 @@ impl<T: 'static> EventProcessor<T> {
deleted = arc.is_none();
arc
})
.map(|window| callback(&*window));
.map(|window| callback(&window));
if deleted {
// Garbage collection
wt.windows.borrow_mut().remove(&window_id);
@@ -107,7 +107,7 @@ impl<T: 'static> EventProcessor<T> {
pub(super) fn process_event<F>(&mut self, xev: &mut ffi::XEvent, mut callback: F)
where
F: FnMut(Event<T>),
F: FnMut(Event<'_, T>),
{
let wt = get_xtarget(&self.target);
// XFilterEvent tells us when an event has been discarded by the input method.
@@ -321,16 +321,11 @@ impl<T: 'static> EventProcessor<T> {
}
ffi::ConfigureNotify => {
#[derive(Debug, Default)]
struct Events {
resized: Option<WindowEvent>,
moved: Option<WindowEvent>,
dpi_changed: Option<WindowEvent>,
}
let xev: &ffi::XConfigureEvent = xev.as_ref();
let xwindow = xev.window;
let events = self.with_window(xwindow, |window| {
let window_id = mkwid(xwindow);
if let Some(window) = self.with_window(xwindow, Arc::clone) {
// So apparently...
// `XSendEvent` (synthetic `ConfigureNotify`) -> position relative to root
// `XConfigureNotify` (real `ConfigureNotify`) -> position relative to parent
@@ -344,7 +339,6 @@ impl<T: 'static> EventProcessor<T> {
let new_inner_size = (xev.width as u32, xev.height as u32);
let new_inner_position = (xev.x as i32, xev.y as i32);
let mut monitor = window.current_monitor(); // This must be done *before* locking!
let mut shared_state_lock = window.shared_state.lock();
let (mut resized, moved) = {
@@ -374,8 +368,6 @@ impl<T: 'static> EventProcessor<T> {
(resized, moved)
};
let mut events = Events::default();
let new_outer_position = if moved || shared_state_lock.position.is_none() {
// We need to convert client area position to window position.
let frame_extents = shared_state_lock
@@ -392,9 +384,10 @@ impl<T: 'static> EventProcessor<T> {
.inner_pos_to_outer(new_inner_position.0, new_inner_position.1);
shared_state_lock.position = Some(outer);
if moved {
let logical_position =
LogicalPosition::from_physical(outer, monitor.hidpi_factor);
events.moved = Some(WindowEvent::Moved(logical_position));
callback(Event::WindowEvent {
window_id,
event: WindowEvent::Moved(outer.into()),
});
}
outer
} else {
@@ -406,36 +399,51 @@ impl<T: 'static> EventProcessor<T> {
// resizing by dragging across monitors *without* dropping the window.
let (width, height) = shared_state_lock
.dpi_adjusted
.unwrap_or_else(|| (xev.width as f64, xev.height as f64));
.unwrap_or_else(|| (xev.width as u32, xev.height as u32));
let last_hidpi_factor = shared_state_lock.last_monitor.hidpi_factor;
let new_hidpi_factor = {
let last_scale_factor = shared_state_lock.last_monitor.scale_factor;
let new_scale_factor = {
let window_rect = util::AaRect::new(new_outer_position, new_inner_size);
let new_monitor = wt.xconn.get_monitor_for_window(Some(window_rect));
let monitor = wt.xconn.get_monitor_for_window(Some(window_rect));
if new_monitor.is_dummy() {
if monitor.is_dummy() {
// Avoid updating monitor using a dummy monitor handle
last_hidpi_factor
last_scale_factor
} else {
monitor = new_monitor;
shared_state_lock.last_monitor = monitor.clone();
monitor.hidpi_factor
monitor.scale_factor
}
};
if last_hidpi_factor != new_hidpi_factor {
events.dpi_changed =
Some(WindowEvent::HiDpiFactorChanged(new_hidpi_factor));
let (new_width, new_height, flusher) = window.adjust_for_dpi(
last_hidpi_factor,
new_hidpi_factor,
if last_scale_factor != new_scale_factor {
let (new_width, new_height) = window.adjust_for_dpi(
last_scale_factor,
new_scale_factor,
width,
height,
&shared_state_lock,
);
flusher.queue();
shared_state_lock.dpi_adjusted = Some((new_width, new_height));
// if the DPI factor changed, force a resize event to ensure the logical
// size is computed with the right DPI factor
resized = true;
let old_inner_size = PhysicalSize::new(width, height);
let mut new_inner_size = PhysicalSize::new(new_width, new_height);
callback(Event::WindowEvent {
window_id,
event: WindowEvent::ScaleFactorChanged {
scale_factor: new_scale_factor,
new_inner_size: &mut new_inner_size,
},
});
if new_inner_size != old_inner_size {
window.set_inner_size_physical(
new_inner_size.width,
new_inner_size.height,
);
shared_state_lock.dpi_adjusted = Some(new_inner_size.into());
// if the DPI factor changed, force a resize event to ensure the logical
// size is computed with the right DPI factor
resized = true;
}
}
}
@@ -444,44 +452,19 @@ impl<T: 'static> EventProcessor<T> {
// WMs constrain the window size, making the resize fail. This would cause an endless stream of
// XResizeWindow requests, making Xorg, the winit client, and the WM consume 100% of CPU.
if let Some(adjusted_size) = shared_state_lock.dpi_adjusted {
let rounded_size = (
adjusted_size.0.round() as u32,
adjusted_size.1.round() as u32,
);
if new_inner_size == rounded_size || !util::wm_name_is_one_of(&["Xfwm4"]) {
if new_inner_size == adjusted_size || !util::wm_name_is_one_of(&["Xfwm4"]) {
// When this finally happens, the event will not be synthetic.
shared_state_lock.dpi_adjusted = None;
} else {
unsafe {
(wt.xconn.xlib.XResizeWindow)(
wt.xconn.display,
xwindow,
rounded_size.0 as c_uint,
rounded_size.1 as c_uint,
);
}
window.set_inner_size_physical(adjusted_size.0, adjusted_size.1);
}
}
if resized {
let logical_size =
LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor);
events.resized = Some(WindowEvent::Resized(logical_size));
}
events
});
if let Some(events) = events {
let window_id = mkwid(xwindow);
if let Some(event) = events.dpi_changed {
callback(Event::WindowEvent { window_id, event });
}
if let Some(event) = events.resized {
callback(Event::WindowEvent { window_id, event });
}
if let Some(event) = events.moved {
callback(Event::WindowEvent { window_id, event });
callback(Event::WindowEvent {
window_id,
event: WindowEvent::Resized(new_inner_size.into()),
});
}
}
}
@@ -579,6 +562,7 @@ impl<T: 'static> EventProcessor<T> {
let modifiers = self.device_mod_state.modifiers();
#[allow(deprecated)]
callback(Event::WindowEvent {
window_id,
event: WindowEvent::KeyboardInput {
@@ -728,24 +712,17 @@ impl<T: 'static> EventProcessor<T> {
util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
});
if cursor_moved == Some(true) {
let dpi_factor =
self.with_window(xev.event, |window| window.hidpi_factor());
if let Some(dpi_factor) = dpi_factor {
let position = LogicalPosition::from_physical(
(xev.event_x as f64, xev.event_y as f64),
dpi_factor,
);
callback(Event::WindowEvent {
window_id,
event: CursorMoved {
device_id,
position,
modifiers,
},
});
} else {
return;
}
let position =
PhysicalPosition::new(xev.event_x as i32, xev.event_y as i32);
callback(Event::WindowEvent {
window_id,
event: CursorMoved {
device_id,
position,
modifiers,
},
});
} else if cursor_moved.is_none() {
return;
}
@@ -836,18 +813,14 @@ impl<T: 'static> EventProcessor<T> {
}
}
if let Some(dpi_factor) =
self.with_window(xev.event, |window| window.hidpi_factor())
{
if self.window_exists(xev.event) {
callback(Event::WindowEvent {
window_id,
event: CursorEntered { device_id },
});
let position = LogicalPosition::from_physical(
(xev.event_x as f64, xev.event_y as f64),
dpi_factor,
);
let position =
PhysicalPosition::new(xev.event_x as i32, xev.event_y as i32);
// The mods field on this event isn't actually populated, so query the
// pointer device. In the future, we can likely remove this round-trip by
@@ -890,11 +863,6 @@ impl<T: 'static> EventProcessor<T> {
ffi::XI_FocusIn => {
let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
let dpi_factor =
match self.with_window(xev.event, |window| window.hidpi_factor()) {
Some(dpi_factor) => dpi_factor,
None => return,
};
let window_id = mkwid(xev.event);
wt.ime
@@ -920,10 +888,9 @@ impl<T: 'static> EventProcessor<T> {
.map(|device| device.attachment)
.unwrap_or(2);
let position = LogicalPosition::from_physical(
(xev.event_x as f64, xev.event_y as f64),
dpi_factor,
);
let position =
PhysicalPosition::new(xev.event_x as i32, xev.event_y as i32);
callback(Event::WindowEvent {
window_id,
event: CursorMoved {
@@ -966,15 +933,11 @@ impl<T: 'static> EventProcessor<T> {
ffi::XI_TouchEnd => TouchPhase::Ended,
_ => unreachable!(),
};
let dpi_factor =
self.with_window(xev.event, |window| window.hidpi_factor());
if let Some(dpi_factor) = dpi_factor {
if self.window_exists(xev.event) {
let id = xev.detail as u64;
let modifiers = self.device_mod_state.modifiers();
let location = LogicalPosition::from_physical(
(xev.event_x as f64, xev.event_y as f64),
dpi_factor,
);
let location =
PhysicalPosition::new(xev.event_x as f64, xev.event_y as f64);
// Mouse cursor position changes when touch events are received.
// Only the first concurrently active touch ID moves the mouse cursor.
@@ -984,7 +947,7 @@ impl<T: 'static> EventProcessor<T> {
window_id,
event: WindowEvent::CursorMoved {
device_id: mkdid(util::VIRTUAL_CORE_POINTER),
position: location,
position: location.cast(),
modifiers,
},
});
@@ -1090,6 +1053,7 @@ impl<T: 'static> EventProcessor<T> {
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
let modifiers = self.device_mod_state.modifiers();
#[allow(deprecated)]
callback(Event::DeviceEvent {
device_id,
event: DeviceEvent::Key(KeyboardInput {
@@ -1157,27 +1121,48 @@ impl<T: 'static> EventProcessor<T> {
.iter()
.find(|prev_monitor| prev_monitor.name == new_monitor.name)
.map(|prev_monitor| {
if new_monitor.hidpi_factor != prev_monitor.hidpi_factor {
if new_monitor.scale_factor != prev_monitor.scale_factor {
for (window_id, window) in wt.windows.borrow().iter() {
if let Some(window) = window.upgrade() {
// Check if the window is on this monitor
let monitor = window.current_monitor();
if monitor.name == new_monitor.name {
callback(Event::WindowEvent {
window_id: mkwid(window_id.0),
event: WindowEvent::HiDpiFactorChanged(
new_monitor.hidpi_factor,
),
});
let (width, height) =
window.inner_size_physical();
let (_, _, flusher) = window.adjust_for_dpi(
prev_monitor.hidpi_factor,
new_monitor.hidpi_factor,
width as f64,
height as f64,
let (new_width, new_height) = window
.adjust_for_dpi(
prev_monitor.scale_factor,
new_monitor.scale_factor,
width,
height,
&*window.shared_state.lock(),
);
let window_id = crate::window::WindowId(
crate::platform_impl::platform::WindowId::X(
*window_id,
),
);
flusher.queue();
let old_inner_size =
PhysicalSize::new(width, height);
let mut new_inner_size =
PhysicalSize::new(new_width, new_height);
callback(Event::WindowEvent {
window_id,
event: WindowEvent::ScaleFactorChanged {
scale_factor: new_monitor.scale_factor,
new_inner_size: &mut new_inner_size,
},
});
if new_inner_size != old_inner_size {
let (new_width, new_height) =
new_inner_size.into();
window.set_inner_size_physical(
new_width, new_height,
);
}
}
}
}
@@ -1203,7 +1188,7 @@ impl<T: 'static> EventProcessor<T> {
state: ElementState,
callback: &mut F,
) where
F: FnMut(Event<T>),
F: FnMut(Event<'_, T>),
{
let wt = get_xtarget(&self.target);
@@ -1222,6 +1207,7 @@ impl<T: 'static> EventProcessor<T> {
let keysym = wt.xconn.keycode_to_keysym(keycode);
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
#[allow(deprecated)]
callback(Event::WindowEvent {
window_id,
event: WindowEvent::KeyboardInput {

View File

@@ -24,7 +24,7 @@ pub use self::{
use std::{
cell::RefCell,
collections::{HashMap, HashSet, VecDeque},
collections::{HashMap, HashSet},
ffi::CStr,
mem::{self, MaybeUninit},
ops::Deref,
@@ -37,6 +37,10 @@ use std::{
use libc::{self, setlocale, LC_CTYPE};
use mio::{unix::EventedFd, Events, Poll, PollOpt, Ready, Token};
use mio_extras::channel::{channel, Receiver, SendError, Sender};
use self::{
dnd::{Dnd, DndState},
event_processor::EventProcessor,
@@ -51,6 +55,9 @@ use crate::{
window::WindowAttributes,
};
const X_TOKEN: Token = Token(0);
const USER_TOKEN: Token = Token(1);
pub struct EventLoopWindowTarget<T> {
xconn: Arc<XConnection>,
wm_delete_window: ffi::Atom,
@@ -64,18 +71,15 @@ pub struct EventLoopWindowTarget<T> {
}
pub struct EventLoop<T: 'static> {
inner_loop: ::calloop::EventLoop<()>,
_x11_source: ::calloop::Source<::calloop::generic::Generic<::calloop::generic::EventedRawFd>>,
_user_source: ::calloop::Source<::calloop::channel::Channel<T>>,
pending_user_events: Rc<RefCell<VecDeque<T>>>,
event_processor: Rc<RefCell<EventProcessor<T>>>,
user_sender: ::calloop::channel::Sender<T>,
pending_events: Rc<RefCell<VecDeque<Event<T>>>>,
pub(crate) target: Rc<RootELW<T>>,
poll: Poll,
event_processor: EventProcessor<T>,
user_channel: Receiver<T>,
user_sender: Sender<T>,
target: Rc<RootELW<T>>,
}
pub struct EventLoopProxy<T: 'static> {
user_sender: ::calloop::channel::Sender<T>,
user_sender: Sender<T>,
}
impl<T: 'static> Clone for EventLoopProxy<T> {
@@ -171,28 +175,27 @@ impl<T: 'static> EventLoop<T> {
_marker: ::std::marker::PhantomData,
});
// A calloop event loop to drive us
let inner_loop = ::calloop::EventLoop::new().unwrap();
let poll = Poll::new().unwrap();
// Handle user events
let pending_user_events = Rc::new(RefCell::new(VecDeque::new()));
let pending_user_events2 = pending_user_events.clone();
let (user_sender, user_channel) = channel();
let (user_sender, user_channel) = ::calloop::channel::channel();
poll.register(
&EventedFd(&get_xtarget(&target).xconn.x11_fd),
X_TOKEN,
Ready::readable(),
PollOpt::level(),
)
.unwrap();
let _user_source = inner_loop
.handle()
.insert_source(user_channel, move |evt, &mut ()| {
if let ::calloop::channel::Event::Msg(msg) = evt {
pending_user_events2.borrow_mut().push_back(msg);
}
})
.unwrap();
poll.register(
&user_channel,
USER_TOKEN,
Ready::readable(),
PollOpt::level(),
)
.unwrap();
// Handle X11 events
let pending_events: Rc<RefCell<VecDeque<_>>> = Default::default();
let processor = EventProcessor {
let event_processor = EventProcessor {
target: target.clone(),
dnd,
devices: Default::default(),
@@ -212,38 +215,12 @@ impl<T: 'static> EventLoop<T> {
.select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask)
.queue();
processor.init_device(ffi::XIAllDevices);
let processor = Rc::new(RefCell::new(processor));
let event_processor = processor.clone();
// Setup the X11 event source
let mut x11_events =
::calloop::generic::Generic::from_raw_fd(get_xtarget(&target).xconn.x11_fd);
x11_events.set_interest(::calloop::mio::Ready::readable());
let _x11_source = inner_loop
.handle()
.insert_source(x11_events, {
let pending_events = pending_events.clone();
move |evt, &mut ()| {
if evt.readiness.is_readable() {
let mut processor = processor.borrow_mut();
let mut pending_events = pending_events.borrow_mut();
let mut pending_redraws = pending_redraws.lock().unwrap();
drain_events(&mut processor, &mut pending_events, &mut pending_redraws);
}
}
})
.unwrap();
event_processor.init_device(ffi::XIAllDevices);
let result = EventLoop {
inner_loop,
pending_events,
_x11_source,
_user_source,
poll,
user_channel,
user_sender,
pending_user_events,
event_processor,
target,
};
@@ -261,12 +238,16 @@ impl<T: 'static> EventLoop<T> {
&self.target
}
pub(crate) fn x_connection(&self) -> &Arc<XConnection> {
get_xtarget(&self.target).x_connection()
}
pub fn run_return<F>(&mut self, mut callback: F)
where
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
let mut control_flow = ControlFlow::default();
let wt = get_xtarget(&self.target);
let mut events = Events::with_capacity(8);
callback(
crate::event::Event::NewEvents(crate::event::StartCause::Init),
@@ -275,22 +256,16 @@ impl<T: 'static> EventLoop<T> {
);
loop {
self.drain_events();
// Process all pending events
self.drain_events(&mut callback, &mut control_flow);
// Empty the event buffer
{
let mut guard = self.pending_events.borrow_mut();
for evt in guard.drain(..) {
sticky_exit_callback(evt, &self.target, &mut control_flow, &mut callback);
}
}
let wt = get_xtarget(&self.target);
// Empty the user event buffer
{
let mut guard = self.pending_user_events.borrow_mut();
for evt in guard.drain(..) {
while let Ok(event) = self.user_channel.try_recv() {
sticky_exit_callback(
crate::event::Event::UserEvent(evt),
crate::event::Event::UserEvent(event),
&self.target,
&mut control_flow,
&mut callback,
@@ -331,7 +306,7 @@ impl<T: 'static> EventLoop<T> {
}
let start = Instant::now();
let (mut cause, deadline, mut timeout);
let (mut cause, deadline, timeout);
match control_flow {
ControlFlow::Exit => break,
@@ -362,26 +337,39 @@ impl<T: 'static> EventLoop<T> {
}
}
if self.events_waiting() {
timeout = Some(Duration::from_millis(0));
}
if self.event_processor.poll() {
// If the XConnection already contains buffered events, we don't
// need to wait for data on the socket.
// However, we still need to check for user events.
self.poll
.poll(&mut events, Some(Duration::from_millis(0)))
.unwrap();
events.clear();
self.inner_loop.dispatch(timeout, &mut ()).unwrap();
callback(
crate::event::Event::NewEvents(cause),
&self.target,
&mut control_flow,
);
} else {
self.poll.poll(&mut events, timeout).unwrap();
events.clear();
if let Some(deadline) = deadline {
if deadline > Instant::now() {
let wait_cancelled = deadline.map_or(false, |deadline| Instant::now() < deadline);
if wait_cancelled {
cause = StartCause::WaitCancelled {
start,
requested_resume: Some(deadline),
requested_resume: deadline,
};
}
}
callback(
crate::event::Event::NewEvents(cause),
&self.target,
&mut control_flow,
);
callback(
crate::event::Event::NewEvents(cause),
&self.target,
&mut control_flow,
);
}
}
callback(
@@ -393,44 +381,42 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(mut self, callback: F) -> !
where
F: 'static + FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
F: 'static + FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
self.run_return(callback);
::std::process::exit(0);
}
fn drain_events(&self) {
let mut processor = self.event_processor.borrow_mut();
let mut pending_events = self.pending_events.borrow_mut();
fn drain_events<F>(&mut self, callback: &mut F, control_flow: &mut ControlFlow)
where
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
let target = &self.target;
let mut xev = MaybeUninit::uninit();
let wt = get_xtarget(&self.target);
let mut pending_redraws = wt.pending_redraws.lock().unwrap();
drain_events(&mut processor, &mut pending_events, &mut pending_redraws);
}
fn events_waiting(&self) -> bool {
!self.pending_events.borrow().is_empty() || self.event_processor.borrow().poll()
}
}
fn drain_events<T: 'static>(
processor: &mut EventProcessor<T>,
pending_events: &mut VecDeque<Event<T>>,
pending_redraws: &mut HashSet<WindowId>,
) {
let mut callback = |event| {
if let Event::RedrawRequested(crate::window::WindowId(super::WindowId::X(wid))) = event {
pending_redraws.insert(wid);
} else {
pending_events.push_back(event);
while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } {
let mut xev = unsafe { xev.assume_init() };
self.event_processor.process_event(&mut xev, |event| {
sticky_exit_callback(
event,
target,
control_flow,
&mut |event, window_target, control_flow| {
if let Event::RedrawRequested(crate::window::WindowId(
super::WindowId::X(wid),
)) = event
{
pending_redraws.insert(wid);
} else {
callback(event, window_target, control_flow);
}
},
);
});
}
};
// process all pending events
let mut xev = MaybeUninit::uninit();
while unsafe { processor.poll_one_event(xev.as_mut_ptr()) } {
let mut xev = unsafe { xev.assume_init() };
processor.process_event(&mut xev, &mut callback);
}
}
@@ -452,7 +438,7 @@ impl<T> EventLoopWindowTarget<T> {
impl<T: 'static> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.user_sender.send(event).map_err(|e| {
EventLoopClosed(if let ::calloop::channel::SendError::Disconnected(x) = e {
EventLoopClosed(if let SendError::Disconnected(x) = e {
x
} else {
unreachable!()

View File

@@ -38,7 +38,7 @@ pub struct VideoMode {
impl VideoMode {
#[inline]
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
self.size.into()
}
@@ -73,7 +73,7 @@ pub struct MonitorHandle {
/// If the monitor is the primary one
primary: bool,
/// The DPI scale factor
pub(crate) hidpi_factor: f64,
pub(crate) scale_factor: f64,
/// Used to determine which windows are on this monitor
pub(crate) rect: util::AaRect,
/// Supported video modes on this monitor
@@ -114,14 +114,14 @@ impl MonitorHandle {
crtc: *mut XRRCrtcInfo,
primary: bool,
) -> Option<Self> {
let (name, hidpi_factor, video_modes) = unsafe { xconn.get_output_info(resources, crtc)? };
let (name, scale_factor, video_modes) = unsafe { xconn.get_output_info(resources, crtc)? };
let dimensions = unsafe { ((*crtc).width as u32, (*crtc).height as u32) };
let position = unsafe { ((*crtc).x as i32, (*crtc).y as i32) };
let rect = util::AaRect::new(position, dimensions);
Some(MonitorHandle {
id,
name,
hidpi_factor,
scale_factor,
dimensions,
position,
primary,
@@ -134,7 +134,7 @@ impl MonitorHandle {
MonitorHandle {
id: 0,
name: "<dummy monitor>".into(),
hidpi_factor: 1.0,
scale_factor: 1.0,
dimensions: (1, 1),
position: (0, 0),
primary: true,
@@ -157,17 +157,17 @@ impl MonitorHandle {
self.id as u32
}
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
self.dimensions.into()
}
pub fn position(&self) -> PhysicalPosition {
pub fn position(&self) -> PhysicalPosition<i32> {
self.position.into()
}
#[inline]
pub fn hidpi_factor(&self) -> f64 {
self.hidpi_factor
pub fn scale_factor(&self) -> f64 {
self.scale_factor
}
#[inline]

View File

@@ -137,9 +137,9 @@ impl FrameExtentsHeuristic {
pub fn inner_pos_to_outer_logical(
&self,
mut logical: LogicalPosition,
mut logical: LogicalPosition<f64>,
factor: f64,
) -> LogicalPosition {
) -> LogicalPosition<f64> {
use self::FrameExtentsHeuristicPath::*;
if self.heuristic_path != UnsupportedBordered {
let frame_extents = self.frame_extents.as_logical(factor);
@@ -166,9 +166,9 @@ impl FrameExtentsHeuristic {
pub fn inner_size_to_outer_logical(
&self,
mut logical: LogicalSize,
mut logical: LogicalSize<f64>,
factor: f64,
) -> LogicalSize {
) -> LogicalSize<f64> {
let frame_extents = self.frame_extents.as_logical(factor);
logical.width += frame_extents.left + frame_extents.right;
logical.height += frame_extents.top + frame_extents.bottom;

View File

@@ -18,9 +18,9 @@ impl ModifiersState {
pub(crate) fn from_x11_mask(mask: c_uint) -> Self {
let mut m = ModifiersState::empty();
m.set(ModifiersState::SHIFT, mask & ffi::Mod1Mask != 0);
m.set(ModifiersState::CTRL, mask & ffi::ShiftMask != 0);
m.set(ModifiersState::ALT, mask & ffi::ControlMask != 0);
m.set(ModifiersState::ALT, mask & ffi::Mod1Mask != 0);
m.set(ModifiersState::SHIFT, mask & ffi::ShiftMask != 0);
m.set(ModifiersState::CTRL, mask & ffi::ControlMask != 0);
m.set(ModifiersState::LOGO, mask & ffi::Mod4Mask != 0);
m
}

View File

@@ -4,7 +4,7 @@ use super::{
ffi::{CurrentTime, RRCrtc, RRMode, Success, XRRCrtcInfo, XRRScreenResources},
*,
};
use crate::{dpi::validate_hidpi_factor, platform_impl::platform::x11::VideoMode};
use crate::{dpi::validate_scale_factor, platform_impl::platform::x11::VideoMode};
/// Represents values of `WINIT_HIDPI_FACTOR`.
pub enum EnvVarDPI {
@@ -26,7 +26,7 @@ pub fn calc_dpi_factor(
let ppmm = ((width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)).sqrt();
// Quantize 1/12 step size
let dpi_factor = ((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0);
assert!(validate_hidpi_factor(dpi_factor));
assert!(validate_scale_factor(dpi_factor));
dpi_factor
}
@@ -100,8 +100,14 @@ impl XConnection {
(*output_info).nameLen as usize,
);
let name = String::from_utf8_lossy(name_slice).into();
// Override DPI if `WINIT_HIDPI_FACTOR` variable is set
let dpi_env = env::var("WINIT_HIDPI_FACTOR").ok().map_or_else(
// Override DPI if `WINIT_X11_SCALE_FACTOR` variable is set
let deprecated_dpi_override = env::var("WINIT_HIDPI_FACTOR").ok();
if deprecated_dpi_override.is_some() {
warn!(
"The WINIT_HIDPI_FACTOR environment variable is deprecated; use WINIT_X11_SCALE_FACTOR"
)
}
let dpi_env = env::var("WINIT_X11_SCALE_FACTOR").ok().map_or_else(
|| EnvVarDPI::NotSet,
|var| {
if var.to_lowercase() == "randr" {
@@ -112,14 +118,14 @@ impl XConnection {
EnvVarDPI::NotSet
} else {
panic!(
"`WINIT_HIDPI_FACTOR` invalid; DPI factors must be either normal floats greater than 0, or `randr`. Got `{}`",
"`WINIT_X11_SCALE_FACTOR` invalid; DPI factors must be either normal floats greater than 0, or `randr`. Got `{}`",
var
);
}
},
);
let hidpi_factor = match dpi_env {
let scale_factor = match dpi_env {
EnvVarDPI::Randr => calc_dpi_factor(
((*crtc).width as u32, (*crtc).height as u32),
(
@@ -128,9 +134,9 @@ impl XConnection {
),
),
EnvVarDPI::Scale(dpi_override) => {
if !validate_hidpi_factor(dpi_override) {
if !validate_scale_factor(dpi_override) {
panic!(
"`WINIT_HIDPI_FACTOR` invalid; DPI factors must be either normal floats greater than 0, or `randr`. Got `{}`",
"`WINIT_X11_SCALE_FACTOR` invalid; DPI factors must be either normal floats greater than 0, or `randr`. Got `{}`",
dpi_override,
);
}
@@ -152,7 +158,7 @@ impl XConnection {
};
(self.xrandr.XRRFreeOutputInfo)(output_info);
Some((name, hidpi_factor, modes))
Some((name, scale_factor, modes))
}
pub fn set_crtc_config(&self, crtc_id: RRCrtc, mode_id: RRMode) -> Result<(), ()> {
unsafe {

View File

@@ -15,7 +15,7 @@ use libc;
use parking_lot::Mutex;
use crate::{
dpi::{LogicalPosition, LogicalSize},
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
platform_impl::{
@@ -36,7 +36,7 @@ pub struct SharedState {
pub inner_position: Option<(i32, i32)>,
pub inner_position_rel_parent: Option<(i32, i32)>,
pub last_monitor: X11MonitorHandle,
pub dpi_adjusted: Option<(f64, f64)>,
pub dpi_adjusted: Option<(u32, u32)>,
pub fullscreen: Option<Fullscreen>,
// Set when application calls `set_fullscreen` when window is not visible
pub desired_fullscreen: Option<Option<Fullscreen>>,
@@ -45,8 +45,10 @@ pub struct SharedState {
// Used to restore video mode after exiting fullscreen
pub desktop_video_mode: Option<(ffi::RRCrtc, ffi::RRMode)>,
pub frame_extents: Option<util::FrameExtentsHeuristic>,
pub min_inner_size: Option<LogicalSize>,
pub max_inner_size: Option<LogicalSize>,
pub min_inner_size: Option<Size>,
pub max_inner_size: Option<Size>,
pub resize_increments: Option<Size>,
pub base_size: Option<Size>,
pub visibility: Visibility,
}
@@ -83,6 +85,8 @@ impl SharedState {
frame_extents: None,
min_inner_size: None,
max_inner_size: None,
resize_increments: None,
base_size: None,
})
}
}
@@ -132,24 +136,24 @@ impl UnownedWindow {
})
.unwrap_or_else(|| monitors.swap_remove(0))
};
let dpi_factor = guessed_monitor.hidpi_factor();
let dpi_factor = guessed_monitor.scale_factor();
info!("Guessed window DPI factor: {}", dpi_factor);
info!("Guessed window scale factor: {}", dpi_factor);
let max_inner_size: Option<(u32, u32)> = window_attrs
.max_inner_size
.map(|size| size.to_physical(dpi_factor).into());
.map(|size| size.to_physical::<u32>(dpi_factor).into());
let min_inner_size: Option<(u32, u32)> = window_attrs
.min_inner_size
.map(|size| size.to_physical(dpi_factor).into());
.map(|size| size.to_physical::<u32>(dpi_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))
.or_else(|| Some((800, 600).into()))
.map(|size| size.to_physical(dpi_factor))
.map(Into::into)
.unwrap();
if let Some(max) = max_inner_size {
@@ -235,7 +239,7 @@ impl UnownedWindow {
)
};
let window = UnownedWindow {
let mut window = UnownedWindow {
xconn: Arc::clone(xconn),
xwindow,
root,
@@ -320,10 +324,11 @@ impl UnownedWindow {
{
let mut min_inner_size = window_attrs
.min_inner_size
.map(|size| size.to_physical(dpi_factor));
.map(|size| size.to_physical::<u32>(dpi_factor));
let mut max_inner_size = window_attrs
.max_inner_size
.map(|size| size.to_physical(dpi_factor));
.map(|size| size.to_physical::<u32>(dpi_factor));
if !window_attrs.resizable {
if util::wm_name_is_one_of(&["Xfwm4"]) {
warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4");
@@ -331,9 +336,11 @@ impl UnownedWindow {
max_inner_size = Some(dimensions.into());
min_inner_size = Some(dimensions.into());
let mut shared_state_lock = window.shared_state.lock();
shared_state_lock.min_inner_size = window_attrs.min_inner_size;
shared_state_lock.max_inner_size = window_attrs.max_inner_size;
let mut shared_state = window.shared_state.get_mut();
shared_state.min_inner_size = window_attrs.min_inner_size;
shared_state.max_inner_size = window_attrs.max_inner_size;
shared_state.resize_increments = pl_attribs.resize_increments;
shared_state.base_size = pl_attribs.base_size;
}
}
@@ -341,8 +348,16 @@ impl UnownedWindow {
normal_hints.set_size(Some(dimensions));
normal_hints.set_min_size(min_inner_size.map(Into::into));
normal_hints.set_max_size(max_inner_size.map(Into::into));
normal_hints.set_resize_increments(pl_attribs.resize_increments);
normal_hints.set_base_size(pl_attribs.base_size);
normal_hints.set_resize_increments(
pl_attribs
.resize_increments
.map(|size| size.to_physical::<u32>(dpi_factor).into()),
);
normal_hints.set_base_size(
pl_attribs
.base_size
.map(|size| size.to_physical::<u32>(dpi_factor).into()),
);
xconn.set_normal_hints(window.xwindow, normal_hints).queue();
}
@@ -440,16 +455,6 @@ impl UnownedWindow {
.map_err(|x_err| os_error!(OsError::XError(x_err)))
}
fn logicalize_coords(&self, (x, y): (i32, i32)) -> LogicalPosition {
let dpi = self.hidpi_factor();
LogicalPosition::from_physical((x, y), dpi)
}
fn logicalize_size(&self, (width, height): (u32, u32)) -> LogicalSize {
let dpi = self.hidpi_factor();
LogicalSize::from_physical((width, height), dpi)
}
fn set_pid(&self) -> Option<util::Flusher<'_>> {
let pid_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_PID\0") };
let client_machine_atom = unsafe { self.xconn.get_atom_unchecked(b"WM_CLIENT_MACHINE\0") };
@@ -951,11 +956,11 @@ impl UnownedWindow {
}
#[inline]
pub fn outer_position(&self) -> Result<LogicalPosition, NotSupportedError> {
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
let extents = (*self.shared_state.lock()).frame_extents.clone();
if let Some(extents) = extents {
let logical = self.inner_position().unwrap();
Ok(extents.inner_pos_to_outer_logical(logical, self.hidpi_factor()))
let (x, y) = self.inner_position_physical();
Ok(extents.inner_pos_to_outer(x, y).into())
} else {
self.update_cached_frame_extents();
self.outer_position()
@@ -972,8 +977,8 @@ impl UnownedWindow {
}
#[inline]
pub fn inner_position(&self) -> Result<LogicalPosition, NotSupportedError> {
Ok(self.logicalize_coords(self.inner_position_physical()))
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
Ok(self.inner_position_physical().into())
}
pub(crate) fn set_position_inner(&self, mut x: i32, mut y: i32) -> util::Flusher<'_> {
@@ -1002,8 +1007,8 @@ impl UnownedWindow {
}
#[inline]
pub fn set_outer_position(&self, logical_position: LogicalPosition) {
let (x, y) = logical_position.to_physical(self.hidpi_factor()).into();
pub fn set_outer_position(&self, position: Position) {
let (x, y) = position.to_physical::<i32>(self.scale_factor()).into();
self.set_position_physical(x, y);
}
@@ -1017,16 +1022,16 @@ impl UnownedWindow {
}
#[inline]
pub fn inner_size(&self) -> LogicalSize {
self.logicalize_size(self.inner_size_physical())
pub fn inner_size(&self) -> PhysicalSize<u32> {
self.inner_size_physical().into()
}
#[inline]
pub fn outer_size(&self) -> LogicalSize {
pub fn outer_size(&self) -> PhysicalSize<u32> {
let extents = self.shared_state.lock().frame_extents.clone();
if let Some(extents) = extents {
let logical = self.inner_size();
extents.inner_size_to_outer_logical(logical, self.hidpi_factor())
let (width, height) = self.inner_size_physical();
extents.inner_size_to_outer(width, height).into()
} else {
self.update_cached_frame_extents();
self.outer_size()
@@ -1047,9 +1052,9 @@ impl UnownedWindow {
}
#[inline]
pub fn set_inner_size(&self, logical_size: LogicalSize) {
let dpi_factor = self.hidpi_factor();
let (width, height) = logical_size.to_physical(dpi_factor).into();
pub fn set_inner_size(&self, size: Size) {
let dpi_factor = self.scale_factor();
let (width, height) = size.to_physical::<u32>(dpi_factor).into();
self.set_inner_size_physical(width, height);
}
@@ -1070,10 +1075,10 @@ impl UnownedWindow {
}
#[inline]
pub fn set_min_inner_size(&self, logical_dimensions: Option<LogicalSize>) {
self.shared_state.lock().min_inner_size = logical_dimensions;
let physical_dimensions = logical_dimensions
.map(|logical_dimensions| logical_dimensions.to_physical(self.hidpi_factor()).into());
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
self.shared_state.lock().min_inner_size = dimensions;
let physical_dimensions =
dimensions.map(|dimensions| dimensions.to_physical::<u32>(self.scale_factor()).into());
self.set_min_inner_size_physical(physical_dimensions);
}
@@ -1083,10 +1088,10 @@ impl UnownedWindow {
}
#[inline]
pub fn set_max_inner_size(&self, logical_dimensions: Option<LogicalSize>) {
self.shared_state.lock().max_inner_size = logical_dimensions;
let physical_dimensions = logical_dimensions
.map(|logical_dimensions| logical_dimensions.to_physical(self.hidpi_factor()).into());
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
self.shared_state.lock().max_inner_size = dimensions;
let physical_dimensions =
dimensions.map(|dimensions| dimensions.to_physical::<u32>(self.scale_factor()).into());
self.set_max_inner_size_physical(physical_dimensions);
}
@@ -1094,37 +1099,29 @@ impl UnownedWindow {
&self,
old_dpi_factor: f64,
new_dpi_factor: f64,
width: f64,
height: f64,
) -> (f64, f64, util::Flusher<'_>) {
width: u32,
height: u32,
shared_state: &SharedState,
) -> (u32, u32) {
let scale_factor = new_dpi_factor / old_dpi_factor;
let new_width = width * scale_factor;
let new_height = height * scale_factor;
self.update_normal_hints(|normal_hints| {
let dpi_adjuster = |(width, height): (u32, u32)| -> (u32, u32) {
let new_width = width as f64 * scale_factor;
let new_height = height as f64 * scale_factor;
(new_width.round() as u32, new_height.round() as u32)
};
let max_size = normal_hints.get_max_size().map(&dpi_adjuster);
let min_size = normal_hints.get_min_size().map(&dpi_adjuster);
let resize_increments = normal_hints.get_resize_increments().map(&dpi_adjuster);
let base_size = normal_hints.get_base_size().map(&dpi_adjuster);
let dpi_adjuster =
|size: Size| -> (u32, u32) { size.to_physical::<u32>(new_dpi_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);
let base_size = shared_state.base_size.map(&dpi_adjuster);
normal_hints.set_max_size(max_size);
normal_hints.set_min_size(min_size);
normal_hints.set_resize_increments(resize_increments);
normal_hints.set_base_size(base_size);
})
.expect("Failed to update normal hints");
unsafe {
(self.xconn.xlib.XResizeWindow)(
self.xconn.display,
self.xwindow,
new_width.round() as c_uint,
new_height.round() as c_uint,
);
}
(new_width, new_height, util::Flusher::new(&self.xconn))
let new_width = (width as f64 * scale_factor).round() as u32;
let new_height = (height as f64 * scale_factor).round() as u32;
(new_width, new_height)
}
pub fn set_resizable(&self, resizable: bool) {
@@ -1136,25 +1133,25 @@ impl UnownedWindow {
return;
}
let (logical_min, logical_max) = if resizable {
let (min_size, max_size) = if resizable {
let shared_state_lock = self.shared_state.lock();
(
shared_state_lock.min_inner_size,
shared_state_lock.max_inner_size,
)
} else {
let window_size = Some(self.inner_size());
let window_size = Some(Size::from(self.inner_size()));
(window_size.clone(), window_size)
};
self.set_maximizable_inner(resizable).queue();
let dpi_factor = self.hidpi_factor();
let min_inner_size = logical_min
.map(|logical_size| logical_size.to_physical(dpi_factor))
let dpi_factor = self.scale_factor();
let min_inner_size = min_size
.map(|size| size.to_physical::<u32>(dpi_factor))
.map(Into::into);
let max_inner_size = logical_max
.map(|logical_size| logical_size.to_physical(dpi_factor))
let max_inner_size = max_size
.map(|size| size.to_physical::<u32>(dpi_factor))
.map(Into::into);
self.update_normal_hints(|normal_hints| {
normal_hints.set_min_size(min_inner_size);
@@ -1275,8 +1272,8 @@ impl UnownedWindow {
}
#[inline]
pub fn hidpi_factor(&self) -> f64 {
self.current_monitor().hidpi_factor
pub fn scale_factor(&self) -> f64 {
self.current_monitor().scale_factor
}
pub fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ExternalError> {
@@ -1289,11 +1286,8 @@ impl UnownedWindow {
}
#[inline]
pub fn set_cursor_position(
&self,
logical_position: LogicalPosition,
) -> Result<(), ExternalError> {
let (x, y) = logical_position.to_physical(self.hidpi_factor()).into();
pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
let (x, y) = position.to_physical::<i32>(self.scale_factor()).into();
self.set_cursor_position_physical(x, y)
}
@@ -1305,8 +1299,8 @@ impl UnownedWindow {
}
#[inline]
pub fn set_ime_position(&self, logical_spot: LogicalPosition) {
let (x, y) = logical_spot.to_physical(self.hidpi_factor()).into();
pub fn set_ime_position(&self, spot: Position) {
let (x, y) = spot.to_physical::<i32>(self.scale_factor()).into();
self.set_ime_position_physical(x, y);
}

View File

@@ -11,7 +11,7 @@ use objc::{
use crate::{
event::{DeviceEvent, ElementState, Event},
platform_impl::platform::{app_state::AppState, util, DEVICE_ID},
platform_impl::platform::{app_state::AppState, event::EventWrapper, util, DEVICE_ID},
};
pub struct AppClass(pub *const Class);
@@ -71,32 +71,32 @@ unsafe fn maybe_dispatch_device_event(event: id) {
let delta_y = event.deltaY() as f64;
if delta_x != 0.0 {
events.push_back(Event::DeviceEvent {
events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent {
device_id: DEVICE_ID,
event: DeviceEvent::Motion {
axis: 0,
value: delta_x,
},
});
}));
}
if delta_y != 0.0 {
events.push_back(Event::DeviceEvent {
events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent {
device_id: DEVICE_ID,
event: DeviceEvent::Motion {
axis: 1,
value: delta_y,
},
});
}));
}
if delta_x != 0.0 || delta_y != 0.0 {
events.push_back(Event::DeviceEvent {
events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent {
device_id: DEVICE_ID,
event: DeviceEvent::MouseMotion {
delta: (delta_x, delta_y),
},
});
}));
}
AppState::queue_events(events);
@@ -104,26 +104,26 @@ unsafe fn maybe_dispatch_device_event(event: id) {
appkit::NSLeftMouseDown | appkit::NSRightMouseDown | appkit::NSOtherMouseDown => {
let mut events = VecDeque::with_capacity(1);
events.push_back(Event::DeviceEvent {
events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent {
device_id: DEVICE_ID,
event: DeviceEvent::Button {
button: event.buttonNumber() as u32,
state: ElementState::Pressed,
},
});
}));
AppState::queue_events(events);
}
appkit::NSLeftMouseUp | appkit::NSRightMouseUp | appkit::NSOtherMouseUp => {
let mut events = VecDeque::with_capacity(1);
events.push_back(Event::DeviceEvent {
events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent {
device_id: DEVICE_ID,
event: DeviceEvent::Button {
button: event.buttonNumber() as u32,
state: ElementState::Released,
},
});
}));
AppState::queue_events(events);
}

View File

@@ -12,15 +12,21 @@ use std::{
};
use cocoa::{
appkit::NSApp,
appkit::{NSApp, NSWindow},
base::nil,
foundation::{NSAutoreleasePool, NSString},
foundation::{NSAutoreleasePool, NSSize, NSString},
};
use crate::{
event::{Event, StartCause},
dpi::LogicalSize,
event::{Event, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoopWindowTarget as RootWindowTarget},
platform_impl::platform::{observer::EventLoopWaker, util::Never},
platform_impl::platform::{
event::{EventProxy, EventWrapper},
observer::EventLoopWaker,
util::{IdRef, Never},
window::get_window_id,
},
window::WindowId,
};
use objc::runtime::Object;
@@ -29,8 +35,8 @@ lazy_static! {
static ref HANDLER: Handler = Default::default();
}
impl Event<Never> {
fn userify<T: 'static>(self) -> Event<T> {
impl<'a, Never> Event<'a, Never> {
fn userify<T: 'static>(self) -> Event<'a, T> {
self.map_nonuser_event()
// `Never` can't be constructed, so the `UserEvent` variant can't
// be present here.
@@ -39,12 +45,13 @@ impl Event<Never> {
}
pub trait EventHandler: Debug {
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow);
// Not sure probably it should accept Event<'static, Never>
fn handle_nonuser_event(&mut self, event: Event<'_, Never>, control_flow: &mut ControlFlow);
fn handle_user_events(&mut self, control_flow: &mut ControlFlow);
}
struct EventLoopHandler<T: 'static> {
callback: Box<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>,
callback: Box<dyn FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow)>,
will_exit: bool,
window_target: Rc<RootWindowTarget<T>>,
}
@@ -59,7 +66,7 @@ impl<T> Debug for EventLoopHandler<T> {
}
impl<T> EventHandler for EventLoopHandler<T> {
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow) {
fn handle_nonuser_event(&mut self, event: Event<'_, Never>, control_flow: &mut ControlFlow) {
(self.callback)(event.userify(), &self.window_target, control_flow);
self.will_exit |= *control_flow == ControlFlow::Exit;
if self.will_exit {
@@ -88,7 +95,7 @@ struct Handler {
control_flow_prev: Mutex<ControlFlow>,
start_time: Mutex<Option<Instant>>,
callback: Mutex<Option<Box<dyn EventHandler>>>,
pending_events: Mutex<VecDeque<Event<Never>>>,
pending_events: Mutex<VecDeque<EventWrapper>>,
pending_redraw: Mutex<Vec<WindowId>>,
waker: Mutex<EventLoopWaker>,
}
@@ -97,7 +104,7 @@ unsafe impl Send for Handler {}
unsafe impl Sync for Handler {}
impl Handler {
fn events<'a>(&'a self) -> MutexGuard<'a, VecDeque<Event<Never>>> {
fn events(&self) -> MutexGuard<'_, VecDeque<EventWrapper>> {
self.pending_events.lock().unwrap()
}
@@ -105,7 +112,7 @@ impl Handler {
self.pending_redraw.lock().unwrap()
}
fn waker<'a>(&'a self) -> MutexGuard<'a, EventLoopWaker> {
fn waker(&self) -> MutexGuard<'_, EventLoopWaker> {
self.waker.lock().unwrap()
}
@@ -141,7 +148,7 @@ impl Handler {
*self.start_time.lock().unwrap() = Some(Instant::now());
}
fn take_events(&self) -> VecDeque<Event<Never>> {
fn take_events(&self) -> VecDeque<EventWrapper> {
mem::replace(&mut *self.events(), Default::default())
}
@@ -157,9 +164,14 @@ impl Handler {
self.in_callback.store(in_callback, Ordering::Release);
}
fn handle_nonuser_event(&self, event: Event<Never>) {
fn handle_nonuser_event(&self, wrapper: EventWrapper) {
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
callback.handle_nonuser_event(event, &mut *self.control_flow.lock().unwrap());
match wrapper {
EventWrapper::StaticEvent(event) => {
callback.handle_nonuser_event(event, &mut *self.control_flow.lock().unwrap())
}
EventWrapper::EventProxy(proxy) => self.handle_proxy(proxy, callback),
}
}
}
@@ -168,6 +180,46 @@ impl Handler {
callback.handle_user_events(&mut *self.control_flow.lock().unwrap());
}
}
fn handle_scale_factor_changed_event(
&self,
callback: &mut Box<dyn EventHandler + 'static>,
ns_window: IdRef,
suggested_size: LogicalSize<f64>,
scale_factor: f64,
) {
let mut size = suggested_size.to_physical(scale_factor);
let new_inner_size = &mut size;
let event = Event::WindowEvent {
window_id: WindowId(get_window_id(*ns_window)),
event: WindowEvent::ScaleFactorChanged {
scale_factor,
new_inner_size,
},
};
callback.handle_nonuser_event(event, &mut *self.control_flow.lock().unwrap());
let physical_size = *new_inner_size;
let logical_size = physical_size.to_logical(scale_factor);
let size = NSSize::new(logical_size.width, logical_size.height);
unsafe { NSWindow::setContentSize_(*ns_window, size) };
}
fn handle_proxy(&self, proxy: EventProxy, callback: &mut Box<dyn EventHandler + 'static>) {
match proxy {
EventProxy::DpiChangedProxy {
ns_window,
suggested_size,
scale_factor,
} => self.handle_scale_factor_changed_event(
callback,
ns_window,
suggested_size,
scale_factor,
),
}
}
}
pub enum AppState {}
@@ -176,7 +228,7 @@ impl AppState {
// This function extends lifetime of `callback` to 'static as its side effect
pub unsafe fn set_callback<F, T>(callback: F, window_target: Rc<RootWindowTarget<T>>)
where
F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
F: FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow),
{
*HANDLER.callback.lock().unwrap() = Some(Box::new(EventLoopHandler {
// This transmute is always safe, in case it was reached through `run`, since our
@@ -184,8 +236,8 @@ impl AppState {
// they passed to callback will actually outlive it, some apps just can't move
// everything to event loop, so this is something that they should care about.
callback: mem::transmute::<
Box<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>,
Box<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>,
Box<dyn FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow)>,
Box<dyn FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow)>,
>(Box::new(callback)),
will_exit: false,
window_target,
@@ -194,7 +246,7 @@ impl AppState {
pub fn exit() {
HANDLER.set_in_callback(true);
HANDLER.handle_nonuser_event(Event::LoopDestroyed);
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::LoopDestroyed));
HANDLER.set_in_callback(false);
HANDLER.callback.lock().unwrap().take();
}
@@ -203,7 +255,9 @@ impl AppState {
HANDLER.set_ready();
HANDLER.waker().start();
HANDLER.set_in_callback(true);
HANDLER.handle_nonuser_event(Event::NewEvents(StartCause::Init));
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::NewEvents(
StartCause::Init,
)));
HANDLER.set_in_callback(false);
}
@@ -234,7 +288,7 @@ impl AppState {
ControlFlow::Exit => StartCause::Poll, //panic!("unexpected `ControlFlow::Exit`"),
};
HANDLER.set_in_callback(true);
HANDLER.handle_nonuser_event(Event::NewEvents(cause));
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::NewEvents(cause)));
HANDLER.set_in_callback(false);
}
@@ -246,18 +300,18 @@ impl AppState {
}
}
pub fn queue_event(event: Event<Never>) {
pub fn queue_event(wrapper: EventWrapper) {
if !unsafe { msg_send![class!(NSThread), isMainThread] } {
panic!("Event queued from different thread: {:#?}", event);
panic!("Event queued from different thread: {:#?}", wrapper);
}
HANDLER.events().push_back(event);
HANDLER.events().push_back(wrapper);
}
pub fn queue_events(mut events: VecDeque<Event<Never>>) {
pub fn queue_events(mut wrappers: VecDeque<EventWrapper>) {
if !unsafe { msg_send![class!(NSThread), isMainThread] } {
panic!("Events queued from different thread: {:#?}", events);
panic!("Events queued from different thread: {:#?}", wrappers);
}
HANDLER.events().append(&mut events);
HANDLER.events().append(&mut wrappers);
}
pub fn cleared() {
@@ -270,11 +324,13 @@ impl AppState {
for event in HANDLER.take_events() {
HANDLER.handle_nonuser_event(event);
}
HANDLER.handle_nonuser_event(Event::MainEventsCleared);
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::MainEventsCleared));
for window_id in HANDLER.should_redraw() {
HANDLER.handle_nonuser_event(Event::RedrawRequested(window_id));
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::RedrawRequested(
window_id,
)));
}
HANDLER.handle_nonuser_event(Event::RedrawEventsCleared);
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::RedrawEventsCleared));
HANDLER.set_in_callback(false);
}
if HANDLER.should_exit() {

View File

@@ -6,10 +6,29 @@ use cocoa::{
};
use crate::{
event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent},
platform_impl::platform::DEVICE_ID,
dpi::LogicalSize,
event::{ElementState, Event, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent},
platform_impl::platform::{
util::{IdRef, Never},
DEVICE_ID,
},
};
#[derive(Debug)]
pub enum EventWrapper {
StaticEvent(Event<'static, Never>),
EventProxy(EventProxy),
}
#[derive(Debug, PartialEq)]
pub enum EventProxy {
DpiChangedProxy {
ns_window: IdRef,
suggested_size: LogicalSize<f64>,
scale_factor: f64,
},
}
pub fn char_to_keycode(c: char) -> Option<VirtualKeyCode> {
// We only translate keys that are affected by keyboard layout.
//
@@ -256,7 +275,7 @@ pub unsafe fn modifier_event(
ns_event: id,
keymask: NSEventModifierFlags,
was_key_pressed: bool,
) -> Option<WindowEvent> {
) -> Option<WindowEvent<'static>> {
if !was_key_pressed && NSEvent::modifierFlags(ns_event).contains(keymask)
|| was_key_pressed && !NSEvent::modifierFlags(ns_event).contains(keymask)
{

View File

@@ -84,7 +84,7 @@ impl<T> EventLoop<T> {
pub fn run<F>(mut self, callback: F) -> !
where
F: 'static + FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
F: 'static + FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow),
{
self.run_return(callback);
process::exit(0);
@@ -92,7 +92,7 @@ impl<T> EventLoop<T> {
pub fn run_return<F>(&mut self, callback: F)
where
F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
F: FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow),
{
unsafe {
let pool = NSAutoreleasePool::new(nil);

View File

@@ -84,7 +84,7 @@ impl Clone for NativeDisplayMode {
}
impl VideoMode {
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
self.size.into()
}
@@ -166,9 +166,9 @@ impl fmt::Debug for MonitorHandle {
struct MonitorHandle {
name: Option<String>,
native_identifier: u32,
size: PhysicalSize,
position: PhysicalPosition,
hidpi_factor: f64,
size: PhysicalSize<u32>,
position: PhysicalPosition<i32>,
scale_factor: f64,
}
let monitor_id_proxy = MonitorHandle {
@@ -176,7 +176,7 @@ impl fmt::Debug for MonitorHandle {
native_identifier: self.native_identifier(),
size: self.size(),
position: self.position(),
hidpi_factor: self.hidpi_factor(),
scale_factor: self.scale_factor(),
};
monitor_id_proxy.fmt(f)
@@ -199,24 +199,24 @@ impl MonitorHandle {
self.0
}
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
let MonitorHandle(display_id) = *self;
let display = CGDisplay::new(display_id);
let height = display.pixels_high();
let width = display.pixels_wide();
PhysicalSize::from_logical((width as f64, height as f64), self.hidpi_factor())
PhysicalSize::from_logical::<_, f64>((width as f64, height as f64), self.scale_factor())
}
#[inline]
pub fn position(&self) -> PhysicalPosition {
pub fn position(&self) -> PhysicalPosition<i32> {
let bounds = unsafe { CGDisplayBounds(self.native_identifier()) };
PhysicalPosition::from_logical(
PhysicalPosition::from_logical::<_, f64>(
(bounds.origin.x as f64, bounds.origin.y as f64),
self.hidpi_factor(),
self.scale_factor(),
)
}
pub fn hidpi_factor(&self) -> f64 {
pub fn scale_factor(&self) -> f64 {
let screen = match self.ns_screen() {
Some(screen) => screen,
None => return 1.0, // default to 1.0 when we can't find the screen

View File

@@ -73,10 +73,10 @@ pub unsafe fn set_style_mask_sync(ns_window: id, ns_view: id, mask: NSWindowStyl
struct SetContentSizeData {
ns_window: id,
size: LogicalSize,
size: LogicalSize<f64>,
}
impl SetContentSizeData {
fn new_ptr(ns_window: id, size: LogicalSize) -> *mut Self {
fn new_ptr(ns_window: id, size: LogicalSize<f64>) -> *mut Self {
Box::into_raw(Box::new(SetContentSizeData { ns_window, size }))
}
}
@@ -98,7 +98,7 @@ extern "C" fn set_content_size_callback(context: *mut c_void) {
}
// `setContentSize:` isn't thread-safe either, though it doesn't log any errors
// and just fails silently. Anyway, GCD to the rescue!
pub unsafe fn set_content_size_async(ns_window: id, size: LogicalSize) {
pub unsafe fn set_content_size_async(ns_window: id, size: LogicalSize<f64>) {
let context = SetContentSizeData::new_ptr(ns_window, size);
dispatch_async_f(
dispatch_get_main_queue(),

View File

@@ -31,6 +31,7 @@ pub const EMPTY_RANGE: ffi::NSRange = ffi::NSRange {
length: 0,
};
#[derive(Debug, PartialEq)]
pub struct IdRef(id);
impl IdRef {

View File

@@ -25,7 +25,7 @@ use crate::{
app_state::AppState,
event::{
char_to_keycode, check_function_keys, event_mods, get_scancode, modifier_event,
scancode_to_keycode,
scancode_to_keycode, EventWrapper,
},
ffi::*,
util::{self, IdRef},
@@ -512,10 +512,10 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran
let mut events = VecDeque::with_capacity(characters.len());
for character in string.chars().filter(|c| !is_corporate_character(*c)) {
events.push_back(Event::WindowEvent {
events.push_back(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::ReceivedCharacter(character),
});
}));
}
AppState::queue_events(events);
@@ -536,10 +536,10 @@ extern "C" fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
// The `else` condition would emit the same character, but I'm keeping this here both...
// 1) as a reminder for how `doCommandBySelector` works
// 2) to make our use of carriage return explicit
events.push_back(Event::WindowEvent {
events.push_back(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::ReceivedCharacter('\r'),
});
}));
} else {
let raw_characters = state.raw_characters.take();
if let Some(raw_characters) = raw_characters {
@@ -547,10 +547,10 @@ extern "C" fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
.chars()
.filter(|c| !is_corporate_character(*c))
{
events.push_back(Event::WindowEvent {
events.push_back(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::ReceivedCharacter(character),
});
}));
}
}
};
@@ -646,14 +646,14 @@ extern "C" fn key_down(this: &Object, _sel: Sel, event: id) {
};
let pass_along = {
AppState::queue_event(window_event);
AppState::queue_event(EventWrapper::StaticEvent(window_event));
// Emit `ReceivedCharacter` for key repeats
if is_repeat && state.is_key_down {
for character in characters.chars().filter(|c| !is_corporate_character(*c)) {
AppState::queue_event(Event::WindowEvent {
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id,
event: WindowEvent::ReceivedCharacter(character),
});
}));
}
false
} else {
@@ -697,7 +697,7 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) {
},
};
AppState::queue_event(window_event);
AppState::queue_event(EventWrapper::StaticEvent(window_event));
}
trace!("Completed `keyUp`");
}
@@ -747,16 +747,16 @@ extern "C" fn flags_changed(this: &Object, _sel: Sel, event: id) {
}
for event in events {
AppState::queue_event(Event::WindowEvent {
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
event,
});
}));
}
AppState::queue_event(Event::DeviceEvent {
AppState::queue_event(EventWrapper::StaticEvent(Event::DeviceEvent {
device_id: DEVICE_ID,
event: DeviceEvent::ModifiersChanged(state.modifiers),
});
}));
}
trace!("Completed `flagsChanged`");
}
@@ -811,7 +811,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
},
};
AppState::queue_event(window_event);
AppState::queue_event(EventWrapper::StaticEvent(window_event));
}
trace!("Completed `cancelOperation`");
}
@@ -831,7 +831,7 @@ fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: Elem
},
};
AppState::queue_event(window_event);
AppState::queue_event(EventWrapper::StaticEvent(window_event));
}
}
@@ -892,7 +892,7 @@ fn mouse_motion(this: &Object, event: id) {
},
};
AppState::queue_event(window_event);
AppState::queue_event(EventWrapper::StaticEvent(window_event));
}
}
@@ -944,8 +944,8 @@ extern "C" fn mouse_entered(this: &Object, _sel: Sel, event: id) {
}
};
AppState::queue_event(enter_event);
AppState::queue_event(move_event);
AppState::queue_event(EventWrapper::StaticEvent(enter_event));
AppState::queue_event(EventWrapper::StaticEvent(move_event));
}
trace!("Completed `mouseEntered`");
}
@@ -963,7 +963,7 @@ extern "C" fn mouse_exited(this: &Object, _sel: Sel, _event: id) {
},
};
AppState::queue_event(window_event);
AppState::queue_event(EventWrapper::StaticEvent(window_event));
}
trace!("Completed `mouseExited`");
}
@@ -1005,8 +1005,8 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
},
};
AppState::queue_event(device_event);
AppState::queue_event(window_event);
AppState::queue_event(EventWrapper::StaticEvent(device_event));
AppState::queue_event(EventWrapper::StaticEvent(window_event));
}
trace!("Completed `scrollWheel`");
}
@@ -1029,7 +1029,7 @@ extern "C" fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) {
},
};
AppState::queue_event(window_event);
AppState::queue_event(EventWrapper::StaticEvent(window_event));
}
trace!("Completed `pressureChangeWithEvent`");
}

View File

@@ -10,7 +10,9 @@ use std::{
};
use crate::{
dpi::{LogicalPosition, LogicalSize},
dpi::{
LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, Size::Logical,
},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
icon::Icon,
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
@@ -66,7 +68,7 @@ pub struct PlatformSpecificWindowBuilderAttributes {
pub titlebar_hidden: bool,
pub titlebar_buttons_hidden: bool,
pub fullsize_content_view: bool,
pub resize_increments: Option<LogicalSize>,
pub resize_increments: Option<LogicalSize<f64>>,
pub disallow_hidpi: bool,
}
@@ -130,12 +132,17 @@ fn create_window(
None => None,
};
let frame = match screen {
Some(screen) => appkit::NSScreen::frame(screen),
Some(screen) => NSScreen::frame(screen),
None => {
let (width, height) = attrs
.inner_size
.map(|logical| (logical.width, logical.height))
.unwrap_or_else(|| (800.0, 600.0));
let screen = NSScreen::mainScreen(nil);
let scale_factor = NSScreen::backingScaleFactor(screen) as f64;
let (width, height) = match attrs.inner_size {
Some(size) => {
let logical = size.to_logical(scale_factor);
(logical.width, logical.height)
}
None => (800.0, 600.0),
};
NSRect::new(NSPoint::new(0.0, 0.0), NSSize::new(width, height))
}
};
@@ -292,6 +299,7 @@ pub struct UnownedWindow {
pub shared_state: Arc<Mutex<SharedState>>,
decorations: AtomicBool,
cursor_state: Weak<Mutex<CursorState>>,
pub inner_rect: Option<PhysicalSize<u32>>,
}
unsafe impl Send for UnownedWindow {}
@@ -328,6 +336,8 @@ impl UnownedWindow {
let input_context = unsafe { util::create_input_context(*ns_view) };
let dpi_factor = unsafe { NSWindow::backingScaleFactor(*ns_window) as f64 };
unsafe {
if win_attribs.transparent {
ns_window.setOpaque_(NO);
@@ -335,13 +345,14 @@ impl UnownedWindow {
}
ns_app.activateIgnoringOtherApps_(YES);
win_attribs
.min_inner_size
.map(|dim| set_min_inner_size(*ns_window, dim));
win_attribs
.max_inner_size
.map(|dim| set_max_inner_size(*ns_window, dim));
win_attribs.min_inner_size.map(|dim| {
let logical_dim = dim.to_logical(dpi_factor);
set_min_inner_size(*ns_window, logical_dim)
});
win_attribs.max_inner_size.map(|dim| {
let logical_dim = dim.to_logical(dpi_factor);
set_max_inner_size(*ns_window, logical_dim)
});
use cocoa::foundation::NSArray;
// register for drag and drop operations.
@@ -361,6 +372,9 @@ impl UnownedWindow {
let maximized = win_attribs.maximized;
let visible = win_attribs.visible;
let decorations = win_attribs.decorations;
let inner_rect = win_attribs
.inner_size
.map(|size| size.to_physical(dpi_factor));
let window = Arc::new(UnownedWindow {
ns_view,
@@ -369,6 +383,7 @@ impl UnownedWindow {
shared_state: Arc::new(Mutex::new(win_attribs.into())),
decorations: AtomicBool::new(decorations),
cursor_state,
inner_rect,
});
let delegate = new_delegate(&window, fullscreen.is_some());
@@ -425,27 +440,31 @@ impl UnownedWindow {
AppState::queue_redraw(RootWindowId(self.id()));
}
pub fn outer_position(&self) -> Result<LogicalPosition, NotSupportedError> {
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
let frame_rect = unsafe { NSWindow::frame(*self.ns_window) };
Ok((
let position = LogicalPosition::new(
frame_rect.origin.x as f64,
util::bottom_left_to_top_left(frame_rect),
)
.into())
);
let dpi_factor = self.scale_factor();
Ok(position.to_physical(dpi_factor))
}
pub fn inner_position(&self) -> Result<LogicalPosition, NotSupportedError> {
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
let content_rect = unsafe {
NSWindow::contentRectForFrameRect_(*self.ns_window, NSWindow::frame(*self.ns_window))
};
Ok((
let position = LogicalPosition::new(
content_rect.origin.x as f64,
util::bottom_left_to_top_left(content_rect),
)
.into())
);
let dpi_factor = self.scale_factor();
Ok(position.to_physical(dpi_factor))
}
pub fn set_outer_position(&self, position: LogicalPosition) {
pub fn set_outer_position(&self, position: Position) {
let dpi_factor = self.scale_factor();
let position = position.to_logical(dpi_factor);
let dummy = NSRect::new(
NSPoint::new(
position.x,
@@ -461,35 +480,50 @@ impl UnownedWindow {
}
#[inline]
pub fn inner_size(&self) -> LogicalSize {
pub fn inner_size(&self) -> PhysicalSize<u32> {
let view_frame = unsafe { NSView::frame(*self.ns_view) };
(view_frame.size.width as f64, view_frame.size.height as f64).into()
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)
}
#[inline]
pub fn outer_size(&self) -> LogicalSize {
pub fn outer_size(&self) -> PhysicalSize<u32> {
let view_frame = unsafe { NSWindow::frame(*self.ns_window) };
(view_frame.size.width as f64, view_frame.size.height as f64).into()
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)
}
#[inline]
pub fn set_inner_size(&self, size: LogicalSize) {
pub fn set_inner_size(&self, size: Size) {
unsafe {
util::set_content_size_async(*self.ns_window, size);
let dpi_factor = self.scale_factor();
util::set_content_size_async(*self.ns_window, size.to_logical(dpi_factor));
}
}
pub fn set_min_inner_size(&self, dimensions: Option<LogicalSize>) {
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
unsafe {
let dimensions = dimensions.unwrap_or_else(|| (0, 0).into());
set_min_inner_size(*self.ns_window, dimensions);
let dimensions = dimensions.unwrap_or(Logical(LogicalSize {
width: 0.0,
height: 0.0,
}));
let dpi_factor = self.scale_factor();
set_min_inner_size(*self.ns_window, dimensions.to_logical(dpi_factor));
}
}
pub fn set_max_inner_size(&self, dimensions: Option<LogicalSize>) {
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
unsafe {
let dimensions = dimensions.unwrap_or_else(|| (!0, !0).into());
set_max_inner_size(*self.ns_window, dimensions);
let dimensions = dimensions.unwrap_or(Logical(LogicalSize {
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));
}
}
@@ -549,19 +583,19 @@ impl UnownedWindow {
}
#[inline]
pub fn hidpi_factor(&self) -> f64 {
pub fn scale_factor(&self) -> f64 {
unsafe { NSWindow::backingScaleFactor(*self.ns_window) as _ }
}
#[inline]
pub fn set_cursor_position(
&self,
cursor_position: LogicalPosition,
) -> Result<(), ExternalError> {
let window_position = self.inner_position().unwrap();
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 point = appkit::CGPoint {
x: (cursor_position.x + window_position.x) as CGFloat,
y: (cursor_position.y + window_position.y) as CGFloat,
x: logical_cursor_position.x + window_position.x,
y: logical_cursor_position.y + window_position.y,
};
CGDisplay::warp_mouse_cursor_position(point)
.map_err(|e| ExternalError::Os(os_error!(OsError::CGError(e))))?;
@@ -894,7 +928,9 @@ impl UnownedWindow {
}
#[inline]
pub fn set_ime_position(&self, logical_spot: LogicalPosition) {
pub fn set_ime_position(&self, spot: Position) {
let dpi_factor = self.scale_factor();
let logical_spot = spot.to_logical(dpi_factor);
unsafe {
view::set_ime_position(
*self.ns_view,
@@ -1059,7 +1095,7 @@ impl Drop for UnownedWindow {
}
}
unsafe fn set_min_inner_size<V: NSWindow + Copy>(window: V, mut min_size: LogicalSize) {
unsafe fn set_min_inner_size<V: NSWindow + Copy>(window: V, mut min_size: LogicalSize<f64>) {
let mut current_rect = NSWindow::frame(window);
let content_rect = NSWindow::contentRectForFrameRect_(window, NSWindow::frame(window));
// Convert from client area size to window size
@@ -1083,7 +1119,7 @@ unsafe fn set_min_inner_size<V: NSWindow + Copy>(window: V, mut min_size: Logica
}
}
unsafe fn set_max_inner_size<V: NSWindow + Copy>(window: V, mut max_size: LogicalSize) {
unsafe fn set_max_inner_size<V: NSWindow + Copy>(window: V, mut max_size: LogicalSize<f64>) {
let mut current_rect = NSWindow::frame(window);
let content_rect = NSWindow::contentRectForFrameRect_(window, NSWindow::frame(window));
// Convert from client area size to window size

View File

@@ -19,6 +19,7 @@ use crate::{
event::{Event, WindowEvent},
platform_impl::platform::{
app_state::AppState,
event::{EventProxy, EventWrapper},
util::{self, IdRef},
window::{get_window_id, UnownedWindow},
},
@@ -47,20 +48,18 @@ pub struct WindowDelegateState {
impl WindowDelegateState {
pub fn new(window: &Arc<UnownedWindow>, initial_fullscreen: bool) -> Self {
let dpi_factor = window.hidpi_factor();
let scale_factor = window.scale_factor();
let mut delegate_state = WindowDelegateState {
ns_window: window.ns_window.clone(),
ns_view: window.ns_view.clone(),
window: Arc::downgrade(&window),
initial_fullscreen,
previous_position: None,
previous_dpi_factor: dpi_factor,
previous_dpi_factor: scale_factor,
};
if dpi_factor != 1.0 {
delegate_state.emit_event(WindowEvent::HiDpiFactorChanged(dpi_factor));
delegate_state.emit_resize_event();
if scale_factor != 1.0 {
delegate_state.emit_static_scale_factor_changed_event();
}
delegate_state
@@ -73,17 +72,34 @@ impl WindowDelegateState {
self.window.upgrade().map(|ref window| callback(window))
}
pub fn emit_event(&mut self, event: WindowEvent) {
pub fn emit_event(&mut self, event: WindowEvent<'static>) {
let event = Event::WindowEvent {
window_id: WindowId(get_window_id(*self.ns_window)),
event,
};
AppState::queue_event(event);
AppState::queue_event(EventWrapper::StaticEvent(event));
}
pub fn emit_static_scale_factor_changed_event(&mut self) {
let scale_factor = self.get_scale_factor();
if scale_factor == self.previous_dpi_factor {
return ();
};
self.previous_dpi_factor = scale_factor;
let wrapper = EventWrapper::EventProxy(EventProxy::DpiChangedProxy {
ns_window: IdRef::retain(*self.ns_window),
suggested_size: self.view_size(),
scale_factor,
});
AppState::queue_event(wrapper);
}
pub fn emit_resize_event(&mut self) {
let rect = unsafe { NSView::frame(*self.ns_view) };
let size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64);
let scale_factor = self.get_scale_factor();
let logical_size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64);
let size = logical_size.to_physical(scale_factor);
self.emit_event(WindowEvent::Resized(size));
}
@@ -97,6 +113,15 @@ impl WindowDelegateState {
self.emit_event(WindowEvent::Moved((x, y).into()));
}
}
fn get_scale_factor(&self) -> f64 {
(unsafe { NSWindow::backingScaleFactor(*self.ns_window) }) as f64
}
fn view_size(&self) -> LogicalSize<f64> {
let ns_size = unsafe { NSView::frame(*self.ns_view).size };
LogicalSize::new(ns_size.width as f64, ns_size.height as f64)
}
}
pub fn new_delegate(window: &Arc<UnownedWindow>, initial_fullscreen: bool) -> IdRef {
@@ -140,10 +165,6 @@ lazy_static! {
sel!(windowDidMove:),
window_did_move as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(windowDidChangeScreen:),
window_did_change_screen as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(windowDidChangeBackingProperties:),
window_did_change_backing_properties as extern "C" fn(&Object, Sel, id),
@@ -277,29 +298,10 @@ extern "C" fn window_did_move(this: &Object, _: Sel, _: id) {
trace!("Completed `windowDidMove:`");
}
extern "C" fn window_did_change_screen(this: &Object, _: Sel, _: id) {
trace!("Triggered `windowDidChangeScreen:`");
with_state(this, |state| {
let dpi_factor = unsafe { NSWindow::backingScaleFactor(*state.ns_window) } as f64;
if state.previous_dpi_factor != dpi_factor {
state.previous_dpi_factor = dpi_factor;
state.emit_event(WindowEvent::HiDpiFactorChanged(dpi_factor));
state.emit_resize_event();
}
});
trace!("Completed `windowDidChangeScreen:`");
}
// This will always be called before `window_did_change_screen`.
extern "C" fn window_did_change_backing_properties(this: &Object, _: Sel, _: id) {
trace!("Triggered `windowDidChangeBackingProperties:`");
with_state(this, |state| {
let dpi_factor = unsafe { NSWindow::backingScaleFactor(*state.ns_window) } as f64;
if state.previous_dpi_factor != dpi_factor {
state.previous_dpi_factor = dpi_factor;
state.emit_event(WindowEvent::HiDpiFactorChanged(dpi_factor));
state.emit_resize_event();
}
state.emit_static_scale_factor_changed_event();
});
trace!("Completed `windowDidChangeBackingProperties:`");
}

View File

@@ -37,7 +37,8 @@ impl<T> EventLoop<T> {
pub fn run<F>(self, mut event_handler: F) -> !
where
F: 'static + FnMut(Event<T>, &root::EventLoopWindowTarget<T>, &mut root::ControlFlow),
F: 'static
+ FnMut(Event<'static, T>, &root::EventLoopWindowTarget<T>, &mut root::ControlFlow),
{
let target = root::EventLoopWindowTarget {
p: self.elw.p.clone(),

View File

@@ -11,7 +11,7 @@ use std::{
rc::Rc,
};
pub struct Shared<T>(Rc<Execution<T>>);
pub struct Shared<T: 'static>(Rc<Execution<T>>);
impl<T> Clone for Shared<T> {
fn clone(&self) -> Self {
@@ -19,21 +19,21 @@ impl<T> Clone for Shared<T> {
}
}
pub struct Execution<T> {
pub struct Execution<T: 'static> {
runner: RefCell<Option<Runner<T>>>,
events: RefCell<VecDeque<Event<T>>>,
events: RefCell<VecDeque<Event<'static, T>>>,
id: RefCell<u32>,
redraw_pending: RefCell<HashSet<WindowId>>,
}
struct Runner<T> {
struct Runner<T: 'static> {
state: State,
is_busy: bool,
event_handler: Box<dyn FnMut(Event<T>, &mut root::ControlFlow)>,
event_handler: Box<dyn FnMut(Event<'static, T>, &mut root::ControlFlow)>,
}
impl<T: 'static> Runner<T> {
pub fn new(event_handler: Box<dyn FnMut(Event<T>, &mut root::ControlFlow)>) -> Self {
pub fn new(event_handler: Box<dyn FnMut(Event<'static, T>, &mut root::ControlFlow)>) -> Self {
Runner {
state: State::Init,
is_busy: false,
@@ -55,7 +55,10 @@ impl<T: 'static> Shared<T> {
// Set the event callback to use for the event loop runner
// This the event callback is a fairly thin layer over the user-provided callback that closes
// over a RootEventLoopWindowTarget reference
pub fn set_listener(&self, event_handler: Box<dyn FnMut(Event<T>, &mut root::ControlFlow)>) {
pub fn set_listener(
&self,
event_handler: Box<dyn FnMut(Event<'static, T>, &mut root::ControlFlow)>,
) {
self.0.runner.replace(Some(Runner::new(event_handler)));
self.send_event(Event::NewEvents(StartCause::Init));
@@ -79,7 +82,7 @@ impl<T: 'static> Shared<T> {
// Add an event to the event loop runner
//
// It will determine if the event should be immediately sent to the user or buffered for later
pub fn send_event(&self, event: Event<T>) {
pub fn send_event(&self, event: Event<'static, T>) {
// If the event loop is closed, it should discard any new events
if self.is_closed() {
return;
@@ -153,7 +156,7 @@ impl<T: 'static> Shared<T> {
// handle_event takes in events and either queues them or applies a callback
//
// It should only ever be called from send_event
fn handle_event(&self, event: Event<T>, control: &mut root::ControlFlow) {
fn handle_event(&self, event: Event<'static, T>, control: &mut root::ControlFlow) {
let is_closed = self.is_closed();
match *self.0.runner.borrow_mut() {

View File

@@ -1,5 +1,5 @@
use super::{backend, device, proxy::Proxy, runner, window};
use crate::dpi::LogicalSize;
use crate::dpi::{PhysicalSize, Size};
use crate::event::{DeviceId, ElementState, Event, KeyboardInput, TouchPhase, WindowEvent};
use crate::event_loop::ControlFlow;
use crate::window::WindowId;
@@ -28,7 +28,7 @@ impl<T> WindowTarget<T> {
Proxy::new(self.runner.clone())
}
pub fn run(&self, event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>) {
pub fn run(&self, event_handler: Box<dyn FnMut(Event<'static, T>, &mut ControlFlow)>) {
self.runner.set_listener(event_handler);
}
@@ -170,25 +170,27 @@ impl<T> WindowTarget<T> {
let runner = self.runner.clone();
let raw = canvas.raw().clone();
let mut intended_size = LogicalSize {
width: raw.width() as f64,
height: raw.height() as f64,
// The size to restore to after exiting fullscreen.
let mut intended_size = PhysicalSize {
width: raw.width() as u32,
height: raw.height() as u32,
};
canvas.on_fullscreen_change(move || {
// If the canvas is marked as fullscreen, it is moving *into* fullscreen
// If it is not, it is moving *out of* fullscreen
let new_size = if backend::is_fullscreen(&raw) {
intended_size = LogicalSize {
width: raw.width() as f64,
height: raw.height() as f64,
intended_size = PhysicalSize {
width: raw.width() as u32,
height: raw.height() as u32,
};
backend::window_size()
backend::window_size().to_physical(backend::scale_factor())
} else {
intended_size
};
raw.set_width(new_size.width as u32);
raw.set_height(new_size.height as u32);
backend::set_canvas_size(&raw, Size::Physical(new_size));
runner.send_event(Event::WindowEvent {
window_id: WindowId(id),
event: WindowEvent::Resized(new_size),

View File

@@ -5,22 +5,22 @@ use crate::monitor::{MonitorHandle, VideoMode};
pub struct Handle;
impl Handle {
pub fn hidpi_factor(&self) -> f64 {
pub fn scale_factor(&self) -> f64 {
1.0
}
pub fn position(&self) -> PhysicalPosition {
PhysicalPosition { x: 0.0, y: 0.0 }
pub fn position(&self) -> PhysicalPosition<i32> {
PhysicalPosition { x: 0, y: 0 }
}
pub fn name(&self) -> Option<String> {
None
}
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
PhysicalSize {
width: 0.0,
height: 0.0,
width: 0,
height: 0,
}
}
@@ -33,7 +33,7 @@ impl Handle {
pub struct Mode;
impl Mode {
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
unimplemented!();
}

View File

@@ -1,5 +1,5 @@
use super::event;
use crate::dpi::{LogicalPosition, LogicalSize};
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use crate::error::OsError as RootOE;
use crate::event::{ModifiersState, MouseButton, MouseScrollDelta, ScanCode, VirtualKeyCode};
use crate::platform_impl::OsError;
@@ -19,6 +19,7 @@ use stdweb::web::{
};
pub struct Canvas {
/// Note: resizing the CanvasElement should go through `backend::set_canvas_size` to ensure the DPI factor is maintained.
raw: CanvasElement,
on_focus: Option<EventListenerHandle>,
on_blur: Option<EventListenerHandle>,
@@ -82,23 +83,20 @@ impl Canvas {
.expect(&format!("Set attribute: {}", attribute));
}
pub fn position(&self) -> (f64, f64) {
pub fn position(&self) -> LogicalPosition<f64> {
let bounds = self.raw.get_bounding_client_rect();
(bounds.get_x(), bounds.get_y())
LogicalPosition {
x: bounds.get_x(),
y: bounds.get_y(),
}
}
pub fn width(&self) -> f64 {
self.raw.width() as f64
}
pub fn height(&self) -> f64 {
self.raw.height() as f64
}
pub fn set_size(&self, size: LogicalSize) {
self.raw.set_width(size.width as u32);
self.raw.set_height(size.height as u32);
pub fn size(&self) -> PhysicalSize<u32> {
PhysicalSize {
width: self.raw.width() as u32,
height: self.raw.height() as u32,
}
}
pub fn raw(&self) -> &CanvasElement {
@@ -209,12 +207,13 @@ impl Canvas {
pub fn on_cursor_move<F>(&mut self, mut handler: F)
where
F: 'static + FnMut(i32, LogicalPosition, ModifiersState),
F: 'static + FnMut(i32, PhysicalPosition<i32>, ModifiersState),
{
// todo
self.on_cursor_move = Some(self.add_event(move |event: PointerMoveEvent| {
handler(
event.pointer_id(),
event::mouse_position(&event),
event::mouse_position(&event).to_physical(super::scale_factor()),
event::mouse_modifiers(&event),
);
}));

View File

@@ -23,7 +23,7 @@ pub fn mouse_modifiers(event: &impl IMouseEvent) -> ModifiersState {
m
}
pub fn mouse_position(event: &impl IMouseEvent) -> LogicalPosition {
pub fn mouse_position(event: &impl IMouseEvent) -> LogicalPosition<f64> {
LogicalPosition {
x: event.offset_x() as f64,
y: event.offset_y() as f64,

View File

@@ -5,7 +5,7 @@ mod timeout;
pub use self::canvas::Canvas;
pub use self::timeout::Timeout;
use crate::dpi::LogicalSize;
use crate::dpi::{LogicalSize, Size};
use crate::platform::web::WindowExtStdweb;
use crate::window::Window;
@@ -33,7 +33,7 @@ impl WindowExtStdweb for Window {
}
}
pub fn window_size() -> LogicalSize {
pub fn window_size() -> LogicalSize<f64> {
let window = window();
let width = window.inner_width() as f64;
let height = window.inner_height() as f64;
@@ -41,6 +41,28 @@ pub fn window_size() -> LogicalSize {
LogicalSize { width, height }
}
pub fn scale_factor() -> f64 {
let window = window();
window.device_pixel_ratio()
}
pub fn set_canvas_size(raw: &CanvasElement, size: Size) {
use stdweb::*;
let scale_factor = scale_factor();
let physical_size = size.to_physical::<u32>(scale_factor);
let logical_size = size.to_logical::<f64>(scale_factor);
raw.set_width(physical_size.width);
raw.set_height(physical_size.height);
js! {
@{raw.as_ref()}.style.width = @{logical_size.width} + "px";
@{raw.as_ref()}.style.height = @{logical_size.height} + "px";
}
}
pub fn is_fullscreen(canvas: &CanvasElement) -> bool {
match document().fullscreen_element() {
Some(elem) => {

View File

@@ -1,5 +1,5 @@
use super::event;
use crate::dpi::{LogicalPosition, LogicalSize};
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use crate::error::OsError as RootOE;
use crate::event::{ModifiersState, MouseButton, MouseScrollDelta, ScanCode, VirtualKeyCode};
use crate::platform_impl::OsError;
@@ -11,6 +11,7 @@ use wasm_bindgen::{closure::Closure, JsCast};
use web_sys::{Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, PointerEvent, WheelEvent};
pub struct Canvas {
/// Note: resizing the HTMLCanvasElement should go through `backend::set_canvas_size` to ensure the DPI factor is maintained.
raw: HtmlCanvasElement,
on_focus: Option<Closure<dyn FnMut(FocusEvent)>>,
on_blur: Option<Closure<dyn FnMut(FocusEvent)>>,
@@ -80,23 +81,20 @@ impl Canvas {
.expect(&format!("Set attribute: {}", attribute));
}
pub fn position(&self) -> (f64, f64) {
pub fn position(&self) -> LogicalPosition<f64> {
let bounds = self.raw.get_bounding_client_rect();
(bounds.x(), bounds.y())
LogicalPosition {
x: bounds.x(),
y: bounds.y(),
}
}
pub fn width(&self) -> f64 {
self.raw.width() as f64
}
pub fn height(&self) -> f64 {
self.raw.height() as f64
}
pub fn set_size(&self, size: LogicalSize) {
self.raw.set_width(size.width as u32);
self.raw.set_height(size.height as u32);
pub fn size(&self) -> PhysicalSize<u32> {
PhysicalSize {
width: self.raw.width(),
height: self.raw.height(),
}
}
pub fn raw(&self) -> &HtmlCanvasElement {
@@ -218,12 +216,12 @@ impl Canvas {
pub fn on_cursor_move<F>(&mut self, mut handler: F)
where
F: 'static + FnMut(i32, LogicalPosition, ModifiersState),
F: 'static + FnMut(i32, PhysicalPosition<i32>, ModifiersState),
{
self.on_cursor_move = Some(self.add_event("pointermove", move |event: PointerEvent| {
handler(
event.pointer_id(),
event::mouse_position(&event),
event::mouse_position(&event).to_physical(super::scale_factor()),
event::mouse_modifiers(&event),
);
}));

View File

@@ -22,7 +22,7 @@ pub fn mouse_modifiers(event: &MouseEvent) -> ModifiersState {
m
}
pub fn mouse_position(event: &MouseEvent) -> LogicalPosition {
pub fn mouse_position(event: &MouseEvent) -> LogicalPosition<f64> {
LogicalPosition {
x: event.offset_x() as f64,
y: event.offset_y() as f64,

View File

@@ -5,7 +5,7 @@ mod timeout;
pub use self::canvas::Canvas;
pub use self::timeout::Timeout;
use crate::dpi::LogicalSize;
use crate::dpi::{LogicalSize, Size};
use crate::platform::web::WindowExtWebSys;
use crate::window::Window;
use wasm_bindgen::{closure::Closure, JsCast};
@@ -40,7 +40,7 @@ impl WindowExtWebSys for Window {
}
}
pub fn window_size() -> LogicalSize {
pub fn window_size() -> LogicalSize<f64> {
let window = web_sys::window().expect("Failed to obtain window");
let width = window
.inner_width()
@@ -56,6 +56,29 @@ pub fn window_size() -> LogicalSize {
LogicalSize { width, height }
}
pub fn scale_factor() -> f64 {
let window = web_sys::window().expect("Failed to obtain window");
window.device_pixel_ratio()
}
pub fn set_canvas_size(raw: &HtmlCanvasElement, size: Size) {
let scale_factor = scale_factor();
let physical_size = size.to_physical::<u32>(scale_factor);
let logical_size = size.to_logical::<f64>(scale_factor);
raw.set_width(physical_size.width);
raw.set_height(physical_size.height);
let style = raw.style();
style
.set_property("width", &format!("{}px", logical_size.width))
.expect("Failed to set canvas width");
style
.set_property("height", &format!("{}px", logical_size.height))
.expect("Failed to set canvas height");
}
pub fn is_fullscreen(canvas: &HtmlCanvasElement) -> bool {
let window = window().expect("Failed to obtain window");
let document = window.document().expect("Failed to obtain document");

View File

@@ -1,4 +1,4 @@
use crate::dpi::{LogicalPosition, LogicalSize};
use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{ExternalError, NotSupportedError, OsError as RootOE};
use crate::icon::Icon;
use crate::monitor::MonitorHandle as RootMH;
@@ -15,7 +15,6 @@ use std::collections::VecDeque;
pub struct Window {
canvas: backend::Canvas,
previous_pointer: RefCell<&'static str>,
position: RefCell<LogicalPosition>,
id: Id,
register_redraw_request: Box<dyn Fn()>,
}
@@ -39,15 +38,14 @@ impl Window {
let window = Window {
canvas,
previous_pointer: RefCell::new("auto"),
position: RefCell::new(LogicalPosition { x: 0.0, y: 0.0 }),
id,
register_redraw_request,
};
window.set_inner_size(attr.inner_size.unwrap_or(LogicalSize {
window.set_inner_size(attr.inner_size.unwrap_or(Size::Logical(LogicalSize {
width: 1024.0,
height: 768.0,
}));
})));
window.set_title(&attr.title);
window.set_maximized(attr.maximized);
window.set_visible(attr.visible);
@@ -72,18 +70,17 @@ impl Window {
(self.register_redraw_request)();
}
pub fn outer_position(&self) -> Result<LogicalPosition, NotSupportedError> {
let (x, y) = self.canvas.position();
Ok(LogicalPosition { x, y })
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
Ok(self.canvas.position().to_physical(self.scale_factor()))
}
pub fn inner_position(&self) -> Result<LogicalPosition, NotSupportedError> {
Ok(*self.position.borrow())
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
// Note: the canvas element has no window decorations, so this is equal to `outer_position`.
self.outer_position()
}
pub fn set_outer_position(&self, position: LogicalPosition) {
*self.position.borrow_mut() = position;
pub fn set_outer_position(&self, position: Position) {
let position = position.to_logical::<f64>(self.scale_factor());
self.canvas.set_attribute("position", "fixed");
self.canvas.set_attribute("left", &position.x.to_string());
@@ -91,33 +88,28 @@ impl Window {
}
#[inline]
pub fn inner_size(&self) -> LogicalSize {
LogicalSize {
width: self.canvas.width() as f64,
height: self.canvas.height() as f64,
}
pub fn inner_size(&self) -> PhysicalSize<u32> {
self.canvas.size()
}
#[inline]
pub fn outer_size(&self) -> LogicalSize {
LogicalSize {
width: self.canvas.width() as f64,
height: self.canvas.height() as f64,
}
pub fn outer_size(&self) -> PhysicalSize<u32> {
// Note: the canvas element has no window decorations, so this is equal to `inner_size`.
self.inner_size()
}
#[inline]
pub fn set_inner_size(&self, size: LogicalSize) {
self.canvas.set_size(size);
pub fn set_inner_size(&self, size: Size) {
backend::set_canvas_size(self.canvas.raw(), size);
}
#[inline]
pub fn set_min_inner_size(&self, _dimensions: Option<LogicalSize>) {
pub fn set_min_inner_size(&self, _dimensions: Option<Size>) {
// Intentionally a no-op: users can't resize canvas elements
}
#[inline]
pub fn set_max_inner_size(&self, _dimensions: Option<LogicalSize>) {
pub fn set_max_inner_size(&self, _dimensions: Option<Size>) {
// Intentionally a no-op: users can't resize canvas elements
}
@@ -127,8 +119,8 @@ impl Window {
}
#[inline]
pub fn hidpi_factor(&self) -> f64 {
1.0
pub fn scale_factor(&self) -> f64 {
super::backend::scale_factor()
}
#[inline]
@@ -178,7 +170,7 @@ impl Window {
}
#[inline]
pub fn set_cursor_position(&self, _position: LogicalPosition) -> Result<(), ExternalError> {
pub fn set_cursor_position(&self, _position: Position) -> Result<(), ExternalError> {
// Intentionally a no-op, as the web does not support setting cursor positions
Ok(())
}
@@ -243,7 +235,7 @@ impl Window {
}
#[inline]
pub fn set_ime_position(&self, _position: LogicalPosition) {
pub fn set_ime_position(&self, _position: Position) {
// Currently a no-op as it does not seem there is good support for this on web
}

View File

@@ -2,54 +2,30 @@
use std::sync::Once;
use crate::platform_impl::platform::util::{
ENABLE_NON_CLIENT_DPI_SCALING, GET_DPI_FOR_MONITOR, GET_DPI_FOR_WINDOW, SET_PROCESS_DPI_AWARE,
SET_PROCESS_DPI_AWARENESS, SET_PROCESS_DPI_AWARENESS_CONTEXT,
};
use winapi::{
shared::{
minwindef::{BOOL, FALSE, UINT},
minwindef::FALSE,
windef::{DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, HMONITOR, HWND},
winerror::S_OK,
},
um::{
shellscalingapi::{
MDT_EFFECTIVE_DPI, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS,
PROCESS_PER_MONITOR_DPI_AWARE,
},
shellscalingapi::{MDT_EFFECTIVE_DPI, PROCESS_PER_MONITOR_DPI_AWARE},
wingdi::{GetDeviceCaps, LOGPIXELSX},
winnt::HRESULT,
winuser::{self, MONITOR_DEFAULTTONEAREST},
},
};
const DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2: DPI_AWARENESS_CONTEXT = -4isize as _;
type SetProcessDPIAware = unsafe extern "system" fn() -> BOOL;
type SetProcessDpiAwareness = unsafe extern "system" fn(value: PROCESS_DPI_AWARENESS) -> HRESULT;
type SetProcessDpiAwarenessContext =
unsafe extern "system" fn(value: DPI_AWARENESS_CONTEXT) -> BOOL;
type GetDpiForWindow = unsafe extern "system" fn(hwnd: HWND) -> UINT;
type GetDpiForMonitor = unsafe extern "system" fn(
hmonitor: HMONITOR,
dpi_type: MONITOR_DPI_TYPE,
dpi_x: *mut UINT,
dpi_y: *mut UINT,
) -> HRESULT;
type EnableNonClientDpiScaling = unsafe extern "system" fn(hwnd: HWND) -> BOOL;
lazy_static! {
static ref GET_DPI_FOR_WINDOW: Option<GetDpiForWindow> =
get_function!("user32.dll", GetDpiForWindow);
static ref GET_DPI_FOR_MONITOR: Option<GetDpiForMonitor> =
get_function!("shcore.dll", GetDpiForMonitor);
static ref ENABLE_NON_CLIENT_DPI_SCALING: Option<EnableNonClientDpiScaling> =
get_function!("user32.dll", EnableNonClientDpiScaling);
}
pub fn become_dpi_aware() {
static ENABLE_DPI_AWARENESS: Once = Once::new();
ENABLE_DPI_AWARENESS.call_once(|| {
unsafe {
if let Some(SetProcessDpiAwarenessContext) =
get_function!("user32.dll", SetProcessDpiAwarenessContext)
{
if let Some(SetProcessDpiAwarenessContext) = *SET_PROCESS_DPI_AWARENESS_CONTEXT {
// We are on Windows 10 Anniversary Update (1607) or later.
if SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
== FALSE
@@ -58,13 +34,10 @@ pub fn become_dpi_aware() {
// V1 if we can't set V2.
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
}
} else if let Some(SetProcessDpiAwareness) =
get_function!("shcore.dll", SetProcessDpiAwareness)
{
} else if let Some(SetProcessDpiAwareness) = *SET_PROCESS_DPI_AWARENESS {
// We are on Windows 8.1 or later.
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
} else if let Some(SetProcessDPIAware) = get_function!("user32.dll", SetProcessDPIAware)
{
} else if let Some(SetProcessDPIAware) = *SET_PROCESS_DPI_AWARE {
// We are on Vista or later.
SetProcessDPIAware();
}
@@ -141,7 +114,3 @@ pub unsafe fn hwnd_dpi(hwnd: HWND) -> u32 {
}
}
}
pub fn hwnd_scale_factor(hwnd: HWND) -> f64 {
dpi_to_scale_factor(unsafe { hwnd_dpi(hwnd) })
}

View File

@@ -31,7 +31,7 @@ pub struct FileDropHandlerData {
pub interface: IDropTarget,
refcount: AtomicUsize,
window: HWND,
send_event: Box<dyn Fn(Event<()>)>,
send_event: Box<dyn Fn(Event<'static, ()>)>,
cursor_effect: DWORD,
hovered_is_valid: bool, /* If the currently hovered item is not valid there must not be any `HoveredFileCancelled` emitted */
}
@@ -42,7 +42,7 @@ pub struct FileDropHandler {
#[allow(non_snake_case)]
impl FileDropHandler {
pub fn new(window: HWND, send_event: Box<dyn Fn(Event<()>)>) -> FileDropHandler {
pub fn new(window: HWND, send_event: Box<dyn Fn(Event<'static, ()>)>) -> FileDropHandler {
let data = Box::new(FileDropHandlerData {
interface: IDropTarget {
lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl,
@@ -227,7 +227,7 @@ impl FileDropHandler {
}
impl FileDropHandlerData {
fn send_event(&self, event: Event<()>) {
fn send_event(&self, event: Event<'static, ()>) {
(self.send_event)(event);
}
}

View File

@@ -29,7 +29,6 @@ use std::{
use winapi::shared::basetsd::{DWORD_PTR, UINT_PTR};
use winapi::{
ctypes::c_int,
shared::{
minwindef::{BOOL, DWORD, HIWORD, INT, LOWORD, LPARAM, LRESULT, UINT, WPARAM},
windef::{HWND, POINT, RECT},
@@ -44,20 +43,17 @@ use winapi::{
use self::runner::{ELRShared, EventLoopRunnerShared};
use crate::{
dpi::{LogicalPosition, LogicalSize, PhysicalSize},
dpi::{PhysicalPosition, PhysicalSize},
event::{DeviceEvent, Event, Force, KeyboardInput, Touch, TouchPhase, WindowEvent},
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
platform_impl::platform::{
dark_mode::try_dark_mode,
dpi::{
become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling, hwnd_scale_factor,
},
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,
},
monitor, raw_input, util,
window::adjust_size,
window_state::{CursorFlags, WindowFlags, WindowState},
wrap_device_id, WindowId, DEVICE_ID,
},
@@ -97,26 +93,30 @@ lazy_static! {
get_function!("user32.dll", GetPointerPenInfo);
}
pub(crate) struct SubclassInput<T> {
pub(crate) struct SubclassInput<T: 'static> {
pub window_state: Arc<Mutex<WindowState>>,
pub event_loop_runner: EventLoopRunnerShared<T>,
pub file_drop_handler: FileDropHandler,
}
impl<T> SubclassInput<T> {
unsafe fn send_event(&self, event: Event<T>) {
unsafe fn send_event(&self, event: Event<'static, T>) {
self.event_loop_runner.send_event(event);
}
unsafe fn send_event_unbuffered<'e>(&self, event: Event<'e, T>) -> Result<(), Event<'e, T>> {
self.event_loop_runner.send_event_unbuffered(event)
}
}
struct ThreadMsgTargetSubclassInput<T> {
struct ThreadMsgTargetSubclassInput<T: 'static> {
event_loop_runner: EventLoopRunnerShared<T>,
user_event_receiver: Receiver<T>,
modifiers_state: ModifiersStateSide,
}
impl<T> ThreadMsgTargetSubclassInput<T> {
unsafe fn send_event(&self, event: Event<T>) {
unsafe fn send_event(&self, event: Event<'static, T>) {
self.event_loop_runner.send_event(event);
}
}
@@ -126,7 +126,7 @@ pub struct EventLoop<T: 'static> {
window_target: RootELW<T>,
}
pub struct EventLoopWindowTarget<T> {
pub struct EventLoopWindowTarget<T: 'static> {
thread_id: DWORD,
thread_msg_target: HWND,
pub(crate) runner_shared: EventLoopRunnerShared<T>,
@@ -191,7 +191,7 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(mut self, event_handler: F) -> !
where
F: 'static + FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
F: 'static + FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
self.run_return(event_handler);
::std::process::exit(0);
@@ -199,7 +199,7 @@ impl<T: 'static> EventLoop<T> {
pub fn run_return<F>(&mut self, mut event_handler: F)
where
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
let event_loop_windows_ref = &self.window_target;
@@ -465,13 +465,6 @@ lazy_static! {
winuser::RegisterWindowMessageA("Winit::DestroyMsg\0".as_ptr() as LPCSTR)
}
};
// Message sent by a `Window` after creation if it has a DPI != 96.
// WPARAM is the the DPI (u32). LOWORD of LPARAM is width, and HIWORD is height.
pub static ref INITIAL_DPI_MSG_ID: u32 = {
unsafe {
winuser::RegisterWindowMessageA("Winit::InitialDpiMsg\0".as_ptr() as LPCSTR)
}
};
// WPARAM is a bool specifying the `WindowFlags::MARKER_RETAIN_STATE_ON_SIZE` flag. See the
// documentation in the `window_state` module for more information.
pub static ref SET_RETAIN_STATE_ON_SIZE_MSG_ID: u32 = unsafe {
@@ -597,7 +590,7 @@ fn normalize_pointer_pressure(pressure: u32) -> Option<Force> {
//
// Returning 0 tells the Win32 API that the message has been processed.
// FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary
unsafe extern "system" fn public_window_callback<T>(
unsafe extern "system" fn public_window_callback<T: 'static>(
window: HWND,
msg: UINT,
wparam: WPARAM,
@@ -713,12 +706,11 @@ unsafe extern "system" fn public_window_callback<T>(
let windowpos = lparam as *const winuser::WINDOWPOS;
if (*windowpos).flags & winuser::SWP_NOMOVE != winuser::SWP_NOMOVE {
let dpi_factor = hwnd_scale_factor(window);
let logical_position =
LogicalPosition::from_physical(((*windowpos).x, (*windowpos).y), dpi_factor);
let physical_position =
PhysicalPosition::new((*windowpos).x as u32, (*windowpos).y as u32);
subclass_input.send_event(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
event: Moved(logical_position),
event: Moved(physical_position),
});
}
@@ -731,11 +723,10 @@ unsafe extern "system" fn public_window_callback<T>(
let w = LOWORD(lparam as DWORD) as u32;
let h = HIWORD(lparam as DWORD) as u32;
let dpi_factor = hwnd_scale_factor(window);
let logical_size = LogicalSize::from_physical((w, h), dpi_factor);
let physical_size = PhysicalSize::new(w, h);
let event = Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
event: Resized(logical_size),
event: Resized(physical_size),
};
{
@@ -838,10 +829,9 @@ unsafe extern "system" fn public_window_callback<T>(
});
}
let x = windowsx::GET_X_LPARAM(lparam) as f64;
let y = windowsx::GET_Y_LPARAM(lparam) as f64;
let dpi_factor = hwnd_scale_factor(window);
let position = LogicalPosition::from_physical((x, y), dpi_factor);
let x = windowsx::GET_X_LPARAM(lparam) as i32;
let y = windowsx::GET_Y_LPARAM(lparam) as i32;
let position = PhysicalPosition::new(x, y);
subclass_input.send_event(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
@@ -1130,7 +1120,6 @@ unsafe extern "system" fn public_window_callback<T>(
mem::size_of::<winuser::TOUCHINPUT>() as INT,
) > 0
{
let dpi_factor = hwnd_scale_factor(window);
for input in &inputs {
let mut location = POINT {
x: input.x / 100,
@@ -1143,7 +1132,7 @@ unsafe extern "system" fn public_window_callback<T>(
let x = location.x as f64 + (input.x % 100) as f64 / 100f64;
let y = location.y as f64 + (input.y % 100) as f64 / 100f64;
let location = LogicalPosition::from_physical((x, y), dpi_factor);
let location = PhysicalPosition::new(x, y);
subclass_input.send_event(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::Touch(Touch {
@@ -1204,7 +1193,6 @@ unsafe extern "system" fn public_window_callback<T>(
return 0;
}
let dpi_factor = hwnd_scale_factor(window);
// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getpointerframeinfohistory
// The information retrieved appears in reverse chronological order, with the most recent entry in the first
// row of the returned array
@@ -1282,7 +1270,7 @@ unsafe extern "system" fn public_window_callback<T>(
let x = location.x as f64 + x.fract();
let y = location.y as f64 + y.fract();
let location = LogicalPosition::from_physical((x, y), dpi_factor);
let location = PhysicalPosition::new(x, y);
subclass_input.send_event(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::Touch(Touch {
@@ -1403,11 +1391,9 @@ unsafe extern "system" fn public_window_callback<T>(
let window_state = subclass_input.window_state.lock();
if window_state.min_size.is_some() || window_state.max_size.is_some() {
let style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD;
let ex_style = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD;
if let Some(min_size) = window_state.min_size {
let min_size = min_size.to_physical(window_state.dpi_factor);
let (width, height) = adjust_size(min_size, style, ex_style);
let (width, height): (u32, u32) = util::adjust_size(window, min_size).into();
(*mmi).ptMinTrackSize = POINT {
x: width as i32,
y: height as i32,
@@ -1415,7 +1401,7 @@ unsafe extern "system" fn public_window_callback<T>(
}
if let Some(max_size) = window_state.max_size {
let max_size = max_size.to_physical(window_state.dpi_factor);
let (width, height) = adjust_size(max_size, style, ex_style);
let (width, height): (u32, u32) = util::adjust_size(window, max_size).into();
(*mmi).ptMaxTrackSize = POINT {
x: width as i32,
y: height as i32,
@@ -1429,7 +1415,7 @@ unsafe extern "system" fn public_window_callback<T>(
// Only sent on Windows 8.1 or newer. On Windows 7 and older user has to log out to change
// DPI, therefore all applications are closed while DPI is changing.
winuser::WM_DPICHANGED => {
use crate::event::WindowEvent::HiDpiFactorChanged;
use crate::event::WindowEvent::ScaleFactorChanged;
// This message actually provides two DPI values - x and y. However MSDN says that
// "you only need to use either the X-axis or the Y-axis value when scaling your
@@ -1437,36 +1423,208 @@ unsafe extern "system" fn public_window_callback<T>(
// 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 allow_resize = {
let mut window_state = subclass_input.window_state.lock();
let old_dpi_factor = window_state.dpi_factor;
old_dpi_factor = window_state.dpi_factor;
window_state.dpi_factor = new_dpi_factor;
new_dpi_factor != old_dpi_factor && window_state.fullscreen.is_none()
if new_dpi_factor == old_dpi_factor {
return 0;
}
window_state.fullscreen.is_none()
&& !window_state.window_flags().contains(WindowFlags::MAXIMIZED)
};
// This prevents us from re-applying DPI adjustment to the restored size after exiting
// fullscreen (the restored size is already DPI adjusted).
if allow_resize {
// Resize window to the size suggested by Windows.
let rect = &*(lparam as *const RECT);
winuser::SetWindowPos(
window,
ptr::null_mut(),
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
winuser::SWP_NOZORDER | winuser::SWP_NOACTIVATE,
let style = winuser::GetWindowLongW(window, winuser::GWL_STYLE) as _;
let style_ex = winuser::GetWindowLongW(window, winuser::GWL_EXSTYLE) as _;
let b_menu = !winuser::GetMenu(window).is_null() as BOOL;
// New size as suggested by Windows.
let suggested_rect = *(lparam as *const RECT);
// The window rect provided is the window's outer size, not it's inner size. However,
// win32 doesn't provide an `UnadjustWindowRectEx` function to get the client rect from
// the outer rect, so we instead adjust the window rect to get the decoration margins
// and remove them from the outer size.
let margin_left: i32;
let margin_top: i32;
// let margin_right: i32;
// let margin_bottom: i32;
{
let mut adjusted_rect = suggested_rect;
winuser::AdjustWindowRectExForDpi(
&mut adjusted_rect,
style,
b_menu,
style_ex,
new_dpi_x,
);
margin_left = suggested_rect.left - adjusted_rect.left;
margin_top = suggested_rect.top - adjusted_rect.top;
// margin_right = adjusted_rect.right - suggested_rect.right;
// margin_bottom = adjusted_rect.bottom - suggested_rect.bottom;
}
subclass_input.send_event(Event::WindowEvent {
let old_physical_inner_rect = {
let mut old_physical_inner_rect = mem::zeroed();
winuser::GetClientRect(window, &mut old_physical_inner_rect);
let mut origin = mem::zeroed();
winuser::ClientToScreen(window, &mut origin);
old_physical_inner_rect.left += origin.x;
old_physical_inner_rect.right += origin.x;
old_physical_inner_rect.top += origin.y;
old_physical_inner_rect.bottom += origin.y;
old_physical_inner_rect
};
let old_physical_inner_size = PhysicalSize::new(
(old_physical_inner_rect.right - old_physical_inner_rect.left) as u32,
(old_physical_inner_rect.bottom - old_physical_inner_rect.top) as u32,
);
// `allow_resize` prevents us from re-applying DPI adjustment to the restored size after
// exiting fullscreen (the restored size is already DPI adjusted).
let mut new_physical_inner_size = match allow_resize {
// 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),
false => old_physical_inner_size,
};
let _ = subclass_input.send_event_unbuffered(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
event: HiDpiFactorChanged(new_dpi_factor),
event: ScaleFactorChanged {
scale_factor: new_dpi_factor,
new_inner_size: &mut new_physical_inner_size,
},
});
// Unset maximized if we're changing the window's size.
if new_physical_inner_size != old_physical_inner_size {
WindowState::set_window_flags(subclass_input.window_state.lock(), window, |f| {
f.set(WindowFlags::MAXIMIZED, false)
});
}
let new_outer_rect: RECT;
{
let suggested_ul = (
suggested_rect.left + margin_left,
suggested_rect.top + margin_top,
);
let mut conservative_rect = RECT {
left: suggested_ul.0,
top: suggested_ul.1,
right: suggested_ul.0 + new_physical_inner_size.width as LONG,
bottom: suggested_ul.1 + new_physical_inner_size.height as LONG,
};
winuser::AdjustWindowRectExForDpi(
&mut conservative_rect,
style,
b_menu,
style_ex,
new_dpi_x,
);
// If we're not dragging the window, offset the window so that the cursor's
// relative horizontal position in the title bar is preserved.
let dragging_window = subclass_input.event_loop_runner.in_modal_loop();
if dragging_window {
let bias = {
let cursor_pos = {
let mut pos = mem::zeroed();
winuser::GetCursorPos(&mut pos);
pos
};
let suggested_cursor_horizontal_ratio = (cursor_pos.x - suggested_rect.left)
as f64
/ (suggested_rect.right - suggested_rect.left) as f64;
(cursor_pos.x
- (suggested_cursor_horizontal_ratio
* (conservative_rect.right - conservative_rect.left) as f64)
as LONG)
- conservative_rect.left
};
conservative_rect.left += bias;
conservative_rect.right += bias;
}
// Check to see if the new window rect is on the monitor with the new DPI factor.
// If it isn't, offset the window so that it is.
let new_dpi_monitor = winuser::MonitorFromWindow(window, 0);
let conservative_rect_monitor = winuser::MonitorFromRect(&conservative_rect, 0);
new_outer_rect = if conservative_rect_monitor == new_dpi_monitor {
conservative_rect
} else {
let get_monitor_rect = |monitor| {
let mut monitor_info = winuser::MONITORINFO {
cbSize: mem::size_of::<winuser::MONITORINFO>() as _,
..mem::zeroed()
};
winuser::GetMonitorInfoW(monitor, &mut monitor_info);
monitor_info.rcMonitor
};
let wrong_monitor = conservative_rect_monitor;
let wrong_monitor_rect = get_monitor_rect(wrong_monitor);
let new_monitor_rect = get_monitor_rect(new_dpi_monitor);
// The direction to nudge the window in to get the window onto the monitor with
// the new DPI factor. We calculate this by seeing which monitor edges are
// shared and nudging away from the wrong monitor based on those.
let delta_nudge_to_dpi_monitor = (
if wrong_monitor_rect.left == new_monitor_rect.right {
-1
} else if wrong_monitor_rect.right == new_monitor_rect.left {
1
} else {
0
},
if wrong_monitor_rect.bottom == new_monitor_rect.top {
1
} else if wrong_monitor_rect.top == new_monitor_rect.bottom {
-1
} else {
0
},
);
let abort_after_iterations = new_monitor_rect.right - new_monitor_rect.left
+ new_monitor_rect.bottom
- new_monitor_rect.top;
for _ in 0..abort_after_iterations {
conservative_rect.left += delta_nudge_to_dpi_monitor.0;
conservative_rect.right += delta_nudge_to_dpi_monitor.0;
conservative_rect.top += delta_nudge_to_dpi_monitor.1;
conservative_rect.bottom += delta_nudge_to_dpi_monitor.1;
if winuser::MonitorFromRect(&conservative_rect, 0) == new_dpi_monitor {
break;
}
}
conservative_rect
};
}
winuser::SetWindowPos(
window,
ptr::null_mut(),
new_outer_rect.left,
new_outer_rect.top,
new_outer_rect.right - new_outer_rect.left,
new_outer_rect.bottom - new_outer_rect.top,
winuser::SWP_NOZORDER | winuser::SWP_NOACTIVATE,
);
0
}
@@ -1502,44 +1660,6 @@ unsafe extern "system" fn public_window_callback<T>(
f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam != 0)
});
0
} else if msg == *INITIAL_DPI_MSG_ID {
use crate::event::WindowEvent::HiDpiFactorChanged;
let scale_factor = dpi_to_scale_factor(wparam as u32);
subclass_input.send_event(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
event: HiDpiFactorChanged(scale_factor),
});
// Automatically resize for actual DPI
let width = LOWORD(lparam as DWORD) as u32;
let height = HIWORD(lparam as DWORD) as u32;
let (adjusted_width, adjusted_height): (u32, u32) =
PhysicalSize::from_logical((width, height), scale_factor).into();
// We're not done yet! `SetWindowPos` needs the window size, not the client area size.
let mut rect = RECT {
top: 0,
left: 0,
bottom: adjusted_height as LONG,
right: adjusted_width as LONG,
};
let dw_style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD;
let b_menu = !winuser::GetMenu(window).is_null() as BOOL;
let dw_style_ex = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD;
winuser::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
let outer_x = (rect.right - rect.left).abs() as c_int;
let outer_y = (rect.top - rect.bottom).abs() as c_int;
winuser::SetWindowPos(
window,
ptr::null_mut(),
0,
0,
outer_x,
outer_y,
winuser::SWP_NOMOVE
| winuser::SWP_NOREPOSITION
| winuser::SWP_NOZORDER
| winuser::SWP_NOACTIVATE,
);
0
} else {
commctrl::DefSubclassProc(window, msg, wparam, lparam)
}
@@ -1547,7 +1667,7 @@ unsafe extern "system" fn public_window_callback<T>(
}
}
unsafe extern "system" fn thread_event_target_callback<T>(
unsafe extern "system" fn thread_event_target_callback<T: 'static>(
window: HWND,
msg: UINT,
wparam: WPARAM,

View File

@@ -10,17 +10,17 @@ use crate::{
};
pub(crate) type EventLoopRunnerShared<T> = Rc<ELRShared<T>>;
pub(crate) struct ELRShared<T> {
pub(crate) struct ELRShared<T: 'static> {
runner: RefCell<Option<EventLoopRunner<T>>>,
buffer: RefCell<VecDeque<Event<T>>>,
buffer: RefCell<VecDeque<Event<'static, T>>>,
redraw_buffer: Rc<RefCell<VecDeque<WindowId>>>,
}
struct EventLoopRunner<T> {
struct EventLoopRunner<T: 'static> {
control_flow: ControlFlow,
runner_state: RunnerState,
modal_redraw_window: HWND,
in_modal_loop: bool,
event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>,
event_handler: Box<dyn FnMut(Event<'_, T>, &mut ControlFlow)>,
panic_error: Option<PanicError>,
redraw_buffer: Rc<RefCell<VecDeque<WindowId>>>,
}
@@ -37,7 +37,7 @@ impl<T> ELRShared<T> {
pub(crate) unsafe fn set_runner<F>(&self, event_loop: &EventLoop<T>, f: F)
where
F: FnMut(Event<T>, &mut ControlFlow),
F: FnMut(Event<'_, T>, &mut ControlFlow),
{
let mut runner = EventLoopRunner::new(event_loop, self.redraw_buffer.clone(), f);
{
@@ -66,7 +66,18 @@ impl<T> ELRShared<T> {
}
}
pub(crate) unsafe fn send_event(&self, event: Event<T>) {
pub(crate) unsafe fn send_event(&self, event: Event<'static, 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.
self.buffer_event(event);
}
}
pub(crate) unsafe 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);
@@ -84,16 +95,14 @@ impl<T> ELRShared<T> {
}
}
return;
return Ok(());
}
}
// 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);
Err(event)
}
pub(crate) unsafe fn call_event_handler(&self, event: Event<T>) {
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);
@@ -143,7 +152,7 @@ impl<T> ELRShared<T> {
}
}
fn buffer_event(&self, event: Event<T>) {
fn buffer_event(&self, event: Event<'static, T>) {
match event {
Event::RedrawRequested(window_id) => {
self.redraw_buffer.borrow_mut().push_back(window_id)
@@ -176,7 +185,7 @@ impl<T> EventLoopRunner<T> {
f: F,
) -> EventLoopRunner<T>
where
F: FnMut(Event<T>, &mut ControlFlow),
F: FnMut(Event<'_, T>, &mut ControlFlow),
{
EventLoopRunner {
control_flow: ControlFlow::default(),
@@ -184,8 +193,8 @@ impl<T> EventLoopRunner<T> {
in_modal_loop: false,
modal_redraw_window: event_loop.window_target.p.thread_msg_target,
event_handler: mem::transmute::<
Box<dyn FnMut(Event<T>, &mut ControlFlow)>,
Box<dyn FnMut(Event<T>, &mut ControlFlow)>,
Box<dyn FnMut(Event<'_, T>, &mut ControlFlow)>,
Box<dyn FnMut(Event<'_, T>, &mut ControlFlow)>,
>(Box::new(f)),
panic_error: None,
redraw_buffer,
@@ -251,7 +260,7 @@ impl<T> EventLoopRunner<T> {
};
}
fn process_event(&mut self, event: Event<T>) {
fn process_event(&mut self, event: Event<'_, T>) {
// If we're in the modal loop, we need to have some mechanism for finding when the event
// queue has been cleared so we can call `events_cleared`. Windows doesn't give any utilities
// for doing this, but it DOES guarantee that WM_PAINT will only occur after input events have
@@ -390,7 +399,7 @@ impl<T> EventLoopRunner<T> {
}
}
fn call_event_handler(&mut self, event: Event<T>) {
fn call_event_handler(&mut self, event: Event<'_, T>) {
if self.panic_error.is_none() {
let EventLoopRunner {
ref mut panic_error,

View File

@@ -62,7 +62,7 @@ impl std::fmt::Debug for VideoMode {
}
impl VideoMode {
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
self.size.into()
}
@@ -168,14 +168,6 @@ impl MonitorHandle {
MonitorHandle(hmonitor)
}
pub(crate) fn contains_point(&self, point: &POINT) -> bool {
let monitor_info = get_monitor_info(self.0).unwrap();
point.x >= monitor_info.rcMonitor.left
&& point.x <= monitor_info.rcMonitor.right
&& point.y >= monitor_info.rcMonitor.top
&& point.y <= monitor_info.rcMonitor.bottom
}
#[inline]
pub fn name(&self) -> Option<String> {
let monitor_info = get_monitor_info(self.0).unwrap();
@@ -193,25 +185,25 @@ impl MonitorHandle {
}
#[inline]
pub fn size(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize<u32> {
let monitor_info = get_monitor_info(self.0).unwrap();
PhysicalSize {
width: (monitor_info.rcMonitor.right - monitor_info.rcMonitor.left) as f64,
height: (monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top) as f64,
width: (monitor_info.rcMonitor.right - monitor_info.rcMonitor.left) as u32,
height: (monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top) as u32,
}
}
#[inline]
pub fn position(&self) -> PhysicalPosition {
pub fn position(&self) -> PhysicalPosition<i32> {
let monitor_info = get_monitor_info(self.0).unwrap();
PhysicalPosition {
x: monitor_info.rcMonitor.left as f64,
y: monitor_info.rcMonitor.top as f64,
x: monitor_info.rcMonitor.left,
y: monitor_info.rcMonitor.top,
}
}
#[inline]
pub fn hidpi_factor(&self) -> f64 {
pub fn scale_factor(&self) -> f64 {
dpi_to_scale_factor(get_monitor_dpi(self.0).unwrap_or(96))
}

View File

@@ -6,51 +6,22 @@ use std::{
sync::atomic::{AtomicBool, Ordering},
};
use crate::window::CursorIcon;
use crate::{dpi::PhysicalSize, window::CursorIcon};
use winapi::{
ctypes::wchar_t,
shared::{
minwindef::{BOOL, DWORD},
windef::{HWND, POINT, RECT},
minwindef::{BOOL, DWORD, UINT},
windef::{DPI_AWARENESS_CONTEXT, HMONITOR, HWND, LPRECT, RECT},
},
um::{
libloaderapi::{GetProcAddress, LoadLibraryA},
shellscalingapi::{MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS},
winbase::lstrlenW,
winnt::LPCSTR,
winnt::{HRESULT, LONG, LPCSTR},
winuser,
},
};
// Helper function to dynamically load function pointer.
// `library` and `function` must be zero-terminated.
pub(super) fn get_function_impl(library: &str, function: &str) -> Option<*const c_void> {
assert_eq!(library.chars().last(), Some('\0'));
assert_eq!(function.chars().last(), Some('\0'));
// Library names we will use are ASCII so we can use the A version to avoid string conversion.
let module = unsafe { LoadLibraryA(library.as_ptr() as LPCSTR) };
if module.is_null() {
return None;
}
let function_ptr = unsafe { GetProcAddress(module, function.as_ptr() as LPCSTR) };
if function_ptr.is_null() {
return None;
}
Some(function_ptr as _)
}
macro_rules! get_function {
($lib:expr, $func:ident) => {
crate::platform_impl::platform::util::get_function_impl(
concat!($lib, '\0'),
concat!(stringify!($func), '\0'),
)
.map(|f| unsafe { std::mem::transmute::<*const _, $func>(f) })
};
}
pub fn has_flag<T>(bitset: T, flag: T) -> bool
where
T: Copy + PartialEq + BitAnd<T, Output = T>,
@@ -85,10 +56,6 @@ fn win_to_err<F: FnOnce() -> BOOL>(f: F) -> Result<(), io::Error> {
}
}
pub fn get_cursor_pos() -> Option<POINT> {
unsafe { status_map(|cursor_pos| winuser::GetCursorPos(cursor_pos)) }
}
pub fn get_window_rect(hwnd: HWND) -> Option<RECT> {
unsafe { status_map(|rect| winuser::GetWindowRect(hwnd, rect)) }
}
@@ -109,6 +76,18 @@ pub fn get_client_rect(hwnd: HWND) -> Result<RECT, io::Error> {
}
}
pub fn adjust_size(hwnd: HWND, size: PhysicalSize<u32>) -> PhysicalSize<u32> {
let (width, height): (u32, u32) = size.into();
let rect = RECT {
left: 0,
right: width as LONG,
top: 0,
bottom: height as LONG,
};
let rect = adjust_window_rect(hwnd, rect).unwrap_or(rect);
PhysicalSize::new((rect.right - rect.left) as _, (rect.bottom - rect.top) as _)
}
pub fn adjust_window_rect(hwnd: HWND, rect: RECT) -> Option<RECT> {
unsafe {
let style = winuser::GetWindowLongW(hwnd, winuser::GWL_STYLE);
@@ -128,7 +107,14 @@ pub fn adjust_window_rect_with_styles(
*r = rect;
let b_menu = !winuser::GetMenu(hwnd).is_null() as BOOL;
winuser::AdjustWindowRectEx(r, style as _, b_menu, style_ex as _)
if let (Some(get_dpi_for_window), Some(adjust_window_rect_ex_for_dpi)) =
(*GET_DPI_FOR_WINDOW, *ADJUST_WINDOW_RECT_EX_FOR_DPI)
{
let dpi = get_dpi_for_window(hwnd);
adjust_window_rect_ex_for_dpi(r, style as _, b_menu, style_ex as _, dpi)
} else {
winuser::AdjustWindowRectEx(r, style as _, b_menu, style_ex as _)
}
})
}
}
@@ -210,3 +196,71 @@ impl CursorIcon {
}
}
}
// Helper function to dynamically load function pointer.
// `library` and `function` must be zero-terminated.
pub(super) fn get_function_impl(library: &str, function: &str) -> Option<*const c_void> {
assert_eq!(library.chars().last(), Some('\0'));
assert_eq!(function.chars().last(), Some('\0'));
// Library names we will use are ASCII so we can use the A version to avoid string conversion.
let module = unsafe { LoadLibraryA(library.as_ptr() as LPCSTR) };
if module.is_null() {
return None;
}
let function_ptr = unsafe { GetProcAddress(module, function.as_ptr() as LPCSTR) };
if function_ptr.is_null() {
return None;
}
Some(function_ptr as _)
}
macro_rules! get_function {
($lib:expr, $func:ident) => {
crate::platform_impl::platform::util::get_function_impl(
concat!($lib, '\0'),
concat!(stringify!($func), '\0'),
)
.map(|f| unsafe { std::mem::transmute::<*const _, $func>(f) })
};
}
pub type SetProcessDPIAware = unsafe extern "system" fn() -> BOOL;
pub type SetProcessDpiAwareness =
unsafe extern "system" fn(value: PROCESS_DPI_AWARENESS) -> HRESULT;
pub type SetProcessDpiAwarenessContext =
unsafe extern "system" fn(value: DPI_AWARENESS_CONTEXT) -> BOOL;
pub type GetDpiForWindow = unsafe extern "system" fn(hwnd: HWND) -> UINT;
pub type GetDpiForMonitor = unsafe extern "system" fn(
hmonitor: HMONITOR,
dpi_type: MONITOR_DPI_TYPE,
dpi_x: *mut UINT,
dpi_y: *mut UINT,
) -> HRESULT;
pub type EnableNonClientDpiScaling = unsafe extern "system" fn(hwnd: HWND) -> BOOL;
pub type AdjustWindowRectExForDpi = unsafe extern "system" fn(
rect: LPRECT,
dwStyle: DWORD,
bMenu: BOOL,
dwExStyle: DWORD,
dpi: UINT,
) -> BOOL;
lazy_static! {
pub static ref GET_DPI_FOR_WINDOW: Option<GetDpiForWindow> =
get_function!("user32.dll", GetDpiForWindow);
pub static ref ADJUST_WINDOW_RECT_EX_FOR_DPI: Option<AdjustWindowRectExForDpi> =
get_function!("user32.dll", AdjustWindowRectExForDpi);
pub static ref GET_DPI_FOR_MONITOR: Option<GetDpiForMonitor> =
get_function!("shcore.dll", GetDpiForMonitor);
pub static ref ENABLE_NON_CLIENT_DPI_SCALING: Option<EnableNonClientDpiScaling> =
get_function!("user32.dll", EnableNonClientDpiScaling);
pub static ref SET_PROCESS_DPI_AWARENESS_CONTEXT: Option<SetProcessDpiAwarenessContext> =
get_function!("user32.dll", SetProcessDpiAwarenessContext);
pub static ref SET_PROCESS_DPI_AWARENESS: Option<SetProcessDpiAwareness> =
get_function!("shcore.dll", SetProcessDpiAwareness);
pub static ref SET_PROCESS_DPI_AWARE: Option<SetProcessDPIAware> =
get_function!("user32.dll", SetProcessDPIAware);
}

View File

@@ -14,7 +14,7 @@ use std::{
use winapi::{
ctypes::c_int,
shared::{
minwindef::{DWORD, HINSTANCE, LPARAM, UINT, WORD, WPARAM},
minwindef::{HINSTANCE, UINT},
windef::{HWND, POINT, RECT},
},
um::{
@@ -30,14 +30,14 @@ use winapi::{
};
use crate::{
dpi::{LogicalPosition, LogicalSize, PhysicalSize},
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
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, INITIAL_DPI_MSG_ID},
event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID},
icon::{self, IconType, WinIcon},
monitor, util,
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
@@ -125,8 +125,8 @@ impl Window {
#[inline]
pub fn set_visible(&self, visible: bool) {
let window_state = Arc::clone(&self.window_state);
let window = self.window.clone();
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
f.set(WindowFlags::VISIBLE, visible)
@@ -146,41 +146,34 @@ impl Window {
}
}
pub(crate) fn outer_position_physical(&self) -> (i32, i32) {
#[inline]
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
util::get_window_rect(self.window.0)
.map(|rect| (rect.left as i32, rect.top as i32))
.unwrap()
.map(|rect| Ok(PhysicalPosition::new(rect.left as i32, rect.top as i32)))
.expect("Unexpected GetWindowRect failure; please report this error to https://github.com/rust-windowing/winit")
}
#[inline]
pub fn outer_position(&self) -> Result<LogicalPosition, NotSupportedError> {
let physical_position = self.outer_position_physical();
let dpi_factor = self.hidpi_factor();
Ok(LogicalPosition::from_physical(
physical_position,
dpi_factor,
))
}
pub(crate) fn inner_position_physical(&self) -> (i32, i32) {
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
let mut position: POINT = unsafe { mem::zeroed() };
if unsafe { winuser::ClientToScreen(self.window.0, &mut position) } == 0 {
panic!("Unexpected ClientToScreen failure: please report this error to https://github.com/rust-windowing/winit")
}
(position.x, position.y)
Ok(PhysicalPosition::new(position.x as i32, position.y as i32))
}
#[inline]
pub fn inner_position(&self) -> Result<LogicalPosition, NotSupportedError> {
let physical_position = self.inner_position_physical();
let dpi_factor = self.hidpi_factor();
Ok(LogicalPosition::from_physical(
physical_position,
dpi_factor,
))
}
pub fn set_outer_position(&self, position: Position) {
let (x, y): (i32, i32) = position.to_physical::<i32>(self.scale_factor()).into();
let window_state = Arc::clone(&self.window_state);
let window = self.window.clone();
self.thread_executor.execute_in_thread(move || {
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
f.set(WindowFlags::MAXIMIZED, false)
});
});
pub(crate) fn set_position_physical(&self, x: i32, y: i32) {
unsafe {
winuser::SetWindowPos(
self.window.0,
@@ -199,43 +192,22 @@ impl Window {
}
#[inline]
pub fn set_outer_position(&self, logical_position: LogicalPosition) {
let dpi_factor = self.hidpi_factor();
let (x, y) = logical_position.to_physical(dpi_factor).into();
let window_state = Arc::clone(&self.window_state);
let window = self.window.clone();
self.thread_executor.execute_in_thread(move || {
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
f.set(WindowFlags::MAXIMIZED, false)
});
});
self.set_position_physical(x, y);
}
pub(crate) fn inner_size_physical(&self) -> (u32, u32) {
pub fn inner_size(&self) -> PhysicalSize<u32> {
let mut rect: RECT = unsafe { mem::zeroed() };
if unsafe { winuser::GetClientRect(self.window.0, &mut rect) } == 0 {
panic!("Unexpected GetClientRect failure: please report this error to https://github.com/rust-windowing/winit")
}
(
PhysicalSize::new(
(rect.right - rect.left) as u32,
(rect.bottom - rect.top) as u32,
)
}
#[inline]
pub fn inner_size(&self) -> LogicalSize {
let physical_size = self.inner_size_physical();
let dpi_factor = self.hidpi_factor();
LogicalSize::from_physical(physical_size, dpi_factor)
}
pub(crate) fn outer_size_physical(&self) -> (u32, u32) {
pub fn outer_size(&self) -> PhysicalSize<u32> {
util::get_window_rect(self.window.0)
.map(|rect| {
(
PhysicalSize::new(
(rect.right - rect.left) as u32,
(rect.bottom - rect.top) as u32,
)
@@ -243,13 +215,6 @@ impl Window {
.unwrap()
}
#[inline]
pub fn outer_size(&self) -> LogicalSize {
let physical_size = self.outer_size_physical();
let dpi_factor = self.hidpi_factor();
LogicalSize::from_physical(physical_size, dpi_factor)
}
pub(crate) fn set_inner_size_physical(&self, x: u32, y: u32) {
unsafe {
let rect = util::adjust_window_rect(
@@ -283,9 +248,9 @@ impl Window {
}
#[inline]
pub fn set_inner_size(&self, logical_size: LogicalSize) {
let dpi_factor = self.hidpi_factor();
let (width, height) = logical_size.to_physical(dpi_factor).into();
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 window_state = Arc::clone(&self.window_state);
let window = self.window.clone();
@@ -298,36 +263,20 @@ impl Window {
self.set_inner_size_physical(width, height);
}
pub(crate) fn set_min_inner_size_physical(&self, dimensions: Option<(u32, u32)>) {
self.window_state.lock().min_size = dimensions.map(Into::into);
#[inline]
pub fn set_min_inner_size(&self, size: Option<Size>) {
self.window_state.lock().min_size = size;
// Make windows re-check the window size bounds.
let (width, height) = self.inner_size_physical();
self.set_inner_size_physical(width, height);
let size = self.inner_size();
self.set_inner_size(size.into());
}
#[inline]
pub fn set_min_inner_size(&self, logical_size: Option<LogicalSize>) {
let physical_size = logical_size.map(|logical_size| {
let dpi_factor = self.hidpi_factor();
logical_size.to_physical(dpi_factor).into()
});
self.set_min_inner_size_physical(physical_size);
}
pub fn set_max_inner_size_physical(&self, dimensions: Option<(u32, u32)>) {
self.window_state.lock().max_size = dimensions.map(Into::into);
pub fn set_max_inner_size(&self, size: Option<Size>) {
self.window_state.lock().max_size = size;
// Make windows re-check the window size bounds.
let (width, height) = self.inner_size_physical();
self.set_inner_size_physical(width, height);
}
#[inline]
pub fn set_max_inner_size(&self, logical_size: Option<LogicalSize>) {
let physical_size = logical_size.map(|logical_size| {
let dpi_factor = self.hidpi_factor();
logical_size.to_physical(dpi_factor).into()
});
self.set_max_inner_size_physical(physical_size);
let size = self.inner_size();
self.set_inner_size(size.into());
}
#[inline]
@@ -407,11 +356,15 @@ impl Window {
}
#[inline]
pub fn hidpi_factor(&self) -> f64 {
pub fn scale_factor(&self) -> f64 {
self.window_state.lock().dpi_factor
}
fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ExternalError> {
#[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 mut point = POINT { x, y };
unsafe {
if winuser::ClientToScreen(self.window.0, &mut point) == 0 {
@@ -424,16 +377,6 @@ impl Window {
Ok(())
}
#[inline]
pub fn set_cursor_position(
&self,
logical_position: LogicalPosition,
) -> Result<(), ExternalError> {
let dpi_factor = self.hidpi_factor();
let (x, y) = logical_position.to_physical(dpi_factor).into();
self.set_cursor_position_physical(x, y)
}
#[inline]
pub fn id(&self) -> WindowId {
WindowId(self.window.0)
@@ -691,7 +634,7 @@ impl Window {
}
#[inline]
pub fn set_ime_position(&self, _logical_spot: LogicalPosition) {
pub fn set_ime_position(&self, _position: Position) {
unimplemented!();
}
@@ -724,22 +667,6 @@ pub struct WindowWrapper(HWND);
unsafe impl Sync for WindowWrapper {}
unsafe impl Send for WindowWrapper {}
pub unsafe fn adjust_size(
physical_size: PhysicalSize,
style: DWORD,
ex_style: DWORD,
) -> (LONG, LONG) {
let (width, height): (u32, u32) = physical_size.into();
let mut rect = RECT {
left: 0,
right: width as LONG,
top: 0,
bottom: height as LONG,
};
winuser::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
(rect.right - rect.left, rect.bottom - rect.top)
}
unsafe fn init<T: 'static>(
mut attributes: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes,
@@ -770,41 +697,6 @@ unsafe fn init<T: 'static>(
// registering the window class
let class_name = register_window_class(&window_icon, &taskbar_icon);
let guessed_dpi_factor = {
let monitors = monitor::available_monitors();
let dpi_factor = if !monitors.is_empty() {
let mut dpi_factor = Some(monitors[0].hidpi_factor());
for monitor in &monitors {
if Some(monitor.hidpi_factor()) != dpi_factor {
dpi_factor = None;
}
}
dpi_factor
} else {
return Err(os_error!(io::Error::new(
io::ErrorKind::NotFound,
"No monitors were detected."
)));
};
dpi_factor.unwrap_or_else(|| {
util::get_cursor_pos()
.and_then(|cursor_pos| {
let mut dpi_factor = None;
for monitor in &monitors {
if monitor.contains_point(&cursor_pos) {
dpi_factor = Some(monitor.hidpi_factor());
break;
}
}
dpi_factor
})
.unwrap_or(1.0)
})
};
info!("Guessed window DPI factor: {}", guessed_dpi_factor);
let dimensions = attributes.inner_size.unwrap_or_else(|| (1024, 768).into());
let mut window_flags = WindowFlags::empty();
window_flags.set(WindowFlags::DECORATIONS, attributes.decorations);
window_flags.set(WindowFlags::ALWAYS_ON_TOP, attributes.always_on_top);
@@ -853,20 +745,6 @@ unsafe fn init<T: 'static>(
let dpi = hwnd_dpi(real_window.0);
let dpi_factor = dpi_to_scale_factor(dpi);
if dpi_factor != guessed_dpi_factor {
let (width, height): (u32, u32) = dimensions.into();
let mut packed_dimensions = 0;
// MAKELPARAM isn't provided by winapi yet.
let ptr = &mut packed_dimensions as *mut LPARAM as *mut WORD;
*ptr.offset(0) = width as WORD;
*ptr.offset(1) = height as WORD;
winuser::PostMessageW(
real_window.0,
*INITIAL_DPI_MSG_ID,
dpi as WPARAM,
packed_dimensions,
);
}
// making the window transparent
if attributes.transparent && !pl_attribs.no_redirection_bitmap {
@@ -900,7 +778,6 @@ unsafe fn init<T: 'static>(
}
}
window_flags.set(WindowFlags::VISIBLE, attributes.visible);
window_flags.set(WindowFlags::MAXIMIZED, attributes.maximized);
// If the system theme is dark, we need to set the window theme now
@@ -927,15 +804,17 @@ unsafe fn init<T: 'static>(
thread_executor: event_loop.create_thread_executor(),
};
let dimensions = attributes
.inner_size
.unwrap_or_else(|| PhysicalSize::new(1024, 768).into());
win.set_inner_size(dimensions);
win.set_visible(attributes.visible);
if let Some(_) = attributes.fullscreen {
win.set_fullscreen(attributes.fullscreen);
force_window_active(win.window.0);
}
if let Some(dimensions) = attributes.inner_size {
win.set_inner_size(dimensions);
}
Ok(win)
}

View File

@@ -1,5 +1,5 @@
use crate::{
dpi::LogicalSize,
dpi::Size,
platform_impl::platform::{event_loop, icon::WinIcon, util},
window::{CursorIcon, Fullscreen, WindowAttributes},
};
@@ -19,8 +19,8 @@ pub struct WindowState {
pub mouse: MouseProperties,
/// Used by `WM_GETMINMAXINFO`.
pub min_size: Option<LogicalSize>,
pub max_size: Option<LogicalSize>,
pub min_size: Option<Size>,
pub max_size: Option<Size>,
pub window_icon: Option<WinIcon>,
pub taskbar_icon: Option<WinIcon>,

View File

@@ -2,7 +2,7 @@
use std::fmt;
use crate::{
dpi::{LogicalPosition, LogicalSize},
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error::{ExternalError, NotSupportedError, OsError},
event_loop::EventLoopWindowTarget,
monitor::{MonitorHandle, VideoMode},
@@ -102,17 +102,17 @@ pub struct WindowAttributes {
/// used.
///
/// The default is `None`.
pub inner_size: Option<LogicalSize>,
pub inner_size: Option<Size>,
/// The minimum dimensions a window can be, If this is `None`, the window will have no minimum dimensions (aside from reserved).
///
/// The default is `None`.
pub min_inner_size: Option<LogicalSize>,
pub min_inner_size: Option<Size>,
/// The maximum dimensions a window can be, If this is `None`, the maximum will have no maximum or will be set to the primary monitor's dimensions by the platform.
///
/// The default is `None`.
pub max_inner_size: Option<LogicalSize>,
pub max_inner_size: Option<Size>,
/// Whether the window is resizable or not.
///
@@ -197,8 +197,8 @@ impl WindowBuilder {
///
/// [`Window::set_inner_size`]: crate::window::Window::set_inner_size
#[inline]
pub fn with_inner_size(mut self, size: LogicalSize) -> Self {
self.window.inner_size = Some(size);
pub fn with_inner_size<S: Into<Size>>(mut self, size: S) -> Self {
self.window.inner_size = Some(size.into());
self
}
@@ -208,8 +208,8 @@ impl WindowBuilder {
///
/// [`Window::set_min_inner_size`]: crate::window::Window::set_min_inner_size
#[inline]
pub fn with_min_inner_size(mut self, min_size: LogicalSize) -> Self {
self.window.min_inner_size = Some(min_size);
pub fn with_min_inner_size<S: Into<Size>>(mut self, min_size: S) -> Self {
self.window.min_inner_size = Some(min_size.into());
self
}
@@ -219,8 +219,8 @@ impl WindowBuilder {
///
/// [`Window::set_max_inner_size`]: crate::window::Window::set_max_inner_size
#[inline]
pub fn with_max_inner_size(mut self, max_size: LogicalSize) -> Self {
self.window.max_inner_size = Some(max_size);
pub fn with_max_inner_size<S: Into<Size>>(mut self, max_size: S) -> Self {
self.window.max_inner_size = Some(max_size.into());
self
}
@@ -366,25 +366,25 @@ impl Window {
WindowId(self.window.id())
}
/// Returns the DPI factor that can be used to map logical pixels to physical pixels, and vice versa.
/// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
///
/// See the [`dpi`](crate::dpi) module for more information.
///
/// Note that this value can change depending on user action (for example if the window is
/// moved to another screen); as such, tracking `WindowEvent::HiDpiFactorChanged` events is
/// moved to another screen); as such, tracking `WindowEvent::ScaleFactorChanged` events is
/// the most robust way to track the DPI you need to use to draw.
///
/// ## Platform-specific
///
/// - **X11:** This respects Xft.dpi, and can be overridden using the `WINIT_HIDPI_FACTOR` environment variable.
/// - **X11:** This respects Xft.dpi, and can be overridden using the `WINIT_X11_SCALE_FACTOR` environment variable.
/// - **Android:** Always returns 1.0.
/// - **iOS:** Can only be called on the main thread. Returns the underlying `UIView`'s
/// [`contentScaleFactor`].
///
/// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc
#[inline]
pub fn hidpi_factor(&self) -> f64 {
self.window.hidpi_factor()
pub fn scale_factor(&self) -> f64 {
self.window.scale_factor()
}
/// Emits a `WindowEvent::RedrawRequested` event in the associated event loop after all OS
@@ -419,10 +419,12 @@ impl Window {
///
/// - **iOS:** Can only be called on the main thread. Returns the top left coordinates of the
/// window's [safe area] in the screen space coordinate system.
/// - **Web:** Returns the top-left coordinates relative to the viewport. _Note: this returns the
/// same value as `outer_position`._
///
/// [safe area]: https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc
#[inline]
pub fn inner_position(&self) -> Result<LogicalPosition, NotSupportedError> {
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
self.window.inner_position()
}
@@ -440,8 +442,9 @@ impl Window {
///
/// - **iOS:** Can only be called on the main thread. Returns the top left coordinates of the
/// window in the screen space coordinate system.
/// - **Web:** Returns the top-left coordinates relative to the viewport.
#[inline]
pub fn outer_position(&self) -> Result<LogicalPosition, NotSupportedError> {
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
self.window.outer_position()
}
@@ -454,25 +457,25 @@ impl Window {
///
/// - **iOS:** Can only be called on the main thread. Sets the top left coordinates of the
/// window in the screen space coordinate system.
/// - **Web:** Sets the top-left coordinates relative to the viewport.
#[inline]
pub fn set_outer_position(&self, position: LogicalPosition) {
self.window.set_outer_position(position)
pub fn set_outer_position<P: Into<Position>>(&self, position: P) {
self.window.set_outer_position(position.into())
}
/// Returns the logical size of the window's client area.
/// Returns the physical size of the window's client area.
///
/// The client area is the content of the window, excluding the title bar and borders.
///
/// Converting the returned `LogicalSize` to `PhysicalSize` produces the size your framebuffer should be.
///
/// ## Platform-specific
///
/// - **iOS:** Can only be called on the main thread. Returns the `LogicalSize` of the window's
/// - **iOS:** Can only be called on the main thread. Returns the `PhysicalSize` of the window's
/// [safe area] in screen space coordinates.
/// - **Web:** Returns the size of the canvas element.
///
/// [safe area]: https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc
#[inline]
pub fn inner_size(&self) -> LogicalSize {
pub fn inner_size(&self) -> PhysicalSize<u32> {
self.window.inner_size()
}
@@ -485,22 +488,25 @@ impl Window {
///
/// - **iOS:** Unimplemented. Currently this panics, as it's not clear what `set_inner_size`
/// would mean for iOS.
/// - **Web:** Sets the size of the canvas element.
#[inline]
pub fn set_inner_size(&self, size: LogicalSize) {
self.window.set_inner_size(size)
pub fn set_inner_size<S: Into<Size>>(&self, size: S) {
self.window.set_inner_size(size.into())
}
/// Returns the logical size of the entire window.
/// Returns the physical size of the entire window.
///
/// These dimensions include the title bar and borders. If you don't want that (and you usually don't),
/// use `inner_size` instead.
///
/// ## Platform-specific
///
/// - **iOS:** Can only be called on the main thread. Returns the `LogicalSize` of the window in
/// - **iOS:** Can only be called on the main thread. Returns the `PhysicalSize` of the window in
/// screen space coordinates.
/// - **Web:** Returns the size of the canvas element. _Note: this returns the same value as
/// `inner_size`._
#[inline]
pub fn outer_size(&self) -> LogicalSize {
pub fn outer_size(&self) -> PhysicalSize<u32> {
self.window.outer_size()
}
@@ -511,8 +517,8 @@ impl Window {
/// - **iOS:** Has no effect.
/// - **Web:** Has no effect.
#[inline]
pub fn set_min_inner_size(&self, dimensions: Option<LogicalSize>) {
self.window.set_min_inner_size(dimensions)
pub fn set_min_inner_size<S: Into<Size>>(&self, min_size: Option<S>) {
self.window.set_min_inner_size(min_size.map(|s| s.into()))
}
/// Sets a maximum dimension size for the window.
@@ -522,8 +528,8 @@ impl Window {
/// - **iOS:** Has no effect.
/// - **Web:** Has no effect.
#[inline]
pub fn set_max_inner_size(&self, dimensions: Option<LogicalSize>) {
self.window.set_max_inner_size(dimensions)
pub fn set_max_inner_size<S: Into<Size>>(&self, max_size: Option<S>) {
self.window.set_max_inner_size(max_size.map(|s| s.into()))
}
}
@@ -675,8 +681,8 @@ impl Window {
/// **iOS:** Has no effect.
/// - **Web:** Has no effect.
#[inline]
pub fn set_ime_position(&self, position: LogicalPosition) {
self.window.set_ime_position(position)
pub fn set_ime_position<P: Into<Position>>(&self, position: P) {
self.window.set_ime_position(position.into())
}
}
@@ -700,8 +706,8 @@ impl Window {
/// - **iOS:** Always returns an `Err`.
/// - **Web:** Has no effect.
#[inline]
pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), ExternalError> {
self.window.set_cursor_position(position)
pub fn set_cursor_position<P: Into<Position>>(&self, position: P) -> Result<(), ExternalError> {
self.window.set_cursor_position(position.into())
}
/// Grabs the cursor, preventing it from leaving the window.

View File

@@ -31,8 +31,9 @@ fn events_serde() {
#[test]
fn dpi_serde() {
needs_serde::<LogicalPosition>();
needs_serde::<PhysicalPosition>();
needs_serde::<LogicalSize>();
needs_serde::<PhysicalSize>();
needs_serde::<LogicalPosition<f64>>();
needs_serde::<PhysicalPosition<i32>>();
needs_serde::<PhysicalPosition<f64>>();
needs_serde::<LogicalSize<f64>>();
needs_serde::<PhysicalSize<u32>>();
}