Compare commits

..

44 Commits

Author SHA1 Message Date
Kirill Chibisov
ea1c031b54 Release 0.26.0 version 2021-12-01 14:43:38 +03:00
Mads Marquart
11a44081df macOS move impl details of platform into platform_impl 2021-12-01 14:20:56 +03:00
Mads Marquart
5eb9c9504b Update raw-window-handle to 0.4.1 (#1957)
* Update raw-window-handle to `0.4.2`

See:
- https://github.com/rust-windowing/raw-window-handle/issues/72
- https://github.com/rust-windowing/raw-window-handle/pull/73
- https://github.com/rust-windowing/raw-window-handle/pull/74

* Clean up raw_window_handle functions a bit
2021-11-30 17:50:23 +01:00
Adrien Bennadji
29a078f65c bump ndk dependencies to 0.5 (#2071) 2021-11-24 16:56:57 +01:00
Alphyr
be61ca13fe On X11, update 'mio' to 0.8 2021-11-20 03:42:23 +03:00
mahkoh
e9d5b2007a On X11, don't panic when getting EINTR
Fixes #1972.
2021-11-20 03:24:45 +03:00
Ian Hobson
f2de8475fc Show the menu bar in borderless fullscreen on macOS (#2053)
* In MacOS, only disable menu bar in exclusive fullscreen

* Save and restore fullscreen options in set_fullscreen

* Don't always cache presentation options when entering exclusive fullscreen

This commit caches presentation options when entering exclusive fullscreen
only if we're coming from borderless fullscreen.

Then, when transitioning from exclusive -> borderless, if no cached presentation
options are present, then the default borderless options are applied.

This fixes the menu bar being unavailable when taking the following path:
[not fullscreen] -> [exclusive fullscreen] -> [borderless fullscreen].

Without this commit, the presentation options from [not fullscreen] were being
cached and then applied to [borderless fullscreen].

* Restore the window level when switching to exclusive fullscreen

The hack of using `CGShieldingWindowLevel() + 1` in borderless fullscreen needs
to be undone when switching from [borderless] -> [exclusive] fullscreen,
otherwise there are menu bar glitches when following a path through
[borderless] -> [exclusive] -> [borderless] modes.

Now, this might appear to conflict with the 'always on top' feature which uses
the 'floating window' level, but this feature appears to be broken anyway when
entering and exiting fullscreen with an always-on-top window. So, rather than
introducing logic to attempt to restore to the 'floating' level here, I think
it's better to do the simple thing for now and then introduce logic for
always-on-top windows when fixing the overall fullscreen behaviour.

* Update the changelog

Co-authored-by: Ehden Sinai <ehdens@gmail.com>
Co-authored-by: Francesca Lovebloom <francesca@brainiumstudios.com>
2021-11-19 13:05:36 -08:00
Markus Siglreithmaier
3ecbea3c39 Windows: Split window initialization across NCCREATE and CREATE (#2062)
* Refactor window initialization by splitting NCCREATE and CREATE related tasks.

Fixes issue with invisible owner windows.

* address review comments

* Update src/platform_impl/windows/event_loop.rs

Co-authored-by: Markus Røyset <maroider@protonmail.com>

Co-authored-by: Markus Røyset <maroider@protonmail.com>
2021-11-17 18:33:44 +01:00
cmeissl
c4df7ad7a5 On Wayland, commit the window surface after setting the decoration mode
Fixes #2064.
2021-11-15 01:23:54 +03:00
Emil Ernerfeldt
387567a917 macOS: Fix native file dialogs freezing the event loop (#2027)
* macOS: Ignore all events while in the callback

Previously all native dialogs, such as [rfd](https://github.com/PolyMeilex/rfd), would cause the event loop (event_loop.run) to freeze.

* Update changelog

* spelling

* Fix merge mistake

* Add link to issue in the code

Co-authored-by: Poly <marynczak.bartlomiej@gmail.com>
2021-11-04 11:37:02 -07:00
Markus Siglreithmaier
cfbe8462cc Windows: Increase wait timer resolution (#2007)
Windows: Increase wait timer resolution for more accurate timing when using `WaitUntil`.
2021-11-02 21:51:39 +01:00
Markus Siglreithmaier
5f4df54895 Android: Bump ndk/ndk-glue version (#2047) 2021-11-02 18:54:59 +01:00
Kirill Chibisov
ed698f2462 On Wayland, add wayland-dlopen feature to use dlopen
While winit was always using dlopen for opening system libs, it
provides a way now to disable dlopen feature helping with linking
on some targets.

Fixes #2037.
2021-10-31 17:06:00 +03:00
Kirill Chibisov
b4774861db On X11, if RANDR based scale factor is higher than 20 reset it to 1
Some video drivers could set display metrics to odd values, which can result in
extra large scale factors (e.g. winit is sending 720 for 5k screen on nvidia
binary drivers), so let's just drop them to prevent clients from using them.
The value 20 was picked, because the DPR for 8k @ 5 inch is ~18.36.

Fixes #1983.
2021-10-30 23:47:31 +03:00
Alexis Hildebrandt
9768f73bb7 Update smithay-client-toolkit
Fixes libxcbcommon linking on OpenBSD by using pkg-config when
building without dlopen feature. For details see [1].

[1] - https://github.com/Smithay/client-toolkit/pull/198
2021-10-30 22:35:36 +03:00
Arthur Kaukal Valladares
805249e27e Android: Window::config now initially returns accurate data. (#2021)
Initialize Android's static 'CONFIG' from NativeActivity's Asset Manager
2021-10-26 23:28:28 +02:00
Markus Siglreithmaier
5f24c40d05 Fix wasm CI failure (#2024)
* Fix wasm ci

* remove cargo-web step
2021-10-26 11:05:01 -07:00
Philippe Renon
1b3b82a3c1 Clippy fixes (#2011)
* windows: bump winapi version

* windows: address dark_mode FIXMEs

use now available winapi structures

* clippy: fix clippy::upper_case_acronyms warnings

* clippy: fix needless_arbitrary_self_type warnings

* clippy: fix clone_on_copy warnings

* clippy: fix unnecessary_mut_passed warnings

* clippy: fix identity_op warnings

* clippy: fix misc warnings

* prefix rustdoc lints with rustdoc::

the prefix was introduced in Rust 1.52

* windows: silence file_drop_handler is never read warning

* clippy: fix from_over_into warnings

and a bit of naming simplification

* clippy: fix missing_safety_doc warnings

* make dummy() functions const
2021-08-30 19:40:02 +02:00
Davester47
9e72396709 Fix X11 memory leak and remove mio-misc (#1987)
* Fix X11 memory leak and remove mio-misc

I also fixed a couple of clippy lints.
Fixes #1984

* Send the redraw event before waking up the main event

* Use .map instead of a match, and remove comments saved by git

* Remove unnecessary pub keywords on `WakeSender` in x11/mod.rs
2021-08-24 12:38:56 +02:00
oxalica
125ee0b446 Emit ScaleFactorChanged event on monitor reconnect (#1963)
When disconnect the only monitor, scale factor is reset to 1.0. We need
to set it back when the monitor is reconnected.

We previously assume current window must be on an existing monitor, but
that's not true in case of reconnecting the only one monitor.
2021-08-24 12:36:13 +02:00
sandmor
3bfb580d7a Fix potential bug (#2009)
* polish and failed to find visual warning

* improvement
2021-08-24 12:35:11 +02:00
sandmor
b54d47796d Fix transparency on X11 (#2006)
* find transparent in x11

* remove debug hooks

* update changelog

* polish and failed to find visual warning
2021-08-22 20:45:24 +02:00
Kirill Chibisov
b5d0d6ff3e On Wayland, implement 'request_user_attention'
This commit implements 'request_user_attention' on Wayland with
new 'xdg_activation_v1' protocol.
2021-08-17 07:59:57 +03:00
Kirill Chibisov
c9520deef8 Update smithay-client-toolkit to 'v0.15.0'
This commit also drops 'Theme' trait with its support types
in favor of 'FallbackFrame' meaning that winit will use some
predefined frame for the time being, since porting 'ConceptFrame'
will require adding font rendering librarires right into winit,
which is not desired.

Fixes #1889.
2021-08-15 22:31:59 +03:00
Steven Bosnick
ceab0f8c40 On Wayland, log error for failure to set cursor
The inability to set the cursor using any of the named cursor files
likely indicates an error in the system on which we are running.
'WinitPointer::set_cursor' also does not have any way of returning an
error so just log the error to assist users in diagnosing the problem.

Fixes: #1988.
2021-08-15 22:05:14 +03:00
Markus Siglreithmaier
b87757c552 Fix Window visibility regression (#1994)
On Windows, set windows visible on init before position update

Ensure that the style change is correctly applied, triggering the necessary events.
2021-08-12 18:57:07 +02:00
Nuno Ribeiro
1972eb952d Adds Android winit<->ndk_glue version match table (#1993)
* Adds Android winit<->ndk_glue version match table

* Fixes justification

* Adds crosses

* Address review and instead of a m:n table it shows a 1:1 version compatibility

* Addresses review
2021-08-11 14:28:49 -07:00
Osspial
f92803d80e Prevent ghost window from showing up on taskbar (#1977)
Add WS_EX_TOOLWINDOW to event target window
2021-08-11 20:02:40 +02:00
Yusuke Kominami
8afeb910bd Add composition event on macOS (#1979)
* Enable to show text when IME is active

* Remove unnecessary variable

* Enable to use IME

* fmt

* Remove println! for debug

* Fix handling of utf-8 string

* clear_marked_text should be rust function, not member function

* Store state information in ViewState

* Remove unnecessary function

* format

* Remove mut

* format

* Remove duplicate marked text

* Remove unused `is_preediting` field

Co-authored-by: Artúr Kovács <kovacs.artur.barnabas@gmail.com>
2021-08-09 11:14:13 +02:00
Sven Niederberger
f16ed98af4 On X11 and Wayland, fx window resize cursor orientation
Fixes #1912.
2021-08-01 11:19:00 +03:00
Aidan Dang
2a9916103b On Wayland, load "hand2" and "hand1" cursor icons for CursorIcon::Hand 2021-08-01 11:16:02 +03:00
Markus Røyset
63ad47a7bf Remove window subclassing (#1933)
* Remove window subclassing

* Always call `DefWindowProcW` when we don't process a message

* Improve window initialization

Note that the error path in `init` is kind of cursed at the moment.

* Rename `ThreadMsgTargetCallbackData` to `ThreadMsgTargetData`

* Simplify window initialization

* Fix compilation on 32-bit targets

* Simplify the creation of the event target window

* Use `.clone()` rather than `Rc::clone()`

* Use concrete types for args to `SetWindowLongPtrW`

On 32-bit targets, `SetWindowLongPtrW` is an alias to `SetWindowLongW`,
which returns `LONG` (`i32`) rather than `LONG_PTR` (`isisze`).

* Minor comment adjustments
2021-07-16 12:40:48 +02:00
jim jammer
27e6548343 macOS: Remove is_key_down from ViewState (#1489)
Fixes #1488.

`is_key_down` was only set to true when `insertText` was called.
Therefore `is_key_down` was actually meant to store whether the most
recently pressed key generated an `insertText` event, at which, winit
produces a `ReceivedCharacter`. The issue is that `insertText` is *not*
called for "key repeat", but winit wants to send `ReceivedCharacter`
events for "key repeat" too. To solve this, the `is_key_down` variable
was then checked in the `key_down` function to determine whether it was
valid to produce repeated `ReceivedCharacter` events during "key
repeat". However this check is not needed since `ReceivedCharacter` must
always be called if the key event has a non-empty `characters` property.

Furthermore `is_key_down` didn't actually store whether the previously
pressed character had an `insertText` event, because it was incorrectly
set to false on every "key up". This meant that if two keys were pressed
consecutively and then the first was released, then `is_key_down` was
set to false even if the most recent keypress did actually produce an
`insertText`.

Update changelog.
2021-07-13 17:38:01 +02:00
Markus Røyset
8c91986dd3 Remove libc dependency on non-linux platforms (#1976) 2021-07-13 17:27:47 +02:00
Aden Haussmann
5a65347c4e Changed description of window scale factor in Events (#1834)
* changed description of window scale factor in Events

* Update src/dpi.rs

Co-authored-by: Markus Røyset <maroider@protonmail.com>

* Fix intra-doc link

Co-authored-by: Markus Røyset <maroider@protonmail.com>
2021-06-16 09:24:49 +02:00
Francesca Lovebloom
635180c8be Update Hall of Champions (#1959)
* Lionize Aubrey

* Lionize Victor

* Lionize Freya

* Stylization consistency
2021-06-14 13:55:13 -07:00
Onirik79
019ce9862f Fix typo in events documentation (#1960) 2021-06-13 14:26:20 +02:00
Artúr Kovács
c7f46876a7 Drop the event callback before exiting on macOS (#1954)
* Drop the event callback before exiting

* Update the changelog

* Apply suggestion from review

Co-authored-by: Markus Røyset <maroider@protonmail.com>

* Apply review suggestions

Co-authored-by: Markus Røyset <maroider@protonmail.com>
2021-06-13 14:25:06 +02:00
garasubo
c916eb6137 On X11 and Wayland, add is_maximized support
Fixes #1845.

Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2021-06-10 10:43:27 +03:00
Tilmann Meyer
67cca71524 Implement Window::request_redraw on Android (#1953) 2021-06-05 12:47:08 +02:00
i509VCB
1eff7ae004 Document how main_thread_id for Windows works (#1951) 2021-06-05 12:46:44 +02:00
Mads Marquart
982ad46c83 Use objc's autoreleasepool instead of manually with NSAutoreleasePool (#1936)
Ensures the pools are released even if we panic
2021-05-27 17:38:41 +02:00
Markus Røyset
657b4fd59e Remove support for stdweb (#1941)
* Remove support for `stdweb`

* Expunge `stdweb`; make `web-sys` the default

* Mark this change as a breaking change

* Re-insert accidental removal of space

* Use the correct cargo feature syntax

* Re-add some `cfg` attributes

* Remove `web-sys` feature from CI
2021-05-24 10:06:21 -07:00
Max de Danschutter
b371b406d5 Implemented focus_window (#1944) 2021-05-19 18:39:53 +02:00
62 changed files with 1562 additions and 2081 deletions

View File

@@ -38,15 +38,14 @@ 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
@@ -77,10 +76,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

View File

@@ -1,3 +1,32 @@
# 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).
- 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`

View File

@@ -1,6 +1,6 @@
[package]
name = "winit"
version = "0.25.0"
version = "0.26.0"
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,7 @@ cocoa = "0.24"
core-foundation = "0.9"
core-graphics = "0.22"
dispatch = "0.2.0"
scopeguard = "1.1"
block = "0.1"
[target.'cfg(target_os = "macos")'.dependencies.core-video-sys]
version = "0.1.4"
@@ -61,7 +59,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 +81,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 +123,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"

View File

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

View File

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

View File

@@ -6,7 +6,7 @@
```toml
[dependencies]
winit = "0.25.0"
winit = "0.26.0"
```
## [Documentation](https://docs.rs/winit)
@@ -72,10 +72,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 +92,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 +126,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.

View File

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

View File

@@ -7,6 +7,7 @@ use winit::{
window::WindowBuilder,
};
#[cfg(not(target_arch = "wasm32"))]
fn main() {
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
}

View File

@@ -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,7 +45,7 @@ pub fn main() {
});
}
#[cfg(feature = "web-sys")]
#[cfg(target_arch = "wasm32")]
mod wasm {
use wasm_bindgen::prelude::*;

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
@@ -454,78 +440,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,
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,7 +407,7 @@ 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")]
_ => (),
}
@@ -418,7 +417,7 @@ 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")]
_ => (),
}
@@ -430,14 +429,22 @@ 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")]
_ => (),
}
}
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),
}
}
#[inline]
pub fn request_redraw(&self) {
@@ -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()),
}
}
}

View File

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

View File

@@ -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,14 +535,9 @@ impl<T: 'static> EventLoop<T> {
f(state)
}
fn loop_dispatch<D: Into<Option<std::time::Duration>>>(
&mut self,
timeout: D,
) -> std::io::Result<()> {
fn loop_dispatch<D: Into<Option<std::time::Duration>>>(&mut self, timeout: D) -> IOResult<()> {
let mut 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!(),
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1164,57 +1164,56 @@ 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);
}
}
}
});
}
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 {
@@ -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 == true => {
// 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
@@ -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
}
}

View File

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

View File

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

View File

@@ -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},
@@ -116,6 +118,8 @@ pub enum NSWindowLevel {
NSScreenSaverWindowLevel = kCGScreenSaverWindowLevelKey as _,
}
pub const NSStringEnumerationByComposedCharacterSequences: NSUInteger = 2;
pub type CGDisplayFadeInterval = f32;
pub type CGDisplayReservationInterval = f32;
pub type CGDisplayBlendFraction = f32;
@@ -159,7 +163,7 @@ 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;
#[cfg_attr(
not(use_colorsync_cgdisplaycreateuuidfromdisplayid),

View File

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

View File

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

View File

@@ -3,18 +3,27 @@ mod cursor;
pub use self::{cursor::*, r#async::*};
use std::ops::{BitAnd, Deref};
use std::{
cell::Cell,
ops::{BitAnd, Deref},
rc::Rc,
};
use block::ConcreteBlock;
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};
use crate::dpi::LogicalPosition;
use crate::platform_impl::platform::ffi;
use crate::{
dpi::LogicalPosition,
platform_impl::platform::ffi::{
self, NSRange, NSStringEnumerationByComposedCharacterSequences,
},
};
// Replace with `!` once stable
#[derive(Debug)]
@@ -61,9 +70,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();
};
}
}
@@ -106,6 +113,31 @@ pub unsafe fn ns_string_id_ref(s: &str) -> IdRef {
IdRef::new(NSString::alloc(nil).init_str(s))
}
/// Returns the number of characters in a string.
/// (A single character may consist of multiple UTF-32 code units.
/// This is possible when long sequences of composing characters are present)
///
/// Unsafe because assumes that the `string` is an `NSString` object
pub unsafe fn ns_string_char_count(string: id) -> usize {
let length: NSUInteger = msg_send![string, length];
let range = NSRange {
location: 0,
length,
};
let char_count = Rc::new(Cell::new(0));
let block = {
let char_count = char_count.clone();
ConcreteBlock::new(move || char_count.set(char_count.get() + 1)).copy()
};
let block = &*block;
let () = msg_send![string,
enumerateSubstringsInRange:range
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:block
];
char_count.get()
}
#[allow(dead_code)] // In case we want to use this function in the future
pub unsafe fn app_name() -> Option<id> {
let bundle: id = msg_send![class!(NSBundle), mainBundle];

View File

@@ -29,7 +29,7 @@ use crate::{
scancode_to_keycode, EventWrapper,
},
ffi::*,
util::{self, IdRef},
util::{self, ns_string_char_count, IdRef},
window::get_window_id,
DEVICE_ID,
},
@@ -55,9 +55,10 @@ 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>,
is_ime_activated: bool,
marked_text: id,
}
impl ViewState {
@@ -69,14 +70,17 @@ impl ViewState {
pub fn new_view(ns_window: id) -> (IdRef, Weak<Mutex<CursorState>>) {
let cursor_state = Default::default();
let cursor_access = Arc::downgrade(&cursor_state);
let marked_text =
unsafe { <id as NSMutableAttributedString>::init(NSMutableAttributedString::alloc(nil)) };
let state = ViewState {
ns_window,
cursor_state,
ime_spot: None,
raw_characters: None,
is_key_down: false,
modifiers: Default::default(),
tracking_rect: None,
is_ime_activated: false,
marked_text,
};
unsafe {
// This is free'd in `dealloc`
@@ -149,7 +153,10 @@ lazy_static! {
sel!(setMarkedText:selectedRange:replacementRange:),
set_marked_text as extern "C" fn(&mut Object, Sel, id, NSRange, NSRange),
);
decl.add_method(sel!(unmarkText), unmark_text as extern "C" fn(&Object, Sel));
decl.add_method(
sel!(unmarkText),
unmark_text as extern "C" fn(&mut Object, Sel),
);
decl.add_method(
sel!(validAttributesForMarkedText),
valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id,
@@ -161,7 +168,7 @@ lazy_static! {
);
decl.add_method(
sel!(insertText:replacementRange:),
insert_text as extern "C" fn(&Object, Sel, id, NSRange),
insert_text as extern "C" fn(&mut Object, Sel, id, NSRange),
);
decl.add_method(
sel!(characterIndexForPoint:),
@@ -260,7 +267,6 @@ lazy_static! {
accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL,
);
decl.add_ivar::<*mut c_void>("winitState");
decl.add_ivar::<id>("markedText");
let protocol = Protocol::get("NSTextInputClient").unwrap();
decl.add_protocol(&protocol);
ViewClass(decl.register())
@@ -270,9 +276,9 @@ lazy_static! {
extern "C" fn dealloc(this: &Object, _sel: Sel) {
unsafe {
let state: *mut c_void = *this.get_ivar("winitState");
let marked_text: id = *this.get_ivar("markedText");
let _: () = msg_send![marked_text, release];
Box::from_raw(state as *mut ViewState);
let state = state as *mut ViewState;
let _: () = msg_send![(*state).marked_text, release];
Box::from_raw(state);
}
}
@@ -281,15 +287,12 @@ extern "C" fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> i
let this: id = msg_send![this, init];
if this != nil {
(*this).set_ivar("winitState", state);
let marked_text =
<id as NSMutableAttributedString>::init(NSMutableAttributedString::alloc(nil));
(*this).set_ivar("markedText", marked_text);
let _: () = msg_send![this, setPostsFrameChangedNotifications: YES];
let notification_center: &Object =
msg_send![class!(NSNotificationCenter), defaultCenter];
let notification_name =
NSString::alloc(nil).init_str("NSViewFrameDidChangeNotification");
IdRef::new(NSString::alloc(nil).init_str("NSViewFrameDidChangeNotification"));
let _: () = msg_send![
notification_center,
addObserver: this
@@ -390,17 +393,20 @@ extern "C" fn reset_cursor_rects(this: &Object, _sel: Sel) {
extern "C" fn has_marked_text(this: &Object, _sel: Sel) -> BOOL {
unsafe {
trace!("Triggered `hasMarkedText`");
let marked_text: id = *this.get_ivar("markedText");
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
let retval = (state.marked_text.length() > 0) as BOOL;
trace!("Completed `hasMarkedText`");
(marked_text.length() > 0) as BOOL
retval
}
}
extern "C" fn marked_range(this: &Object, _sel: Sel) -> NSRange {
unsafe {
trace!("Triggered `markedRange`");
let marked_text: id = *this.get_ivar("markedText");
let length = marked_text.length();
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
let length = state.marked_text.length();
trace!("Completed `markedRange`");
if length > 0 {
NSRange::new(0, length - 1)
@@ -425,32 +431,62 @@ extern "C" fn set_marked_text(
) {
trace!("Triggered `setMarkedText`");
unsafe {
let marked_text_ref: &mut id = this.get_mut_ivar("markedText");
let _: () = msg_send![(*marked_text_ref), release];
let marked_text = NSMutableAttributedString::alloc(nil);
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
// Delete previous marked text
let char_count = ns_string_char_count(state.marked_text.string());
delete_marked_text(state, char_count);
state.is_ime_activated = true;
let _: () = msg_send![state.marked_text, release];
state.marked_text = NSMutableAttributedString::alloc(nil);
let has_attr = msg_send![string, isKindOfClass: class!(NSAttributedString)];
if has_attr {
marked_text.initWithAttributedString(string);
state.marked_text.initWithAttributedString(string);
} else {
marked_text.initWithString(string);
state.marked_text.initWithString(string);
};
*marked_text_ref = marked_text;
let text_ns_str = state.marked_text.string();
let slice = slice::from_raw_parts(
text_ns_str.UTF8String() as *const c_uchar,
text_ns_str.len(),
);
let text_str = str::from_utf8_unchecked(slice);
for character in text_str.chars() {
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::ReceivedCharacter(character),
}));
}
}
trace!("Completed `setMarkedText`");
}
extern "C" fn unmark_text(this: &Object, _sel: Sel) {
extern "C" fn unmark_text(this: &mut Object, _sel: Sel) {
trace!("Triggered `unmarkText`");
unsafe {
let marked_text: id = *this.get_ivar("markedText");
let mutable_string = marked_text.mutableString();
let _: () = msg_send![mutable_string, setString:""];
let input_context: id = msg_send![this, inputContext];
let _: () = msg_send![input_context, discardMarkedText];
clear_marked_text(this);
}
trace!("Completed `unmarkText`");
}
/// Unsafe because assumes that `this` is an instance of the `WinitView` class that we declare
/// programmatically
unsafe fn clear_marked_text(this: &mut Object) {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
let _: () = msg_send![state.marked_text, release];
state.marked_text = NSMutableAttributedString::alloc(nil);
let input_context: id = msg_send![this, inputContext];
let _: () = msg_send![input_context, discardMarkedText];
}
extern "C" fn valid_attributes_for_marked_text(_this: &Object, _sel: Sel) -> id {
trace!("Triggered `validAttributesForMarkedText`");
trace!("Completed `validAttributesForMarkedText`");
@@ -498,12 +534,19 @@ extern "C" fn first_rect_for_character_range(
}
}
extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange) {
extern "C" fn insert_text(this: &mut Object, _sel: Sel, string: id, _replacement_range: NSRange) {
trace!("Triggered `insertText`");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
let is_ime_activated: bool = state.is_ime_activated;
if is_ime_activated {
clear_marked_text(this);
state.is_ime_activated = false;
return;
}
let has_attr = msg_send![string, isKindOfClass: class!(NSAttributedString)];
let characters = if has_attr {
// This is a *mut NSAttributedString
@@ -516,7 +559,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];
@@ -571,6 +613,15 @@ extern "C" fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
trace!("Completed `doCommandBySelector`");
}
fn delete_marked_text(state: &mut ViewState, count: usize) {
for _ in 0..count {
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::ReceivedCharacter('\u{7f}'), // fire DELETE
}));
}
}
fn get_characters(event: id, ignore_modifiers: bool) -> String {
unsafe {
let characters: id = if ignore_modifiers {
@@ -675,7 +726,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 +756,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);

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
pub struct Id(pub i32);
impl Id {
pub unsafe fn dummy() -> Self {
pub const unsafe fn dummy() -> Self {
Id(0)
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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};
@@ -281,6 +281,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 +322,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 +341,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)
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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},
};
@@ -38,9 +38,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 +71,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 +233,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 +403,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 +564,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 +637,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 +676,180 @@ 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);
// 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,
};
dwmapi::DwmEnableBlurBehindWindow(window, &bb);
DeleteObject(region as _);
}
// 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");
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 +880,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 +939,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 +996,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(

View File

@@ -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()
@@ -731,6 +735,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 +759,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)