mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-27 07:03:15 -04:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3e6949007 | ||
|
|
a033b25ecb | ||
|
|
39dd30c239 | ||
|
|
c5c99d2357 | ||
|
|
25ff30ee8c | ||
|
|
6b250a74f8 | ||
|
|
5331397c6c | ||
|
|
0b39024133 | ||
|
|
438d286fd5 | ||
|
|
18a61f1058 | ||
|
|
20d012ae3f | ||
|
|
efc54ab8ba | ||
|
|
ea1c031b54 | ||
|
|
11a44081df | ||
|
|
5eb9c9504b | ||
|
|
29a078f65c | ||
|
|
be61ca13fe | ||
|
|
e9d5b2007a | ||
|
|
f2de8475fc | ||
|
|
3ecbea3c39 | ||
|
|
c4df7ad7a5 | ||
|
|
387567a917 | ||
|
|
cfbe8462cc | ||
|
|
5f4df54895 | ||
|
|
ed698f2462 | ||
|
|
b4774861db | ||
|
|
9768f73bb7 | ||
|
|
805249e27e | ||
|
|
5f24c40d05 | ||
|
|
1b3b82a3c1 | ||
|
|
9e72396709 | ||
|
|
125ee0b446 | ||
|
|
3bfb580d7a | ||
|
|
b54d47796d | ||
|
|
b5d0d6ff3e | ||
|
|
c9520deef8 | ||
|
|
ceab0f8c40 | ||
|
|
b87757c552 | ||
|
|
1972eb952d | ||
|
|
f92803d80e | ||
|
|
8afeb910bd | ||
|
|
f16ed98af4 | ||
|
|
2a9916103b | ||
|
|
63ad47a7bf | ||
|
|
27e6548343 | ||
|
|
8c91986dd3 | ||
|
|
5a65347c4e | ||
|
|
635180c8be | ||
|
|
019ce9862f | ||
|
|
c7f46876a7 | ||
|
|
c916eb6137 | ||
|
|
67cca71524 | ||
|
|
1eff7ae004 | ||
|
|
982ad46c83 | ||
|
|
657b4fd59e | ||
|
|
b371b406d5 |
19
.github/workflows/ci.yml
vendored
19
.github/workflows/ci.yml
vendored
@@ -2,16 +2,8 @@ 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:
|
||||
@@ -38,20 +30,19 @@ jobs:
|
||||
- { target: i686-unknown-linux-gnu, os: ubuntu-latest, }
|
||||
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, }
|
||||
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: x11 }
|
||||
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: wayland }
|
||||
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: "wayland,wayland-dlopen" }
|
||||
- { target: aarch64-linux-android, os: ubuntu-latest, cmd: 'apk --' }
|
||||
- { target: x86_64-apple-darwin, os: macos-latest, }
|
||||
- { target: x86_64-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, cmd: web }
|
||||
- { target: wasm32-unknown-unknown, os: windows-latest, features: web-sys, cmd: web }
|
||||
- { target: wasm32-unknown-unknown, os: windows-latest, }
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
CARGO_INCREMENTAL: 0
|
||||
RUSTFLAGS: "-C debuginfo=0"
|
||||
RUSTFLAGS: "-C debuginfo=0 --deny warnings"
|
||||
OPTIONS: ${{ matrix.platform.options }}
|
||||
FEATURES: ${{ format(',{0}', matrix.platform.features ) }}
|
||||
CMD: ${{ matrix.platform.cmd }}
|
||||
@@ -77,10 +68,6 @@ jobs:
|
||||
- name: Install cargo-apk
|
||||
if: contains(matrix.platform.target, 'android')
|
||||
run: cargo install cargo-apk
|
||||
- name: Install cargo-web
|
||||
continue-on-error: true
|
||||
if: contains(matrix.platform.target, 'wasm32')
|
||||
run: cargo install cargo-web
|
||||
|
||||
- name: Check documentation
|
||||
shell: bash
|
||||
|
||||
37
CHANGELOG.md
37
CHANGELOG.md
@@ -1,3 +1,40 @@
|
||||
# 0.26.1 (2022-01-05)
|
||||
|
||||
- Fix linking to the `ColorSync` framework on macOS 10.7, and in newer Rust versions.
|
||||
- On Web, implement cursor grabbing through the pointer lock API.
|
||||
- On X11, add mappings for numpad comma, numpad enter, numlock and pause.
|
||||
- On macOS, fix Pinyin IME input by reverting a change that intended to improve IME.
|
||||
- On Windows, fix a crash with transparent windows on Windows 11.
|
||||
|
||||
# 0.26.0 (2021-12-01)
|
||||
|
||||
- Update `raw-window-handle` to `v0.4`. This is _not_ a breaking change, we still implement `HasRawWindowHandle` from `v0.3`, see [rust-windowing/raw-window-handle#74](https://github.com/rust-windowing/raw-window-handle/pull/74). Note that you might have to run `cargo update -p raw-window-handle` after upgrading.
|
||||
- On X11, bump `mio` to 0.8.
|
||||
- On Android, fixed `WindowExtAndroid::config` initially returning an empty `Configuration`.
|
||||
- On Android, fixed `Window::scale_factor` and `MonitorHandle::scale_factor` initially always returning 1.0.
|
||||
- On X11, select an appropriate visual for transparency if is requested
|
||||
- On Wayland and X11, fix diagonal window resize cursor orientation.
|
||||
- On macOS, drop the event callback before exiting.
|
||||
- On Android, implement `Window::request_redraw`
|
||||
- **Breaking:** On Web, remove the `stdweb` backend.
|
||||
- Added `Window::focus_window`to bring the window to the front and set input focus.
|
||||
- On Wayland and X11, implement `is_maximized` method on `Window`.
|
||||
- On Windows, prevent ghost window from showing up in the taskbar after either several hours of use or restarting `explorer.exe`.
|
||||
- On macOS, fix issue where `ReceivedCharacter` was not being emitted during some key repeat events.
|
||||
- On Wayland, load cursor icons `hand2` and `hand1` for `CursorIcon::Hand`.
|
||||
- **Breaking:** On Wayland, Theme trait and its support types are dropped.
|
||||
- On Wayland, bump `smithay-client-toolkit` to 0.15.1.
|
||||
- On Wayland, implement `request_user_attention` with `xdg_activation_v1`.
|
||||
- On X11, emit missing `WindowEvent::ScaleFactorChanged` when the only monitor gets reconnected.
|
||||
- On X11, if RANDR based scale factor is higher than 20 reset it to 1
|
||||
- On Wayland, add an enabled-by-default feature called `wayland-dlopen` so users can opt out of using `dlopen` to load system libraries.
|
||||
- **Breaking:** On Android, bump `ndk` and `ndk-glue` to 0.5.
|
||||
- On Windows, increase wait timer resolution for more accurate timing when using `WaitUntil`.
|
||||
- On macOS, fix native file dialogs hanging the event loop.
|
||||
- On Wayland, implement a workaround for wrong configure size when using `xdg_decoration` in `kwin_wayland`
|
||||
- On macOS, fix an issue that prevented the menu bar from showing in borderless fullscreen mode.
|
||||
- On X11, EINTR while polling for events no longer causes a panic. Instead it will be treated as a spurious wakeup.
|
||||
|
||||
# 0.25.0 (2021-05-15)
|
||||
|
||||
- **Breaking:** On macOS, replace `WindowBuilderExtMacOS::with_activation_policy` with `EventLoopExtMacOS::set_activation_policy`
|
||||
|
||||
44
Cargo.toml
44
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.25.0"
|
||||
version = "0.26.1"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
edition = "2018"
|
||||
@@ -12,24 +12,22 @@ documentation = "https://docs.rs/winit"
|
||||
categories = ["gui"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["serde", "web-sys"]
|
||||
features = ["serde"]
|
||||
default-target = "x86_64-unknown-linux-gnu"
|
||||
targets = ["i686-pc-windows-msvc", "x86_64-pc-windows-msvc", "i686-unknown-linux-gnu", "x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "wasm32-unknown-unknown"]
|
||||
|
||||
[features]
|
||||
default = ["x11", "wayland"]
|
||||
web-sys = ["web_sys", "wasm-bindgen", "instant/wasm-bindgen"]
|
||||
stdweb = ["std_web", "instant/stdweb"]
|
||||
x11 = ["x11-dl", "mio", "mio-misc", "percent-encoding", "parking_lot"]
|
||||
wayland = ["wayland-client", "sctk"]
|
||||
default = ["x11", "wayland", "wayland-dlopen"]
|
||||
x11 = ["x11-dl", "mio", "percent-encoding", "parking_lot"]
|
||||
wayland = ["wayland-client", "wayland-protocols", "sctk"]
|
||||
wayland-dlopen = ["sctk/dlopen", "wayland-client/dlopen"]
|
||||
|
||||
[dependencies]
|
||||
instant = "0.1"
|
||||
instant = { version = "0.1", features = ["wasm-bindgen"] }
|
||||
lazy_static = "1"
|
||||
libc = "0.2.64"
|
||||
log = "0.4"
|
||||
serde = { version = "1", optional = true, features = ["serde_derive"] }
|
||||
raw-window-handle = "0.3"
|
||||
raw-window-handle = "0.4.2"
|
||||
bitflags = "1"
|
||||
mint = { version = "0.5.6", optional = true }
|
||||
|
||||
@@ -38,9 +36,9 @@ image = "0.23.12"
|
||||
simple_logger = "1.9"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
ndk = "0.3"
|
||||
ndk = "0.5"
|
||||
ndk-sys = "0.2.0"
|
||||
ndk-glue = "0.3"
|
||||
ndk-glue = "0.5"
|
||||
|
||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||
objc = "0.2.7"
|
||||
@@ -50,7 +48,6 @@ cocoa = "0.24"
|
||||
core-foundation = "0.9"
|
||||
core-graphics = "0.22"
|
||||
dispatch = "0.2.0"
|
||||
scopeguard = "1.1"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies.core-video-sys]
|
||||
version = "0.1.4"
|
||||
@@ -61,7 +58,7 @@ features = ["display_link"]
|
||||
parking_lot = "0.11"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
||||
version = "0.3.6"
|
||||
version = "0.3.9"
|
||||
features = [
|
||||
"combaseapi",
|
||||
"commctrl",
|
||||
@@ -83,21 +80,23 @@ features = [
|
||||
"wingdi",
|
||||
"winnt",
|
||||
"winuser",
|
||||
"mmsystem",
|
||||
"timeapi"
|
||||
]
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
|
||||
wayland-client = { version = "0.28", features = [ "dlopen"] , optional = true }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.12.3", optional = true }
|
||||
mio = { version = "0.7", features = ["os-ext"], optional = true }
|
||||
mio-misc = { version = "1.0", optional = true }
|
||||
wayland-client = { version = "0.29", default_features = false, features = ["use_system_lib"], optional = true }
|
||||
wayland-protocols = { version = "0.29", features = [ "staging_protocols"], optional = true }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.15.1", default_features = false, features = ["calloop"], optional = true }
|
||||
mio = { version = "0.8", features = ["os-ext"], optional = true }
|
||||
x11-dl = { version = "2.18.5", optional = true }
|
||||
percent-encoding = { version = "2.0", optional = true }
|
||||
parking_lot = { version = "0.11.0", optional = true }
|
||||
libc = "0.2.64"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies.web_sys]
|
||||
package = "web-sys"
|
||||
version = "0.3.22"
|
||||
optional = true
|
||||
features = [
|
||||
'console',
|
||||
"AddEventListenerOptions",
|
||||
@@ -123,13 +122,6 @@ features = [
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies.wasm-bindgen]
|
||||
version = "0.2.45"
|
||||
optional = true
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies.std_web]
|
||||
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.2"
|
||||
|
||||
@@ -183,11 +183,9 @@ Legend:
|
||||
|Fullscreen |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |
|
||||
|Fullscreen toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |
|
||||
|Exclusive fullscreen |✔️ |✔️ |✔️ |**N/A** |❌ |✔️ |**N/A**|
|
||||
|HiDPI support |✔️ |✔️ |✔️ |✔️ |▢[#721]|✔️ |✔️ \*1|
|
||||
|HiDPI support |✔️ |✔️ |✔️ |✔️ |▢[#721]|✔️ |✔️ |
|
||||
|Popup windows |❌ |❌ |❌ |❌ |❌ |❌ |**N/A**|
|
||||
|
||||
\*1: `WindowEvent::ScaleFactorChanged` is not sent on `stdweb` backend.
|
||||
|
||||
### System information
|
||||
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |
|
||||
|---------------- | ----- | ---- | ------- | ----------- | ----- | ------- | -------- |
|
||||
@@ -199,7 +197,7 @@ Legend:
|
||||
|----------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|
||||
|Mouse events |✔️ |▢[#63] |✔️ |✔️ |**N/A**|**N/A**|✔️ |
|
||||
|Mouse set location |✔️ |✔️ |✔️ |❓ |**N/A**|**N/A**|**N/A**|
|
||||
|Cursor grab |✔️ |▢[#165] |▢[#242] |✔️ |**N/A**|**N/A**|❓ |
|
||||
|Cursor grab |✔️ |▢[#165] |▢[#242] |✔️ |**N/A**|**N/A**|✔️ |
|
||||
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |
|
||||
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ |
|
||||
|Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |❌ |
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
# Hall of Champions
|
||||
|
||||
The Winit maintainers would like to recognize the following former Winit
|
||||
contributors, without whom Winit would not exist in its current form. We thank
|
||||
The winit maintainers would like to recognize the following former winit
|
||||
contributors, without whom winit would not exist in its current form. We thank
|
||||
them deeply for their time and efforts, and wish them best of luck in their
|
||||
future endeavors:
|
||||
|
||||
* [@tomaka]: For creating the Winit project and guiding it through its early
|
||||
* [@tomaka]: For creating the winit project and guiding it through its early
|
||||
years of existence.
|
||||
* [@vberger]: For diligently creating the Wayland backend, and being its
|
||||
extremely helpful and benevolent maintainer for years.
|
||||
* [@francesca64]: For taking over the responsibility of maintaining almost every
|
||||
Winit backend, and standardizing HiDPI support across all of them
|
||||
winit backend, and standardizing HiDPI support across all of them.
|
||||
* [@Osspial]: For heroically landing EventLoop 2.0, and valiantly ushering in a
|
||||
vastly more sustainable era of winit.
|
||||
* [@goddessfreya]: For selflessly taking over maintainership of glutin, and her
|
||||
stellar dedication to improving both winit and glutin.
|
||||
|
||||
[@tomaka]: https://github.com/tomaka
|
||||
[@vberger]: https://github.com/vberger
|
||||
[@francesca64]: https://github.com/francesca64
|
||||
[@Osspial]: https://github.com/Osspial
|
||||
[@goddessfreya]: https://github.com/goddessfreya
|
||||
|
||||
25
README.md
25
README.md
@@ -6,7 +6,7 @@
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.25.0"
|
||||
winit = "0.26.1"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
@@ -19,9 +19,8 @@ For features _outside_ the scope of winit, see [Missing features provided by oth
|
||||
|
||||
Join us in any of these:
|
||||
|
||||
[](http://webchat.freenode.net?channels=%23glutin&uio=MTY9dHJ1ZSYyPXRydWUmND10cnVlJjExPTE4NSYxMj10cnVlJjE1PXRydWU7a)
|
||||
[](https://matrix.to/#/#Glutin:matrix.org)
|
||||
[](https://gitter.im/tomaka/glutin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://matrix.to/#/#rust-windowing:matrix.org)
|
||||
[](https://web.libera.chat/#winit)
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -72,10 +71,7 @@ Winit provides the following features, which can be enabled in your `Cargo.toml`
|
||||
|
||||
#### WebAssembly
|
||||
|
||||
Winit supports compiling to the `wasm32-unknown-unknown` target with either a
|
||||
`stdweb` or a `web-sys` backend for use on web browsers. However, please note
|
||||
that **the `stdweb` backend is being deprecated and may be removed in a future
|
||||
release of Winit**. The `web-sys` backend is also more feature complete.
|
||||
Winit supports compiling to the `wasm32-unknown-unknown` target with `web-sys`.
|
||||
|
||||
On the web platform, a Winit window is backed by a `<canvas>` element. You can
|
||||
either [provide Winit with a `<canvas>` element][web with_canvas], or [let Winit
|
||||
@@ -95,7 +91,18 @@ book].
|
||||
|
||||
This library makes use of the [ndk-rs](https://github.com/rust-windowing/android-ndk-rs) crates, refer to that repo for more documentation.
|
||||
|
||||
The `ndk_glue` version needs to match the version used by `winit`. Otherwise, the application will not start correctly as `ndk_glue`'s internal NativeActivity static is not the same due to version mismatch.
|
||||
|
||||
`ndk_glue` <-> `winit` version comparison compatibility:
|
||||
|
||||
| winit | ndk_glue |
|
||||
| :---: | :------------------: |
|
||||
| 0.24 | `ndk_glue = "0.2.0"` |
|
||||
| 0.25 | `ndk_glue = "0.3.0"` |
|
||||
| 0.26 | `ndk_glue = "0.5.0"` |
|
||||
|
||||
Running on an Android device needs a dynamic system library, add this to Cargo.toml:
|
||||
|
||||
```toml
|
||||
[[example]]
|
||||
name = "request_redraw_threaded"
|
||||
@@ -118,6 +125,6 @@ To ensure compatibility with older MacOS systems, winit links to
|
||||
CGDisplayCreateUUIDFromDisplayID through the CoreGraphics framework.
|
||||
However, under certain setups this function is only available to be linked
|
||||
through the newer ColorSync framework. So, winit provides the
|
||||
`WINIT_LINK_COLORSYNC` environment variable which can be set to `1` or `true`
|
||||
`WINIT_LINK_COLORSYNC` environment variable which can be set to `1` or `true`
|
||||
while compiling to enable linking via ColorSync.
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ fn main() {
|
||||
if wait_cancelled {
|
||||
*control_flow
|
||||
} else {
|
||||
ControlFlow::WaitUntil(time::Instant::now() + WAIT_TIME)
|
||||
ControlFlow::WaitUntil(instant::Instant::now() + WAIT_TIME)
|
||||
}
|
||||
}
|
||||
Mode::Poll => {
|
||||
|
||||
@@ -43,7 +43,6 @@ fn main() {
|
||||
..
|
||||
} => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
return;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ fn main() {
|
||||
|
||||
let mut num = String::new();
|
||||
stdin().read_line(&mut num).unwrap();
|
||||
let num = num.trim().parse().ok().expect("Please enter a number");
|
||||
let num = num.trim().parse().expect("Please enter a number");
|
||||
|
||||
let fullscreen = Some(match num {
|
||||
1 => Fullscreen::Exclusive(prompt_for_video_mode(&prompt_for_monitor(&event_loop))),
|
||||
@@ -85,7 +85,7 @@ fn prompt_for_monitor(event_loop: &EventLoop<()>) -> MonitorHandle {
|
||||
|
||||
let mut num = String::new();
|
||||
stdin().read_line(&mut num).unwrap();
|
||||
let num = num.trim().parse().ok().expect("Please enter a number");
|
||||
let num = num.trim().parse().expect("Please enter a number");
|
||||
let monitor = event_loop
|
||||
.available_monitors()
|
||||
.nth(num)
|
||||
@@ -106,7 +106,7 @@ fn prompt_for_video_mode(monitor: &MonitorHandle) -> VideoMode {
|
||||
|
||||
let mut num = String::new();
|
||||
stdin().read_line(&mut num).unwrap();
|
||||
let num = num.trim().parse().ok().expect("Please enter a number");
|
||||
let num = num.trim().parse().expect("Please enter a number");
|
||||
let video_mode = monitor
|
||||
.video_modes()
|
||||
.nth(num)
|
||||
|
||||
@@ -34,10 +34,10 @@ fn main() {
|
||||
// We need to update our chosen video mode if the window
|
||||
// was moved to an another monitor, so that the window
|
||||
// appears on this monitor instead when we go fullscreen
|
||||
let previous_video_mode = video_modes.iter().cloned().nth(video_mode_id);
|
||||
let previous_video_mode = video_modes.get(video_mode_id).cloned();
|
||||
video_modes = window.current_monitor().unwrap().video_modes().collect();
|
||||
video_mode_id = video_mode_id.min(video_modes.len());
|
||||
let video_mode = video_modes.iter().nth(video_mode_id);
|
||||
let video_mode = video_modes.get(video_mode_id);
|
||||
|
||||
// Different monitors may support different video modes,
|
||||
// and the index we chose previously may now point to a
|
||||
@@ -45,7 +45,7 @@ fn main() {
|
||||
if video_mode != previous_video_mode.as_ref() {
|
||||
println!(
|
||||
"Window moved to another monitor, picked video mode: {}",
|
||||
video_modes.iter().nth(video_mode_id).unwrap()
|
||||
video_modes.get(video_mode_id).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -77,16 +77,13 @@ fn main() {
|
||||
Right => (video_modes.len() - 1).min(video_mode_id + 1),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
println!(
|
||||
"Picking video mode: {}",
|
||||
video_modes.iter().nth(video_mode_id).unwrap()
|
||||
);
|
||||
println!("Picking video mode: {}", video_modes[video_mode_id]);
|
||||
}
|
||||
F => window.set_fullscreen(match (state, modifiers.alt()) {
|
||||
(true, false) => Some(Fullscreen::Borderless(None)),
|
||||
(true, true) => Some(Fullscreen::Exclusive(
|
||||
video_modes.iter().nth(video_mode_id).unwrap().clone(),
|
||||
)),
|
||||
(true, true) => {
|
||||
Some(Fullscreen::Exclusive(video_modes[video_mode_id].clone()))
|
||||
}
|
||||
(false, _) => None,
|
||||
}),
|
||||
G => window.set_cursor_grab(state).unwrap(),
|
||||
@@ -173,7 +170,7 @@ fn main() {
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ fn main() {
|
||||
},
|
||||
..
|
||||
} => {
|
||||
let window = Window::new(&event_loop).unwrap();
|
||||
let window = Window::new(event_loop).unwrap();
|
||||
windows.insert(window.id(), window);
|
||||
}
|
||||
_ => (),
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use std::{thread, time};
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn main() {
|
||||
use std::{thread, time};
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
@@ -38,3 +39,8 @@ fn main() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn main() {
|
||||
unimplemented!() // `Window` can't be sent between threads
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ fn main() {
|
||||
..
|
||||
} => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
return;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ pub fn main() {
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
use winit::platform::web::WindowExtWebSys;
|
||||
|
||||
@@ -26,28 +26,12 @@ pub fn main() {
|
||||
.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")]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
log::debug!("{:?}", event);
|
||||
|
||||
#[cfg(feature = "stdweb")]
|
||||
std_web::console!(log, "%s", format!("{:?}", event));
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
@@ -61,13 +45,13 @@ pub fn main() {
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod wasm {
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() {
|
||||
console_log::init_with_level(log::Level::Debug);
|
||||
console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
|
||||
|
||||
super::main();
|
||||
}
|
||||
|
||||
93
src/dpi.rs
93
src/dpi.rs
@@ -58,7 +58,7 @@
|
||||
//! 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`.
|
||||
//! then its scale factor can be found by calling [window.scale_factor()].
|
||||
//!
|
||||
//! ## How is the scale factor calculated?
|
||||
//!
|
||||
@@ -95,6 +95,7 @@
|
||||
//!
|
||||
//! [points]: https://en.wikipedia.org/wiki/Point_(typography)
|
||||
//! [picas]: https://en.wikipedia.org/wiki/Pica_(typography)
|
||||
//! [window.scale_factor()]: crate::window::Window::scale_factor
|
||||
//! [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/
|
||||
@@ -210,9 +211,9 @@ impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalPosition<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> Into<(X, X)> for LogicalPosition<P> {
|
||||
fn into(self: Self) -> (X, X) {
|
||||
(self.x.cast(), self.y.cast())
|
||||
impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for (X, X) {
|
||||
fn from(p: LogicalPosition<P>) -> (X, X) {
|
||||
(p.x.cast(), p.y.cast())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,26 +223,23 @@ impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalPosition<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> Into<[X; 2]> for LogicalPosition<P> {
|
||||
fn into(self: Self) -> [X; 2] {
|
||||
[self.x.cast(), self.y.cast()]
|
||||
impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for [X; 2] {
|
||||
fn from(p: LogicalPosition<P>) -> [X; 2] {
|
||||
[p.x.cast(), p.y.cast()]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<P: Pixel> From<mint::Point2<P>> for LogicalPosition<P> {
|
||||
fn from(mint: mint::Point2<P>) -> Self {
|
||||
Self::new(mint.x, mint.y)
|
||||
fn from(p: mint::Point2<P>) -> Self {
|
||||
Self::new(p.x, p.y)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<P: Pixel> From<LogicalPosition<P>> for mint::Point2<P> {
|
||||
fn from(winit: LogicalPosition<P>) -> Self {
|
||||
mint::Point2 {
|
||||
x: winit.x,
|
||||
y: winit.y,
|
||||
}
|
||||
fn from(p: LogicalPosition<P>) -> Self {
|
||||
mint::Point2 { x: p.x, y: p.y }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,9 +290,9 @@ impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalPosition<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> Into<(X, X)> for PhysicalPosition<P> {
|
||||
fn into(self: Self) -> (X, X) {
|
||||
(self.x.cast(), self.y.cast())
|
||||
impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for (X, X) {
|
||||
fn from(p: PhysicalPosition<P>) -> (X, X) {
|
||||
(p.x.cast(), p.y.cast())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,26 +302,23 @@ impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalPosition<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> Into<[X; 2]> for PhysicalPosition<P> {
|
||||
fn into(self: Self) -> [X; 2] {
|
||||
[self.x.cast(), self.y.cast()]
|
||||
impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for [X; 2] {
|
||||
fn from(p: PhysicalPosition<P>) -> [X; 2] {
|
||||
[p.x.cast(), p.y.cast()]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<P: Pixel> From<mint::Point2<P>> for PhysicalPosition<P> {
|
||||
fn from(mint: mint::Point2<P>) -> Self {
|
||||
Self::new(mint.x, mint.y)
|
||||
fn from(p: mint::Point2<P>) -> Self {
|
||||
Self::new(p.x, p.y)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<P: Pixel> From<PhysicalPosition<P>> for mint::Point2<P> {
|
||||
fn from(winit: PhysicalPosition<P>) -> Self {
|
||||
mint::Point2 {
|
||||
x: winit.x,
|
||||
y: winit.y,
|
||||
}
|
||||
fn from(p: PhysicalPosition<P>) -> Self {
|
||||
mint::Point2 { x: p.x, y: p.y }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,9 +369,9 @@ impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalSize<P> {
|
||||
}
|
||||
}
|
||||
|
||||
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<P: Pixel, X: Pixel> From<LogicalSize<P>> for (X, X) {
|
||||
fn from(s: LogicalSize<P>) -> (X, X) {
|
||||
(s.width.cast(), s.height.cast())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,25 +381,25 @@ impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalSize<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> Into<[X; 2]> for LogicalSize<P> {
|
||||
fn into(self: Self) -> [X; 2] {
|
||||
[self.width.cast(), self.height.cast()]
|
||||
impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for [X; 2] {
|
||||
fn from(s: LogicalSize<P>) -> [X; 2] {
|
||||
[s.width.cast(), s.height.cast()]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<P: Pixel> From<mint::Vector2<P>> for LogicalSize<P> {
|
||||
fn from(mint: mint::Vector2<P>) -> Self {
|
||||
Self::new(mint.x, mint.y)
|
||||
fn from(v: mint::Vector2<P>) -> Self {
|
||||
Self::new(v.x, v.y)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<P: Pixel> From<LogicalSize<P>> for mint::Vector2<P> {
|
||||
fn from(winit: LogicalSize<P>) -> Self {
|
||||
fn from(s: LogicalSize<P>) -> Self {
|
||||
mint::Vector2 {
|
||||
x: winit.width,
|
||||
y: winit.height,
|
||||
x: s.width,
|
||||
y: s.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -453,9 +448,9 @@ impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalSize<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> Into<(X, X)> for PhysicalSize<P> {
|
||||
fn into(self: Self) -> (X, X) {
|
||||
(self.width.cast(), self.height.cast())
|
||||
impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for (X, X) {
|
||||
fn from(s: PhysicalSize<P>) -> (X, X) {
|
||||
(s.width.cast(), s.height.cast())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,25 +460,25 @@ impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalSize<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> Into<[X; 2]> for PhysicalSize<P> {
|
||||
fn into(self: Self) -> [X; 2] {
|
||||
[self.width.cast(), self.height.cast()]
|
||||
impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for [X; 2] {
|
||||
fn from(s: PhysicalSize<P>) -> [X; 2] {
|
||||
[s.width.cast(), s.height.cast()]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<P: Pixel> From<mint::Vector2<P>> for PhysicalSize<P> {
|
||||
fn from(mint: mint::Vector2<P>) -> Self {
|
||||
Self::new(mint.x, mint.y)
|
||||
fn from(v: mint::Vector2<P>) -> Self {
|
||||
Self::new(v.x, v.y)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<P: Pixel> From<PhysicalSize<P>> for mint::Vector2<P> {
|
||||
fn from(winit: PhysicalSize<P>) -> Self {
|
||||
fn from(s: PhysicalSize<P>) -> Self {
|
||||
mint::Vector2 {
|
||||
x: winit.width,
|
||||
y: winit.height,
|
||||
x: s.width,
|
||||
y: s.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
30
src/event.rs
30
src/event.rs
@@ -113,7 +113,7 @@ pub enum Event<'a, T: 'static> {
|
||||
|
||||
/// 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
|
||||
/// This is irreversible - 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,
|
||||
}
|
||||
@@ -131,7 +131,7 @@ impl<T: Clone> Clone for Event<'static, T> {
|
||||
device_id: *device_id,
|
||||
event: event.clone(),
|
||||
},
|
||||
NewEvents(cause) => NewEvents(cause.clone()),
|
||||
NewEvents(cause) => NewEvents(*cause),
|
||||
MainEventsCleared => MainEventsCleared,
|
||||
RedrawRequested(wid) => RedrawRequested(*wid),
|
||||
RedrawEventsCleared => RedrawEventsCleared,
|
||||
@@ -358,8 +358,8 @@ impl Clone for WindowEvent<'static> {
|
||||
fn clone(&self) -> Self {
|
||||
use self::WindowEvent::*;
|
||||
return match self {
|
||||
Resized(size) => Resized(size.clone()),
|
||||
Moved(pos) => Moved(pos.clone()),
|
||||
Resized(size) => Resized(*size),
|
||||
Moved(pos) => Moved(*pos),
|
||||
CloseRequested => CloseRequested,
|
||||
Destroyed => Destroyed,
|
||||
DroppedFile(file) => DroppedFile(file.clone()),
|
||||
@@ -377,7 +377,7 @@ impl Clone for WindowEvent<'static> {
|
||||
is_synthetic: *is_synthetic,
|
||||
},
|
||||
|
||||
ModifiersChanged(modifiers) => ModifiersChanged(modifiers.clone()),
|
||||
ModifiersChanged(modifiers) => ModifiersChanged(*modifiers),
|
||||
#[allow(deprecated)]
|
||||
CursorMoved {
|
||||
device_id,
|
||||
@@ -437,7 +437,7 @@ impl Clone for WindowEvent<'static> {
|
||||
value: *value,
|
||||
},
|
||||
Touch(touch) => Touch(*touch),
|
||||
ThemeChanged(theme) => ThemeChanged(theme.clone()),
|
||||
ThemeChanged(theme) => ThemeChanged(*theme),
|
||||
ScaleFactorChanged { .. } => {
|
||||
unreachable!("Static event can't be about scale factor changing")
|
||||
}
|
||||
@@ -538,12 +538,16 @@ impl<'a> WindowEvent<'a> {
|
||||
pub struct DeviceId(pub(crate) platform_impl::DeviceId);
|
||||
|
||||
impl DeviceId {
|
||||
/// Returns a dummy `DeviceId`, useful for unit testing. The only guarantee made about the return
|
||||
/// value of this function is that it will always be equal to itself and to future values returned
|
||||
/// by this function. No other guarantees are made. This may be equal to a real `DeviceId`.
|
||||
/// Returns a dummy `DeviceId`, useful for unit testing.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The only guarantee made about the return value of this function is that
|
||||
/// it will always be equal to itself and to future values returned by this function.
|
||||
/// No other guarantees are made. This may be equal to a real `DeviceId`.
|
||||
///
|
||||
/// **Passing this into a winit function will result in undefined behavior.**
|
||||
pub unsafe fn dummy() -> Self {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
DeviceId(platform_impl::DeviceId::dummy())
|
||||
}
|
||||
}
|
||||
@@ -998,9 +1002,9 @@ bitflags! {
|
||||
// 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;
|
||||
const SHIFT = 0b100;
|
||||
// const LSHIFT = 0b010;
|
||||
// const RSHIFT = 0b001;
|
||||
/// The "control" key.
|
||||
const CTRL = 0b100 << 3;
|
||||
// const LCTRL = 0b010 << 3;
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
|
||||
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(broken_intra_doc_links)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
@@ -146,8 +146,6 @@ extern crate bitflags;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
#[macro_use]
|
||||
extern crate objc;
|
||||
#[cfg(all(target_arch = "wasm32", feature = "std_web"))]
|
||||
extern crate std_web as stdweb;
|
||||
|
||||
pub mod dpi;
|
||||
#[macro_use]
|
||||
|
||||
@@ -241,14 +241,10 @@ pub trait EventLoopWindowTargetExtMacOS {
|
||||
|
||||
impl<T> EventLoopWindowTargetExtMacOS for EventLoopWindowTarget<T> {
|
||||
fn hide_application(&self) {
|
||||
let cls = objc::runtime::Class::get("NSApplication").unwrap();
|
||||
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
|
||||
unsafe { msg_send![app, hide: 0] }
|
||||
self.p.hide_application()
|
||||
}
|
||||
|
||||
fn hide_other_applications(&self) {
|
||||
let cls = objc::runtime::Class::get("NSApplication").unwrap();
|
||||
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
|
||||
unsafe { msg_send![app, hideOtherApplications: 0] }
|
||||
self.p.hide_other_applications()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,10 +237,6 @@ pub trait WindowExtUnix {
|
||||
#[cfg(feature = "wayland")]
|
||||
fn wayland_display(&self) -> Option<*mut raw::c_void>;
|
||||
|
||||
/// Sets the color theme of the client side window decorations on wayland
|
||||
#[cfg(feature = "wayland")]
|
||||
fn set_wayland_theme<T: Theme>(&self, theme: T);
|
||||
|
||||
/// Check if the window is ready for drawing
|
||||
///
|
||||
/// It is a remnant of a previous implementation detail for the
|
||||
@@ -323,16 +319,6 @@ impl WindowExtUnix for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "wayland")]
|
||||
fn set_wayland_theme<T: Theme>(&self, theme: T) {
|
||||
match self.window {
|
||||
LinuxWindow::Wayland(ref w) => w.set_theme(theme),
|
||||
#[cfg(feature = "x11")]
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_ready(&self) -> bool {
|
||||
true
|
||||
@@ -359,9 +345,31 @@ pub trait WindowBuilderExtUnix {
|
||||
#[cfg(feature = "x11")]
|
||||
fn with_gtk_theme_variant(self, variant: String) -> Self;
|
||||
/// Build window with resize increment hint. Only implemented on X11.
|
||||
///
|
||||
/// ```
|
||||
/// # use winit::dpi::{LogicalSize, PhysicalSize};
|
||||
/// # use winit::window::WindowBuilder;
|
||||
/// # use winit::platform::unix::WindowBuilderExtUnix;
|
||||
/// // Specify the size in logical dimensions like this:
|
||||
/// WindowBuilder::new().with_resize_increments(LogicalSize::new(400.0, 200.0));
|
||||
///
|
||||
/// // Or specify the size in physical dimensions like this:
|
||||
/// WindowBuilder::new().with_resize_increments(PhysicalSize::new(400, 200));
|
||||
/// ```
|
||||
#[cfg(feature = "x11")]
|
||||
fn with_resize_increments<S: Into<Size>>(self, increments: S) -> Self;
|
||||
/// Build window with base size hint. Only implemented on X11.
|
||||
///
|
||||
/// ```
|
||||
/// # use winit::dpi::{LogicalSize, PhysicalSize};
|
||||
/// # use winit::window::WindowBuilder;
|
||||
/// # use winit::platform::unix::WindowBuilderExtUnix;
|
||||
/// // Specify the size in logical dimensions like this:
|
||||
/// WindowBuilder::new().with_base_size(LogicalSize::new(400.0, 200.0));
|
||||
///
|
||||
/// // Or specify the size in physical dimensions like this:
|
||||
/// WindowBuilder::new().with_base_size(PhysicalSize::new(400, 200));
|
||||
/// ```
|
||||
#[cfg(feature = "x11")]
|
||||
fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self;
|
||||
|
||||
@@ -454,78 +462,3 @@ impl MonitorHandleExtUnix for MonitorHandle {
|
||||
self.inner.native_identifier()
|
||||
}
|
||||
}
|
||||
|
||||
/// A theme for a Wayland's client side decorations.
|
||||
#[cfg(feature = "wayland")]
|
||||
pub trait Theme: Send + 'static {
|
||||
/// Title bar color.
|
||||
fn element_color(&self, element: Element, window_active: bool) -> ARGBColor;
|
||||
|
||||
/// Color for a given button part.
|
||||
fn button_color(
|
||||
&self,
|
||||
button: Button,
|
||||
state: ButtonState,
|
||||
foreground: bool,
|
||||
window_active: bool,
|
||||
) -> ARGBColor;
|
||||
|
||||
/// Font name and the size for the title bar.
|
||||
///
|
||||
/// By default the font is `sans-serif` at the size of 17.
|
||||
///
|
||||
/// Returning `None` means that title won't be drawn.
|
||||
fn font(&self) -> Option<(String, f32)> {
|
||||
// Not having any title isn't something desirable for the users, so setting it to
|
||||
// something generic.
|
||||
Some((String::from("sans-serif"), 17.))
|
||||
}
|
||||
}
|
||||
|
||||
/// A button on Wayland's client side decorations.
|
||||
#[cfg(feature = "wayland")]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Button {
|
||||
/// Button that maximizes the window.
|
||||
Maximize,
|
||||
|
||||
/// Button that minimizes the window.
|
||||
Minimize,
|
||||
|
||||
/// Button that closes the window.
|
||||
Close,
|
||||
}
|
||||
|
||||
/// A button state of the button on Wayland's client side decorations.
|
||||
#[cfg(feature = "wayland")]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum ButtonState {
|
||||
/// Button is being hovered over by pointer.
|
||||
Hovered,
|
||||
/// Button is not being hovered over by pointer.
|
||||
Idle,
|
||||
/// Button is disabled.
|
||||
Disabled,
|
||||
}
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Element {
|
||||
/// Bar itself.
|
||||
Bar,
|
||||
|
||||
/// Separator between window and title bar.
|
||||
Separator,
|
||||
|
||||
/// Title bar text.
|
||||
Text,
|
||||
}
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct ARGBColor {
|
||||
pub a: u8,
|
||||
pub r: u8,
|
||||
pub g: u8,
|
||||
pub b: u8,
|
||||
}
|
||||
|
||||
@@ -1,28 +1,14 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
//! The web target does not automatically insert the canvas element object into the web page, to
|
||||
//! allow end users to determine how the page should be laid out. Use the `WindowExtStdweb` or
|
||||
//! `WindowExtWebSys` traits (depending on your web backend) to retrieve the canvas from the
|
||||
//! Window. Alternatively, use the `WindowBuilderExtStdweb` or `WindowBuilderExtWebSys` to provide
|
||||
//! your own canvas.
|
||||
//! allow end users to determine how the page should be laid out. Use the `WindowExtWebSys` trait
|
||||
//! to retrieve the canvas from the Window. Alternatively, use the `WindowBuilderExtWebSys` trait
|
||||
//! to provide your own canvas.
|
||||
|
||||
use crate::window::WindowBuilder;
|
||||
|
||||
#[cfg(feature = "stdweb")]
|
||||
use stdweb::web::html_element::CanvasElement;
|
||||
|
||||
#[cfg(feature = "stdweb")]
|
||||
pub trait WindowExtStdweb {
|
||||
fn canvas(&self) -> CanvasElement;
|
||||
|
||||
/// Whether the browser reports the preferred color scheme to be "dark".
|
||||
fn is_dark_mode(&self) -> bool;
|
||||
}
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
use web_sys::HtmlCanvasElement;
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
pub trait WindowExtWebSys {
|
||||
fn canvas(&self) -> HtmlCanvasElement;
|
||||
|
||||
@@ -30,26 +16,10 @@ pub trait WindowExtWebSys {
|
||||
fn is_dark_mode(&self) -> bool;
|
||||
}
|
||||
|
||||
#[cfg(feature = "stdweb")]
|
||||
pub trait WindowBuilderExtStdweb {
|
||||
fn with_canvas(self, canvas: Option<CanvasElement>) -> Self;
|
||||
}
|
||||
|
||||
#[cfg(feature = "stdweb")]
|
||||
impl WindowBuilderExtStdweb for WindowBuilder {
|
||||
fn with_canvas(mut self, canvas: Option<CanvasElement>) -> Self {
|
||||
self.platform_specific.canvas = canvas;
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
pub trait WindowBuilderExtWebSys {
|
||||
fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self;
|
||||
}
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
impl WindowBuilderExtWebSys for WindowBuilder {
|
||||
fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self {
|
||||
self.platform_specific.canvas = canvas;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
use std::os::raw::c_void;
|
||||
use std::path::Path;
|
||||
|
||||
use libc;
|
||||
use winapi::shared::minwindef::WORD;
|
||||
use winapi::shared::windef::{HMENU, HWND};
|
||||
|
||||
@@ -72,11 +71,11 @@ impl<T> EventLoopExtWindows for EventLoop<T> {
|
||||
/// Additional methods on `Window` that are specific to Windows.
|
||||
pub trait WindowExtWindows {
|
||||
/// Returns the HINSTANCE of the window
|
||||
fn hinstance(&self) -> *mut libc::c_void;
|
||||
fn hinstance(&self) -> *mut c_void;
|
||||
/// Returns the native handle that is used by this window.
|
||||
///
|
||||
/// The pointer will become invalid when the native window was destroyed.
|
||||
fn hwnd(&self) -> *mut libc::c_void;
|
||||
fn hwnd(&self) -> *mut c_void;
|
||||
|
||||
/// Enables or disables mouse and keyboard input to the specified window.
|
||||
///
|
||||
@@ -102,20 +101,18 @@ pub trait WindowExtWindows {
|
||||
|
||||
impl WindowExtWindows for Window {
|
||||
#[inline]
|
||||
fn hinstance(&self) -> *mut libc::c_void {
|
||||
fn hinstance(&self) -> *mut c_void {
|
||||
self.window.hinstance() as *mut _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hwnd(&self) -> *mut libc::c_void {
|
||||
fn hwnd(&self) -> *mut c_void {
|
||||
self.window.hwnd() as *mut _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_enable(&self, enabled: bool) {
|
||||
unsafe {
|
||||
winapi::um::winuser::EnableWindow(self.hwnd() as _, enabled as _);
|
||||
}
|
||||
self.window.set_enable(enabled)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -12,6 +12,7 @@ use ndk::{
|
||||
looper::{ForeignLooper, Poll, ThreadLooper},
|
||||
};
|
||||
use ndk_glue::{Event, Rect};
|
||||
use raw_window_handle::{AndroidNdkHandle, RawWindowHandle};
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
sync::{Arc, Mutex, RwLock},
|
||||
@@ -19,13 +20,27 @@ use std::{
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
static ref CONFIG: RwLock<Configuration> = RwLock::new(Configuration::new());
|
||||
static ref CONFIG: RwLock<Configuration> = RwLock::new(Configuration::from_asset_manager(
|
||||
&ndk_glue::native_activity().asset_manager()
|
||||
));
|
||||
// If this is `Some()` a `Poll::Wake` is considered an `EventSource::Internal` with the event
|
||||
// contained in the `Option`. The event is moved outside of the `Option` replacing it with a
|
||||
// `None`.
|
||||
//
|
||||
// This allows us to inject event into the event loop without going through `ndk-glue` and
|
||||
// calling unsafe function that should only be called by Android.
|
||||
static ref INTERNAL_EVENT: RwLock<Option<InternalEvent>> = RwLock::new(None);
|
||||
}
|
||||
|
||||
enum InternalEvent {
|
||||
RedrawRequested,
|
||||
}
|
||||
|
||||
enum EventSource {
|
||||
Callback,
|
||||
InputQueue,
|
||||
User,
|
||||
Internal(InternalEvent),
|
||||
}
|
||||
|
||||
fn poll(poll: Poll) -> Option<EventSource> {
|
||||
@@ -36,7 +51,13 @@ fn poll(poll: Poll) -> Option<EventSource> {
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Poll::Timeout => None,
|
||||
Poll::Wake => Some(EventSource::User),
|
||||
Poll::Wake => Some(
|
||||
INTERNAL_EVENT
|
||||
.write()
|
||||
.unwrap()
|
||||
.take()
|
||||
.map_or(EventSource::User, EventSource::Internal),
|
||||
),
|
||||
Poll::Callback => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -283,6 +304,9 @@ impl<T: 'static> EventLoop<T> {
|
||||
);
|
||||
}
|
||||
}
|
||||
Some(EventSource::Internal(internal)) => match internal {
|
||||
InternalEvent::RedrawRequested => redraw = true,
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
|
||||
@@ -422,7 +446,7 @@ impl<T: 'static> EventLoopWindowTarget<T> {
|
||||
pub struct WindowId;
|
||||
|
||||
impl WindowId {
|
||||
pub fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
WindowId
|
||||
}
|
||||
}
|
||||
@@ -431,7 +455,7 @@ impl WindowId {
|
||||
pub struct DeviceId;
|
||||
|
||||
impl DeviceId {
|
||||
pub fn dummy() -> Self {
|
||||
pub const fn dummy() -> Self {
|
||||
DeviceId
|
||||
}
|
||||
}
|
||||
@@ -478,7 +502,8 @@ impl Window {
|
||||
}
|
||||
|
||||
pub fn request_redraw(&self) {
|
||||
// TODO
|
||||
*INTERNAL_EVENT.write().unwrap() = Some(InternalEvent::RedrawRequested);
|
||||
ForeignLooper::for_thread().unwrap().wake();
|
||||
}
|
||||
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, error::NotSupportedError> {
|
||||
@@ -539,6 +564,8 @@ impl Window {
|
||||
|
||||
pub fn set_ime_position(&self, _position: Position) {}
|
||||
|
||||
pub fn focus_window(&self) {}
|
||||
|
||||
pub fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {}
|
||||
|
||||
pub fn set_cursor_icon(&self, _: window::CursorIcon) {}
|
||||
@@ -563,15 +590,14 @@ impl Window {
|
||||
))
|
||||
}
|
||||
|
||||
pub fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
|
||||
let a_native_window = if let Some(native_window) = ndk_glue::native_window().as_ref() {
|
||||
unsafe { native_window.ptr().as_mut() as *mut _ as *mut _ }
|
||||
pub fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
let mut handle = AndroidNdkHandle::empty();
|
||||
if let Some(native_window) = ndk_glue::native_window().as_ref() {
|
||||
handle.a_native_window = unsafe { native_window.ptr().as_mut() as *mut _ as *mut _ }
|
||||
} else {
|
||||
panic!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events.");
|
||||
};
|
||||
let mut handle = raw_window_handle::android::AndroidHandle::empty();
|
||||
handle.a_native_window = a_native_window;
|
||||
raw_window_handle::RawWindowHandle::Android(handle)
|
||||
RawWindowHandle::AndroidNdk(handle)
|
||||
}
|
||||
|
||||
pub fn config(&self) -> Configuration {
|
||||
|
||||
@@ -91,7 +91,7 @@ pub struct DeviceId {
|
||||
}
|
||||
|
||||
impl DeviceId {
|
||||
pub unsafe fn dummy() -> Self {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
DeviceId {
|
||||
uiscreen: std::ptr::null_mut(),
|
||||
}
|
||||
|
||||
@@ -168,7 +168,9 @@ impl Drop for MonitorHandle {
|
||||
|
||||
impl fmt::Debug for MonitorHandle {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// TODO: Do this using the proper fmt API
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
struct MonitorHandle {
|
||||
name: Option<String>,
|
||||
size: PhysicalSize<u32>,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use raw_window_handle::{ios::IOSHandle, RawWindowHandle};
|
||||
use raw_window_handle::{RawWindowHandle, UiKitHandle};
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
ops::{Deref, DerefMut},
|
||||
@@ -271,6 +271,10 @@ impl Inner {
|
||||
warn!("`Window::set_ime_position` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn focus_window(&self) {
|
||||
warn!("`Window::set_focus` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn request_user_attention(&self, _request_type: Option<UserAttentionType>) {
|
||||
warn!("`Window::request_user_attention` is ignored on iOS")
|
||||
}
|
||||
@@ -303,13 +307,11 @@ impl Inner {
|
||||
}
|
||||
|
||||
pub fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
let handle = IOSHandle {
|
||||
ui_window: self.window as _,
|
||||
ui_view: self.view as _,
|
||||
ui_view_controller: self.view_controller as _,
|
||||
..IOSHandle::empty()
|
||||
};
|
||||
RawWindowHandle::IOS(handle)
|
||||
let mut handle = UiKitHandle::empty();
|
||||
handle.ui_window = self.window as _;
|
||||
handle.ui_view = self.view as _;
|
||||
handle.ui_view_controller = self.view_controller as _;
|
||||
RawWindowHandle::UiKit(handle)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,7 +609,7 @@ pub struct WindowId {
|
||||
}
|
||||
|
||||
impl WindowId {
|
||||
pub unsafe fn dummy() -> Self {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
WindowId {
|
||||
window: std::ptr::null_mut(),
|
||||
}
|
||||
|
||||
@@ -118,9 +118,9 @@ impl fmt::Display for OsError {
|
||||
#[cfg(feature = "x11")]
|
||||
OsError::XError(ref e) => _f.pad(&e.description),
|
||||
#[cfg(feature = "x11")]
|
||||
OsError::XMisc(ref e) => _f.pad(e),
|
||||
OsError::XMisc(e) => _f.pad(e),
|
||||
#[cfg(feature = "wayland")]
|
||||
OsError::WaylandMisc(ref e) => _f.pad(e),
|
||||
OsError::WaylandMisc(e) => _f.pad(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,7 +141,7 @@ pub enum WindowId {
|
||||
}
|
||||
|
||||
impl WindowId {
|
||||
pub unsafe fn dummy() -> Self {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
#[cfg(feature = "wayland")]
|
||||
return WindowId::Wayland(wayland::WindowId::dummy());
|
||||
#[cfg(all(not(feature = "wayland"), feature = "x11"))]
|
||||
@@ -158,7 +158,7 @@ pub enum DeviceId {
|
||||
}
|
||||
|
||||
impl DeviceId {
|
||||
pub unsafe fn dummy() -> Self {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
#[cfg(feature = "wayland")]
|
||||
return DeviceId::Wayland(wayland::DeviceId::dummy());
|
||||
#[cfg(all(not(feature = "wayland"), feature = "x11"))]
|
||||
@@ -380,8 +380,7 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn is_maximized(&self) -> bool {
|
||||
// TODO: Not implemented
|
||||
false
|
||||
x11_or_wayland!(match self; Window(w) => w.is_maximized())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -408,9 +407,9 @@ impl Window {
|
||||
pub fn set_always_on_top(&self, _always_on_top: bool) {
|
||||
match self {
|
||||
#[cfg(feature = "x11")]
|
||||
&Window::X(ref w) => w.set_always_on_top(_always_on_top),
|
||||
Window::X(ref w) => w.set_always_on_top(_always_on_top),
|
||||
#[cfg(feature = "wayland")]
|
||||
_ => (),
|
||||
Window::Wayland(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,9 +417,9 @@ impl Window {
|
||||
pub fn set_window_icon(&self, _window_icon: Option<Icon>) {
|
||||
match self {
|
||||
#[cfg(feature = "x11")]
|
||||
&Window::X(ref w) => w.set_window_icon(_window_icon),
|
||||
Window::X(ref w) => w.set_window_icon(_window_icon),
|
||||
#[cfg(feature = "wayland")]
|
||||
_ => (),
|
||||
Window::Wayland(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,12 +429,20 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_user_attention(&self, _request_type: Option<UserAttentionType>) {
|
||||
pub fn focus_window(&self) {
|
||||
match self {
|
||||
#[cfg(feature = "x11")]
|
||||
&Window::X(ref w) => w.request_user_attention(_request_type),
|
||||
Window::X(ref w) => w.focus_window(),
|
||||
#[cfg(feature = "wayland")]
|
||||
_ => (),
|
||||
Window::Wayland(_) => (),
|
||||
}
|
||||
}
|
||||
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
match self {
|
||||
#[cfg(feature = "x11")]
|
||||
Window::X(ref w) => w.request_user_attention(request_type),
|
||||
#[cfg(feature = "wayland")]
|
||||
Window::Wayland(ref w) => w.request_user_attention(request_type),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,14 +455,14 @@ impl Window {
|
||||
pub fn current_monitor(&self) -> Option<RootMonitorHandle> {
|
||||
match self {
|
||||
#[cfg(feature = "x11")]
|
||||
&Window::X(ref window) => {
|
||||
Window::X(ref window) => {
|
||||
let current_monitor = MonitorHandle::X(window.current_monitor());
|
||||
Some(RootMonitorHandle {
|
||||
inner: current_monitor,
|
||||
})
|
||||
}
|
||||
#[cfg(feature = "wayland")]
|
||||
&Window::Wayland(ref window) => {
|
||||
Window::Wayland(ref window) => {
|
||||
let current_monitor = MonitorHandle::Wayland(window.current_monitor()?);
|
||||
Some(RootMonitorHandle {
|
||||
inner: current_monitor,
|
||||
@@ -468,13 +475,13 @@ impl Window {
|
||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||
match self {
|
||||
#[cfg(feature = "x11")]
|
||||
&Window::X(ref window) => window
|
||||
Window::X(ref window) => window
|
||||
.available_monitors()
|
||||
.into_iter()
|
||||
.map(MonitorHandle::X)
|
||||
.collect(),
|
||||
#[cfg(feature = "wayland")]
|
||||
&Window::Wayland(ref window) => window
|
||||
Window::Wayland(ref window) => window
|
||||
.available_monitors()
|
||||
.into_iter()
|
||||
.map(MonitorHandle::Wayland)
|
||||
@@ -486,23 +493,23 @@ impl Window {
|
||||
pub fn primary_monitor(&self) -> Option<RootMonitorHandle> {
|
||||
match self {
|
||||
#[cfg(feature = "x11")]
|
||||
&Window::X(ref window) => {
|
||||
Window::X(ref window) => {
|
||||
let primary_monitor = MonitorHandle::X(window.primary_monitor());
|
||||
Some(RootMonitorHandle {
|
||||
inner: primary_monitor,
|
||||
})
|
||||
}
|
||||
#[cfg(feature = "wayland")]
|
||||
&Window::Wayland(ref window) => window.primary_monitor(),
|
||||
Window::Wayland(ref window) => window.primary_monitor(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
match self {
|
||||
#[cfg(feature = "x11")]
|
||||
&Window::X(ref window) => RawWindowHandle::Xlib(window.raw_window_handle()),
|
||||
Window::X(ref window) => RawWindowHandle::Xlib(window.raw_window_handle()),
|
||||
#[cfg(feature = "wayland")]
|
||||
&Window::Wayland(ref window) => RawWindowHandle::Wayland(window.raw_window_handle()),
|
||||
Window::Wayland(ref window) => RawWindowHandle::Wayland(window.raw_window_handle()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use sctk::reexports::protocols::xdg_shell::client::xdg_wm_base::XdgWmBase;
|
||||
use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1;
|
||||
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_pointer_constraints_v1::ZwpPointerConstraintsV1;
|
||||
use sctk::reexports::protocols::unstable::text_input::v3::client::zwp_text_input_manager_v3::ZwpTextInputManagerV3;
|
||||
use sctk::reexports::protocols::staging::xdg_activation::v1::client::xdg_activation_v1::XdgActivationV1;
|
||||
|
||||
use sctk::environment::{Environment, SimpleGlobal};
|
||||
use sctk::output::{OutputHandler, OutputHandling, OutputInfo, OutputStatusListener};
|
||||
@@ -24,18 +25,27 @@ use sctk::shm::ShmHandler;
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct WindowingFeatures {
|
||||
cursor_grab: bool,
|
||||
xdg_activation: bool,
|
||||
}
|
||||
|
||||
impl WindowingFeatures {
|
||||
/// Create `WindowingFeatures` based on the presented interfaces.
|
||||
pub fn new(env: &Environment<WinitEnv>) -> Self {
|
||||
let cursor_grab = env.get_global::<ZwpPointerConstraintsV1>().is_some();
|
||||
Self { cursor_grab }
|
||||
let xdg_activation = env.get_global::<XdgActivationV1>().is_some();
|
||||
Self {
|
||||
cursor_grab,
|
||||
xdg_activation,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cursor_grab(&self) -> bool {
|
||||
self.cursor_grab
|
||||
}
|
||||
|
||||
pub fn xdg_activation(&self) -> bool {
|
||||
self.xdg_activation
|
||||
}
|
||||
}
|
||||
|
||||
sctk::environment!(WinitEnv,
|
||||
@@ -50,6 +60,7 @@ sctk::environment!(WinitEnv,
|
||||
ZwpRelativePointerManagerV1 => relative_pointer_manager,
|
||||
ZwpPointerConstraintsV1 => pointer_constraints,
|
||||
ZwpTextInputManagerV3 => text_input_manager,
|
||||
XdgActivationV1 => xdg_activation,
|
||||
],
|
||||
multis = [
|
||||
WlSeat => seats,
|
||||
@@ -78,6 +89,8 @@ pub struct WinitEnv {
|
||||
text_input_manager: SimpleGlobal<ZwpTextInputManagerV3>,
|
||||
|
||||
decoration_manager: SimpleGlobal<ZxdgDecorationManagerV1>,
|
||||
|
||||
xdg_activation: SimpleGlobal<XdgActivationV1>,
|
||||
}
|
||||
|
||||
impl WinitEnv {
|
||||
@@ -109,6 +122,9 @@ impl WinitEnv {
|
||||
// IME handling.
|
||||
let text_input_manager = SimpleGlobal::new();
|
||||
|
||||
// Surface activation.
|
||||
let xdg_activation = SimpleGlobal::new();
|
||||
|
||||
Self {
|
||||
seats,
|
||||
outputs,
|
||||
@@ -120,6 +136,7 @@ impl WinitEnv {
|
||||
relative_pointer_manager,
|
||||
pointer_constraints,
|
||||
text_input_manager,
|
||||
xdg_activation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::io::Result as IOResult;
|
||||
use std::process;
|
||||
use std::rc::Rc;
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -18,6 +19,7 @@ use sctk::WaylandSource;
|
||||
use crate::event::{Event, StartCause, WindowEvent};
|
||||
use crate::event_loop::{ControlFlow, EventLoopWindowTarget as RootEventLoopWindowTarget};
|
||||
use crate::platform_impl::platform::sticky_exit_callback;
|
||||
use crate::platform_impl::EventLoopWindowTarget as PlatformEventLoopWindowTarget;
|
||||
|
||||
use super::env::{WindowingFeatures, WinitEnv};
|
||||
use super::output::OutputManager;
|
||||
@@ -34,6 +36,8 @@ pub use state::WinitState;
|
||||
|
||||
use sink::EventSink;
|
||||
|
||||
type WinitDispatcher = calloop::Dispatcher<'static, WaylandSource, WinitState>;
|
||||
|
||||
pub struct EventLoopWindowTarget<T> {
|
||||
/// Wayland display.
|
||||
pub display: Display,
|
||||
@@ -42,7 +46,7 @@ pub struct EventLoopWindowTarget<T> {
|
||||
pub env: Environment<WinitEnv>,
|
||||
|
||||
/// Event loop handle.
|
||||
pub event_loop_handle: calloop::LoopHandle<WinitState>,
|
||||
pub event_loop_handle: calloop::LoopHandle<'static, WinitState>,
|
||||
|
||||
/// Output manager.
|
||||
pub output_manager: OutputManager,
|
||||
@@ -50,8 +54,8 @@ pub struct EventLoopWindowTarget<T> {
|
||||
/// State that we share across callbacks.
|
||||
pub state: RefCell<WinitState>,
|
||||
|
||||
/// Wayland source.
|
||||
pub wayland_source: Rc<calloop::Source<WaylandSource>>,
|
||||
/// Dispatcher of Wayland events.
|
||||
pub wayland_dispatcher: WinitDispatcher,
|
||||
|
||||
/// A proxy to wake up event loop.
|
||||
pub event_loop_awakener: calloop::ping::Ping,
|
||||
@@ -70,7 +74,7 @@ pub struct EventLoopWindowTarget<T> {
|
||||
|
||||
pub struct EventLoop<T: 'static> {
|
||||
/// Event loop.
|
||||
event_loop: calloop::EventLoop<WinitState>,
|
||||
event_loop: calloop::EventLoop<'static, WinitState>,
|
||||
|
||||
/// Wayland display.
|
||||
display: Display,
|
||||
@@ -81,8 +85,8 @@ pub struct EventLoop<T: 'static> {
|
||||
/// Sender of user events.
|
||||
user_events_sender: calloop::channel::Sender<T>,
|
||||
|
||||
/// Wayland source of events.
|
||||
wayland_source: Rc<calloop::Source<WaylandSource>>,
|
||||
/// Dispatcher of Wayland events.
|
||||
pub wayland_dispatcher: WinitDispatcher,
|
||||
|
||||
/// Window target.
|
||||
window_target: RootEventLoopWindowTarget<T>,
|
||||
@@ -102,7 +106,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
let env = Environment::new(&display_proxy, &mut event_queue, WinitEnv::new())?;
|
||||
|
||||
// Create event loop.
|
||||
let event_loop = calloop::EventLoop::<WinitState>::new()?;
|
||||
let event_loop = calloop::EventLoop::<'static, WinitState>::try_new()?;
|
||||
// Build windowing features.
|
||||
let windowing_features = WindowingFeatures::new(&env);
|
||||
|
||||
@@ -116,8 +120,22 @@ impl<T: 'static> EventLoop<T> {
|
||||
let output_manager = OutputManager::new(&env);
|
||||
|
||||
// A source of events that we plug into our event loop.
|
||||
let wayland_source = WaylandSource::new(event_queue).quick_insert(event_loop.handle())?;
|
||||
let wayland_source = Rc::new(wayland_source);
|
||||
let wayland_source = WaylandSource::new(event_queue);
|
||||
let wayland_dispatcher =
|
||||
calloop::Dispatcher::new(wayland_source, |_, queue, winit_state| {
|
||||
queue.dispatch_pending(winit_state, |event, object, _| {
|
||||
panic!(
|
||||
"[calloop] Encountered an orphan event: {}@{} : {}",
|
||||
event.interface,
|
||||
object.as_ref().id(),
|
||||
event.name
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
let _wayland_source_dispatcher = event_loop
|
||||
.handle()
|
||||
.register_dispatcher(wayland_dispatcher.clone())?;
|
||||
|
||||
// A source of user events.
|
||||
let pending_user_events = Rc::new(RefCell::new(Vec::new()));
|
||||
@@ -161,7 +179,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
event_loop_handle,
|
||||
output_manager,
|
||||
event_loop_awakener,
|
||||
wayland_source: wayland_source.clone(),
|
||||
wayland_dispatcher: wayland_dispatcher.clone(),
|
||||
windowing_features,
|
||||
theme_manager,
|
||||
_marker: std::marker::PhantomData,
|
||||
@@ -172,11 +190,11 @@ impl<T: 'static> EventLoop<T> {
|
||||
event_loop,
|
||||
display,
|
||||
pending_user_events,
|
||||
wayland_source,
|
||||
wayland_dispatcher,
|
||||
_seat_manager: seat_manager,
|
||||
user_events_sender,
|
||||
window_target: RootEventLoopWindowTarget {
|
||||
p: crate::platform_impl::EventLoopWindowTarget::Wayland(event_loop_window_target),
|
||||
p: PlatformEventLoopWindowTarget::Wayland(event_loop_window_target),
|
||||
_marker: std::marker::PhantomData,
|
||||
},
|
||||
};
|
||||
@@ -243,7 +261,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
for (window_id, window_update) in window_updates.iter_mut() {
|
||||
if let Some(scale_factor) = window_update.scale_factor.map(|f| f as f64) {
|
||||
let mut physical_size = self.with_state(|state| {
|
||||
let window_handle = state.window_map.get(&window_id).unwrap();
|
||||
let window_handle = state.window_map.get(window_id).unwrap();
|
||||
let mut size = window_handle.size.lock().unwrap();
|
||||
|
||||
// Update the new logical size if it was changed.
|
||||
@@ -276,7 +294,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
if let Some(size) = window_update.size.take() {
|
||||
let physical_size = self.with_state(|state| {
|
||||
let window_handle = state.window_map.get_mut(&window_id).unwrap();
|
||||
let window_handle = state.window_map.get_mut(window_id).unwrap();
|
||||
let mut window_size = window_handle.size.lock().unwrap();
|
||||
|
||||
// Always issue resize event on scale factor change.
|
||||
@@ -287,7 +305,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
} else {
|
||||
*window_size = size;
|
||||
let scale_factor =
|
||||
sctk::get_surface_scale_factor(&window_handle.window.surface());
|
||||
sctk::get_surface_scale_factor(window_handle.window.surface());
|
||||
let physical_size = size.to_physical(scale_factor as f64);
|
||||
Some(physical_size)
|
||||
};
|
||||
@@ -363,7 +381,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
// Handle refresh of the frame.
|
||||
if window_update.refresh_frame {
|
||||
self.with_state(|state| {
|
||||
let window_handle = state.window_map.get_mut(&window_id).unwrap();
|
||||
let window_handle = state.window_map.get_mut(window_id).unwrap();
|
||||
window_handle.window.refresh();
|
||||
if !window_update.redraw_requested {
|
||||
window_handle.window.surface().commit();
|
||||
@@ -403,16 +421,17 @@ impl<T: 'static> EventLoop<T> {
|
||||
// woken up by messages arriving from the Wayland socket, to avoid delaying the
|
||||
// dispatch of these events until we're woken up again.
|
||||
let instant_wakeup = {
|
||||
let handle = self.event_loop.handle();
|
||||
let source = self.wayland_source.clone();
|
||||
let dispatched = handle.with_source(&source, |wayland_source| {
|
||||
let queue = wayland_source.queue();
|
||||
self.with_state(|state| {
|
||||
queue.dispatch_pending(state, |_, _, _| unimplemented!())
|
||||
})
|
||||
});
|
||||
let mut wayland_source = self.wayland_dispatcher.as_source_mut();
|
||||
let queue = wayland_source.queue();
|
||||
let state = match &mut self.window_target.p {
|
||||
PlatformEventLoopWindowTarget::Wayland(window_target) => {
|
||||
window_target.state.get_mut()
|
||||
}
|
||||
#[cfg(feature = "x11")]
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if let Ok(dispatched) = dispatched {
|
||||
if let Ok(dispatched) = queue.dispatch_pending(state, |_, _, _| unimplemented!()) {
|
||||
dispatched > 0
|
||||
} else {
|
||||
break;
|
||||
@@ -508,9 +527,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
fn with_state<U, F: FnOnce(&mut WinitState) -> U>(&mut self, f: F) -> U {
|
||||
let state = match &mut self.window_target.p {
|
||||
crate::platform_impl::EventLoopWindowTarget::Wayland(ref mut window_target) => {
|
||||
window_target.state.get_mut()
|
||||
}
|
||||
PlatformEventLoopWindowTarget::Wayland(window_target) => window_target.state.get_mut(),
|
||||
#[cfg(feature = "x11")]
|
||||
_ => unreachable!(),
|
||||
};
|
||||
@@ -518,18 +535,13 @@ impl<T: 'static> EventLoop<T> {
|
||||
f(state)
|
||||
}
|
||||
|
||||
fn loop_dispatch<D: Into<Option<std::time::Duration>>>(
|
||||
&mut self,
|
||||
timeout: D,
|
||||
) -> std::io::Result<()> {
|
||||
let mut state = match &mut self.window_target.p {
|
||||
crate::platform_impl::EventLoopWindowTarget::Wayland(ref mut window_target) => {
|
||||
window_target.state.get_mut()
|
||||
}
|
||||
fn loop_dispatch<D: Into<Option<std::time::Duration>>>(&mut self, timeout: D) -> IOResult<()> {
|
||||
let state = match &mut self.window_target.p {
|
||||
PlatformEventLoopWindowTarget::Wayland(window_target) => window_target.state.get_mut(),
|
||||
#[cfg(feature = "x11")]
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
self.event_loop.dispatch(timeout, &mut state)
|
||||
self.event_loop.dispatch(timeout, state)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ mod window;
|
||||
pub struct DeviceId;
|
||||
|
||||
impl DeviceId {
|
||||
pub unsafe fn dummy() -> Self {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
DeviceId
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ impl DeviceId {
|
||||
pub struct WindowId(usize);
|
||||
|
||||
impl WindowId {
|
||||
pub unsafe fn dummy() -> Self {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
WindowId(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ impl Eq for MonitorHandle {}
|
||||
|
||||
impl PartialOrd for MonitorHandle {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(&other))
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ use sctk::reexports::client::protocol::wl_keyboard::WlKeyboard;
|
||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||
use sctk::reexports::client::Attached;
|
||||
|
||||
use sctk::reexports::calloop::{LoopHandle, Source};
|
||||
use sctk::reexports::calloop::{LoopHandle, RegistrationToken};
|
||||
|
||||
use sctk::seat::keyboard::{self, RepeatSource};
|
||||
use sctk::seat::keyboard;
|
||||
|
||||
use crate::event::ModifiersState;
|
||||
use crate::platform_impl::wayland::event_loop::WinitState;
|
||||
@@ -22,22 +22,22 @@ pub(crate) struct Keyboard {
|
||||
pub keyboard: WlKeyboard,
|
||||
|
||||
/// The source for repeat keys.
|
||||
pub repeat_source: Option<Source<RepeatSource>>,
|
||||
pub repeat_token: Option<RegistrationToken>,
|
||||
|
||||
/// LoopHandle to drop `RepeatSource`, when dropping the keyboard.
|
||||
pub loop_handle: LoopHandle<WinitState>,
|
||||
pub loop_handle: LoopHandle<'static, WinitState>,
|
||||
}
|
||||
|
||||
impl Keyboard {
|
||||
pub fn new(
|
||||
seat: &Attached<WlSeat>,
|
||||
loop_handle: LoopHandle<WinitState>,
|
||||
loop_handle: LoopHandle<'static, WinitState>,
|
||||
modifiers_state: Rc<RefCell<ModifiersState>>,
|
||||
) -> Option<Self> {
|
||||
let mut inner = KeyboardInner::new(modifiers_state);
|
||||
let keyboard_data = keyboard::map_keyboard_repeat(
|
||||
loop_handle.clone(),
|
||||
&seat,
|
||||
seat,
|
||||
None,
|
||||
keyboard::RepeatKind::System,
|
||||
move |event, _, mut dispatch_data| {
|
||||
@@ -46,12 +46,12 @@ impl Keyboard {
|
||||
},
|
||||
);
|
||||
|
||||
let (keyboard, repeat_source) = keyboard_data.ok()?;
|
||||
let (keyboard, repeat_token) = keyboard_data.ok()?;
|
||||
|
||||
Some(Self {
|
||||
keyboard,
|
||||
loop_handle,
|
||||
repeat_source: Some(repeat_source),
|
||||
repeat_token: Some(repeat_token),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -62,8 +62,8 @@ impl Drop for Keyboard {
|
||||
self.keyboard.release();
|
||||
}
|
||||
|
||||
if let Some(repeat_source) = self.repeat_source.take() {
|
||||
self.loop_handle.remove(repeat_source);
|
||||
if let Some(repeat_token) = self.repeat_token.take() {
|
||||
self.loop_handle.remove(repeat_token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ pub struct SeatManager {
|
||||
impl SeatManager {
|
||||
pub fn new(
|
||||
env: &Environment<WinitEnv>,
|
||||
loop_handle: LoopHandle<WinitState>,
|
||||
loop_handle: LoopHandle<'static, WinitState>,
|
||||
theme_manager: ThemeManager,
|
||||
) -> Self {
|
||||
let relative_pointer_manager = env.get_global::<ZwpRelativePointerManagerV1>();
|
||||
@@ -63,7 +63,7 @@ impl SeatManager {
|
||||
}
|
||||
|
||||
let seat_listener = env.listen_for_seats(move |seat, seat_data, _| {
|
||||
inner.process_seat_update(&seat, &seat_data);
|
||||
inner.process_seat_update(&seat, seat_data);
|
||||
});
|
||||
|
||||
Self {
|
||||
@@ -78,7 +78,7 @@ struct SeatManagerInner {
|
||||
seats: Vec<SeatInfo>,
|
||||
|
||||
/// Loop handle.
|
||||
loop_handle: LoopHandle<WinitState>,
|
||||
loop_handle: LoopHandle<'static, WinitState>,
|
||||
|
||||
/// Relative pointer manager.
|
||||
relative_pointer_manager: Option<Attached<ZwpRelativePointerManagerV1>>,
|
||||
@@ -99,7 +99,7 @@ impl SeatManagerInner {
|
||||
relative_pointer_manager: Option<Attached<ZwpRelativePointerManagerV1>>,
|
||||
pointer_constraints: Option<Attached<ZwpPointerConstraintsV1>>,
|
||||
text_input_manager: Option<Attached<ZwpTextInputManagerV3>>,
|
||||
loop_handle: LoopHandle<WinitState>,
|
||||
loop_handle: LoopHandle<'static, WinitState>,
|
||||
) -> Self {
|
||||
Self {
|
||||
seats: Vec::new(),
|
||||
@@ -127,7 +127,7 @@ impl SeatManagerInner {
|
||||
if seat_data.has_pointer && !seat_data.defunct {
|
||||
if seat_info.pointer.is_none() {
|
||||
seat_info.pointer = Some(Pointers::new(
|
||||
&seat,
|
||||
seat,
|
||||
&self.theme_manager,
|
||||
&self.relative_pointer_manager,
|
||||
&self.pointer_constraints,
|
||||
@@ -142,7 +142,7 @@ impl SeatManagerInner {
|
||||
if seat_data.has_keyboard && !seat_data.defunct {
|
||||
if seat_info.keyboard.is_none() {
|
||||
seat_info.keyboard = Keyboard::new(
|
||||
&seat,
|
||||
seat,
|
||||
self.loop_handle.clone(),
|
||||
seat_info.modifiers_state.clone(),
|
||||
);
|
||||
@@ -154,7 +154,7 @@ impl SeatManagerInner {
|
||||
// Handle touch.
|
||||
if seat_data.has_touch && !seat_data.defunct {
|
||||
if seat_info.touch.is_none() {
|
||||
seat_info.touch = Some(Touch::new(&seat));
|
||||
seat_info.touch = Some(Touch::new(seat));
|
||||
}
|
||||
} else {
|
||||
seat_info.touch = None;
|
||||
@@ -165,7 +165,7 @@ impl SeatManagerInner {
|
||||
if seat_data.defunct {
|
||||
seat_info.text_input = None;
|
||||
} else if seat_info.text_input.is_none() {
|
||||
seat_info.text_input = Some(TextInput::new(&seat, &text_input_manager));
|
||||
seat_info.text_input = Some(TextInput::new(seat, text_input_manager));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ pub(super) fn handle_pointer(
|
||||
|
||||
let window_id = wayland::make_wid(surface);
|
||||
|
||||
let scale_factor = sctk::get_surface_scale_factor(&surface) as f64;
|
||||
let scale_factor = sctk::get_surface_scale_factor(surface) as f64;
|
||||
let position = LogicalPosition::new(surface_x, surface_y).to_physical(scale_factor);
|
||||
|
||||
event_sink.push_window_event(
|
||||
@@ -186,7 +186,7 @@ pub(super) fn handle_pointer(
|
||||
None => return,
|
||||
};
|
||||
|
||||
let window_id = wayland::make_wid(&surface);
|
||||
let window_id = wayland::make_wid(surface);
|
||||
|
||||
if pointer.as_ref().version() < 5 {
|
||||
let (mut x, mut y) = (0.0, 0.0);
|
||||
@@ -199,7 +199,7 @@ pub(super) fn handle_pointer(
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let scale_factor = sctk::get_surface_scale_factor(&surface) as f64;
|
||||
let scale_factor = sctk::get_surface_scale_factor(surface) as f64;
|
||||
let delta = LogicalPosition::new(x as f64, y as f64).to_physical(scale_factor);
|
||||
|
||||
event_sink.push_window_event(
|
||||
@@ -262,7 +262,7 @@ pub(super) fn handle_pointer(
|
||||
Some(surface) => surface,
|
||||
None => return,
|
||||
};
|
||||
let window_id = wayland::make_wid(&surface);
|
||||
let window_id = wayland::make_wid(surface);
|
||||
|
||||
let window_event = if let Some((x, y)) = axis_discrete_buffer {
|
||||
WindowEvent::MouseWheel {
|
||||
@@ -274,7 +274,7 @@ pub(super) fn handle_pointer(
|
||||
modifiers: *pointer_data.modifiers_state.borrow(),
|
||||
}
|
||||
} else if let Some((x, y)) = axis_buffer {
|
||||
let scale_factor = sctk::get_surface_scale_factor(&surface) as f64;
|
||||
let scale_factor = sctk::get_surface_scale_factor(surface) as f64;
|
||||
let delta = LogicalPosition::new(x, y).to_physical(scale_factor);
|
||||
|
||||
WindowEvent::MouseWheel {
|
||||
|
||||
@@ -13,7 +13,7 @@ use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_p
|
||||
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_confined_pointer_v1::ZwpConfinedPointerV1;
|
||||
|
||||
use sctk::seat::pointer::{ThemeManager, ThemedPointer};
|
||||
use sctk::window::{ConceptFrame, Window};
|
||||
use sctk::window::{FallbackFrame, Window};
|
||||
|
||||
use crate::event::ModifiersState;
|
||||
use crate::platform_impl::wayland::event_loop::WinitState;
|
||||
@@ -70,7 +70,7 @@ impl WinitPointer {
|
||||
CursorIcon::Copy => &["copy"],
|
||||
CursorIcon::Crosshair => &["crosshair"],
|
||||
CursorIcon::Default => &["left_ptr"],
|
||||
CursorIcon::Hand => &["hand"],
|
||||
CursorIcon::Hand => &["hand2", "hand1"],
|
||||
CursorIcon::Help => &["question_arrow"],
|
||||
CursorIcon::Move => &["move"],
|
||||
CursorIcon::Grab => &["openhand", "grab"],
|
||||
@@ -93,8 +93,8 @@ impl WinitPointer {
|
||||
CursorIcon::WResize => &["left_side"],
|
||||
CursorIcon::EwResize => &["h_double_arrow"],
|
||||
CursorIcon::NsResize => &["v_double_arrow"],
|
||||
CursorIcon::NwseResize => &["bd_double_arrow", "size_bdiag"],
|
||||
CursorIcon::NeswResize => &["fd_double_arrow", "size_fdiag"],
|
||||
CursorIcon::NwseResize => &["bd_double_arrow", "size_fdiag"],
|
||||
CursorIcon::NeswResize => &["fd_double_arrow", "size_bdiag"],
|
||||
CursorIcon::ColResize => &["split_h", "h_double_arrow"],
|
||||
CursorIcon::RowResize => &["split_v", "v_double_arrow"],
|
||||
CursorIcon::Text => &["text", "xterm"],
|
||||
@@ -109,9 +109,10 @@ impl WinitPointer {
|
||||
let serial = Some(self.latest_serial.get());
|
||||
for cursor in cursors {
|
||||
if self.pointer.set_cursor(cursor, serial).is_ok() {
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
warn!("Failed to set cursor to {:?}", cursor_icon);
|
||||
}
|
||||
|
||||
/// Confine the pointer to a surface.
|
||||
@@ -128,8 +129,8 @@ impl WinitPointer {
|
||||
};
|
||||
|
||||
*confined_pointer.borrow_mut() = Some(init_confined_pointer(
|
||||
&pointer_constraints,
|
||||
&surface,
|
||||
pointer_constraints,
|
||||
surface,
|
||||
&*self.pointer,
|
||||
));
|
||||
}
|
||||
@@ -149,7 +150,7 @@ impl WinitPointer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drag_window(&self, window: &Window<ConceptFrame>) {
|
||||
pub fn drag_window(&self, window: &Window<FallbackFrame>) {
|
||||
window.start_interactive_move(&self.seat, self.latest_serial.get());
|
||||
}
|
||||
}
|
||||
@@ -196,12 +197,11 @@ impl Pointers {
|
||||
);
|
||||
|
||||
// Setup relative_pointer if it's available.
|
||||
let relative_pointer = match relative_pointer_manager.as_ref() {
|
||||
Some(relative_pointer_manager) => {
|
||||
Some(init_relative_pointer(&relative_pointer_manager, &*pointer))
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let relative_pointer = relative_pointer_manager
|
||||
.as_ref()
|
||||
.map(|relative_pointer_manager| {
|
||||
init_relative_pointer(relative_pointer_manager, &*pointer)
|
||||
});
|
||||
|
||||
Self {
|
||||
pointer,
|
||||
@@ -249,7 +249,7 @@ pub(super) fn init_confined_pointer(
|
||||
pointer: &WlPointer,
|
||||
) -> ZwpConfinedPointerV1 {
|
||||
let confined_pointer =
|
||||
pointer_constraints.confine_pointer(surface, pointer, None, Lifetime::Persistent.to_raw());
|
||||
pointer_constraints.confine_pointer(surface, pointer, None, Lifetime::Persistent);
|
||||
|
||||
confined_pointer.quick_assign(move |_, _, _| {});
|
||||
|
||||
|
||||
@@ -7,21 +7,17 @@ use sctk::reexports::client::Display;
|
||||
|
||||
use sctk::reexports::calloop;
|
||||
|
||||
use sctk::window::{
|
||||
ARGBColor, ButtonColorSpec, ColorSpec, ConceptConfig, ConceptFrame, Decorations,
|
||||
};
|
||||
|
||||
use raw_window_handle::unix::WaylandHandle;
|
||||
use raw_window_handle::WaylandHandle;
|
||||
use sctk::window::{Decorations, FallbackFrame};
|
||||
|
||||
use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
|
||||
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||
use crate::platform::unix::{ARGBColor as LocalARGBColor, Button, ButtonState, Element, Theme};
|
||||
use crate::platform_impl::{
|
||||
MonitorHandle as PlatformMonitorHandle, OsError,
|
||||
PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
|
||||
};
|
||||
use crate::window::{CursorIcon, Fullscreen, WindowAttributes};
|
||||
use crate::window::{CursorIcon, Fullscreen, UserAttentionType, WindowAttributes};
|
||||
|
||||
use super::env::WindowingFeatures;
|
||||
use super::event_loop::WinitState;
|
||||
@@ -54,6 +50,9 @@ pub struct Window {
|
||||
/// Fullscreen state.
|
||||
fullscreen: Arc<AtomicBool>,
|
||||
|
||||
/// Maximized state.
|
||||
maximized: Arc<AtomicBool>,
|
||||
|
||||
/// Available windowing features.
|
||||
windowing_features: WindowingFeatures,
|
||||
|
||||
@@ -87,6 +86,8 @@ impl Window {
|
||||
let scale_factor = sctk::get_surface_scale_factor(&surface);
|
||||
|
||||
let window_id = super::make_wid(&surface);
|
||||
let maximized = Arc::new(AtomicBool::new(false));
|
||||
let maximzied_clone = maximized.clone();
|
||||
let fullscreen = Arc::new(AtomicBool::new(false));
|
||||
let fullscreen_clone = fullscreen.clone();
|
||||
|
||||
@@ -98,7 +99,7 @@ impl Window {
|
||||
let theme_manager = event_loop_window_target.theme_manager.clone();
|
||||
let mut window = event_loop_window_target
|
||||
.env
|
||||
.create_window::<ConceptFrame, _>(
|
||||
.create_window::<FallbackFrame, _>(
|
||||
surface.clone(),
|
||||
Some(theme_manager),
|
||||
(width, height),
|
||||
@@ -113,6 +114,8 @@ impl Window {
|
||||
window_update.refresh_frame = true;
|
||||
}
|
||||
Event::Configure { new_size, states } => {
|
||||
let is_maximized = states.contains(&State::Maximized);
|
||||
maximzied_clone.store(is_maximized, Ordering::Relaxed);
|
||||
let is_fullscreen = states.contains(&State::Fullscreen);
|
||||
fullscreen_clone.store(is_fullscreen, Ordering::Relaxed);
|
||||
|
||||
@@ -136,6 +139,12 @@ impl Window {
|
||||
} else {
|
||||
window.set_decorate(Decorations::None);
|
||||
}
|
||||
// Without this commit here at least on kwin 5.23.3 the initial configure
|
||||
// will have a size (1,1), the second configure including the decoration
|
||||
// mode will have the min_size as its size. With this commit the initial
|
||||
// configure will have no size, the application will draw it's content
|
||||
// with the initial size and everything works as expected afterwards.
|
||||
window.surface().commit();
|
||||
|
||||
// Min dimensions.
|
||||
let min_size = attributes
|
||||
@@ -194,7 +203,12 @@ impl Window {
|
||||
let window_requests = Arc::new(Mutex::new(Vec::with_capacity(64)));
|
||||
|
||||
// Create a handle that performs all the requests on underlying sctk a window.
|
||||
let window_handle = WindowHandle::new(window, size.clone(), window_requests.clone());
|
||||
let window_handle = WindowHandle::new(
|
||||
&event_loop_window_target.env,
|
||||
window,
|
||||
size.clone(),
|
||||
window_requests.clone(),
|
||||
);
|
||||
|
||||
let mut winit_state = event_loop_window_target.state.borrow_mut();
|
||||
|
||||
@@ -206,17 +220,14 @@ impl Window {
|
||||
|
||||
let windowing_features = event_loop_window_target.windowing_features;
|
||||
|
||||
// Send all updates to the server.
|
||||
let wayland_source = &event_loop_window_target.wayland_source;
|
||||
let event_loop_handle = &event_loop_window_target.event_loop_handle;
|
||||
|
||||
// To make our window usable for drawing right away we must `ack` a `configure`
|
||||
// from the server, the acking part here is done by SCTK window frame, so we just
|
||||
// need to sync with server so it'll be done automatically for us.
|
||||
event_loop_handle.with_source(&wayland_source, |event_queue| {
|
||||
let event_queue = event_queue.queue();
|
||||
{
|
||||
let mut wayland_source = event_loop_window_target.wayland_dispatcher.as_source_mut();
|
||||
let event_queue = wayland_source.queue();
|
||||
let _ = event_queue.sync_roundtrip(&mut *winit_state, |_, _, _| unreachable!());
|
||||
});
|
||||
}
|
||||
|
||||
// We all praise GNOME for these 3 lines of pure magic. If we don't do that,
|
||||
// GNOME will shrink our window a bit for the size of the decorations. I guess it
|
||||
@@ -235,6 +246,7 @@ impl Window {
|
||||
window_requests,
|
||||
event_loop_awakener: event_loop_window_target.event_loop_awakener.clone(),
|
||||
fullscreen,
|
||||
maximized,
|
||||
windowing_features,
|
||||
};
|
||||
|
||||
@@ -250,9 +262,7 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn set_title(&self, title: &str) {
|
||||
let title_request = WindowRequest::Title(title.to_owned());
|
||||
self.window_requests.lock().unwrap().push(title_request);
|
||||
self.event_loop_awakener.ping();
|
||||
self.send_request(WindowRequest::Title(title.to_owned()));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -284,9 +294,7 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn request_redraw(&self) {
|
||||
let redraw_request = WindowRequest::Redraw;
|
||||
self.window_requests.lock().unwrap().push(redraw_request);
|
||||
self.event_loop_awakener.ping();
|
||||
self.send_request(WindowRequest::Redraw);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -304,12 +312,7 @@ impl Window {
|
||||
let size = size.to_logical::<u32>(scale_factor);
|
||||
*self.size.lock().unwrap() = size;
|
||||
|
||||
let frame_size_request = WindowRequest::FrameSize(size);
|
||||
self.window_requests
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(frame_size_request);
|
||||
self.event_loop_awakener.ping();
|
||||
self.send_request(WindowRequest::FrameSize(size));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -317,9 +320,7 @@ impl Window {
|
||||
let scale_factor = self.scale_factor() as f64;
|
||||
let size = dimensions.map(|size| size.to_logical::<u32>(scale_factor));
|
||||
|
||||
let min_size_request = WindowRequest::MinSize(size);
|
||||
self.window_requests.lock().unwrap().push(min_size_request);
|
||||
self.event_loop_awakener.ping();
|
||||
self.send_request(WindowRequest::MinSize(size));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -327,19 +328,12 @@ impl Window {
|
||||
let scale_factor = self.scale_factor() as f64;
|
||||
let size = dimensions.map(|size| size.to_logical::<u32>(scale_factor));
|
||||
|
||||
let max_size_request = WindowRequest::MaxSize(size);
|
||||
self.window_requests.lock().unwrap().push(max_size_request);
|
||||
self.event_loop_awakener.ping();
|
||||
self.send_request(WindowRequest::MaxSize(size));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
let resizeable_request = WindowRequest::Resizeable(resizable);
|
||||
self.window_requests
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(resizeable_request);
|
||||
self.event_loop_awakener.ping();
|
||||
self.send_request(WindowRequest::Resizeable(resizable));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -351,9 +345,7 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, decorate: bool) {
|
||||
let decorate_request = WindowRequest::Decorate(decorate);
|
||||
self.window_requests.lock().unwrap().push(decorate_request);
|
||||
self.event_loop_awakener.ping();
|
||||
self.send_request(WindowRequest::Decorate(decorate));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -363,16 +355,17 @@ impl Window {
|
||||
return;
|
||||
}
|
||||
|
||||
let minimize_request = WindowRequest::Minimize;
|
||||
self.window_requests.lock().unwrap().push(minimize_request);
|
||||
self.event_loop_awakener.ping();
|
||||
self.send_request(WindowRequest::Minimize);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_maximized(&self) -> bool {
|
||||
self.maximized.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
let maximize_request = WindowRequest::Maximize(maximized);
|
||||
self.window_requests.lock().unwrap().push(maximize_request);
|
||||
self.event_loop_awakener.ping();
|
||||
self.send_request(WindowRequest::Maximize(maximized));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -408,154 +401,17 @@ impl Window {
|
||||
None => WindowRequest::UnsetFullscreen,
|
||||
};
|
||||
|
||||
self.window_requests
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(fullscreen_request);
|
||||
self.event_loop_awakener.ping();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_theme<T: Theme>(&self, theme: T) {
|
||||
// First buttons is minimize, then maximize, and then close.
|
||||
let buttons: Vec<(ButtonColorSpec, ButtonColorSpec)> =
|
||||
[Button::Minimize, Button::Maximize, Button::Close]
|
||||
.iter()
|
||||
.map(|button| {
|
||||
let button = *button;
|
||||
let idle_active_bg = theme
|
||||
.button_color(button, ButtonState::Idle, false, true)
|
||||
.into();
|
||||
let idle_inactive_bg = theme
|
||||
.button_color(button, ButtonState::Idle, false, false)
|
||||
.into();
|
||||
let idle_active_icon = theme
|
||||
.button_color(button, ButtonState::Idle, true, true)
|
||||
.into();
|
||||
let idle_inactive_icon = theme
|
||||
.button_color(button, ButtonState::Idle, true, false)
|
||||
.into();
|
||||
let idle_bg = ColorSpec {
|
||||
active: idle_active_bg,
|
||||
inactive: idle_inactive_bg,
|
||||
};
|
||||
let idle_icon = ColorSpec {
|
||||
active: idle_active_icon,
|
||||
inactive: idle_inactive_icon,
|
||||
};
|
||||
|
||||
let hovered_active_bg = theme
|
||||
.button_color(button, ButtonState::Hovered, false, true)
|
||||
.into();
|
||||
let hovered_inactive_bg = theme
|
||||
.button_color(button, ButtonState::Hovered, false, false)
|
||||
.into();
|
||||
let hovered_active_icon = theme
|
||||
.button_color(button, ButtonState::Hovered, true, true)
|
||||
.into();
|
||||
let hovered_inactive_icon = theme
|
||||
.button_color(button, ButtonState::Hovered, true, false)
|
||||
.into();
|
||||
let hovered_bg = ColorSpec {
|
||||
active: hovered_active_bg,
|
||||
inactive: hovered_inactive_bg,
|
||||
};
|
||||
let hovered_icon = ColorSpec {
|
||||
active: hovered_active_icon,
|
||||
inactive: hovered_inactive_icon,
|
||||
};
|
||||
|
||||
let disabled_active_bg = theme
|
||||
.button_color(button, ButtonState::Disabled, false, true)
|
||||
.into();
|
||||
let disabled_inactive_bg = theme
|
||||
.button_color(button, ButtonState::Disabled, false, false)
|
||||
.into();
|
||||
let disabled_active_icon = theme
|
||||
.button_color(button, ButtonState::Disabled, true, true)
|
||||
.into();
|
||||
let disabled_inactive_icon = theme
|
||||
.button_color(button, ButtonState::Disabled, true, false)
|
||||
.into();
|
||||
let disabled_bg = ColorSpec {
|
||||
active: disabled_active_bg,
|
||||
inactive: disabled_inactive_bg,
|
||||
};
|
||||
let disabled_icon = ColorSpec {
|
||||
active: disabled_active_icon,
|
||||
inactive: disabled_inactive_icon,
|
||||
};
|
||||
|
||||
let button_bg = ButtonColorSpec {
|
||||
idle: idle_bg,
|
||||
hovered: hovered_bg,
|
||||
disabled: disabled_bg,
|
||||
};
|
||||
let button_icon = ButtonColorSpec {
|
||||
idle: idle_icon,
|
||||
hovered: hovered_icon,
|
||||
disabled: disabled_icon,
|
||||
};
|
||||
|
||||
(button_icon, button_bg)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let minimize_button = Some(buttons[0]);
|
||||
let maximize_button = Some(buttons[1]);
|
||||
let close_button = Some(buttons[2]);
|
||||
|
||||
// The first color is bar, then separator, and then text color.
|
||||
let titlebar_colors: Vec<ColorSpec> = [Element::Bar, Element::Separator, Element::Text]
|
||||
.iter()
|
||||
.map(|element| {
|
||||
let element = *element;
|
||||
let active = theme.element_color(element, true).into();
|
||||
let inactive = theme.element_color(element, false).into();
|
||||
|
||||
ColorSpec { active, inactive }
|
||||
})
|
||||
.collect();
|
||||
|
||||
let primary_color = titlebar_colors[0];
|
||||
let secondary_color = titlebar_colors[1];
|
||||
let title_color = titlebar_colors[2];
|
||||
|
||||
let title_font = theme.font();
|
||||
|
||||
let concept_config = ConceptConfig {
|
||||
primary_color,
|
||||
secondary_color,
|
||||
title_color,
|
||||
title_font,
|
||||
minimize_button,
|
||||
maximize_button,
|
||||
close_button,
|
||||
};
|
||||
|
||||
let theme_request = WindowRequest::Theme(concept_config);
|
||||
self.window_requests.lock().unwrap().push(theme_request);
|
||||
self.event_loop_awakener.ping();
|
||||
self.send_request(fullscreen_request);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
|
||||
let cursor_icon_request = WindowRequest::NewCursorIcon(cursor);
|
||||
self.window_requests
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(cursor_icon_request);
|
||||
self.event_loop_awakener.ping();
|
||||
self.send_request(WindowRequest::NewCursorIcon(cursor));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_visible(&self, visible: bool) {
|
||||
let cursor_visible_request = WindowRequest::ShowCursor(visible);
|
||||
self.window_requests
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(cursor_visible_request);
|
||||
self.event_loop_awakener.ping();
|
||||
self.send_request(WindowRequest::ShowCursor(visible));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -564,16 +420,20 @@ impl Window {
|
||||
return Err(ExternalError::NotSupported(NotSupportedError::new()));
|
||||
}
|
||||
|
||||
let cursor_grab_request = WindowRequest::GrabCursor(grab);
|
||||
self.window_requests
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(cursor_grab_request);
|
||||
self.event_loop_awakener.ping();
|
||||
self.send_request(WindowRequest::GrabCursor(grab));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
if !self.windowing_features.xdg_activation() {
|
||||
warn!("`request_user_attention` isn't supported");
|
||||
return;
|
||||
}
|
||||
|
||||
self.send_request(WindowRequest::Attention(request_type));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, _: Position) -> Result<(), ExternalError> {
|
||||
// XXX This is possible if the locked pointer is being used. We don't have any
|
||||
@@ -588,12 +448,7 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
let drag_window_request = WindowRequest::DragWindow;
|
||||
self.window_requests
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(drag_window_request);
|
||||
self.event_loop_awakener.ping();
|
||||
self.send_request(WindowRequest::DragWindow);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -602,12 +457,7 @@ impl Window {
|
||||
pub fn set_ime_position(&self, position: Position) {
|
||||
let scale_factor = self.scale_factor() as f64;
|
||||
let position = position.to_logical(scale_factor);
|
||||
let ime_position_request = WindowRequest::IMEPosition(position);
|
||||
self.window_requests
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(ime_position_request);
|
||||
self.event_loop_awakener.ping();
|
||||
self.send_request(WindowRequest::IMEPosition(position));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -638,31 +488,21 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn raw_window_handle(&self) -> WaylandHandle {
|
||||
let display = self.display.get_display_ptr() as *mut _;
|
||||
let surface = self.surface.as_ref().c_ptr() as *mut _;
|
||||
|
||||
WaylandHandle {
|
||||
display,
|
||||
surface,
|
||||
..WaylandHandle::empty()
|
||||
}
|
||||
let mut handle = WaylandHandle::empty();
|
||||
handle.display = self.display.get_display_ptr() as *mut _;
|
||||
handle.surface = self.surface.as_ref().c_ptr() as *mut _;
|
||||
handle
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LocalARGBColor> for ARGBColor {
|
||||
fn from(color: LocalARGBColor) -> Self {
|
||||
let a = color.a;
|
||||
let r = color.r;
|
||||
let g = color.g;
|
||||
let b = color.b;
|
||||
Self { a, r, g, b }
|
||||
#[inline]
|
||||
fn send_request(&self, request: WindowRequest) {
|
||||
self.window_requests.lock().unwrap().push(request);
|
||||
self.event_loop_awakener.ping();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
let close_request = WindowRequest::Close;
|
||||
self.window_requests.lock().unwrap().push(close_request);
|
||||
self.event_loop_awakener.ping();
|
||||
self.send_request(WindowRequest::Close);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,23 @@ use std::cell::Cell;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use sctk::reexports::client::protocol::wl_output::WlOutput;
|
||||
use sctk::reexports::client::Attached;
|
||||
use sctk::reexports::protocols::staging::xdg_activation::v1::client::xdg_activation_token_v1;
|
||||
use sctk::reexports::protocols::staging::xdg_activation::v1::client::xdg_activation_v1::XdgActivationV1;
|
||||
|
||||
use sctk::window::{ConceptConfig, ConceptFrame, Decorations, Window};
|
||||
use sctk::environment::Environment;
|
||||
use sctk::window::{Decorations, FallbackFrame, Window};
|
||||
|
||||
use crate::dpi::{LogicalPosition, LogicalSize};
|
||||
|
||||
use crate::event::WindowEvent;
|
||||
use crate::platform_impl::wayland;
|
||||
use crate::platform_impl::wayland::env::WinitEnv;
|
||||
use crate::platform_impl::wayland::event_loop::WinitState;
|
||||
use crate::platform_impl::wayland::seat::pointer::WinitPointer;
|
||||
use crate::platform_impl::wayland::seat::text_input::TextInputHandler;
|
||||
use crate::platform_impl::wayland::WindowId;
|
||||
use crate::window::CursorIcon;
|
||||
use crate::window::{CursorIcon, UserAttentionType};
|
||||
|
||||
/// A request to SCTK window from Winit window.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -64,12 +70,14 @@ pub enum WindowRequest {
|
||||
/// Set IME window position.
|
||||
IMEPosition(LogicalPosition<u32>),
|
||||
|
||||
/// Request Attention.
|
||||
///
|
||||
/// `None` unsets the attention request.
|
||||
Attention(Option<UserAttentionType>),
|
||||
|
||||
/// Redraw was requested.
|
||||
Redraw,
|
||||
|
||||
/// A new theme for a concept frame was requested.
|
||||
Theme(ConceptConfig),
|
||||
|
||||
/// Window should be closed.
|
||||
Close,
|
||||
}
|
||||
@@ -131,7 +139,7 @@ impl WindowUpdate {
|
||||
/// and react to events.
|
||||
pub struct WindowHandle {
|
||||
/// An actual window.
|
||||
pub window: Window<ConceptFrame>,
|
||||
pub window: Window<FallbackFrame>,
|
||||
|
||||
/// The current size of the window.
|
||||
pub size: Arc<Mutex<LogicalSize<u32>>>,
|
||||
@@ -153,14 +161,23 @@ pub struct WindowHandle {
|
||||
|
||||
/// Text inputs on the current surface.
|
||||
text_inputs: Vec<TextInputHandler>,
|
||||
|
||||
/// XdgActivation object.
|
||||
xdg_activation: Option<Attached<XdgActivationV1>>,
|
||||
|
||||
/// Indicator whether user attention is requested.
|
||||
attention_requested: Cell<bool>,
|
||||
}
|
||||
|
||||
impl WindowHandle {
|
||||
pub fn new(
|
||||
window: Window<ConceptFrame>,
|
||||
env: &Environment<WinitEnv>,
|
||||
window: Window<FallbackFrame>,
|
||||
size: Arc<Mutex<LogicalSize<u32>>>,
|
||||
pending_window_requests: Arc<Mutex<Vec<WindowRequest>>>,
|
||||
) -> Self {
|
||||
let xdg_activation = env.get_global::<XdgActivationV1>();
|
||||
|
||||
Self {
|
||||
window,
|
||||
size,
|
||||
@@ -170,6 +187,8 @@ impl WindowHandle {
|
||||
cursor_visible: Cell::new(true),
|
||||
pointers: Vec::new(),
|
||||
text_inputs: Vec::new(),
|
||||
xdg_activation,
|
||||
attention_requested: Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,13 +203,55 @@ impl WindowHandle {
|
||||
for pointer in self.pointers.iter() {
|
||||
if self.confined.get() {
|
||||
let surface = self.window.surface();
|
||||
pointer.confine(&surface);
|
||||
pointer.confine(surface);
|
||||
} else {
|
||||
pointer.unconfine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
let xdg_activation = match self.xdg_activation.as_ref() {
|
||||
None => return,
|
||||
Some(xdg_activation) => xdg_activation,
|
||||
};
|
||||
|
||||
// Urgency is only removed by the compositor and there's no need to raise urgency when it
|
||||
// was already raised.
|
||||
if request_type.is_none() || self.attention_requested.get() {
|
||||
return;
|
||||
}
|
||||
|
||||
let xdg_activation_token = xdg_activation.get_activation_token();
|
||||
let surface = self.window.surface();
|
||||
let window_id = wayland::make_wid(surface);
|
||||
let xdg_activation = xdg_activation.clone();
|
||||
|
||||
xdg_activation_token.quick_assign(move |xdg_token, event, mut dispatch_data| {
|
||||
let token = match event {
|
||||
xdg_activation_token_v1::Event::Done { token } => token,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let winit_state = dispatch_data.get::<WinitState>().unwrap();
|
||||
let window_handle = match winit_state.window_map.get_mut(&window_id) {
|
||||
Some(window_handle) => window_handle,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let surface = window_handle.window.surface();
|
||||
xdg_activation.activate(token, surface);
|
||||
|
||||
// Mark that attention request was done and drop the token.
|
||||
window_handle.attention_requested.replace(false);
|
||||
xdg_token.destroy();
|
||||
});
|
||||
|
||||
xdg_activation_token.set_surface(surface);
|
||||
xdg_activation_token.commit();
|
||||
self.attention_requested.replace(true);
|
||||
}
|
||||
|
||||
/// Pointer appeared over the window.
|
||||
pub fn pointer_entered(&mut self, pointer: WinitPointer) {
|
||||
let position = self.pointers.iter().position(|p| *p == pointer);
|
||||
@@ -198,7 +259,7 @@ impl WindowHandle {
|
||||
if position.is_none() {
|
||||
if self.confined.get() {
|
||||
let surface = self.window.surface();
|
||||
pointer.confine(&surface);
|
||||
pointer.confine(surface);
|
||||
}
|
||||
self.pointers.push(pointer);
|
||||
}
|
||||
@@ -222,12 +283,7 @@ impl WindowHandle {
|
||||
}
|
||||
|
||||
pub fn text_input_entered(&mut self, text_input: TextInputHandler) {
|
||||
if self
|
||||
.text_inputs
|
||||
.iter()
|
||||
.find(|t| *t == &text_input)
|
||||
.is_none()
|
||||
{
|
||||
if !self.text_inputs.iter().any(|t| *t == text_input) {
|
||||
self.text_inputs.push(text_input);
|
||||
}
|
||||
}
|
||||
@@ -330,35 +386,35 @@ pub fn handle_window_requests(winit_state: &mut WinitState) {
|
||||
window_handle.window.set_decorate(decorations);
|
||||
|
||||
// We should refresh the frame to apply decorations change.
|
||||
let window_update = window_updates.get_mut(&window_id).unwrap();
|
||||
let window_update = window_updates.get_mut(window_id).unwrap();
|
||||
window_update.refresh_frame = true;
|
||||
}
|
||||
WindowRequest::Resizeable(resizeable) => {
|
||||
window_handle.window.set_resizable(resizeable);
|
||||
|
||||
// We should refresh the frame to update button state.
|
||||
let window_update = window_updates.get_mut(&window_id).unwrap();
|
||||
let window_update = window_updates.get_mut(window_id).unwrap();
|
||||
window_update.refresh_frame = true;
|
||||
}
|
||||
WindowRequest::Title(title) => {
|
||||
window_handle.window.set_title(title);
|
||||
|
||||
// We should refresh the frame to draw new title.
|
||||
let window_update = window_updates.get_mut(&window_id).unwrap();
|
||||
let window_update = window_updates.get_mut(window_id).unwrap();
|
||||
window_update.refresh_frame = true;
|
||||
}
|
||||
WindowRequest::MinSize(size) => {
|
||||
let size = size.map(|size| (size.width, size.height));
|
||||
window_handle.window.set_min_size(size);
|
||||
|
||||
let window_update = window_updates.get_mut(&window_id).unwrap();
|
||||
let window_update = window_updates.get_mut(window_id).unwrap();
|
||||
window_update.redraw_requested = true;
|
||||
}
|
||||
WindowRequest::MaxSize(size) => {
|
||||
let size = size.map(|size| (size.width, size.height));
|
||||
window_handle.window.set_max_size(size);
|
||||
|
||||
let window_update = window_updates.get_mut(&window_id).unwrap();
|
||||
let window_update = window_updates.get_mut(window_id).unwrap();
|
||||
window_update.redraw_requested = true;
|
||||
}
|
||||
WindowRequest::FrameSize(size) => {
|
||||
@@ -366,20 +422,16 @@ pub fn handle_window_requests(winit_state: &mut WinitState) {
|
||||
window_handle.window.resize(size.width, size.height);
|
||||
|
||||
// We should refresh the frame after resize.
|
||||
let window_update = window_updates.get_mut(&window_id).unwrap();
|
||||
let window_update = window_updates.get_mut(window_id).unwrap();
|
||||
window_update.refresh_frame = true;
|
||||
}
|
||||
WindowRequest::Attention(request_type) => {
|
||||
window_handle.set_user_attention(request_type);
|
||||
}
|
||||
WindowRequest::Redraw => {
|
||||
let window_update = window_updates.get_mut(&window_id).unwrap();
|
||||
let window_update = window_updates.get_mut(window_id).unwrap();
|
||||
window_update.redraw_requested = true;
|
||||
}
|
||||
WindowRequest::Theme(concept_config) => {
|
||||
window_handle.window.set_frame_config(concept_config);
|
||||
|
||||
// We should refresh the frame to apply new theme.
|
||||
let window_update = window_updates.get_mut(&window_id).unwrap();
|
||||
window_update.refresh_frame = true;
|
||||
}
|
||||
WindowRequest::Close => {
|
||||
// The window was requested to be closed.
|
||||
windows_to_close.push(*window_id);
|
||||
|
||||
@@ -45,7 +45,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
if let Some(info) = DeviceInfo::get(&wt.xconn, device) {
|
||||
for info in info.iter() {
|
||||
devices.insert(DeviceId(info.deviceid), Device::new(&self, info));
|
||||
devices.insert(DeviceId(info.deviceid), Device::new(self, info));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -925,7 +925,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
|
||||
// Issue key press events for all pressed keys
|
||||
Self::handle_pressed_keys(
|
||||
&wt,
|
||||
wt,
|
||||
window_id,
|
||||
ElementState::Pressed,
|
||||
&self.mod_keymap,
|
||||
@@ -949,7 +949,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
|
||||
// Issue key release events for all pressed keys
|
||||
Self::handle_pressed_keys(
|
||||
&wt,
|
||||
wt,
|
||||
window_id,
|
||||
ElementState::Released,
|
||||
&self.mod_keymap,
|
||||
@@ -1164,68 +1164,64 @@ impl<T: 'static> EventProcessor<T> {
|
||||
if let Some(prev_list) = prev_list {
|
||||
let new_list = wt.xconn.available_monitors();
|
||||
for new_monitor in new_list {
|
||||
prev_list
|
||||
// Previous list may be empty, in case of disconnecting and
|
||||
// reconnecting the only one monitor. We still need to emit events in
|
||||
// this case.
|
||||
let maybe_prev_scale_factor = prev_list
|
||||
.iter()
|
||||
.find(|prev_monitor| prev_monitor.name == new_monitor.name)
|
||||
.map(|prev_monitor| {
|
||||
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 {
|
||||
let (width, height) =
|
||||
window.inner_size_physical();
|
||||
let (new_width, new_height) = window
|
||||
.adjust_for_dpi(
|
||||
prev_monitor.scale_factor,
|
||||
new_monitor.scale_factor,
|
||||
width,
|
||||
height,
|
||||
&*window.shared_state.lock(),
|
||||
);
|
||||
.map(|prev_monitor| prev_monitor.scale_factor);
|
||||
if Some(new_monitor.scale_factor) != maybe_prev_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 {
|
||||
let (width, height) = window.inner_size_physical();
|
||||
let (new_width, new_height) = window.adjust_for_dpi(
|
||||
// If there all monitors are closed before, scale
|
||||
// factor would be already changed to 1.0.
|
||||
maybe_prev_scale_factor.unwrap_or(1.0),
|
||||
new_monitor.scale_factor,
|
||||
width,
|
||||
height,
|
||||
&*window.shared_state.lock(),
|
||||
);
|
||||
|
||||
let window_id = crate::window::WindowId(
|
||||
crate::platform_impl::platform::WindowId::X(
|
||||
*window_id,
|
||||
),
|
||||
);
|
||||
let old_inner_size =
|
||||
PhysicalSize::new(width, height);
|
||||
let mut new_inner_size =
|
||||
PhysicalSize::new(new_width, new_height);
|
||||
let window_id = crate::window::WindowId(
|
||||
crate::platform_impl::platform::WindowId::X(
|
||||
*window_id,
|
||||
),
|
||||
);
|
||||
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,
|
||||
},
|
||||
});
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match self.ime_receiver.try_recv() {
|
||||
Ok((window_id, x, y)) => {
|
||||
wt.ime.borrow_mut().send_xim_spot(window_id, x, y);
|
||||
}
|
||||
Err(_) => (),
|
||||
if let Ok((window_id, x, y)) = self.ime_receiver.try_recv() {
|
||||
wt.ime.borrow_mut().send_xim_spot(window_id, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> {
|
||||
//ffi::XK_Linefeed => VirtualKeyCode::Linefeed,
|
||||
//ffi::XK_Clear => VirtualKeyCode::Clear,
|
||||
ffi::XK_Return => VirtualKeyCode::Return,
|
||||
//ffi::XK_Pause => VirtualKeyCode::Pause,
|
||||
ffi::XK_Pause => VirtualKeyCode::Pause,
|
||||
//ffi::XK_Scroll_Lock => VirtualKeyCode::Scroll_lock,
|
||||
//ffi::XK_Sys_Req => VirtualKeyCode::Sys_req,
|
||||
ffi::XK_Escape => VirtualKeyCode::Escape,
|
||||
@@ -59,10 +59,10 @@ pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> {
|
||||
//ffi::XK_Break => VirtualKeyCode::Break,
|
||||
//ffi::XK_Mode_switch => VirtualKeyCode::Mode_switch,
|
||||
//ffi::XK_script_switch => VirtualKeyCode::Script_switch,
|
||||
//ffi::XK_Num_Lock => VirtualKeyCode::Num_lock,
|
||||
ffi::XK_Num_Lock => VirtualKeyCode::Numlock,
|
||||
//ffi::XK_KP_Space => VirtualKeyCode::Kp_space,
|
||||
//ffi::XK_KP_Tab => VirtualKeyCode::Kp_tab,
|
||||
//ffi::XK_KP_Enter => VirtualKeyCode::Kp_enter,
|
||||
ffi::XK_KP_Enter => VirtualKeyCode::NumpadEnter,
|
||||
//ffi::XK_KP_F1 => VirtualKeyCode::Kp_f1,
|
||||
//ffi::XK_KP_F2 => VirtualKeyCode::Kp_f2,
|
||||
//ffi::XK_KP_F3 => VirtualKeyCode::Kp_f3,
|
||||
@@ -83,7 +83,7 @@ pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> {
|
||||
ffi::XK_KP_Equal => VirtualKeyCode::NumpadEquals,
|
||||
ffi::XK_KP_Multiply => VirtualKeyCode::NumpadMultiply,
|
||||
ffi::XK_KP_Add => VirtualKeyCode::NumpadAdd,
|
||||
//ffi::XK_KP_Separator => VirtualKeyCode::Kp_separator,
|
||||
ffi::XK_KP_Separator => VirtualKeyCode::NumpadComma,
|
||||
ffi::XK_KP_Subtract => VirtualKeyCode::NumpadSubtract,
|
||||
ffi::XK_KP_Decimal => VirtualKeyCode::NumpadDecimal,
|
||||
ffi::XK_KP_Divide => VirtualKeyCode::NumpadDivide,
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
use x11_dl::xmd::CARD32;
|
||||
pub use x11_dl::{
|
||||
error::OpenError, keysym::*, xcursor::*, xinput::*, xinput2::*, xlib::*, xlib_xcb::*,
|
||||
xrandr::*, xrender::*,
|
||||
};
|
||||
|
||||
// Isn't defined by x11_dl
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const IconicState: CARD32 = 3;
|
||||
|
||||
@@ -62,7 +62,7 @@ pub unsafe fn set_destroy_callback(
|
||||
inner: &ImeInner,
|
||||
) -> Result<(), XError> {
|
||||
xim_set_callback(
|
||||
&xconn,
|
||||
xconn,
|
||||
im,
|
||||
ffi::XNDestroyCallback_0.as_ptr() as *const _,
|
||||
&inner.destroy_callback as *const _ as *mut _,
|
||||
@@ -70,6 +70,7 @@ pub unsafe fn set_destroy_callback(
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
enum ReplaceImError {
|
||||
MethodOpenFailed(PotentialInputMethods),
|
||||
ContextCreationFailed(ImeContextCreationError),
|
||||
@@ -136,13 +137,17 @@ pub unsafe extern "C" fn xim_instantiate_callback(
|
||||
let inner: *mut ImeInner = client_data as _;
|
||||
if !inner.is_null() {
|
||||
let xconn = &(*inner).xconn;
|
||||
let result = replace_im(inner);
|
||||
if result.is_ok() {
|
||||
let _ = unset_instantiate_callback(xconn, client_data);
|
||||
(*inner).is_fallback = false;
|
||||
} else if result.is_err() && (*inner).is_destroyed {
|
||||
// We have no usable input methods!
|
||||
result.expect("Failed to reopen input method");
|
||||
match replace_im(inner) {
|
||||
Ok(()) => {
|
||||
let _ = unset_instantiate_callback(xconn, client_data);
|
||||
(*inner).is_fallback = false;
|
||||
}
|
||||
Err(err) => {
|
||||
if (*inner).is_destroyed {
|
||||
// We have no usable input methods!
|
||||
panic!("Failed to reopen input method: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,12 +168,12 @@ pub unsafe extern "C" fn xim_destroy_callback(
|
||||
if !(*inner).is_fallback {
|
||||
let _ = set_instantiate_callback(xconn, client_data);
|
||||
// Attempt to open fallback input method.
|
||||
let result = replace_im(inner);
|
||||
if result.is_ok() {
|
||||
(*inner).is_fallback = true;
|
||||
} else {
|
||||
// We have no usable input methods!
|
||||
result.expect("Failed to open fallback input method");
|
||||
match replace_im(inner) {
|
||||
Ok(()) => (*inner).is_fallback = true,
|
||||
Err(err) => {
|
||||
// We have no usable input methods!
|
||||
panic!("Failed to open fallback input method: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ impl ImeContext {
|
||||
|
||||
Ok(ImeContext {
|
||||
ic,
|
||||
ic_spot: ic_spot.unwrap_or_else(|| ffi::XPoint { x: 0, y: 0 }),
|
||||
ic_spot: ic_spot.unwrap_or(ffi::XPoint { x: 0, y: 0 }),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -58,10 +58,8 @@ impl ImeInner {
|
||||
}
|
||||
|
||||
pub unsafe fn destroy_all_contexts_if_necessary(&self) -> Result<bool, XError> {
|
||||
for context in self.contexts.values() {
|
||||
if let &Some(ref context) = context {
|
||||
self.destroy_ic_if_necessary(context.ic)?;
|
||||
}
|
||||
for context in self.contexts.values().flatten() {
|
||||
self.destroy_ic_if_necessary(context.ic)?;
|
||||
}
|
||||
Ok(!self.is_destroyed)
|
||||
}
|
||||
|
||||
@@ -42,12 +42,12 @@ unsafe fn open_im(xconn: &Arc<XConnection>, locale_modifiers: &CStr) -> Option<f
|
||||
#[derive(Debug)]
|
||||
pub struct InputMethod {
|
||||
pub im: ffi::XIM,
|
||||
name: String,
|
||||
_name: String,
|
||||
}
|
||||
|
||||
impl InputMethod {
|
||||
fn new(im: ffi::XIM, name: String) -> Self {
|
||||
InputMethod { im, name }
|
||||
InputMethod { im, _name: name }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,11 +63,7 @@ pub enum InputMethodResult {
|
||||
|
||||
impl InputMethodResult {
|
||||
pub fn is_fallback(&self) -> bool {
|
||||
if let &InputMethodResult::Fallback(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, InputMethodResult::Fallback(_))
|
||||
}
|
||||
|
||||
pub fn ok(self) -> Option<InputMethod> {
|
||||
@@ -249,7 +245,7 @@ impl PotentialInputMethods {
|
||||
pub fn open_im(
|
||||
&mut self,
|
||||
xconn: &Arc<XConnection>,
|
||||
callback: Option<&dyn Fn() -> ()>,
|
||||
callback: Option<&dyn Fn()>,
|
||||
) -> InputMethodResult {
|
||||
use self::InputMethodResult::*;
|
||||
|
||||
@@ -259,10 +255,8 @@ impl PotentialInputMethods {
|
||||
let im = input_method.open_im(xconn);
|
||||
if let Some(im) = im {
|
||||
return XModifiers(im);
|
||||
} else {
|
||||
if let Some(ref callback) = callback {
|
||||
callback();
|
||||
}
|
||||
} else if let Some(ref callback) = callback {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ use std::{
|
||||
ptr,
|
||||
rc::Rc,
|
||||
slice,
|
||||
sync::mpsc::Receiver,
|
||||
sync::mpsc::{Receiver, Sender},
|
||||
sync::{mpsc, Arc, Weak},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
@@ -41,12 +41,6 @@ use libc::{self, setlocale, LC_CTYPE};
|
||||
|
||||
use mio::{unix::SourceFd, Events, Interest, Poll, Token, Waker};
|
||||
|
||||
use mio_misc::{
|
||||
channel::{channel, SendError, Sender},
|
||||
queue::NotificationQueue,
|
||||
NotificationId,
|
||||
};
|
||||
|
||||
use self::{
|
||||
dnd::{Dnd, DndState},
|
||||
event_processor::EventProcessor,
|
||||
@@ -64,6 +58,11 @@ use crate::{
|
||||
const X_TOKEN: Token = Token(0);
|
||||
const USER_REDRAW_TOKEN: Token = Token(1);
|
||||
|
||||
struct WakeSender<T> {
|
||||
sender: Sender<T>,
|
||||
waker: Arc<Waker>,
|
||||
}
|
||||
|
||||
pub struct EventLoopWindowTarget<T> {
|
||||
xconn: Arc<XConnection>,
|
||||
wm_delete_window: ffi::Atom,
|
||||
@@ -72,27 +71,30 @@ pub struct EventLoopWindowTarget<T> {
|
||||
root: ffi::Window,
|
||||
ime: RefCell<Ime>,
|
||||
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
|
||||
redraw_sender: Sender<WindowId>,
|
||||
redraw_sender: WakeSender<WindowId>,
|
||||
_marker: ::std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
pub struct EventLoop<T: 'static> {
|
||||
poll: Poll,
|
||||
waker: Arc<Waker>,
|
||||
event_processor: EventProcessor<T>,
|
||||
redraw_channel: Receiver<WindowId>,
|
||||
user_channel: Receiver<T>,
|
||||
user_channel: Receiver<T>, //waker.wake needs to be called whenever something gets sent
|
||||
user_sender: Sender<T>,
|
||||
target: Rc<RootELW<T>>,
|
||||
}
|
||||
|
||||
pub struct EventLoopProxy<T: 'static> {
|
||||
user_sender: Sender<T>,
|
||||
waker: Arc<Waker>,
|
||||
}
|
||||
|
||||
impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||
fn clone(&self) -> Self {
|
||||
EventLoopProxy {
|
||||
user_sender: self.user_sender.clone(),
|
||||
waker: self.waker.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,15 +187,13 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
let poll = Poll::new().unwrap();
|
||||
let waker = Arc::new(Waker::new(poll.registry(), USER_REDRAW_TOKEN).unwrap());
|
||||
let queue = Arc::new(NotificationQueue::new(waker));
|
||||
|
||||
poll.registry()
|
||||
.register(&mut SourceFd(&xconn.x11_fd), X_TOKEN, Interest::READABLE)
|
||||
.unwrap();
|
||||
|
||||
let (user_sender, user_channel) = channel(queue.clone(), NotificationId::gen_next());
|
||||
|
||||
let (redraw_sender, redraw_channel) = channel(queue, NotificationId::gen_next());
|
||||
let (user_sender, user_channel) = std::sync::mpsc::channel();
|
||||
let (redraw_sender, redraw_channel) = std::sync::mpsc::channel();
|
||||
|
||||
let target = Rc::new(RootELW {
|
||||
p: super::EventLoopWindowTarget::X(EventLoopWindowTarget {
|
||||
@@ -205,7 +205,10 @@ impl<T: 'static> EventLoop<T> {
|
||||
xconn,
|
||||
wm_delete_window,
|
||||
net_wm_ping,
|
||||
redraw_sender,
|
||||
redraw_sender: WakeSender {
|
||||
sender: redraw_sender, // not used again so no clone
|
||||
waker: waker.clone(),
|
||||
},
|
||||
}),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
});
|
||||
@@ -233,21 +236,21 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
event_processor.init_device(ffi::XIAllDevices);
|
||||
|
||||
let result = EventLoop {
|
||||
EventLoop {
|
||||
poll,
|
||||
waker,
|
||||
event_processor,
|
||||
redraw_channel,
|
||||
user_channel,
|
||||
user_sender,
|
||||
event_processor,
|
||||
target,
|
||||
};
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy {
|
||||
user_sender: self.user_sender.clone(),
|
||||
waker: self.waker.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,7 +360,11 @@ impl<T: 'static> EventLoop<T> {
|
||||
// If the XConnection already contains buffered events, we don't
|
||||
// need to wait for data on the socket.
|
||||
if !self.event_processor.poll() {
|
||||
self.poll.poll(&mut events, timeout).unwrap();
|
||||
if let Err(e) = self.poll.poll(&mut events, timeout) {
|
||||
if e.raw_os_error() != Some(libc::EINTR) {
|
||||
panic!("epoll returned an error: {:?}", e);
|
||||
}
|
||||
}
|
||||
events.clear();
|
||||
}
|
||||
|
||||
@@ -392,7 +399,6 @@ impl<T: 'static> EventLoop<T> {
|
||||
{
|
||||
let target = &self.target;
|
||||
let mut xev = MaybeUninit::uninit();
|
||||
|
||||
let wt = get_xtarget(&self.target);
|
||||
|
||||
while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } {
|
||||
@@ -407,7 +413,8 @@ impl<T: 'static> EventLoop<T> {
|
||||
super::WindowId::X(wid),
|
||||
)) = event
|
||||
{
|
||||
wt.redraw_sender.send(wid).unwrap();
|
||||
wt.redraw_sender.sender.send(wid).unwrap();
|
||||
wt.redraw_sender.waker.wake().unwrap();
|
||||
} else {
|
||||
callback(event, window_target, control_flow);
|
||||
}
|
||||
@@ -436,13 +443,10 @@ 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 SendError::Disconnected(x) = e {
|
||||
x
|
||||
} else {
|
||||
unreachable!()
|
||||
})
|
||||
})
|
||||
self.user_sender
|
||||
.send(event)
|
||||
.map_err(|e| EventLoopClosed(e.0))
|
||||
.map(|_| self.waker.wake().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,7 +494,7 @@ impl<'a> Deref for DeviceInfo<'a> {
|
||||
pub struct WindowId(ffi::Window);
|
||||
|
||||
impl WindowId {
|
||||
pub unsafe fn dummy() -> Self {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
WindowId(0)
|
||||
}
|
||||
}
|
||||
@@ -499,7 +503,7 @@ impl WindowId {
|
||||
pub struct DeviceId(c_int);
|
||||
|
||||
impl DeviceId {
|
||||
pub unsafe fn dummy() -> Self {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
DeviceId(0)
|
||||
}
|
||||
}
|
||||
@@ -520,7 +524,7 @@ impl Window {
|
||||
attribs: WindowAttributes,
|
||||
pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||
) -> Result<Self, RootOsError> {
|
||||
let window = Arc::new(UnownedWindow::new(&event_loop, attribs, pl_attribs)?);
|
||||
let window = Arc::new(UnownedWindow::new(event_loop, attribs, pl_attribs)?);
|
||||
event_loop
|
||||
.windows
|
||||
.borrow_mut()
|
||||
@@ -588,7 +592,7 @@ fn mkdid(w: c_int) -> crate::event::DeviceId {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Device {
|
||||
name: String,
|
||||
_name: String,
|
||||
scroll_axes: Vec<(i32, ScrollAxis)>,
|
||||
// For master devices, this is the paired device (pointer <-> keyboard).
|
||||
// For slave devices, this is the master.
|
||||
@@ -630,31 +634,28 @@ impl Device {
|
||||
// Identify scroll axes
|
||||
for class_ptr in Device::classes(info) {
|
||||
let class = unsafe { &**class_ptr };
|
||||
match class._type {
|
||||
ffi::XIScrollClass => {
|
||||
let info = unsafe {
|
||||
mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIScrollClassInfo>(class)
|
||||
};
|
||||
scroll_axes.push((
|
||||
info.number,
|
||||
ScrollAxis {
|
||||
increment: info.increment,
|
||||
orientation: match info.scroll_type {
|
||||
ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal,
|
||||
ffi::XIScrollTypeVertical => ScrollOrientation::Vertical,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
position: 0.0,
|
||||
if class._type == ffi::XIScrollClass {
|
||||
let info = unsafe {
|
||||
mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIScrollClassInfo>(class)
|
||||
};
|
||||
scroll_axes.push((
|
||||
info.number,
|
||||
ScrollAxis {
|
||||
increment: info.increment,
|
||||
orientation: match info.scroll_type {
|
||||
ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal,
|
||||
ffi::XIScrollTypeVertical => ScrollOrientation::Vertical,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
position: 0.0,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut device = Device {
|
||||
name: name.into_owned(),
|
||||
_name: name.into_owned(),
|
||||
scroll_axes,
|
||||
attachment: info.attachment,
|
||||
};
|
||||
@@ -666,20 +667,17 @@ impl Device {
|
||||
if Device::physical_device(info) {
|
||||
for class_ptr in Device::classes(info) {
|
||||
let class = unsafe { &**class_ptr };
|
||||
match class._type {
|
||||
ffi::XIValuatorClass => {
|
||||
let info = unsafe {
|
||||
mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIValuatorClassInfo>(class)
|
||||
};
|
||||
if let Some(&mut (_, ref mut axis)) = self
|
||||
.scroll_axes
|
||||
.iter_mut()
|
||||
.find(|&&mut (axis, _)| axis == info.number)
|
||||
{
|
||||
axis.position = info.value;
|
||||
}
|
||||
if class._type == ffi::XIValuatorClass {
|
||||
let info = unsafe {
|
||||
mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIValuatorClassInfo>(class)
|
||||
};
|
||||
if let Some(&mut (_, ref mut axis)) = self
|
||||
.scroll_axes
|
||||
.iter_mut()
|
||||
.find(|&&mut (axis, _)| axis == info.number)
|
||||
{
|
||||
axis.position = info.value;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ impl Eq for MonitorHandle {}
|
||||
|
||||
impl PartialOrd for MonitorHandle {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(&other))
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ impl XConnection {
|
||||
let overlapping_area = window_rect.get_overlapping_area(&monitor.rect);
|
||||
if overlapping_area > largest_overlap {
|
||||
largest_overlap = overlapping_area;
|
||||
matched_monitor = &monitor;
|
||||
matched_monitor = monitor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,8 +242,11 @@ impl XConnection {
|
||||
if is_active {
|
||||
let is_primary = *(*crtc).outputs.offset(0) == primary;
|
||||
has_primary |= is_primary;
|
||||
MonitorHandle::new(self, resources, crtc_id, crtc, is_primary)
|
||||
.map(|monitor_id| available.push(monitor_id));
|
||||
if let Some(monitor_id) =
|
||||
MonitorHandle::new(self, resources, crtc_id, crtc, is_primary)
|
||||
{
|
||||
available.push(monitor_id)
|
||||
}
|
||||
}
|
||||
(self.xrandr.XRRFreeCrtcInfo)(crtc);
|
||||
}
|
||||
|
||||
@@ -104,8 +104,8 @@ impl XConnection {
|
||||
CursorIcon::WResize => load(b"left_side\0"),
|
||||
CursorIcon::EwResize => load(b"h_double_arrow\0"),
|
||||
CursorIcon::NsResize => load(b"v_double_arrow\0"),
|
||||
CursorIcon::NwseResize => loadn(&[b"bd_double_arrow\0", b"size_bdiag\0"]),
|
||||
CursorIcon::NeswResize => loadn(&[b"fd_double_arrow\0", b"size_fdiag\0"]),
|
||||
CursorIcon::NwseResize => loadn(&[b"bd_double_arrow\0", b"size_fdiag\0"]),
|
||||
CursorIcon::NeswResize => loadn(&[b"fd_double_arrow\0", b"size_bdiag\0"]),
|
||||
CursorIcon::ColResize => loadn(&[b"split_h\0", b"h_double_arrow\0"]),
|
||||
CursorIcon::RowResize => loadn(&[b"split_v\0", b"v_double_arrow\0"]),
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@ impl Format {
|
||||
|
||||
pub fn get_actual_size(&self) -> usize {
|
||||
match self {
|
||||
&Format::Char => mem::size_of::<c_char>(),
|
||||
&Format::Short => mem::size_of::<c_short>(),
|
||||
&Format::Long => mem::size_of::<c_long>(),
|
||||
Format::Char => mem::size_of::<c_char>(),
|
||||
Format::Short => mem::size_of::<c_short>(),
|
||||
Format::Long => mem::size_of::<c_long>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ impl XConnection {
|
||||
);
|
||||
|
||||
// The list of children isn't used
|
||||
if children != ptr::null_mut() {
|
||||
if !children.is_null() {
|
||||
(self.xlib.XFree)(children as *mut _);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ impl Icon {
|
||||
data.push(rgba_icon.height as Cardinal);
|
||||
let pixels = rgba_icon.rgba.as_ptr() as *const Pixel;
|
||||
for pixel_index in 0..pixel_count {
|
||||
let pixel = unsafe { &*pixels.offset(pixel_index as isize) };
|
||||
let pixel = unsafe { &*pixels.add(pixel_index) };
|
||||
data.push(pixel.to_packed_argb());
|
||||
}
|
||||
data
|
||||
|
||||
@@ -36,7 +36,7 @@ impl Iterator for KeymapIter<'_> {
|
||||
|
||||
fn next(&mut self) -> Option<ffi::KeyCode> {
|
||||
if self.item.is_none() {
|
||||
while let Some((index, &item)) = self.iter.next() {
|
||||
for (index, &item) in self.iter.by_ref() {
|
||||
if item != 0 {
|
||||
self.index = index;
|
||||
self.item = Some(item);
|
||||
|
||||
@@ -151,7 +151,7 @@ impl ModifierKeyState {
|
||||
|
||||
pub fn key_release(&mut self, keycode: ffi::KeyCode) {
|
||||
if let Some(modifier) = self.keys.remove(&keycode) {
|
||||
if self.keys.values().find(|&&m| m == modifier).is_none() {
|
||||
if !self.keys.values().any(|&m| m == modifier) {
|
||||
set_modifier(&mut self.state, modifier, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,11 @@ pub fn calc_dpi_factor(
|
||||
// Quantize 1/12 step size
|
||||
let dpi_factor = ((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0);
|
||||
assert!(validate_scale_factor(dpi_factor));
|
||||
dpi_factor
|
||||
if dpi_factor <= 20. {
|
||||
dpi_factor
|
||||
} else {
|
||||
1.
|
||||
}
|
||||
}
|
||||
|
||||
impl XConnection {
|
||||
@@ -35,15 +39,15 @@ impl XConnection {
|
||||
pub unsafe fn get_xft_dpi(&self) -> Option<f64> {
|
||||
(self.xlib.XrmInitialize)();
|
||||
let resource_manager_str = (self.xlib.XResourceManagerString)(self.display);
|
||||
if resource_manager_str == ptr::null_mut() {
|
||||
if resource_manager_str.is_null() {
|
||||
return None;
|
||||
}
|
||||
if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() {
|
||||
let name: &str = "Xft.dpi:\t";
|
||||
for pair in res.split("\n") {
|
||||
for pair in res.split('\n') {
|
||||
if pair.starts_with(&name) {
|
||||
let res = &pair[name.len()..];
|
||||
return f64::from_str(&res).ok();
|
||||
return f64::from_str(res).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ impl XConnection {
|
||||
quantity_returned,
|
||||
new_data,
|
||||
);*/
|
||||
data.extend_from_slice(&new_data);
|
||||
data.extend_from_slice(new_data);
|
||||
// Fun fact: XGetWindowProperty allocates one extra byte at the end.
|
||||
(self.xlib.XFree)(buf as _); // Don't try to access new_data after this.
|
||||
} else {
|
||||
|
||||
@@ -62,11 +62,7 @@ impl XConnection {
|
||||
|
||||
let wm_check = result.ok().and_then(|wm_check| wm_check.get(0).cloned());
|
||||
|
||||
if let Some(wm_check) = wm_check {
|
||||
wm_check
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
wm_check?
|
||||
};
|
||||
|
||||
// Querying the same property on the child window we were given, we should get this child
|
||||
@@ -76,11 +72,7 @@ impl XConnection {
|
||||
|
||||
let wm_check = result.ok().and_then(|wm_check| wm_check.get(0).cloned());
|
||||
|
||||
if let Some(wm_check) = wm_check {
|
||||
wm_check
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
wm_check?
|
||||
};
|
||||
|
||||
// These values should be the same.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use raw_window_handle::unix::XlibHandle;
|
||||
use raw_window_handle::XlibHandle;
|
||||
use std::{
|
||||
cmp, env,
|
||||
ffi::CString,
|
||||
@@ -8,9 +8,9 @@ use std::{
|
||||
ptr, slice,
|
||||
sync::Arc,
|
||||
};
|
||||
use x11_dl::xlib::TrueColor;
|
||||
|
||||
use libc;
|
||||
use mio_misc::channel::Sender;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use crate::{
|
||||
@@ -25,7 +25,9 @@ use crate::{
|
||||
window::{CursorIcon, Fullscreen, Icon, UserAttentionType, WindowAttributes},
|
||||
};
|
||||
|
||||
use super::{ffi, util, EventLoopWindowTarget, ImeSender, WindowId, XConnection, XError};
|
||||
use super::{
|
||||
ffi, util, EventLoopWindowTarget, ImeSender, WakeSender, WindowId, XConnection, XError,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SharedState {
|
||||
@@ -103,7 +105,7 @@ pub struct UnownedWindow {
|
||||
cursor_visible: Mutex<bool>,
|
||||
ime_sender: Mutex<ImeSender>,
|
||||
pub shared_state: Mutex<SharedState>,
|
||||
redraw_sender: Sender<WindowId>,
|
||||
redraw_sender: WakeSender<WindowId>,
|
||||
}
|
||||
|
||||
impl UnownedWindow {
|
||||
@@ -148,7 +150,7 @@ impl UnownedWindow {
|
||||
|
||||
let position = window_attrs
|
||||
.position
|
||||
.map(|position| position.to_physical::<i32>(scale_factor).into());
|
||||
.map(|position| position.to_physical::<i32>(scale_factor));
|
||||
|
||||
let dimensions = {
|
||||
// x11 only applies constraints when the window is actively resized
|
||||
@@ -180,6 +182,39 @@ impl UnownedWindow {
|
||||
};
|
||||
|
||||
// creating
|
||||
let (visual, depth, require_colormap) = match pl_attribs.visual_infos {
|
||||
Some(vi) => (vi.visual, vi.depth, false),
|
||||
None if window_attrs.transparent => {
|
||||
// Find a suitable visual
|
||||
let mut vinfo = MaybeUninit::uninit();
|
||||
let vinfo_initialized = unsafe {
|
||||
(xconn.xlib.XMatchVisualInfo)(
|
||||
xconn.display,
|
||||
screen_id,
|
||||
32,
|
||||
TrueColor,
|
||||
vinfo.as_mut_ptr(),
|
||||
) != 0
|
||||
};
|
||||
if vinfo_initialized {
|
||||
let vinfo = unsafe { vinfo.assume_init() };
|
||||
(vinfo.visual, vinfo.depth, true)
|
||||
} else {
|
||||
debug!("Could not set transparency, because XMatchVisualInfo returned zero for the required parameters");
|
||||
(
|
||||
ffi::CopyFromParent as *mut ffi::Visual,
|
||||
ffi::CopyFromParent,
|
||||
false,
|
||||
)
|
||||
}
|
||||
}
|
||||
_ => (
|
||||
ffi::CopyFromParent as *mut ffi::Visual,
|
||||
ffi::CopyFromParent,
|
||||
false,
|
||||
),
|
||||
};
|
||||
|
||||
let mut set_win_attr = {
|
||||
let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() };
|
||||
swa.colormap = if let Some(vi) = pl_attribs.visual_infos {
|
||||
@@ -187,6 +222,8 @@ impl UnownedWindow {
|
||||
let visual = vi.visual;
|
||||
(xconn.xlib.XCreateColormap)(xconn.display, root, visual, ffi::AllocNone)
|
||||
}
|
||||
} else if require_colormap {
|
||||
unsafe { (xconn.xlib.XCreateColormap)(xconn.display, root, visual, ffi::AllocNone) }
|
||||
} else {
|
||||
0
|
||||
};
|
||||
@@ -220,23 +257,9 @@ impl UnownedWindow {
|
||||
dimensions.0 as c_uint,
|
||||
dimensions.1 as c_uint,
|
||||
0,
|
||||
match pl_attribs.visual_infos {
|
||||
Some(vi) => vi.depth,
|
||||
None => ffi::CopyFromParent,
|
||||
},
|
||||
depth,
|
||||
ffi::InputOutput as c_uint,
|
||||
// TODO: If window wants transparency and `visual_infos` is None,
|
||||
// we need to find our own visual which has an `alphaMask` which
|
||||
// is > 0, like we do in glutin.
|
||||
//
|
||||
// It is non obvious which masks, if any, we should pass to
|
||||
// `XGetVisualInfo`. winit doesn't receive any info about what
|
||||
// properties the user wants. Users should consider choosing the
|
||||
// visual themselves as glutin does.
|
||||
match pl_attribs.visual_infos {
|
||||
Some(vi) => vi.visual,
|
||||
None => ffi::CopyFromParent as *mut ffi::Visual,
|
||||
},
|
||||
visual,
|
||||
window_attributes,
|
||||
&mut set_win_attr,
|
||||
)
|
||||
@@ -252,7 +275,10 @@ impl UnownedWindow {
|
||||
cursor_visible: Mutex::new(true),
|
||||
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
|
||||
shared_state: SharedState::new(guessed_monitor, window_attrs.visible),
|
||||
redraw_sender: event_loop.redraw_sender.clone(),
|
||||
redraw_sender: WakeSender {
|
||||
waker: event_loop.redraw_sender.waker.clone(),
|
||||
sender: event_loop.redraw_sender.sender.clone(),
|
||||
},
|
||||
};
|
||||
|
||||
// Title must be set before mapping. Some tiling window managers (i.e. i3) use the window
|
||||
@@ -315,7 +341,9 @@ impl UnownedWindow {
|
||||
} //.queue();
|
||||
}
|
||||
|
||||
window.set_pid().map(|flusher| flusher.queue());
|
||||
if let Some(flusher) = window.set_pid() {
|
||||
flusher.queue()
|
||||
}
|
||||
|
||||
window.set_window_types(pl_attribs.x11_window_types).queue();
|
||||
|
||||
@@ -404,8 +432,7 @@ impl UnownedWindow {
|
||||
}
|
||||
|
||||
// Select XInput2 events
|
||||
let mask = {
|
||||
let mask = ffi::XI_MotionMask
|
||||
let mask = ffi::XI_MotionMask
|
||||
| ffi::XI_ButtonPressMask
|
||||
| ffi::XI_ButtonReleaseMask
|
||||
//| ffi::XI_KeyPressMask
|
||||
@@ -417,8 +444,6 @@ impl UnownedWindow {
|
||||
| ffi::XI_TouchBeginMask
|
||||
| ffi::XI_TouchUpdateMask
|
||||
| ffi::XI_TouchEndMask;
|
||||
mask
|
||||
};
|
||||
xconn
|
||||
.select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask)
|
||||
.queue();
|
||||
@@ -441,9 +466,10 @@ impl UnownedWindow {
|
||||
window.set_maximized_inner(window_attrs.maximized).queue();
|
||||
}
|
||||
if window_attrs.fullscreen.is_some() {
|
||||
window
|
||||
.set_fullscreen_inner(window_attrs.fullscreen.clone())
|
||||
.map(|flusher| flusher.queue());
|
||||
if let Some(flusher) = window.set_fullscreen_inner(window_attrs.fullscreen.clone())
|
||||
{
|
||||
flusher.queue()
|
||||
}
|
||||
|
||||
if let Some(PhysicalPosition { x, y }) = position {
|
||||
let shared_state = window.shared_state.get_mut();
|
||||
@@ -779,6 +805,30 @@ impl UnownedWindow {
|
||||
.expect("Failed to change window minimization");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_maximized(&self) -> bool {
|
||||
let state_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE\0") };
|
||||
let state = self
|
||||
.xconn
|
||||
.get_property(self.xwindow, state_atom, ffi::XA_ATOM);
|
||||
let horz_atom = unsafe {
|
||||
self.xconn
|
||||
.get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_HORZ\0")
|
||||
};
|
||||
let vert_atom = unsafe {
|
||||
self.xconn
|
||||
.get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_VERT\0")
|
||||
};
|
||||
match state {
|
||||
Ok(atoms) => {
|
||||
let horz_maximized = atoms.iter().any(|atom: &ffi::Atom| *atom == horz_atom);
|
||||
let vert_maximized = atoms.iter().any(|atom: &ffi::Atom| *atom == vert_atom);
|
||||
horz_maximized && vert_maximized
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_maximized_inner(&self, maximized: bool) -> util::Flusher<'_> {
|
||||
let horz_atom = unsafe {
|
||||
self.xconn
|
||||
@@ -1056,7 +1106,7 @@ impl UnownedWindow {
|
||||
|
||||
fn update_normal_hints<F>(&self, callback: F) -> Result<(), XError>
|
||||
where
|
||||
F: FnOnce(&mut util::NormalHints<'_>) -> (),
|
||||
F: FnOnce(&mut util::NormalHints<'_>),
|
||||
{
|
||||
let mut normal_hints = self.xconn.get_normal_hints(self.xwindow)?;
|
||||
callback(&mut normal_hints);
|
||||
@@ -1137,7 +1187,7 @@ impl UnownedWindow {
|
||||
)
|
||||
} else {
|
||||
let window_size = Some(Size::from(self.inner_size()));
|
||||
(window_size.clone(), window_size)
|
||||
(window_size, window_size)
|
||||
};
|
||||
|
||||
self.set_maximizable_inner(resizable).queue();
|
||||
@@ -1340,6 +1390,41 @@ impl UnownedWindow {
|
||||
self.set_ime_position_physical(x, y);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn focus_window(&self) {
|
||||
let state_atom = unsafe { self.xconn.get_atom_unchecked(b"WM_STATE\0") };
|
||||
let state_type_atom = unsafe { self.xconn.get_atom_unchecked(b"CARD32\0") };
|
||||
let is_minimized = if let Ok(state) =
|
||||
self.xconn
|
||||
.get_property(self.xwindow, state_atom, state_type_atom)
|
||||
{
|
||||
state.contains(&(ffi::IconicState as c_ulong))
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let is_visible = match self.shared_state.lock().visibility {
|
||||
Visibility::Yes => true,
|
||||
Visibility::YesWait | Visibility::No => false,
|
||||
};
|
||||
|
||||
if is_visible && !is_minimized {
|
||||
let atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_ACTIVE_WINDOW\0") };
|
||||
let flusher = self.xconn.send_client_msg(
|
||||
self.xwindow,
|
||||
self.root,
|
||||
atom,
|
||||
Some(ffi::SubstructureRedirectMask | ffi::SubstructureNotifyMask),
|
||||
[1, ffi::CurrentTime as c_long, 0, 0, 0],
|
||||
);
|
||||
if let Err(e) = flusher.flush() {
|
||||
log::error!(
|
||||
"`flush` returned an error when focusing the window. Error was: {}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
let mut wm_hints = self
|
||||
@@ -1364,15 +1449,18 @@ impl UnownedWindow {
|
||||
|
||||
#[inline]
|
||||
pub fn request_redraw(&self) {
|
||||
self.redraw_sender.send(WindowId(self.xwindow)).unwrap();
|
||||
self.redraw_sender
|
||||
.sender
|
||||
.send(WindowId(self.xwindow))
|
||||
.unwrap();
|
||||
self.redraw_sender.waker.wake().unwrap();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn raw_window_handle(&self) -> XlibHandle {
|
||||
XlibHandle {
|
||||
window: self.xwindow,
|
||||
display: self.xconn.display as _,
|
||||
..XlibHandle::empty()
|
||||
}
|
||||
let mut handle = XlibHandle::empty();
|
||||
handle.window = self.xlib_window();
|
||||
handle.display = self.xlib_display();
|
||||
handle
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,12 @@ use std::{
|
||||
use cocoa::{
|
||||
appkit::{NSApp, NSApplication, NSWindow},
|
||||
base::{id, nil},
|
||||
foundation::{NSAutoreleasePool, NSSize},
|
||||
foundation::NSSize,
|
||||
};
|
||||
use objc::{
|
||||
rc::autoreleasepool,
|
||||
runtime::{Object, YES},
|
||||
};
|
||||
use objc::runtime::YES;
|
||||
|
||||
use objc::runtime::Object;
|
||||
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
@@ -306,7 +307,9 @@ impl AppState {
|
||||
let panic_info = panic_info
|
||||
.upgrade()
|
||||
.expect("The panic info must exist here. This failure indicates a developer error.");
|
||||
if panic_info.is_panicking() || !HANDLER.is_ready() {
|
||||
|
||||
// Return when in callback due to https://github.com/rust-windowing/winit/issues/1779
|
||||
if panic_info.is_panicking() || !HANDLER.is_ready() || HANDLER.get_in_callback() {
|
||||
return;
|
||||
}
|
||||
let start = HANDLER.get_start_time().unwrap();
|
||||
@@ -370,24 +373,25 @@ impl AppState {
|
||||
let panic_info = panic_info
|
||||
.upgrade()
|
||||
.expect("The panic info must exist here. This failure indicates a developer error.");
|
||||
if panic_info.is_panicking() || !HANDLER.is_ready() {
|
||||
|
||||
// Return when in callback due to https://github.com/rust-windowing/winit/issues/1779
|
||||
if panic_info.is_panicking() || !HANDLER.is_ready() || HANDLER.get_in_callback() {
|
||||
return;
|
||||
}
|
||||
if !HANDLER.get_in_callback() {
|
||||
HANDLER.set_in_callback(true);
|
||||
HANDLER.handle_user_events();
|
||||
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(EventWrapper::StaticEvent(Event::RedrawRequested(
|
||||
window_id,
|
||||
)));
|
||||
}
|
||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::RedrawEventsCleared));
|
||||
HANDLER.set_in_callback(false);
|
||||
|
||||
HANDLER.set_in_callback(true);
|
||||
HANDLER.handle_user_events();
|
||||
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(EventWrapper::StaticEvent(Event::RedrawRequested(window_id)));
|
||||
}
|
||||
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::RedrawEventsCleared));
|
||||
HANDLER.set_in_callback(false);
|
||||
|
||||
if HANDLER.should_exit() {
|
||||
unsafe {
|
||||
let app: id = NSApp();
|
||||
@@ -403,16 +407,16 @@ impl AppState {
|
||||
};
|
||||
|
||||
let dialog_is_closing = HANDLER.dialog_is_closing.load(Ordering::SeqCst);
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
if !INTERRUPT_EVENT_LOOP_EXIT.load(Ordering::SeqCst)
|
||||
&& !dialog_open
|
||||
&& !dialog_is_closing
|
||||
{
|
||||
let () = msg_send![app, stop: nil];
|
||||
// To stop event loop immediately, we need to post some event here.
|
||||
post_dummy_event(app);
|
||||
}
|
||||
pool.drain();
|
||||
autoreleasepool(|| {
|
||||
if !INTERRUPT_EVENT_LOOP_EXIT.load(Ordering::SeqCst)
|
||||
&& !dialog_open
|
||||
&& !dialog_is_closing
|
||||
{
|
||||
let () = msg_send![app, stop: nil];
|
||||
// To stop event loop immediately, we need to post some event here.
|
||||
post_dummy_event(app);
|
||||
}
|
||||
});
|
||||
|
||||
if window_count > 0 {
|
||||
let window: id = msg_send![windows, objectAtIndex:0];
|
||||
|
||||
@@ -14,10 +14,9 @@ use std::{
|
||||
use cocoa::{
|
||||
appkit::{NSApp, NSEventType::NSApplicationDefined},
|
||||
base::{id, nil, YES},
|
||||
foundation::{NSAutoreleasePool, NSPoint},
|
||||
foundation::NSPoint,
|
||||
};
|
||||
|
||||
use scopeguard::defer;
|
||||
use objc::rc::autoreleasepool;
|
||||
|
||||
use crate::{
|
||||
event::Event,
|
||||
@@ -86,6 +85,20 @@ impl<T: 'static> EventLoopWindowTarget<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventLoopWindowTarget<T> {
|
||||
pub(crate) fn hide_application(&self) {
|
||||
let cls = objc::runtime::Class::get("NSApplication").unwrap();
|
||||
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
|
||||
unsafe { msg_send![app, hide: 0] }
|
||||
}
|
||||
|
||||
pub(crate) fn hide_other_applications(&self) {
|
||||
let cls = objc::runtime::Class::get("NSApplication").unwrap();
|
||||
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
|
||||
unsafe { msg_send![app, hideOtherApplications: 0] }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoop<T: 'static> {
|
||||
pub(crate) delegate: IdRef,
|
||||
|
||||
@@ -115,9 +128,9 @@ impl<T> EventLoop<T> {
|
||||
let app: id = msg_send![APP_CLASS.0, sharedApplication];
|
||||
|
||||
let delegate = IdRef::new(msg_send![APP_DELEGATE_CLASS.0, new]);
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
let _: () = msg_send![app, setDelegate:*delegate];
|
||||
let _: () = msg_send![pool, drain];
|
||||
autoreleasepool(|| {
|
||||
let _: () = msg_send![app, setDelegate:*delegate];
|
||||
});
|
||||
delegate
|
||||
};
|
||||
let panic_info: Rc<PanicInfo> = Default::default();
|
||||
@@ -162,9 +175,7 @@ impl<T> EventLoop<T> {
|
||||
|
||||
self._callback = Some(Rc::clone(&callback));
|
||||
|
||||
unsafe {
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
defer!(pool.drain());
|
||||
autoreleasepool(|| unsafe {
|
||||
let app = NSApp();
|
||||
assert_ne!(app, nil);
|
||||
|
||||
@@ -177,10 +188,12 @@ impl<T> EventLoop<T> {
|
||||
let () = msg_send![app, run];
|
||||
|
||||
if let Some(panic) = self.panic_info.take() {
|
||||
drop(self._callback.take());
|
||||
resume_unwind(panic);
|
||||
}
|
||||
AppState::exit();
|
||||
}
|
||||
});
|
||||
drop(self._callback.take());
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> Proxy<T> {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#![allow(dead_code, non_snake_case, non_upper_case_globals)]
|
||||
|
||||
use std::ffi::c_void;
|
||||
|
||||
use cocoa::{
|
||||
base::id,
|
||||
foundation::{NSInteger, NSUInteger},
|
||||
@@ -159,11 +161,20 @@ pub const IOYUV422Pixels: &str = "Y4U2V2";
|
||||
pub const IO8BitOverlayPixels: &str = "O8";
|
||||
|
||||
pub type CGWindowLevel = i32;
|
||||
pub type CGDisplayModeRef = *mut libc::c_void;
|
||||
pub type CGDisplayModeRef = *mut c_void;
|
||||
|
||||
// `CGDisplayCreateUUIDFromDisplayID` comes from the `ColorSync` framework.
|
||||
// However, that framework was only introduced "publicly" in macOS 10.13.
|
||||
//
|
||||
// Since we want to support older versions, we can't link to `ColorSync`
|
||||
// directly. Fortunately, it has always been available as a subframework of
|
||||
// `ApplicationServices`, see:
|
||||
// https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/OSX_Technology_Overview/SystemFrameworks/SystemFrameworks.html#//apple_ref/doc/uid/TP40001067-CH210-BBCFFIEG
|
||||
//
|
||||
// TODO: Remove the WINIT_LINK_COLORSYNC hack, it is probably not needed.
|
||||
#[cfg_attr(
|
||||
not(use_colorsync_cgdisplaycreateuuidfromdisplayid),
|
||||
link(name = "CoreGraphics", kind = "framework")
|
||||
link(name = "ApplicationServices", kind = "framework")
|
||||
)]
|
||||
#[cfg_attr(
|
||||
use_colorsync_cgdisplaycreateuuidfromdisplayid,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::util::IdRef;
|
||||
use cocoa::appkit::{NSApp, NSApplication, NSEventModifierFlags, NSMenu, NSMenuItem};
|
||||
use cocoa::base::{nil, selector};
|
||||
use cocoa::foundation::{NSAutoreleasePool, NSProcessInfo, NSString};
|
||||
use cocoa::foundation::{NSProcessInfo, NSString};
|
||||
use objc::{
|
||||
rc::autoreleasepool,
|
||||
runtime::{Object, Sel},
|
||||
@@ -13,11 +14,11 @@ struct KeyEquivalent<'a> {
|
||||
|
||||
pub fn initialize() {
|
||||
autoreleasepool(|| unsafe {
|
||||
let menubar = NSMenu::new(nil).autorelease();
|
||||
let app_menu_item = NSMenuItem::new(nil).autorelease();
|
||||
menubar.addItem_(app_menu_item);
|
||||
let menubar = IdRef::new(NSMenu::new(nil));
|
||||
let app_menu_item = IdRef::new(NSMenuItem::new(nil));
|
||||
menubar.addItem_(*app_menu_item);
|
||||
let app = NSApp();
|
||||
app.setMainMenu_(menubar);
|
||||
app.setMainMenu_(*menubar);
|
||||
|
||||
let app_menu = NSMenu::new(nil);
|
||||
let process_name = NSProcessInfo::processInfo(nil).processName();
|
||||
|
||||
@@ -25,6 +25,7 @@ pub use self::{
|
||||
use crate::{
|
||||
error::OsError as RootOsError, event::DeviceId as RootDeviceId, window::WindowAttributes,
|
||||
};
|
||||
use objc::rc::autoreleasepool;
|
||||
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
|
||||
@@ -32,7 +33,7 @@ pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
pub struct DeviceId;
|
||||
|
||||
impl DeviceId {
|
||||
pub unsafe fn dummy() -> Self {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
DeviceId
|
||||
}
|
||||
}
|
||||
@@ -69,7 +70,7 @@ impl Window {
|
||||
attributes: WindowAttributes,
|
||||
pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||
) -> Result<Self, RootOsError> {
|
||||
let (window, _delegate) = UnownedWindow::new(attributes, pl_attribs)?;
|
||||
let (window, _delegate) = autoreleasepool(|| UnownedWindow::new(attributes, pl_attribs))?;
|
||||
Ok(Window { window, _delegate })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,6 +162,7 @@ impl fmt::Debug for MonitorHandle {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// TODO: Do this using the proper fmt API
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
struct MonitorHandle {
|
||||
name: Option<String>,
|
||||
native_identifier: u32,
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::ops::{BitAnd, Deref};
|
||||
use cocoa::{
|
||||
appkit::{NSApp, NSWindowStyleMask},
|
||||
base::{id, nil},
|
||||
foundation::{NSAutoreleasePool, NSPoint, NSRect, NSString, NSUInteger},
|
||||
foundation::{NSPoint, NSRect, NSString, NSUInteger},
|
||||
};
|
||||
use core_graphics::display::CGDisplay;
|
||||
use objc::runtime::{Class, Object, Sel, BOOL, YES};
|
||||
@@ -61,9 +61,7 @@ impl Drop for IdRef {
|
||||
fn drop(&mut self) {
|
||||
if self.0 != nil {
|
||||
unsafe {
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
let () = msg_send![self.0, release];
|
||||
pool.drain();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,6 @@ pub(super) struct ViewState {
|
||||
pub cursor_state: Arc<Mutex<CursorState>>,
|
||||
ime_spot: Option<(f64, f64)>,
|
||||
raw_characters: Option<String>,
|
||||
is_key_down: bool,
|
||||
pub(super) modifiers: ModifiersState,
|
||||
tracking_rect: Option<NSInteger>,
|
||||
}
|
||||
@@ -74,7 +73,6 @@ pub fn new_view(ns_window: id) -> (IdRef, Weak<Mutex<CursorState>>) {
|
||||
cursor_state,
|
||||
ime_spot: None,
|
||||
raw_characters: None,
|
||||
is_key_down: false,
|
||||
modifiers: Default::default(),
|
||||
tracking_rect: None,
|
||||
};
|
||||
@@ -289,7 +287,7 @@ extern "C" fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> i
|
||||
let notification_center: &Object =
|
||||
msg_send![class!(NSNotificationCenter), defaultCenter];
|
||||
let notification_name =
|
||||
NSString::alloc(nil).init_str("NSViewFrameDidChangeNotification");
|
||||
IdRef::new(NSString::alloc(nil).init_str("NSViewFrameDidChangeNotification"));
|
||||
let _: () = msg_send![
|
||||
notification_center,
|
||||
addObserver: this
|
||||
@@ -516,7 +514,6 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran
|
||||
let slice =
|
||||
slice::from_raw_parts(characters.UTF8String() as *const c_uchar, characters.len());
|
||||
let string = str::from_utf8_unchecked(slice);
|
||||
state.is_key_down = true;
|
||||
|
||||
// We don't need this now, but it's here if that changes.
|
||||
//let event: id = msg_send![NSApp(), currentEvent];
|
||||
@@ -675,7 +672,7 @@ extern "C" fn key_down(this: &Object, _sel: Sel, event: id) {
|
||||
let pass_along = {
|
||||
AppState::queue_event(EventWrapper::StaticEvent(window_event));
|
||||
// Emit `ReceivedCharacter` for key repeats
|
||||
if is_repeat && state.is_key_down {
|
||||
if is_repeat {
|
||||
for character in characters.chars().filter(|c| !is_corporate_character(*c)) {
|
||||
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id,
|
||||
@@ -705,8 +702,6 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
state.is_key_down = false;
|
||||
|
||||
let scancode = get_scancode(event) as u32;
|
||||
let virtual_keycode = retrieve_keycode(event);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use raw_window_handle::{macos::MacOSHandle, RawWindowHandle};
|
||||
use raw_window_handle::{AppKitHandle, RawWindowHandle};
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
f64,
|
||||
@@ -38,11 +38,12 @@ use cocoa::{
|
||||
NSRequestUserAttentionType, NSScreen, NSView, NSWindow, NSWindowButton, NSWindowStyleMask,
|
||||
},
|
||||
base::{id, nil},
|
||||
foundation::{NSAutoreleasePool, NSDictionary, NSPoint, NSRect, NSSize},
|
||||
foundation::{NSDictionary, NSPoint, NSRect, NSSize},
|
||||
};
|
||||
use core_graphics::display::{CGDisplay, CGDisplayMode};
|
||||
use objc::{
|
||||
declare::ClassDecl,
|
||||
rc::autoreleasepool,
|
||||
runtime::{Class, Object, Sel, BOOL, NO, YES},
|
||||
};
|
||||
|
||||
@@ -50,7 +51,7 @@ use objc::{
|
||||
pub struct Id(pub usize);
|
||||
|
||||
impl Id {
|
||||
pub unsafe fn dummy() -> Self {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
Id(0)
|
||||
}
|
||||
}
|
||||
@@ -118,8 +119,7 @@ fn create_window(
|
||||
attrs: &WindowAttributes,
|
||||
pl_attrs: &PlatformSpecificWindowBuilderAttributes,
|
||||
) -> Option<IdRef> {
|
||||
unsafe {
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
autoreleasepool(|| unsafe {
|
||||
let screen = match attrs.fullscreen {
|
||||
Some(Fullscreen::Borderless(Some(RootMonitorHandle { inner: ref monitor })))
|
||||
| Some(Fullscreen::Exclusive(RootVideoMode {
|
||||
@@ -241,9 +241,8 @@ fn create_window(
|
||||
}
|
||||
ns_window
|
||||
});
|
||||
pool.drain();
|
||||
res
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
struct WindowClass(*const Class);
|
||||
@@ -282,7 +281,10 @@ pub struct SharedState {
|
||||
is_simple_fullscreen: bool,
|
||||
pub saved_style: Option<NSWindowStyleMask>,
|
||||
/// Presentation options saved before entering `set_simple_fullscreen`, and
|
||||
/// restored upon exiting it
|
||||
/// restored upon exiting it. Also used when transitioning from Borderless to
|
||||
/// Exclusive fullscreen in `set_fullscreen` because we need to disable the menu
|
||||
/// bar in exclusive fullscreen but want to restore the original options when
|
||||
/// transitioning back to borderless fullscreen.
|
||||
save_presentation_opts: Option<NSApplicationPresentationOptions>,
|
||||
pub saved_desktop_display_mode: Option<(CGDisplay, CGDisplayMode)>,
|
||||
}
|
||||
@@ -336,17 +338,11 @@ impl UnownedWindow {
|
||||
}
|
||||
trace!("Creating new window");
|
||||
|
||||
let pool = unsafe { NSAutoreleasePool::new(nil) };
|
||||
let ns_window = create_window(&win_attribs, &pl_attribs).ok_or_else(|| {
|
||||
unsafe { pool.drain() };
|
||||
os_error!(OsError::CreationError("Couldn't create `NSWindow`"))
|
||||
})?;
|
||||
let ns_window = create_window(&win_attribs, &pl_attribs)
|
||||
.ok_or_else(|| os_error!(OsError::CreationError("Couldn't create `NSWindow`")))?;
|
||||
|
||||
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`"))
|
||||
})?;
|
||||
let (ns_view, cursor_state) = unsafe { create_view(*ns_window, &pl_attribs) }
|
||||
.ok_or_else(|| os_error!(OsError::CreationError("Couldn't create `NSView`")))?;
|
||||
|
||||
// Configure the new view as the "key view" for the window
|
||||
unsafe {
|
||||
@@ -422,8 +418,6 @@ impl UnownedWindow {
|
||||
window.set_maximized(maximized);
|
||||
}
|
||||
|
||||
unsafe { pool.drain() };
|
||||
|
||||
Ok((window, delegate))
|
||||
}
|
||||
|
||||
@@ -791,6 +785,15 @@ impl UnownedWindow {
|
||||
|
||||
let mut fade_token = ffi::kCGDisplayFadeReservationInvalidToken;
|
||||
|
||||
if matches!(old_fullscreen, Some(Fullscreen::Borderless(_))) {
|
||||
unsafe {
|
||||
let app = NSApp();
|
||||
trace!("Locked shared state in `set_fullscreen`");
|
||||
let mut shared_state_lock = self.shared_state.lock().unwrap();
|
||||
shared_state_lock.save_presentation_opts = Some(app.presentationOptions_());
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// Fade to black (and wait for the fade to complete) to hide the
|
||||
// flicker from capturing the display and switching display mode
|
||||
@@ -841,7 +844,6 @@ impl UnownedWindow {
|
||||
trace!("Locked shared state in `set_fullscreen`");
|
||||
let mut shared_state_lock = self.shared_state.lock().unwrap();
|
||||
shared_state_lock.fullscreen = fullscreen.clone();
|
||||
trace!("Unlocked shared state in `set_fullscreen`");
|
||||
|
||||
INTERRUPT_EVENT_LOOP_EXIT.store(true, Ordering::SeqCst);
|
||||
|
||||
@@ -882,16 +884,42 @@ impl UnownedWindow {
|
||||
// of the menu bar, and this looks broken, so we must make sure
|
||||
// that the menu bar is disabled. This is done in the window
|
||||
// delegate in `window:willUseFullScreenPresentationOptions:`.
|
||||
let app = NSApp();
|
||||
trace!("Locked shared state in `set_fullscreen`");
|
||||
shared_state_lock.save_presentation_opts = Some(app.presentationOptions_());
|
||||
|
||||
let presentation_options =
|
||||
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
|
||||
app.setPresentationOptions_(presentation_options);
|
||||
|
||||
let () = msg_send![*self.ns_window, setLevel: ffi::CGShieldingWindowLevel() + 1];
|
||||
},
|
||||
(
|
||||
&Some(Fullscreen::Exclusive(RootVideoMode { ref video_mode })),
|
||||
&Some(Fullscreen::Borderless(_)),
|
||||
) => unsafe {
|
||||
let presentation_options =
|
||||
shared_state_lock.save_presentation_opts.unwrap_or_else(|| {
|
||||
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar
|
||||
});
|
||||
NSApp().setPresentationOptions_(presentation_options);
|
||||
|
||||
util::restore_display_mode_async(video_mode.monitor().inner.native_identifier());
|
||||
|
||||
// Restore the normal window level following the Borderless fullscreen
|
||||
// `CGShieldingWindowLevel() + 1` hack.
|
||||
let () = msg_send![
|
||||
*self.ns_window,
|
||||
setLevel: ffi::NSWindowLevel::NSNormalWindowLevel
|
||||
];
|
||||
},
|
||||
_ => INTERRUPT_EVENT_LOOP_EXIT.store(false, Ordering::SeqCst),
|
||||
}
|
||||
};
|
||||
trace!("Unlocked shared state in `set_fullscreen`");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -970,6 +998,21 @@ impl UnownedWindow {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn focus_window(&self) {
|
||||
let is_minimized: BOOL = unsafe { msg_send![*self.ns_window, isMiniaturized] };
|
||||
let is_minimized = is_minimized == YES;
|
||||
let is_visible: BOOL = unsafe { msg_send![*self.ns_window, isVisible] };
|
||||
let is_visible = is_visible == YES;
|
||||
|
||||
if !is_minimized && is_visible {
|
||||
unsafe {
|
||||
NSApp().activateIgnoringOtherApps_(YES);
|
||||
util::make_key_and_order_front_async(*self.ns_window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
let ns_request_type = request_type.map(|ty| match ty {
|
||||
@@ -1016,12 +1059,10 @@ impl UnownedWindow {
|
||||
|
||||
#[inline]
|
||||
pub fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
let handle = MacOSHandle {
|
||||
ns_window: *self.ns_window as *mut _,
|
||||
ns_view: *self.ns_view as *mut _,
|
||||
..MacOSHandle::empty()
|
||||
};
|
||||
RawWindowHandle::MacOS(handle)
|
||||
let mut handle = AppKitHandle::empty();
|
||||
handle.ns_window = *self.ns_window as *mut _;
|
||||
handle.ns_view = *self.ns_view as *mut _;
|
||||
RawWindowHandle::AppKit(handle)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,11 @@ use std::{
|
||||
use cocoa::{
|
||||
appkit::{self, NSApplicationPresentationOptions, NSView, NSWindow},
|
||||
base::{id, nil},
|
||||
foundation::{NSAutoreleasePool, NSUInteger},
|
||||
foundation::NSUInteger,
|
||||
};
|
||||
use objc::{
|
||||
declare::ClassDecl,
|
||||
rc::autoreleasepool,
|
||||
runtime::{Class, Object, Sel, BOOL, NO, YES},
|
||||
};
|
||||
|
||||
@@ -274,11 +275,11 @@ extern "C" fn window_will_close(this: &Object, _: Sel, _: id) {
|
||||
trace!("Triggered `windowWillClose:`");
|
||||
with_state(this, |state| unsafe {
|
||||
// `setDelegate:` retains the previous value and then autoreleases it
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
// Since El Capitan, we need to be careful that delegate methods can't
|
||||
// be called after the window closes.
|
||||
let () = msg_send![*state.ns_window, setDelegate: nil];
|
||||
pool.drain();
|
||||
autoreleasepool(|| {
|
||||
// Since El Capitan, we need to be careful that delegate methods can't
|
||||
// be called after the window closes.
|
||||
let () = msg_send![*state.ns_window, setDelegate: nil];
|
||||
});
|
||||
state.emit_event(WindowEvent::Destroyed);
|
||||
});
|
||||
trace!("Completed `windowWillClose:`");
|
||||
@@ -481,10 +482,10 @@ extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) {
|
||||
}
|
||||
|
||||
extern "C" fn window_will_use_fullscreen_presentation_options(
|
||||
_this: &Object,
|
||||
this: &Object,
|
||||
_: Sel,
|
||||
_: id,
|
||||
_proposed_options: NSUInteger,
|
||||
proposed_options: NSUInteger,
|
||||
) -> NSUInteger {
|
||||
// Generally, games will want to disable the menu bar and the dock. Ideally,
|
||||
// this would be configurable by the user. Unfortunately because of our
|
||||
@@ -494,10 +495,22 @@ extern "C" fn window_will_use_fullscreen_presentation_options(
|
||||
// still want to make this configurable for borderless fullscreen. Right now
|
||||
// we don't, for consistency. If we do, it should be documented that the
|
||||
// user-provided options are ignored in exclusive fullscreen.
|
||||
(NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar)
|
||||
.bits()
|
||||
let mut options: NSUInteger = proposed_options;
|
||||
with_state(this, |state| {
|
||||
state.with_window(|window| {
|
||||
trace!("Locked shared state in `window_will_use_fullscreen_presentation_options`");
|
||||
let shared_state = window.shared_state.lock().unwrap();
|
||||
if let Some(Fullscreen::Exclusive(_)) = shared_state.fullscreen {
|
||||
options = (NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar)
|
||||
.bits();
|
||||
}
|
||||
trace!("Unlocked shared state in `window_will_use_fullscreen_presentation_options`");
|
||||
})
|
||||
});
|
||||
|
||||
options
|
||||
}
|
||||
|
||||
/// Invoked when entered fullscreen
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
pub struct Id(pub i32);
|
||||
|
||||
impl Id {
|
||||
pub unsafe fn dummy() -> Self {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
Id(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Brief introduction to the internals of the web backend:
|
||||
// Currently, the web backend supports both wasm-bindgen and stdweb as methods of binding to the
|
||||
// The web backend used to support both wasm-bindgen and stdweb as methods of binding to the
|
||||
// environment. Because they are both supporting the same underlying APIs, the actual web bindings
|
||||
// are cordoned off into backend abstractions, which present the thinnest unifying layer possible.
|
||||
//
|
||||
@@ -17,26 +17,15 @@
|
||||
// incoming events (from the registered handlers) and ensuring they are passed to the user in a
|
||||
// compliant way.
|
||||
|
||||
// Silence warnings from use of deprecated stdweb backend
|
||||
#![allow(deprecated)]
|
||||
|
||||
mod device;
|
||||
mod error;
|
||||
mod event_loop;
|
||||
mod monitor;
|
||||
mod window;
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
#[path = "web_sys/mod.rs"]
|
||||
mod backend;
|
||||
|
||||
#[cfg(feature = "stdweb")]
|
||||
#[path = "stdweb/mod.rs"]
|
||||
mod backend;
|
||||
|
||||
#[cfg(not(any(feature = "web-sys", feature = "stdweb")))]
|
||||
compile_error!("Please select a feature to build for web: `web-sys`, `stdweb`");
|
||||
|
||||
pub use self::device::Id as DeviceId;
|
||||
pub use self::error::OsError;
|
||||
pub use self::event_loop::{
|
||||
|
||||
@@ -1,319 +0,0 @@
|
||||
use super::event;
|
||||
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, PlatformSpecificWindowBuilderAttributes};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use stdweb::js;
|
||||
use stdweb::traits::IPointerEvent;
|
||||
use stdweb::unstable::TryInto;
|
||||
use stdweb::web::event::{
|
||||
BlurEvent, ConcreteEvent, FocusEvent, FullscreenChangeEvent, IEvent, IKeyboardEvent,
|
||||
KeyDownEvent, KeyPressEvent, KeyUpEvent, ModifierKey, MouseWheelEvent, PointerDownEvent,
|
||||
PointerMoveEvent, PointerOutEvent, PointerOverEvent, PointerUpEvent,
|
||||
};
|
||||
use stdweb::web::html_element::CanvasElement;
|
||||
use stdweb::web::{document, EventListenerHandle, IElement, IEventTarget, IHtmlElement};
|
||||
|
||||
#[allow(dead_code)]
|
||||
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>,
|
||||
on_keyboard_release: Option<EventListenerHandle>,
|
||||
on_keyboard_press: Option<EventListenerHandle>,
|
||||
on_received_character: Option<EventListenerHandle>,
|
||||
on_cursor_leave: Option<EventListenerHandle>,
|
||||
on_cursor_enter: Option<EventListenerHandle>,
|
||||
on_cursor_move: Option<EventListenerHandle>,
|
||||
on_mouse_press: Option<EventListenerHandle>,
|
||||
on_mouse_release: Option<EventListenerHandle>,
|
||||
on_mouse_wheel: Option<EventListenerHandle>,
|
||||
on_fullscreen_change: Option<EventListenerHandle>,
|
||||
wants_fullscreen: Rc<RefCell<bool>>,
|
||||
}
|
||||
|
||||
impl Canvas {
|
||||
pub fn create(attr: PlatformSpecificWindowBuilderAttributes) -> Result<Self, RootOE> {
|
||||
let canvas = match attr.canvas {
|
||||
Some(canvas) => canvas,
|
||||
None => document()
|
||||
.create_element("canvas")
|
||||
.map_err(|_| os_error!(OsError("Failed to create canvas element".to_owned())))?
|
||||
.try_into()
|
||||
.map_err(|_| os_error!(OsError("Failed to create canvas element".to_owned())))?,
|
||||
};
|
||||
|
||||
// A tabindex is needed in order to capture local keyboard events.
|
||||
// A "0" value means that the element should be focusable in
|
||||
// sequential keyboard navigation, but its order is defined by the
|
||||
// document's source order.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
|
||||
canvas
|
||||
.set_attribute("tabindex", "0")
|
||||
.map_err(|_| os_error!(OsError("Failed to set a tabindex".to_owned())))?;
|
||||
|
||||
Ok(Canvas {
|
||||
raw: canvas,
|
||||
on_blur: None,
|
||||
on_focus: None,
|
||||
on_keyboard_release: None,
|
||||
on_keyboard_press: None,
|
||||
on_received_character: None,
|
||||
on_cursor_leave: None,
|
||||
on_cursor_enter: None,
|
||||
on_cursor_move: None,
|
||||
on_mouse_release: None,
|
||||
on_mouse_press: None,
|
||||
on_mouse_wheel: None,
|
||||
on_fullscreen_change: None,
|
||||
wants_fullscreen: Rc::new(RefCell::new(false)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_attribute(&self, attribute: &str, value: &str) {
|
||||
self.raw
|
||||
.set_attribute(attribute, value)
|
||||
.expect(&format!("Set attribute: {}", attribute));
|
||||
}
|
||||
|
||||
pub fn position(&self) -> LogicalPosition<f64> {
|
||||
let bounds = self.raw.get_bounding_client_rect();
|
||||
|
||||
LogicalPosition {
|
||||
x: bounds.get_x(),
|
||||
y: bounds.get_y(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
PhysicalSize {
|
||||
width: self.raw.width() as u32,
|
||||
height: self.raw.height() as u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raw(&self) -> &CanvasElement {
|
||||
&self.raw
|
||||
}
|
||||
|
||||
pub fn on_blur<F>(&mut self, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(),
|
||||
{
|
||||
self.on_blur = Some(self.add_event(move |_: BlurEvent| {
|
||||
handler();
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_focus<F>(&mut self, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(),
|
||||
{
|
||||
self.on_focus = Some(self.add_event(move |_: FocusEvent| {
|
||||
handler();
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_keyboard_release<F>(&mut self, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(ScanCode, Option<VirtualKeyCode>, ModifiersState),
|
||||
{
|
||||
self.on_keyboard_release = Some(self.add_user_event(move |event: KeyUpEvent| {
|
||||
event.prevent_default();
|
||||
handler(
|
||||
event::scan_code(&event),
|
||||
event::virtual_key_code(&event),
|
||||
event::keyboard_modifiers(&event),
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_keyboard_press<F>(&mut self, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(ScanCode, Option<VirtualKeyCode>, ModifiersState),
|
||||
{
|
||||
self.on_keyboard_press = Some(self.add_user_event(move |event: KeyDownEvent| {
|
||||
// event.prevent_default() would suppress subsequent on_received_character() calls. That
|
||||
// supression is correct for key sequences like Tab/Shift-Tab, Ctrl+R, PgUp/Down to
|
||||
// scroll, etc. We should not do it for key sequences that result in meaningful character
|
||||
// input though.
|
||||
let event_key = &event.key();
|
||||
let is_key_string = event_key.len() == 1 || !event_key.is_ascii();
|
||||
let is_shortcut_modifiers = (event.ctrl_key() || event.alt_key())
|
||||
&& !event.get_modifier_state(ModifierKey::AltGr);
|
||||
if !is_key_string || is_shortcut_modifiers {
|
||||
event.prevent_default();
|
||||
}
|
||||
handler(
|
||||
event::scan_code(&event),
|
||||
event::virtual_key_code(&event),
|
||||
event::keyboard_modifiers(&event),
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_received_character<F>(&mut self, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(char),
|
||||
{
|
||||
// TODO: Use `beforeinput`.
|
||||
//
|
||||
// The `keypress` event is deprecated, but there does not seem to be a
|
||||
// viable/compatible alternative as of now. `beforeinput` is still widely
|
||||
// unsupported.
|
||||
self.on_received_character = Some(self.add_user_event(move |event: KeyPressEvent| {
|
||||
// Supress further handling to stop keys like the space key from scrolling the page.
|
||||
event.prevent_default();
|
||||
handler(event::codepoint(&event));
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_cursor_leave<F>(&mut self, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(i32),
|
||||
{
|
||||
self.on_cursor_leave = Some(self.add_event(move |event: PointerOutEvent| {
|
||||
handler(event.pointer_id());
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_cursor_enter<F>(&mut self, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(i32),
|
||||
{
|
||||
self.on_cursor_enter = Some(self.add_event(move |event: PointerOverEvent| {
|
||||
handler(event.pointer_id());
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_mouse_release<F>(&mut self, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(i32, MouseButton, ModifiersState),
|
||||
{
|
||||
self.on_mouse_release = Some(self.add_user_event(move |event: PointerUpEvent| {
|
||||
handler(
|
||||
event.pointer_id(),
|
||||
event::mouse_button(&event),
|
||||
event::mouse_modifiers(&event),
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_mouse_press<F>(&mut self, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(i32, PhysicalPosition<f64>, MouseButton, ModifiersState),
|
||||
{
|
||||
let canvas = self.raw.clone();
|
||||
self.on_mouse_press = Some(self.add_user_event(move |event: PointerDownEvent| {
|
||||
handler(
|
||||
event.pointer_id(),
|
||||
event::mouse_position(&event).to_physical(super::scale_factor()),
|
||||
event::mouse_button(&event),
|
||||
event::mouse_modifiers(&event),
|
||||
);
|
||||
canvas
|
||||
.set_pointer_capture(event.pointer_id())
|
||||
.expect("Failed to set pointer capture");
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_cursor_move<F>(&mut self, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(i32, PhysicalPosition<f64>, PhysicalPosition<f64>, ModifiersState),
|
||||
{
|
||||
// todo
|
||||
self.on_cursor_move = Some(self.add_event(move |event: PointerMoveEvent| {
|
||||
handler(
|
||||
event.pointer_id(),
|
||||
event::mouse_position(&event).to_physical(super::scale_factor()),
|
||||
event::mouse_delta(&event).to_physical(super::scale_factor()),
|
||||
event::mouse_modifiers(&event),
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_mouse_wheel<F>(&mut self, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(i32, MouseScrollDelta, ModifiersState),
|
||||
{
|
||||
self.on_mouse_wheel = Some(self.add_event(move |event: MouseWheelEvent| {
|
||||
event.prevent_default();
|
||||
if let Some(delta) = event::mouse_scroll_delta(&event) {
|
||||
handler(0, delta, event::mouse_modifiers(&event));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_fullscreen_change<F>(&mut self, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(),
|
||||
{
|
||||
self.on_fullscreen_change = Some(self.add_event(move |_: FullscreenChangeEvent| handler()));
|
||||
}
|
||||
|
||||
pub fn on_dark_mode<F>(&mut self, handler: F)
|
||||
where
|
||||
F: 'static + FnMut(bool),
|
||||
{
|
||||
// TODO: upstream to stdweb
|
||||
js! {
|
||||
var handler = @{handler};
|
||||
|
||||
if (window.matchMedia) {
|
||||
window.matchMedia("(prefers-color-scheme: dark)").addListener(function(e) {
|
||||
handler(event.matches)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_event<E, F>(&self, mut handler: F) -> EventListenerHandle
|
||||
where
|
||||
E: ConcreteEvent,
|
||||
F: 'static + FnMut(E),
|
||||
{
|
||||
self.raw.add_event_listener(move |event: E| {
|
||||
event.stop_propagation();
|
||||
event.cancel_bubble();
|
||||
|
||||
handler(event);
|
||||
})
|
||||
}
|
||||
|
||||
// The difference between add_event and add_user_event is that the latter has a special meaning
|
||||
// for browser security. A user event is a deliberate action by the user (like a mouse or key
|
||||
// press) and is the only time things like a fullscreen request may be successfully completed.)
|
||||
fn add_user_event<E, F>(&self, mut handler: F) -> EventListenerHandle
|
||||
where
|
||||
E: ConcreteEvent,
|
||||
F: 'static + FnMut(E),
|
||||
{
|
||||
let wants_fullscreen = self.wants_fullscreen.clone();
|
||||
let canvas = self.raw.clone();
|
||||
|
||||
self.add_event(move |event: E| {
|
||||
handler(event);
|
||||
|
||||
if *wants_fullscreen.borrow() {
|
||||
canvas.request_fullscreen();
|
||||
*wants_fullscreen.borrow_mut() = false;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn request_fullscreen(&self) {
|
||||
*self.wants_fullscreen.borrow_mut() = true;
|
||||
}
|
||||
|
||||
pub fn is_fullscreen(&self) -> bool {
|
||||
super::is_fullscreen(&self.raw)
|
||||
}
|
||||
|
||||
pub fn remove_listeners(&mut self) {
|
||||
// TODO: Stub, unimplemented (see web_sys for reference).
|
||||
}
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
use crate::dpi::LogicalPosition;
|
||||
use crate::event::{ModifiersState, MouseButton, MouseScrollDelta, ScanCode, VirtualKeyCode};
|
||||
|
||||
use stdweb::web::event::{IKeyboardEvent, IMouseEvent, MouseWheelDeltaMode, MouseWheelEvent};
|
||||
use stdweb::{js, unstable::TryInto, JsSerialize};
|
||||
|
||||
pub fn mouse_button(event: &impl IMouseEvent) -> MouseButton {
|
||||
match event.button() {
|
||||
stdweb::web::event::MouseButton::Left => MouseButton::Left,
|
||||
stdweb::web::event::MouseButton::Right => MouseButton::Right,
|
||||
stdweb::web::event::MouseButton::Wheel => MouseButton::Middle,
|
||||
stdweb::web::event::MouseButton::Button4 => MouseButton::Other(0),
|
||||
stdweb::web::event::MouseButton::Button5 => MouseButton::Other(1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mouse_modifiers(event: &impl IMouseEvent) -> ModifiersState {
|
||||
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<f64> {
|
||||
LogicalPosition {
|
||||
x: event.offset_x() as f64,
|
||||
y: event.offset_y() as f64,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mouse_delta(event: &impl IMouseEvent) -> LogicalPosition<f64> {
|
||||
LogicalPosition {
|
||||
x: event.movement_x() as f64,
|
||||
y: event.movement_y() as f64,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mouse_scroll_delta(event: &MouseWheelEvent) -> Option<MouseScrollDelta> {
|
||||
let x = event.delta_x();
|
||||
let y = -event.delta_y();
|
||||
|
||||
match event.delta_mode() {
|
||||
MouseWheelDeltaMode::Line => Some(MouseScrollDelta::LineDelta(x as f32, y as f32)),
|
||||
MouseWheelDeltaMode::Pixel => {
|
||||
let delta = LogicalPosition::new(x, y).to_physical(super::scale_factor());
|
||||
Some(MouseScrollDelta::PixelDelta(delta))
|
||||
}
|
||||
MouseWheelDeltaMode::Page => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scan_code<T: JsSerialize>(event: &T) -> ScanCode {
|
||||
let key_code = js! ( return @{event}.keyCode; );
|
||||
|
||||
key_code
|
||||
.try_into()
|
||||
.expect("The which value should be a number")
|
||||
}
|
||||
|
||||
pub fn virtual_key_code(event: &impl IKeyboardEvent) -> Option<VirtualKeyCode> {
|
||||
Some(match &event.code()[..] {
|
||||
"Digit1" => VirtualKeyCode::Key1,
|
||||
"Digit2" => VirtualKeyCode::Key2,
|
||||
"Digit3" => VirtualKeyCode::Key3,
|
||||
"Digit4" => VirtualKeyCode::Key4,
|
||||
"Digit5" => VirtualKeyCode::Key5,
|
||||
"Digit6" => VirtualKeyCode::Key6,
|
||||
"Digit7" => VirtualKeyCode::Key7,
|
||||
"Digit8" => VirtualKeyCode::Key8,
|
||||
"Digit9" => VirtualKeyCode::Key9,
|
||||
"Digit0" => VirtualKeyCode::Key0,
|
||||
"KeyA" => VirtualKeyCode::A,
|
||||
"KeyB" => VirtualKeyCode::B,
|
||||
"KeyC" => VirtualKeyCode::C,
|
||||
"KeyD" => VirtualKeyCode::D,
|
||||
"KeyE" => VirtualKeyCode::E,
|
||||
"KeyF" => VirtualKeyCode::F,
|
||||
"KeyG" => VirtualKeyCode::G,
|
||||
"KeyH" => VirtualKeyCode::H,
|
||||
"KeyI" => VirtualKeyCode::I,
|
||||
"KeyJ" => VirtualKeyCode::J,
|
||||
"KeyK" => VirtualKeyCode::K,
|
||||
"KeyL" => VirtualKeyCode::L,
|
||||
"KeyM" => VirtualKeyCode::M,
|
||||
"KeyN" => VirtualKeyCode::N,
|
||||
"KeyO" => VirtualKeyCode::O,
|
||||
"KeyP" => VirtualKeyCode::P,
|
||||
"KeyQ" => VirtualKeyCode::Q,
|
||||
"KeyR" => VirtualKeyCode::R,
|
||||
"KeyS" => VirtualKeyCode::S,
|
||||
"KeyT" => VirtualKeyCode::T,
|
||||
"KeyU" => VirtualKeyCode::U,
|
||||
"KeyV" => VirtualKeyCode::V,
|
||||
"KeyW" => VirtualKeyCode::W,
|
||||
"KeyX" => VirtualKeyCode::X,
|
||||
"KeyY" => VirtualKeyCode::Y,
|
||||
"KeyZ" => VirtualKeyCode::Z,
|
||||
"Escape" => VirtualKeyCode::Escape,
|
||||
"F1" => VirtualKeyCode::F1,
|
||||
"F2" => VirtualKeyCode::F2,
|
||||
"F3" => VirtualKeyCode::F3,
|
||||
"F4" => VirtualKeyCode::F4,
|
||||
"F5" => VirtualKeyCode::F5,
|
||||
"F6" => VirtualKeyCode::F6,
|
||||
"F7" => VirtualKeyCode::F7,
|
||||
"F8" => VirtualKeyCode::F8,
|
||||
"F9" => VirtualKeyCode::F9,
|
||||
"F10" => VirtualKeyCode::F10,
|
||||
"F11" => VirtualKeyCode::F11,
|
||||
"F12" => VirtualKeyCode::F12,
|
||||
"F13" => VirtualKeyCode::F13,
|
||||
"F14" => VirtualKeyCode::F14,
|
||||
"F15" => VirtualKeyCode::F15,
|
||||
"F16" => VirtualKeyCode::F16,
|
||||
"F17" => VirtualKeyCode::F17,
|
||||
"F18" => VirtualKeyCode::F18,
|
||||
"F19" => VirtualKeyCode::F19,
|
||||
"F20" => VirtualKeyCode::F20,
|
||||
"F21" => VirtualKeyCode::F21,
|
||||
"F22" => VirtualKeyCode::F22,
|
||||
"F23" => VirtualKeyCode::F23,
|
||||
"F24" => VirtualKeyCode::F24,
|
||||
"PrintScreen" => VirtualKeyCode::Snapshot,
|
||||
"ScrollLock" => VirtualKeyCode::Scroll,
|
||||
"Pause" => VirtualKeyCode::Pause,
|
||||
"Insert" => VirtualKeyCode::Insert,
|
||||
"Home" => VirtualKeyCode::Home,
|
||||
"Delete" => VirtualKeyCode::Delete,
|
||||
"End" => VirtualKeyCode::End,
|
||||
"PageDown" => VirtualKeyCode::PageDown,
|
||||
"PageUp" => VirtualKeyCode::PageUp,
|
||||
"ArrowLeft" => VirtualKeyCode::Left,
|
||||
"ArrowUp" => VirtualKeyCode::Up,
|
||||
"ArrowRight" => VirtualKeyCode::Right,
|
||||
"ArrowDown" => VirtualKeyCode::Down,
|
||||
"Backspace" => VirtualKeyCode::Back,
|
||||
"Enter" => VirtualKeyCode::Return,
|
||||
"Space" => VirtualKeyCode::Space,
|
||||
"Compose" => VirtualKeyCode::Compose,
|
||||
"Caret" => VirtualKeyCode::Caret,
|
||||
"NumLock" => VirtualKeyCode::Numlock,
|
||||
"Numpad0" => VirtualKeyCode::Numpad0,
|
||||
"Numpad1" => VirtualKeyCode::Numpad1,
|
||||
"Numpad2" => VirtualKeyCode::Numpad2,
|
||||
"Numpad3" => VirtualKeyCode::Numpad3,
|
||||
"Numpad4" => VirtualKeyCode::Numpad4,
|
||||
"Numpad5" => VirtualKeyCode::Numpad5,
|
||||
"Numpad6" => VirtualKeyCode::Numpad6,
|
||||
"Numpad7" => VirtualKeyCode::Numpad7,
|
||||
"Numpad8" => VirtualKeyCode::Numpad8,
|
||||
"Numpad9" => VirtualKeyCode::Numpad9,
|
||||
"AbntC1" => VirtualKeyCode::AbntC1,
|
||||
"AbntC2" => VirtualKeyCode::AbntC2,
|
||||
"NumpadAdd" => VirtualKeyCode::NumpadAdd,
|
||||
"Quote" => VirtualKeyCode::Apostrophe,
|
||||
"Apps" => VirtualKeyCode::Apps,
|
||||
"At" => VirtualKeyCode::At,
|
||||
"Ax" => VirtualKeyCode::Ax,
|
||||
"Backslash" => VirtualKeyCode::Backslash,
|
||||
"Calculator" => VirtualKeyCode::Calculator,
|
||||
"Capital" => VirtualKeyCode::Capital,
|
||||
"Semicolon" => VirtualKeyCode::Semicolon,
|
||||
"Comma" => VirtualKeyCode::Comma,
|
||||
"Convert" => VirtualKeyCode::Convert,
|
||||
"NumpadDecimal" => VirtualKeyCode::NumpadDecimal,
|
||||
"NumpadDivide" => VirtualKeyCode::NumpadDivide,
|
||||
"Equal" => VirtualKeyCode::Equals,
|
||||
"Backquote" => VirtualKeyCode::Grave,
|
||||
"Kana" => VirtualKeyCode::Kana,
|
||||
"Kanji" => VirtualKeyCode::Kanji,
|
||||
"AltLeft" => VirtualKeyCode::LAlt,
|
||||
"BracketLeft" => VirtualKeyCode::LBracket,
|
||||
"ControlLeft" => VirtualKeyCode::LControl,
|
||||
"ShiftLeft" => VirtualKeyCode::LShift,
|
||||
"MetaLeft" => VirtualKeyCode::LWin,
|
||||
"Mail" => VirtualKeyCode::Mail,
|
||||
"MediaSelect" => VirtualKeyCode::MediaSelect,
|
||||
"MediaStop" => VirtualKeyCode::MediaStop,
|
||||
"Minus" => VirtualKeyCode::Minus,
|
||||
"NumpadMultiply" => VirtualKeyCode::NumpadMultiply,
|
||||
"Mute" => VirtualKeyCode::Mute,
|
||||
"LaunchMyComputer" => VirtualKeyCode::MyComputer,
|
||||
"NavigateForward" => VirtualKeyCode::NavigateForward,
|
||||
"NavigateBackward" => VirtualKeyCode::NavigateBackward,
|
||||
"NextTrack" => VirtualKeyCode::NextTrack,
|
||||
"NoConvert" => VirtualKeyCode::NoConvert,
|
||||
"NumpadComma" => VirtualKeyCode::NumpadComma,
|
||||
"NumpadEnter" => VirtualKeyCode::NumpadEnter,
|
||||
"NumpadEquals" => VirtualKeyCode::NumpadEquals,
|
||||
"OEM102" => VirtualKeyCode::OEM102,
|
||||
"Period" => VirtualKeyCode::Period,
|
||||
"PlayPause" => VirtualKeyCode::PlayPause,
|
||||
"Power" => VirtualKeyCode::Power,
|
||||
"PrevTrack" => VirtualKeyCode::PrevTrack,
|
||||
"AltRight" => VirtualKeyCode::RAlt,
|
||||
"BracketRight" => VirtualKeyCode::RBracket,
|
||||
"ControlRight" => VirtualKeyCode::RControl,
|
||||
"ShiftRight" => VirtualKeyCode::RShift,
|
||||
"MetaRight" => VirtualKeyCode::RWin,
|
||||
"Slash" => VirtualKeyCode::Slash,
|
||||
"Sleep" => VirtualKeyCode::Sleep,
|
||||
"Stop" => VirtualKeyCode::Stop,
|
||||
"NumpadSubtract" => VirtualKeyCode::NumpadSubtract,
|
||||
"Sysrq" => VirtualKeyCode::Sysrq,
|
||||
"Tab" => VirtualKeyCode::Tab,
|
||||
"Underline" => VirtualKeyCode::Underline,
|
||||
"Unlabeled" => VirtualKeyCode::Unlabeled,
|
||||
"AudioVolumeDown" => VirtualKeyCode::VolumeDown,
|
||||
"AudioVolumeUp" => VirtualKeyCode::VolumeUp,
|
||||
"Wake" => VirtualKeyCode::Wake,
|
||||
"WebBack" => VirtualKeyCode::WebBack,
|
||||
"WebFavorites" => VirtualKeyCode::WebFavorites,
|
||||
"WebForward" => VirtualKeyCode::WebForward,
|
||||
"WebHome" => VirtualKeyCode::WebHome,
|
||||
"WebRefresh" => VirtualKeyCode::WebRefresh,
|
||||
"WebSearch" => VirtualKeyCode::WebSearch,
|
||||
"WebStop" => VirtualKeyCode::WebStop,
|
||||
"Yen" => VirtualKeyCode::Yen,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn keyboard_modifiers(event: &impl IKeyboardEvent) -> ModifiersState {
|
||||
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 {
|
||||
// `event.key()` always returns a non-empty `String`. Therefore, this should
|
||||
// never panic.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
|
||||
event.key().chars().next().unwrap()
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
#![deprecated(since = "0.23.0", note = "Please migrate to web-sys over stdweb")]
|
||||
|
||||
mod canvas;
|
||||
mod event;
|
||||
mod scaling;
|
||||
mod timeout;
|
||||
|
||||
pub use self::canvas::Canvas;
|
||||
pub use self::scaling::ScaleChangeDetector;
|
||||
pub use self::timeout::{AnimationFrameRequest, Timeout};
|
||||
|
||||
use crate::dpi::{LogicalSize, Size};
|
||||
use crate::platform::web::WindowExtStdweb;
|
||||
use crate::window::Window;
|
||||
|
||||
use stdweb::js;
|
||||
use stdweb::unstable::TryInto;
|
||||
use stdweb::web::event::BeforeUnloadEvent;
|
||||
use stdweb::web::window;
|
||||
use stdweb::web::IEventTarget;
|
||||
use stdweb::web::{document, html_element::CanvasElement, Element};
|
||||
|
||||
pub fn throw(msg: &str) {
|
||||
js! { throw @{msg} }
|
||||
}
|
||||
|
||||
pub fn exit_fullscreen() {
|
||||
document().exit_fullscreen();
|
||||
}
|
||||
|
||||
pub type UnloadEventHandle = ();
|
||||
|
||||
pub fn on_unload(mut handler: impl FnMut() + 'static) -> UnloadEventHandle {
|
||||
window().add_event_listener(move |_: BeforeUnloadEvent| handler());
|
||||
}
|
||||
|
||||
impl WindowExtStdweb for Window {
|
||||
fn canvas(&self) -> CanvasElement {
|
||||
self.window.canvas().raw().clone()
|
||||
}
|
||||
|
||||
fn is_dark_mode(&self) -> bool {
|
||||
// TODO: upstream to stdweb
|
||||
let is_dark_mode = js! {
|
||||
return (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches)
|
||||
};
|
||||
|
||||
is_dark_mode.try_into().expect("should return a bool")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn window_size() -> LogicalSize<f64> {
|
||||
let window = window();
|
||||
let width = window.inner_width() as f64;
|
||||
let height = window.inner_height() as f64;
|
||||
|
||||
LogicalSize { width, height }
|
||||
}
|
||||
|
||||
pub fn scale_factor() -> f64 {
|
||||
let window = window();
|
||||
window.device_pixel_ratio()
|
||||
}
|
||||
|
||||
pub fn set_canvas_size(raw: &CanvasElement, 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);
|
||||
|
||||
set_canvas_style_property(raw, "width", &format!("{}px", logical_size.width));
|
||||
set_canvas_style_property(raw, "height", &format!("{}px", logical_size.height));
|
||||
}
|
||||
|
||||
pub fn set_canvas_style_property(raw: &CanvasElement, style_attribute: &str, value: &str) {
|
||||
js! {
|
||||
@{raw.as_ref()}.style[@{style_attribute}] = @{value};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_fullscreen(canvas: &CanvasElement) -> bool {
|
||||
match document().fullscreen_element() {
|
||||
Some(elem) => {
|
||||
let raw: Element = canvas.clone().into();
|
||||
raw == elem
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub type RawCanvasType = CanvasElement;
|
||||
@@ -1,13 +0,0 @@
|
||||
use super::super::ScaleChangeArgs;
|
||||
|
||||
pub struct ScaleChangeDetector(());
|
||||
|
||||
impl ScaleChangeDetector {
|
||||
pub(crate) fn new<F>(_handler: F) -> Self
|
||||
where
|
||||
F: 'static + FnMut(ScaleChangeArgs),
|
||||
{
|
||||
// TODO: Stub, unimplemented (see web_sys for reference).
|
||||
Self(())
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
use stdweb::web::{window, IWindowOrWorker, RequestAnimationFrameHandle, TimeoutHandle};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Timeout {
|
||||
handle: Option<TimeoutHandle>,
|
||||
}
|
||||
|
||||
impl Timeout {
|
||||
pub fn new<F>(f: F, duration: Duration) -> Timeout
|
||||
where
|
||||
F: 'static + FnMut(),
|
||||
{
|
||||
Timeout {
|
||||
handle: Some(window().set_clearable_timeout(f, duration.as_millis() as u32)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Timeout {
|
||||
fn drop(&mut self) {
|
||||
let handle = self.handle.take().unwrap();
|
||||
handle.clear();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AnimationFrameRequest {
|
||||
handle: Option<RequestAnimationFrameHandle>,
|
||||
// track callback state, because `cancelAnimationFrame` is slow
|
||||
fired: Rc<Cell<bool>>,
|
||||
}
|
||||
|
||||
impl AnimationFrameRequest {
|
||||
pub fn new<F>(mut f: F) -> AnimationFrameRequest
|
||||
where
|
||||
F: 'static + FnMut(),
|
||||
{
|
||||
let fired = Rc::new(Cell::new(false));
|
||||
let c_fired = fired.clone();
|
||||
let handle = window().request_animation_frame(move |_| {
|
||||
(*c_fired).set(true);
|
||||
f();
|
||||
});
|
||||
|
||||
AnimationFrameRequest {
|
||||
handle: Some(handle),
|
||||
fired,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AnimationFrameRequest {
|
||||
fn drop(&mut self) {
|
||||
if !(*self.fired).get() {
|
||||
if let Some(handle) = self.handle.take() {
|
||||
handle.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,6 +89,20 @@ impl Canvas {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), RootOE> {
|
||||
if grab {
|
||||
self.raw().request_pointer_lock();
|
||||
} else {
|
||||
let window = web_sys::window()
|
||||
.ok_or(os_error!(OsError("Failed to obtain window".to_owned())))?;
|
||||
let document = window
|
||||
.document()
|
||||
.ok_or(os_error!(OsError("Failed to obtain document".to_owned())))?;
|
||||
document.exit_pointer_lock();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_attribute(&self, attribute: &str, value: &str) {
|
||||
self.common
|
||||
.raw
|
||||
|
||||
@@ -79,9 +79,11 @@ impl PointerHandler {
|
||||
event::mouse_button(&event),
|
||||
event::mouse_modifiers(&event),
|
||||
);
|
||||
canvas
|
||||
.set_pointer_capture(event.pointer_id())
|
||||
.expect("Failed to set pointer capture");
|
||||
|
||||
// Error is swallowed here since the error would occur every time the mouse is
|
||||
// clicked when the cursor is grabbed, and there is probably not a situation where
|
||||
// this could fail, that we care if it fails.
|
||||
let _e = canvas.set_pointer_capture(event.pointer_id());
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::window::{
|
||||
CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWI,
|
||||
};
|
||||
|
||||
use raw_window_handle::web::WebHandle;
|
||||
use raw_window_handle::{RawWindowHandle, WebHandle};
|
||||
|
||||
use super::{backend, monitor, EventLoopWindowTarget};
|
||||
|
||||
@@ -207,8 +207,11 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_grab(&self, _grab: bool) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
|
||||
self.canvas
|
||||
.borrow()
|
||||
.set_cursor_grab(grab)
|
||||
.map_err(|e| ExternalError::Os(e))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -281,6 +284,11 @@ impl Window {
|
||||
// Currently a no-op as it does not seem there is good support for this on web
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn focus_window(&self) {
|
||||
// Currently a no-op as it does not seem there is good support for this on web
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_user_attention(&self, _request_type: Option<UserAttentionType>) {
|
||||
// Currently an intentional no-op
|
||||
@@ -317,13 +325,10 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
|
||||
let handle = WebHandle {
|
||||
id: self.id.0,
|
||||
..WebHandle::empty()
|
||||
};
|
||||
|
||||
raw_window_handle::RawWindowHandle::Web(handle)
|
||||
pub fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
let mut handle = WebHandle::empty();
|
||||
handle.id = self.id.0;
|
||||
RawWindowHandle::Web(handle)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,7 +344,7 @@ impl Drop for Window {
|
||||
pub struct Id(pub(crate) u32);
|
||||
|
||||
impl Id {
|
||||
pub unsafe fn dummy() -> Id {
|
||||
pub const unsafe fn dummy() -> Id {
|
||||
Id(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,38 +6,24 @@ use std::os::windows::ffi::OsStrExt;
|
||||
use winapi::{
|
||||
shared::{
|
||||
basetsd::SIZE_T,
|
||||
minwindef::{BOOL, DWORD, FALSE, UINT, ULONG, WORD},
|
||||
ntdef::{LPSTR, NTSTATUS, NT_SUCCESS, PVOID, WCHAR},
|
||||
minwindef::{BOOL, DWORD, FALSE, WORD},
|
||||
ntdef::{NTSTATUS, NT_SUCCESS, PVOID},
|
||||
windef::HWND,
|
||||
winerror::S_OK,
|
||||
},
|
||||
um::{libloaderapi, uxtheme, winuser},
|
||||
um::{libloaderapi, uxtheme, winnt, winuser},
|
||||
};
|
||||
|
||||
use crate::window::Theme;
|
||||
|
||||
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;
|
||||
type RtlGetVersion = unsafe extern "system" fn (*mut winnt::OSVERSIONINFOW) -> NTSTATUS;
|
||||
let handle = get_function!("ntdll.dll", RtlGetVersion);
|
||||
|
||||
if let Some(rtl_get_version) = handle {
|
||||
unsafe {
|
||||
let mut vi = OSVERSIONINFOW {
|
||||
let mut vi = winnt::OSVERSIONINFOW {
|
||||
dwOSVersionInfoSize: 0,
|
||||
dwMajorVersion: 0,
|
||||
dwMinorVersion: 0,
|
||||
@@ -108,11 +94,12 @@ fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) -> bool {
|
||||
type SetWindowCompositionAttribute =
|
||||
unsafe extern "system" fn(HWND, *mut WINDOWCOMPOSITIONATTRIBDATA) -> BOOL;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
type WINDOWCOMPOSITIONATTRIB = u32;
|
||||
const WCA_USEDARKMODECOLORS: WINDOWCOMPOSITIONATTRIB = 26;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[repr(C)]
|
||||
struct WINDOWCOMPOSITIONATTRIBDATA {
|
||||
Attrib: WINDOWCOMPOSITIONATTRIB,
|
||||
@@ -181,21 +168,8 @@ fn should_apps_use_dark_mode() -> bool {
|
||||
.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 {
|
||||
let mut hc = winuser::HIGHCONTRASTA {
|
||||
cbSize: 0,
|
||||
dwFlags: 0,
|
||||
lpszDefaultScheme: std::ptr::null_mut(),
|
||||
@@ -210,7 +184,7 @@ fn is_high_contrast() -> bool {
|
||||
)
|
||||
};
|
||||
|
||||
ok != FALSE && (HCF_HIGHCONTRASTON & hc.dwFlags) == 1
|
||||
ok != FALSE && (winuser::HCF_HIGHCONTRASTON & hc.dwFlags) == 1
|
||||
}
|
||||
|
||||
fn widestring(src: &'static str) -> Vec<u16> {
|
||||
|
||||
@@ -181,7 +181,7 @@ impl FileDropHandler {
|
||||
},
|
||||
};
|
||||
|
||||
let mut drop_format = FORMATETC {
|
||||
let drop_format = FORMATETC {
|
||||
cfFormat: CF_HDROP as CLIPFORMAT,
|
||||
ptd: ptr::null(),
|
||||
dwAspect: DVASPECT_CONTENT,
|
||||
@@ -190,7 +190,7 @@ impl FileDropHandler {
|
||||
};
|
||||
|
||||
let mut medium = std::mem::zeroed();
|
||||
let get_data_result = (*data_obj).GetData(&mut drop_format, &mut medium);
|
||||
let get_data_result = (*data_obj).GetData(&drop_format, &mut medium);
|
||||
if SUCCEEDED(get_data_result) {
|
||||
let hglobal = (*medium.u).hGlobal();
|
||||
let hdrop = (*hglobal) as shellapi::HDROP;
|
||||
@@ -213,15 +213,15 @@ impl FileDropHandler {
|
||||
callback(OsString::from_wide(&path_buf[0..character_count]).into());
|
||||
}
|
||||
|
||||
return Some(hdrop);
|
||||
Some(hdrop)
|
||||
} else if get_data_result == DV_E_FORMATETC {
|
||||
// If the dropped item is not a file this error will occur.
|
||||
// In this case it is OK to return without taking further action.
|
||||
debug!("Error occured while processing dropped/hovered item: item is not a file.");
|
||||
return None;
|
||||
None
|
||||
} else {
|
||||
debug!("Unexpected error occured while processing dropped/hovered item.");
|
||||
return None;
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ pub fn get_key_mods() -> ModifiersState {
|
||||
bitflags! {
|
||||
#[derive(Default)]
|
||||
pub struct ModifiersStateSide: u32 {
|
||||
const LSHIFT = 0b010 << 0;
|
||||
const RSHIFT = 0b001 << 0;
|
||||
const LSHIFT = 0b010;
|
||||
const RSHIFT = 0b001;
|
||||
|
||||
const LCTRL = 0b010 << 3;
|
||||
const RCTRL = 0b001 << 3;
|
||||
@@ -343,7 +343,7 @@ pub fn handle_extended_keys(
|
||||
extended: bool,
|
||||
) -> Option<(c_int, UINT)> {
|
||||
// Welcome to hell https://blog.molecular-matters.com/2011/09/05/properly-handling-keyboard-input/
|
||||
scancode = if extended { 0xE000 } else { 0x0000 } | scancode;
|
||||
scancode |= if extended { 0xE000 } else { 0x0000 };
|
||||
let vkey = match vkey {
|
||||
winuser::VK_SHIFT => unsafe {
|
||||
winuser::MapVirtualKeyA(scancode, winuser::MAPVK_VSC_TO_VK_EX) as _
|
||||
@@ -369,7 +369,7 @@ pub fn handle_extended_keys(
|
||||
// Don't emit anything for the LeftControl event in the pair...
|
||||
0xE01D if vkey == winuser::VK_PAUSE => return None,
|
||||
// ...and emit the Pause event for the second event in the pair.
|
||||
0x45 if vkey == winuser::VK_PAUSE || vkey == 0xFF as _ => {
|
||||
0x45 if vkey == winuser::VK_PAUSE || vkey == 0xFF => {
|
||||
scancode = 0xE059;
|
||||
winuser::VK_PAUSE
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@ use crate::{
|
||||
pub(crate) type EventLoopRunnerShared<T> = Rc<EventLoopRunner<T>>;
|
||||
pub(crate) struct EventLoopRunner<T: 'static> {
|
||||
// The event loop's win32 handles
|
||||
thread_msg_target: HWND,
|
||||
pub(super) thread_msg_target: HWND,
|
||||
wait_thread_id: DWORD,
|
||||
|
||||
control_flow: Cell<ControlFlow>,
|
||||
|
||||
@@ -58,7 +58,7 @@ unsafe impl Sync for Cursor {}
|
||||
pub struct DeviceId(u32);
|
||||
|
||||
impl DeviceId {
|
||||
pub unsafe fn dummy() -> Self {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
DeviceId(0)
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,7 @@ unsafe impl Send for WindowId {}
|
||||
unsafe impl Sync for WindowId {}
|
||||
|
||||
impl WindowId {
|
||||
pub unsafe fn dummy() -> Self {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
use std::ptr::null_mut;
|
||||
|
||||
WindowId(null_mut())
|
||||
|
||||
@@ -30,7 +30,7 @@ where
|
||||
}
|
||||
|
||||
pub fn wchar_to_string(wchar: &[wchar_t]) -> String {
|
||||
String::from_utf16_lossy(wchar).to_string()
|
||||
String::from_utf16_lossy(wchar)
|
||||
}
|
||||
|
||||
pub fn wchar_ptr_to_string(wchar: *const wchar_t) -> String {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use raw_window_handle::{windows::WindowsHandle, RawWindowHandle};
|
||||
use raw_window_handle::{RawWindowHandle, Win32Handle};
|
||||
use std::{
|
||||
cell::Cell,
|
||||
ffi::OsStr,
|
||||
io, mem,
|
||||
os::windows::ffi::OsStrExt,
|
||||
ptr,
|
||||
panic, ptr,
|
||||
sync::{mpsc::channel, Arc},
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ use winapi::{
|
||||
shared::{
|
||||
minwindef::{HINSTANCE, LPARAM, UINT, WPARAM},
|
||||
windef::{HWND, POINT, POINTS, RECT},
|
||||
winerror::SUCCEEDED,
|
||||
},
|
||||
um::{
|
||||
combaseapi, dwmapi,
|
||||
@@ -38,9 +39,9 @@ use crate::{
|
||||
monitor::MonitorHandle as RootMonitorHandle,
|
||||
platform_impl::platform::{
|
||||
dark_mode::try_theme,
|
||||
dpi::{dpi_to_scale_factor, hwnd_dpi},
|
||||
dpi::{dpi_to_scale_factor, enable_non_client_dpi_scaling, hwnd_dpi},
|
||||
drop_handler::FileDropHandler,
|
||||
event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID},
|
||||
event_loop::{self, EventLoopWindowTarget, WindowLongPtr, DESTROY_MSG_ID},
|
||||
icon::{self, IconType},
|
||||
monitor, util,
|
||||
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
|
||||
@@ -71,58 +72,7 @@ impl Window {
|
||||
// First person to remove the need for cloning here gets a cookie!
|
||||
//
|
||||
// done. you owe me -- ossi
|
||||
unsafe {
|
||||
let drag_and_drop = pl_attr.drag_and_drop;
|
||||
init(w_attr, pl_attr, event_loop).map(|win| {
|
||||
let file_drop_handler = if drag_and_drop {
|
||||
use winapi::shared::winerror::{OLE_E_WRONGCOMPOBJ, RPC_E_CHANGED_MODE, S_OK};
|
||||
|
||||
let ole_init_result = ole2::OleInitialize(ptr::null_mut());
|
||||
// It is ok if the initialize result is `S_FALSE` because it might happen that
|
||||
// multiple windows are created on the same thread.
|
||||
if ole_init_result == OLE_E_WRONGCOMPOBJ {
|
||||
panic!("OleInitialize failed! Result was: `OLE_E_WRONGCOMPOBJ`");
|
||||
} else if ole_init_result == RPC_E_CHANGED_MODE {
|
||||
panic!(
|
||||
"OleInitialize failed! Result was: `RPC_E_CHANGED_MODE`. \
|
||||
Make sure other crates are not using multithreaded COM library \
|
||||
on the same thread or disable drag and drop support."
|
||||
);
|
||||
}
|
||||
|
||||
let file_drop_runner = event_loop.runner_shared.clone();
|
||||
let file_drop_handler = FileDropHandler::new(
|
||||
win.window.0,
|
||||
Box::new(move |event| {
|
||||
if let Ok(e) = event.map_nonuser_event() {
|
||||
file_drop_runner.send_event(e)
|
||||
}
|
||||
}),
|
||||
);
|
||||
let handler_interface_ptr =
|
||||
&mut (*file_drop_handler.data).interface as LPDROPTARGET;
|
||||
|
||||
assert_eq!(
|
||||
ole2::RegisterDragDrop(win.window.0, handler_interface_ptr),
|
||||
S_OK
|
||||
);
|
||||
Some(file_drop_handler)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let subclass_input = event_loop::SubclassInput {
|
||||
window_state: win.window_state.clone(),
|
||||
event_loop_runner: event_loop.runner_shared.clone(),
|
||||
file_drop_handler,
|
||||
subclass_removed: Cell::new(false),
|
||||
recurse_depth: Cell::new(0),
|
||||
};
|
||||
|
||||
event_loop::subclass_window(win.window.0, subclass_input);
|
||||
win
|
||||
})
|
||||
}
|
||||
unsafe { init(w_attr, pl_attr, event_loop) }
|
||||
}
|
||||
|
||||
pub fn set_title(&self, text: &str) {
|
||||
@@ -284,12 +234,10 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
let handle = WindowsHandle {
|
||||
hwnd: self.window.0 as *mut _,
|
||||
hinstance: self.hinstance() as *mut _,
|
||||
..WindowsHandle::empty()
|
||||
};
|
||||
RawWindowHandle::Windows(handle)
|
||||
let mut handle = Win32Handle::empty();
|
||||
handle.hwnd = self.window.0 as *mut _;
|
||||
handle.hinstance = self.hinstance() as *mut _;
|
||||
RawWindowHandle::Win32(handle)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -456,7 +404,7 @@ impl Window {
|
||||
// string, so add it
|
||||
display_name.push(0);
|
||||
|
||||
let mut native_video_mode = video_mode.video_mode.native_video_mode.clone();
|
||||
let mut native_video_mode = video_mode.video_mode.native_video_mode;
|
||||
|
||||
let res = unsafe {
|
||||
winuser::ChangeDisplaySettingsExW(
|
||||
@@ -617,6 +565,11 @@ impl Window {
|
||||
self.window_state.lock().window_icon = window_icon;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_enable(&self, enabled: bool) {
|
||||
unsafe { winuser::EnableWindow(self.hwnd() as _, enabled as _) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
|
||||
if let Some(ref taskbar_icon) = taskbar_icon {
|
||||
@@ -685,6 +638,20 @@ impl Window {
|
||||
pub fn theme(&self) -> Theme {
|
||||
self.window_state.lock().current_theme
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn focus_window(&self) {
|
||||
let window = self.window.clone();
|
||||
let window_flags = self.window_state.lock().window_flags();
|
||||
|
||||
let is_visible = window_flags.contains(WindowFlags::VISIBLE);
|
||||
let is_minimized = window_flags.contains(WindowFlags::MINIMIZED);
|
||||
let is_foreground = window.0 == unsafe { winuser::GetForegroundWindow() };
|
||||
|
||||
if is_visible && !is_minimized && !is_foreground {
|
||||
unsafe { force_window_active(window.0) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
@@ -710,18 +677,186 @@ pub struct WindowWrapper(HWND);
|
||||
unsafe impl Sync for WindowWrapper {}
|
||||
unsafe impl Send for WindowWrapper {}
|
||||
|
||||
unsafe fn init<T: 'static>(
|
||||
pub(super) struct InitData<'a, T: 'static> {
|
||||
// inputs
|
||||
pub event_loop: &'a EventLoopWindowTarget<T>,
|
||||
pub attributes: WindowAttributes,
|
||||
pub pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||
pub window_flags: WindowFlags,
|
||||
// outputs
|
||||
pub window: Option<Window>,
|
||||
}
|
||||
|
||||
impl<'a, T: 'static> InitData<'a, T> {
|
||||
unsafe fn create_window(&self, window: HWND) -> Window {
|
||||
// Register for touch events if applicable
|
||||
{
|
||||
let digitizer = winuser::GetSystemMetrics(winuser::SM_DIGITIZER) as u32;
|
||||
if digitizer & winuser::NID_READY != 0 {
|
||||
winuser::RegisterTouchWindow(window, winuser::TWF_WANTPALM);
|
||||
}
|
||||
}
|
||||
|
||||
let dpi = hwnd_dpi(window);
|
||||
let scale_factor = dpi_to_scale_factor(dpi);
|
||||
|
||||
// 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 current_theme = try_theme(window, self.pl_attribs.preferred_theme);
|
||||
|
||||
let window_state = {
|
||||
let window_state = WindowState::new(
|
||||
&self.attributes,
|
||||
self.pl_attribs.taskbar_icon.clone(),
|
||||
scale_factor,
|
||||
current_theme,
|
||||
self.pl_attribs.preferred_theme,
|
||||
);
|
||||
let window_state = Arc::new(Mutex::new(window_state));
|
||||
WindowState::set_window_flags(window_state.lock(), window, |f| *f = self.window_flags);
|
||||
window_state
|
||||
};
|
||||
|
||||
enable_non_client_dpi_scaling(window);
|
||||
|
||||
Window {
|
||||
window: WindowWrapper(window),
|
||||
window_state,
|
||||
thread_executor: self.event_loop.create_thread_executor(),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn create_window_data(&self, win: &Window) -> event_loop::WindowData<T> {
|
||||
let file_drop_handler = if self.pl_attribs.drag_and_drop {
|
||||
use winapi::shared::winerror::{OLE_E_WRONGCOMPOBJ, RPC_E_CHANGED_MODE, S_OK};
|
||||
|
||||
let ole_init_result = ole2::OleInitialize(ptr::null_mut());
|
||||
// It is ok if the initialize result is `S_FALSE` because it might happen that
|
||||
// multiple windows are created on the same thread.
|
||||
if ole_init_result == OLE_E_WRONGCOMPOBJ {
|
||||
panic!("OleInitialize failed! Result was: `OLE_E_WRONGCOMPOBJ`");
|
||||
} else if ole_init_result == RPC_E_CHANGED_MODE {
|
||||
panic!(
|
||||
"OleInitialize failed! Result was: `RPC_E_CHANGED_MODE`. \
|
||||
Make sure other crates are not using multithreaded COM library \
|
||||
on the same thread or disable drag and drop support."
|
||||
);
|
||||
}
|
||||
|
||||
let file_drop_runner = self.event_loop.runner_shared.clone();
|
||||
let file_drop_handler = FileDropHandler::new(
|
||||
win.window.0,
|
||||
Box::new(move |event| {
|
||||
if let Ok(e) = event.map_nonuser_event() {
|
||||
file_drop_runner.send_event(e)
|
||||
}
|
||||
}),
|
||||
);
|
||||
let handler_interface_ptr = &mut (*file_drop_handler.data).interface as LPDROPTARGET;
|
||||
|
||||
assert_eq!(
|
||||
ole2::RegisterDragDrop(win.window.0, handler_interface_ptr),
|
||||
S_OK
|
||||
);
|
||||
Some(file_drop_handler)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.event_loop.runner_shared.register_window(win.window.0);
|
||||
|
||||
event_loop::WindowData {
|
||||
window_state: win.window_state.clone(),
|
||||
event_loop_runner: self.event_loop.runner_shared.clone(),
|
||||
_file_drop_handler: file_drop_handler,
|
||||
userdata_removed: Cell::new(false),
|
||||
recurse_depth: Cell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a pointer to window user data on success.
|
||||
// The user data will be registered for the window and can be accessed within the window event callback.
|
||||
pub unsafe fn on_nccreate(&mut self, window: HWND) -> Option<WindowLongPtr> {
|
||||
let runner = self.event_loop.runner_shared.clone();
|
||||
let result = runner.catch_unwind(|| unsafe {
|
||||
let mut window = self.create_window(window);
|
||||
let window_data = self.create_window_data(&mut window);
|
||||
(window, window_data)
|
||||
});
|
||||
|
||||
result.map(|(win, userdata)| {
|
||||
self.window = Some(win);
|
||||
let userdata = Box::into_raw(Box::new(userdata));
|
||||
userdata as _
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn on_create(&mut self) {
|
||||
let win = self.window.as_mut().expect("failed window creation");
|
||||
|
||||
// making the window transparent
|
||||
if self.attributes.transparent && !self.pl_attribs.no_redirection_bitmap {
|
||||
// Empty region for the blur effect, so the window is fully transparent
|
||||
let region = CreateRectRgn(0, 0, -1, -1);
|
||||
|
||||
let bb = dwmapi::DWM_BLURBEHIND {
|
||||
dwFlags: dwmapi::DWM_BB_ENABLE | dwmapi::DWM_BB_BLURREGION,
|
||||
fEnable: 1,
|
||||
hRgnBlur: region,
|
||||
fTransitionOnMaximized: 0,
|
||||
};
|
||||
let hr = dwmapi::DwmEnableBlurBehindWindow(win.hwnd(), &bb);
|
||||
if !SUCCEEDED(hr) {
|
||||
warn!(
|
||||
"Setting transparent window is failed. HRESULT Code: 0x{:X}",
|
||||
hr
|
||||
);
|
||||
}
|
||||
DeleteObject(region as _);
|
||||
}
|
||||
|
||||
let attributes = self.attributes.clone();
|
||||
|
||||
// Set visible before setting the size to ensure the
|
||||
// attribute is correctly applied.
|
||||
win.set_visible(attributes.visible);
|
||||
|
||||
let dimensions = attributes
|
||||
.inner_size
|
||||
.unwrap_or_else(|| PhysicalSize::new(800, 600).into());
|
||||
win.set_inner_size(dimensions);
|
||||
if attributes.maximized {
|
||||
// Need to set MAXIMIZED after setting `inner_size` as
|
||||
// `Window::set_inner_size` changes MAXIMIZED to false.
|
||||
win.set_maximized(true);
|
||||
}
|
||||
|
||||
if attributes.fullscreen.is_some() {
|
||||
win.set_fullscreen(attributes.fullscreen);
|
||||
force_window_active(win.window.0);
|
||||
}
|
||||
|
||||
if let Some(position) = attributes.position {
|
||||
win.set_outer_position(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe fn init<T>(
|
||||
attributes: WindowAttributes,
|
||||
pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||
event_loop: &EventLoopWindowTarget<T>,
|
||||
) -> Result<Window, RootOsError> {
|
||||
) -> Result<Window, RootOsError>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
let title = OsStr::new(&attributes.title)
|
||||
.encode_wide()
|
||||
.chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// registering the window class
|
||||
let class_name = register_window_class(&attributes.window_icon, &pl_attribs.taskbar_icon);
|
||||
let class_name = register_window_class::<T>(&attributes.window_icon, &pl_attribs.taskbar_icon);
|
||||
|
||||
let mut window_flags = WindowFlags::empty();
|
||||
window_flags.set(WindowFlags::DECORATIONS, attributes.decorations);
|
||||
@@ -752,106 +887,45 @@ unsafe fn init<T: 'static>(
|
||||
}
|
||||
};
|
||||
|
||||
// creating the real window this time, by using the functions in `extra_functions`
|
||||
let real_window = {
|
||||
let (style, ex_style) = window_flags.to_window_styles();
|
||||
let handle = winuser::CreateWindowExW(
|
||||
ex_style,
|
||||
class_name.as_ptr(),
|
||||
title.as_ptr() as LPCWSTR,
|
||||
style,
|
||||
winuser::CW_USEDEFAULT,
|
||||
winuser::CW_USEDEFAULT,
|
||||
winuser::CW_USEDEFAULT,
|
||||
winuser::CW_USEDEFAULT,
|
||||
parent.unwrap_or(ptr::null_mut()),
|
||||
pl_attribs.menu.unwrap_or(ptr::null_mut()),
|
||||
libloaderapi::GetModuleHandleW(ptr::null()),
|
||||
ptr::null_mut(),
|
||||
);
|
||||
|
||||
if handle.is_null() {
|
||||
return Err(os_error!(io::Error::last_os_error()));
|
||||
}
|
||||
|
||||
WindowWrapper(handle)
|
||||
let mut initdata = InitData {
|
||||
event_loop,
|
||||
attributes,
|
||||
pl_attribs: pl_attribs.clone(),
|
||||
window_flags,
|
||||
window: None,
|
||||
};
|
||||
|
||||
// Register for touch events if applicable
|
||||
{
|
||||
let digitizer = winuser::GetSystemMetrics(winuser::SM_DIGITIZER) as u32;
|
||||
if digitizer & winuser::NID_READY != 0 {
|
||||
winuser::RegisterTouchWindow(real_window.0, winuser::TWF_WANTPALM);
|
||||
}
|
||||
let (style, ex_style) = window_flags.to_window_styles();
|
||||
let handle = winuser::CreateWindowExW(
|
||||
ex_style,
|
||||
class_name.as_ptr(),
|
||||
title.as_ptr() as LPCWSTR,
|
||||
style,
|
||||
winuser::CW_USEDEFAULT,
|
||||
winuser::CW_USEDEFAULT,
|
||||
winuser::CW_USEDEFAULT,
|
||||
winuser::CW_USEDEFAULT,
|
||||
parent.unwrap_or(ptr::null_mut()),
|
||||
pl_attribs.menu.unwrap_or(ptr::null_mut()),
|
||||
libloaderapi::GetModuleHandleW(ptr::null()),
|
||||
&mut initdata as *mut _ as *mut _,
|
||||
);
|
||||
|
||||
// If the window creation in `InitData` panicked, then should resume panicking here
|
||||
if let Err(panic_error) = event_loop.runner_shared.take_panic_error() {
|
||||
panic::resume_unwind(panic_error)
|
||||
}
|
||||
|
||||
let dpi = hwnd_dpi(real_window.0);
|
||||
let scale_factor = dpi_to_scale_factor(dpi);
|
||||
|
||||
// making the window transparent
|
||||
if attributes.transparent && !pl_attribs.no_redirection_bitmap {
|
||||
// Empty region for the blur effect, so the window is fully transparent
|
||||
let region = CreateRectRgn(0, 0, -1, -1);
|
||||
|
||||
let bb = dwmapi::DWM_BLURBEHIND {
|
||||
dwFlags: dwmapi::DWM_BB_ENABLE | dwmapi::DWM_BB_BLURREGION,
|
||||
fEnable: 1,
|
||||
hRgnBlur: region,
|
||||
fTransitionOnMaximized: 0,
|
||||
};
|
||||
|
||||
dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb);
|
||||
DeleteObject(region as _);
|
||||
if handle.is_null() {
|
||||
return Err(os_error!(io::Error::last_os_error()));
|
||||
}
|
||||
|
||||
// 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 current_theme = try_theme(real_window.0, pl_attribs.preferred_theme);
|
||||
|
||||
let window_state = {
|
||||
let window_state = WindowState::new(
|
||||
&attributes,
|
||||
pl_attribs.taskbar_icon,
|
||||
scale_factor,
|
||||
current_theme,
|
||||
pl_attribs.preferred_theme,
|
||||
);
|
||||
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
|
||||
};
|
||||
|
||||
let win = Window {
|
||||
window: real_window,
|
||||
window_state,
|
||||
thread_executor: event_loop.create_thread_executor(),
|
||||
};
|
||||
|
||||
let dimensions = attributes
|
||||
.inner_size
|
||||
.unwrap_or_else(|| PhysicalSize::new(800, 600).into());
|
||||
win.set_inner_size(dimensions);
|
||||
if attributes.maximized {
|
||||
// Need to set MAXIMIZED after setting `inner_size` as
|
||||
// `Window::set_inner_size` changes MAXIMIZED to false.
|
||||
win.set_maximized(true);
|
||||
}
|
||||
win.set_visible(attributes.visible);
|
||||
|
||||
if let Some(_) = attributes.fullscreen {
|
||||
win.set_fullscreen(attributes.fullscreen);
|
||||
force_window_active(win.window.0);
|
||||
}
|
||||
|
||||
if let Some(position) = attributes.position {
|
||||
win.set_outer_position(position);
|
||||
}
|
||||
|
||||
Ok(win)
|
||||
// If the handle is non-null, then window creation must have succeeded, which means
|
||||
// that we *must* have populated the `InitData.window` field.
|
||||
Ok(initdata.window.unwrap())
|
||||
}
|
||||
|
||||
unsafe fn register_window_class(
|
||||
unsafe fn register_window_class<T: 'static>(
|
||||
window_icon: &Option<Icon>,
|
||||
taskbar_icon: &Option<Icon>,
|
||||
) -> Vec<u16> {
|
||||
@@ -872,7 +946,7 @@ unsafe fn register_window_class(
|
||||
let class = winuser::WNDCLASSEXW {
|
||||
cbSize: mem::size_of::<winuser::WNDCLASSEXW>() as UINT,
|
||||
style: winuser::CS_HREDRAW | winuser::CS_VREDRAW | winuser::CS_OWNDC,
|
||||
lpfnWndProc: Some(winuser::DefWindowProcW),
|
||||
lpfnWndProc: Some(super::event_loop::public_window_callback::<T>),
|
||||
cbClsExtra: 0,
|
||||
cbWndExtra: 0,
|
||||
hInstance: libloaderapi::GetModuleHandleW(ptr::null()),
|
||||
@@ -929,7 +1003,7 @@ unsafe fn taskbar_mark_fullscreen(handle: HWND, fullscreen: bool) {
|
||||
TASKBAR_LIST.with(|task_bar_list_ptr| {
|
||||
let mut task_bar_list = task_bar_list_ptr.get();
|
||||
|
||||
if task_bar_list == ptr::null_mut() {
|
||||
if task_bar_list.is_null() {
|
||||
use winapi::{shared::winerror::S_OK, Interface};
|
||||
|
||||
let hr = combaseapi::CoCreateInstance(
|
||||
|
||||
112
src/window.rs
112
src/window.rs
@@ -69,12 +69,16 @@ impl Drop for Window {
|
||||
pub struct WindowId(pub(crate) platform_impl::WindowId);
|
||||
|
||||
impl WindowId {
|
||||
/// Returns a dummy `WindowId`, useful for unit testing. The only guarantee made about the return
|
||||
/// value of this function is that it will always be equal to itself and to future values returned
|
||||
/// by this function. No other guarantees are made. This may be equal to a real `WindowId`.
|
||||
/// Returns a dummy `WindowId`, useful for unit testing.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The only guarantee made about the return value of this function is that
|
||||
/// it will always be equal to itself and to future values returned by this function.
|
||||
/// No other guarantees are made. This may be equal to a real `WindowId`.
|
||||
///
|
||||
/// **Passing this into a winit function will result in undefined behavior.**
|
||||
pub unsafe fn dummy() -> Self {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
WindowId(platform_impl::WindowId::dummy())
|
||||
}
|
||||
}
|
||||
@@ -438,7 +442,7 @@ impl Window {
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS:** Can only be called on the main thread.
|
||||
/// - **Android:** Unsupported.
|
||||
/// - **Android:** Subsequent calls after `MainEventsCleared` are not handled.
|
||||
#[inline]
|
||||
pub fn request_redraw(&self) {
|
||||
self.window.request_redraw()
|
||||
@@ -492,6 +496,19 @@ impl Window {
|
||||
/// See `outer_position` for more information about the coordinates. This automatically un-maximizes the
|
||||
/// window if it's maximized.
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use winit::dpi::{LogicalPosition, PhysicalPosition};
|
||||
/// # use winit::event_loop::EventLoop;
|
||||
/// # use winit::window::Window;
|
||||
/// # let mut event_loop = EventLoop::new();
|
||||
/// # let window = Window::new(&event_loop).unwrap();
|
||||
/// // Specify the position in logical dimensions like this:
|
||||
/// window.set_outer_position(LogicalPosition::new(400.0, 200.0));
|
||||
///
|
||||
/// // Or specify the position in physical dimensions like this:
|
||||
/// window.set_outer_position(PhysicalPosition::new(400, 200));
|
||||
/// ```
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS:** Can only be called on the main thread. Sets the top left coordinates of the
|
||||
@@ -524,6 +541,19 @@ impl Window {
|
||||
/// See `inner_size` for more information about the values. This automatically un-maximizes the
|
||||
/// window if it's maximized.
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use winit::dpi::{LogicalSize, PhysicalSize};
|
||||
/// # use winit::event_loop::EventLoop;
|
||||
/// # use winit::window::Window;
|
||||
/// # let mut event_loop = EventLoop::new();
|
||||
/// # let window = Window::new(&event_loop).unwrap();
|
||||
/// // Specify the size in logical dimensions like this:
|
||||
/// window.set_inner_size(LogicalSize::new(400.0, 200.0));
|
||||
///
|
||||
/// // Or specify the size in physical dimensions like this:
|
||||
/// window.set_inner_size(PhysicalSize::new(400, 200));
|
||||
/// ```
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS / Android:** Unsupported.
|
||||
@@ -551,6 +581,19 @@ impl Window {
|
||||
|
||||
/// Sets a minimum dimension size for the window.
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use winit::dpi::{LogicalSize, PhysicalSize};
|
||||
/// # use winit::event_loop::EventLoop;
|
||||
/// # use winit::window::Window;
|
||||
/// # let mut event_loop = EventLoop::new();
|
||||
/// # let window = Window::new(&event_loop).unwrap();
|
||||
/// // Specify the size in logical dimensions like this:
|
||||
/// window.set_min_inner_size(Some(LogicalSize::new(400.0, 200.0)));
|
||||
///
|
||||
/// // Or specify the size in physical dimensions like this:
|
||||
/// window.set_min_inner_size(Some(PhysicalSize::new(400, 200)));
|
||||
/// ```
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS / Android / Web:** Unsupported.
|
||||
@@ -561,6 +604,19 @@ impl Window {
|
||||
|
||||
/// Sets a maximum dimension size for the window.
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use winit::dpi::{LogicalSize, PhysicalSize};
|
||||
/// # use winit::event_loop::EventLoop;
|
||||
/// # use winit::window::Window;
|
||||
/// # let mut event_loop = EventLoop::new();
|
||||
/// # let window = Window::new(&event_loop).unwrap();
|
||||
/// // Specify the size in logical dimensions like this:
|
||||
/// window.set_max_inner_size(Some(LogicalSize::new(400.0, 200.0)));
|
||||
///
|
||||
/// // Or specify the size in physical dimensions like this:
|
||||
/// window.set_max_inner_size(Some(PhysicalSize::new(400, 200)));
|
||||
/// ```
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS / Android / Web:** Unsupported.
|
||||
@@ -723,6 +779,19 @@ impl Window {
|
||||
|
||||
/// Sets location of IME candidate box in client area coordinates relative to the top left.
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use winit::dpi::{LogicalPosition, PhysicalPosition};
|
||||
/// # use winit::event_loop::EventLoop;
|
||||
/// # use winit::window::Window;
|
||||
/// # let mut event_loop = EventLoop::new();
|
||||
/// # let window = Window::new(&event_loop).unwrap();
|
||||
/// // Specify the position in logical dimensions like this:
|
||||
/// window.set_ime_position(LogicalPosition::new(400.0, 200.0));
|
||||
///
|
||||
/// // Or specify the position in physical dimensions like this:
|
||||
/// window.set_ime_position(PhysicalPosition::new(400, 200));
|
||||
/// ```
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS / Android / Web:** Unsupported.
|
||||
@@ -731,6 +800,21 @@ impl Window {
|
||||
self.window.set_ime_position(position.into())
|
||||
}
|
||||
|
||||
/// Brings the window to the front and sets input focus. Has no effect if the window is
|
||||
/// already in focus, minimized, or not visible.
|
||||
///
|
||||
/// This method steals input focus from other applications. Do not use this method unless
|
||||
/// you are certain that's what the user wants. Focus stealing can cause an extremely disruptive
|
||||
/// user experience.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS / Android / Web / Wayland:** Unsupported.
|
||||
#[inline]
|
||||
pub fn focus_window(&self) {
|
||||
self.window.focus_window()
|
||||
}
|
||||
|
||||
/// Requests user attention to the window, this has no effect if the application
|
||||
/// is already focused. How requesting for user attention manifests is platform dependent,
|
||||
/// see `UserAttentionType` for details.
|
||||
@@ -740,9 +824,10 @@ impl Window {
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS / Android / Web / Wayland:** Unsupported.
|
||||
/// - **iOS / Android / Web :** Unsupported.
|
||||
/// - **macOS:** `None` has no effect.
|
||||
/// - **X11:** Requests for user attention must be manually cleared.
|
||||
/// - **Wayland:** Requires `xdg_activation_v1` protocol, `None` has no effect.
|
||||
#[inline]
|
||||
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
self.window.request_user_attention(request_type)
|
||||
@@ -763,6 +848,19 @@ impl Window {
|
||||
|
||||
/// Changes the position of the cursor in window coordinates.
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use winit::dpi::{LogicalPosition, PhysicalPosition};
|
||||
/// # use winit::event_loop::EventLoop;
|
||||
/// # use winit::window::Window;
|
||||
/// # let mut event_loop = EventLoop::new();
|
||||
/// # let window = Window::new(&event_loop).unwrap();
|
||||
/// // Specify the position in logical dimensions like this:
|
||||
/// window.set_cursor_position(LogicalPosition::new(400.0, 200.0));
|
||||
///
|
||||
/// // Or specify the position in physical dimensions like this:
|
||||
/// window.set_cursor_position(PhysicalPosition::new(400, 200));
|
||||
/// ```
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS / Android / Web / Wayland:** Always returns an [`ExternalError::NotSupported`].
|
||||
@@ -779,7 +877,7 @@ impl Window {
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **macOS:** This locks the cursor in a fixed location, which looks visually awkward.
|
||||
/// - **iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`].
|
||||
/// - **iOS / Android:** Always returns an [`ExternalError::NotSupported`].
|
||||
#[inline]
|
||||
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
|
||||
self.window.set_cursor_grab(grab)
|
||||
|
||||
Reference in New Issue
Block a user