Compare commits

..

2 Commits

Author SHA1 Message Date
Artúr Kovács
316d8306ab Merge branch 'master' of https://github.com/rust-windowing/winit 2021-05-15 19:17:41 +02:00
Artúr Kovács
3f155167ba Release 0.25.0 2021-05-15 18:52:47 +02:00
84 changed files with 2216 additions and 1721 deletions

View File

@@ -2,8 +2,16 @@ name: CI
on:
pull_request:
paths:
- '**.rs'
- '**.toml'
- '.github/workflows/ci.yml'
push:
branches: [master]
paths:
- '**.rs'
- '**.toml'
- '.github/workflows/ci.yml'
jobs:
Check_Formatting:
@@ -30,19 +38,20 @@ 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,wayland-dlopen" }
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: wayland }
- { 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, }
- { target: wasm32-unknown-unknown, os: windows-latest, features: stdweb, cmd: web }
- { target: wasm32-unknown-unknown, os: windows-latest, features: web-sys, cmd: web }
env:
RUST_BACKTRACE: 1
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-C debuginfo=0 --deny warnings"
RUSTFLAGS: "-C debuginfo=0"
OPTIONS: ${{ matrix.platform.options }}
FEATURES: ${{ format(',{0}', matrix.platform.features ) }}
CMD: ${{ matrix.platform.cmd }}
@@ -68,6 +77,10 @@ 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,40 +1,3 @@
# 0.26.1 (2022-01-05)
- Fix linking to the `ColorSync` framework on macOS 10.7, and in newer Rust versions.
- On Web, implement cursor grabbing through the pointer lock API.
- On X11, add mappings for numpad comma, numpad enter, numlock and pause.
- On macOS, fix Pinyin IME input by reverting a change that intended to improve IME.
- On Windows, fix a crash with transparent windows on Windows 11.
# 0.26.0 (2021-12-01)
- Update `raw-window-handle` to `v0.4`. This is _not_ a breaking change, we still implement `HasRawWindowHandle` from `v0.3`, see [rust-windowing/raw-window-handle#74](https://github.com/rust-windowing/raw-window-handle/pull/74). Note that you might have to run `cargo update -p raw-window-handle` after upgrading.
- On X11, bump `mio` to 0.8.
- On Android, fixed `WindowExtAndroid::config` initially returning an empty `Configuration`.
- On Android, fixed `Window::scale_factor` and `MonitorHandle::scale_factor` initially always returning 1.0.
- On X11, select an appropriate visual for transparency if is requested
- On Wayland and X11, fix diagonal window resize cursor orientation.
- On macOS, drop the event callback before exiting.
- On Android, implement `Window::request_redraw`
- **Breaking:** On Web, remove the `stdweb` backend.
- Added `Window::focus_window`to bring the window to the front and set input focus.
- On Wayland and X11, implement `is_maximized` method on `Window`.
- On Windows, prevent ghost window from showing up in the taskbar after either several hours of use or restarting `explorer.exe`.
- On macOS, fix issue where `ReceivedCharacter` was not being emitted during some key repeat events.
- On Wayland, load cursor icons `hand2` and `hand1` for `CursorIcon::Hand`.
- **Breaking:** On Wayland, Theme trait and its support types are dropped.
- On Wayland, bump `smithay-client-toolkit` to 0.15.1.
- On Wayland, implement `request_user_attention` with `xdg_activation_v1`.
- On X11, emit missing `WindowEvent::ScaleFactorChanged` when the only monitor gets reconnected.
- On X11, if RANDR based scale factor is higher than 20 reset it to 1
- On Wayland, add an enabled-by-default feature called `wayland-dlopen` so users can opt out of using `dlopen` to load system libraries.
- **Breaking:** On Android, bump `ndk` and `ndk-glue` to 0.5.
- On Windows, increase wait timer resolution for more accurate timing when using `WaitUntil`.
- On macOS, fix native file dialogs hanging the event loop.
- On Wayland, implement a workaround for wrong configure size when using `xdg_decoration` in `kwin_wayland`
- On macOS, fix an issue that prevented the menu bar from showing in borderless fullscreen mode.
- On X11, EINTR while polling for events no longer causes a panic. Instead it will be treated as a spurious wakeup.
# 0.25.0 (2021-05-15)
- **Breaking:** On macOS, replace `WindowBuilderExtMacOS::with_activation_policy` with `EventLoopExtMacOS::set_activation_policy`

View File

@@ -1,6 +1,6 @@
[package]
name = "winit"
version = "0.26.1"
version = "0.25.0"
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library."
edition = "2018"
@@ -12,22 +12,24 @@ documentation = "https://docs.rs/winit"
categories = ["gui"]
[package.metadata.docs.rs]
features = ["serde"]
features = ["serde", "web-sys"]
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", "wayland-dlopen"]
x11 = ["x11-dl", "mio", "percent-encoding", "parking_lot"]
wayland = ["wayland-client", "wayland-protocols", "sctk"]
wayland-dlopen = ["sctk/dlopen", "wayland-client/dlopen"]
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"]
[dependencies]
instant = { version = "0.1", features = ["wasm-bindgen"] }
instant = "0.1"
lazy_static = "1"
libc = "0.2.64"
log = "0.4"
serde = { version = "1", optional = true, features = ["serde_derive"] }
raw-window-handle = "0.4.2"
raw-window-handle = "0.3"
bitflags = "1"
mint = { version = "0.5.6", optional = true }
@@ -36,9 +38,9 @@ image = "0.23.12"
simple_logger = "1.9"
[target.'cfg(target_os = "android")'.dependencies]
ndk = "0.5"
ndk = "0.3"
ndk-sys = "0.2.0"
ndk-glue = "0.5"
ndk-glue = "0.3"
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
objc = "0.2.7"
@@ -48,6 +50,7 @@ cocoa = "0.24"
core-foundation = "0.9"
core-graphics = "0.22"
dispatch = "0.2.0"
scopeguard = "1.1"
[target.'cfg(target_os = "macos")'.dependencies.core-video-sys]
version = "0.1.4"
@@ -58,7 +61,7 @@ features = ["display_link"]
parking_lot = "0.11"
[target.'cfg(target_os = "windows")'.dependencies.winapi]
version = "0.3.9"
version = "0.3.6"
features = [
"combaseapi",
"commctrl",
@@ -80,23 +83,21 @@ 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.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 }
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 }
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",
@@ -122,6 +123,13 @@ 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,9 +183,11 @@ Legend:
|Fullscreen |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |
|Fullscreen toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |
|Exclusive fullscreen |✔️ |✔️ |✔️ |**N/A** |❌ |✔️ |**N/A**|
|HiDPI support |✔️ |✔️ |✔️ |✔️ |▢[#721]|✔️ |✔️ |
|HiDPI support |✔️ |✔️ |✔️ |✔️ |▢[#721]|✔️ |✔️ \*1|
|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 |
|---------------- | ----- | ---- | ------- | ----------- | ----- | ------- | -------- |
@@ -197,7 +199,7 @@ Legend:
|----------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|Mouse events |✔️ |▢[#63] |✔️ |✔️ |**N/A**|**N/A**|✔️ |
|Mouse set location |✔️ |✔️ |✔️ |❓ |**N/A**|**N/A**|**N/A**|
|Cursor grab |✔️ |▢[#165] |▢[#242] |✔️ |**N/A**|**N/A**|✔️ |
|Cursor grab |✔️ |▢[#165] |▢[#242] |✔️ |**N/A**|**N/A**| |
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ |
|Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |❌ |

View File

@@ -1,23 +1,14 @@
# 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.
* [@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.
Winit backend, and standardizing HiDPI support across all of them
[@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.26.1"
winit = "0.25.0"
```
## [Documentation](https://docs.rs/winit)
@@ -19,8 +19,9 @@ For features _outside_ the scope of winit, see [Missing features provided by oth
Join us in any of these:
[![Matrix](https://img.shields.io/badge/Matrix-%23rust--windowing%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#rust-windowing:matrix.org)
[![Libera.Chat](https://img.shields.io/badge/libera.chat-%23winit-red.svg)](https://web.libera.chat/#winit)
[![Freenode](https://img.shields.io/badge/freenode.net-%23glutin-red.svg)](http://webchat.freenode.net?channels=%23glutin&uio=MTY9dHJ1ZSYyPXRydWUmND10cnVlJjExPTE4NSYxMj10cnVlJjE1PXRydWU7a)
[![Matrix](https://img.shields.io/badge/Matrix-%23Glutin%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#Glutin:matrix.org)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/tomaka/glutin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Usage
@@ -71,7 +72,10 @@ Winit provides the following features, which can be enabled in your `Cargo.toml`
#### WebAssembly
Winit supports compiling to the `wasm32-unknown-unknown` target with `web-sys`.
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.
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
@@ -91,18 +95,7 @@ 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"
@@ -125,6 +118,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(instant::Instant::now() + WAIT_TIME)
ControlFlow::WaitUntil(time::Instant::now() + WAIT_TIME)
}
}
Mode::Poll => {

View File

@@ -43,6 +43,7 @@ fn main() {
..
} => {
*control_flow = ControlFlow::Exit;
return;
}
_ => (),
}

View File

@@ -15,7 +15,7 @@ fn main() {
let mut num = String::new();
stdin().read_line(&mut num).unwrap();
let num = num.trim().parse().expect("Please enter a number");
let num = num.trim().parse().ok().expect("Please enter a number");
let fullscreen = Some(match num {
1 => Fullscreen::Exclusive(prompt_for_video_mode(&prompt_for_monitor(&event_loop))),
@@ -85,7 +85,7 @@ fn prompt_for_monitor(event_loop: &EventLoop<()>) -> MonitorHandle {
let mut num = String::new();
stdin().read_line(&mut num).unwrap();
let num = num.trim().parse().expect("Please enter a number");
let num = num.trim().parse().ok().expect("Please enter a number");
let monitor = event_loop
.available_monitors()
.nth(num)
@@ -106,7 +106,7 @@ fn prompt_for_video_mode(monitor: &MonitorHandle) -> VideoMode {
let mut num = String::new();
stdin().read_line(&mut num).unwrap();
let num = num.trim().parse().expect("Please enter a number");
let num = num.trim().parse().ok().expect("Please enter a number");
let video_mode = monitor
.video_modes()
.nth(num)

View File

@@ -34,10 +34,10 @@ fn main() {
// We need to update our chosen video mode if the window
// was moved to an another monitor, so that the window
// appears on this monitor instead when we go fullscreen
let previous_video_mode = video_modes.get(video_mode_id).cloned();
let previous_video_mode = video_modes.iter().cloned().nth(video_mode_id);
video_modes = window.current_monitor().unwrap().video_modes().collect();
video_mode_id = video_mode_id.min(video_modes.len());
let video_mode = video_modes.get(video_mode_id);
let video_mode = video_modes.iter().nth(video_mode_id);
// Different monitors may support different video modes,
// and the index we chose previously may now point to a
@@ -45,7 +45,7 @@ fn main() {
if video_mode != previous_video_mode.as_ref() {
println!(
"Window moved to another monitor, picked video mode: {}",
video_modes.get(video_mode_id).unwrap()
video_modes.iter().nth(video_mode_id).unwrap()
);
}
}
@@ -77,13 +77,16 @@ fn main() {
Right => (video_modes.len() - 1).min(video_mode_id + 1),
_ => unreachable!(),
};
println!("Picking video mode: {}", video_modes[video_mode_id]);
println!(
"Picking video mode: {}",
video_modes.iter().nth(video_mode_id).unwrap()
);
}
F => window.set_fullscreen(match (state, modifiers.alt()) {
(true, false) => Some(Fullscreen::Borderless(None)),
(true, true) => {
Some(Fullscreen::Exclusive(video_modes[video_mode_id].clone()))
}
(true, true) => Some(Fullscreen::Exclusive(
video_modes.iter().nth(video_mode_id).unwrap().clone(),
)),
(false, _) => None,
}),
G => window.set_cursor_grab(state).unwrap(),
@@ -170,7 +173,7 @@ fn main() {
}
}
},
_ => {}
_ => (),
}
})
}

View File

@@ -41,7 +41,7 @@ fn main() {
},
..
} => {
let window = Window::new(event_loop).unwrap();
let window = Window::new(&event_loop).unwrap();
windows.insert(window.id(), window);
}
_ => (),

View File

@@ -1,14 +1,13 @@
#[cfg(not(target_arch = "wasm32"))]
use std::{thread, time};
use simple_logger::SimpleLogger;
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
fn main() {
use std::{thread, time};
use simple_logger::SimpleLogger;
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
@@ -39,8 +38,3 @@ fn main() {
}
});
}
#[cfg(target_arch = "wasm32")]
fn main() {
unimplemented!() // `Window` can't be sent between threads
}

View File

@@ -46,6 +46,7 @@ fn main() {
..
} => {
*control_flow = ControlFlow::Exit;
return;
}
_ => (),
}

View File

@@ -12,7 +12,7 @@ pub fn main() {
.build(&event_loop)
.unwrap();
#[cfg(target_arch = "wasm32")]
#[cfg(feature = "web-sys")]
{
use winit::platform::web::WindowExtWebSys;
@@ -26,12 +26,28 @@ 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(target_arch = "wasm32")]
#[cfg(feature = "web-sys")]
log::debug!("{:?}", event);
#[cfg(feature = "stdweb")]
std_web::console!(log, "%s", format!("{:?}", event));
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
@@ -45,13 +61,13 @@ pub fn main() {
});
}
#[cfg(target_arch = "wasm32")]
#[cfg(feature = "web-sys")]
mod wasm {
use wasm_bindgen::prelude::*;
#[wasm_bindgen(start)]
pub fn run() {
console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
console_log::init_with_level(log::Level::Debug);
super::main();
}

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 can be found by calling [window.scale_factor()].
//! then its scale factor is `1.0`.
//!
//! ## How is the scale factor calculated?
//!
@@ -95,7 +95,6 @@
//!
//! [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/
@@ -211,9 +210,9 @@ impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalPosition<P> {
}
}
impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for (X, X) {
fn from(p: LogicalPosition<P>) -> (X, X) {
(p.x.cast(), p.y.cast())
impl<P: Pixel, X: Pixel> Into<(X, X)> for LogicalPosition<P> {
fn into(self: Self) -> (X, X) {
(self.x.cast(), self.y.cast())
}
}
@@ -223,23 +222,26 @@ impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalPosition<P> {
}
}
impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for [X; 2] {
fn from(p: LogicalPosition<P>) -> [X; 2] {
[p.x.cast(), p.y.cast()]
impl<P: Pixel, X: Pixel> Into<[X; 2]> for LogicalPosition<P> {
fn into(self: Self) -> [X; 2] {
[self.x.cast(), self.y.cast()]
}
}
#[cfg(feature = "mint")]
impl<P: Pixel> From<mint::Point2<P>> for LogicalPosition<P> {
fn from(p: mint::Point2<P>) -> Self {
Self::new(p.x, p.y)
fn from(mint: mint::Point2<P>) -> Self {
Self::new(mint.x, mint.y)
}
}
#[cfg(feature = "mint")]
impl<P: Pixel> From<LogicalPosition<P>> for mint::Point2<P> {
fn from(p: LogicalPosition<P>) -> Self {
mint::Point2 { x: p.x, y: p.y }
fn from(winit: LogicalPosition<P>) -> Self {
mint::Point2 {
x: winit.x,
y: winit.y,
}
}
}
@@ -290,9 +292,9 @@ impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalPosition<P> {
}
}
impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for (X, X) {
fn from(p: PhysicalPosition<P>) -> (X, X) {
(p.x.cast(), p.y.cast())
impl<P: Pixel, X: Pixel> Into<(X, X)> for PhysicalPosition<P> {
fn into(self: Self) -> (X, X) {
(self.x.cast(), self.y.cast())
}
}
@@ -302,23 +304,26 @@ impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalPosition<P> {
}
}
impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for [X; 2] {
fn from(p: PhysicalPosition<P>) -> [X; 2] {
[p.x.cast(), p.y.cast()]
impl<P: Pixel, X: Pixel> Into<[X; 2]> for PhysicalPosition<P> {
fn into(self: Self) -> [X; 2] {
[self.x.cast(), self.y.cast()]
}
}
#[cfg(feature = "mint")]
impl<P: Pixel> From<mint::Point2<P>> for PhysicalPosition<P> {
fn from(p: mint::Point2<P>) -> Self {
Self::new(p.x, p.y)
fn from(mint: mint::Point2<P>) -> Self {
Self::new(mint.x, mint.y)
}
}
#[cfg(feature = "mint")]
impl<P: Pixel> From<PhysicalPosition<P>> for mint::Point2<P> {
fn from(p: PhysicalPosition<P>) -> Self {
mint::Point2 { x: p.x, y: p.y }
fn from(winit: PhysicalPosition<P>) -> Self {
mint::Point2 {
x: winit.x,
y: winit.y,
}
}
}
@@ -369,9 +374,9 @@ impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalSize<P> {
}
}
impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for (X, X) {
fn from(s: LogicalSize<P>) -> (X, X) {
(s.width.cast(), s.height.cast())
impl<P: Pixel, X: Pixel> Into<(X, X)> for LogicalSize<P> {
fn into(self: LogicalSize<P>) -> (X, X) {
(self.width.cast(), self.height.cast())
}
}
@@ -381,25 +386,25 @@ impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalSize<P> {
}
}
impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for [X; 2] {
fn from(s: LogicalSize<P>) -> [X; 2] {
[s.width.cast(), s.height.cast()]
impl<P: Pixel, X: Pixel> Into<[X; 2]> for LogicalSize<P> {
fn into(self: Self) -> [X; 2] {
[self.width.cast(), self.height.cast()]
}
}
#[cfg(feature = "mint")]
impl<P: Pixel> From<mint::Vector2<P>> for LogicalSize<P> {
fn from(v: mint::Vector2<P>) -> Self {
Self::new(v.x, v.y)
fn from(mint: mint::Vector2<P>) -> Self {
Self::new(mint.x, mint.y)
}
}
#[cfg(feature = "mint")]
impl<P: Pixel> From<LogicalSize<P>> for mint::Vector2<P> {
fn from(s: LogicalSize<P>) -> Self {
fn from(winit: LogicalSize<P>) -> Self {
mint::Vector2 {
x: s.width,
y: s.height,
x: winit.width,
y: winit.height,
}
}
}
@@ -448,9 +453,9 @@ impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalSize<P> {
}
}
impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for (X, X) {
fn from(s: PhysicalSize<P>) -> (X, X) {
(s.width.cast(), s.height.cast())
impl<P: Pixel, X: Pixel> Into<(X, X)> for PhysicalSize<P> {
fn into(self: Self) -> (X, X) {
(self.width.cast(), self.height.cast())
}
}
@@ -460,25 +465,25 @@ impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalSize<P> {
}
}
impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for [X; 2] {
fn from(s: PhysicalSize<P>) -> [X; 2] {
[s.width.cast(), s.height.cast()]
impl<P: Pixel, X: Pixel> Into<[X; 2]> for PhysicalSize<P> {
fn into(self: Self) -> [X; 2] {
[self.width.cast(), self.height.cast()]
}
}
#[cfg(feature = "mint")]
impl<P: Pixel> From<mint::Vector2<P>> for PhysicalSize<P> {
fn from(v: mint::Vector2<P>) -> Self {
Self::new(v.x, v.y)
fn from(mint: mint::Vector2<P>) -> Self {
Self::new(mint.x, mint.y)
}
}
#[cfg(feature = "mint")]
impl<P: Pixel> From<PhysicalSize<P>> for mint::Vector2<P> {
fn from(s: PhysicalSize<P>) -> Self {
fn from(winit: PhysicalSize<P>) -> Self {
mint::Vector2 {
x: s.width,
y: s.height,
x: winit.width,
y: winit.height,
}
}
}

View File

@@ -113,7 +113,7 @@ pub enum Event<'a, T: 'static> {
/// Emitted when the event loop is being shut down.
///
/// This is irreversible - if this event is emitted, it is guaranteed to be the last event that
/// This is irreversable - if this event is emitted, it is guaranteed to be the last event that
/// gets emitted. You generally want to treat this as an "do on quit" event.
LoopDestroyed,
}
@@ -131,7 +131,7 @@ impl<T: Clone> Clone for Event<'static, T> {
device_id: *device_id,
event: event.clone(),
},
NewEvents(cause) => NewEvents(*cause),
NewEvents(cause) => NewEvents(cause.clone()),
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),
Moved(pos) => Moved(*pos),
Resized(size) => Resized(size.clone()),
Moved(pos) => Moved(pos.clone()),
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),
ModifiersChanged(modifiers) => ModifiersChanged(modifiers.clone()),
#[allow(deprecated)]
CursorMoved {
device_id,
@@ -437,7 +437,7 @@ impl Clone for WindowEvent<'static> {
value: *value,
},
Touch(touch) => Touch(*touch),
ThemeChanged(theme) => ThemeChanged(*theme),
ThemeChanged(theme) => ThemeChanged(theme.clone()),
ScaleFactorChanged { .. } => {
unreachable!("Static event can't be about scale factor changing")
}
@@ -538,16 +538,12 @@ impl<'a> WindowEvent<'a> {
pub struct DeviceId(pub(crate) platform_impl::DeviceId);
impl 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`.
/// 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`.
///
/// **Passing this into a winit function will result in undefined behavior.**
pub const unsafe fn dummy() -> Self {
pub unsafe fn dummy() -> Self {
DeviceId(platform_impl::DeviceId::dummy())
}
}
@@ -1002,9 +998,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;
// const LSHIFT = 0b010;
// const RSHIFT = 0b001;
const SHIFT = 0b100 << 0;
// const LSHIFT = 0b010 << 0;
// const RSHIFT = 0b001 << 0;
/// 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(rustdoc::broken_intra_doc_links)]
#![deny(broken_intra_doc_links)]
#[allow(unused_imports)]
#[macro_use]
@@ -146,6 +146,8 @@ 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,10 +241,14 @@ pub trait EventLoopWindowTargetExtMacOS {
impl<T> EventLoopWindowTargetExtMacOS for EventLoopWindowTarget<T> {
fn hide_application(&self) {
self.p.hide_application()
let cls = objc::runtime::Class::get("NSApplication").unwrap();
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
unsafe { msg_send![app, hide: 0] }
}
fn hide_other_applications(&self) {
self.p.hide_other_applications()
let cls = objc::runtime::Class::get("NSApplication").unwrap();
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
unsafe { msg_send![app, hideOtherApplications: 0] }
}
}

View File

@@ -237,6 +237,10 @@ 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
@@ -319,6 +323,16 @@ 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
@@ -345,31 +359,9 @@ pub trait WindowBuilderExtUnix {
#[cfg(feature = "x11")]
fn with_gtk_theme_variant(self, variant: String) -> Self;
/// Build window with resize increment hint. Only implemented on X11.
///
/// ```
/// # use winit::dpi::{LogicalSize, PhysicalSize};
/// # use winit::window::WindowBuilder;
/// # use winit::platform::unix::WindowBuilderExtUnix;
/// // Specify the size in logical dimensions like this:
/// WindowBuilder::new().with_resize_increments(LogicalSize::new(400.0, 200.0));
///
/// // Or specify the size in physical dimensions like this:
/// WindowBuilder::new().with_resize_increments(PhysicalSize::new(400, 200));
/// ```
#[cfg(feature = "x11")]
fn with_resize_increments<S: Into<Size>>(self, increments: S) -> Self;
/// Build window with base size hint. Only implemented on X11.
///
/// ```
/// # use winit::dpi::{LogicalSize, PhysicalSize};
/// # use winit::window::WindowBuilder;
/// # use winit::platform::unix::WindowBuilderExtUnix;
/// // Specify the size in logical dimensions like this:
/// WindowBuilder::new().with_base_size(LogicalSize::new(400.0, 200.0));
///
/// // Or specify the size in physical dimensions like this:
/// WindowBuilder::new().with_base_size(PhysicalSize::new(400, 200));
/// ```
#[cfg(feature = "x11")]
fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self;
@@ -462,3 +454,78 @@ 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,14 +1,28 @@
#![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 `WindowExtWebSys` trait
//! to retrieve the canvas from the Window. Alternatively, use the `WindowBuilderExtWebSys` trait
//! to provide your own canvas.
//! 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.
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;
@@ -16,10 +30,26 @@ 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,6 +3,7 @@
use std::os::raw::c_void;
use std::path::Path;
use libc;
use winapi::shared::minwindef::WORD;
use winapi::shared::windef::{HMENU, HWND};
@@ -71,11 +72,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 c_void;
fn hinstance(&self) -> *mut libc::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 c_void;
fn hwnd(&self) -> *mut libc::c_void;
/// Enables or disables mouse and keyboard input to the specified window.
///
@@ -101,18 +102,20 @@ pub trait WindowExtWindows {
impl WindowExtWindows for Window {
#[inline]
fn hinstance(&self) -> *mut c_void {
fn hinstance(&self) -> *mut libc::c_void {
self.window.hinstance() as *mut _
}
#[inline]
fn hwnd(&self) -> *mut c_void {
fn hwnd(&self) -> *mut libc::c_void {
self.window.hwnd() as *mut _
}
#[inline]
fn set_enable(&self, enabled: bool) {
self.window.set_enable(enabled)
unsafe {
winapi::um::winuser::EnableWindow(self.hwnd() as _, enabled as _);
}
}
#[inline]

View File

@@ -12,7 +12,6 @@ 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},
@@ -20,27 +19,13 @@ use std::{
};
lazy_static! {
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,
static ref CONFIG: RwLock<Configuration> = RwLock::new(Configuration::new());
}
enum EventSource {
Callback,
InputQueue,
User,
Internal(InternalEvent),
}
fn poll(poll: Poll) -> Option<EventSource> {
@@ -51,13 +36,7 @@ fn poll(poll: Poll) -> Option<EventSource> {
_ => unreachable!(),
},
Poll::Timeout => None,
Poll::Wake => Some(
INTERNAL_EVENT
.write()
.unwrap()
.take()
.map_or(EventSource::User, EventSource::Internal),
),
Poll::Wake => Some(EventSource::User),
Poll::Callback => unreachable!(),
}
}
@@ -304,9 +283,6 @@ impl<T: 'static> EventLoop<T> {
);
}
}
Some(EventSource::Internal(internal)) => match internal {
InternalEvent::RedrawRequested => redraw = true,
},
None => {}
}
@@ -446,7 +422,7 @@ impl<T: 'static> EventLoopWindowTarget<T> {
pub struct WindowId;
impl WindowId {
pub const fn dummy() -> Self {
pub fn dummy() -> Self {
WindowId
}
}
@@ -455,7 +431,7 @@ impl WindowId {
pub struct DeviceId;
impl DeviceId {
pub const fn dummy() -> Self {
pub fn dummy() -> Self {
DeviceId
}
}
@@ -502,8 +478,7 @@ impl Window {
}
pub fn request_redraw(&self) {
*INTERNAL_EVENT.write().unwrap() = Some(InternalEvent::RedrawRequested);
ForeignLooper::for_thread().unwrap().wake();
// TODO
}
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, error::NotSupportedError> {
@@ -564,8 +539,6 @@ 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) {}
@@ -590,14 +563,15 @@ impl Window {
))
}
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 _ }
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 _ }
} 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.");
};
RawWindowHandle::AndroidNdk(handle)
let mut handle = raw_window_handle::android::AndroidHandle::empty();
handle.a_native_window = a_native_window;
raw_window_handle::RawWindowHandle::Android(handle)
}
pub fn config(&self) -> Configuration {

View File

@@ -91,7 +91,7 @@ pub struct DeviceId {
}
impl DeviceId {
pub const unsafe fn dummy() -> Self {
pub unsafe fn dummy() -> Self {
DeviceId {
uiscreen: std::ptr::null_mut(),
}

View File

@@ -168,9 +168,7 @@ impl Drop for MonitorHandle {
impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// TODO: Do this using the proper fmt API
#[derive(Debug)]
#[allow(dead_code)]
struct MonitorHandle {
name: Option<String>,
size: PhysicalSize<u32>,

View File

@@ -1,4 +1,4 @@
use raw_window_handle::{RawWindowHandle, UiKitHandle};
use raw_window_handle::{ios::IOSHandle, RawWindowHandle};
use std::{
collections::VecDeque,
ops::{Deref, DerefMut},
@@ -271,10 +271,6 @@ 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")
}
@@ -307,11 +303,13 @@ impl Inner {
}
pub fn raw_window_handle(&self) -> RawWindowHandle {
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)
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)
}
}
@@ -609,7 +607,7 @@ pub struct WindowId {
}
impl WindowId {
pub const unsafe fn dummy() -> Self {
pub unsafe fn dummy() -> Self {
WindowId {
window: std::ptr::null_mut(),
}

View File

@@ -118,9 +118,9 @@ impl fmt::Display for OsError {
#[cfg(feature = "x11")]
OsError::XError(ref e) => _f.pad(&e.description),
#[cfg(feature = "x11")]
OsError::XMisc(e) => _f.pad(e),
OsError::XMisc(ref e) => _f.pad(e),
#[cfg(feature = "wayland")]
OsError::WaylandMisc(e) => _f.pad(e),
OsError::WaylandMisc(ref e) => _f.pad(e),
}
}
}
@@ -141,7 +141,7 @@ pub enum WindowId {
}
impl WindowId {
pub const unsafe fn dummy() -> Self {
pub 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 const unsafe fn dummy() -> Self {
pub unsafe fn dummy() -> Self {
#[cfg(feature = "wayland")]
return DeviceId::Wayland(wayland::DeviceId::dummy());
#[cfg(all(not(feature = "wayland"), feature = "x11"))]
@@ -380,7 +380,8 @@ impl Window {
#[inline]
pub fn is_maximized(&self) -> bool {
x11_or_wayland!(match self; Window(w) => w.is_maximized())
// TODO: Not implemented
false
}
#[inline]
@@ -407,9 +408,9 @@ impl Window {
pub fn set_always_on_top(&self, _always_on_top: bool) {
match self {
#[cfg(feature = "x11")]
Window::X(ref w) => w.set_always_on_top(_always_on_top),
&Window::X(ref w) => w.set_always_on_top(_always_on_top),
#[cfg(feature = "wayland")]
Window::Wayland(_) => (),
_ => (),
}
}
@@ -417,9 +418,9 @@ impl Window {
pub fn set_window_icon(&self, _window_icon: Option<Icon>) {
match self {
#[cfg(feature = "x11")]
Window::X(ref w) => w.set_window_icon(_window_icon),
&Window::X(ref w) => w.set_window_icon(_window_icon),
#[cfg(feature = "wayland")]
Window::Wayland(_) => (),
_ => (),
}
}
@@ -429,20 +430,12 @@ impl Window {
}
#[inline]
pub fn focus_window(&self) {
pub fn request_user_attention(&self, _request_type: Option<UserAttentionType>) {
match self {
#[cfg(feature = "x11")]
Window::X(ref w) => w.focus_window(),
&Window::X(ref w) => w.request_user_attention(_request_type),
#[cfg(feature = "wayland")]
Window::Wayland(_) => (),
}
}
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
match self {
#[cfg(feature = "x11")]
Window::X(ref w) => w.request_user_attention(request_type),
#[cfg(feature = "wayland")]
Window::Wayland(ref w) => w.request_user_attention(request_type),
_ => (),
}
}
@@ -455,14 +448,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,
@@ -475,13 +468,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)
@@ -493,23 +486,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,7 +13,6 @@ 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};
@@ -25,27 +24,18 @@ 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();
let xdg_activation = env.get_global::<XdgActivationV1>().is_some();
Self {
cursor_grab,
xdg_activation,
}
Self { cursor_grab }
}
pub fn cursor_grab(&self) -> bool {
self.cursor_grab
}
pub fn xdg_activation(&self) -> bool {
self.xdg_activation
}
}
sctk::environment!(WinitEnv,
@@ -60,7 +50,6 @@ sctk::environment!(WinitEnv,
ZwpRelativePointerManagerV1 => relative_pointer_manager,
ZwpPointerConstraintsV1 => pointer_constraints,
ZwpTextInputManagerV3 => text_input_manager,
XdgActivationV1 => xdg_activation,
],
multis = [
WlSeat => seats,
@@ -89,8 +78,6 @@ pub struct WinitEnv {
text_input_manager: SimpleGlobal<ZwpTextInputManagerV3>,
decoration_manager: SimpleGlobal<ZxdgDecorationManagerV1>,
xdg_activation: SimpleGlobal<XdgActivationV1>,
}
impl WinitEnv {
@@ -122,9 +109,6 @@ impl WinitEnv {
// IME handling.
let text_input_manager = SimpleGlobal::new();
// Surface activation.
let xdg_activation = SimpleGlobal::new();
Self {
seats,
outputs,
@@ -136,7 +120,6 @@ impl WinitEnv {
relative_pointer_manager,
pointer_constraints,
text_input_manager,
xdg_activation,
}
}
}

View File

@@ -1,7 +1,6 @@
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};
@@ -19,7 +18,6 @@ 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;
@@ -36,8 +34,6 @@ pub use state::WinitState;
use sink::EventSink;
type WinitDispatcher = calloop::Dispatcher<'static, WaylandSource, WinitState>;
pub struct EventLoopWindowTarget<T> {
/// Wayland display.
pub display: Display,
@@ -46,7 +42,7 @@ pub struct EventLoopWindowTarget<T> {
pub env: Environment<WinitEnv>,
/// Event loop handle.
pub event_loop_handle: calloop::LoopHandle<'static, WinitState>,
pub event_loop_handle: calloop::LoopHandle<WinitState>,
/// Output manager.
pub output_manager: OutputManager,
@@ -54,8 +50,8 @@ pub struct EventLoopWindowTarget<T> {
/// State that we share across callbacks.
pub state: RefCell<WinitState>,
/// Dispatcher of Wayland events.
pub wayland_dispatcher: WinitDispatcher,
/// Wayland source.
pub wayland_source: Rc<calloop::Source<WaylandSource>>,
/// A proxy to wake up event loop.
pub event_loop_awakener: calloop::ping::Ping,
@@ -74,7 +70,7 @@ pub struct EventLoopWindowTarget<T> {
pub struct EventLoop<T: 'static> {
/// Event loop.
event_loop: calloop::EventLoop<'static, WinitState>,
event_loop: calloop::EventLoop<WinitState>,
/// Wayland display.
display: Display,
@@ -85,8 +81,8 @@ pub struct EventLoop<T: 'static> {
/// Sender of user events.
user_events_sender: calloop::channel::Sender<T>,
/// Dispatcher of Wayland events.
pub wayland_dispatcher: WinitDispatcher,
/// Wayland source of events.
wayland_source: Rc<calloop::Source<WaylandSource>>,
/// Window target.
window_target: RootEventLoopWindowTarget<T>,
@@ -106,7 +102,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::<'static, WinitState>::try_new()?;
let event_loop = calloop::EventLoop::<WinitState>::new()?;
// Build windowing features.
let windowing_features = WindowingFeatures::new(&env);
@@ -120,22 +116,8 @@ 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);
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())?;
let wayland_source = WaylandSource::new(event_queue).quick_insert(event_loop.handle())?;
let wayland_source = Rc::new(wayland_source);
// A source of user events.
let pending_user_events = Rc::new(RefCell::new(Vec::new()));
@@ -179,7 +161,7 @@ impl<T: 'static> EventLoop<T> {
event_loop_handle,
output_manager,
event_loop_awakener,
wayland_dispatcher: wayland_dispatcher.clone(),
wayland_source: wayland_source.clone(),
windowing_features,
theme_manager,
_marker: std::marker::PhantomData,
@@ -190,11 +172,11 @@ impl<T: 'static> EventLoop<T> {
event_loop,
display,
pending_user_events,
wayland_dispatcher,
wayland_source,
_seat_manager: seat_manager,
user_events_sender,
window_target: RootEventLoopWindowTarget {
p: PlatformEventLoopWindowTarget::Wayland(event_loop_window_target),
p: crate::platform_impl::EventLoopWindowTarget::Wayland(event_loop_window_target),
_marker: std::marker::PhantomData,
},
};
@@ -261,7 +243,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.
@@ -294,7 +276,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.
@@ -305,7 +287,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)
};
@@ -381,7 +363,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();
@@ -421,17 +403,16 @@ 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 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!(),
};
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!())
})
});
if let Ok(dispatched) = queue.dispatch_pending(state, |_, _, _| unimplemented!()) {
if let Ok(dispatched) = dispatched {
dispatched > 0
} else {
break;
@@ -527,7 +508,9 @@ 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 {
PlatformEventLoopWindowTarget::Wayland(window_target) => window_target.state.get_mut(),
crate::platform_impl::EventLoopWindowTarget::Wayland(ref mut window_target) => {
window_target.state.get_mut()
}
#[cfg(feature = "x11")]
_ => unreachable!(),
};
@@ -535,13 +518,18 @@ impl<T: 'static> EventLoop<T> {
f(state)
}
fn loop_dispatch<D: Into<Option<std::time::Duration>>>(&mut self, timeout: D) -> IOResult<()> {
let state = match &mut self.window_target.p {
PlatformEventLoopWindowTarget::Wayland(window_target) => window_target.state.get_mut(),
fn loop_dispatch<D: Into<Option<std::time::Duration>>>(
&mut self,
timeout: D,
) -> std::io::Result<()> {
let mut state = match &mut self.window_target.p {
crate::platform_impl::EventLoopWindowTarget::Wayland(ref mut window_target) => {
window_target.state.get_mut()
}
#[cfg(feature = "x11")]
_ => unreachable!(),
};
self.event_loop.dispatch(timeout, state)
self.event_loop.dispatch(timeout, &mut state)
}
}

View File

@@ -22,7 +22,7 @@ mod window;
pub struct DeviceId;
impl DeviceId {
pub const unsafe fn dummy() -> Self {
pub unsafe fn dummy() -> Self {
DeviceId
}
}
@@ -31,7 +31,7 @@ impl DeviceId {
pub struct WindowId(usize);
impl WindowId {
pub const unsafe fn dummy() -> Self {
pub 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, RegistrationToken};
use sctk::reexports::calloop::{LoopHandle, Source};
use sctk::seat::keyboard;
use sctk::seat::keyboard::{self, RepeatSource};
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_token: Option<RegistrationToken>,
pub repeat_source: Option<Source<RepeatSource>>,
/// LoopHandle to drop `RepeatSource`, when dropping the keyboard.
pub loop_handle: LoopHandle<'static, WinitState>,
pub loop_handle: LoopHandle<WinitState>,
}
impl Keyboard {
pub fn new(
seat: &Attached<WlSeat>,
loop_handle: LoopHandle<'static, WinitState>,
loop_handle: LoopHandle<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_token) = keyboard_data.ok()?;
let (keyboard, repeat_source) = keyboard_data.ok()?;
Some(Self {
keyboard,
loop_handle,
repeat_token: Some(repeat_token),
repeat_source: Some(repeat_source),
})
}
}
@@ -62,8 +62,8 @@ impl Drop for Keyboard {
self.keyboard.release();
}
if let Some(repeat_token) = self.repeat_token.take() {
self.loop_handle.remove(repeat_token);
if let Some(repeat_source) = self.repeat_source.take() {
self.loop_handle.remove(repeat_source);
}
}
}

View File

@@ -37,7 +37,7 @@ pub struct SeatManager {
impl SeatManager {
pub fn new(
env: &Environment<WinitEnv>,
loop_handle: LoopHandle<'static, WinitState>,
loop_handle: LoopHandle<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<'static, WinitState>,
loop_handle: LoopHandle<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<'static, WinitState>,
loop_handle: LoopHandle<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::{FallbackFrame, Window};
use sctk::window::{ConceptFrame, 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 => &["hand2", "hand1"],
CursorIcon::Hand => &["hand"],
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_fdiag"],
CursorIcon::NeswResize => &["fd_double_arrow", "size_bdiag"],
CursorIcon::NwseResize => &["bd_double_arrow", "size_bdiag"],
CursorIcon::NeswResize => &["fd_double_arrow", "size_fdiag"],
CursorIcon::ColResize => &["split_h", "h_double_arrow"],
CursorIcon::RowResize => &["split_v", "v_double_arrow"],
CursorIcon::Text => &["text", "xterm"],
@@ -109,10 +109,9 @@ impl WinitPointer {
let serial = Some(self.latest_serial.get());
for cursor in cursors {
if self.pointer.set_cursor(cursor, serial).is_ok() {
return;
break;
}
}
warn!("Failed to set cursor to {:?}", cursor_icon);
}
/// Confine the pointer to a surface.
@@ -129,8 +128,8 @@ impl WinitPointer {
};
*confined_pointer.borrow_mut() = Some(init_confined_pointer(
pointer_constraints,
surface,
&pointer_constraints,
&surface,
&*self.pointer,
));
}
@@ -150,7 +149,7 @@ impl WinitPointer {
}
}
pub fn drag_window(&self, window: &Window<FallbackFrame>) {
pub fn drag_window(&self, window: &Window<ConceptFrame>) {
window.start_interactive_move(&self.seat, self.latest_serial.get());
}
}
@@ -197,11 +196,12 @@ impl Pointers {
);
// Setup relative_pointer if it's available.
let relative_pointer = relative_pointer_manager
.as_ref()
.map(|relative_pointer_manager| {
init_relative_pointer(relative_pointer_manager, &*pointer)
});
let relative_pointer = match relative_pointer_manager.as_ref() {
Some(relative_pointer_manager) => {
Some(init_relative_pointer(&relative_pointer_manager, &*pointer))
}
None => None,
};
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);
pointer_constraints.confine_pointer(surface, pointer, None, Lifetime::Persistent.to_raw());
confined_pointer.quick_assign(move |_, _, _| {});

View File

@@ -7,17 +7,21 @@ use sctk::reexports::client::Display;
use sctk::reexports::calloop;
use raw_window_handle::WaylandHandle;
use sctk::window::{Decorations, FallbackFrame};
use sctk::window::{
ARGBColor, ButtonColorSpec, ColorSpec, ConceptConfig, ConceptFrame, Decorations,
};
use raw_window_handle::unix::WaylandHandle;
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, UserAttentionType, WindowAttributes};
use crate::window::{CursorIcon, Fullscreen, WindowAttributes};
use super::env::WindowingFeatures;
use super::event_loop::WinitState;
@@ -50,9 +54,6 @@ pub struct Window {
/// Fullscreen state.
fullscreen: Arc<AtomicBool>,
/// Maximized state.
maximized: Arc<AtomicBool>,
/// Available windowing features.
windowing_features: WindowingFeatures,
@@ -86,8 +87,6 @@ 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();
@@ -99,7 +98,7 @@ impl Window {
let theme_manager = event_loop_window_target.theme_manager.clone();
let mut window = event_loop_window_target
.env
.create_window::<FallbackFrame, _>(
.create_window::<ConceptFrame, _>(
surface.clone(),
Some(theme_manager),
(width, height),
@@ -114,8 +113,6 @@ 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);
@@ -139,12 +136,6 @@ 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
@@ -203,12 +194,7 @@ 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(
&event_loop_window_target.env,
window,
size.clone(),
window_requests.clone(),
);
let window_handle = WindowHandle::new(window, size.clone(), window_requests.clone());
let mut winit_state = event_loop_window_target.state.borrow_mut();
@@ -220,14 +206,17 @@ 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.
{
let mut wayland_source = event_loop_window_target.wayland_dispatcher.as_source_mut();
let event_queue = wayland_source.queue();
event_loop_handle.with_source(&wayland_source, |event_queue| {
let event_queue = event_queue.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
@@ -246,7 +235,6 @@ impl Window {
window_requests,
event_loop_awakener: event_loop_window_target.event_loop_awakener.clone(),
fullscreen,
maximized,
windowing_features,
};
@@ -262,7 +250,9 @@ impl Window {
#[inline]
pub fn set_title(&self, title: &str) {
self.send_request(WindowRequest::Title(title.to_owned()));
let title_request = WindowRequest::Title(title.to_owned());
self.window_requests.lock().unwrap().push(title_request);
self.event_loop_awakener.ping();
}
#[inline]
@@ -294,7 +284,9 @@ impl Window {
#[inline]
pub fn request_redraw(&self) {
self.send_request(WindowRequest::Redraw);
let redraw_request = WindowRequest::Redraw;
self.window_requests.lock().unwrap().push(redraw_request);
self.event_loop_awakener.ping();
}
#[inline]
@@ -312,7 +304,12 @@ impl Window {
let size = size.to_logical::<u32>(scale_factor);
*self.size.lock().unwrap() = size;
self.send_request(WindowRequest::FrameSize(size));
let frame_size_request = WindowRequest::FrameSize(size);
self.window_requests
.lock()
.unwrap()
.push(frame_size_request);
self.event_loop_awakener.ping();
}
#[inline]
@@ -320,7 +317,9 @@ impl Window {
let scale_factor = self.scale_factor() as f64;
let size = dimensions.map(|size| size.to_logical::<u32>(scale_factor));
self.send_request(WindowRequest::MinSize(size));
let min_size_request = WindowRequest::MinSize(size);
self.window_requests.lock().unwrap().push(min_size_request);
self.event_loop_awakener.ping();
}
#[inline]
@@ -328,12 +327,19 @@ impl Window {
let scale_factor = self.scale_factor() as f64;
let size = dimensions.map(|size| size.to_logical::<u32>(scale_factor));
self.send_request(WindowRequest::MaxSize(size));
let max_size_request = WindowRequest::MaxSize(size);
self.window_requests.lock().unwrap().push(max_size_request);
self.event_loop_awakener.ping();
}
#[inline]
pub fn set_resizable(&self, resizable: bool) {
self.send_request(WindowRequest::Resizeable(resizable));
let resizeable_request = WindowRequest::Resizeable(resizable);
self.window_requests
.lock()
.unwrap()
.push(resizeable_request);
self.event_loop_awakener.ping();
}
#[inline]
@@ -345,7 +351,9 @@ impl Window {
#[inline]
pub fn set_decorations(&self, decorate: bool) {
self.send_request(WindowRequest::Decorate(decorate));
let decorate_request = WindowRequest::Decorate(decorate);
self.window_requests.lock().unwrap().push(decorate_request);
self.event_loop_awakener.ping();
}
#[inline]
@@ -355,17 +363,16 @@ impl Window {
return;
}
self.send_request(WindowRequest::Minimize);
}
#[inline]
pub fn is_maximized(&self) -> bool {
self.maximized.load(Ordering::Relaxed)
let minimize_request = WindowRequest::Minimize;
self.window_requests.lock().unwrap().push(minimize_request);
self.event_loop_awakener.ping();
}
#[inline]
pub fn set_maximized(&self, maximized: bool) {
self.send_request(WindowRequest::Maximize(maximized));
let maximize_request = WindowRequest::Maximize(maximized);
self.window_requests.lock().unwrap().push(maximize_request);
self.event_loop_awakener.ping();
}
#[inline]
@@ -401,17 +408,154 @@ impl Window {
None => WindowRequest::UnsetFullscreen,
};
self.send_request(fullscreen_request);
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();
}
#[inline]
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
self.send_request(WindowRequest::NewCursorIcon(cursor));
let cursor_icon_request = WindowRequest::NewCursorIcon(cursor);
self.window_requests
.lock()
.unwrap()
.push(cursor_icon_request);
self.event_loop_awakener.ping();
}
#[inline]
pub fn set_cursor_visible(&self, visible: bool) {
self.send_request(WindowRequest::ShowCursor(visible));
let cursor_visible_request = WindowRequest::ShowCursor(visible);
self.window_requests
.lock()
.unwrap()
.push(cursor_visible_request);
self.event_loop_awakener.ping();
}
#[inline]
@@ -420,20 +564,16 @@ impl Window {
return Err(ExternalError::NotSupported(NotSupportedError::new()));
}
self.send_request(WindowRequest::GrabCursor(grab));
let cursor_grab_request = WindowRequest::GrabCursor(grab);
self.window_requests
.lock()
.unwrap()
.push(cursor_grab_request);
self.event_loop_awakener.ping();
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
@@ -448,7 +588,12 @@ impl Window {
#[inline]
pub fn drag_window(&self) -> Result<(), ExternalError> {
self.send_request(WindowRequest::DragWindow);
let drag_window_request = WindowRequest::DragWindow;
self.window_requests
.lock()
.unwrap()
.push(drag_window_request);
self.event_loop_awakener.ping();
Ok(())
}
@@ -457,7 +602,12 @@ 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);
self.send_request(WindowRequest::IMEPosition(position));
let ime_position_request = WindowRequest::IMEPosition(position);
self.window_requests
.lock()
.unwrap()
.push(ime_position_request);
self.event_loop_awakener.ping();
}
#[inline]
@@ -488,21 +638,31 @@ impl Window {
#[inline]
pub fn raw_window_handle(&self) -> WaylandHandle {
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
}
let display = self.display.get_display_ptr() as *mut _;
let surface = self.surface.as_ref().c_ptr() as *mut _;
#[inline]
fn send_request(&self, request: WindowRequest) {
self.window_requests.lock().unwrap().push(request);
self.event_loop_awakener.ping();
WaylandHandle {
display,
surface,
..WaylandHandle::empty()
}
}
}
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 }
}
}
impl Drop for Window {
fn drop(&mut self) {
self.send_request(WindowRequest::Close);
let close_request = WindowRequest::Close;
self.window_requests.lock().unwrap().push(close_request);
self.event_loop_awakener.ping();
}
}

View File

@@ -2,23 +2,17 @@ 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::environment::Environment;
use sctk::window::{Decorations, FallbackFrame, Window};
use sctk::window::{ConceptConfig, ConceptFrame, Decorations, 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, UserAttentionType};
use crate::window::CursorIcon;
/// A request to SCTK window from Winit window.
#[derive(Debug, Clone)]
@@ -70,14 +64,12 @@ 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,
}
@@ -139,7 +131,7 @@ impl WindowUpdate {
/// and react to events.
pub struct WindowHandle {
/// An actual window.
pub window: Window<FallbackFrame>,
pub window: Window<ConceptFrame>,
/// The current size of the window.
pub size: Arc<Mutex<LogicalSize<u32>>>,
@@ -161,23 +153,14 @@ 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(
env: &Environment<WinitEnv>,
window: Window<FallbackFrame>,
window: Window<ConceptFrame>,
size: Arc<Mutex<LogicalSize<u32>>>,
pending_window_requests: Arc<Mutex<Vec<WindowRequest>>>,
) -> Self {
let xdg_activation = env.get_global::<XdgActivationV1>();
Self {
window,
size,
@@ -187,8 +170,6 @@ impl WindowHandle {
cursor_visible: Cell::new(true),
pointers: Vec::new(),
text_inputs: Vec::new(),
xdg_activation,
attention_requested: Cell::new(false),
}
}
@@ -203,55 +184,13 @@ 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);
@@ -259,7 +198,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);
}
@@ -283,7 +222,12 @@ impl WindowHandle {
}
pub fn text_input_entered(&mut self, text_input: TextInputHandler) {
if !self.text_inputs.iter().any(|t| *t == text_input) {
if self
.text_inputs
.iter()
.find(|t| *t == &text_input)
.is_none()
{
self.text_inputs.push(text_input);
}
}
@@ -386,35 +330,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) => {
@@ -422,16 +366,20 @@ 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

@@ -45,7 +45,7 @@ impl<T: 'static> EventProcessor<T> {
let mut devices = self.devices.borrow_mut();
if let Some(info) = DeviceInfo::get(&wt.xconn, device) {
for info in info.iter() {
devices.insert(DeviceId(info.deviceid), Device::new(self, info));
devices.insert(DeviceId(info.deviceid), Device::new(&self, info));
}
}
}
@@ -925,7 +925,7 @@ impl<T: 'static> EventProcessor<T> {
// Issue key press events for all pressed keys
Self::handle_pressed_keys(
wt,
&wt,
window_id,
ElementState::Pressed,
&self.mod_keymap,
@@ -949,7 +949,7 @@ impl<T: 'static> EventProcessor<T> {
// Issue key release events for all pressed keys
Self::handle_pressed_keys(
wt,
&wt,
window_id,
ElementState::Released,
&self.mod_keymap,
@@ -1164,64 +1164,68 @@ 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 {
// 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
prev_list
.iter()
.find(|prev_monitor| prev_monitor.name == new_monitor.name)
.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(),
);
.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(),
);
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,
);
}
}
}
}
}
}
}
});
}
}
}
}
}
if let Ok((window_id, x, y)) = self.ime_receiver.try_recv() {
wt.ime.borrow_mut().send_xim_spot(window_id, x, y);
match self.ime_receiver.try_recv() {
Ok((window_id, x, y)) => {
wt.ime.borrow_mut().send_xim_spot(window_id, x, y);
}
Err(_) => (),
}
}

View File

@@ -9,7 +9,7 @@ pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> {
//ffi::XK_Linefeed => VirtualKeyCode::Linefeed,
//ffi::XK_Clear => VirtualKeyCode::Clear,
ffi::XK_Return => VirtualKeyCode::Return,
ffi::XK_Pause => VirtualKeyCode::Pause,
//ffi::XK_Pause => VirtualKeyCode::Pause,
//ffi::XK_Scroll_Lock => VirtualKeyCode::Scroll_lock,
//ffi::XK_Sys_Req => VirtualKeyCode::Sys_req,
ffi::XK_Escape => VirtualKeyCode::Escape,
@@ -59,10 +59,10 @@ pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> {
//ffi::XK_Break => VirtualKeyCode::Break,
//ffi::XK_Mode_switch => VirtualKeyCode::Mode_switch,
//ffi::XK_script_switch => VirtualKeyCode::Script_switch,
ffi::XK_Num_Lock => VirtualKeyCode::Numlock,
//ffi::XK_Num_Lock => VirtualKeyCode::Num_lock,
//ffi::XK_KP_Space => VirtualKeyCode::Kp_space,
//ffi::XK_KP_Tab => VirtualKeyCode::Kp_tab,
ffi::XK_KP_Enter => VirtualKeyCode::NumpadEnter,
//ffi::XK_KP_Enter => VirtualKeyCode::Kp_enter,
//ffi::XK_KP_F1 => VirtualKeyCode::Kp_f1,
//ffi::XK_KP_F2 => VirtualKeyCode::Kp_f2,
//ffi::XK_KP_F3 => VirtualKeyCode::Kp_f3,
@@ -83,7 +83,7 @@ pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> {
ffi::XK_KP_Equal => VirtualKeyCode::NumpadEquals,
ffi::XK_KP_Multiply => VirtualKeyCode::NumpadMultiply,
ffi::XK_KP_Add => VirtualKeyCode::NumpadAdd,
ffi::XK_KP_Separator => VirtualKeyCode::NumpadComma,
//ffi::XK_KP_Separator => VirtualKeyCode::Kp_separator,
ffi::XK_KP_Subtract => VirtualKeyCode::NumpadSubtract,
ffi::XK_KP_Decimal => VirtualKeyCode::NumpadDecimal,
ffi::XK_KP_Divide => VirtualKeyCode::NumpadDivide,

View File

@@ -1,9 +1,4 @@
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

@@ -62,7 +62,7 @@ pub unsafe fn set_destroy_callback(
inner: &ImeInner,
) -> Result<(), XError> {
xim_set_callback(
xconn,
&xconn,
im,
ffi::XNDestroyCallback_0.as_ptr() as *const _,
&inner.destroy_callback as *const _ as *mut _,
@@ -70,7 +70,6 @@ pub unsafe fn set_destroy_callback(
}
#[derive(Debug)]
#[allow(clippy::enum_variant_names)]
enum ReplaceImError {
MethodOpenFailed(PotentialInputMethods),
ContextCreationFailed(ImeContextCreationError),
@@ -137,17 +136,13 @@ pub unsafe extern "C" fn xim_instantiate_callback(
let inner: *mut ImeInner = client_data as _;
if !inner.is_null() {
let xconn = &(*inner).xconn;
match replace_im(inner) {
Ok(()) => {
let _ = unset_instantiate_callback(xconn, client_data);
(*inner).is_fallback = false;
}
Err(err) => {
if (*inner).is_destroyed {
// We have no usable input methods!
panic!("Failed to reopen input method: {:?}", err);
}
}
let result = replace_im(inner);
if result.is_ok() {
let _ = unset_instantiate_callback(xconn, client_data);
(*inner).is_fallback = false;
} else if result.is_err() && (*inner).is_destroyed {
// We have no usable input methods!
result.expect("Failed to reopen input method");
}
}
}
@@ -168,12 +163,12 @@ pub unsafe extern "C" fn xim_destroy_callback(
if !(*inner).is_fallback {
let _ = set_instantiate_callback(xconn, client_data);
// Attempt to open fallback input method.
match replace_im(inner) {
Ok(()) => (*inner).is_fallback = true,
Err(err) => {
// We have no usable input methods!
panic!("Failed to open fallback input method: {:?}", err);
}
let result = replace_im(inner);
if result.is_ok() {
(*inner).is_fallback = true;
} else {
// We have no usable input methods!
result.expect("Failed to open fallback input method");
}
}
}

View File

@@ -58,7 +58,7 @@ impl ImeContext {
Ok(ImeContext {
ic,
ic_spot: ic_spot.unwrap_or(ffi::XPoint { x: 0, y: 0 }),
ic_spot: ic_spot.unwrap_or_else(|| ffi::XPoint { x: 0, y: 0 }),
})
}

View File

@@ -58,8 +58,10 @@ impl ImeInner {
}
pub unsafe fn destroy_all_contexts_if_necessary(&self) -> Result<bool, XError> {
for context in self.contexts.values().flatten() {
self.destroy_ic_if_necessary(context.ic)?;
for context in self.contexts.values() {
if let &Some(ref context) = context {
self.destroy_ic_if_necessary(context.ic)?;
}
}
Ok(!self.is_destroyed)
}

View File

@@ -42,12 +42,12 @@ unsafe fn open_im(xconn: &Arc<XConnection>, locale_modifiers: &CStr) -> Option<f
#[derive(Debug)]
pub struct InputMethod {
pub im: ffi::XIM,
_name: String,
name: String,
}
impl InputMethod {
fn new(im: ffi::XIM, name: String) -> Self {
InputMethod { im, _name: name }
InputMethod { im, name }
}
}
@@ -63,7 +63,11 @@ pub enum InputMethodResult {
impl InputMethodResult {
pub fn is_fallback(&self) -> bool {
matches!(self, InputMethodResult::Fallback(_))
if let &InputMethodResult::Fallback(_) = self {
true
} else {
false
}
}
pub fn ok(self) -> Option<InputMethod> {
@@ -245,7 +249,7 @@ impl PotentialInputMethods {
pub fn open_im(
&mut self,
xconn: &Arc<XConnection>,
callback: Option<&dyn Fn()>,
callback: Option<&dyn Fn() -> ()>,
) -> InputMethodResult {
use self::InputMethodResult::*;
@@ -255,8 +259,10 @@ impl PotentialInputMethods {
let im = input_method.open_im(xconn);
if let Some(im) = im {
return XModifiers(im);
} else if let Some(ref callback) = callback {
callback();
} else {
if let Some(ref callback) = callback {
callback();
}
}
}

View File

@@ -32,7 +32,7 @@ use std::{
ptr,
rc::Rc,
slice,
sync::mpsc::{Receiver, Sender},
sync::mpsc::Receiver,
sync::{mpsc, Arc, Weak},
time::{Duration, Instant},
};
@@ -41,6 +41,12 @@ 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,
@@ -58,11 +64,6 @@ 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,
@@ -71,30 +72,27 @@ pub struct EventLoopWindowTarget<T> {
root: ffi::Window,
ime: RefCell<Ime>,
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
redraw_sender: WakeSender<WindowId>,
redraw_sender: Sender<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>, //waker.wake needs to be called whenever something gets sent
user_channel: Receiver<T>,
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(),
}
}
}
@@ -187,13 +185,15 @@ 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) = std::sync::mpsc::channel();
let (redraw_sender, redraw_channel) = std::sync::mpsc::channel();
let (user_sender, user_channel) = channel(queue.clone(), NotificationId::gen_next());
let (redraw_sender, redraw_channel) = channel(queue, NotificationId::gen_next());
let target = Rc::new(RootELW {
p: super::EventLoopWindowTarget::X(EventLoopWindowTarget {
@@ -205,10 +205,7 @@ impl<T: 'static> EventLoop<T> {
xconn,
wm_delete_window,
net_wm_ping,
redraw_sender: WakeSender {
sender: redraw_sender, // not used again so no clone
waker: waker.clone(),
},
redraw_sender,
}),
_marker: ::std::marker::PhantomData,
});
@@ -236,21 +233,21 @@ impl<T: 'static> EventLoop<T> {
event_processor.init_device(ffi::XIAllDevices);
EventLoop {
let result = 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(),
}
}
@@ -360,11 +357,7 @@ 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() {
if let Err(e) = self.poll.poll(&mut events, timeout) {
if e.raw_os_error() != Some(libc::EINTR) {
panic!("epoll returned an error: {:?}", e);
}
}
self.poll.poll(&mut events, timeout).unwrap();
events.clear();
}
@@ -399,6 +392,7 @@ 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()) } {
@@ -413,8 +407,7 @@ impl<T: 'static> EventLoop<T> {
super::WindowId::X(wid),
)) = event
{
wt.redraw_sender.sender.send(wid).unwrap();
wt.redraw_sender.waker.wake().unwrap();
wt.redraw_sender.send(wid).unwrap();
} else {
callback(event, window_target, control_flow);
}
@@ -443,10 +436,13 @@ 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(e.0))
.map(|_| self.waker.wake().unwrap())
self.user_sender.send(event).map_err(|e| {
EventLoopClosed(if let SendError::Disconnected(x) = e {
x
} else {
unreachable!()
})
})
}
}
@@ -494,7 +490,7 @@ impl<'a> Deref for DeviceInfo<'a> {
pub struct WindowId(ffi::Window);
impl WindowId {
pub const unsafe fn dummy() -> Self {
pub unsafe fn dummy() -> Self {
WindowId(0)
}
}
@@ -503,7 +499,7 @@ impl WindowId {
pub struct DeviceId(c_int);
impl DeviceId {
pub const unsafe fn dummy() -> Self {
pub unsafe fn dummy() -> Self {
DeviceId(0)
}
}
@@ -524,7 +520,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()
@@ -592,7 +588,7 @@ fn mkdid(w: c_int) -> crate::event::DeviceId {
#[derive(Debug)]
struct Device {
_name: String,
name: String,
scroll_axes: Vec<(i32, ScrollAxis)>,
// For master devices, this is the paired device (pointer <-> keyboard).
// For slave devices, this is the master.
@@ -634,28 +630,31 @@ impl Device {
// Identify scroll axes
for class_ptr in Device::classes(info) {
let class = unsafe { &**class_ptr };
if class._type == ffi::XIScrollClass {
let info = unsafe {
mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIScrollClassInfo>(class)
};
scroll_axes.push((
info.number,
ScrollAxis {
increment: info.increment,
orientation: match info.scroll_type {
ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal,
ffi::XIScrollTypeVertical => ScrollOrientation::Vertical,
_ => unreachable!(),
match class._type {
ffi::XIScrollClass => {
let info = unsafe {
mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIScrollClassInfo>(class)
};
scroll_axes.push((
info.number,
ScrollAxis {
increment: info.increment,
orientation: match info.scroll_type {
ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal,
ffi::XIScrollTypeVertical => ScrollOrientation::Vertical,
_ => unreachable!(),
},
position: 0.0,
},
position: 0.0,
},
));
));
}
_ => {}
}
}
}
let mut device = Device {
_name: name.into_owned(),
name: name.into_owned(),
scroll_axes,
attachment: info.attachment,
};
@@ -667,17 +666,20 @@ impl Device {
if Device::physical_device(info) {
for class_ptr in Device::classes(info) {
let class = unsafe { &**class_ptr };
if class._type == ffi::XIValuatorClass {
let info = unsafe {
mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIValuatorClassInfo>(class)
};
if let Some(&mut (_, ref mut axis)) = self
.scroll_axes
.iter_mut()
.find(|&&mut (axis, _)| axis == info.number)
{
axis.position = info.value;
match class._type {
ffi::XIValuatorClass => {
let info = unsafe {
mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIValuatorClassInfo>(class)
};
if let Some(&mut (_, ref mut axis)) = self
.scroll_axes
.iter_mut()
.find(|&&mut (axis, _)| axis == info.number)
{
axis.position = info.value;
}
}
_ => {}
}
}
}

View File

@@ -90,7 +90,7 @@ impl Eq for MonitorHandle {}
impl PartialOrd for MonitorHandle {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
Some(self.cmp(&other))
}
}
@@ -204,7 +204,7 @@ impl XConnection {
let overlapping_area = window_rect.get_overlapping_area(&monitor.rect);
if overlapping_area > largest_overlap {
largest_overlap = overlapping_area;
matched_monitor = monitor;
matched_monitor = &monitor;
}
}
@@ -242,11 +242,8 @@ impl XConnection {
if is_active {
let is_primary = *(*crtc).outputs.offset(0) == primary;
has_primary |= is_primary;
if let Some(monitor_id) =
MonitorHandle::new(self, resources, crtc_id, crtc, is_primary)
{
available.push(monitor_id)
}
MonitorHandle::new(self, resources, crtc_id, crtc, is_primary)
.map(|monitor_id| available.push(monitor_id));
}
(self.xrandr.XRRFreeCrtcInfo)(crtc);
}

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_fdiag\0"]),
CursorIcon::NeswResize => loadn(&[b"fd_double_arrow\0", b"size_bdiag\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::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

@@ -23,9 +23,9 @@ impl Format {
pub fn get_actual_size(&self) -> usize {
match self {
Format::Char => mem::size_of::<c_char>(),
Format::Short => mem::size_of::<c_short>(),
Format::Long => mem::size_of::<c_long>(),
&Format::Char => mem::size_of::<c_char>(),
&Format::Short => mem::size_of::<c_short>(),
&Format::Long => mem::size_of::<c_long>(),
}
}
}

View File

@@ -248,7 +248,7 @@ impl XConnection {
);
// The list of children isn't used
if !children.is_null() {
if children != ptr::null_mut() {
(self.xlib.XFree)(children as *mut _);
}

View File

@@ -27,7 +27,7 @@ impl Icon {
data.push(rgba_icon.height as Cardinal);
let pixels = rgba_icon.rgba.as_ptr() as *const Pixel;
for pixel_index in 0..pixel_count {
let pixel = unsafe { &*pixels.add(pixel_index) };
let pixel = unsafe { &*pixels.offset(pixel_index as isize) };
data.push(pixel.to_packed_argb());
}
data

View File

@@ -36,7 +36,7 @@ impl Iterator for KeymapIter<'_> {
fn next(&mut self) -> Option<ffi::KeyCode> {
if self.item.is_none() {
for (index, &item) in self.iter.by_ref() {
while let Some((index, &item)) = self.iter.next() {
if item != 0 {
self.index = index;
self.item = Some(item);

View File

@@ -151,7 +151,7 @@ impl ModifierKeyState {
pub fn key_release(&mut self, keycode: ffi::KeyCode) {
if let Some(modifier) = self.keys.remove(&keycode) {
if !self.keys.values().any(|&m| m == modifier) {
if self.keys.values().find(|&&m| m == modifier).is_none() {
set_modifier(&mut self.state, modifier, false);
}
}

View File

@@ -27,11 +27,7 @@ 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));
if dpi_factor <= 20. {
dpi_factor
} else {
1.
}
dpi_factor
}
impl XConnection {
@@ -39,15 +35,15 @@ impl XConnection {
pub unsafe fn get_xft_dpi(&self) -> Option<f64> {
(self.xlib.XrmInitialize)();
let resource_manager_str = (self.xlib.XResourceManagerString)(self.display);
if resource_manager_str.is_null() {
if resource_manager_str == ptr::null_mut() {
return None;
}
if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() {
let name: &str = "Xft.dpi:\t";
for pair in res.split('\n') {
for pair in res.split("\n") {
if pair.starts_with(&name) {
let res = &pair[name.len()..];
return f64::from_str(res).ok();
return f64::from_str(&res).ok();
}
}
}

View File

@@ -97,7 +97,7 @@ impl XConnection {
quantity_returned,
new_data,
);*/
data.extend_from_slice(new_data);
data.extend_from_slice(&new_data);
// Fun fact: XGetWindowProperty allocates one extra byte at the end.
(self.xlib.XFree)(buf as _); // Don't try to access new_data after this.
} else {

View File

@@ -62,7 +62,11 @@ impl XConnection {
let wm_check = result.ok().and_then(|wm_check| wm_check.get(0).cloned());
wm_check?
if let Some(wm_check) = wm_check {
wm_check
} else {
return None;
}
};
// Querying the same property on the child window we were given, we should get this child
@@ -72,7 +76,11 @@ impl XConnection {
let wm_check = result.ok().and_then(|wm_check| wm_check.get(0).cloned());
wm_check?
if let Some(wm_check) = wm_check {
wm_check
} else {
return None;
}
};
// These values should be the same.

View File

@@ -1,4 +1,4 @@
use raw_window_handle::XlibHandle;
use raw_window_handle::unix::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,9 +25,7 @@ use crate::{
window::{CursorIcon, Fullscreen, Icon, UserAttentionType, WindowAttributes},
};
use super::{
ffi, util, EventLoopWindowTarget, ImeSender, WakeSender, WindowId, XConnection, XError,
};
use super::{ffi, util, EventLoopWindowTarget, ImeSender, WindowId, XConnection, XError};
#[derive(Debug)]
pub struct SharedState {
@@ -105,7 +103,7 @@ pub struct UnownedWindow {
cursor_visible: Mutex<bool>,
ime_sender: Mutex<ImeSender>,
pub shared_state: Mutex<SharedState>,
redraw_sender: WakeSender<WindowId>,
redraw_sender: Sender<WindowId>,
}
impl UnownedWindow {
@@ -150,7 +148,7 @@ impl UnownedWindow {
let position = window_attrs
.position
.map(|position| position.to_physical::<i32>(scale_factor));
.map(|position| position.to_physical::<i32>(scale_factor).into());
let dimensions = {
// x11 only applies constraints when the window is actively resized
@@ -182,39 +180,6 @@ impl UnownedWindow {
};
// creating
let (visual, depth, require_colormap) = match pl_attribs.visual_infos {
Some(vi) => (vi.visual, vi.depth, false),
None if window_attrs.transparent => {
// Find a suitable visual
let mut vinfo = MaybeUninit::uninit();
let vinfo_initialized = unsafe {
(xconn.xlib.XMatchVisualInfo)(
xconn.display,
screen_id,
32,
TrueColor,
vinfo.as_mut_ptr(),
) != 0
};
if vinfo_initialized {
let vinfo = unsafe { vinfo.assume_init() };
(vinfo.visual, vinfo.depth, true)
} else {
debug!("Could not set transparency, because XMatchVisualInfo returned zero for the required parameters");
(
ffi::CopyFromParent as *mut ffi::Visual,
ffi::CopyFromParent,
false,
)
}
}
_ => (
ffi::CopyFromParent as *mut ffi::Visual,
ffi::CopyFromParent,
false,
),
};
let mut set_win_attr = {
let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() };
swa.colormap = if let Some(vi) = pl_attribs.visual_infos {
@@ -222,8 +187,6 @@ 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
};
@@ -257,9 +220,23 @@ impl UnownedWindow {
dimensions.0 as c_uint,
dimensions.1 as c_uint,
0,
depth,
match pl_attribs.visual_infos {
Some(vi) => vi.depth,
None => ffi::CopyFromParent,
},
ffi::InputOutput as c_uint,
visual,
// 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,
},
window_attributes,
&mut set_win_attr,
)
@@ -275,10 +252,7 @@ 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: WakeSender {
waker: event_loop.redraw_sender.waker.clone(),
sender: event_loop.redraw_sender.sender.clone(),
},
redraw_sender: event_loop.redraw_sender.clone(),
};
// Title must be set before mapping. Some tiling window managers (i.e. i3) use the window
@@ -341,9 +315,7 @@ impl UnownedWindow {
} //.queue();
}
if let Some(flusher) = window.set_pid() {
flusher.queue()
}
window.set_pid().map(|flusher| flusher.queue());
window.set_window_types(pl_attribs.x11_window_types).queue();
@@ -432,7 +404,8 @@ impl UnownedWindow {
}
// Select XInput2 events
let mask = ffi::XI_MotionMask
let mask = {
let mask = ffi::XI_MotionMask
| ffi::XI_ButtonPressMask
| ffi::XI_ButtonReleaseMask
//| ffi::XI_KeyPressMask
@@ -444,6 +417,8 @@ impl UnownedWindow {
| ffi::XI_TouchBeginMask
| ffi::XI_TouchUpdateMask
| ffi::XI_TouchEndMask;
mask
};
xconn
.select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask)
.queue();
@@ -466,10 +441,9 @@ impl UnownedWindow {
window.set_maximized_inner(window_attrs.maximized).queue();
}
if window_attrs.fullscreen.is_some() {
if let Some(flusher) = window.set_fullscreen_inner(window_attrs.fullscreen.clone())
{
flusher.queue()
}
window
.set_fullscreen_inner(window_attrs.fullscreen.clone())
.map(|flusher| flusher.queue());
if let Some(PhysicalPosition { x, y }) = position {
let shared_state = window.shared_state.get_mut();
@@ -805,30 +779,6 @@ 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
@@ -1106,7 +1056,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);
@@ -1187,7 +1137,7 @@ impl UnownedWindow {
)
} else {
let window_size = Some(Size::from(self.inner_size()));
(window_size, window_size)
(window_size.clone(), window_size)
};
self.set_maximizable_inner(resizable).queue();
@@ -1390,41 +1340,6 @@ 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
@@ -1449,18 +1364,15 @@ impl UnownedWindow {
#[inline]
pub fn request_redraw(&self) {
self.redraw_sender
.sender
.send(WindowId(self.xwindow))
.unwrap();
self.redraw_sender.waker.wake().unwrap();
self.redraw_sender.send(WindowId(self.xwindow)).unwrap();
}
#[inline]
pub fn raw_window_handle(&self) -> XlibHandle {
let mut handle = XlibHandle::empty();
handle.window = self.xlib_window();
handle.display = self.xlib_display();
handle
XlibHandle {
window: self.xwindow,
display: self.xconn.display as _,
..XlibHandle::empty()
}
}
}

View File

@@ -15,12 +15,11 @@ use std::{
use cocoa::{
appkit::{NSApp, NSApplication, NSWindow},
base::{id, nil},
foundation::NSSize,
};
use objc::{
rc::autoreleasepool,
runtime::{Object, YES},
foundation::{NSAutoreleasePool, NSSize},
};
use objc::runtime::YES;
use objc::runtime::Object;
use crate::{
dpi::LogicalSize,
@@ -307,9 +306,7 @@ impl AppState {
let panic_info = panic_info
.upgrade()
.expect("The panic info must exist here. This failure indicates a developer error.");
// 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() {
if panic_info.is_panicking() || !HANDLER.is_ready() {
return;
}
let start = HANDLER.get_start_time().unwrap();
@@ -373,25 +370,24 @@ impl AppState {
let panic_info = panic_info
.upgrade()
.expect("The panic info must exist here. This failure indicates a developer error.");
// 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() {
if panic_info.is_panicking() || !HANDLER.is_ready() {
return;
}
HANDLER.set_in_callback(true);
HANDLER.handle_user_events();
for event in HANDLER.take_events() {
HANDLER.handle_nonuser_event(event);
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.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();
@@ -407,16 +403,16 @@ impl AppState {
};
let dialog_is_closing = HANDLER.dialog_is_closing.load(Ordering::SeqCst);
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);
}
});
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();
if window_count > 0 {
let window: id = msg_send![windows, objectAtIndex:0];

View File

@@ -14,9 +14,10 @@ use std::{
use cocoa::{
appkit::{NSApp, NSEventType::NSApplicationDefined},
base::{id, nil, YES},
foundation::NSPoint,
foundation::{NSAutoreleasePool, NSPoint},
};
use objc::rc::autoreleasepool;
use scopeguard::defer;
use crate::{
event::Event,
@@ -85,20 +86,6 @@ 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,
@@ -128,9 +115,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]);
autoreleasepool(|| {
let _: () = msg_send![app, setDelegate:*delegate];
});
let pool = NSAutoreleasePool::new(nil);
let _: () = msg_send![app, setDelegate:*delegate];
let _: () = msg_send![pool, drain];
delegate
};
let panic_info: Rc<PanicInfo> = Default::default();
@@ -175,7 +162,9 @@ impl<T> EventLoop<T> {
self._callback = Some(Rc::clone(&callback));
autoreleasepool(|| unsafe {
unsafe {
let pool = NSAutoreleasePool::new(nil);
defer!(pool.drain());
let app = NSApp();
assert_ne!(app, nil);
@@ -188,12 +177,10 @@ 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,8 +2,6 @@
#![allow(dead_code, non_snake_case, non_upper_case_globals)]
use std::ffi::c_void;
use cocoa::{
base::id,
foundation::{NSInteger, NSUInteger},
@@ -161,20 +159,11 @@ pub const IOYUV422Pixels: &str = "Y4U2V2";
pub const IO8BitOverlayPixels: &str = "O8";
pub type CGWindowLevel = i32;
pub type CGDisplayModeRef = *mut c_void;
pub type CGDisplayModeRef = *mut libc::c_void;
// `CGDisplayCreateUUIDFromDisplayID` comes from the `ColorSync` framework.
// However, that framework was only introduced "publicly" in macOS 10.13.
//
// Since we want to support older versions, we can't link to `ColorSync`
// directly. Fortunately, it has always been available as a subframework of
// `ApplicationServices`, see:
// https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/OSX_Technology_Overview/SystemFrameworks/SystemFrameworks.html#//apple_ref/doc/uid/TP40001067-CH210-BBCFFIEG
//
// TODO: Remove the WINIT_LINK_COLORSYNC hack, it is probably not needed.
#[cfg_attr(
not(use_colorsync_cgdisplaycreateuuidfromdisplayid),
link(name = "ApplicationServices", kind = "framework")
link(name = "CoreGraphics", kind = "framework")
)]
#[cfg_attr(
use_colorsync_cgdisplaycreateuuidfromdisplayid,

View File

@@ -1,7 +1,6 @@
use super::util::IdRef;
use cocoa::appkit::{NSApp, NSApplication, NSEventModifierFlags, NSMenu, NSMenuItem};
use cocoa::base::{nil, selector};
use cocoa::foundation::{NSProcessInfo, NSString};
use cocoa::foundation::{NSAutoreleasePool, NSProcessInfo, NSString};
use objc::{
rc::autoreleasepool,
runtime::{Object, Sel},
@@ -14,11 +13,11 @@ struct KeyEquivalent<'a> {
pub fn initialize() {
autoreleasepool(|| unsafe {
let menubar = IdRef::new(NSMenu::new(nil));
let app_menu_item = IdRef::new(NSMenuItem::new(nil));
menubar.addItem_(*app_menu_item);
let menubar = NSMenu::new(nil).autorelease();
let app_menu_item = NSMenuItem::new(nil).autorelease();
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,7 +25,6 @@ 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;
@@ -33,7 +32,7 @@ pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub struct DeviceId;
impl DeviceId {
pub const unsafe fn dummy() -> Self {
pub unsafe fn dummy() -> Self {
DeviceId
}
}
@@ -70,7 +69,7 @@ impl Window {
attributes: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, RootOsError> {
let (window, _delegate) = autoreleasepool(|| UnownedWindow::new(attributes, pl_attribs))?;
let (window, _delegate) = UnownedWindow::new(attributes, pl_attribs)?;
Ok(Window { window, _delegate })
}
}

View File

@@ -162,7 +162,6 @@ impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// TODO: Do this using the proper fmt API
#[derive(Debug)]
#[allow(dead_code)]
struct MonitorHandle {
name: Option<String>,
native_identifier: u32,

View File

@@ -8,7 +8,7 @@ use std::ops::{BitAnd, Deref};
use cocoa::{
appkit::{NSApp, NSWindowStyleMask},
base::{id, nil},
foundation::{NSPoint, NSRect, NSString, NSUInteger},
foundation::{NSAutoreleasePool, NSPoint, NSRect, NSString, NSUInteger},
};
use core_graphics::display::CGDisplay;
use objc::runtime::{Class, Object, Sel, BOOL, YES};
@@ -61,7 +61,9 @@ 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();
};
}
}

View File

@@ -55,6 +55,7 @@ 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>,
}
@@ -73,6 +74,7 @@ pub fn new_view(ns_window: id) -> (IdRef, Weak<Mutex<CursorState>>) {
cursor_state,
ime_spot: None,
raw_characters: None,
is_key_down: false,
modifiers: Default::default(),
tracking_rect: None,
};
@@ -287,7 +289,7 @@ extern "C" fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> i
let notification_center: &Object =
msg_send![class!(NSNotificationCenter), defaultCenter];
let notification_name =
IdRef::new(NSString::alloc(nil).init_str("NSViewFrameDidChangeNotification"));
NSString::alloc(nil).init_str("NSViewFrameDidChangeNotification");
let _: () = msg_send![
notification_center,
addObserver: this
@@ -514,6 +516,7 @@ 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];
@@ -672,7 +675,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 {
if is_repeat && state.is_key_down {
for character in characters.chars().filter(|c| !is_corporate_character(*c)) {
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id,
@@ -702,6 +705,8 @@ 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::{AppKitHandle, RawWindowHandle};
use raw_window_handle::{macos::MacOSHandle, RawWindowHandle};
use std::{
collections::VecDeque,
f64,
@@ -38,12 +38,11 @@ use cocoa::{
NSRequestUserAttentionType, NSScreen, NSView, NSWindow, NSWindowButton, NSWindowStyleMask,
},
base::{id, nil},
foundation::{NSDictionary, NSPoint, NSRect, NSSize},
foundation::{NSAutoreleasePool, NSDictionary, NSPoint, NSRect, NSSize},
};
use core_graphics::display::{CGDisplay, CGDisplayMode};
use objc::{
declare::ClassDecl,
rc::autoreleasepool,
runtime::{Class, Object, Sel, BOOL, NO, YES},
};
@@ -51,7 +50,7 @@ use objc::{
pub struct Id(pub usize);
impl Id {
pub const unsafe fn dummy() -> Self {
pub unsafe fn dummy() -> Self {
Id(0)
}
}
@@ -119,7 +118,8 @@ fn create_window(
attrs: &WindowAttributes,
pl_attrs: &PlatformSpecificWindowBuilderAttributes,
) -> Option<IdRef> {
autoreleasepool(|| unsafe {
unsafe {
let pool = NSAutoreleasePool::new(nil);
let screen = match attrs.fullscreen {
Some(Fullscreen::Borderless(Some(RootMonitorHandle { inner: ref monitor })))
| Some(Fullscreen::Exclusive(RootVideoMode {
@@ -241,8 +241,9 @@ fn create_window(
}
ns_window
});
pool.drain();
res
})
}
}
struct WindowClass(*const Class);
@@ -281,10 +282,7 @@ 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. 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.
/// restored upon exiting it
save_presentation_opts: Option<NSApplicationPresentationOptions>,
pub saved_desktop_display_mode: Option<(CGDisplay, CGDisplayMode)>,
}
@@ -338,11 +336,17 @@ impl UnownedWindow {
}
trace!("Creating new window");
let ns_window = create_window(&win_attribs, &pl_attribs)
.ok_or_else(|| os_error!(OsError::CreationError("Couldn't create `NSWindow`")))?;
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_view, cursor_state) = unsafe { create_view(*ns_window, &pl_attribs) }
.ok_or_else(|| os_error!(OsError::CreationError("Couldn't create `NSView`")))?;
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`"))
})?;
// Configure the new view as the "key view" for the window
unsafe {
@@ -418,6 +422,8 @@ impl UnownedWindow {
window.set_maximized(maximized);
}
unsafe { pool.drain() };
Ok((window, delegate))
}
@@ -785,15 +791,6 @@ 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
@@ -844,6 +841,7 @@ 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);
@@ -884,42 +882,16 @@ 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]
@@ -998,21 +970,6 @@ 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 {
@@ -1059,10 +1016,12 @@ impl UnownedWindow {
#[inline]
pub fn raw_window_handle(&self) -> RawWindowHandle {
let mut handle = AppKitHandle::empty();
handle.ns_window = *self.ns_window as *mut _;
handle.ns_view = *self.ns_view as *mut _;
RawWindowHandle::AppKit(handle)
let handle = MacOSHandle {
ns_window: *self.ns_window as *mut _,
ns_view: *self.ns_view as *mut _,
..MacOSHandle::empty()
};
RawWindowHandle::MacOS(handle)
}
}

View File

@@ -7,11 +7,10 @@ use std::{
use cocoa::{
appkit::{self, NSApplicationPresentationOptions, NSView, NSWindow},
base::{id, nil},
foundation::NSUInteger,
foundation::{NSAutoreleasePool, NSUInteger},
};
use objc::{
declare::ClassDecl,
rc::autoreleasepool,
runtime::{Class, Object, Sel, BOOL, NO, YES},
};
@@ -275,11 +274,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
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];
});
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();
state.emit_event(WindowEvent::Destroyed);
});
trace!("Completed `windowWillClose:`");
@@ -482,10 +481,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
@@ -495,22 +494,10 @@ 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.
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
(NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar)
.bits()
}
/// Invoked when entered fullscreen

View File

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

View File

@@ -1,5 +1,5 @@
// Brief introduction to the internals of the web backend:
// The web backend used to support both wasm-bindgen and stdweb as methods of binding to the
// Currently, the web backend supports 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,15 +17,26 @@
// 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

@@ -0,0 +1,319 @@
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

@@ -0,0 +1,239 @@
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

@@ -0,0 +1,94 @@
#![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

@@ -0,0 +1,13 @@
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

@@ -0,0 +1,63 @@
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

@@ -89,20 +89,6 @@ impl Canvas {
})
}
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), RootOE> {
if grab {
self.raw().request_pointer_lock();
} else {
let window = web_sys::window()
.ok_or(os_error!(OsError("Failed to obtain window".to_owned())))?;
let document = window
.document()
.ok_or(os_error!(OsError("Failed to obtain document".to_owned())))?;
document.exit_pointer_lock();
}
Ok(())
}
pub fn set_attribute(&self, attribute: &str, value: &str) {
self.common
.raw

View File

@@ -79,11 +79,9 @@ impl PointerHandler {
event::mouse_button(&event),
event::mouse_modifiers(&event),
);
// Error is swallowed here since the error would occur every time the mouse is
// clicked when the cursor is grabbed, and there is probably not a situation where
// this could fail, that we care if it fails.
let _e = canvas.set_pointer_capture(event.pointer_id());
canvas
.set_pointer_capture(event.pointer_id())
.expect("Failed to set pointer capture");
},
));
}

View File

@@ -7,7 +7,7 @@ use crate::window::{
CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWI,
};
use raw_window_handle::{RawWindowHandle, WebHandle};
use raw_window_handle::web::WebHandle;
use super::{backend, monitor, EventLoopWindowTarget};
@@ -207,11 +207,8 @@ impl Window {
}
#[inline]
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
self.canvas
.borrow()
.set_cursor_grab(grab)
.map_err(|e| ExternalError::Os(e))
pub fn set_cursor_grab(&self, _grab: bool) -> Result<(), ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
#[inline]
@@ -284,11 +281,6 @@ 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
@@ -325,10 +317,13 @@ impl Window {
}
#[inline]
pub fn raw_window_handle(&self) -> RawWindowHandle {
let mut handle = WebHandle::empty();
handle.id = self.id.0;
RawWindowHandle::Web(handle)
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)
}
}
@@ -344,7 +339,7 @@ impl Drop for Window {
pub struct Id(pub(crate) u32);
impl Id {
pub const unsafe fn dummy() -> Id {
pub unsafe fn dummy() -> Id {
Id(0)
}
}

View File

@@ -6,24 +6,38 @@ use std::os::windows::ffi::OsStrExt;
use winapi::{
shared::{
basetsd::SIZE_T,
minwindef::{BOOL, DWORD, FALSE, WORD},
ntdef::{NTSTATUS, NT_SUCCESS, PVOID},
minwindef::{BOOL, DWORD, FALSE, UINT, ULONG, WORD},
ntdef::{LPSTR, NTSTATUS, NT_SUCCESS, PVOID, WCHAR},
windef::HWND,
winerror::S_OK,
},
um::{libloaderapi, uxtheme, winnt, winuser},
um::{libloaderapi, uxtheme, winuser},
};
use crate::window::Theme;
lazy_static! {
static ref WIN10_BUILD_VERSION: Option<DWORD> = {
type RtlGetVersion = unsafe extern "system" fn (*mut winnt::OSVERSIONINFOW) -> NTSTATUS;
// FIXME: RtlGetVersion is a documented windows API,
// should be part of winapi!
#[allow(non_snake_case)]
#[repr(C)]
struct OSVERSIONINFOW {
dwOSVersionInfoSize: ULONG,
dwMajorVersion: ULONG,
dwMinorVersion: ULONG,
dwBuildNumber: ULONG,
dwPlatformId: ULONG,
szCSDVersion: [WCHAR; 128],
}
type RtlGetVersion = unsafe extern "system" fn (*mut OSVERSIONINFOW) -> NTSTATUS;
let handle = get_function!("ntdll.dll", RtlGetVersion);
if let Some(rtl_get_version) = handle {
unsafe {
let mut vi = winnt::OSVERSIONINFOW {
let mut vi = OSVERSIONINFOW {
dwOSVersionInfoSize: 0,
dwMajorVersion: 0,
dwMinorVersion: 0,
@@ -94,12 +108,11 @@ fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) -> bool {
type SetWindowCompositionAttribute =
unsafe extern "system" fn(HWND, *mut WINDOWCOMPOSITIONATTRIBDATA) -> BOOL;
#[allow(clippy::upper_case_acronyms)]
#[allow(non_snake_case)]
type WINDOWCOMPOSITIONATTRIB = u32;
const WCA_USEDARKMODECOLORS: WINDOWCOMPOSITIONATTRIB = 26;
#[allow(non_snake_case)]
#[allow(clippy::upper_case_acronyms)]
#[repr(C)]
struct WINDOWCOMPOSITIONATTRIBDATA {
Attrib: WINDOWCOMPOSITIONATTRIB,
@@ -168,8 +181,21 @@ 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 = winuser::HIGHCONTRASTA {
let mut hc = HIGHCONTRASTA {
cbSize: 0,
dwFlags: 0,
lpszDefaultScheme: std::ptr::null_mut(),
@@ -184,7 +210,7 @@ fn is_high_contrast() -> bool {
)
};
ok != FALSE && (winuser::HCF_HIGHCONTRASTON & hc.dwFlags) == 1
ok != FALSE && (HCF_HIGHCONTRASTON & hc.dwFlags) == 1
}
fn widestring(src: &'static str) -> Vec<u16> {

View File

@@ -181,7 +181,7 @@ impl FileDropHandler {
},
};
let drop_format = FORMATETC {
let mut 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(&drop_format, &mut medium);
let get_data_result = (*data_obj).GetData(&mut 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());
}
Some(hdrop)
return 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.");
None
return None;
} else {
debug!("Unexpected error occured while processing dropped/hovered item.");
None
return None;
}
}
}

View File

@@ -39,8 +39,8 @@ pub fn get_key_mods() -> ModifiersState {
bitflags! {
#[derive(Default)]
pub struct ModifiersStateSide: u32 {
const LSHIFT = 0b010;
const RSHIFT = 0b001;
const LSHIFT = 0b010 << 0;
const RSHIFT = 0b001 << 0;
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 = if extended { 0xE000 } else { 0x0000 } | scancode;
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 => {
0x45 if vkey == winuser::VK_PAUSE || vkey == 0xFF as _ => {
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
pub(super) thread_msg_target: HWND,
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 const unsafe fn dummy() -> Self {
pub unsafe fn dummy() -> Self {
DeviceId(0)
}
}
@@ -88,7 +88,7 @@ unsafe impl Send for WindowId {}
unsafe impl Sync for WindowId {}
impl WindowId {
pub const unsafe fn dummy() -> Self {
pub 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)
String::from_utf16_lossy(wchar).to_string()
}
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::{RawWindowHandle, Win32Handle};
use raw_window_handle::{windows::WindowsHandle, RawWindowHandle};
use std::{
cell::Cell,
ffi::OsStr,
io, mem,
os::windows::ffi::OsStrExt,
panic, ptr,
ptr,
sync::{mpsc::channel, Arc},
};
@@ -16,7 +16,6 @@ use winapi::{
shared::{
minwindef::{HINSTANCE, LPARAM, UINT, WPARAM},
windef::{HWND, POINT, POINTS, RECT},
winerror::SUCCEEDED,
},
um::{
combaseapi, dwmapi,
@@ -39,9 +38,9 @@ use crate::{
monitor::MonitorHandle as RootMonitorHandle,
platform_impl::platform::{
dark_mode::try_theme,
dpi::{dpi_to_scale_factor, enable_non_client_dpi_scaling, hwnd_dpi},
dpi::{dpi_to_scale_factor, hwnd_dpi},
drop_handler::FileDropHandler,
event_loop::{self, EventLoopWindowTarget, WindowLongPtr, DESTROY_MSG_ID},
event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID},
icon::{self, IconType},
monitor, util,
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
@@ -72,7 +71,58 @@ impl Window {
// First person to remove the need for cloning here gets a cookie!
//
// done. you owe me -- ossi
unsafe { init(w_attr, pl_attr, event_loop) }
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
})
}
}
pub fn set_title(&self, text: &str) {
@@ -234,10 +284,12 @@ impl Window {
#[inline]
pub fn raw_window_handle(&self) -> RawWindowHandle {
let mut handle = Win32Handle::empty();
handle.hwnd = self.window.0 as *mut _;
handle.hinstance = self.hinstance() as *mut _;
RawWindowHandle::Win32(handle)
let handle = WindowsHandle {
hwnd: self.window.0 as *mut _,
hinstance: self.hinstance() as *mut _,
..WindowsHandle::empty()
};
RawWindowHandle::Windows(handle)
}
#[inline]
@@ -404,7 +456,7 @@ impl Window {
// string, so add it
display_name.push(0);
let mut native_video_mode = video_mode.video_mode.native_video_mode;
let mut native_video_mode = video_mode.video_mode.native_video_mode.clone();
let res = unsafe {
winuser::ChangeDisplaySettingsExW(
@@ -565,11 +617,6 @@ 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 {
@@ -638,20 +685,6 @@ 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 {
@@ -677,186 +710,18 @@ pub struct WindowWrapper(HWND);
unsafe impl Sync for WindowWrapper {}
unsafe impl Send for WindowWrapper {}
pub(super) struct InitData<'a, T: 'static> {
// inputs
pub event_loop: &'a EventLoopWindowTarget<T>,
pub attributes: WindowAttributes,
pub pl_attribs: PlatformSpecificWindowBuilderAttributes,
pub window_flags: WindowFlags,
// outputs
pub window: Option<Window>,
}
impl<'a, T: 'static> InitData<'a, T> {
unsafe fn create_window(&self, window: HWND) -> Window {
// Register for touch events if applicable
{
let digitizer = winuser::GetSystemMetrics(winuser::SM_DIGITIZER) as u32;
if digitizer & winuser::NID_READY != 0 {
winuser::RegisterTouchWindow(window, winuser::TWF_WANTPALM);
}
}
let dpi = hwnd_dpi(window);
let scale_factor = dpi_to_scale_factor(dpi);
// If the system theme is dark, we need to set the window theme now
// before we update the window flags (and possibly show the
// window for the first time).
let current_theme = try_theme(window, self.pl_attribs.preferred_theme);
let window_state = {
let window_state = WindowState::new(
&self.attributes,
self.pl_attribs.taskbar_icon.clone(),
scale_factor,
current_theme,
self.pl_attribs.preferred_theme,
);
let window_state = Arc::new(Mutex::new(window_state));
WindowState::set_window_flags(window_state.lock(), window, |f| *f = self.window_flags);
window_state
};
enable_non_client_dpi_scaling(window);
Window {
window: WindowWrapper(window),
window_state,
thread_executor: self.event_loop.create_thread_executor(),
}
}
unsafe fn create_window_data(&self, win: &Window) -> event_loop::WindowData<T> {
let file_drop_handler = if self.pl_attribs.drag_and_drop {
use winapi::shared::winerror::{OLE_E_WRONGCOMPOBJ, RPC_E_CHANGED_MODE, S_OK};
let ole_init_result = ole2::OleInitialize(ptr::null_mut());
// It is ok if the initialize result is `S_FALSE` because it might happen that
// multiple windows are created on the same thread.
if ole_init_result == OLE_E_WRONGCOMPOBJ {
panic!("OleInitialize failed! Result was: `OLE_E_WRONGCOMPOBJ`");
} else if ole_init_result == RPC_E_CHANGED_MODE {
panic!(
"OleInitialize failed! Result was: `RPC_E_CHANGED_MODE`. \
Make sure other crates are not using multithreaded COM library \
on the same thread or disable drag and drop support."
);
}
let file_drop_runner = self.event_loop.runner_shared.clone();
let file_drop_handler = FileDropHandler::new(
win.window.0,
Box::new(move |event| {
if let Ok(e) = event.map_nonuser_event() {
file_drop_runner.send_event(e)
}
}),
);
let handler_interface_ptr = &mut (*file_drop_handler.data).interface as LPDROPTARGET;
assert_eq!(
ole2::RegisterDragDrop(win.window.0, handler_interface_ptr),
S_OK
);
Some(file_drop_handler)
} else {
None
};
self.event_loop.runner_shared.register_window(win.window.0);
event_loop::WindowData {
window_state: win.window_state.clone(),
event_loop_runner: self.event_loop.runner_shared.clone(),
_file_drop_handler: file_drop_handler,
userdata_removed: Cell::new(false),
recurse_depth: Cell::new(0),
}
}
// Returns a pointer to window user data on success.
// The user data will be registered for the window and can be accessed within the window event callback.
pub unsafe fn on_nccreate(&mut self, window: HWND) -> Option<WindowLongPtr> {
let runner = self.event_loop.runner_shared.clone();
let result = runner.catch_unwind(|| unsafe {
let mut window = self.create_window(window);
let window_data = self.create_window_data(&mut window);
(window, window_data)
});
result.map(|(win, userdata)| {
self.window = Some(win);
let userdata = Box::into_raw(Box::new(userdata));
userdata as _
})
}
pub unsafe fn on_create(&mut self) {
let win = self.window.as_mut().expect("failed window creation");
// making the window transparent
if self.attributes.transparent && !self.pl_attribs.no_redirection_bitmap {
// Empty region for the blur effect, so the window is fully transparent
let region = CreateRectRgn(0, 0, -1, -1);
let bb = dwmapi::DWM_BLURBEHIND {
dwFlags: dwmapi::DWM_BB_ENABLE | dwmapi::DWM_BB_BLURREGION,
fEnable: 1,
hRgnBlur: region,
fTransitionOnMaximized: 0,
};
let hr = dwmapi::DwmEnableBlurBehindWindow(win.hwnd(), &bb);
if !SUCCEEDED(hr) {
warn!(
"Setting transparent window is failed. HRESULT Code: 0x{:X}",
hr
);
}
DeleteObject(region as _);
}
let attributes = self.attributes.clone();
// Set visible before setting the size to ensure the
// attribute is correctly applied.
win.set_visible(attributes.visible);
let dimensions = attributes
.inner_size
.unwrap_or_else(|| PhysicalSize::new(800, 600).into());
win.set_inner_size(dimensions);
if attributes.maximized {
// Need to set MAXIMIZED after setting `inner_size` as
// `Window::set_inner_size` changes MAXIMIZED to false.
win.set_maximized(true);
}
if attributes.fullscreen.is_some() {
win.set_fullscreen(attributes.fullscreen);
force_window_active(win.window.0);
}
if let Some(position) = attributes.position {
win.set_outer_position(position);
}
}
}
unsafe fn init<T>(
unsafe fn init<T: 'static>(
attributes: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes,
event_loop: &EventLoopWindowTarget<T>,
) -> Result<Window, RootOsError>
where
T: 'static,
{
) -> Result<Window, RootOsError> {
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::<T>(&attributes.window_icon, &pl_attribs.taskbar_icon);
let class_name = register_window_class(&attributes.window_icon, &pl_attribs.taskbar_icon);
let mut window_flags = WindowFlags::empty();
window_flags.set(WindowFlags::DECORATIONS, attributes.decorations);
@@ -887,45 +752,106 @@ where
}
};
let mut initdata = InitData {
event_loop,
attributes,
pl_attribs: pl_attribs.clone(),
window_flags,
window: None,
// 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 (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)
// 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);
}
}
if handle.is_null() {
return Err(os_error!(io::Error::last_os_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 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())
// 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)
}
unsafe fn register_window_class<T: 'static>(
unsafe fn register_window_class(
window_icon: &Option<Icon>,
taskbar_icon: &Option<Icon>,
) -> Vec<u16> {
@@ -946,7 +872,7 @@ unsafe fn register_window_class<T: 'static>(
let class = winuser::WNDCLASSEXW {
cbSize: mem::size_of::<winuser::WNDCLASSEXW>() as UINT,
style: winuser::CS_HREDRAW | winuser::CS_VREDRAW | winuser::CS_OWNDC,
lpfnWndProc: Some(super::event_loop::public_window_callback::<T>),
lpfnWndProc: Some(winuser::DefWindowProcW),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: libloaderapi::GetModuleHandleW(ptr::null()),
@@ -1003,7 +929,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.is_null() {
if task_bar_list == ptr::null_mut() {
use winapi::{shared::winerror::S_OK, Interface};
let hr = combaseapi::CoCreateInstance(

View File

@@ -69,16 +69,12 @@ impl Drop for Window {
pub struct WindowId(pub(crate) platform_impl::WindowId);
impl 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`.
/// 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`.
///
/// **Passing this into a winit function will result in undefined behavior.**
pub const unsafe fn dummy() -> Self {
pub unsafe fn dummy() -> Self {
WindowId(platform_impl::WindowId::dummy())
}
}
@@ -442,7 +438,7 @@ impl Window {
/// ## Platform-specific
///
/// - **iOS:** Can only be called on the main thread.
/// - **Android:** Subsequent calls after `MainEventsCleared` are not handled.
/// - **Android:** Unsupported.
#[inline]
pub fn request_redraw(&self) {
self.window.request_redraw()
@@ -496,19 +492,6 @@ impl Window {
/// See `outer_position` for more information about the coordinates. This automatically un-maximizes the
/// window if it's maximized.
///
/// ```no_run
/// # use winit::dpi::{LogicalPosition, PhysicalPosition};
/// # use winit::event_loop::EventLoop;
/// # use winit::window::Window;
/// # let mut event_loop = EventLoop::new();
/// # let window = Window::new(&event_loop).unwrap();
/// // Specify the position in logical dimensions like this:
/// window.set_outer_position(LogicalPosition::new(400.0, 200.0));
///
/// // Or specify the position in physical dimensions like this:
/// window.set_outer_position(PhysicalPosition::new(400, 200));
/// ```
///
/// ## Platform-specific
///
/// - **iOS:** Can only be called on the main thread. Sets the top left coordinates of the
@@ -541,19 +524,6 @@ impl Window {
/// See `inner_size` for more information about the values. This automatically un-maximizes the
/// window if it's maximized.
///
/// ```no_run
/// # use winit::dpi::{LogicalSize, PhysicalSize};
/// # use winit::event_loop::EventLoop;
/// # use winit::window::Window;
/// # let mut event_loop = EventLoop::new();
/// # let window = Window::new(&event_loop).unwrap();
/// // Specify the size in logical dimensions like this:
/// window.set_inner_size(LogicalSize::new(400.0, 200.0));
///
/// // Or specify the size in physical dimensions like this:
/// window.set_inner_size(PhysicalSize::new(400, 200));
/// ```
///
/// ## Platform-specific
///
/// - **iOS / Android:** Unsupported.
@@ -581,19 +551,6 @@ impl Window {
/// Sets a minimum dimension size for the window.
///
/// ```no_run
/// # use winit::dpi::{LogicalSize, PhysicalSize};
/// # use winit::event_loop::EventLoop;
/// # use winit::window::Window;
/// # let mut event_loop = EventLoop::new();
/// # let window = Window::new(&event_loop).unwrap();
/// // Specify the size in logical dimensions like this:
/// window.set_min_inner_size(Some(LogicalSize::new(400.0, 200.0)));
///
/// // Or specify the size in physical dimensions like this:
/// window.set_min_inner_size(Some(PhysicalSize::new(400, 200)));
/// ```
///
/// ## Platform-specific
///
/// - **iOS / Android / Web:** Unsupported.
@@ -604,19 +561,6 @@ impl Window {
/// Sets a maximum dimension size for the window.
///
/// ```no_run
/// # use winit::dpi::{LogicalSize, PhysicalSize};
/// # use winit::event_loop::EventLoop;
/// # use winit::window::Window;
/// # let mut event_loop = EventLoop::new();
/// # let window = Window::new(&event_loop).unwrap();
/// // Specify the size in logical dimensions like this:
/// window.set_max_inner_size(Some(LogicalSize::new(400.0, 200.0)));
///
/// // Or specify the size in physical dimensions like this:
/// window.set_max_inner_size(Some(PhysicalSize::new(400, 200)));
/// ```
///
/// ## Platform-specific
///
/// - **iOS / Android / Web:** Unsupported.
@@ -779,19 +723,6 @@ impl Window {
/// Sets location of IME candidate box in client area coordinates relative to the top left.
///
/// ```no_run
/// # use winit::dpi::{LogicalPosition, PhysicalPosition};
/// # use winit::event_loop::EventLoop;
/// # use winit::window::Window;
/// # let mut event_loop = EventLoop::new();
/// # let window = Window::new(&event_loop).unwrap();
/// // Specify the position in logical dimensions like this:
/// window.set_ime_position(LogicalPosition::new(400.0, 200.0));
///
/// // Or specify the position in physical dimensions like this:
/// window.set_ime_position(PhysicalPosition::new(400, 200));
/// ```
///
/// ## Platform-specific
///
/// - **iOS / Android / Web:** Unsupported.
@@ -800,21 +731,6 @@ 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.
@@ -824,10 +740,9 @@ impl Window {
///
/// ## Platform-specific
///
/// - **iOS / Android / Web :** Unsupported.
/// - **iOS / Android / Web / Wayland:** 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)
@@ -848,19 +763,6 @@ impl Window {
/// Changes the position of the cursor in window coordinates.
///
/// ```no_run
/// # use winit::dpi::{LogicalPosition, PhysicalPosition};
/// # use winit::event_loop::EventLoop;
/// # use winit::window::Window;
/// # let mut event_loop = EventLoop::new();
/// # let window = Window::new(&event_loop).unwrap();
/// // Specify the position in logical dimensions like this:
/// window.set_cursor_position(LogicalPosition::new(400.0, 200.0));
///
/// // Or specify the position in physical dimensions like this:
/// window.set_cursor_position(PhysicalPosition::new(400, 200));
/// ```
///
/// ## Platform-specific
///
/// - **iOS / Android / Web / Wayland:** Always returns an [`ExternalError::NotSupported`].
@@ -877,7 +779,7 @@ impl Window {
/// ## Platform-specific
///
/// - **macOS:** This locks the cursor in a fixed location, which looks visually awkward.
/// - **iOS / Android:** Always returns an [`ExternalError::NotSupported`].
/// - **iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`].
#[inline]
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
self.window.set_cursor_grab(grab)