snapshot of https://github.com/tauri-apps/tauri.git (dev, 2026-09-23; blobs > 131072B omitted)
Some checks failed
Audit / audit-js (push) Failing after 0s
bench / bench (map[os:ubuntu-latest runner:xvfb-run --auto-servernum target:x86_64-unknown-linux-gnu], nightly) (push) Failing after 0s
covector version or publish / run-integration-tests (ubuntu-latest) (push) Failing after 0s
Audit / audit-rust (push) Failing after 0s
test `@tauri-apps/cli` / test (ubuntu-latest) (push) Failing after 0s
test `tauri-cli` / test (map[os:ubuntu-latest target:x86_64-unknown-linux-gnu]) (push) Failing after 0s
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:ubuntu-latest target:x86_64-unknown-linux-gnu toolchain:1.90]) (push) Failing after 0s
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:ubuntu-latest target:x86_64-unknown-linux-gnu toolchain:1.90]) (push) Failing after 0s
test core / test (map[args:--no-default-features key:no-default], map[cargo:cross command:build os:ubuntu-latest target:aarch64-linux-android toolchain:1.90]) (push) Failing after 0s
deploy schema worker / deploy (push) Failing after 0s
lint rust / clippy (map[cargo:cargo include: os:ubuntu-latest target:x86_64-unknown-linux-gnu]) (push) Failing after 0s
lint rust / clippy (map[cargo:cross include:--package api os:ubuntu-latest target:aarch64-linux-android]) (push) Failing after 0s
supply chain health status / check rust dependencies with cargo vet (push) Failing after 0s
test `@tauri-apps/api` e2e (mobile) / android (push) Failing after 0s
test `@tauri-apps/api` e2e / test (map[name:Linux os:ubuntu-latest]) (push) Failing after 0s
test core / test (map[args:--all-features key:all], map[cargo:cross command:build os:ubuntu-latest target:aarch64-linux-android toolchain:1.90]) (push) Failing after 0s
Udeps / changes (push) Failing after 0s
Udeps / setup (push) Has been skipped
Udeps / udeps (push) Has been skipped
bench / bench (map[os:macos-latest runner: target:aarch64-apple-darwin], nightly) (push) Has been cancelled
bench / bench (map[os:windows-latest runner: target:x86_64-pc-windows-msvc], nightly) (push) Has been cancelled
covector version or publish / run-integration-tests (macos-latest) (push) Has been cancelled
covector version or publish / run-integration-tests (windows-latest) (push) Has been cancelled
covector version or publish / version-or-publish (push) Has been cancelled
test `@tauri-apps/cli` / test (macos-latest) (push) Has been cancelled
test `tauri-cli` / test (map[args:--no-default-features --features native-tls-vendored os:windows-latest restore-only:true target:aarch64-pc-windows-msvc]) (push) Has been cancelled
test `tauri-cli` / test (map[os:macos-latest target:x86_64-apple-darwin]) (push) Has been cancelled
test `tauri-cli` / test (map[os:windows-latest target:x86_64-pc-windows-msvc]) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:build os:macos-latest target:aarch64-apple-ios toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:macos-14 target:aarch64-apple-darwin toolchain:1.90]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include: os:macos-latest target:aarch64-apple-darwin]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include: os:windows-latest target:x86_64-pc-windows-msvc]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include:--package api os:macos-latest target:aarch64-apple-ios]) (push) Has been cancelled
test `@tauri-apps/api` e2e (mobile) / ios (push) Has been cancelled
test `@tauri-apps/api` e2e / test (map[name:Windows os:windows-latest]) (push) Has been cancelled
test `@tauri-apps/api` e2e / test (map[name:macOS os:macos-latest]) (push) Has been cancelled
test `@tauri-apps/cli` / test (windows-latest) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:windows-latest target:x86_64-pc-windows-msvc toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:build os:macos-latest target:aarch64-apple-ios toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:macos-14 target:aarch64-apple-darwin toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:windows-latest target:x86_64-pc-windows-msvc toolchain:1.90]) (push) Has been cancelled
Some checks failed
Audit / audit-js (push) Failing after 0s
bench / bench (map[os:ubuntu-latest runner:xvfb-run --auto-servernum target:x86_64-unknown-linux-gnu], nightly) (push) Failing after 0s
covector version or publish / run-integration-tests (ubuntu-latest) (push) Failing after 0s
Audit / audit-rust (push) Failing after 0s
test `@tauri-apps/cli` / test (ubuntu-latest) (push) Failing after 0s
test `tauri-cli` / test (map[os:ubuntu-latest target:x86_64-unknown-linux-gnu]) (push) Failing after 0s
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:ubuntu-latest target:x86_64-unknown-linux-gnu toolchain:1.90]) (push) Failing after 0s
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:ubuntu-latest target:x86_64-unknown-linux-gnu toolchain:1.90]) (push) Failing after 0s
test core / test (map[args:--no-default-features key:no-default], map[cargo:cross command:build os:ubuntu-latest target:aarch64-linux-android toolchain:1.90]) (push) Failing after 0s
deploy schema worker / deploy (push) Failing after 0s
lint rust / clippy (map[cargo:cargo include: os:ubuntu-latest target:x86_64-unknown-linux-gnu]) (push) Failing after 0s
lint rust / clippy (map[cargo:cross include:--package api os:ubuntu-latest target:aarch64-linux-android]) (push) Failing after 0s
supply chain health status / check rust dependencies with cargo vet (push) Failing after 0s
test `@tauri-apps/api` e2e (mobile) / android (push) Failing after 0s
test `@tauri-apps/api` e2e / test (map[name:Linux os:ubuntu-latest]) (push) Failing after 0s
test core / test (map[args:--all-features key:all], map[cargo:cross command:build os:ubuntu-latest target:aarch64-linux-android toolchain:1.90]) (push) Failing after 0s
Udeps / changes (push) Failing after 0s
Udeps / setup (push) Has been skipped
Udeps / udeps (push) Has been skipped
bench / bench (map[os:macos-latest runner: target:aarch64-apple-darwin], nightly) (push) Has been cancelled
bench / bench (map[os:windows-latest runner: target:x86_64-pc-windows-msvc], nightly) (push) Has been cancelled
covector version or publish / run-integration-tests (macos-latest) (push) Has been cancelled
covector version or publish / run-integration-tests (windows-latest) (push) Has been cancelled
covector version or publish / version-or-publish (push) Has been cancelled
test `@tauri-apps/cli` / test (macos-latest) (push) Has been cancelled
test `tauri-cli` / test (map[args:--no-default-features --features native-tls-vendored os:windows-latest restore-only:true target:aarch64-pc-windows-msvc]) (push) Has been cancelled
test `tauri-cli` / test (map[os:macos-latest target:x86_64-apple-darwin]) (push) Has been cancelled
test `tauri-cli` / test (map[os:windows-latest target:x86_64-pc-windows-msvc]) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:build os:macos-latest target:aarch64-apple-ios toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:macos-14 target:aarch64-apple-darwin toolchain:1.90]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include: os:macos-latest target:aarch64-apple-darwin]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include: os:windows-latest target:x86_64-pc-windows-msvc]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include:--package api os:macos-latest target:aarch64-apple-ios]) (push) Has been cancelled
test `@tauri-apps/api` e2e (mobile) / ios (push) Has been cancelled
test `@tauri-apps/api` e2e / test (map[name:Windows os:windows-latest]) (push) Has been cancelled
test `@tauri-apps/api` e2e / test (map[name:macOS os:macos-latest]) (push) Has been cancelled
test `@tauri-apps/cli` / test (windows-latest) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:windows-latest target:x86_64-pc-windows-msvc toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:build os:macos-latest target:aarch64-apple-ios toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:macos-14 target:aarch64-apple-darwin toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:windows-latest target:x86_64-pc-windows-msvc toolchain:1.90]) (push) Has been cancelled
This commit is contained in:
1517
crates/tauri-runtime-wry/CHANGELOG.md
Normal file
1517
crates/tauri-runtime-wry/CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
77
crates/tauri-runtime-wry/Cargo.toml
Normal file
77
crates/tauri-runtime-wry/Cargo.toml
Normal file
@@ -0,0 +1,77 @@
|
||||
[package]
|
||||
name = "tauri-runtime-wry"
|
||||
version = "2.11.4"
|
||||
description = "Wry bindings to the Tauri runtime"
|
||||
exclude = ["CHANGELOG.md", "/target"]
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
categories.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
wry = { version = "0.57.0", default-features = false, features = [
|
||||
"os-webview",
|
||||
"linux-body",
|
||||
] }
|
||||
tao = { version = "0.37.0", default-features = false, features = ["rwh_06"] }
|
||||
tauri-runtime = { workspace = true }
|
||||
tauri-utils = { workspace = true }
|
||||
raw-window-handle = "0.6"
|
||||
http = "1"
|
||||
url = "2"
|
||||
tracing = { version = "0.1", optional = true }
|
||||
log = "0.4.21"
|
||||
|
||||
[target."cfg(windows)".dependencies]
|
||||
webview2-com = "0.39"
|
||||
softbuffer = { version = "0.4", default-features = false }
|
||||
once_cell = "1.20"
|
||||
|
||||
[target."cfg(windows)".dependencies.windows]
|
||||
version = "0.62"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Graphics_Dwm",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
]
|
||||
|
||||
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
|
||||
gtk = { version = "0.18", features = ["v3_24"] }
|
||||
webkit2gtk = { version = "=2.0", features = ["v2_40"] }
|
||||
percent-encoding = "2"
|
||||
|
||||
[target.'cfg(target_vendor = "apple")'.dependencies]
|
||||
objc2 = "0.6"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
objc2-app-kit = { version = "0.3.2", default-features = false, features = [
|
||||
"block2",
|
||||
"NSApplication",
|
||||
"NSResponder",
|
||||
"NSView",
|
||||
"NSWindow",
|
||||
"NSScreen",
|
||||
"NSGraphics",
|
||||
] }
|
||||
|
||||
[target."cfg(target_os = \"android\")".dependencies]
|
||||
jni = "0.21"
|
||||
|
||||
[features]
|
||||
default = ["x11", "dbus"]
|
||||
devtools = ["wry/devtools", "tauri-runtime/devtools"]
|
||||
x11 = ["tao/x11", "wry/x11"]
|
||||
macos-private-api = ["tauri-runtime/macos-private-api"]
|
||||
# TODO: Remove in v3 - wry does not have this feature anymore
|
||||
objc-exception = []
|
||||
tracing = ["dep:tracing", "wry/tracing"]
|
||||
macos-proxy = ["wry/mac-proxy"]
|
||||
unstable = []
|
||||
common-controls-v6 = ["windows/Win32_UI_Controls", "windows/Win32_UI_Shell"]
|
||||
dbus = ["tao/dbus"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
177
crates/tauri-runtime-wry/LICENSE-APACHE-2.0
Normal file
177
crates/tauri-runtime-wry/LICENSE-APACHE-2.0
Normal file
@@ -0,0 +1,177 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
21
crates/tauri-runtime-wry/LICENSE-MIT
Normal file
21
crates/tauri-runtime-wry/LICENSE-MIT
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 - Present Tauri Apps Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
44
crates/tauri-runtime-wry/README.md
Normal file
44
crates/tauri-runtime-wry/README.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# tauri-runtime-wry
|
||||
|
||||
<img align="right" src="https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png" height="128" width="128">
|
||||
|
||||
[](https://github.com/tauri-apps/tauri)
|
||||
[](https://discord.gg/SpmNs4S)
|
||||
|
||||
[](https://github.com/tauri-apps/tauri/actions/workflows/test-core.yml)
|
||||
[](https://tauri.app)
|
||||
|
||||
[](https://good-labs.github.io/greater-good-affirmation)
|
||||
[](https://opencollective.com/tauri)
|
||||
|
||||
| Component | Version |
|
||||
| ----------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
||||
| tauri-runtime-wry | [](https://crates.io/crates/tauri-runtime-wry) |
|
||||
|
||||
## About Tauri
|
||||
|
||||
Tauri is a polyglot and generic system that is very composable and allows engineers to make a wide variety of applications. It is used for building applications for Desktop Computers using a combination of Rust tools and HTML rendered in a Webview. Apps built with Tauri can ship with any number of pieces of an optional JS API / Rust API so that webviews can control the system via message passing. In fact, developers can extend the default API with their own functionality and bridge the Webview and Rust-based backend easily.
|
||||
|
||||
Tauri apps can have custom menus and have tray-type interfaces. They can be updated, and are managed by the user's operating system as expected. They are very small, because they use the system's webview. They do not ship a runtime, since the final binary is compiled from rust. This makes the reversing of Tauri apps not a trivial task.
|
||||
|
||||
## This module
|
||||
|
||||
This crate opens up direct systems-level interactions specifically for WRY, such as printing, monitor detection, and other windowing related tasks. `tauri-runtime` implementation for WRY.
|
||||
None of the exposed API of this crate is stable, and it may break semver
|
||||
compatibility in the future. The major version only signifies the intended Tauri version.
|
||||
|
||||
To learn more about the details of how all of these pieces fit together, please consult this [ARCHITECTURE.md](https://github.com/tauri-apps/tauri/blob/dev/ARCHITECTURE.md) document.
|
||||
|
||||
## Semver
|
||||
|
||||
**tauri** is following [Semantic Versioning 2.0](https://semver.org/).
|
||||
|
||||
## Licenses
|
||||
|
||||
Code: (c) 2021 - The Tauri Programme within The Commons Conservancy.
|
||||
|
||||
MIT or MIT/Apache 2.0 where applicable.
|
||||
|
||||
Logo: CC-BY-NC-ND
|
||||
|
||||
- Original Tauri Logo Designs by [Daniel Thompson-Yvetot](https://github.com/nothingismagick) and [Guillaume Chau](https://github.com/akryum)
|
||||
19
crates/tauri-runtime-wry/build.rs
Normal file
19
crates/tauri-runtime-wry/build.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// creates a cfg alias if `has_feature` is true.
|
||||
// `alias` must be a snake case string.
|
||||
fn alias(alias: &str, has_feature: bool) {
|
||||
println!("cargo:rustc-check-cfg=cfg({alias})");
|
||||
if has_feature {
|
||||
println!("cargo:rustc-cfg={alias}");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
let mobile = target_os == "ios" || target_os == "android";
|
||||
alias("desktop", !mobile);
|
||||
alias("mobile", mobile);
|
||||
}
|
||||
19
crates/tauri-runtime-wry/src/dialog/mod.rs
Normal file
19
crates/tauri-runtime-wry/src/dialog/mod.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#[cfg(windows)]
|
||||
mod windows;
|
||||
|
||||
// Takes a `&'static str` here since we convert clickable hyperlinks,
|
||||
// DO NOT pass in untrusted input
|
||||
#[cfg_attr(not(windows), allow(unused))]
|
||||
pub fn error(err: &'static str) {
|
||||
#[cfg(windows)]
|
||||
windows::error(err);
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
unimplemented!("Error dialog is not implemented for this platform");
|
||||
}
|
||||
}
|
||||
124
crates/tauri-runtime-wry/src/dialog/windows.rs
Normal file
124
crates/tauri-runtime-wry/src/dialog/windows.rs
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use windows::core::{HSTRING, w};
|
||||
|
||||
enum Level {
|
||||
Error,
|
||||
#[allow(unused)]
|
||||
Warning,
|
||||
#[allow(unused)]
|
||||
Info,
|
||||
}
|
||||
|
||||
pub fn error(err: &'static str) {
|
||||
dialog_inner(err, Level::Error);
|
||||
}
|
||||
|
||||
fn dialog_inner(err: &str, level: Level) {
|
||||
let title = match level {
|
||||
Level::Warning => w!("Warning"),
|
||||
Level::Error => w!("Error"),
|
||||
Level::Info => w!("Info"),
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "common-controls-v6"))]
|
||||
{
|
||||
use windows::Win32::UI::WindowsAndMessaging::*;
|
||||
|
||||
let err = remove_hyperlink(err);
|
||||
let err = HSTRING::from(err);
|
||||
|
||||
unsafe {
|
||||
MessageBoxW(
|
||||
None,
|
||||
&err,
|
||||
title,
|
||||
match level {
|
||||
Level::Warning => MB_ICONWARNING,
|
||||
Level::Error => MB_ICONERROR,
|
||||
Level::Info => MB_ICONINFORMATION,
|
||||
},
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "common-controls-v6")]
|
||||
{
|
||||
use windows::Win32::Foundation::*;
|
||||
use windows::Win32::UI::Controls::*;
|
||||
use windows::Win32::UI::Shell::*;
|
||||
use windows::Win32::UI::WindowsAndMessaging::*;
|
||||
use windows::core::{HRESULT, PCWSTR};
|
||||
|
||||
extern "system" fn task_dialog_callback(
|
||||
_hwnd: HWND,
|
||||
msg: TASKDIALOG_NOTIFICATIONS,
|
||||
_wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
_data: isize,
|
||||
) -> HRESULT {
|
||||
if msg == TDN_HYPERLINK_CLICKED {
|
||||
let link = PCWSTR(lparam.0 as _);
|
||||
let _ = unsafe { ShellExecuteW(None, None, link, None, None, SW_SHOWNORMAL) };
|
||||
}
|
||||
|
||||
S_OK
|
||||
}
|
||||
|
||||
let err = HSTRING::from(err);
|
||||
let err = PCWSTR(err.as_ptr());
|
||||
|
||||
let task_dialog_config = TASKDIALOGCONFIG {
|
||||
cbSize: std::mem::size_of::<TASKDIALOGCONFIG>() as u32,
|
||||
dwFlags: TDF_ALLOW_DIALOG_CANCELLATION | TDF_ENABLE_HYPERLINKS,
|
||||
pszWindowTitle: title,
|
||||
pszContent: err,
|
||||
Anonymous1: TASKDIALOGCONFIG_0 {
|
||||
pszMainIcon: match level {
|
||||
Level::Warning => TD_WARNING_ICON,
|
||||
Level::Error => TD_ERROR_ICON,
|
||||
Level::Info => TD_INFORMATION_ICON,
|
||||
},
|
||||
},
|
||||
dwCommonButtons: TDCBF_OK_BUTTON,
|
||||
pfCallback: Some(task_dialog_callback),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let _ = unsafe { TaskDialogIndirect(&task_dialog_config, None, None, None) };
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "common-controls-v6"))]
|
||||
fn remove_hyperlink(str: &str) -> String {
|
||||
let mut result = String::new();
|
||||
let mut in_hyperlink = false;
|
||||
|
||||
for c in str.chars() {
|
||||
if c == '<' {
|
||||
in_hyperlink = true;
|
||||
} else if c == '>' {
|
||||
in_hyperlink = false;
|
||||
} else if !in_hyperlink {
|
||||
result.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(not(feature = "common-controls-v6"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_remove_hyperlink() {
|
||||
let input = "This is a <A href=\"some link\">test</A> string.";
|
||||
let expected = "This is a test string.";
|
||||
let result = remove_hyperlink(input);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
}
|
||||
18
crates/tauri-runtime-wry/src/monitor/linux.rs
Normal file
18
crates/tauri-runtime-wry/src/monitor/linux.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use gtk::prelude::MonitorExt;
|
||||
use tao::platform::unix::MonitorHandleExtUnix;
|
||||
use tauri_runtime::dpi::{LogicalPosition, LogicalSize, PhysicalRect};
|
||||
|
||||
impl super::MonitorExt for tao::monitor::MonitorHandle {
|
||||
fn work_area(&self) -> PhysicalRect<i32, u32> {
|
||||
let rect = self.gdk_monitor().workarea();
|
||||
let scale_factor = self.scale_factor();
|
||||
PhysicalRect {
|
||||
size: LogicalSize::new(rect.width() as u32, rect.height() as u32).to_physical(scale_factor),
|
||||
position: LogicalPosition::new(rect.x(), rect.y()).to_physical(scale_factor),
|
||||
}
|
||||
}
|
||||
}
|
||||
36
crates/tauri-runtime-wry/src/monitor/macos.rs
Normal file
36
crates/tauri-runtime-wry/src/monitor/macos.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use tauri_runtime::dpi::{LogicalSize, PhysicalRect};
|
||||
|
||||
impl super::MonitorExt for tao::monitor::MonitorHandle {
|
||||
fn work_area(&self) -> PhysicalRect<i32, u32> {
|
||||
use objc2_app_kit::NSScreen;
|
||||
use tao::platform::macos::MonitorHandleExtMacOS;
|
||||
if let Some(ns_screen) = self.ns_screen() {
|
||||
let ns_screen: &NSScreen = unsafe { &*ns_screen.cast() };
|
||||
let screen_frame = ns_screen.frame();
|
||||
let visible_frame = ns_screen.visibleFrame();
|
||||
|
||||
let scale_factor = self.scale_factor();
|
||||
|
||||
let mut position = self.position().to_logical::<f64>(scale_factor);
|
||||
|
||||
position.x += visible_frame.origin.x - screen_frame.origin.x;
|
||||
|
||||
position.y += (screen_frame.origin.y + screen_frame.size.height)
|
||||
- (visible_frame.origin.y + visible_frame.size.height);
|
||||
PhysicalRect {
|
||||
size: LogicalSize::new(visible_frame.size.width, visible_frame.size.height)
|
||||
.to_physical(scale_factor),
|
||||
position: position.to_physical(scale_factor),
|
||||
}
|
||||
} else {
|
||||
PhysicalRect {
|
||||
size: self.size(),
|
||||
position: self.position(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
crates/tauri-runtime-wry/src/monitor/mod.rs
Normal file
37
crates/tauri-runtime-wry/src/monitor/mod.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use tauri_runtime::dpi::PhysicalRect;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
mod linux;
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
#[cfg(windows)]
|
||||
mod windows;
|
||||
|
||||
pub trait MonitorExt {
|
||||
/// Get the work area of this monitor
|
||||
///
|
||||
/// ## Platform-specific:
|
||||
///
|
||||
/// - **Android / iOS**: Unsupported.
|
||||
fn work_area(&self) -> PhysicalRect<i32, u32>;
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
impl MonitorExt for tao::monitor::MonitorHandle {
|
||||
fn work_area(&self) -> PhysicalRect<i32, u32> {
|
||||
PhysicalRect {
|
||||
size: self.size(),
|
||||
position: self.position(),
|
||||
}
|
||||
}
|
||||
}
|
||||
32
crates/tauri-runtime-wry/src/monitor/windows.rs
Normal file
32
crates/tauri-runtime-wry/src/monitor/windows.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use tao::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use tauri_runtime::dpi::PhysicalRect;
|
||||
|
||||
impl super::MonitorExt for tao::monitor::MonitorHandle {
|
||||
fn work_area(&self) -> PhysicalRect<i32, u32> {
|
||||
use tao::platform::windows::MonitorHandleExtWindows;
|
||||
use windows::Win32::Graphics::Gdi::{GetMonitorInfoW, HMONITOR, MONITORINFO};
|
||||
let mut monitor_info = MONITORINFO {
|
||||
cbSize: std::mem::size_of::<MONITORINFO>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
let status = unsafe { GetMonitorInfoW(HMONITOR(self.hmonitor() as _), &mut monitor_info) };
|
||||
if status.as_bool() {
|
||||
PhysicalRect {
|
||||
size: PhysicalSize::new(
|
||||
(monitor_info.rcWork.right - monitor_info.rcWork.left) as u32,
|
||||
(monitor_info.rcWork.bottom - monitor_info.rcWork.top) as u32,
|
||||
),
|
||||
position: PhysicalPosition::new(monitor_info.rcWork.left, monitor_info.rcWork.top),
|
||||
}
|
||||
} else {
|
||||
PhysicalRect {
|
||||
size: self.size(),
|
||||
position: self.position(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
654
crates/tauri-runtime-wry/src/undecorated_resizing.rs
Normal file
654
crates/tauri-runtime-wry/src/undecorated_resizing.rs
Normal file
@@ -0,0 +1,654 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#![cfg(any(
|
||||
windows,
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
|
||||
const CLIENT: isize = 0b0000;
|
||||
const LEFT: isize = 0b0001;
|
||||
const RIGHT: isize = 0b0010;
|
||||
const TOP: isize = 0b0100;
|
||||
const BOTTOM: isize = 0b1000;
|
||||
const TOPLEFT: isize = TOP | LEFT;
|
||||
const TOPRIGHT: isize = TOP | RIGHT;
|
||||
const BOTTOMLEFT: isize = BOTTOM | LEFT;
|
||||
const BOTTOMRIGHT: isize = BOTTOM | RIGHT;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub use self::gtk::*;
|
||||
#[cfg(windows)]
|
||||
pub use self::windows::*;
|
||||
|
||||
#[cfg(windows)]
|
||||
type WindowPositions = i32;
|
||||
#[cfg(not(windows))]
|
||||
type WindowPositions = f64;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
enum HitTestResult {
|
||||
Client,
|
||||
Left,
|
||||
Right,
|
||||
Top,
|
||||
Bottom,
|
||||
TopLeft,
|
||||
TopRight,
|
||||
BottomLeft,
|
||||
BottomRight,
|
||||
NoWhere,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn hit_test(
|
||||
left: WindowPositions,
|
||||
top: WindowPositions,
|
||||
right: WindowPositions,
|
||||
bottom: WindowPositions,
|
||||
cx: WindowPositions,
|
||||
cy: WindowPositions,
|
||||
border_x: WindowPositions,
|
||||
border_y: WindowPositions,
|
||||
) -> HitTestResult {
|
||||
#[rustfmt::skip]
|
||||
let result = (LEFT * (cx < left + border_x) as isize)
|
||||
| (RIGHT * (cx >= right - border_x) as isize)
|
||||
| (TOP * (cy < top + border_y) as isize)
|
||||
| (BOTTOM * (cy >= bottom - border_y) as isize);
|
||||
|
||||
match result {
|
||||
CLIENT => HitTestResult::Client,
|
||||
LEFT => HitTestResult::Left,
|
||||
RIGHT => HitTestResult::Right,
|
||||
TOP => HitTestResult::Top,
|
||||
BOTTOM => HitTestResult::Bottom,
|
||||
TOPLEFT => HitTestResult::TopLeft,
|
||||
TOPRIGHT => HitTestResult::TopRight,
|
||||
BOTTOMLEFT => HitTestResult::BottomLeft,
|
||||
BOTTOMRIGHT => HitTestResult::BottomRight,
|
||||
_ => HitTestResult::NoWhere,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod windows {
|
||||
use crate::util;
|
||||
|
||||
use super::{HitTestResult, hit_test};
|
||||
|
||||
use windows::Win32::System::LibraryLoader::*;
|
||||
use windows::Win32::UI::WindowsAndMessaging::*;
|
||||
use windows::Win32::{Foundation::*, UI::Shell::SetWindowSubclass};
|
||||
use windows::Win32::{Graphics::Gdi::*, UI::Shell::DefSubclassProc};
|
||||
use windows::core::*;
|
||||
|
||||
impl HitTestResult {
|
||||
fn to_win32(self) -> i32 {
|
||||
match self {
|
||||
HitTestResult::Left => HTLEFT as _,
|
||||
HitTestResult::Right => HTRIGHT as _,
|
||||
HitTestResult::Top => HTTOP as _,
|
||||
HitTestResult::Bottom => HTBOTTOM as _,
|
||||
HitTestResult::TopLeft => HTTOPLEFT as _,
|
||||
HitTestResult::TopRight => HTTOPRIGHT as _,
|
||||
HitTestResult::BottomLeft => HTBOTTOMLEFT as _,
|
||||
HitTestResult::BottomRight => HTBOTTOMRIGHT as _,
|
||||
_ => HTTRANSPARENT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const CLASS_NAME: PCWSTR = w!("TAURI_DRAG_RESIZE_BORDERS");
|
||||
const WINDOW_NAME: PCWSTR = w!("TAURI_DRAG_RESIZE_WINDOW");
|
||||
|
||||
const WM_UPDATE_UNDECORATED_SHADOWS: u32 = WM_USER + 100;
|
||||
|
||||
struct UndecoratedResizingData {
|
||||
child: HWND,
|
||||
has_undecorated_shadows: bool,
|
||||
}
|
||||
|
||||
pub fn attach_resize_handler(hwnd: isize, has_undecorated_shadows: bool) {
|
||||
let parent = HWND(hwnd as _);
|
||||
|
||||
// return early if we already attached
|
||||
if unsafe { FindWindowExW(Some(parent), None, CLASS_NAME, WINDOW_NAME) }.is_ok() {
|
||||
return;
|
||||
}
|
||||
|
||||
let class = WNDCLASSEXW {
|
||||
cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
|
||||
style: WNDCLASS_STYLES::default(),
|
||||
lpfnWndProc: Some(drag_resize_window_proc),
|
||||
cbClsExtra: 0,
|
||||
cbWndExtra: 0,
|
||||
hInstance: unsafe { HINSTANCE(GetModuleHandleW(PCWSTR::null()).unwrap_or_default().0) },
|
||||
hIcon: HICON::default(),
|
||||
hCursor: HCURSOR::default(),
|
||||
hbrBackground: HBRUSH::default(),
|
||||
lpszMenuName: PCWSTR::null(),
|
||||
lpszClassName: CLASS_NAME,
|
||||
hIconSm: HICON::default(),
|
||||
};
|
||||
|
||||
unsafe { RegisterClassExW(&class) };
|
||||
|
||||
let mut rect = RECT::default();
|
||||
unsafe { GetClientRect(parent, &mut rect).unwrap() };
|
||||
let width = rect.right - rect.left;
|
||||
let height = rect.bottom - rect.top;
|
||||
|
||||
let data = UndecoratedResizingData {
|
||||
child: HWND::default(),
|
||||
has_undecorated_shadows,
|
||||
};
|
||||
|
||||
let Ok(drag_window) = (unsafe {
|
||||
CreateWindowExW(
|
||||
WINDOW_EX_STYLE::default(),
|
||||
CLASS_NAME,
|
||||
WINDOW_NAME,
|
||||
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height,
|
||||
Some(parent),
|
||||
None,
|
||||
GetModuleHandleW(None).ok().map(|h| HINSTANCE(h.0)),
|
||||
Some(Box::into_raw(Box::new(data)) as _),
|
||||
)
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
unsafe {
|
||||
set_drag_hwnd_rgn(drag_window, width, height, has_undecorated_shadows);
|
||||
|
||||
let _ = SetWindowPos(
|
||||
drag_window,
|
||||
Some(HWND_TOP),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
SWP_ASYNCWINDOWPOS | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSIZE,
|
||||
);
|
||||
|
||||
let data = UndecoratedResizingData {
|
||||
child: drag_window,
|
||||
has_undecorated_shadows,
|
||||
};
|
||||
|
||||
let _ = SetWindowSubclass(
|
||||
parent,
|
||||
Some(subclass_parent),
|
||||
(WM_USER + 1) as _,
|
||||
Box::into_raw(Box::new(data)) as _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn subclass_parent(
|
||||
parent: HWND,
|
||||
msg: u32,
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
_: usize,
|
||||
data: usize,
|
||||
) -> LRESULT {
|
||||
unsafe {
|
||||
match msg {
|
||||
WM_SIZE => {
|
||||
let data = data as *mut UndecoratedResizingData;
|
||||
let data = &*data;
|
||||
let child = data.child;
|
||||
let has_undecorated_shadows = data.has_undecorated_shadows;
|
||||
|
||||
// when parent is maximized, remove the undecorated window drag resize region
|
||||
if is_maximized(parent).unwrap_or(false) {
|
||||
let _ = SetWindowPos(
|
||||
child,
|
||||
Some(HWND_TOP),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
SWP_ASYNCWINDOWPOS | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE,
|
||||
);
|
||||
} else {
|
||||
// otherwise update the cutout region
|
||||
let mut rect = RECT::default();
|
||||
if GetClientRect(parent, &mut rect).is_ok() {
|
||||
let width = rect.right - rect.left;
|
||||
let height = rect.bottom - rect.top;
|
||||
|
||||
let _ = SetWindowPos(
|
||||
child,
|
||||
Some(HWND_TOP),
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height,
|
||||
SWP_ASYNCWINDOWPOS | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE,
|
||||
);
|
||||
|
||||
set_drag_hwnd_rgn(child, width, height, has_undecorated_shadows);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WM_UPDATE_UNDECORATED_SHADOWS => {
|
||||
let data = data as *mut UndecoratedResizingData;
|
||||
let data = &mut *data;
|
||||
data.has_undecorated_shadows = wparam.0 != 0;
|
||||
}
|
||||
|
||||
WM_NCDESTROY => {
|
||||
let data = data as *mut UndecoratedResizingData;
|
||||
drop(Box::from_raw(data));
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
DefSubclassProc(parent, msg, wparam, lparam)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn drag_resize_window_proc(
|
||||
child: HWND,
|
||||
msg: u32,
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
) -> LRESULT {
|
||||
unsafe {
|
||||
match msg {
|
||||
WM_CREATE => {
|
||||
let data = lparam.0 as *mut CREATESTRUCTW;
|
||||
let data = (*data).lpCreateParams as *mut UndecoratedResizingData;
|
||||
(*data).child = child;
|
||||
SetWindowLongPtrW(child, GWLP_USERDATA, data as _);
|
||||
}
|
||||
|
||||
WM_NCHITTEST => {
|
||||
let data = GetWindowLongPtrW(child, GWLP_USERDATA);
|
||||
let data = &*(data as *mut UndecoratedResizingData);
|
||||
|
||||
let Ok(parent) = GetParent(child) else {
|
||||
return DefWindowProcW(child, msg, wparam, lparam);
|
||||
};
|
||||
let style = GetWindowLongPtrW(parent, GWL_STYLE);
|
||||
let style = WINDOW_STYLE(style as u32);
|
||||
|
||||
let is_resizable = (style & WS_SIZEBOX).0 != 0;
|
||||
if !is_resizable {
|
||||
return DefWindowProcW(child, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
// if the window has undecorated shadows,
|
||||
// it should always be the top border,
|
||||
// ensured by the cutout drag window
|
||||
if data.has_undecorated_shadows {
|
||||
return LRESULT(HTTOP as _);
|
||||
}
|
||||
|
||||
let mut rect = RECT::default();
|
||||
if GetWindowRect(child, &mut rect).is_err() {
|
||||
return DefWindowProcW(child, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
let (cx, cy) = (GET_X_LPARAM(lparam) as i32, GET_Y_LPARAM(lparam) as i32);
|
||||
|
||||
let dpi = util::hwnd_dpi(child);
|
||||
let border_x = util::get_system_metrics_for_dpi(SM_CXFRAME, dpi);
|
||||
let border_y = util::get_system_metrics_for_dpi(SM_CYFRAME, dpi);
|
||||
|
||||
let res = hit_test(
|
||||
rect.left,
|
||||
rect.top,
|
||||
rect.right,
|
||||
rect.bottom,
|
||||
cx,
|
||||
cy,
|
||||
border_x,
|
||||
border_y,
|
||||
);
|
||||
|
||||
return LRESULT(res.to_win32() as _);
|
||||
}
|
||||
|
||||
WM_NCLBUTTONDOWN => {
|
||||
let data = GetWindowLongPtrW(child, GWLP_USERDATA);
|
||||
let data = &*(data as *mut UndecoratedResizingData);
|
||||
|
||||
let Ok(parent) = GetParent(child) else {
|
||||
return DefWindowProcW(child, msg, wparam, lparam);
|
||||
};
|
||||
let style = GetWindowLongPtrW(parent, GWL_STYLE);
|
||||
let style = WINDOW_STYLE(style as u32);
|
||||
|
||||
let is_resizable = (style & WS_SIZEBOX).0 != 0;
|
||||
if !is_resizable {
|
||||
return DefWindowProcW(child, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
let (cx, cy) = (GET_X_LPARAM(lparam) as i32, GET_Y_LPARAM(lparam) as i32);
|
||||
|
||||
// if the window has undecorated shadows,
|
||||
// it should always be the top border,
|
||||
// ensured by the cutout drag window
|
||||
let res = if data.has_undecorated_shadows {
|
||||
HitTestResult::Top
|
||||
} else {
|
||||
let mut rect = RECT::default();
|
||||
if GetWindowRect(child, &mut rect).is_err() {
|
||||
return DefWindowProcW(child, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
let dpi = util::hwnd_dpi(child);
|
||||
let border_x = util::get_system_metrics_for_dpi(SM_CXFRAME, dpi);
|
||||
let border_y = util::get_system_metrics_for_dpi(SM_CYFRAME, dpi);
|
||||
|
||||
hit_test(
|
||||
rect.left,
|
||||
rect.top,
|
||||
rect.right,
|
||||
rect.bottom,
|
||||
cx,
|
||||
cy,
|
||||
border_x,
|
||||
border_y,
|
||||
)
|
||||
};
|
||||
|
||||
if res != HitTestResult::NoWhere {
|
||||
let points = POINTS {
|
||||
x: cx as i16,
|
||||
y: cy as i16,
|
||||
};
|
||||
|
||||
let _ = PostMessageW(
|
||||
Some(parent),
|
||||
WM_NCLBUTTONDOWN,
|
||||
WPARAM(res.to_win32() as _),
|
||||
LPARAM(&points as *const _ as _),
|
||||
);
|
||||
}
|
||||
|
||||
return LRESULT(0);
|
||||
}
|
||||
|
||||
WM_UPDATE_UNDECORATED_SHADOWS => {
|
||||
let data = GetWindowLongPtrW(child, GWLP_USERDATA);
|
||||
let data = &mut *(data as *mut UndecoratedResizingData);
|
||||
data.has_undecorated_shadows = wparam.0 != 0;
|
||||
}
|
||||
|
||||
WM_NCDESTROY => {
|
||||
let data = GetWindowLongPtrW(child, GWLP_USERDATA);
|
||||
let data = data as *mut UndecoratedResizingData;
|
||||
drop(Box::from_raw(data));
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
DefWindowProcW(child, msg, wparam, lparam)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detach_resize_handler(hwnd: isize) {
|
||||
let hwnd = HWND(hwnd as _);
|
||||
|
||||
let Ok(child) = (unsafe { FindWindowExW(Some(hwnd), None, CLASS_NAME, WINDOW_NAME) }) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let _ = unsafe { DestroyWindow(child) };
|
||||
}
|
||||
|
||||
unsafe fn set_drag_hwnd_rgn(hwnd: HWND, width: i32, height: i32, only_top: bool) {
|
||||
unsafe {
|
||||
// The window used for drag resizing an undecorated window
|
||||
// is a child HWND that covers the whole window
|
||||
// and so we need create a cut out in the middle for the parent and other child
|
||||
// windows like the webview can receive mouse events.
|
||||
|
||||
let dpi = util::hwnd_dpi(hwnd);
|
||||
let border_x = util::get_system_metrics_for_dpi(SM_CXFRAME, dpi);
|
||||
let border_y = util::get_system_metrics_for_dpi(SM_CYFRAME, dpi);
|
||||
|
||||
// hrgn1 must be mutable to call .free() later
|
||||
let mut hrgn1 = CreateRectRgn(0, 0, width, height);
|
||||
|
||||
let x1 = if only_top { 0 } else { border_x };
|
||||
let y1 = border_y;
|
||||
let x2 = if only_top { width } else { width - border_x };
|
||||
let y2 = if only_top { height } else { height - border_y };
|
||||
|
||||
// Wrap hrgn2 in Owned so it is automatically freed when going out of scope
|
||||
let hrgn2 = Owned::new(CreateRectRgn(x1, y1, x2, y2));
|
||||
|
||||
CombineRgn(Some(hrgn1), Some(hrgn1), Some(*hrgn2), RGN_DIFF);
|
||||
|
||||
// Try to set the window region
|
||||
if SetWindowRgn(hwnd, Some(hrgn1), true) == 0 {
|
||||
// If it fails, we must free hrgn1 manually
|
||||
hrgn1.free();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_drag_hwnd_rgn_for_undecorated(hwnd: isize, has_undecorated_shadows: bool) {
|
||||
let hwnd = HWND(hwnd as _);
|
||||
|
||||
let mut rect = RECT::default();
|
||||
let Ok(_) = (unsafe { GetClientRect(hwnd, &mut rect) }) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let width = rect.right - rect.left;
|
||||
let height = rect.bottom - rect.top;
|
||||
|
||||
let Ok(child) = (unsafe { FindWindowExW(Some(hwnd), None, CLASS_NAME, WINDOW_NAME) }) else {
|
||||
return;
|
||||
};
|
||||
|
||||
unsafe { set_drag_hwnd_rgn(child, width, height, has_undecorated_shadows) };
|
||||
|
||||
unsafe {
|
||||
SendMessageW(
|
||||
hwnd,
|
||||
WM_UPDATE_UNDECORATED_SHADOWS,
|
||||
None,
|
||||
Some(LPARAM(has_undecorated_shadows as _)),
|
||||
)
|
||||
};
|
||||
unsafe {
|
||||
SendMessageW(
|
||||
child,
|
||||
WM_UPDATE_UNDECORATED_SHADOWS,
|
||||
None,
|
||||
Some(LPARAM(has_undecorated_shadows as _)),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
fn is_maximized(window: HWND) -> windows::core::Result<bool> {
|
||||
let mut placement = WINDOWPLACEMENT {
|
||||
length: std::mem::size_of::<WINDOWPLACEMENT>() as u32,
|
||||
..WINDOWPLACEMENT::default()
|
||||
};
|
||||
unsafe { GetWindowPlacement(window, &mut placement)? };
|
||||
Ok(placement.showCmd == SW_MAXIMIZE.0 as u32)
|
||||
}
|
||||
|
||||
/// Implementation of the `GET_X_LPARAM` macro.
|
||||
#[allow(non_snake_case)]
|
||||
#[inline]
|
||||
fn GET_X_LPARAM(lparam: LPARAM) -> i16 {
|
||||
((lparam.0 as usize) & 0xFFFF) as u16 as i16
|
||||
}
|
||||
|
||||
/// Implementation of the `GET_Y_LPARAM` macro.
|
||||
#[allow(non_snake_case)]
|
||||
#[inline]
|
||||
fn GET_Y_LPARAM(lparam: LPARAM) -> i16 {
|
||||
(((lparam.0 as usize) & 0xFFFF_0000) >> 16) as u16 as i16
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
mod gtk {
|
||||
use super::{HitTestResult, hit_test};
|
||||
use gtk::prelude::*;
|
||||
|
||||
const BORDERLESS_RESIZE_INSET: i32 = 5;
|
||||
|
||||
impl HitTestResult {
|
||||
fn to_gtk_edge(self) -> gtk::gdk::WindowEdge {
|
||||
match self {
|
||||
HitTestResult::Client | HitTestResult::NoWhere => gtk::gdk::WindowEdge::__Unknown(0),
|
||||
HitTestResult::Left => gtk::gdk::WindowEdge::West,
|
||||
HitTestResult::Right => gtk::gdk::WindowEdge::East,
|
||||
HitTestResult::Top => gtk::gdk::WindowEdge::North,
|
||||
HitTestResult::Bottom => gtk::gdk::WindowEdge::South,
|
||||
HitTestResult::TopLeft => gtk::gdk::WindowEdge::NorthWest,
|
||||
HitTestResult::TopRight => gtk::gdk::WindowEdge::NorthEast,
|
||||
HitTestResult::BottomLeft => gtk::gdk::WindowEdge::SouthWest,
|
||||
HitTestResult::BottomRight => gtk::gdk::WindowEdge::SouthEast,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_cursor(self, display: >k::gdk::Display) -> Option<gtk::gdk::Cursor> {
|
||||
use gtk::gdk::CursorType;
|
||||
|
||||
let cursor_type = match self {
|
||||
HitTestResult::Left => CursorType::LeftSide,
|
||||
HitTestResult::Right => CursorType::RightSide,
|
||||
HitTestResult::Top => CursorType::TopSide,
|
||||
HitTestResult::Bottom => CursorType::BottomSide,
|
||||
HitTestResult::TopLeft => CursorType::TopLeftCorner,
|
||||
HitTestResult::TopRight => CursorType::TopRightCorner,
|
||||
HitTestResult::BottomLeft => CursorType::BottomLeftCorner,
|
||||
HitTestResult::BottomRight => CursorType::BottomRightCorner,
|
||||
HitTestResult::Client | HitTestResult::NoWhere => return None,
|
||||
};
|
||||
|
||||
gtk::gdk::Cursor::for_display(display, cursor_type)
|
||||
}
|
||||
}
|
||||
|
||||
fn resizable_window(webview: &webkit2gtk::WebView) -> Option<gtk::Window> {
|
||||
let window = webview.parent()?.parent()?.downcast::<gtk::Window>().ok()?;
|
||||
(!window.is_decorated() && window.is_resizable() && !window.is_maximized()).then_some(window)
|
||||
}
|
||||
|
||||
fn hit_test_window(window: >k::gdk::Window, x: f64, y: f64) -> HitTestResult {
|
||||
let border = window.scale_factor() * BORDERLESS_RESIZE_INSET;
|
||||
hit_test(
|
||||
0.0,
|
||||
0.0,
|
||||
window.width() as f64,
|
||||
window.height() as f64,
|
||||
x,
|
||||
y,
|
||||
border as _,
|
||||
border as _,
|
||||
)
|
||||
}
|
||||
|
||||
fn hit_test_window_root(window: >k::gdk::Window, root_x: f64, root_y: f64) -> HitTestResult {
|
||||
let (window_x, window_y) = window.position();
|
||||
hit_test_window(window, root_x - window_x as f64, root_y - window_y as f64)
|
||||
}
|
||||
|
||||
pub fn attach_resize_handler(webview: &wry::WebView) {
|
||||
use gtk::{gdk::WindowEdge, glib::Propagation};
|
||||
use wry::WebViewExtUnix;
|
||||
|
||||
let webview = webview.webview();
|
||||
|
||||
webview.add_events(
|
||||
gtk::gdk::EventMask::POINTER_MOTION_MASK
|
||||
| gtk::gdk::EventMask::BUTTON1_MOTION_MASK
|
||||
| gtk::gdk::EventMask::BUTTON_PRESS_MASK
|
||||
| gtk::gdk::EventMask::TOUCH_MASK,
|
||||
);
|
||||
|
||||
webview.connect_motion_notify_event(
|
||||
move |webview: &webkit2gtk::WebView, event: >k::gdk::EventMotion| {
|
||||
let Some(window) = resizable_window(webview).and_then(|_| webview.window()) else {
|
||||
return Propagation::Proceed;
|
||||
};
|
||||
|
||||
let (x, y) = event.position();
|
||||
let result = hit_test_window(&window, x, y);
|
||||
if matches!(result, HitTestResult::Client | HitTestResult::NoWhere) {
|
||||
return Propagation::Proceed;
|
||||
}
|
||||
|
||||
let cursor = result.to_cursor(&window.display());
|
||||
window.set_cursor(cursor.as_ref());
|
||||
Propagation::Stop
|
||||
},
|
||||
);
|
||||
|
||||
webview.connect_button_press_event(
|
||||
move |webview: &webkit2gtk::WebView, event: >k::gdk::EventButton| {
|
||||
if event.button() != 1 {
|
||||
return Propagation::Proceed;
|
||||
}
|
||||
let Some(window) = resizable_window(webview).and_then(|w| w.window()) else {
|
||||
return Propagation::Proceed;
|
||||
};
|
||||
|
||||
let (root_x, root_y) = event.root();
|
||||
let edge = hit_test_window_root(&window, root_x, root_y).to_gtk_edge();
|
||||
if matches!(&edge, WindowEdge::__Unknown(_)) {
|
||||
return Propagation::Proceed;
|
||||
}
|
||||
|
||||
window.begin_resize_drag(edge, 1, root_x as i32, root_y as i32, event.time());
|
||||
// Prevent the webview from handling an event claimed by the resize inset
|
||||
Propagation::Stop
|
||||
},
|
||||
);
|
||||
|
||||
webview.connect_touch_event(
|
||||
move |webview: &webkit2gtk::WebView, event: >k::gdk::Event| {
|
||||
let Some(window) = resizable_window(webview).and_then(|w| w.window()) else {
|
||||
return Propagation::Proceed;
|
||||
};
|
||||
let Some((root_x, root_y)) = event.root_coords() else {
|
||||
return Propagation::Proceed;
|
||||
};
|
||||
let Some(device) = event.device() else {
|
||||
return Propagation::Proceed;
|
||||
};
|
||||
|
||||
let edge = hit_test_window_root(&window, root_x, root_y).to_gtk_edge();
|
||||
if matches!(&edge, WindowEdge::__Unknown(_)) {
|
||||
return Propagation::Proceed;
|
||||
}
|
||||
|
||||
window.begin_resize_drag_for_device(
|
||||
edge,
|
||||
&device,
|
||||
0,
|
||||
root_x as i32,
|
||||
root_y as i32,
|
||||
event.time(),
|
||||
);
|
||||
Propagation::Proceed
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
124
crates/tauri-runtime-wry/src/util.rs
Normal file
124
crates/tauri-runtime-wry/src/util.rs
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#[cfg_attr(not(windows), allow(unused_imports))]
|
||||
pub use imp::*;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
mod imp {}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod imp {
|
||||
use std::{iter::once, os::windows::ffi::OsStrExt};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use windows::{
|
||||
Win32::{
|
||||
Foundation::*,
|
||||
Graphics::Gdi::*,
|
||||
System::LibraryLoader::{GetProcAddress, LoadLibraryW},
|
||||
UI::{HiDpi::*, WindowsAndMessaging::*},
|
||||
},
|
||||
core::{HRESULT, PCSTR, PCWSTR},
|
||||
};
|
||||
|
||||
pub fn encode_wide(string: impl AsRef<std::ffi::OsStr>) -> Vec<u16> {
|
||||
string.as_ref().encode_wide().chain(once(0)).collect()
|
||||
}
|
||||
|
||||
// Helper function to dynamically load function pointer.
|
||||
// `library` and `function` must be zero-terminated.
|
||||
pub(super) fn get_function_impl(library: &str, function: &str) -> FARPROC {
|
||||
let library = encode_wide(library);
|
||||
assert_eq!(function.chars().last(), Some('\0'));
|
||||
|
||||
// Library names we will use are ASCII so we can use the A version to avoid string conversion.
|
||||
let module = unsafe { LoadLibraryW(PCWSTR::from_raw(library.as_ptr())) }.unwrap_or_default();
|
||||
if module.is_invalid() {
|
||||
return None;
|
||||
}
|
||||
|
||||
unsafe { GetProcAddress(module, PCSTR::from_raw(function.as_ptr())) }
|
||||
}
|
||||
|
||||
macro_rules! get_function {
|
||||
($lib:expr, $func:ident) => {
|
||||
$crate::util::get_function_impl($lib, concat!(stringify!($func), '\0'))
|
||||
.map(|f| unsafe { std::mem::transmute::<_, $func>(f) })
|
||||
};
|
||||
}
|
||||
|
||||
type GetDpiForWindow = unsafe extern "system" fn(hwnd: HWND) -> u32;
|
||||
type GetDpiForMonitor = unsafe extern "system" fn(
|
||||
hmonitor: HMONITOR,
|
||||
dpi_type: MONITOR_DPI_TYPE,
|
||||
dpi_x: *mut u32,
|
||||
dpi_y: *mut u32,
|
||||
) -> HRESULT;
|
||||
type GetSystemMetricsForDpi =
|
||||
unsafe extern "system" fn(nindex: SYSTEM_METRICS_INDEX, dpi: u32) -> i32;
|
||||
|
||||
static GET_DPI_FOR_WINDOW: Lazy<Option<GetDpiForWindow>> =
|
||||
Lazy::new(|| get_function!("user32.dll", GetDpiForWindow));
|
||||
static GET_DPI_FOR_MONITOR: Lazy<Option<GetDpiForMonitor>> =
|
||||
Lazy::new(|| get_function!("shcore.dll", GetDpiForMonitor));
|
||||
static GET_SYSTEM_METRICS_FOR_DPI: Lazy<Option<GetSystemMetricsForDpi>> =
|
||||
Lazy::new(|| get_function!("user32.dll", GetSystemMetricsForDpi));
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn hwnd_dpi(hwnd: HWND) -> u32 {
|
||||
unsafe {
|
||||
if let Some(GetDpiForWindow) = *GET_DPI_FOR_WINDOW {
|
||||
// We are on Windows 10 Anniversary Update (1607) or later.
|
||||
match GetDpiForWindow(hwnd) {
|
||||
0 => USER_DEFAULT_SCREEN_DPI, // 0 is returned if hwnd is invalid
|
||||
dpi => dpi,
|
||||
}
|
||||
} else if let Some(GetDpiForMonitor) = *GET_DPI_FOR_MONITOR {
|
||||
// We are on Windows 8.1 or later.
|
||||
let monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||
if monitor.is_invalid() {
|
||||
return USER_DEFAULT_SCREEN_DPI;
|
||||
}
|
||||
|
||||
let mut dpi_x = 0;
|
||||
let mut dpi_y = 0;
|
||||
if GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y).is_ok() {
|
||||
dpi_x
|
||||
} else {
|
||||
USER_DEFAULT_SCREEN_DPI
|
||||
}
|
||||
} else {
|
||||
// We are on Vista or later.
|
||||
if IsProcessDPIAware().as_bool() {
|
||||
let hdc = GetDC(Some(hwnd));
|
||||
if hdc.is_invalid() {
|
||||
return USER_DEFAULT_SCREEN_DPI;
|
||||
}
|
||||
// If the process is DPI aware, then scaling must be handled by the application using
|
||||
// this DPI value.
|
||||
let dpi = GetDeviceCaps(Some(hdc), LOGPIXELSX) as u32;
|
||||
ReleaseDC(Some(hwnd), hdc);
|
||||
dpi
|
||||
} else {
|
||||
// If the process is DPI unaware, then scaling is performed by the OS; we thus return
|
||||
// 96 (scale factor 1.0) to prevent the window from being re-scaled by both the
|
||||
// application and the WM.
|
||||
USER_DEFAULT_SCREEN_DPI
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn get_system_metrics_for_dpi(nindex: SYSTEM_METRICS_INDEX, dpi: u32) -> i32 {
|
||||
unsafe {
|
||||
if let Some(GetSystemMetricsForDpi) = *GET_SYSTEM_METRICS_FOR_DPI {
|
||||
GetSystemMetricsForDpi(nindex, dpi)
|
||||
} else {
|
||||
GetSystemMetrics(nindex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
49
crates/tauri-runtime-wry/src/webview.rs
Normal file
49
crates/tauri-runtime-wry/src/webview.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
mod imp {
|
||||
pub type Webview = webkit2gtk::WebView;
|
||||
}
|
||||
|
||||
#[cfg(target_vendor = "apple")]
|
||||
mod imp {
|
||||
use std::ffi::c_void;
|
||||
|
||||
// These pointers are borrowed from ObjC `Retained` handles owned elsewhere and must
|
||||
// not be mutated through. TODO: change these to `*const c_void` in v3 (breaking change).
|
||||
pub struct Webview {
|
||||
pub webview: *mut c_void,
|
||||
pub manager: *mut c_void,
|
||||
#[cfg(target_os = "macos")]
|
||||
pub ns_window: *mut c_void,
|
||||
#[cfg(target_os = "ios")]
|
||||
pub view_controller: *mut c_void,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod imp {
|
||||
use webview2_com::Microsoft::Web::WebView2::Win32::{
|
||||
ICoreWebView2Controller, ICoreWebView2Environment,
|
||||
};
|
||||
pub struct Webview {
|
||||
pub controller: ICoreWebView2Controller,
|
||||
pub environment: ICoreWebView2Environment,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
mod imp {
|
||||
use wry::JniHandle;
|
||||
pub type Webview = JniHandle;
|
||||
}
|
||||
|
||||
pub use imp::*;
|
||||
35
crates/tauri-runtime-wry/src/webview_permissions.rs
Normal file
35
crates/tauri-runtime-wry/src/webview_permissions.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use tauri_runtime::webview::{PermissionKind, PermissionResponse};
|
||||
|
||||
pub(super) fn from_wry_permission_kind(kind: wry::PermissionKind) -> PermissionKind {
|
||||
match kind {
|
||||
wry::PermissionKind::Microphone => PermissionKind::Microphone,
|
||||
wry::PermissionKind::Camera => PermissionKind::Camera,
|
||||
wry::PermissionKind::Geolocation => PermissionKind::Geolocation,
|
||||
wry::PermissionKind::Notifications => PermissionKind::Notifications,
|
||||
wry::PermissionKind::ClipboardRead => PermissionKind::ClipboardRead,
|
||||
wry::PermissionKind::DisplayCapture => PermissionKind::DisplayCapture,
|
||||
wry::PermissionKind::Midi => PermissionKind::Midi,
|
||||
wry::PermissionKind::Sensors => PermissionKind::Sensors,
|
||||
wry::PermissionKind::MediaKeySystemAccess => PermissionKind::MediaKeySystemAccess,
|
||||
wry::PermissionKind::LocalFonts => PermissionKind::LocalFonts,
|
||||
wry::PermissionKind::WindowManagement => PermissionKind::WindowManagement,
|
||||
wry::PermissionKind::PointerLock => PermissionKind::PointerLock,
|
||||
wry::PermissionKind::AutomaticDownloads => PermissionKind::AutomaticDownloads,
|
||||
wry::PermissionKind::FileSystemAccess => PermissionKind::FileSystemAccess,
|
||||
wry::PermissionKind::Autoplay => PermissionKind::Autoplay,
|
||||
wry::PermissionKind::Other => PermissionKind::Other,
|
||||
_ => PermissionKind::Other,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn to_wry_permission_response(response: PermissionResponse) -> wry::PermissionResponse {
|
||||
match response {
|
||||
PermissionResponse::Allow => wry::PermissionResponse::Allow,
|
||||
PermissionResponse::Deny => wry::PermissionResponse::Deny,
|
||||
PermissionResponse::Default => wry::PermissionResponse::Default,
|
||||
}
|
||||
}
|
||||
31
crates/tauri-runtime-wry/src/window/linux.rs
Normal file
31
crates/tauri-runtime-wry/src/window/linux.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use gtk::prelude::*;
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
use tao::platform::unix::WindowExtUnix;
|
||||
|
||||
impl super::WindowExt for tao::window::Window {
|
||||
fn set_enabled(&self, enabled: bool) {
|
||||
self.gtk_window().set_sensitive(enabled);
|
||||
}
|
||||
|
||||
fn is_enabled(&self) -> bool {
|
||||
self.gtk_window().is_sensitive()
|
||||
}
|
||||
|
||||
fn center(&self) {
|
||||
if let Some(monitor) = self.current_monitor() {
|
||||
let window_size = self.outer_size();
|
||||
let new_pos = super::calculate_window_center_position(window_size, monitor);
|
||||
self.set_outer_position(new_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
43
crates/tauri-runtime-wry/src/window/macos.rs
Normal file
43
crates/tauri-runtime-wry/src/window/macos.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use objc2::MainThreadMarker;
|
||||
use objc2_app_kit::{NSBackingStoreType, NSWindow, NSWindowStyleMask};
|
||||
use tao::platform::macos::WindowExtMacOS;
|
||||
|
||||
impl super::WindowExt for tao::window::Window {
|
||||
// based on electron implementation
|
||||
// https://github.com/electron/electron/blob/15db63e26df3e3d59ce6281f030624f746518511/shell/browser/native_window_mac.mm#L474
|
||||
fn set_enabled(&self, enabled: bool) {
|
||||
let ns_window: &NSWindow = unsafe { &*self.ns_window().cast() };
|
||||
if !enabled {
|
||||
let frame = ns_window.frame();
|
||||
let mtm = MainThreadMarker::new()
|
||||
.expect("`Window::set_enabled` can only be called on the main thread");
|
||||
let sheet = unsafe {
|
||||
NSWindow::initWithContentRect_styleMask_backing_defer(
|
||||
mtm.alloc(),
|
||||
frame,
|
||||
NSWindowStyleMask::Titled,
|
||||
NSBackingStoreType::Buffered,
|
||||
false,
|
||||
)
|
||||
};
|
||||
sheet.setAlphaValue(0.5);
|
||||
ns_window.beginSheet_completionHandler(&sheet, None);
|
||||
} else if let Some(attached) = ns_window.attachedSheet() {
|
||||
ns_window.endSheet(&attached);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_enabled(&self) -> bool {
|
||||
let ns_window: &NSWindow = unsafe { &*self.ns_window().cast() };
|
||||
ns_window.attachedSheet().is_none()
|
||||
}
|
||||
|
||||
fn center(&self) {
|
||||
let ns_window: &NSWindow = unsafe { &*self.ns_window().cast() };
|
||||
ns_window.center();
|
||||
}
|
||||
}
|
||||
74
crates/tauri-runtime-wry/src/window/mod.rs
Normal file
74
crates/tauri-runtime-wry/src/window/mod.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
mod linux;
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
#[cfg(windows)]
|
||||
mod windows;
|
||||
|
||||
#[cfg(desktop)]
|
||||
use crate::monitor::MonitorExt;
|
||||
|
||||
pub trait WindowExt {
|
||||
/// Enable or disable the window
|
||||
///
|
||||
/// ## Platform-specific:
|
||||
///
|
||||
/// - **Android / iOS**: Unsupported.
|
||||
fn set_enabled(&self, enabled: bool);
|
||||
|
||||
/// Whether the window is enabled or disabled.
|
||||
///
|
||||
/// ## Platform-specific:
|
||||
///
|
||||
/// - **Android / iOS**: Unsupported, always returns `true`.
|
||||
fn is_enabled(&self) -> bool;
|
||||
|
||||
/// Center the window
|
||||
///
|
||||
/// ## Platform-specific:
|
||||
///
|
||||
/// - **Android / iOS**: Unsupported.
|
||||
fn center(&self) {}
|
||||
|
||||
/// Clears the window surface. i.e make it transparent.
|
||||
#[cfg(windows)]
|
||||
fn draw_surface(
|
||||
&self,
|
||||
surface: &mut softbuffer::Surface<
|
||||
std::sync::Arc<tao::window::Window>,
|
||||
std::sync::Arc<tao::window::Window>,
|
||||
>,
|
||||
background_color: Option<tao::window::RGBA>,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
impl WindowExt for tao::window::Window {
|
||||
fn set_enabled(&self, _: bool) {}
|
||||
fn is_enabled(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(desktop)]
|
||||
pub fn calculate_window_center_position(
|
||||
window_size: tao::dpi::PhysicalSize<u32>,
|
||||
target_monitor: tao::monitor::MonitorHandle,
|
||||
) -> tao::dpi::PhysicalPosition<i32> {
|
||||
let work_area = target_monitor.work_area();
|
||||
|
||||
tao::dpi::PhysicalPosition::new(
|
||||
(work_area.size.width as i32 - window_size.width as i32) / 2 + work_area.position.x,
|
||||
(work_area.size.height as i32 - window_size.height as i32) / 2 + work_area.position.y,
|
||||
)
|
||||
}
|
||||
68
crates/tauri-runtime-wry/src/window/windows.rs
Normal file
68
crates/tauri-runtime-wry/src/window/windows.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use windows::Win32::{
|
||||
Foundation::{HWND, RECT},
|
||||
Graphics::Dwm::{DWMWA_EXTENDED_FRAME_BOUNDS, DwmGetWindowAttribute},
|
||||
UI::Input::KeyboardAndMouse::{EnableWindow, IsWindowEnabled},
|
||||
};
|
||||
|
||||
use tao::platform::windows::WindowExtWindows;
|
||||
|
||||
impl super::WindowExt for tao::window::Window {
|
||||
fn set_enabled(&self, enabled: bool) {
|
||||
let _ = unsafe { EnableWindow(HWND(self.hwnd() as _), enabled) };
|
||||
}
|
||||
|
||||
fn is_enabled(&self) -> bool {
|
||||
unsafe { IsWindowEnabled(HWND(self.hwnd() as _)) }.as_bool()
|
||||
}
|
||||
|
||||
fn center(&self) {
|
||||
if let Some(monitor) = self.current_monitor() {
|
||||
let mut window_size = self.outer_size();
|
||||
|
||||
if self.is_decorated() {
|
||||
let mut rect = RECT::default();
|
||||
let result = unsafe {
|
||||
DwmGetWindowAttribute(
|
||||
HWND(self.hwnd() as _),
|
||||
DWMWA_EXTENDED_FRAME_BOUNDS,
|
||||
&mut rect as *mut _ as *mut _,
|
||||
std::mem::size_of::<RECT>() as u32,
|
||||
)
|
||||
};
|
||||
if result.is_ok() {
|
||||
window_size.height = (rect.bottom - rect.top) as u32;
|
||||
}
|
||||
}
|
||||
|
||||
let new_pos = super::calculate_window_center_position(window_size, monitor);
|
||||
self.set_outer_position(new_pos);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_surface(
|
||||
&self,
|
||||
surface: &mut softbuffer::Surface<
|
||||
std::sync::Arc<tao::window::Window>,
|
||||
std::sync::Arc<tao::window::Window>,
|
||||
>,
|
||||
background_color: Option<tao::window::RGBA>,
|
||||
) {
|
||||
let size = self.inner_size();
|
||||
if let (Some(width), Some(height)) = (
|
||||
std::num::NonZeroU32::new(size.width),
|
||||
std::num::NonZeroU32::new(size.height),
|
||||
) {
|
||||
surface.resize(width, height).unwrap();
|
||||
let mut buffer = surface.buffer_mut().unwrap();
|
||||
let color = background_color
|
||||
.map(|(r, g, b, _)| (b as u32) | ((g as u32) << 8) | ((r as u32) << 16))
|
||||
.unwrap_or(0);
|
||||
buffer.fill(color);
|
||||
let _ = buffer.present();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user