mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
60 Commits
v0.20.0-al
...
v0.20.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
627a127f1b | ||
|
|
ec1ae68cfc | ||
|
|
3aa3880e69 | ||
|
|
a1b8d265d0 | ||
|
|
9b122c3804 | ||
|
|
28b82fb9aa | ||
|
|
7753bbba94 | ||
|
|
ac69a9c0dc | ||
|
|
d29f7f34aa | ||
|
|
85ea3f1d5d | ||
|
|
55166da437 | ||
|
|
777d9edeaa | ||
|
|
28a20aec10 | ||
|
|
3a1e694c2f | ||
|
|
b16042a047 | ||
|
|
cbf61e5cb9 | ||
|
|
077ee4d851 | ||
|
|
7b43b0bc94 | ||
|
|
6bb7db7c11 | ||
|
|
6ffd78767f | ||
|
|
f379d069b9 | ||
|
|
2da24089de | ||
|
|
57f29aa6d7 | ||
|
|
028d3ec16d | ||
|
|
d1c6506865 | ||
|
|
c0b46a03b5 | ||
|
|
114c18e70d | ||
|
|
7367b8be6c | ||
|
|
dd768fe655 | ||
|
|
d9bda3e985 | ||
|
|
fa7a3025ec | ||
|
|
e4451d6786 | ||
|
|
468b6b83ec | ||
|
|
8a3a32f286 | ||
|
|
20e81695ca | ||
|
|
027c52171d | ||
|
|
cc206d31b7 | ||
|
|
5d99316c96 | ||
|
|
d59eec4633 | ||
|
|
25e018d1ce | ||
|
|
25123bed23 | ||
|
|
a8d6db0fc1 | ||
|
|
8a9a9cd92d | ||
|
|
530ff5420b | ||
|
|
133b11fa6d | ||
|
|
5b489284e4 | ||
|
|
cdc32eb817 | ||
|
|
eb38ff453a | ||
|
|
8eb7853a1a | ||
|
|
0c151f9fb3 | ||
|
|
c10c820311 | ||
|
|
82889e2367 | ||
|
|
92741aa4ec | ||
|
|
2f352ca5cf | ||
|
|
38c8cb9f4a | ||
|
|
73248bdced | ||
|
|
01203b247b | ||
|
|
3e1d169160 | ||
|
|
c1b93fc3d0 | ||
|
|
1f81e5c872 |
107
.github/workflows/ci.yml
vendored
Normal file
107
.github/workflows/ci.yml
vendored
Normal 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
18
.github/workflows/publish.yml
vendored
Normal 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 }}
|
||||
104
.travis.yml
104
.travis.yml
@@ -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}
|
||||
48
CHANGELOG.md
48
CHANGELOG.md
@@ -1,6 +1,46 @@
|
||||
# Unreleased
|
||||
|
||||
# # 0.20.0 Alpha 5 (2019-12-09)
|
||||
# 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.
|
||||
- On macOS, fix `CursorEntered` and `CursorLeft` events fired at old window size.
|
||||
- On macOS, fix error when `set_fullscreen` is called during fullscreen transition.
|
||||
- On all platforms except mobile and WASM, implement `Window::set_minimized`.
|
||||
- On X11, fix `CursorEntered` event being generated for non-winit windows.
|
||||
- On macOS, fix crash when starting maximized without decorations.
|
||||
- On macOS, fix application not to terminate on `run_return`.
|
||||
- On Wayland, fix cursor icon updates on window borders when using CSD.
|
||||
- On Wayland, under mutter(GNOME Wayland), fix CSD being behind the status bar, when starting window in maximized mode.
|
||||
- On Windows, theme the title bar according to whether the system theme is "Light" or "Dark".
|
||||
- Added `WindowEvent::ThemeChanged` variant to handle changes to the system theme. Currently only implemented on Windows.
|
||||
- **Breaking**: Changes to the `RedrawRequested` event (#1041):
|
||||
- `RedrawRequested` has been moved from `WindowEvent` to `Event`.
|
||||
- `EventsCleared` has been renamed to `MainEventsCleared`.
|
||||
- `RedrawRequested` is now issued only after `MainEventsCleared`.
|
||||
- `RedrawEventsCleared` is issued after each set of `RedrawRequested` events.
|
||||
- Implement synthetic window focus key events on Windows.
|
||||
- **Breaking**: Change `ModifiersState` to a `bitflags` struct.
|
||||
- On Windows, implement `VirtualKeyCode` translation for `LWin` and `RWin`.
|
||||
- On Windows, fix closing the last opened window causing `DeviceEvent`s to stop getting emitted.
|
||||
- On Windows, fix `Window::set_visible` not setting internal flags correctly. This resulted in some weird behavior.
|
||||
- Add `DeviceEvent::ModifiersChanged`.
|
||||
- Deprecate `modifiers` fields in other events in favor of `ModifiersChanged`.
|
||||
- On X11, `WINIT_HIDPI_FACTOR` now dominates `Xft.dpi` when picking DPI factor for output.
|
||||
- On X11, add special value `randr` for `WINIT_HIDPI_FACTOR` to make winit use self computed DPI factor instead of the one from `Xft.dpi`.
|
||||
|
||||
# 0.20.0 Alpha 5 (2019-12-09)
|
||||
|
||||
- On macOS, fix application termination on `ControlFlow::Exit`
|
||||
- On Windows, fix missing `ReceivedCharacter` events when Alt is held.
|
||||
@@ -12,9 +52,11 @@
|
||||
- On Windows, fix focusing unfocused windows when switching from fullscreen to windowed.
|
||||
- On X11, fix reporting incorrect DPI factor when waking from suspend.
|
||||
- Change `EventLoopClosed` to contain the original event.
|
||||
- Add `is_synthetic` field to `WindowEvent` variant `KeyboardInput`,
|
||||
- **Breaking**: Add `is_synthetic` field to `WindowEvent` variant `KeyboardInput`,
|
||||
indicating that the event is generated by winit.
|
||||
- On X11, generate synthetic key events for keys held when a window gains or loses focus.
|
||||
- On X11, issue a `CursorMoved` event when a `Touch` event occurs,
|
||||
as X11 implicitly moves the cursor for such events.
|
||||
|
||||
# 0.20.0 Alpha 4 (2019-10-18)
|
||||
|
||||
@@ -42,6 +84,7 @@
|
||||
- On X11, return dummy monitor data to avoid panicking when no monitors exist.
|
||||
- On X11, prevent stealing input focus when creating a new window.
|
||||
Only steal input focus when entering fullscreen mode.
|
||||
- On Wayland, fixed DeviceEvents for relative mouse movement is not always produced
|
||||
- On Wayland, add support for set_cursor_visible and set_cursor_grab.
|
||||
- On Wayland, fixed DeviceEvents for relative mouse movement is not always produced.
|
||||
- Removed `derivative` crate dependency.
|
||||
@@ -58,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)
|
||||
|
||||
|
||||
15
Cargo.toml
15
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.20.0-alpha5"
|
||||
version = "0.20.0"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
edition = "2018"
|
||||
@@ -25,6 +25,7 @@ libc = "0.2.64"
|
||||
log = "0.4"
|
||||
serde = { version = "1", optional = true, features = ["serde_derive"] }
|
||||
raw-window-handle = "0.3"
|
||||
bitflags = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
image = "0.21"
|
||||
@@ -41,16 +42,13 @@ 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"
|
||||
default_features = false
|
||||
features = ["display_link"]
|
||||
|
||||
[target.'cfg(any(target_os = "ios", target_os = "windows"))'.dependencies]
|
||||
bitflags = "1"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
||||
version = "0.3.6"
|
||||
features = [
|
||||
@@ -77,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"
|
||||
@@ -91,6 +90,7 @@ version = "0.3.22"
|
||||
optional = true
|
||||
features = [
|
||||
'console',
|
||||
'CssStyleDeclaration',
|
||||
'BeforeUnloadEvent',
|
||||
'Document',
|
||||
'DomRect',
|
||||
@@ -117,3 +117,6 @@ package = "stdweb"
|
||||
version = "=0.4.20"
|
||||
optional = true
|
||||
features = ["experimental_features_which_may_break_on_minor_version_bumps"]
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||
console_log = "0.1"
|
||||
|
||||
@@ -80,6 +80,7 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
- **Window maximization**: The windows created by winit can be maximized upon creation.
|
||||
- **Window maximization toggle**: The windows created by winit can be maximized and unmaximized after
|
||||
creation.
|
||||
- **Window minimization**: The windows created by winit can be minimized after creation.
|
||||
- **Fullscreen**: The windows created by winit can be put into fullscreen mode.
|
||||
- **Fullscreen toggle**: The windows created by winit can be switched to and from fullscreen after
|
||||
creation.
|
||||
@@ -116,6 +117,7 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
* Setting the taskbar icon
|
||||
* Setting the parent window
|
||||
* `WS_EX_NOREDIRECTIONBITMAP` support
|
||||
* Theme the title bar according to Windows 10 Dark Mode setting
|
||||
|
||||
### macOS
|
||||
* Window activation policy
|
||||
@@ -173,6 +175,7 @@ Legend:
|
||||
|Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A |
|
||||
|Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|
|
||||
|Window maximization toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|
|
||||
|Window minimization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|
|
||||
|Fullscreen |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |
|
||||
|Fullscreen toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |
|
||||
|Exclusive fullscreen |✔️ |✔️ |✔️ |**N/A** |❌ |✔️ |**N/A**|
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.20.0-alpha4"
|
||||
winit = "0.20.0"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
|
||||
35
appveyor.yml
35
appveyor.yml
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
_ => (),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use winit::{
|
||||
event::{DeviceEvent, ElementState, Event, KeyboardInput, WindowEvent},
|
||||
event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
@@ -12,8 +12,11 @@ fn main() {
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut modifiers = ModifiersState::default();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
@@ -22,7 +25,6 @@ fn main() {
|
||||
KeyboardInput {
|
||||
state: ElementState::Released,
|
||||
virtual_keycode: Some(key),
|
||||
modifiers,
|
||||
..
|
||||
},
|
||||
..
|
||||
@@ -30,8 +32,8 @@ fn main() {
|
||||
use winit::event::VirtualKeyCode::*;
|
||||
match key {
|
||||
Escape => *control_flow = ControlFlow::Exit,
|
||||
G => window.set_cursor_grab(!modifiers.shift).unwrap(),
|
||||
H => window.set_cursor_visible(modifiers.shift),
|
||||
G => window.set_cursor_grab(!modifiers.shift()).unwrap(),
|
||||
H => window.set_cursor_visible(modifiers.shift()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@@ -43,6 +45,7 @@ fn main() {
|
||||
ElementState::Pressed => println!("mouse button {} pressed", button),
|
||||
ElementState::Released => println!("mouse button {} released", button),
|
||||
},
|
||||
DeviceEvent::ModifiersChanged(m) => modifiers = m,
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
|
||||
@@ -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,
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
39
examples/minimize.rs
Normal file
39
examples/minimize.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
extern crate winit;
|
||||
|
||||
use winit::event::{Event, VirtualKeyCode, WindowEvent};
|
||||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
use winit::window::WindowBuilder;
|
||||
|
||||
fn main() {
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -60,7 +62,7 @@ fn main() {
|
||||
..
|
||||
} => {
|
||||
window.set_title(&format!("{:?}", key));
|
||||
let state = !modifiers.shift;
|
||||
let state = !modifiers.shift();
|
||||
use VirtualKeyCode::*;
|
||||
match key {
|
||||
A => window.set_always_on_top(state),
|
||||
@@ -81,7 +83,7 @@ fn main() {
|
||||
video_modes.iter().nth(video_mode_id).unwrap()
|
||||
);
|
||||
}
|
||||
F => window.set_fullscreen(match (state, modifiers.alt) {
|
||||
F => window.set_fullscreen(match (state, modifiers.alt()) {
|
||||
(true, false) => {
|
||||
Some(Fullscreen::Borderless(window.current_monitor()))
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,21 +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::EventsCleared => {
|
||||
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::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => {
|
||||
println!("{:?}", event);
|
||||
}
|
||||
_ => (),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
74
examples/web.rs
Normal file
74
examples/web.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
pub fn main() {
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
{
|
||||
use winit::platform::web::WindowExtWebSys;
|
||||
|
||||
let canvas = window.canvas();
|
||||
|
||||
let window = web_sys::window().unwrap();
|
||||
let document = window.document().unwrap();
|
||||
let body = document.body().unwrap();
|
||||
|
||||
body.append_child(&canvas)
|
||||
.expect("Append canvas to HTML body");
|
||||
}
|
||||
|
||||
#[cfg(feature = "stdweb")]
|
||||
{
|
||||
use std_web::web::INode;
|
||||
use winit::platform::web::WindowExtStdweb;
|
||||
|
||||
let canvas = window.canvas();
|
||||
|
||||
let document = std_web::web::document();
|
||||
let body: std_web::web::Node = document.body().expect("Get HTML body").into();
|
||||
|
||||
body.append_child(&canvas);
|
||||
}
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
log::debug!("{:?}", event);
|
||||
|
||||
#[cfg(feature = "stdweb")]
|
||||
std_web::console!(log, "%s", format!("{:?}", event));
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
|
||||
Event::MainEventsCleared => {
|
||||
window.request_redraw();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
mod wasm {
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() {
|
||||
console_log::init_with_level(log::Level::Debug);
|
||||
|
||||
super::main();
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -20,7 +22,10 @@ fn main() {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
|
||||
_ => *control_flow = ControlFlow::Wait,
|
||||
Event::MainEventsCleared => {
|
||||
window.request_redraw();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -38,9 +38,8 @@ fn main() {
|
||||
..
|
||||
} => {
|
||||
quit = true;
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
Event::EventsCleared => {
|
||||
Event::MainEventsCleared => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
_ => *control_flow = ControlFlow::Wait,
|
||||
@@ -48,6 +47,7 @@ fn main() {
|
||||
});
|
||||
|
||||
// Sleep for 1/60 second to simulate rendering
|
||||
println!("rendering");
|
||||
sleep(Duration::from_millis(16));
|
||||
}
|
||||
}
|
||||
|
||||
557
src/dpi.rs
557
src/dpi.rs
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
381
src/event.rs
381
src/event.rs
@@ -3,62 +3,153 @@
|
||||
//! 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::WindowId,
|
||||
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 of the event loop's events have been processed and control flow is about
|
||||
/// to be taken away from the program.
|
||||
EventsCleared,
|
||||
|
||||
/// 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),
|
||||
WindowEvent { window_id, event } => Ok(WindowEvent { window_id, event }),
|
||||
DeviceEvent { device_id, event } => Ok(DeviceEvent { device_id, event }),
|
||||
NewEvents(cause) => Ok(NewEvents(cause)),
|
||||
EventsCleared => Ok(EventsCleared),
|
||||
MainEventsCleared => Ok(MainEventsCleared),
|
||||
RedrawRequested(wid) => Ok(RedrawRequested(wid)),
|
||||
RedrawEventsCleared => Ok(RedrawEventsCleared),
|
||||
LoopDestroyed => Ok(LoopDestroyed),
|
||||
Suspended => Ok(Suspended),
|
||||
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.
|
||||
@@ -88,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,
|
||||
@@ -135,9 +226,10 @@ pub enum WindowEvent {
|
||||
/// If `true`, the event was generated synthetically by winit
|
||||
/// in one of the following circumstances:
|
||||
///
|
||||
/// * **X11**: Synthetic key press events are generated for all keys pressed
|
||||
/// * Synthetic key press events are generated for all keys pressed
|
||||
/// when a window gains focus. Likewise, synthetic key release events
|
||||
/// are generated for all keys pressed when a window goes out of focus.
|
||||
/// ***Currently, this is only functional on X11 and Windows***
|
||||
///
|
||||
/// Otherwise, this value is always `false`.
|
||||
is_synthetic: bool,
|
||||
@@ -150,7 +242,8 @@ 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,
|
||||
},
|
||||
|
||||
@@ -165,6 +258,7 @@ pub enum WindowEvent {
|
||||
device_id: DeviceId,
|
||||
delta: MouseScrollDelta,
|
||||
phase: TouchPhase,
|
||||
#[deprecated = "Deprecated in favor of DeviceEvent::ModifiersChanged"]
|
||||
modifiers: ModifiersState,
|
||||
},
|
||||
|
||||
@@ -173,6 +267,7 @@ pub enum WindowEvent {
|
||||
device_id: DeviceId,
|
||||
state: ElementState,
|
||||
button: MouseButton,
|
||||
#[deprecated = "Deprecated in favor of DeviceEvent::ModifiersChanged"]
|
||||
modifiers: ModifiersState,
|
||||
},
|
||||
|
||||
@@ -194,22 +289,117 @@ pub enum WindowEvent {
|
||||
value: f64,
|
||||
},
|
||||
|
||||
/// The OS or application has requested that the window be redrawn.
|
||||
RedrawRequested,
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// Applications might wish to react to this to change the theme of the content of the window
|
||||
/// when the system changes the window theme.
|
||||
///
|
||||
/// At the moment this is only supported on Windows.
|
||||
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.
|
||||
@@ -274,11 +464,15 @@ pub enum DeviceEvent {
|
||||
|
||||
Key(KeyboardInput),
|
||||
|
||||
/// Keyboard modifiers have changed
|
||||
#[doc(hidden)]
|
||||
ModifiersChanged {
|
||||
modifiers: ModifiersState,
|
||||
},
|
||||
/// The keyboard modifiers have changed.
|
||||
///
|
||||
/// This is tracked internally to avoid tracking errors arising from modifier key state changes when events from
|
||||
/// this device are not being delivered to the application, e.g. due to keyboard focus being elsewhere.
|
||||
///
|
||||
/// Platform-specific behavior:
|
||||
/// - **Web**: This API is currently unimplemented on the web. This isn't by design - it's an
|
||||
/// issue, and it should get fixed - but it's the current state of the API.
|
||||
ModifiersChanged(ModifiersState),
|
||||
|
||||
Text {
|
||||
codepoint: char,
|
||||
@@ -308,6 +502,7 @@ pub struct KeyboardInput {
|
||||
///
|
||||
/// This is tracked internally to avoid tracking errors arising from modifier key state changes when events from
|
||||
/// this device are not being delivered to the application, e.g. due to keyboard focus being elsewhere.
|
||||
#[deprecated = "Deprecated in favor of DeviceEvent::ModifiersChanged"]
|
||||
pub modifiers: ModifiersState,
|
||||
}
|
||||
|
||||
@@ -341,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.
|
||||
///
|
||||
@@ -452,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.
|
||||
@@ -654,21 +849,99 @@ pub enum VirtualKeyCode {
|
||||
Cut,
|
||||
}
|
||||
|
||||
/// Represents the current state of the keyboard modifiers
|
||||
///
|
||||
/// Each field of this struct represents a modifier and is `true` if this modifier is active.
|
||||
#[derive(Default, Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct ModifiersState {
|
||||
/// The "shift" key
|
||||
pub shift: bool,
|
||||
/// The "control" key
|
||||
pub ctrl: bool,
|
||||
/// The "alt" key
|
||||
pub alt: bool,
|
||||
/// The "logo" key
|
||||
///
|
||||
/// This is the "windows" key on PC and "command" key on Mac.
|
||||
pub logo: bool,
|
||||
impl ModifiersState {
|
||||
/// Returns `true` if the shift key is pressed.
|
||||
pub fn shift(&self) -> bool {
|
||||
self.intersects(Self::SHIFT)
|
||||
}
|
||||
/// Returns `true` if the control key is pressed.
|
||||
pub fn ctrl(&self) -> bool {
|
||||
self.intersects(Self::CTRL)
|
||||
}
|
||||
/// Returns `true` if the alt key is pressed.
|
||||
pub fn alt(&self) -> bool {
|
||||
self.intersects(Self::ALT)
|
||||
}
|
||||
/// Returns `true` if the logo key is pressed.
|
||||
pub fn logo(&self) -> bool {
|
||||
self.intersects(Self::LOGO)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Represents the current state of the keyboard modifiers
|
||||
///
|
||||
/// Each flag represents a modifier and is set if this modifier is active.
|
||||
#[derive(Default)]
|
||||
pub struct ModifiersState: u32 {
|
||||
// left and right modifiers are currently commented out, but we should be able to support
|
||||
// them in a future release
|
||||
/// The "shift" key.
|
||||
const SHIFT = 0b100 << 0;
|
||||
// const LSHIFT = 0b010 << 0;
|
||||
// const RSHIFT = 0b001 << 0;
|
||||
/// The "control" key.
|
||||
const CTRL = 0b100 << 3;
|
||||
// const LCTRL = 0b010 << 3;
|
||||
// const RCTRL = 0b001 << 3;
|
||||
/// The "alt" key.
|
||||
const ALT = 0b100 << 6;
|
||||
// const LALT = 0b010 << 6;
|
||||
// const RALT = 0b001 << 6;
|
||||
/// This is the "windows" key on PC and "command" key on Mac.
|
||||
const LOGO = 0b100 << 9;
|
||||
// const LLOGO = 0b010 << 9;
|
||||
// const RLOGO = 0b001 << 9;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod modifiers_serde {
|
||||
use super::ModifiersState;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
#[serde(rename = "ModifiersState")]
|
||||
pub struct ModifiersStateSerialize {
|
||||
pub shift: bool,
|
||||
pub ctrl: bool,
|
||||
pub alt: bool,
|
||||
pub logo: bool,
|
||||
}
|
||||
|
||||
impl Serialize for ModifiersState {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let s = ModifiersStateSerialize {
|
||||
shift: self.shift(),
|
||||
ctrl: self.ctrl(),
|
||||
alt: self.alt(),
|
||||
logo: self.logo(),
|
||||
};
|
||||
s.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ModifiersState {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let ModifiersStateSerialize {
|
||||
shift,
|
||||
ctrl,
|
||||
alt,
|
||||
logo,
|
||||
} = ModifiersStateSerialize::deserialize(deserializer)?;
|
||||
let mut m = ModifiersState::empty();
|
||||
m.set(ModifiersState::SHIFT, shift);
|
||||
m.set(ModifiersState::CTRL, ctrl);
|
||||
m.set(ModifiersState::ALT, alt);
|
||||
m.set(ModifiersState::LOGO, logo);
|
||||
Ok(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ impl<T> fmt::Debug for EventLoopWindowTarget<T> {
|
||||
|
||||
/// Set by the user callback given to the `EventLoop::run` method.
|
||||
///
|
||||
/// Indicates the desired behavior of the event loop after [`Event::EventsCleared`][events_cleared]
|
||||
/// Indicates the desired behavior of the event loop after [`Event::RedrawEventsCleared`][events_cleared]
|
||||
/// is emitted. Defaults to `Poll`.
|
||||
///
|
||||
/// ## Persistency
|
||||
@@ -68,7 +68,7 @@ impl<T> fmt::Debug for EventLoopWindowTarget<T> {
|
||||
/// are **not** persistent between multiple calls to `run_return` - issuing a new call will reset
|
||||
/// the control flow to `Poll`.
|
||||
///
|
||||
/// [events_cleared]: crate::event::Event::EventsCleared
|
||||
/// [events_cleared]: crate::event::Event::RedrawEventsCleared
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ControlFlow {
|
||||
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
96
src/lib.rs
96
src/lib.rs
@@ -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,23 +52,16 @@
|
||||
//! let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
//!
|
||||
//! event_loop.run(move |event, _, control_flow| {
|
||||
//! match event {
|
||||
//! Event::EventsCleared => {
|
||||
//! // Application update code.
|
||||
//! // 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;
|
||||
//!
|
||||
//! // Queue a RedrawRequested event.
|
||||
//! window.request_redraw();
|
||||
//! },
|
||||
//! Event::WindowEvent {
|
||||
//! event: WindowEvent::RedrawRequested,
|
||||
//! ..
|
||||
//! } => {
|
||||
//! // Redraw the application.
|
||||
//! //
|
||||
//! // It's preferrable to render in this event rather than in EventsCleared, since
|
||||
//! // rendering in here allows the program to gracefully handle redraws requested
|
||||
//! // by the OS.
|
||||
//! },
|
||||
//! // 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,
|
||||
//! ..
|
||||
@@ -71,33 +69,42 @@
|
||||
//! 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,
|
||||
//! Event::MainEventsCleared => {
|
||||
//! // Application update code.
|
||||
//!
|
||||
//! // Queue a RedrawRequested event.
|
||||
//! window.request_redraw();
|
||||
//! },
|
||||
//! Event::RedrawRequested(_) => {
|
||||
//! // Redraw the application.
|
||||
//! //
|
||||
//! // It's preferrable to render in this event rather than in MainEventsCleared, since
|
||||
//! // rendering in here allows the program to gracefully handle redraws requested
|
||||
//! // by the OS.
|
||||
//! },
|
||||
//! _ => ()
|
||||
//! }
|
||||
//! });
|
||||
//! ```
|
||||
//!
|
||||
//! 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
|
||||
@@ -122,7 +129,6 @@ extern crate log;
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
#[cfg(any(target_os = "ios", target_os = "windows"))]
|
||||
extern crate bitflags;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
#[macro_use]
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -77,6 +77,9 @@ pub trait WindowExtWindows {
|
||||
|
||||
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
|
||||
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>);
|
||||
|
||||
/// Whether the system theme is currently Windows 10's "Dark Mode".
|
||||
fn is_dark_mode(&self) -> bool;
|
||||
}
|
||||
|
||||
impl WindowExtWindows for Window {
|
||||
@@ -94,6 +97,11 @@ impl WindowExtWindows for Window {
|
||||
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
|
||||
self.window.set_taskbar_icon(taskbar_icon)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_dark_mode(&self) -> bool {
|
||||
self.window.is_dark_mode()
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to Windows.
|
||||
|
||||
@@ -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,10 +356,18 @@ 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()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_minimized(&self, _minimized: bool) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, _maximized: bool) {
|
||||
// N/A
|
||||
@@ -395,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
|
||||
}
|
||||
|
||||
|
||||
@@ -12,15 +12,16 @@ use std::{
|
||||
use objc::runtime::{BOOL, YES};
|
||||
|
||||
use crate::{
|
||||
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,17 +46,13 @@ 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::WindowEvent {
|
||||
window_id: _,
|
||||
event: WindowEvent::RedrawRequested,
|
||||
} = self
|
||||
{
|
||||
if let Event::RedrawRequested(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
@@ -69,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>,
|
||||
},
|
||||
@@ -85,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 {
|
||||
@@ -226,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,
|
||||
@@ -249,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() {
|
||||
@@ -262,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 {
|
||||
@@ -271,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),
|
||||
@@ -284,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)
|
||||
}
|
||||
@@ -591,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.
|
||||
@@ -620,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() {
|
||||
@@ -642,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 {
|
||||
@@ -692,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -755,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);
|
||||
}
|
||||
@@ -776,16 +800,20 @@ 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::EventsCleared);
|
||||
handle_nonuser_event(EventWrapper::StaticEvent(Event::MainEventsCleared));
|
||||
|
||||
let mut this = AppState::get_mut();
|
||||
let redraw_events = this
|
||||
let mut redraw_events: Vec<EventWrapper> = this
|
||||
.main_events_cleared_transition()
|
||||
.into_iter()
|
||||
.map(|window| Event::WindowEvent {
|
||||
window_id: RootWindowId(window.into()),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
});
|
||||
.map(|window| {
|
||||
EventWrapper::StaticEvent(Event::RedrawRequested(RootWindowId(window.into())))
|
||||
})
|
||||
.collect();
|
||||
|
||||
if !redraw_events.is_empty() {
|
||||
redraw_events.push(EventWrapper::StaticEvent(Event::RedrawEventsCleared));
|
||||
}
|
||||
drop(this);
|
||||
|
||||
handle_nonuser_events(redraw_events);
|
||||
@@ -806,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,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -18,31 +18,44 @@ pub struct VideoMode {
|
||||
pub(crate) size: (u32, u32),
|
||||
pub(crate) bit_depth: u16,
|
||||
pub(crate) refresh_rate: u16,
|
||||
pub(crate) screen_mode: id,
|
||||
pub(crate) screen_mode: NativeDisplayMode,
|
||||
pub(crate) monitor: MonitorHandle,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct NativeDisplayMode(pub id);
|
||||
|
||||
unsafe impl Send for NativeDisplayMode {}
|
||||
|
||||
impl Drop for NativeDisplayMode {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let () = msg_send![self.0, release];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for NativeDisplayMode {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let _: id = msg_send![self.0, retain];
|
||||
}
|
||||
NativeDisplayMode(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for VideoMode {
|
||||
fn clone(&self) -> VideoMode {
|
||||
VideoMode {
|
||||
size: self.size,
|
||||
bit_depth: self.bit_depth,
|
||||
refresh_rate: self.refresh_rate,
|
||||
screen_mode: unsafe { msg_send![self.screen_mode, retain] },
|
||||
screen_mode: self.screen_mode.clone(),
|
||||
monitor: self.monitor.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VideoMode {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
assert_main_thread!("`VideoMode` can only be dropped on the main thread on iOS");
|
||||
let () = msg_send![self.screen_mode, release];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoMode {
|
||||
unsafe fn retained_new(uiscreen: id, screen_mode: id) -> VideoMode {
|
||||
assert_main_thread!("`VideoMode` can only be created on the main thread on iOS");
|
||||
@@ -64,16 +77,18 @@ impl VideoMode {
|
||||
60
|
||||
};
|
||||
let size: CGSize = msg_send![screen_mode, size];
|
||||
let screen_mode: id = msg_send![screen_mode, retain];
|
||||
let screen_mode = NativeDisplayMode(screen_mode);
|
||||
VideoMode {
|
||||
size: (size.width as u32, size.height as u32),
|
||||
bit_depth: 32,
|
||||
refresh_rate: refresh_rate as u16,
|
||||
screen_mode: msg_send![screen_mode, retain],
|
||||
screen_mode,
|
||||
monitor: MonitorHandle::retained_new(uiscreen),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> PhysicalSize {
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
self.size.into()
|
||||
}
|
||||
|
||||
@@ -156,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)
|
||||
@@ -201,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
|
||||
|
||||
@@ -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,
|
||||
@@ -102,10 +102,14 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
|
||||
unsafe {
|
||||
let window: id = msg_send![object, window];
|
||||
assert!(!window.is_null());
|
||||
app_state::handle_nonuser_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(window.into()),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
});
|
||||
app_state::handle_nonuser_events(
|
||||
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
|
||||
@@ -497,7 +507,7 @@ pub unsafe fn create_window(
|
||||
match window_attributes.fullscreen {
|
||||
Some(Fullscreen::Exclusive(ref video_mode)) => {
|
||||
let uiscreen = video_mode.monitor().ui_screen() as id;
|
||||
let () = msg_send![uiscreen, setCurrentMode: video_mode.video_mode.screen_mode];
|
||||
let () = msg_send![uiscreen, setCurrentMode: video_mode.video_mode.screen_mode.0];
|
||||
msg_send![window, setScreen:video_mode.monitor().ui_screen()]
|
||||
}
|
||||
Some(Fullscreen::Borderless(ref monitor)) => {
|
||||
@@ -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);
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
|
||||
@@ -169,6 +180,10 @@ impl Inner {
|
||||
debug!("`Window::set_cursor_visible` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn set_minimized(&self, _minimized: bool) {
|
||||
warn!("`Window::set_minimized` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn set_maximized(&self, _maximized: bool) {
|
||||
warn!("`Window::set_maximized` is ignored on iOS")
|
||||
}
|
||||
@@ -239,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")
|
||||
}
|
||||
|
||||
@@ -339,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,
|
||||
};
|
||||
|
||||
@@ -379,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];
|
||||
@@ -393,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)),
|
||||
},
|
||||
))),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -421,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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,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,
|
||||
@@ -605,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,
|
||||
|
||||
@@ -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),
|
||||
@@ -367,6 +365,14 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_minimized(&self, minimized: bool) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_minimized(minimized),
|
||||
&Window::Wayland(ref w) => w.set_minimized(minimized),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
match self {
|
||||
@@ -408,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(_) => (),
|
||||
@@ -595,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()
|
||||
@@ -608,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()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,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),
|
||||
@@ -633,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),
|
||||
@@ -674,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.
|
||||
|
||||
@@ -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,67 +448,84 @@ 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");
|
||||
}
|
||||
}
|
||||
// do a second run of post-dispatch-triggers, to handle user-generated "request-redraw"
|
||||
// in response of resize & friends
|
||||
self.post_dispatch_triggers();
|
||||
{
|
||||
let mut guard = sink.lock().unwrap();
|
||||
guard.empty_with(|evt| {
|
||||
sticky_exit_callback(
|
||||
evt,
|
||||
&self.window_target,
|
||||
&mut control_flow,
|
||||
&mut callback,
|
||||
);
|
||||
});
|
||||
|
||||
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::EventsCleared,
|
||||
Event::MainEventsCleared,
|
||||
&self.window_target,
|
||||
&mut control_flow,
|
||||
&mut callback,
|
||||
);
|
||||
}
|
||||
|
||||
// handle request-redraw
|
||||
{
|
||||
self.redraw_triggers(|wid, window_target| {
|
||||
sticky_exit_callback(
|
||||
Event::RedrawRequested(crate::window::WindowId(
|
||||
crate::platform_impl::WindowId::Wayland(wid),
|
||||
)),
|
||||
window_target,
|
||||
&mut control_flow,
|
||||
&mut callback,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// send RedrawEventsCleared
|
||||
{
|
||||
sticky_exit_callback(
|
||||
Event::RedrawEventsCleared,
|
||||
&self.window_target,
|
||||
&mut control_flow,
|
||||
&mut callback,
|
||||
@@ -559,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,
|
||||
}),
|
||||
@@ -590,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,
|
||||
);
|
||||
@@ -621,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 {
|
||||
@@ -652,12 +646,44 @@ impl<T> EventLoopWindowTarget<T> {
|
||||
*/
|
||||
|
||||
impl<T> EventLoop<T> {
|
||||
fn post_dispatch_triggers(&mut self) {
|
||||
let mut sink = self.sink.lock().unwrap();
|
||||
fn redraw_triggers<F>(&mut self, mut callback: F)
|
||||
where
|
||||
F: FnMut(WindowId, &RootELW<T>),
|
||||
{
|
||||
let window_target = match self.window_target.p {
|
||||
crate::platform_impl::EventLoopWindowTarget::Wayland(ref wt) => wt,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
window_target.store.lock().unwrap().for_each_redraw_trigger(
|
||||
|refresh, frame_refresh, wid, frame| {
|
||||
if let Some(frame) = frame {
|
||||
if frame_refresh {
|
||||
frame.refresh();
|
||||
if !refresh {
|
||||
frame.surface().commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
if refresh {
|
||||
callback(wid, &self.window_target);
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -665,47 +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 {
|
||||
// Drop resize events equaled to the current size
|
||||
if newsize != *window.size {
|
||||
let (w, h) = newsize;
|
||||
frame.resize(w, h);
|
||||
frame.refresh();
|
||||
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,
|
||||
);
|
||||
*window.size = (w, h);
|
||||
} else {
|
||||
// Refresh csd, etc, otherwise
|
||||
frame.refresh();
|
||||
}
|
||||
} else if window.frame_refresh {
|
||||
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();
|
||||
if !window.refresh {
|
||||
frame.surface().commit()
|
||||
|
||||
// Don't send resize event downstream if the new size is identical to the
|
||||
// current one.
|
||||
if (w, h) != *window.size {
|
||||
let logical_size = crate::dpi::LogicalSize::new(w as f64, h as f64);
|
||||
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 window.refresh {
|
||||
sink.send_window_event(crate::event::WindowEvent::RedrawRequested, 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 {
|
||||
@@ -720,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;
|
||||
|
||||
@@ -746,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(),
|
||||
};
|
||||
@@ -770,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>>>,
|
||||
@@ -783,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 { .. } => (),
|
||||
@@ -829,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(),
|
||||
))
|
||||
}
|
||||
@@ -863,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 {
|
||||
@@ -897,7 +945,7 @@ pub struct VideoMode {
|
||||
|
||||
impl VideoMode {
|
||||
#[inline]
|
||||
pub fn size(&self) -> PhysicalSize {
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
self.size.into()
|
||||
}
|
||||
|
||||
@@ -957,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 {
|
||||
@@ -967,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)
|
||||
@@ -986,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()
|
||||
@@ -999,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))
|
||||
@@ -1007,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)
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
@@ -404,19 +372,11 @@ fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
|
||||
|
||||
impl ModifiersState {
|
||||
pub(crate) fn from_wayland(mods: keyboard::ModifiersState) -> ModifiersState {
|
||||
ModifiersState {
|
||||
shift: mods.shift,
|
||||
ctrl: mods.ctrl,
|
||||
alt: mods.alt,
|
||||
logo: mods.logo,
|
||||
}
|
||||
let mut m = ModifiersState::empty();
|
||||
m.set(ModifiersState::SHIFT, mods.shift);
|
||||
m.set(ModifiersState::CTRL, mods.ctrl);
|
||||
m.set(ModifiersState::ALT, mods.alt);
|
||||
m.set(ModifiersState::LOGO, mods.logo);
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
@@ -56,6 +55,13 @@ pub fn implement_pointer<T: 'static>(
|
||||
let wid = store.find_wid(&surface);
|
||||
if let Some(wid) = wid {
|
||||
mouse_focus = Some(wid);
|
||||
|
||||
// Reload cursor style only when we enter winit's surface. Calling
|
||||
// this function every time on `PtrEvent::Enter` could interfere with
|
||||
// SCTK CSD handling, since it changes cursor icons when you hover
|
||||
// cursor over the window borders.
|
||||
cursor_manager.reload_cursor_style();
|
||||
|
||||
sink.send_window_event(
|
||||
WindowEvent::CursorEntered {
|
||||
device_id: crate::event::DeviceId(
|
||||
@@ -75,8 +81,6 @@ pub fn implement_pointer<T: 'static>(
|
||||
wid,
|
||||
);
|
||||
}
|
||||
|
||||
cursor_manager.reload_cursor_style();
|
||||
}
|
||||
PtrEvent::Leave { surface, .. } => {
|
||||
mouse_focus = None;
|
||||
@@ -237,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!(),
|
||||
},
|
||||
(),
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -259,6 +281,13 @@ impl Window {
|
||||
*(self.need_frame_refresh.lock().unwrap()) = true;
|
||||
}
|
||||
|
||||
pub fn set_minimized(&self, minimized: bool) {
|
||||
// An app cannot un-minimize itself on Wayland
|
||||
if minimized {
|
||||
self.frame.lock().unwrap().set_minimized();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
if maximized {
|
||||
self.frame.lock().unwrap().set_maximized();
|
||||
@@ -318,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()))
|
||||
}
|
||||
|
||||
@@ -368,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>>,
|
||||
@@ -388,9 +418,8 @@ 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 refresh: bool,
|
||||
pub frame_refresh: bool,
|
||||
pub closed: bool,
|
||||
pub grab_cursor: Option<bool>,
|
||||
pub surface: &'a wl_surface::WlSurface,
|
||||
@@ -455,9 +484,8 @@ impl WindowStore {
|
||||
f(WindowStoreForEach {
|
||||
newsize: window.newsize.take(),
|
||||
size: &mut *(window.size.lock().unwrap()),
|
||||
prev_dpi: window.current_dpi,
|
||||
new_dpi: window.new_dpi,
|
||||
refresh: replace(&mut *window.need_refresh.lock().unwrap(), false),
|
||||
frame_refresh: replace(&mut *window.need_frame_refresh.lock().unwrap(), false),
|
||||
closed: window.closed,
|
||||
grab_cursor: window.cursor_grab_changed.lock().unwrap().take(),
|
||||
surface: &window.surface,
|
||||
@@ -471,4 +499,20 @@ impl WindowStore {
|
||||
window.closed = false;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_each_redraw_trigger<F>(&mut self, mut f: F)
|
||||
where
|
||||
F: FnMut(bool, bool, WindowId, Option<&mut SWindow<ConceptFrame>>),
|
||||
{
|
||||
for window in &mut self.windows {
|
||||
let opt_arc = window.frame.upgrade();
|
||||
let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap());
|
||||
f(
|
||||
replace(&mut *window.need_refresh.lock().unwrap(), false),
|
||||
replace(&mut *window.need_frame_refresh.lock().unwrap(), false),
|
||||
make_wid(&window.surface),
|
||||
opt_mutex_lock.as_mut().map(|m| &mut **m),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,8 +11,10 @@ use super::{
|
||||
use util::modifiers::{ModifierKeyState, ModifierKeymap};
|
||||
|
||||
use crate::{
|
||||
dpi::{LogicalPosition, LogicalSize},
|
||||
event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent},
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
event::{
|
||||
DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, TouchPhase, WindowEvent,
|
||||
},
|
||||
event_loop::EventLoopWindowTarget as RootELW,
|
||||
};
|
||||
|
||||
@@ -25,6 +27,9 @@ pub(super) struct EventProcessor<T: 'static> {
|
||||
pub(super) target: Rc<RootELW<T>>,
|
||||
pub(super) mod_keymap: ModifierKeymap,
|
||||
pub(super) device_mod_state: ModifierKeyState,
|
||||
// Number of touch events currently in progress
|
||||
pub(super) num_touch: u32,
|
||||
pub(super) first_touch: Option<u64>,
|
||||
}
|
||||
|
||||
impl<T: 'static> EventProcessor<T> {
|
||||
@@ -40,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);
|
||||
@@ -54,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);
|
||||
@@ -102,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.
|
||||
@@ -132,7 +137,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
|
||||
callback(Event::DeviceEvent {
|
||||
device_id,
|
||||
event: DeviceEvent::ModifiersChanged { modifiers },
|
||||
event: DeviceEvent::ModifiersChanged(modifiers),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -316,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
|
||||
@@ -339,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) = {
|
||||
@@ -369,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
|
||||
@@ -387,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 {
|
||||
@@ -401,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,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()),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -529,13 +517,14 @@ impl<T: 'static> EventProcessor<T> {
|
||||
ffi::Expose => {
|
||||
let xev: &ffi::XExposeEvent = xev.as_ref();
|
||||
|
||||
let window = xev.window;
|
||||
let window_id = mkwid(window);
|
||||
// Multiple Expose events may be received for subareas of a window.
|
||||
// We issue `RedrawRequested` only for the last event of such a series.
|
||||
if xev.count == 0 {
|
||||
let window = xev.window;
|
||||
let window_id = mkwid(window);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::RedrawRequested,
|
||||
});
|
||||
callback(Event::RedrawRequested(window_id));
|
||||
}
|
||||
}
|
||||
|
||||
ffi::KeyPress | ffi::KeyRelease => {
|
||||
@@ -573,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 {
|
||||
@@ -620,7 +610,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
ElementState::{Pressed, Released},
|
||||
MouseButton::{Left, Middle, Other, Right},
|
||||
MouseScrollDelta::LineDelta,
|
||||
Touch, TouchPhase,
|
||||
Touch,
|
||||
WindowEvent::{
|
||||
AxisMotion, CursorEntered, CursorLeft, CursorMoved, Focused, MouseInput,
|
||||
MouseWheel,
|
||||
@@ -722,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;
|
||||
}
|
||||
@@ -829,18 +812,15 @@ impl<T: 'static> EventProcessor<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: CursorEntered { device_id },
|
||||
});
|
||||
|
||||
if let Some(dpi_factor) =
|
||||
self.with_window(xev.event, |window| window.hidpi_factor())
|
||||
{
|
||||
let position = LogicalPosition::from_physical(
|
||||
(xev.event_x as f64, xev.event_y as f64),
|
||||
dpi_factor,
|
||||
);
|
||||
if self.window_exists(xev.event) {
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: CursorEntered { device_id },
|
||||
});
|
||||
|
||||
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
|
||||
@@ -883,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
|
||||
@@ -913,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 {
|
||||
@@ -959,13 +933,26 @@ 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 {
|
||||
let location = LogicalPosition::from_physical(
|
||||
(xev.event_x as f64, xev.event_y as f64),
|
||||
dpi_factor,
|
||||
);
|
||||
if self.window_exists(xev.event) {
|
||||
let id = xev.detail as u64;
|
||||
let modifiers = self.device_mod_state.modifiers();
|
||||
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.
|
||||
if is_first_touch(&mut self.first_touch, &mut self.num_touch, id, phase)
|
||||
{
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::CursorMoved {
|
||||
device_id: mkdid(util::VIRTUAL_CORE_POINTER),
|
||||
position: location.cast(),
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Touch(Touch {
|
||||
@@ -973,7 +960,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
phase,
|
||||
location,
|
||||
force: None, // TODO
|
||||
id: xev.detail as u64,
|
||||
id,
|
||||
}),
|
||||
})
|
||||
}
|
||||
@@ -1066,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 {
|
||||
@@ -1090,9 +1078,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
if modifiers != new_modifiers {
|
||||
callback(Event::DeviceEvent {
|
||||
device_id,
|
||||
event: DeviceEvent::ModifiersChanged {
|
||||
modifiers: new_modifiers,
|
||||
},
|
||||
event: DeviceEvent::ModifiersChanged(new_modifiers),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1135,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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1181,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);
|
||||
|
||||
@@ -1200,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 {
|
||||
@@ -1216,3 +1224,23 @@ impl<T: 'static> EventProcessor<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_first_touch(first: &mut Option<u64>, num: &mut u32, id: u64, phase: TouchPhase) -> bool {
|
||||
match phase {
|
||||
TouchPhase::Started => {
|
||||
if *num == 0 {
|
||||
*first = Some(id);
|
||||
}
|
||||
*num += 1;
|
||||
}
|
||||
TouchPhase::Cancelled | TouchPhase::Ended => {
|
||||
if *first == Some(id) {
|
||||
*first = None;
|
||||
}
|
||||
*num = num.saturating_sub(1);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
*first == Some(id)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -45,12 +49,15 @@ use self::{
|
||||
};
|
||||
use crate::{
|
||||
error::OsError as RootOsError,
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event::{Event, StartCause},
|
||||
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
||||
platform_impl::{platform::sticky_exit_callback, PlatformSpecificWindowBuilderAttributes},
|
||||
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> {
|
||||
@@ -151,6 +155,8 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
xconn.update_cached_wm_info(root);
|
||||
|
||||
let pending_redraws: Arc<Mutex<HashSet<WindowId>>> = Default::default();
|
||||
|
||||
let mut mod_keymap = ModifierKeymap::new();
|
||||
mod_keymap.reset_from_x_connection(&xconn);
|
||||
|
||||
@@ -164,33 +170,32 @@ impl<T: 'static> EventLoop<T> {
|
||||
xconn,
|
||||
wm_delete_window,
|
||||
net_wm_ping,
|
||||
pending_redraws: Default::default(),
|
||||
pending_redraws: pending_redraws.clone(),
|
||||
}),
|
||||
_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(),
|
||||
@@ -199,6 +204,8 @@ impl<T: 'static> EventLoop<T> {
|
||||
xi2ext,
|
||||
mod_keymap,
|
||||
device_mod_state: Default::default(),
|
||||
num_touch: 0,
|
||||
first_touch: None,
|
||||
};
|
||||
|
||||
// Register for device hotplug events
|
||||
@@ -208,36 +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();
|
||||
drain_events(&mut processor, &mut pending_events);
|
||||
}
|
||||
}
|
||||
})
|
||||
.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,
|
||||
};
|
||||
@@ -255,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),
|
||||
@@ -269,28 +256,31 @@ 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
// send MainEventsCleared
|
||||
{
|
||||
sticky_exit_callback(
|
||||
crate::event::Event::MainEventsCleared,
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
&mut callback,
|
||||
);
|
||||
}
|
||||
// Empty the redraw requests
|
||||
{
|
||||
// Release the lock to prevent deadlock
|
||||
@@ -298,20 +288,17 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
for wid in windows {
|
||||
sticky_exit_callback(
|
||||
Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(super::WindowId::X(wid)),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
},
|
||||
Event::RedrawRequested(crate::window::WindowId(super::WindowId::X(wid))),
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
&mut callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
// send Events cleared
|
||||
// send RedrawEventsCleared
|
||||
{
|
||||
sticky_exit_callback(
|
||||
crate::event::Event::EventsCleared,
|
||||
crate::event::Event::RedrawEventsCleared,
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
&mut callback,
|
||||
@@ -319,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,
|
||||
@@ -350,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(
|
||||
@@ -381,37 +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();
|
||||
|
||||
drain_events(&mut processor, &mut pending_events);
|
||||
}
|
||||
let wt = get_xtarget(&self.target);
|
||||
let mut pending_redraws = wt.pending_redraws.lock().unwrap();
|
||||
|
||||
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>>,
|
||||
) {
|
||||
let mut callback = |event| {
|
||||
pending_events.push_back(event);
|
||||
};
|
||||
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,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!()
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -17,12 +17,12 @@ impl ModifiersState {
|
||||
}
|
||||
|
||||
pub(crate) fn from_x11_mask(mask: c_uint) -> Self {
|
||||
ModifiersState {
|
||||
alt: mask & ffi::Mod1Mask != 0,
|
||||
shift: mask & ffi::ShiftMask != 0,
|
||||
ctrl: mask & ffi::ControlMask != 0,
|
||||
logo: mask & ffi::Mod4Mask != 0,
|
||||
}
|
||||
let mut m = ModifiersState::empty();
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -116,10 +116,10 @@ impl ModifierKeyState {
|
||||
let mut new_state = *state;
|
||||
|
||||
match except {
|
||||
Some(Modifier::Alt) => new_state.alt = self.state.alt,
|
||||
Some(Modifier::Ctrl) => new_state.ctrl = self.state.ctrl,
|
||||
Some(Modifier::Shift) => new_state.shift = self.state.shift,
|
||||
Some(Modifier::Logo) => new_state.logo = self.state.logo,
|
||||
Some(Modifier::Alt) => new_state.set(ModifiersState::ALT, self.state.alt()),
|
||||
Some(Modifier::Ctrl) => new_state.set(ModifiersState::CTRL, self.state.ctrl()),
|
||||
Some(Modifier::Shift) => new_state.set(ModifiersState::SHIFT, self.state.shift()),
|
||||
Some(Modifier::Logo) => new_state.set(ModifiersState::LOGO, self.state.logo()),
|
||||
None => (),
|
||||
}
|
||||
|
||||
@@ -170,18 +170,18 @@ impl ModifierKeyState {
|
||||
|
||||
fn get_modifier(state: &ModifiersState, modifier: Modifier) -> bool {
|
||||
match modifier {
|
||||
Modifier::Alt => state.alt,
|
||||
Modifier::Ctrl => state.ctrl,
|
||||
Modifier::Shift => state.shift,
|
||||
Modifier::Logo => state.logo,
|
||||
Modifier::Alt => state.alt(),
|
||||
Modifier::Ctrl => state.ctrl(),
|
||||
Modifier::Shift => state.shift(),
|
||||
Modifier::Logo => state.logo(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_modifier(state: &mut ModifiersState, modifier: Modifier, value: bool) {
|
||||
match modifier {
|
||||
Modifier::Alt => state.alt = value,
|
||||
Modifier::Ctrl => state.ctrl = value,
|
||||
Modifier::Shift => state.shift = value,
|
||||
Modifier::Logo => state.logo = value,
|
||||
Modifier::Alt => state.set(ModifiersState::ALT, value),
|
||||
Modifier::Ctrl => state.set(ModifiersState::CTRL, value),
|
||||
Modifier::Shift => state.set(ModifiersState::SHIFT, value),
|
||||
Modifier::Logo => state.set(ModifiersState::LOGO, value),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,26 +4,19 @@ 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 {
|
||||
Randr,
|
||||
Scale(f64),
|
||||
NotSet,
|
||||
}
|
||||
|
||||
pub fn calc_dpi_factor(
|
||||
(width_px, height_px): (u32, u32),
|
||||
(width_mm, height_mm): (u64, u64),
|
||||
) -> f64 {
|
||||
// Override DPI if `WINIT_HIDPI_FACTOR` variable is set
|
||||
let dpi_override = env::var("WINIT_HIDPI_FACTOR")
|
||||
.ok()
|
||||
.and_then(|var| f64::from_str(&var).ok());
|
||||
if let Some(dpi_override) = dpi_override {
|
||||
if !validate_hidpi_factor(dpi_override) {
|
||||
panic!(
|
||||
"`WINIT_HIDPI_FACTOR` invalid; DPI factors must be normal floats greater than 0. Got `{}`",
|
||||
dpi_override,
|
||||
);
|
||||
}
|
||||
return dpi_override;
|
||||
}
|
||||
|
||||
// See http://xpra.org/trac/ticket/728 for more information.
|
||||
if width_mm == 0 || height_mm == 0 {
|
||||
warn!("XRandR reported that the display's 0mm in size, which is certifiably insane");
|
||||
@@ -33,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
|
||||
}
|
||||
|
||||
@@ -107,20 +100,65 @@ impl XConnection {
|
||||
(*output_info).nameLen as usize,
|
||||
);
|
||||
let name = String::from_utf8_lossy(name_slice).into();
|
||||
let hidpi_factor = if let Some(dpi) = self.get_xft_dpi() {
|
||||
dpi / 96.
|
||||
} else {
|
||||
calc_dpi_factor(
|
||||
// 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" {
|
||||
EnvVarDPI::Randr
|
||||
} else if let Ok(dpi) = f64::from_str(&var) {
|
||||
EnvVarDPI::Scale(dpi)
|
||||
} else if var.is_empty() {
|
||||
EnvVarDPI::NotSet
|
||||
} else {
|
||||
panic!(
|
||||
"`WINIT_X11_SCALE_FACTOR` invalid; DPI factors must be either normal floats greater than 0, or `randr`. Got `{}`",
|
||||
var
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let scale_factor = match dpi_env {
|
||||
EnvVarDPI::Randr => calc_dpi_factor(
|
||||
((*crtc).width as u32, (*crtc).height as u32),
|
||||
(
|
||||
(*output_info).mm_width as u64,
|
||||
(*output_info).mm_height as u64,
|
||||
),
|
||||
)
|
||||
),
|
||||
EnvVarDPI::Scale(dpi_override) => {
|
||||
if !validate_scale_factor(dpi_override) {
|
||||
panic!(
|
||||
"`WINIT_X11_SCALE_FACTOR` invalid; DPI factors must be either normal floats greater than 0, or `randr`. Got `{}`",
|
||||
dpi_override,
|
||||
);
|
||||
}
|
||||
dpi_override
|
||||
}
|
||||
EnvVarDPI::NotSet => {
|
||||
if let Some(dpi) = self.get_xft_dpi() {
|
||||
dpi / 96.
|
||||
} else {
|
||||
calc_dpi_factor(
|
||||
((*crtc).width as u32, (*crtc).height as u32),
|
||||
(
|
||||
(*output_info).mm_width as u64,
|
||||
(*output_info).mm_height as u64,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(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 {
|
||||
|
||||
@@ -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") };
|
||||
@@ -749,6 +754,35 @@ impl UnownedWindow {
|
||||
self.xconn.primary_monitor()
|
||||
}
|
||||
|
||||
fn set_minimized_inner(&self, minimized: bool) -> util::Flusher<'_> {
|
||||
unsafe {
|
||||
if minimized {
|
||||
let screen = (self.xconn.xlib.XDefaultScreen)(self.xconn.display);
|
||||
|
||||
(self.xconn.xlib.XIconifyWindow)(self.xconn.display, self.xwindow, screen);
|
||||
|
||||
util::Flusher::new(&self.xconn)
|
||||
} else {
|
||||
let atom = self.xconn.get_atom_unchecked(b"_NET_ACTIVE_WINDOW\0");
|
||||
|
||||
self.xconn.send_client_msg(
|
||||
self.xwindow,
|
||||
self.root,
|
||||
atom,
|
||||
Some(ffi::SubstructureRedirectMask | ffi::SubstructureNotifyMask),
|
||||
[1, ffi::CurrentTime as c_long, 0, 0, 0],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_minimized(&self, minimized: bool) {
|
||||
self.set_minimized_inner(minimized)
|
||||
.flush()
|
||||
.expect("Failed to change window minimization");
|
||||
}
|
||||
|
||||
fn set_maximized_inner(&self, maximized: bool) -> util::Flusher<'_> {
|
||||
let horz_atom = unsafe {
|
||||
self.xconn
|
||||
@@ -922,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()
|
||||
@@ -943,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<'_> {
|
||||
@@ -973,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);
|
||||
}
|
||||
|
||||
@@ -988,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()
|
||||
@@ -1018,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);
|
||||
}
|
||||
|
||||
@@ -1041,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);
|
||||
}
|
||||
|
||||
@@ -1054,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);
|
||||
}
|
||||
|
||||
@@ -1065,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) {
|
||||
@@ -1107,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);
|
||||
@@ -1246,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> {
|
||||
@@ -1260,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)
|
||||
}
|
||||
|
||||
@@ -1276,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -11,21 +11,32 @@ use std::{
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use cocoa::{appkit::NSApp, base::nil};
|
||||
use cocoa::{
|
||||
appkit::{NSApp, NSWindow},
|
||||
base::nil,
|
||||
foundation::{NSAutoreleasePool, NSSize, NSString},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
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;
|
||||
|
||||
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.
|
||||
@@ -34,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>>,
|
||||
}
|
||||
@@ -54,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 {
|
||||
@@ -83,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>,
|
||||
}
|
||||
@@ -92,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()
|
||||
}
|
||||
|
||||
@@ -100,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()
|
||||
}
|
||||
|
||||
@@ -136,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())
|
||||
}
|
||||
|
||||
@@ -152,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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,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 {}
|
||||
@@ -171,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
|
||||
@@ -179,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,
|
||||
@@ -189,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();
|
||||
}
|
||||
@@ -198,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);
|
||||
}
|
||||
|
||||
@@ -229,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);
|
||||
}
|
||||
|
||||
@@ -241,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() {
|
||||
@@ -265,18 +324,39 @@ impl AppState {
|
||||
for event in HANDLER.take_events() {
|
||||
HANDLER.handle_nonuser_event(event);
|
||||
}
|
||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::MainEventsCleared));
|
||||
for window_id in HANDLER.should_redraw() {
|
||||
HANDLER.handle_nonuser_event(Event::WindowEvent {
|
||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::RedrawRequested(
|
||||
window_id,
|
||||
event: WindowEvent::RedrawRequested,
|
||||
});
|
||||
)));
|
||||
}
|
||||
HANDLER.handle_nonuser_event(Event::EventsCleared);
|
||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::RedrawEventsCleared));
|
||||
HANDLER.set_in_callback(false);
|
||||
}
|
||||
if HANDLER.should_exit() {
|
||||
let _: () = unsafe { msg_send![NSApp(), terminate: nil] };
|
||||
return;
|
||||
unsafe {
|
||||
let _: () = msg_send![NSApp(), stop: nil];
|
||||
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
|
||||
let windows: *const Object = msg_send![NSApp(), windows];
|
||||
let window: *const Object = msg_send![windows, objectAtIndex:0];
|
||||
assert_ne!(window, nil);
|
||||
|
||||
let title: *const Object = msg_send![window, title];
|
||||
assert_ne!(title, nil);
|
||||
let postfix = NSString::alloc(nil).init_str("*");
|
||||
let some_unique_title: *const Object =
|
||||
msg_send![title, stringByAppendingString: postfix];
|
||||
assert_ne!(some_unique_title, nil);
|
||||
|
||||
// To stop event loop immediately, we need to send some UI event here.
|
||||
let _: () = msg_send![window, setTitle: some_unique_title];
|
||||
// And restore it.
|
||||
let _: () = msg_send![window, setTitle: title];
|
||||
|
||||
pool.drain();
|
||||
};
|
||||
}
|
||||
HANDLER.update_start_time();
|
||||
match HANDLER.get_old_and_new_control_flow() {
|
||||
|
||||
@@ -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.
|
||||
//
|
||||
@@ -224,12 +243,24 @@ pub fn check_function_keys(string: &str) -> Option<VirtualKeyCode> {
|
||||
|
||||
pub fn event_mods(event: id) -> ModifiersState {
|
||||
let flags = unsafe { NSEvent::modifierFlags(event) };
|
||||
ModifiersState {
|
||||
shift: flags.contains(NSEventModifierFlags::NSShiftKeyMask),
|
||||
ctrl: flags.contains(NSEventModifierFlags::NSControlKeyMask),
|
||||
alt: flags.contains(NSEventModifierFlags::NSAlternateKeyMask),
|
||||
logo: flags.contains(NSEventModifierFlags::NSCommandKeyMask),
|
||||
}
|
||||
let mut m = ModifiersState::empty();
|
||||
m.set(
|
||||
ModifiersState::SHIFT,
|
||||
flags.contains(NSEventModifierFlags::NSShiftKeyMask),
|
||||
);
|
||||
m.set(
|
||||
ModifiersState::CTRL,
|
||||
flags.contains(NSEventModifierFlags::NSControlKeyMask),
|
||||
);
|
||||
m.set(
|
||||
ModifiersState::ALT,
|
||||
flags.contains(NSEventModifierFlags::NSAlternateKeyMask),
|
||||
);
|
||||
m.set(
|
||||
ModifiersState::LOGO,
|
||||
flags.contains(NSEventModifierFlags::NSCommandKeyMask),
|
||||
);
|
||||
m
|
||||
}
|
||||
|
||||
pub fn get_scancode(event: cocoa::base::id) -> c_ushort {
|
||||
@@ -244,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)
|
||||
{
|
||||
|
||||
@@ -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,15 +92,16 @@ 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);
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
let app = NSApp();
|
||||
assert_ne!(app, nil);
|
||||
AppState::set_callback(callback, Rc::clone(&self.window_target));
|
||||
let _: () = msg_send![app, run];
|
||||
AppState::exit();
|
||||
pool.drain();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -60,19 +60,23 @@ pub unsafe fn set_style_mask_async(ns_window: id, ns_view: id, mask: NSWindowSty
|
||||
}
|
||||
pub unsafe fn set_style_mask_sync(ns_window: id, ns_view: id, mask: NSWindowStyleMask) {
|
||||
let context = SetStyleMaskData::new_ptr(ns_window, ns_view, mask);
|
||||
dispatch_sync_f(
|
||||
dispatch_get_main_queue(),
|
||||
context as *mut _,
|
||||
set_style_mask_callback,
|
||||
);
|
||||
if msg_send![class!(NSThread), isMainThread] {
|
||||
set_style_mask_callback(context as *mut _);
|
||||
} else {
|
||||
dispatch_sync_f(
|
||||
dispatch_get_main_queue(),
|
||||
context as *mut _,
|
||||
set_style_mask_callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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 }))
|
||||
}
|
||||
}
|
||||
@@ -94,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(),
|
||||
|
||||
@@ -3,7 +3,8 @@ use cocoa::{
|
||||
base::{id, nil},
|
||||
foundation::{NSDictionary, NSPoint, NSString},
|
||||
};
|
||||
use objc::runtime::Sel;
|
||||
use objc::{runtime::Sel, runtime::NO};
|
||||
use std::cell::RefCell;
|
||||
|
||||
use crate::window::CursorIcon;
|
||||
|
||||
@@ -126,3 +127,38 @@ pub unsafe fn load_webkit_cursor(cursor_name: &str) -> id {
|
||||
hotSpot:point
|
||||
]
|
||||
}
|
||||
|
||||
pub unsafe fn invisible_cursor() -> id {
|
||||
// 16x16 GIF data for invisible cursor
|
||||
// You can reproduce this via ImageMagick.
|
||||
// $ convert -size 16x16 xc:none cursor.gif
|
||||
static CURSOR_BYTES: &[u8] = &[
|
||||
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x10, 0x00, 0x10, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00,
|
||||
0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x84, 0x8F, 0xA9, 0xCB, 0xED, 0x0F,
|
||||
0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B,
|
||||
];
|
||||
|
||||
thread_local! {
|
||||
// We can't initialize this at startup.
|
||||
static CURSOR_OBJECT: RefCell<id> = RefCell::new(nil);
|
||||
}
|
||||
|
||||
CURSOR_OBJECT.with(|cursor_obj| {
|
||||
if *cursor_obj.borrow() == nil {
|
||||
// Create a cursor from `CURSOR_BYTES`
|
||||
let cursor_data: id = msg_send![class!(NSData),
|
||||
dataWithBytesNoCopy:CURSOR_BYTES as *const [u8]
|
||||
length:CURSOR_BYTES.len()
|
||||
freeWhenDone:NO
|
||||
];
|
||||
|
||||
let ns_image: id = msg_send![class!(NSImage), alloc];
|
||||
let _: id = msg_send![ns_image, initWithData: cursor_data];
|
||||
let cursor: id = msg_send![class!(NSCursor), alloc];
|
||||
*cursor_obj.borrow_mut() =
|
||||
msg_send![cursor, initWithImage:ns_image hotSpot: NSPoint::new(0.0, 0.0)];
|
||||
}
|
||||
*cursor_obj.borrow()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ pub const EMPTY_RANGE: ffi::NSRange = ffi::NSRange {
|
||||
length: 0,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct IdRef(id);
|
||||
|
||||
impl IdRef {
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::{
|
||||
use cocoa::{
|
||||
appkit::{NSApp, NSEvent, NSEventModifierFlags, NSEventPhase, NSView, NSWindow},
|
||||
base::{id, nil},
|
||||
foundation::{NSPoint, NSRect, NSSize, NSString, NSUInteger},
|
||||
foundation::{NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger},
|
||||
};
|
||||
use objc::{
|
||||
declare::ClassDecl,
|
||||
@@ -18,14 +18,14 @@ use objc::{
|
||||
|
||||
use crate::{
|
||||
event::{
|
||||
DeviceEvent, ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, TouchPhase,
|
||||
VirtualKeyCode, WindowEvent,
|
||||
DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, MouseButton,
|
||||
MouseScrollDelta, TouchPhase, VirtualKeyCode, WindowEvent,
|
||||
},
|
||||
platform_impl::platform::{
|
||||
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},
|
||||
@@ -35,33 +35,41 @@ use crate::{
|
||||
window::WindowId,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
struct Modifiers {
|
||||
shift_pressed: bool,
|
||||
ctrl_pressed: bool,
|
||||
win_pressed: bool,
|
||||
alt_pressed: bool,
|
||||
pub struct CursorState {
|
||||
pub visible: bool,
|
||||
pub cursor: util::Cursor,
|
||||
}
|
||||
|
||||
impl Default for CursorState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
visible: true,
|
||||
cursor: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ViewState {
|
||||
ns_window: id,
|
||||
pub cursor: Arc<Mutex<util::Cursor>>,
|
||||
pub cursor_state: Arc<Mutex<CursorState>>,
|
||||
ime_spot: Option<(f64, f64)>,
|
||||
raw_characters: Option<String>,
|
||||
is_key_down: bool,
|
||||
modifiers: Modifiers,
|
||||
modifiers: ModifiersState,
|
||||
tracking_rect: Option<NSInteger>,
|
||||
}
|
||||
|
||||
pub fn new_view(ns_window: id) -> (IdRef, Weak<Mutex<util::Cursor>>) {
|
||||
let cursor = Default::default();
|
||||
let cursor_access = Arc::downgrade(&cursor);
|
||||
pub fn new_view(ns_window: id) -> (IdRef, Weak<Mutex<CursorState>>) {
|
||||
let cursor_state = Default::default();
|
||||
let cursor_access = Arc::downgrade(&cursor_state);
|
||||
let state = ViewState {
|
||||
ns_window,
|
||||
cursor,
|
||||
cursor_state,
|
||||
ime_spot: None,
|
||||
raw_characters: None,
|
||||
is_key_down: false,
|
||||
modifiers: Default::default(),
|
||||
tracking_rect: None,
|
||||
};
|
||||
unsafe {
|
||||
// This is free'd in `dealloc`
|
||||
@@ -236,6 +244,10 @@ lazy_static! {
|
||||
sel!(cancelOperation:),
|
||||
cancel_operation as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(frameDidChange:),
|
||||
frame_did_change as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.add_ivar::<*mut c_void>("winitState");
|
||||
decl.add_ivar::<id>("markedText");
|
||||
let protocol = Protocol::get("NSTextInputClient").unwrap();
|
||||
@@ -261,6 +273,19 @@ extern "C" fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> i
|
||||
let marked_text =
|
||||
<id as NSMutableAttributedString>::init(NSMutableAttributedString::alloc(nil));
|
||||
(*this).set_ivar("markedText", marked_text);
|
||||
let _: () = msg_send![this, setPostsFrameChangedNotifications: YES];
|
||||
|
||||
let notification_center: &Object =
|
||||
msg_send![class!(NSNotificationCenter), defaultCenter];
|
||||
let notification_name =
|
||||
NSString::alloc(nil).init_str("NSViewFrameDidChangeNotification");
|
||||
let _: () = msg_send![
|
||||
notification_center,
|
||||
addObserver: this
|
||||
selector: sel!(frameDidChange:)
|
||||
name: notification_name
|
||||
object: this
|
||||
];
|
||||
}
|
||||
this
|
||||
}
|
||||
@@ -269,17 +294,46 @@ extern "C" fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> i
|
||||
extern "C" fn view_did_move_to_window(this: &Object, _sel: Sel) {
|
||||
trace!("Triggered `viewDidMoveToWindow`");
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
if let Some(tracking_rect) = state.tracking_rect.take() {
|
||||
let _: () = msg_send![this, removeTrackingRect: tracking_rect];
|
||||
}
|
||||
|
||||
let rect: NSRect = msg_send![this, visibleRect];
|
||||
let _: () = msg_send![this,
|
||||
let tracking_rect: NSInteger = msg_send![this,
|
||||
addTrackingRect:rect
|
||||
owner:this
|
||||
userData:nil
|
||||
assumeInside:NO
|
||||
];
|
||||
state.tracking_rect = Some(tracking_rect);
|
||||
}
|
||||
trace!("Completed `viewDidMoveToWindow`");
|
||||
}
|
||||
|
||||
extern "C" fn frame_did_change(this: &Object, _sel: Sel, _event: id) {
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
if let Some(tracking_rect) = state.tracking_rect.take() {
|
||||
let _: () = msg_send![this, removeTrackingRect: tracking_rect];
|
||||
}
|
||||
|
||||
let rect: NSRect = msg_send![this, visibleRect];
|
||||
let tracking_rect: NSInteger = msg_send![this,
|
||||
addTrackingRect:rect
|
||||
owner:this
|
||||
userData:nil
|
||||
assumeInside:NO
|
||||
];
|
||||
|
||||
state.tracking_rect = Some(tracking_rect);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn draw_rect(this: &Object, _sel: Sel, rect: NSRect) {
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||
@@ -309,7 +363,12 @@ extern "C" fn reset_cursor_rects(this: &Object, _sel: Sel) {
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
let bounds: NSRect = msg_send![this, bounds];
|
||||
let cursor = state.cursor.lock().unwrap().load();
|
||||
let cursor_state = state.cursor_state.lock().unwrap();
|
||||
let cursor = if cursor_state.visible {
|
||||
cursor_state.cursor.load()
|
||||
} else {
|
||||
util::invisible_cursor()
|
||||
};
|
||||
let _: () = msg_send![this,
|
||||
addCursorRect:bounds
|
||||
cursor:cursor
|
||||
@@ -453,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);
|
||||
@@ -477,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 {
|
||||
@@ -488,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),
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -587,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 {
|
||||
@@ -638,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`");
|
||||
}
|
||||
@@ -654,45 +713,50 @@ extern "C" fn flags_changed(this: &Object, _sel: Sel, event: id) {
|
||||
if let Some(window_event) = modifier_event(
|
||||
event,
|
||||
NSEventModifierFlags::NSShiftKeyMask,
|
||||
state.modifiers.shift_pressed,
|
||||
state.modifiers.shift(),
|
||||
) {
|
||||
state.modifiers.shift_pressed = !state.modifiers.shift_pressed;
|
||||
state.modifiers.toggle(ModifiersState::SHIFT);
|
||||
events.push_back(window_event);
|
||||
}
|
||||
|
||||
if let Some(window_event) = modifier_event(
|
||||
event,
|
||||
NSEventModifierFlags::NSControlKeyMask,
|
||||
state.modifiers.ctrl_pressed,
|
||||
state.modifiers.ctrl(),
|
||||
) {
|
||||
state.modifiers.ctrl_pressed = !state.modifiers.ctrl_pressed;
|
||||
state.modifiers.toggle(ModifiersState::CTRL);
|
||||
events.push_back(window_event);
|
||||
}
|
||||
|
||||
if let Some(window_event) = modifier_event(
|
||||
event,
|
||||
NSEventModifierFlags::NSCommandKeyMask,
|
||||
state.modifiers.win_pressed,
|
||||
state.modifiers.logo(),
|
||||
) {
|
||||
state.modifiers.win_pressed = !state.modifiers.win_pressed;
|
||||
state.modifiers.toggle(ModifiersState::LOGO);
|
||||
events.push_back(window_event);
|
||||
}
|
||||
|
||||
if let Some(window_event) = modifier_event(
|
||||
event,
|
||||
NSEventModifierFlags::NSAlternateKeyMask,
|
||||
state.modifiers.alt_pressed,
|
||||
state.modifiers.alt(),
|
||||
) {
|
||||
state.modifiers.alt_pressed = !state.modifiers.alt_pressed;
|
||||
state.modifiers.toggle(ModifiersState::ALT);
|
||||
events.push_back(window_event);
|
||||
}
|
||||
|
||||
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(EventWrapper::StaticEvent(Event::DeviceEvent {
|
||||
device_id: DEVICE_ID,
|
||||
event: DeviceEvent::ModifiersChanged(state.modifiers),
|
||||
}));
|
||||
}
|
||||
trace!("Completed `flagsChanged`");
|
||||
}
|
||||
@@ -747,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`");
|
||||
}
|
||||
@@ -767,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -828,7 +892,7 @@ fn mouse_motion(this: &Object, event: id) {
|
||||
},
|
||||
};
|
||||
|
||||
AppState::queue_event(window_event);
|
||||
AppState::queue_event(EventWrapper::StaticEvent(window_event));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -880,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`");
|
||||
}
|
||||
@@ -899,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`");
|
||||
}
|
||||
@@ -941,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`");
|
||||
}
|
||||
@@ -965,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`");
|
||||
}
|
||||
|
||||
@@ -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},
|
||||
@@ -20,6 +22,7 @@ use crate::{
|
||||
ffi,
|
||||
monitor::{self, MonitorHandle, VideoMode},
|
||||
util::{self, IdRef},
|
||||
view::CursorState,
|
||||
view::{self, new_view},
|
||||
window_delegate::new_delegate,
|
||||
OsError,
|
||||
@@ -65,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,
|
||||
}
|
||||
|
||||
@@ -90,8 +93,8 @@ fn create_app(activation_policy: ActivationPolicy) -> Option<id> {
|
||||
unsafe fn create_view(
|
||||
ns_window: id,
|
||||
pl_attribs: &PlatformSpecificWindowBuilderAttributes,
|
||||
) -> Option<(IdRef, Weak<Mutex<util::Cursor>>)> {
|
||||
let (ns_view, cursor) = new_view(ns_window);
|
||||
) -> Option<(IdRef, Weak<Mutex<CursorState>>)> {
|
||||
let (ns_view, cursor_state) = new_view(ns_window);
|
||||
ns_view.non_nil().map(|ns_view| {
|
||||
if !pl_attribs.disallow_hidpi {
|
||||
ns_view.setWantsBestResolutionOpenGLSurface_(YES);
|
||||
@@ -108,7 +111,7 @@ unsafe fn create_view(
|
||||
|
||||
ns_window.setContentView_(*ns_view);
|
||||
ns_window.makeFirstResponder_(*ns_view);
|
||||
(ns_view, cursor)
|
||||
(ns_view, cursor_state)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -129,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))
|
||||
}
|
||||
};
|
||||
@@ -243,6 +251,13 @@ lazy_static! {
|
||||
pub struct SharedState {
|
||||
pub resizable: bool,
|
||||
pub fullscreen: Option<Fullscreen>,
|
||||
// This is true between windowWillEnterFullScreen and windowDidEnterFullScreen
|
||||
// or windowWillExitFullScreen and windowDidExitFullScreen.
|
||||
// We must not toggle fullscreen when this is true.
|
||||
pub in_fullscreen_transition: bool,
|
||||
// If it is attempted to toggle fullscreen when in_fullscreen_transition is true,
|
||||
// Set target_fullscreen and do after fullscreen transition is end.
|
||||
pub target_fullscreen: Option<Option<Fullscreen>>,
|
||||
pub maximized: bool,
|
||||
pub standard_frame: Option<NSRect>,
|
||||
is_simple_fullscreen: bool,
|
||||
@@ -283,8 +298,8 @@ pub struct UnownedWindow {
|
||||
input_context: IdRef, // never changes
|
||||
pub shared_state: Arc<Mutex<SharedState>>,
|
||||
decorations: AtomicBool,
|
||||
cursor: Weak<Mutex<util::Cursor>>,
|
||||
cursor_visible: AtomicBool,
|
||||
cursor_state: Weak<Mutex<CursorState>>,
|
||||
pub inner_rect: Option<PhysicalSize<u32>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for UnownedWindow {}
|
||||
@@ -313,7 +328,7 @@ impl UnownedWindow {
|
||||
os_error!(OsError::CreationError("Couldn't create `NSWindow`"))
|
||||
})?;
|
||||
|
||||
let (ns_view, cursor) =
|
||||
let (ns_view, cursor_state) =
|
||||
unsafe { create_view(*ns_window, &pl_attribs) }.ok_or_else(|| {
|
||||
unsafe { pool.drain() };
|
||||
os_error!(OsError::CreationError("Couldn't create `NSView`"))
|
||||
@@ -321,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);
|
||||
@@ -328,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.
|
||||
@@ -354,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,
|
||||
@@ -361,8 +382,8 @@ impl UnownedWindow {
|
||||
input_context,
|
||||
shared_state: Arc::new(Mutex::new(win_attribs.into())),
|
||||
decorations: AtomicBool::new(decorations),
|
||||
cursor,
|
||||
cursor_visible: AtomicBool::new(true),
|
||||
cursor_state,
|
||||
inner_rect,
|
||||
});
|
||||
|
||||
let delegate = new_delegate(&window, fullscreen.is_some());
|
||||
@@ -419,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,
|
||||
@@ -455,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,8 +549,8 @@ impl UnownedWindow {
|
||||
|
||||
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
|
||||
let cursor = util::Cursor::from(cursor);
|
||||
if let Some(cursor_access) = self.cursor.upgrade() {
|
||||
*cursor_access.lock().unwrap() = cursor;
|
||||
if let Some(cursor_access) = self.cursor_state.upgrade() {
|
||||
cursor_access.lock().unwrap().cursor = cursor;
|
||||
}
|
||||
unsafe {
|
||||
let _: () = msg_send![*self.ns_window,
|
||||
@@ -528,33 +568,34 @@ impl UnownedWindow {
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_visible(&self, visible: bool) {
|
||||
let cursor_class = class!(NSCursor);
|
||||
// macOS uses a "hide counter" like Windows does, so we avoid incrementing it more than once.
|
||||
// (otherwise, `hide_cursor(false)` would need to be called n times!)
|
||||
if visible != self.cursor_visible.load(Ordering::Acquire) {
|
||||
if visible {
|
||||
let _: () = unsafe { msg_send![cursor_class, unhide] };
|
||||
} else {
|
||||
let _: () = unsafe { msg_send![cursor_class, hide] };
|
||||
if let Some(cursor_access) = self.cursor_state.upgrade() {
|
||||
let mut cursor_state = cursor_access.lock().unwrap();
|
||||
if visible != cursor_state.visible {
|
||||
cursor_state.visible = visible;
|
||||
drop(cursor_state);
|
||||
unsafe {
|
||||
let _: () = msg_send![*self.ns_window,
|
||||
invalidateCursorRectsForView:*self.ns_view
|
||||
];
|
||||
}
|
||||
}
|
||||
self.cursor_visible.store(visible, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
#[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))))?;
|
||||
@@ -617,6 +658,25 @@ impl UnownedWindow {
|
||||
self.set_maximized(maximized);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_minimized(&self, minimized: bool) {
|
||||
let is_minimized: BOOL = unsafe { msg_send![*self.ns_window, isMiniaturized] };
|
||||
let is_minimized: bool = is_minimized == YES;
|
||||
if is_minimized == minimized {
|
||||
return;
|
||||
}
|
||||
|
||||
if minimized {
|
||||
unsafe {
|
||||
NSWindow::miniaturize_(*self.ns_window, *self.ns_window);
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
NSWindow::deminiaturize_(*self.ns_window, *self.ns_window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
let is_zoomed = self.is_zoomed();
|
||||
@@ -642,11 +702,18 @@ impl UnownedWindow {
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
||||
trace!("Locked shared state in `set_fullscreen`");
|
||||
let shared_state_lock = self.shared_state.lock().unwrap();
|
||||
let mut shared_state_lock = self.shared_state.lock().unwrap();
|
||||
if shared_state_lock.is_simple_fullscreen {
|
||||
trace!("Unlocked shared state in `set_fullscreen`");
|
||||
return;
|
||||
}
|
||||
if shared_state_lock.in_fullscreen_transition {
|
||||
// We can't set fullscreen here.
|
||||
// Set fullscreen after transition.
|
||||
shared_state_lock.target_fullscreen = Some(fullscreen);
|
||||
trace!("Unlocked shared state in `set_fullscreen`");
|
||||
return;
|
||||
}
|
||||
let old_fullscreen = shared_state_lock.fullscreen.clone();
|
||||
if fullscreen == old_fullscreen {
|
||||
trace!("Unlocked shared state in `set_fullscreen`");
|
||||
@@ -861,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,
|
||||
@@ -1026,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
|
||||
@@ -1050,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
|
||||
|
||||
@@ -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),
|
||||
@@ -195,6 +216,10 @@ lazy_static! {
|
||||
sel!(windowDidExitFullScreen:),
|
||||
window_did_exit_fullscreen as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowWillExitFullScreen:),
|
||||
window_will_exit_fullscreen as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidFailToEnterFullScreen:),
|
||||
window_did_fail_to_enter_fullscreen as extern "C" fn(&Object, Sel, id),
|
||||
@@ -273,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:`");
|
||||
}
|
||||
@@ -419,13 +425,27 @@ extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
||||
shared_state.fullscreen = Some(Fullscreen::Borderless(window.current_monitor()))
|
||||
}
|
||||
}
|
||||
|
||||
shared_state.in_fullscreen_transition = true;
|
||||
trace!("Unlocked shared state in `window_will_enter_fullscreen`");
|
||||
})
|
||||
});
|
||||
trace!("Completed `windowWillEnterFullscreen:`");
|
||||
}
|
||||
|
||||
/// Invoked when before exit fullscreen
|
||||
extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) {
|
||||
trace!("Triggered `windowWillExitFullScreen:`");
|
||||
with_state(this, |state| {
|
||||
state.with_window(|window| {
|
||||
trace!("Locked shared state in `window_will_exit_fullscreen`");
|
||||
let mut shared_state = window.shared_state.lock().unwrap();
|
||||
shared_state.in_fullscreen_transition = true;
|
||||
trace!("Unlocked shared state in `window_will_exit_fullscreen`");
|
||||
});
|
||||
});
|
||||
trace!("Completed `windowWillExitFullScreen:`");
|
||||
}
|
||||
|
||||
extern "C" fn window_will_use_fullscreen_presentation_options(
|
||||
_this: &Object,
|
||||
_: Sel,
|
||||
@@ -451,6 +471,17 @@ extern "C" fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
||||
trace!("Triggered `windowDidEnterFullscreen:`");
|
||||
with_state(this, |state| {
|
||||
state.initial_fullscreen = false;
|
||||
state.with_window(|window| {
|
||||
trace!("Locked shared state in `window_did_enter_fullscreen`");
|
||||
let mut shared_state = window.shared_state.lock().unwrap();
|
||||
shared_state.in_fullscreen_transition = false;
|
||||
let target_fullscreen = shared_state.target_fullscreen.take();
|
||||
trace!("Unlocked shared state in `window_did_enter_fullscreen`");
|
||||
drop(shared_state);
|
||||
if let Some(target_fullscreen) = target_fullscreen {
|
||||
window.set_fullscreen(target_fullscreen);
|
||||
}
|
||||
});
|
||||
});
|
||||
trace!("Completed `windowDidEnterFullscreen:`");
|
||||
}
|
||||
@@ -461,6 +492,15 @@ extern "C" fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) {
|
||||
with_state(this, |state| {
|
||||
state.with_window(|window| {
|
||||
window.restore_state_from_fullscreen();
|
||||
trace!("Locked shared state in `window_did_exit_fullscreen`");
|
||||
let mut shared_state = window.shared_state.lock().unwrap();
|
||||
shared_state.in_fullscreen_transition = false;
|
||||
let target_fullscreen = shared_state.target_fullscreen.take();
|
||||
trace!("Unlocked shared state in `window_did_exit_fullscreen`");
|
||||
drop(shared_state);
|
||||
if let Some(target_fullscreen) = target_fullscreen {
|
||||
window.set_fullscreen(target_fullscreen);
|
||||
}
|
||||
})
|
||||
});
|
||||
trace!("Completed `windowDidExitFullscreen:`");
|
||||
@@ -485,6 +525,13 @@ extern "C" fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
||||
trace!("Triggered `windowDidFailToEnterFullscreen:`");
|
||||
with_state(this, |state| {
|
||||
state.with_window(|window| {
|
||||
trace!("Locked shared state in `window_did_fail_to_enter_fullscreen`");
|
||||
let mut shared_state = window.shared_state.lock().unwrap();
|
||||
shared_state.in_fullscreen_transition = false;
|
||||
shared_state.target_fullscreen = None;
|
||||
trace!("Unlocked shared state in `window_did_fail_to_enter_fullscreen`");
|
||||
});
|
||||
if state.initial_fullscreen {
|
||||
let _: () = unsafe {
|
||||
msg_send![*state.ns_window,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::{backend, state::State};
|
||||
use crate::event::{Event, StartCause, WindowEvent};
|
||||
use crate::event::{Event, StartCause};
|
||||
use crate::event_loop as root;
|
||||
use crate::window::WindowId;
|
||||
|
||||
@@ -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;
|
||||
@@ -127,18 +130,15 @@ impl<T: 'static> Shared<T> {
|
||||
if !event_is_start {
|
||||
self.handle_event(event, &mut control);
|
||||
}
|
||||
self.handle_event(Event::MainEventsCleared, &mut control);
|
||||
|
||||
// Collect all of the redraw events to avoid double-locking the RefCell
|
||||
let redraw_events: Vec<WindowId> = self.0.redraw_pending.borrow_mut().drain().collect();
|
||||
for window_id in redraw_events {
|
||||
self.handle_event(
|
||||
Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::RedrawRequested,
|
||||
},
|
||||
&mut control,
|
||||
);
|
||||
self.handle_event(Event::RedrawRequested(window_id), &mut control);
|
||||
}
|
||||
self.handle_event(Event::EventsCleared, &mut control);
|
||||
self.handle_event(Event::RedrawEventsCleared, &mut control);
|
||||
|
||||
self.apply_control_flow(control);
|
||||
// If the event loop is closed, it has been closed this iteration and now the closing
|
||||
// event should be emitted
|
||||
@@ -156,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() {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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!();
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}));
|
||||
|
||||
@@ -15,15 +15,15 @@ pub fn mouse_button(event: &impl IMouseEvent) -> MouseButton {
|
||||
}
|
||||
|
||||
pub fn mouse_modifiers(event: &impl IMouseEvent) -> ModifiersState {
|
||||
ModifiersState {
|
||||
shift: event.shift_key(),
|
||||
ctrl: event.ctrl_key(),
|
||||
alt: event.alt_key(),
|
||||
logo: event.meta_key(),
|
||||
}
|
||||
let mut m = ModifiersState::empty();
|
||||
m.set(ModifiersState::SHIFT, event.shift_key());
|
||||
m.set(ModifiersState::CTRL, event.ctrl_key());
|
||||
m.set(ModifiersState::ALT, event.alt_key());
|
||||
m.set(ModifiersState::LOGO, event.meta_key());
|
||||
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,
|
||||
@@ -213,12 +213,12 @@ pub fn virtual_key_code(event: &impl IKeyboardEvent) -> Option<VirtualKeyCode> {
|
||||
}
|
||||
|
||||
pub fn keyboard_modifiers(event: &impl IKeyboardEvent) -> ModifiersState {
|
||||
ModifiersState {
|
||||
shift: event.shift_key(),
|
||||
ctrl: event.ctrl_key(),
|
||||
alt: event.alt_key(),
|
||||
logo: event.meta_key(),
|
||||
}
|
||||
let mut m = ModifiersState::empty();
|
||||
m.set(ModifiersState::SHIFT, event.shift_key());
|
||||
m.set(ModifiersState::CTRL, event.ctrl_key());
|
||||
m.set(ModifiersState::ALT, event.alt_key());
|
||||
m.set(ModifiersState::LOGO, event.meta_key());
|
||||
m
|
||||
}
|
||||
|
||||
pub fn codepoint(event: &impl IKeyboardEvent) -> char {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -3,7 +3,7 @@ use stdweb::web::{window, IWindowOrWorker, TimeoutHandle};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Timeout {
|
||||
handle: TimeoutHandle,
|
||||
handle: Option<TimeoutHandle>,
|
||||
}
|
||||
|
||||
impl Timeout {
|
||||
@@ -12,14 +12,14 @@ impl Timeout {
|
||||
F: 'static + FnMut(),
|
||||
{
|
||||
Timeout {
|
||||
handle: window().set_clearable_timeout(f, duration.as_millis() as u32),
|
||||
handle: Some(window().set_clearable_timeout(f, duration.as_millis() as u32)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Timeout {
|
||||
fn drop(&mut self) {
|
||||
let handle = std::mem::replace(&mut self.handle, unsafe { std::mem::uninitialized() });
|
||||
let handle = self.handle.take().unwrap();
|
||||
handle.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}));
|
||||
|
||||
@@ -14,15 +14,15 @@ pub fn mouse_button(event: &MouseEvent) -> MouseButton {
|
||||
}
|
||||
|
||||
pub fn mouse_modifiers(event: &MouseEvent) -> ModifiersState {
|
||||
ModifiersState {
|
||||
shift: event.shift_key(),
|
||||
ctrl: event.ctrl_key(),
|
||||
alt: event.alt_key(),
|
||||
logo: event.meta_key(),
|
||||
}
|
||||
let mut m = ModifiersState::empty();
|
||||
m.set(ModifiersState::SHIFT, event.shift_key());
|
||||
m.set(ModifiersState::CTRL, event.ctrl_key());
|
||||
m.set(ModifiersState::ALT, event.alt_key());
|
||||
m.set(ModifiersState::LOGO, event.meta_key());
|
||||
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,
|
||||
@@ -211,12 +211,12 @@ pub fn virtual_key_code(event: &KeyboardEvent) -> Option<VirtualKeyCode> {
|
||||
}
|
||||
|
||||
pub fn keyboard_modifiers(event: &KeyboardEvent) -> ModifiersState {
|
||||
ModifiersState {
|
||||
shift: event.shift_key(),
|
||||
ctrl: event.ctrl_key(),
|
||||
alt: event.alt_key(),
|
||||
logo: event.meta_key(),
|
||||
}
|
||||
let mut m = ModifiersState::empty();
|
||||
m.set(ModifiersState::SHIFT, event.shift_key());
|
||||
m.set(ModifiersState::CTRL, event.ctrl_key());
|
||||
m.set(ModifiersState::ALT, event.alt_key());
|
||||
m.set(ModifiersState::LOGO, event.meta_key());
|
||||
m
|
||||
}
|
||||
|
||||
pub fn codepoint(event: &KeyboardEvent) -> char {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -199,6 +191,11 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_minimized(&self, _minimized: bool) {
|
||||
// Intentionally a no-op, as canvases cannot be 'minimized'
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, _maximized: bool) {
|
||||
// Intentionally a no-op, as canvases cannot be 'maximized'
|
||||
@@ -238,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
|
||||
}
|
||||
|
||||
|
||||
216
src/platform_impl/windows/dark_mode.rs
Normal file
216
src/platform_impl/windows/dark_mode.rs
Normal file
@@ -0,0 +1,216 @@
|
||||
/// This is a simple implementation of support for Windows Dark Mode,
|
||||
/// which is inspired by the solution in https://github.com/ysc3839/win32-darkmode
|
||||
use std::ffi::OsStr;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
|
||||
use winapi::{
|
||||
shared::{
|
||||
basetsd::SIZE_T,
|
||||
minwindef::{BOOL, DWORD, UINT, ULONG, WORD},
|
||||
ntdef::{LPSTR, NTSTATUS, NT_SUCCESS, PVOID, WCHAR},
|
||||
windef::HWND,
|
||||
},
|
||||
um::{libloaderapi, uxtheme, winuser},
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
static ref WIN10_BUILD_VERSION: Option<DWORD> = {
|
||||
// FIXME: RtlGetVersion is a documented windows API,
|
||||
// should be part of winapi!
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[repr(C)]
|
||||
struct OSVERSIONINFOW {
|
||||
dwOSVersionInfoSize: ULONG,
|
||||
dwMajorVersion: ULONG,
|
||||
dwMinorVersion: ULONG,
|
||||
dwBuildNumber: ULONG,
|
||||
dwPlatformId: ULONG,
|
||||
szCSDVersion: [WCHAR; 128],
|
||||
}
|
||||
|
||||
type RtlGetVersion = unsafe extern "system" fn (*mut OSVERSIONINFOW) -> NTSTATUS;
|
||||
let handle = get_function!("ntdll.dll", RtlGetVersion);
|
||||
|
||||
if let Some(rtl_get_version) = handle {
|
||||
unsafe {
|
||||
let mut vi = OSVERSIONINFOW {
|
||||
dwOSVersionInfoSize: 0,
|
||||
dwMajorVersion: 0,
|
||||
dwMinorVersion: 0,
|
||||
dwBuildNumber: 0,
|
||||
dwPlatformId: 0,
|
||||
szCSDVersion: [0; 128],
|
||||
};
|
||||
|
||||
let status = (rtl_get_version)(&mut vi as _);
|
||||
assert!(NT_SUCCESS(status));
|
||||
|
||||
if vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0 {
|
||||
Some(vi.dwBuildNumber)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
static ref DARK_MODE_SUPPORTED: bool = {
|
||||
// We won't try to do anything for windows versions < 17763
|
||||
// (Windows 10 October 2018 update)
|
||||
match *WIN10_BUILD_VERSION {
|
||||
Some(v) => v >= 17763,
|
||||
None => false
|
||||
}
|
||||
};
|
||||
|
||||
static ref DARK_THEME_NAME: Vec<u16> = widestring("DarkMode_Explorer");
|
||||
static ref LIGHT_THEME_NAME: Vec<u16> = widestring("");
|
||||
}
|
||||
|
||||
/// Attempt to set dark mode on a window, if necessary.
|
||||
/// Returns true if dark mode was set, false if not.
|
||||
pub fn try_dark_mode(hwnd: HWND) -> bool {
|
||||
if *DARK_MODE_SUPPORTED {
|
||||
let is_dark_mode = should_use_dark_mode();
|
||||
|
||||
let theme_name = if is_dark_mode {
|
||||
DARK_THEME_NAME.as_ptr()
|
||||
} else {
|
||||
LIGHT_THEME_NAME.as_ptr()
|
||||
};
|
||||
|
||||
unsafe {
|
||||
assert_eq!(
|
||||
0,
|
||||
uxtheme::SetWindowTheme(hwnd, theme_name as _, std::ptr::null())
|
||||
);
|
||||
|
||||
set_dark_mode_for_window(hwnd, is_dark_mode)
|
||||
}
|
||||
|
||||
is_dark_mode
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) {
|
||||
// Uses Windows undocumented API SetWindowCompositionAttribute,
|
||||
// as seen in win32-darkmode example linked at top of file.
|
||||
|
||||
type SetWindowCompositionAttribute =
|
||||
unsafe extern "system" fn(HWND, *mut WINDOWCOMPOSITIONATTRIBDATA) -> BOOL;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
type WINDOWCOMPOSITIONATTRIB = u32;
|
||||
const WCA_USEDARKMODECOLORS: WINDOWCOMPOSITIONATTRIB = 26;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[repr(C)]
|
||||
struct WINDOWCOMPOSITIONATTRIBDATA {
|
||||
Attrib: WINDOWCOMPOSITIONATTRIB,
|
||||
pvData: PVOID,
|
||||
cbData: SIZE_T,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref SET_WINDOW_COMPOSITION_ATTRIBUTE: Option<SetWindowCompositionAttribute> =
|
||||
get_function!("user32.dll", SetWindowCompositionAttribute);
|
||||
}
|
||||
|
||||
if let Some(set_window_composition_attribute) = *SET_WINDOW_COMPOSITION_ATTRIBUTE {
|
||||
unsafe {
|
||||
// SetWindowCompositionAttribute needs a bigbool (i32), not bool.
|
||||
let mut is_dark_mode_bigbool = is_dark_mode as BOOL;
|
||||
|
||||
let mut data = WINDOWCOMPOSITIONATTRIBDATA {
|
||||
Attrib: WCA_USEDARKMODECOLORS,
|
||||
pvData: &mut is_dark_mode_bigbool as *mut _ as _,
|
||||
cbData: std::mem::size_of_val(&is_dark_mode_bigbool) as _,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
1,
|
||||
set_window_composition_attribute(hwnd, &mut data as *mut _)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn should_use_dark_mode() -> bool {
|
||||
should_apps_use_dark_mode() && !is_high_contrast()
|
||||
}
|
||||
|
||||
fn should_apps_use_dark_mode() -> bool {
|
||||
type ShouldAppsUseDarkMode = unsafe extern "system" fn() -> bool;
|
||||
lazy_static! {
|
||||
static ref SHOULD_APPS_USE_DARK_MODE: Option<ShouldAppsUseDarkMode> = {
|
||||
unsafe {
|
||||
const UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL: WORD = 132;
|
||||
|
||||
let module = libloaderapi::LoadLibraryA("uxtheme.dll\0".as_ptr() as _);
|
||||
|
||||
if module.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let handle = libloaderapi::GetProcAddress(
|
||||
module,
|
||||
winuser::MAKEINTRESOURCEA(UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL),
|
||||
);
|
||||
|
||||
if handle.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(std::mem::transmute(handle))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
SHOULD_APPS_USE_DARK_MODE
|
||||
.map(|should_apps_use_dark_mode| unsafe { (should_apps_use_dark_mode)() })
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
// FIXME: This definition was missing from winapi. Can remove from
|
||||
// here and use winapi once the following PR is released:
|
||||
// https://github.com/retep998/winapi-rs/pull/815
|
||||
#[repr(C)]
|
||||
#[allow(non_snake_case)]
|
||||
struct HIGHCONTRASTA {
|
||||
cbSize: UINT,
|
||||
dwFlags: DWORD,
|
||||
lpszDefaultScheme: LPSTR,
|
||||
}
|
||||
|
||||
const HCF_HIGHCONTRASTON: DWORD = 1;
|
||||
|
||||
fn is_high_contrast() -> bool {
|
||||
let mut hc = HIGHCONTRASTA {
|
||||
cbSize: 0,
|
||||
dwFlags: 0,
|
||||
lpszDefaultScheme: std::ptr::null_mut(),
|
||||
};
|
||||
|
||||
let ok = unsafe {
|
||||
winuser::SystemParametersInfoA(
|
||||
winuser::SPI_GETHIGHCONTRAST,
|
||||
std::mem::size_of_val(&hc) as _,
|
||||
&mut hc as *mut _ as _,
|
||||
0,
|
||||
)
|
||||
};
|
||||
|
||||
(ok > 0) && ((HCF_HIGHCONTRASTON & hc.dwFlags) == 1)
|
||||
}
|
||||
|
||||
fn widestring(src: &'static str) -> Vec<u16> {
|
||||
OsStr::new(src)
|
||||
.encode_wide()
|
||||
.chain(Some(0).into_iter())
|
||||
.collect()
|
||||
}
|
||||
@@ -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) })
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,16 +17,84 @@ fn key_pressed(vkey: c_int) -> bool {
|
||||
}
|
||||
|
||||
pub fn get_key_mods() -> ModifiersState {
|
||||
let mut mods = ModifiersState::default();
|
||||
let filter_out_altgr = layout_uses_altgr() && key_pressed(winuser::VK_RMENU);
|
||||
|
||||
mods.shift = key_pressed(winuser::VK_SHIFT);
|
||||
mods.ctrl = key_pressed(winuser::VK_CONTROL) && !filter_out_altgr;
|
||||
mods.alt = key_pressed(winuser::VK_MENU) && !filter_out_altgr;
|
||||
mods.logo = key_pressed(winuser::VK_LWIN) || key_pressed(winuser::VK_RWIN);
|
||||
let mut mods = ModifiersState::empty();
|
||||
mods.set(ModifiersState::SHIFT, key_pressed(winuser::VK_SHIFT));
|
||||
mods.set(
|
||||
ModifiersState::CTRL,
|
||||
key_pressed(winuser::VK_CONTROL) && !filter_out_altgr,
|
||||
);
|
||||
mods.set(
|
||||
ModifiersState::ALT,
|
||||
key_pressed(winuser::VK_MENU) && !filter_out_altgr,
|
||||
);
|
||||
mods.set(
|
||||
ModifiersState::LOGO,
|
||||
key_pressed(winuser::VK_LWIN) || key_pressed(winuser::VK_RWIN),
|
||||
);
|
||||
mods
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Default)]
|
||||
pub struct ModifiersStateSide: u32 {
|
||||
const LSHIFT = 0b010 << 0;
|
||||
const RSHIFT = 0b001 << 0;
|
||||
|
||||
const LCTRL = 0b010 << 3;
|
||||
const RCTRL = 0b001 << 3;
|
||||
|
||||
const LALT = 0b010 << 6;
|
||||
const RALT = 0b001 << 6;
|
||||
|
||||
const LLOGO = 0b010 << 9;
|
||||
const RLOGO = 0b001 << 9;
|
||||
}
|
||||
}
|
||||
|
||||
impl ModifiersStateSide {
|
||||
pub fn filter_out_altgr(&self) -> ModifiersStateSide {
|
||||
match layout_uses_altgr() && self.contains(Self::RALT) {
|
||||
false => *self,
|
||||
true => *self & !(Self::LCTRL | Self::RCTRL | Self::LALT | Self::RALT),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ModifiersStateSide> for ModifiersState {
|
||||
fn from(side: ModifiersStateSide) -> Self {
|
||||
let mut state = ModifiersState::default();
|
||||
state.set(
|
||||
Self::SHIFT,
|
||||
side.intersects(ModifiersStateSide::LSHIFT | ModifiersStateSide::RSHIFT),
|
||||
);
|
||||
state.set(
|
||||
Self::CTRL,
|
||||
side.intersects(ModifiersStateSide::LCTRL | ModifiersStateSide::RCTRL),
|
||||
);
|
||||
state.set(
|
||||
Self::ALT,
|
||||
side.intersects(ModifiersStateSide::LALT | ModifiersStateSide::RALT),
|
||||
);
|
||||
state.set(
|
||||
Self::LOGO,
|
||||
side.intersects(ModifiersStateSide::LLOGO | ModifiersStateSide::RLOGO),
|
||||
);
|
||||
state
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_pressed_keys() -> impl Iterator<Item = c_int> {
|
||||
let mut keyboard_state = vec![0u8; 256];
|
||||
unsafe { winuser::GetKeyboardState(keyboard_state.as_mut_ptr()) };
|
||||
keyboard_state
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.filter(|(_, p)| (*p & (1 << 7)) != 0) // whether or not a key is pressed is communicated via the high-order bit
|
||||
.map(|(i, _)| i as c_int)
|
||||
}
|
||||
|
||||
unsafe fn get_char(keyboard_state: &[u8; 256], v_key: u32, hkl: HKL) -> Option<char> {
|
||||
let mut unicode_bytes = [0u16; 5];
|
||||
let len = winuser::ToUnicodeEx(
|
||||
@@ -177,8 +245,8 @@ pub fn vkey_to_winit_vkey(vkey: c_int) -> Option<VirtualKeyCode> {
|
||||
0x58 => Some(VirtualKeyCode::X),
|
||||
0x59 => Some(VirtualKeyCode::Y),
|
||||
0x5A => Some(VirtualKeyCode::Z),
|
||||
//winuser::VK_LWIN => Some(VirtualKeyCode::Lwin),
|
||||
//winuser::VK_RWIN => Some(VirtualKeyCode::Rwin),
|
||||
winuser::VK_LWIN => Some(VirtualKeyCode::LWin),
|
||||
winuser::VK_RWIN => Some(VirtualKeyCode::RWin),
|
||||
winuser::VK_APPS => Some(VirtualKeyCode::Apps),
|
||||
winuser::VK_SLEEP => Some(VirtualKeyCode::Sleep),
|
||||
winuser::VK_NUMPAD0 => Some(VirtualKeyCode::Numpad0),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
420
src/platform_impl/windows/event_loop/runner.rs
Normal file
420
src/platform_impl/windows/event_loop/runner.rs
Normal file
@@ -0,0 +1,420 @@
|
||||
use std::{any::Any, cell::RefCell, collections::VecDeque, mem, panic, ptr, rc::Rc, time::Instant};
|
||||
|
||||
use winapi::{shared::windef::HWND, um::winuser};
|
||||
|
||||
use crate::{
|
||||
event::{Event, StartCause},
|
||||
event_loop::ControlFlow,
|
||||
platform_impl::platform::event_loop::EventLoop,
|
||||
window::WindowId,
|
||||
};
|
||||
|
||||
pub(crate) type EventLoopRunnerShared<T> = Rc<ELRShared<T>>;
|
||||
pub(crate) struct ELRShared<T: 'static> {
|
||||
runner: RefCell<Option<EventLoopRunner<T>>>,
|
||||
buffer: RefCell<VecDeque<Event<'static, T>>>,
|
||||
redraw_buffer: Rc<RefCell<VecDeque<WindowId>>>,
|
||||
}
|
||||
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)>,
|
||||
panic_error: Option<PanicError>,
|
||||
redraw_buffer: Rc<RefCell<VecDeque<WindowId>>>,
|
||||
}
|
||||
pub type PanicError = Box<dyn Any + Send + 'static>;
|
||||
|
||||
impl<T> ELRShared<T> {
|
||||
pub(crate) fn new() -> ELRShared<T> {
|
||||
ELRShared {
|
||||
runner: RefCell::new(None),
|
||||
buffer: RefCell::new(VecDeque::new()),
|
||||
redraw_buffer: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn set_runner<F>(&self, event_loop: &EventLoop<T>, f: F)
|
||||
where
|
||||
F: FnMut(Event<'_, T>, &mut ControlFlow),
|
||||
{
|
||||
let mut runner = EventLoopRunner::new(event_loop, self.redraw_buffer.clone(), f);
|
||||
{
|
||||
let mut runner_ref = self.runner.borrow_mut();
|
||||
loop {
|
||||
let event = self.buffer.borrow_mut().pop_front();
|
||||
match event {
|
||||
Some(e) => {
|
||||
runner.process_event(e);
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
*runner_ref = Some(runner);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn destroy_runner(&self) {
|
||||
*self.runner.borrow_mut() = None;
|
||||
}
|
||||
|
||||
pub(crate) fn new_events(&self) {
|
||||
let mut runner_ref = self.runner.borrow_mut();
|
||||
if let Some(ref mut runner) = *runner_ref {
|
||||
runner.new_events();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// Dispatch any events that were buffered during the call to `process_event`.
|
||||
loop {
|
||||
// We do this instead of using a `while let` loop because if we use a `while let`
|
||||
// loop the reference returned `borrow_mut()` doesn't get dropped until the end
|
||||
// of the loop's body and attempts to add events to the event buffer while in
|
||||
// `process_event` will fail.
|
||||
let buffered_event_opt = self.buffer.borrow_mut().pop_front();
|
||||
match buffered_event_opt {
|
||||
Some(event) => runner.process_event(event),
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Err(event)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn call_event_handler(&self, event: Event<'static, T>) {
|
||||
if let Ok(mut runner_ref) = self.runner.try_borrow_mut() {
|
||||
if let Some(ref mut runner) = *runner_ref {
|
||||
runner.call_event_handler(event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn events_cleared(&self) {
|
||||
let mut runner_ref = self.runner.borrow_mut();
|
||||
if let Some(ref mut runner) = *runner_ref {
|
||||
runner.events_cleared();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn take_panic_error(&self) -> Result<(), PanicError> {
|
||||
let mut runner_ref = self.runner.borrow_mut();
|
||||
if let Some(ref mut runner) = *runner_ref {
|
||||
runner.take_panic_error()
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_modal_loop(&self, in_modal_loop: bool) {
|
||||
let mut runner_ref = self.runner.borrow_mut();
|
||||
if let Some(ref mut runner) = *runner_ref {
|
||||
runner.in_modal_loop = in_modal_loop;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn in_modal_loop(&self) -> bool {
|
||||
let runner = self.runner.borrow();
|
||||
if let Some(ref runner) = *runner {
|
||||
runner.in_modal_loop
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn control_flow(&self) -> ControlFlow {
|
||||
let runner_ref = self.runner.borrow();
|
||||
if let Some(ref runner) = *runner_ref {
|
||||
runner.control_flow
|
||||
} else {
|
||||
ControlFlow::Exit
|
||||
}
|
||||
}
|
||||
|
||||
fn buffer_event(&self, event: Event<'static, T>) {
|
||||
match event {
|
||||
Event::RedrawRequested(window_id) => {
|
||||
self.redraw_buffer.borrow_mut().push_back(window_id)
|
||||
}
|
||||
_ => self.buffer.borrow_mut().push_back(event),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum RunnerState {
|
||||
/// The event loop has just been created, and an `Init` event must be sent.
|
||||
New,
|
||||
/// The event loop is idling, and began idling at the given instant.
|
||||
Idle(Instant),
|
||||
/// The event loop has received a signal from the OS that the loop may resume, but no winit
|
||||
/// events have been generated yet. We're waiting for an event to be processed or the events
|
||||
/// to be marked as cleared to send `NewEvents`, depending on the current `ControlFlow`.
|
||||
DeferredNewEvents(Instant),
|
||||
/// The event loop is handling the OS's events and sending them to the user's callback.
|
||||
/// `NewEvents` has been sent, and `EventsCleared` hasn't.
|
||||
HandlingEvents,
|
||||
HandlingRedraw,
|
||||
}
|
||||
|
||||
impl<T> EventLoopRunner<T> {
|
||||
unsafe fn new<F>(
|
||||
event_loop: &EventLoop<T>,
|
||||
redraw_buffer: Rc<RefCell<VecDeque<WindowId>>>,
|
||||
f: F,
|
||||
) -> EventLoopRunner<T>
|
||||
where
|
||||
F: FnMut(Event<'_, T>, &mut ControlFlow),
|
||||
{
|
||||
EventLoopRunner {
|
||||
control_flow: ControlFlow::default(),
|
||||
runner_state: RunnerState::New,
|
||||
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::new(f)),
|
||||
panic_error: None,
|
||||
redraw_buffer,
|
||||
}
|
||||
}
|
||||
|
||||
fn take_panic_error(&mut self) -> Result<(), PanicError> {
|
||||
match self.panic_error.take() {
|
||||
Some(err) => Err(err),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_events(&mut self) {
|
||||
self.runner_state = match self.runner_state {
|
||||
// If we're already handling events or have deferred `NewEvents`, we don't need to do
|
||||
// do any processing.
|
||||
RunnerState::HandlingEvents
|
||||
| RunnerState::HandlingRedraw
|
||||
| RunnerState::DeferredNewEvents(..) => self.runner_state,
|
||||
|
||||
// Send the `Init` `NewEvents` and immediately move into event processing.
|
||||
RunnerState::New => {
|
||||
self.call_event_handler(Event::NewEvents(StartCause::Init));
|
||||
RunnerState::HandlingEvents
|
||||
}
|
||||
|
||||
// When `NewEvents` gets sent after an idle depends on the control flow...
|
||||
RunnerState::Idle(wait_start) => {
|
||||
match self.control_flow {
|
||||
// If we're polling, send `NewEvents` and immediately move into event processing.
|
||||
ControlFlow::Poll => {
|
||||
self.call_event_handler(Event::NewEvents(StartCause::Poll));
|
||||
RunnerState::HandlingEvents
|
||||
},
|
||||
// If the user was waiting until a specific time, the `NewEvents` call gets sent
|
||||
// at varying times depending on the current time.
|
||||
ControlFlow::WaitUntil(resume_time) => {
|
||||
match Instant::now() >= resume_time {
|
||||
// If the current time is later than the requested resume time, we can tell the
|
||||
// user that the resume time has been reached with `NewEvents` and immdiately move
|
||||
// into event processing.
|
||||
true => {
|
||||
self.call_event_handler(Event::NewEvents(StartCause::ResumeTimeReached {
|
||||
start: wait_start,
|
||||
requested_resume: resume_time,
|
||||
}));
|
||||
RunnerState::HandlingEvents
|
||||
},
|
||||
// However, if the current time is EARLIER than the requested resume time, we
|
||||
// don't want to send the `WaitCancelled` event until we know an event is being
|
||||
// sent. Defer.
|
||||
false => RunnerState::DeferredNewEvents(wait_start)
|
||||
}
|
||||
},
|
||||
// If we're waiting, `NewEvents` doesn't get sent until winit gets an event, so
|
||||
// we defer.
|
||||
ControlFlow::Wait |
|
||||
// `Exit` shouldn't really ever get sent here, but if it does do something somewhat sane.
|
||||
ControlFlow::Exit => RunnerState::DeferredNewEvents(wait_start),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
// been processed. So, we send WM_PAINT to a dummy window which calls `events_cleared` when
|
||||
// the events queue has been emptied.
|
||||
if self.in_modal_loop {
|
||||
unsafe {
|
||||
winuser::RedrawWindow(
|
||||
self.modal_redraw_window,
|
||||
ptr::null(),
|
||||
ptr::null_mut(),
|
||||
winuser::RDW_INTERNALPAINT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// If new event processing has to be done (i.e. call NewEvents or defer), do it. If we're
|
||||
// already in processing nothing happens with this call.
|
||||
self.new_events();
|
||||
|
||||
// Now that an event has been received, we have to send any `NewEvents` calls that were
|
||||
// deferred.
|
||||
if let RunnerState::DeferredNewEvents(wait_start) = self.runner_state {
|
||||
match self.control_flow {
|
||||
ControlFlow::Exit | ControlFlow::Wait => {
|
||||
self.call_event_handler(Event::NewEvents(StartCause::WaitCancelled {
|
||||
start: wait_start,
|
||||
requested_resume: None,
|
||||
}))
|
||||
}
|
||||
ControlFlow::WaitUntil(resume_time) => {
|
||||
let start_cause = match Instant::now() >= resume_time {
|
||||
// If the current time is later than the requested resume time, the resume time
|
||||
// has been reached.
|
||||
true => StartCause::ResumeTimeReached {
|
||||
start: wait_start,
|
||||
requested_resume: resume_time,
|
||||
},
|
||||
// Otherwise, the requested resume time HASN'T been reached and we send a WaitCancelled.
|
||||
false => StartCause::WaitCancelled {
|
||||
start: wait_start,
|
||||
requested_resume: Some(resume_time),
|
||||
},
|
||||
};
|
||||
self.call_event_handler(Event::NewEvents(start_cause));
|
||||
}
|
||||
// This can be reached if the control flow is changed to poll during a `RedrawRequested`
|
||||
// that was sent after `EventsCleared`.
|
||||
ControlFlow::Poll => self.call_event_handler(Event::NewEvents(StartCause::Poll)),
|
||||
}
|
||||
}
|
||||
|
||||
match (self.runner_state, &event) {
|
||||
(RunnerState::HandlingRedraw, Event::RedrawRequested(_)) => {
|
||||
self.call_event_handler(event)
|
||||
}
|
||||
(_, Event::RedrawRequested(_)) => {
|
||||
self.call_event_handler(Event::MainEventsCleared);
|
||||
self.runner_state = RunnerState::HandlingRedraw;
|
||||
self.call_event_handler(event);
|
||||
}
|
||||
(RunnerState::HandlingRedraw, _) => {
|
||||
warn!("Non-redraw event dispatched durning redraw phase");
|
||||
self.events_cleared();
|
||||
self.new_events();
|
||||
self.call_event_handler(event);
|
||||
}
|
||||
(_, _) => {
|
||||
self.runner_state = RunnerState::HandlingEvents;
|
||||
self.call_event_handler(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_redraws(&mut self) {
|
||||
loop {
|
||||
let redraw_window_opt = self.redraw_buffer.borrow_mut().pop_front();
|
||||
match redraw_window_opt {
|
||||
Some(window_id) => self.process_event(Event::RedrawRequested(window_id)),
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn events_cleared(&mut self) {
|
||||
match self.runner_state {
|
||||
// If we were handling events, send the EventsCleared message.
|
||||
RunnerState::HandlingEvents => {
|
||||
self.call_event_handler(Event::MainEventsCleared);
|
||||
self.flush_redraws();
|
||||
self.call_event_handler(Event::RedrawEventsCleared);
|
||||
self.runner_state = RunnerState::Idle(Instant::now());
|
||||
}
|
||||
|
||||
RunnerState::HandlingRedraw => {
|
||||
self.call_event_handler(Event::RedrawEventsCleared);
|
||||
self.runner_state = RunnerState::Idle(Instant::now());
|
||||
}
|
||||
|
||||
// If we *weren't* handling events, we don't have to do anything.
|
||||
RunnerState::New | RunnerState::Idle(..) => (),
|
||||
|
||||
// Some control flows require a NewEvents call even if no events were received. This
|
||||
// branch handles those.
|
||||
RunnerState::DeferredNewEvents(wait_start) => {
|
||||
match self.control_flow {
|
||||
// If we had deferred a Poll, send the Poll NewEvents and EventsCleared.
|
||||
ControlFlow::Poll => {
|
||||
self.call_event_handler(Event::NewEvents(StartCause::Poll));
|
||||
self.call_event_handler(Event::MainEventsCleared);
|
||||
self.flush_redraws();
|
||||
self.call_event_handler(Event::RedrawEventsCleared);
|
||||
}
|
||||
// If we had deferred a WaitUntil and the resume time has since been reached,
|
||||
// send the resume notification and EventsCleared event.
|
||||
ControlFlow::WaitUntil(resume_time) => {
|
||||
if Instant::now() >= resume_time {
|
||||
self.call_event_handler(Event::NewEvents(
|
||||
StartCause::ResumeTimeReached {
|
||||
start: wait_start,
|
||||
requested_resume: resume_time,
|
||||
},
|
||||
));
|
||||
self.call_event_handler(Event::MainEventsCleared);
|
||||
self.flush_redraws();
|
||||
self.call_event_handler(Event::RedrawEventsCleared);
|
||||
}
|
||||
}
|
||||
// If we deferred a wait and no events were received, the user doesn't have to
|
||||
// get an event.
|
||||
ControlFlow::Wait | ControlFlow::Exit => (),
|
||||
}
|
||||
// Mark that we've entered an idle state.
|
||||
self.runner_state = RunnerState::Idle(wait_start)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn call_event_handler(&mut self, event: Event<'_, T>) {
|
||||
if self.panic_error.is_none() {
|
||||
let EventLoopRunner {
|
||||
ref mut panic_error,
|
||||
ref mut event_handler,
|
||||
ref mut control_flow,
|
||||
..
|
||||
} = self;
|
||||
*panic_error = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
if *control_flow != ControlFlow::Exit {
|
||||
(*event_handler)(event, control_flow);
|
||||
} else {
|
||||
(*event_handler)(event, &mut ControlFlow::Exit);
|
||||
}
|
||||
}))
|
||||
.err();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,7 @@ impl WindowId {
|
||||
|
||||
#[macro_use]
|
||||
mod util;
|
||||
mod dark_mode;
|
||||
mod dpi;
|
||||
mod drop_handler;
|
||||
mod event;
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,20 +30,16 @@ 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,
|
||||
REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID,
|
||||
},
|
||||
event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID},
|
||||
icon::{self, IconType, WinIcon},
|
||||
monitor,
|
||||
raw_input::register_all_mice_and_keyboards_for_raw_input,
|
||||
util,
|
||||
monitor, util,
|
||||
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
|
||||
PlatformSpecificWindowBuilderAttributes, WindowId,
|
||||
},
|
||||
@@ -129,71 +125,55 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn set_visible(&self, visible: bool) {
|
||||
match visible {
|
||||
true => unsafe {
|
||||
winuser::ShowWindow(self.window.0, winuser::SW_SHOW);
|
||||
},
|
||||
false => unsafe {
|
||||
winuser::ShowWindow(self.window.0, winuser::SW_HIDE);
|
||||
},
|
||||
}
|
||||
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)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_redraw(&self) {
|
||||
unsafe {
|
||||
if self.thread_executor.trigger_newevents_on_redraw() {
|
||||
winuser::RedrawWindow(
|
||||
self.window.0,
|
||||
ptr::null(),
|
||||
ptr::null_mut(),
|
||||
winuser::RDW_INTERNALPAINT,
|
||||
);
|
||||
} else {
|
||||
let mut window_state = self.window_state.lock();
|
||||
if !window_state.queued_out_of_band_redraw {
|
||||
window_state.queued_out_of_band_redraw = true;
|
||||
winuser::PostMessageW(self.window.0, *REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID, 0, 0);
|
||||
}
|
||||
}
|
||||
winuser::RedrawWindow(
|
||||
self.window.0,
|
||||
ptr::null(),
|
||||
ptr::null_mut(),
|
||||
winuser::RDW_INTERNALPAINT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -212,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,
|
||||
)
|
||||
@@ -256,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(
|
||||
@@ -296,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();
|
||||
@@ -311,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]
|
||||
@@ -420,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 {
|
||||
@@ -438,18 +378,20 @@ impl Window {
|
||||
}
|
||||
|
||||
#[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)
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId(self.window.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId(self.window.0)
|
||||
pub fn set_minimized(&self, minimized: bool) {
|
||||
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::MINIMIZED, minimized)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -692,9 +634,14 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_position(&self, _logical_spot: LogicalPosition) {
|
||||
pub fn set_ime_position(&self, _position: Position) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_dark_mode(&self) -> bool {
|
||||
self.window_state.lock().is_dark_mode
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
@@ -720,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,
|
||||
@@ -766,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);
|
||||
@@ -839,9 +735,6 @@ unsafe fn init<T: 'static>(
|
||||
WindowWrapper(handle)
|
||||
};
|
||||
|
||||
// Set up raw input
|
||||
register_all_mice_and_keyboards_for_raw_input(real_window.0);
|
||||
|
||||
// Register for touch events if applicable
|
||||
{
|
||||
let digitizer = winuser::GetSystemMetrics(winuser::SM_DIGITIZER) as u32;
|
||||
@@ -852,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 {
|
||||
@@ -899,11 +778,21 @@ 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
|
||||
// before we update the window flags (and possibly show the
|
||||
// window for the first time).
|
||||
let dark_mode = try_dark_mode(real_window.0);
|
||||
|
||||
let window_state = {
|
||||
let window_state = WindowState::new(&attributes, window_icon, taskbar_icon, dpi_factor);
|
||||
let window_state = WindowState::new(
|
||||
&attributes,
|
||||
window_icon,
|
||||
taskbar_icon,
|
||||
dpi_factor,
|
||||
dark_mode,
|
||||
);
|
||||
let window_state = Arc::new(Mutex::new(window_state));
|
||||
WindowState::set_window_flags(window_state.lock(), real_window.0, |f| *f = window_flags);
|
||||
window_state
|
||||
@@ -915,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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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>,
|
||||
@@ -32,6 +32,7 @@ pub struct WindowState {
|
||||
/// Used to supress duplicate redraw attempts when calling `request_redraw` multiple
|
||||
/// times in `EventsCleared`.
|
||||
pub queued_out_of_band_redraw: bool,
|
||||
pub is_dark_mode: bool,
|
||||
pub high_surrogate: Option<u16>,
|
||||
window_flags: WindowFlags,
|
||||
}
|
||||
@@ -79,6 +80,8 @@ bitflags! {
|
||||
/// window's state to match our stored state. This controls whether to accept those changes.
|
||||
const MARKER_RETAIN_STATE_ON_SIZE = 1 << 10;
|
||||
|
||||
const MINIMIZED = 1 << 11;
|
||||
|
||||
const FULLSCREEN_AND_MASK = !(
|
||||
WindowFlags::DECORATIONS.bits |
|
||||
WindowFlags::RESIZABLE.bits |
|
||||
@@ -96,6 +99,7 @@ impl WindowState {
|
||||
window_icon: Option<WinIcon>,
|
||||
taskbar_icon: Option<WinIcon>,
|
||||
dpi_factor: f64,
|
||||
is_dark_mode: bool,
|
||||
) -> WindowState {
|
||||
WindowState {
|
||||
mouse: MouseProperties {
|
||||
@@ -115,6 +119,7 @@ impl WindowState {
|
||||
|
||||
fullscreen: None,
|
||||
queued_out_of_band_redraw: false,
|
||||
is_dark_mode,
|
||||
high_surrogate: None,
|
||||
window_flags: WindowFlags::empty(),
|
||||
}
|
||||
@@ -212,6 +217,9 @@ impl WindowFlags {
|
||||
if self.contains(WindowFlags::CHILD) {
|
||||
style |= WS_CHILD; // This is incompatible with WS_POPUP if that gets added eventually.
|
||||
}
|
||||
if self.contains(WindowFlags::MINIMIZED) {
|
||||
style |= WS_MINIMIZE;
|
||||
}
|
||||
if self.contains(WindowFlags::MAXIMIZED) {
|
||||
style |= WS_MAXIMIZE;
|
||||
}
|
||||
@@ -276,14 +284,30 @@ impl WindowFlags {
|
||||
}
|
||||
}
|
||||
|
||||
// Minimize operations should execute after maximize for proper window animations
|
||||
if diff.contains(WindowFlags::MINIMIZED) {
|
||||
unsafe {
|
||||
winuser::ShowWindow(
|
||||
window,
|
||||
match new.contains(WindowFlags::MINIMIZED) {
|
||||
true => winuser::SW_MINIMIZE,
|
||||
false => winuser::SW_RESTORE,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if diff != WindowFlags::empty() {
|
||||
let (style, style_ex) = new.to_window_styles();
|
||||
|
||||
unsafe {
|
||||
winuser::SendMessageW(window, *event_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID, 1, 0);
|
||||
|
||||
winuser::SetWindowLongW(window, winuser::GWL_STYLE, style as _);
|
||||
winuser::SetWindowLongW(window, winuser::GWL_EXSTYLE, style_ex as _);
|
||||
// This condition is necessary to avoid having an unrestorable window
|
||||
if !new.contains(WindowFlags::MINIMIZED) {
|
||||
winuser::SetWindowLongW(window, winuser::GWL_STYLE, style as _);
|
||||
winuser::SetWindowLongW(window, winuser::GWL_EXSTYLE, style_ex as _);
|
||||
}
|
||||
|
||||
let mut flags = winuser::SWP_NOZORDER
|
||||
| winuser::SWP_NOMOVE
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,6 +578,16 @@ impl Window {
|
||||
self.window.set_resizable(resizable)
|
||||
}
|
||||
|
||||
/// Sets the window to minimized or back
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS:** Has no effect
|
||||
#[inline]
|
||||
pub fn set_minimized(&self, minimized: bool) {
|
||||
self.window.set_minimized(minimized);
|
||||
}
|
||||
|
||||
/// Sets the window to maximized or back.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
@@ -665,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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -690,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.
|
||||
@@ -847,3 +863,9 @@ pub enum Fullscreen {
|
||||
Exclusive(VideoMode),
|
||||
Borderless(MonitorHandle),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Theme {
|
||||
Light,
|
||||
Dark,
|
||||
}
|
||||
|
||||
@@ -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>>();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user