mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-27 07:03:15 -04:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30aa5a5057 | ||
|
|
a46fcaee31 | ||
|
|
52e2748869 | ||
|
|
d2d127a4c4 | ||
|
|
0fca8e8cb5 | ||
|
|
6bec912961 | ||
|
|
214e157e5d | ||
|
|
da1d479e55 | ||
|
|
062bb0cef2 | ||
|
|
c744b016ce | ||
|
|
f486845f7f | ||
|
|
7baa96c5c7 | ||
|
|
ea07ec1fda | ||
|
|
26b70e457b | ||
|
|
5d5fcb3911 | ||
|
|
50008dff3d | ||
|
|
808638fee3 | ||
|
|
b0e3865562 | ||
|
|
bc03ffb317 | ||
|
|
1edbca1775 | ||
|
|
5a0bc016e7 | ||
|
|
bb66b7f28e | ||
|
|
0331491b2b | ||
|
|
54a782c8ae | ||
|
|
a70bc20829 | ||
|
|
102ed3b800 | ||
|
|
c8e339fe6d | ||
|
|
e4e53fe315 | ||
|
|
1c795c3f1c | ||
|
|
b2b740fed7 | ||
|
|
09550397d7 | ||
|
|
a32f7f2ec5 | ||
|
|
e8e9fa2418 | ||
|
|
1a119bdfe9 | ||
|
|
21ff2e0ffc | ||
|
|
df9b23c96a | ||
|
|
4c117aa282 | ||
|
|
88427262a6 | ||
|
|
72b24a9348 | ||
|
|
01cb8e59e3 | ||
|
|
3910326709 | ||
|
|
7ee46d80e6 | ||
|
|
0cb5450999 | ||
|
|
8c78013257 | ||
|
|
bd944898f0 |
@@ -65,8 +65,12 @@ install:
|
||||
|
||||
script:
|
||||
- cargo build --target $TARGET --verbose
|
||||
- cargo build --target $TARGET --features serde --verbose
|
||||
- cargo build --target $TARGET --features icon_loading --verbose
|
||||
# Running iOS apps on OSX requires the simulator so we skip that for now
|
||||
- if [ "$TARGET" != "x86_64-apple-ios" ]; then cargo test --target $TARGET --verbose; fi
|
||||
- if [ "$TARGET" != "x86_64-apple-ios" ]; then cargo test --target $TARGET --features serde --verbose; fi
|
||||
- if [ "$TARGET" != "x86_64-apple-ios" ]; then cargo test --target $TARGET --features icon_loading --verbose; fi
|
||||
|
||||
after_success:
|
||||
- |
|
||||
|
||||
54
CHANGELOG.md
54
CHANGELOG.md
@@ -1,5 +1,59 @@
|
||||
# Unreleased
|
||||
|
||||
# Version 0.18.0 (2018-11-07)
|
||||
|
||||
- **Breaking:** `image` crate upgraded to 0.20. This is exposed as part of the `icon_loading` API.
|
||||
- On Wayland, pointer events will now provide the current modifiers state.
|
||||
- On Wayland, titles will now be displayed in the window header decoration.
|
||||
- On Wayland, key repetition is now ended when keyboard loses focus.
|
||||
- On Wayland, windows will now use more stylish and modern client side decorations.
|
||||
- On Wayland, windows will use server-side decorations when available.
|
||||
- **Breaking:** Added support for F16-F24 keys (variants were added to the `VirtualKeyCode` enum).
|
||||
- Fixed graphical glitches when resizing on Wayland.
|
||||
- On Windows, fix freezes when performing certain actions after a window resize has been triggered. Reintroduces some visual artifacts when resizing.
|
||||
- Updated window manager hints under X11 to v1.5 of [Extended Window Manager Hints](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html#idm140200472629520).
|
||||
- Added `WindowBuilderExt::with_gtk_theme_variant` to X11-specific `WindowBuilder` functions.
|
||||
- Fixed UTF8 handling bug in X11 `set_title` function.
|
||||
- On Windows, `Window::set_cursor` now applies immediately instead of requiring specific events to occur first.
|
||||
- On Windows, the `HoveredFile` and `HoveredFileCancelled` events are now implemented.
|
||||
- On Windows, fix `Window::set_maximized`.
|
||||
- On Windows 10, fix transparency (#260).
|
||||
- On macOS, fix modifiers during key repeat.
|
||||
- Implemented the `Debug` trait for `Window`, `EventsLoop`, `EventsLoopProxy` and `WindowBuilder`.
|
||||
- On X11, now a `Resized` event will always be generated after a DPI change to ensure the window's logical size is consistent with the new DPI.
|
||||
- Added further clarifications to the DPI docs.
|
||||
- On Linux, if neither X11 nor Wayland manage to initialize, the corresponding panic now consists of a single line only.
|
||||
- Add optional `serde` feature with implementations of `Serialize`/`Deserialize` for DPI types and various event types.
|
||||
- Add `PartialEq`, `Eq`, and `Hash` implementations on public types that could have them but were missing them.
|
||||
- On X11, drag-and-drop receiving an unsupported drop type can no longer cause the WM to freeze.
|
||||
- Fix issue whereby the OpenGL context would not appear at startup on macOS Mojave (#1069).
|
||||
- **Breaking:** Removed `From<NSApplicationActivationPolicy>` impl from `ActivationPolicy` on macOS.
|
||||
- On macOS, the application can request the user's attention with `WindowExt::request_user_attention`.
|
||||
|
||||
# Version 0.17.2 (2018-08-19)
|
||||
|
||||
- On macOS, fix `<C-Tab>` so applications receive the event.
|
||||
- On macOS, fix `<Cmd-{key}>` so applications receive the event.
|
||||
- On Wayland, key press events will now be repeated.
|
||||
|
||||
# Version 0.17.1 (2018-08-05)
|
||||
|
||||
- On X11, prevent a compilation failure in release mode for versions of Rust greater than or equal to 1.30.
|
||||
- Fixed deadlock that broke fullscreen mode on Windows.
|
||||
|
||||
# Version 0.17.0 (2018-08-02)
|
||||
|
||||
- Cocoa and core-graphics updates.
|
||||
- Fixed thread-safety issues in several `Window` functions on Windows.
|
||||
- On MacOS, the key state for modifiers key events is now properly set.
|
||||
- On iOS, the view is now set correctly. This makes it possible to render things (instead of being stuck on a black screen), and touch events work again.
|
||||
- Added NetBSD support.
|
||||
- **Breaking:** On iOS, `UIView` is now the default root view. `WindowBuilderExt::with_root_view_class` can be used to set the root view objective-c class to `GLKView` (OpenGLES) or `MTKView` (Metal/MoltenVK).
|
||||
- On iOS, the `UIApplication` is not started until `Window::new` is called.
|
||||
- Fixed thread unsafety with cursor hiding on macOS.
|
||||
- On iOS, fixed the size of the `JmpBuf` type used for `setjmp`/`longjmp` calls. Previously this was a buffer overflow on most architectures.
|
||||
- On Windows, use cached window DPI instead of repeatedly querying the system. This fixes sporadic crashes on Windows 7.
|
||||
|
||||
# Version 0.16.2 (2018-07-07)
|
||||
|
||||
- On Windows, non-resizable windows now have the maximization button disabled. This is consistent with behavior on macOS and popular X11 WMs.
|
||||
|
||||
31
Cargo.toml
31
Cargo.toml
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.16.2"
|
||||
authors = ["The winit contributors, Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
version = "0.18.0"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
keywords = ["windowing"]
|
||||
license = "Apache-2.0"
|
||||
@@ -11,7 +11,7 @@ documentation = "https://docs.rs/winit"
|
||||
categories = ["gui"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["icon_loading"]
|
||||
features = ["icon_loading", "serde"]
|
||||
|
||||
[features]
|
||||
icon_loading = ["image"]
|
||||
@@ -20,42 +20,47 @@ icon_loading = ["image"]
|
||||
lazy_static = "1"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
image = { version = "0.19", optional = true }
|
||||
image = { version = "0.20.1", optional = true }
|
||||
serde = { version = "1", optional = true, features = ["serde_derive"] }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies.android_glue]
|
||||
version = "0.2"
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies]
|
||||
objc = "0.2"
|
||||
objc = "0.2.3"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
objc = "0.2"
|
||||
cocoa = "0.15"
|
||||
objc = "0.2.3"
|
||||
cocoa = "0.18.4"
|
||||
core-foundation = "0.6"
|
||||
core-graphics = "0.14"
|
||||
core-graphics = "0.17.3"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
features = [
|
||||
"combaseapi",
|
||||
"dwmapi",
|
||||
"errhandlingapi",
|
||||
"hidusage",
|
||||
"libloaderapi",
|
||||
"objbase",
|
||||
"ole2",
|
||||
"processthreadsapi",
|
||||
"shellapi",
|
||||
"shellscalingapi",
|
||||
"shobjidl_core",
|
||||
"unknwnbase",
|
||||
"winbase",
|
||||
"windowsx",
|
||||
"winerror",
|
||||
"wingdi",
|
||||
"winnt",
|
||||
"winuser",
|
||||
]
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies]
|
||||
wayland-client = { version = "0.20.6", features = [ "dlopen", "egl", "cursor"] }
|
||||
smithay-client-toolkit = "0.2.2"
|
||||
x11-dl = "2.17.5"
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
|
||||
wayland-client = { version = "0.21", features = [ "dlopen", "egl", "cursor"] }
|
||||
smithay-client-toolkit = "0.4"
|
||||
x11-dl = "2.18.3"
|
||||
parking_lot = "0.6"
|
||||
percent-encoding = "1.0"
|
||||
|
||||
10
README.md
10
README.md
@@ -1,15 +1,13 @@
|
||||
# winit - Cross-platform window creation and management in Rust
|
||||
|
||||
[](https://crates.io/crates/winit)
|
||||
|
||||
[](https://docs.rs/winit)
|
||||
|
||||
[](https://travis-ci.org/tomaka/winit)
|
||||
[](https://ci.appveyor.com/project/tomaka/winit/branch/master)
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.16"
|
||||
winit = "0.18"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
@@ -43,6 +41,12 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
### Cargo Features
|
||||
|
||||
Winit provides the following features, which can be enabled in your `Cargo.toml` file:
|
||||
* `icon_loading`: Enables loading window icons directly from files. Depends on the [`image` crate](https://crates.io/crates/image).
|
||||
* `serde`: Enables serialization/deserialization of certain types with [Serde](https://crates.io/crates/serde).
|
||||
|
||||
### Platform-specific usage
|
||||
|
||||
#### Emscripten and WebAssembly
|
||||
|
||||
@@ -22,3 +22,5 @@ build: false
|
||||
|
||||
test_script:
|
||||
- cargo test --verbose
|
||||
- cargo test --features serde --verbose
|
||||
- cargo test --features icon_loading --verbose
|
||||
|
||||
@@ -6,10 +6,9 @@ extern crate winit;
|
||||
#[cfg(feature = "icon_loading")]
|
||||
extern crate image;
|
||||
|
||||
use winit::Icon;
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
fn main() {
|
||||
use winit::Icon;
|
||||
// You'll have to choose an icon size at your own discretion. On X11, the desired size varies
|
||||
// by WM, and on Windows, you still have to account for screen scaling. Here we use 32px,
|
||||
// since it seems to work well enough in most cases. Be careful about going too high, or
|
||||
@@ -37,7 +36,7 @@ fn main() {
|
||||
match event {
|
||||
CloseRequested => return winit::ControlFlow::Break,
|
||||
DroppedFile(path) => {
|
||||
use image::GenericImage;
|
||||
use image::GenericImageView;
|
||||
|
||||
let icon_image = image::open(path).expect("Failed to open window icon");
|
||||
|
||||
|
||||
42
src/dpi.rs
42
src/dpi.rs
@@ -19,19 +19,29 @@
|
||||
//! small.
|
||||
//!
|
||||
//! That's a description of what happens when the button is 100x100 *physical* pixels. Instead, let's try using 100x100
|
||||
//! *logical* pixels. To map logical pixels to physical pixels, we simply multiply by the DPI (dots per inch) factor.
|
||||
//! On a "typical" desktop display, the DPI factor will be 1.0, so 100x100 logical pixels equates to 100x100 physical
|
||||
//! pixels. However, a 1440p display may have a DPI factor of 1.25, so the button is rendered as 125x125 physical pixels.
|
||||
//! *logical* pixels. To map logical pixels to physical pixels, we simply multiply by the DPI (dots per inch) factor.
|
||||
//! On a "typical" desktop display, the DPI factor will be 1.0, so 100x100 logical pixels equates to 100x100 physical
|
||||
//! pixels. However, a 1440p display may have a DPI factor of 1.25, so the button is rendered as 125x125 physical pixels.
|
||||
//! Ideally, the button now has approximately the same perceived size across varying displays.
|
||||
//!
|
||||
//! Failure to account for the DPI factor can create a badly degraded user experience. Most notably, it can make users
|
||||
//! feel like they have bad eyesight, which will potentially cause them to think about growing elderly, resulting in
|
||||
//! them entering an existential panic. Once users enter that state, they will no longer be focused on your application.
|
||||
//!
|
||||
//! There are two ways to get the DPI factor: either by calling
|
||||
//! [`MonitorId::get_hidpi_factor`](../struct.MonitorId.html#method.get_hidpi_factor), or
|
||||
//! [`Window::get_hidpi_factor`](../struct.Window.html#method.get_hidpi_factor). You'll almost always use the latter,
|
||||
//! which is basically equivalent to `window.get_current_monitor().get_hidpi_factor()` anyway.
|
||||
//! There are two ways to get the DPI factor:
|
||||
//! - You can track the [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) event of your
|
||||
//! windows. This event is sent any time the DPI factor changes, either because the window moved to another monitor,
|
||||
//! or because the user changed the configuration of their screen.
|
||||
//! - You can also retrieve the DPI factor of a monitor by calling
|
||||
//! [`MonitorId::get_hidpi_factor`](../struct.MonitorId.html#method.get_hidpi_factor), or the
|
||||
//! current DPI factor applied to a window by calling
|
||||
//! [`Window::get_hidpi_factor`](../struct.Window.html#method.get_hidpi_factor), which is roughly equivalent
|
||||
//! to `window.get_current_monitor().get_hidpi_factor()`.
|
||||
//!
|
||||
//! Depending on the platform, the window's actual DPI factor may only be known after
|
||||
//! the event loop has started and your window has been drawn once. To properly handle these cases,
|
||||
//! the most robust way is to monitor the [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged)
|
||||
//! event and dynamically adapt your drawing logic to follow the DPI factor.
|
||||
//!
|
||||
//! Here's an overview of what sort of DPI factors you can expect, and where they come from:
|
||||
//! - **Windows:** On Windows 8 and 10, per-monitor scaling is readily configured by users from the display settings.
|
||||
@@ -43,18 +53,28 @@
|
||||
//! - **X11:** On X11, we calcuate the DPI factor based on the millimeter dimensions provided by XRandR. This can
|
||||
//! result in a wide range of possible values, including some interesting ones like 1.0833333333333333. This can be
|
||||
//! overridden using the `WINIT_HIDPI_FACTOR` environment variable, though that's not recommended.
|
||||
//! - **Wayland:** On Wayland, DPI factors are very much at the discretion of the user.
|
||||
//! - **Wayland:** On Wayland, DPI factors are set per-screen by the server, and are always integers (most often 1 or 2).
|
||||
//! - **iOS:** DPI factors are both constant and device-specific on iOS.
|
||||
//! - **Android:** This feature isn't yet implemented on Android, so the DPI factor will always be returned as 1.0.
|
||||
//!
|
||||
//! The window's logical size is conserved across DPI changes, resulting in the physical size changing instead. This
|
||||
//! may be surprising on X11, but is quite standard elsewhere. Physical size changes produce a
|
||||
//! may be surprising on X11, but is quite standard elsewhere. Physical size changes always produce a
|
||||
//! [`Resized`](../enum.WindowEvent.html#variant.Resized) event, even on platforms where no resize actually occurs,
|
||||
//! such as macOS and Wayland. As a result, it's not necessary to separately handle
|
||||
//! [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) if you're only listening for size.
|
||||
//!
|
||||
//! Your GPU has no awareness of the concept of logical pixels, and unless you like wasting pixel density, your
|
||||
//! framebuffer's size should be in physical pixels.
|
||||
//!
|
||||
//! `winit` will send [`Resized`](../enum.WindowEvent.html#variant.Resized) events whenever a window's logical size
|
||||
//! changes, and [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) events
|
||||
//! whenever the DPI factor changes. Receiving either of these events means that the physical size of your window has
|
||||
//! changed, and you should recompute it using the latest values you received for each. If the logical size and the
|
||||
//! DPI factor change simultaneously, `winit` will send both events together; thus, it's recommended to buffer
|
||||
//! these events and process them at the end of the queue.
|
||||
//!
|
||||
//! If you never received any [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) events,
|
||||
//! then your window's DPI factor is 1.
|
||||
|
||||
/// Checks that the DPI factor is a normal positive `f64`.
|
||||
///
|
||||
@@ -72,6 +92,7 @@ pub fn validate_hidpi_factor(dpi_factor: f64) -> bool {
|
||||
/// which can cause noticable issues. To help with that, an `Into<(i32, i32)>` implementation is provided which
|
||||
/// does the rounding for you.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct LogicalPosition {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
@@ -132,6 +153,7 @@ impl Into<(i32, i32)> for LogicalPosition {
|
||||
/// which can cause noticable issues. To help with that, an `Into<(i32, i32)>` implementation is provided which
|
||||
/// does the rounding for you.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct PhysicalPosition {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
@@ -192,6 +214,7 @@ impl Into<(i32, i32)> for PhysicalPosition {
|
||||
/// which can cause noticable issues. To help with that, an `Into<(u32, u32)>` implementation is provided which
|
||||
/// does the rounding for you.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct LogicalSize {
|
||||
pub width: f64,
|
||||
pub height: f64,
|
||||
@@ -252,6 +275,7 @@ impl Into<(u32, u32)> for LogicalSize {
|
||||
/// which can cause noticable issues. To help with that, an `Into<(u32, u32)>` implementation is provided which
|
||||
/// does the rounding for you.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct PhysicalSize {
|
||||
pub width: f64,
|
||||
pub height: f64,
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::path::PathBuf;
|
||||
use {DeviceId, LogicalPosition, LogicalSize, WindowId};
|
||||
|
||||
/// Describes a generic event.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Event {
|
||||
WindowEvent {
|
||||
window_id: WindowId,
|
||||
@@ -22,7 +22,7 @@ pub enum Event {
|
||||
}
|
||||
|
||||
/// Describes an event from a `Window`.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum WindowEvent {
|
||||
/// The size of the window has changed. Contains the client area's new dimensions.
|
||||
Resized(LogicalSize),
|
||||
@@ -116,7 +116,7 @@ pub enum WindowEvent {
|
||||
/// may not match.
|
||||
///
|
||||
/// Note that these events are delivered regardless of input focus.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum DeviceEvent {
|
||||
Added,
|
||||
Removed,
|
||||
@@ -147,7 +147,8 @@ pub enum DeviceEvent {
|
||||
}
|
||||
|
||||
/// Describes a keyboard input event.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct KeyboardInput {
|
||||
/// Identifies the physical key pressed
|
||||
///
|
||||
@@ -173,6 +174,7 @@ pub struct KeyboardInput {
|
||||
|
||||
/// Describes touch-screen input state.
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum TouchPhase {
|
||||
Started,
|
||||
Moved,
|
||||
@@ -195,7 +197,7 @@ pub enum TouchPhase {
|
||||
/// as previously received End event is a new finger and has nothing to do with an old one.
|
||||
///
|
||||
/// Touch may be cancelled if for example window lost focus.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Touch {
|
||||
pub device_id: DeviceId,
|
||||
pub phase: TouchPhase,
|
||||
@@ -215,6 +217,7 @@ pub type ButtonId = u32;
|
||||
|
||||
/// Describes the input state of a key.
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ElementState {
|
||||
Pressed,
|
||||
Released,
|
||||
@@ -222,6 +225,7 @@ pub enum ElementState {
|
||||
|
||||
/// Describes a button of a mouse controller.
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum MouseButton {
|
||||
Left,
|
||||
Right,
|
||||
@@ -231,6 +235,7 @@ pub enum MouseButton {
|
||||
|
||||
/// Describes a difference in the mouse scroll wheel state.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum MouseScrollDelta {
|
||||
/// Amount in lines or rows to scroll in the horizontal
|
||||
/// and vertical directions.
|
||||
@@ -250,6 +255,7 @@ pub enum MouseScrollDelta {
|
||||
/// Symbolic name for a keyboard key.
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
#[repr(u32)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum VirtualKeyCode {
|
||||
/// The '1' key over the letters.
|
||||
Key1,
|
||||
@@ -317,6 +323,15 @@ pub enum VirtualKeyCode {
|
||||
F13,
|
||||
F14,
|
||||
F15,
|
||||
F16,
|
||||
F17,
|
||||
F18,
|
||||
F19,
|
||||
F20,
|
||||
F21,
|
||||
F22,
|
||||
F23,
|
||||
F24,
|
||||
|
||||
/// Print Screen/SysRq.
|
||||
Snapshot,
|
||||
@@ -440,6 +455,8 @@ pub enum VirtualKeyCode {
|
||||
///
|
||||
/// Each field of this struct represents a modifier and is `true` if this modifier is active.
|
||||
#[derive(Default, Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct ModifiersState {
|
||||
/// The "shift" key
|
||||
pub shift: bool,
|
||||
|
||||
@@ -146,7 +146,7 @@ impl Icon {
|
||||
/// Requires the `icon_loading` feature.
|
||||
impl From<image::DynamicImage> for Icon {
|
||||
fn from(image: image::DynamicImage) -> Self {
|
||||
use image::{GenericImage, Pixel};
|
||||
use image::{GenericImageView, Pixel};
|
||||
let (width, height) = image.dimensions();
|
||||
let mut rgba = Vec::with_capacity((width * height) as usize * PIXEL_SIZE);
|
||||
for (_, _, pixel) in image.pixels() {
|
||||
|
||||
51
src/lib.rs
51
src/lib.rs
@@ -25,11 +25,11 @@
|
||||
//! Once a window has been created, it will *generate events*. For example whenever the user moves
|
||||
//! the window, resizes the window, moves the mouse, etc. an event is generated.
|
||||
//!
|
||||
//! The events generated by a window can be retreived from the `EventsLoop` the window was created
|
||||
//! The events generated by a window can be retrieved from the `EventsLoop` the window was created
|
||||
//! with.
|
||||
//!
|
||||
//! There are two ways to do so. The first is to call `events_loop.poll_events(...)`, which will
|
||||
//! retreive all the events pending on the windows and immediately return after no new event is
|
||||
//! retrieve all the events pending on the windows and immediately return after no new event is
|
||||
//! available. You usually want to use this method in application that render continuously on the
|
||||
//! screen, such as video games.
|
||||
//!
|
||||
@@ -80,7 +80,7 @@
|
||||
//! # Drawing on the window
|
||||
//!
|
||||
//! Winit doesn't provide any function that allows drawing on a window. However it allows you to
|
||||
//! retreive the raw handle of the window (see the `os` module for that), which in turn allows you
|
||||
//! retrieve the raw handle of the window (see the `os` module for that), which in turn allows you
|
||||
//! to create an OpenGL/Vulkan/DirectX/Metal/etc. context that will draw on the window.
|
||||
//!
|
||||
|
||||
@@ -92,6 +92,9 @@ extern crate libc;
|
||||
extern crate log;
|
||||
#[cfg(feature = "icon_loading")]
|
||||
extern crate image;
|
||||
#[cfg(feature = "serde")]
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate winapi;
|
||||
@@ -104,13 +107,13 @@ extern crate cocoa;
|
||||
extern crate core_foundation;
|
||||
#[cfg(target_os = "macos")]
|
||||
extern crate core_graphics;
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
||||
extern crate x11_dl;
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
||||
extern crate parking_lot;
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
||||
extern crate percent_encoding;
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
||||
extern crate smithay_client_toolkit as sctk;
|
||||
|
||||
pub(crate) use dpi::*; // TODO: Actually change the imports throughout the codebase.
|
||||
@@ -149,6 +152,12 @@ pub struct Window {
|
||||
window: platform::Window,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Window {
|
||||
fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
fmtr.pad("Window { .. }")
|
||||
}
|
||||
}
|
||||
|
||||
/// Identifier of a window. Unique for each window.
|
||||
///
|
||||
/// Can be obtained with `window.id()`.
|
||||
@@ -166,7 +175,7 @@ pub struct WindowId(platform::WindowId);
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId(platform::DeviceId);
|
||||
|
||||
/// Provides a way to retreive events from the system and from the windows that were registered to
|
||||
/// Provides a way to retrieve events from the system and from the windows that were registered to
|
||||
/// the events loop.
|
||||
///
|
||||
/// An `EventsLoop` can be seen more or less as a "context". Calling `EventsLoop::new()`
|
||||
@@ -184,10 +193,17 @@ pub struct EventsLoop {
|
||||
_marker: ::std::marker::PhantomData<*mut ()> // Not Send nor Sync
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for EventsLoop {
|
||||
fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
fmtr.pad("EventsLoop { .. }")
|
||||
}
|
||||
}
|
||||
|
||||
/// Returned by the user callback given to the `EventsLoop::run_forever` method.
|
||||
///
|
||||
/// Indicates whether the `run_forever` method should continue or complete.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ControlFlow {
|
||||
/// Continue looping and waiting for events.
|
||||
Continue,
|
||||
@@ -263,6 +279,12 @@ pub struct EventsLoopProxy {
|
||||
events_loop_proxy: platform::EventsLoopProxy,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for EventsLoopProxy {
|
||||
fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
fmtr.pad("EventsLoopProxy { .. }")
|
||||
}
|
||||
}
|
||||
|
||||
impl EventsLoopProxy {
|
||||
/// Wake up the `EventsLoop` from which this proxy was created.
|
||||
///
|
||||
@@ -301,6 +323,14 @@ pub struct WindowBuilder {
|
||||
platform_specific: platform::PlatformSpecificWindowBuilderAttributes,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WindowBuilder {
|
||||
fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
fmtr.debug_struct("WindowBuilder")
|
||||
.field("window", &self.window)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Error that can happen while creating a window or a headless renderer.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CreationError {
|
||||
@@ -331,7 +361,8 @@ impl std::error::Error for CreationError {
|
||||
}
|
||||
|
||||
/// Describes the appearance of the mouse cursor.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum MouseCursor {
|
||||
/// The platform-dependent default cursor.
|
||||
Default,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use {MonitorId, Window};
|
||||
use {MonitorId, Window, WindowBuilder};
|
||||
|
||||
/// Additional methods on `Window` that are specific to iOS.
|
||||
pub trait WindowExt {
|
||||
@@ -29,6 +29,22 @@ impl WindowExt for Window {
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to iOS.
|
||||
pub trait WindowBuilderExt {
|
||||
/// Sets the root view class used by the `Window`, otherwise a barebones `UIView` is provided.
|
||||
///
|
||||
/// The class will be initialized by calling `[root_view initWithFrame:CGRect]`
|
||||
fn with_root_view_class(self, root_view_class: *const c_void) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExt for WindowBuilder {
|
||||
#[inline]
|
||||
fn with_root_view_class(mut self, root_view_class: *const c_void) -> WindowBuilder {
|
||||
self.platform_specific.root_view_class = unsafe { &*(root_view_class as *const _) };
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorId` that are specific to iOS.
|
||||
pub trait MonitorIdExt {
|
||||
/// Returns a pointer to the `UIScreen` that is used by this monitor.
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#![cfg(target_os = "macos")]
|
||||
|
||||
use std::convert::From;
|
||||
use std::os::raw::c_void;
|
||||
use cocoa::appkit::NSApplicationActivationPolicy;
|
||||
use {LogicalSize, MonitorId, Window, WindowBuilder};
|
||||
|
||||
/// Additional methods on `Window` that are specific to MacOS.
|
||||
@@ -16,6 +14,14 @@ pub trait WindowExt {
|
||||
///
|
||||
/// The pointer will become invalid when the `Window` is destroyed.
|
||||
fn get_nsview(&self) -> *mut c_void;
|
||||
|
||||
/// Request user attention, causing the application's dock icon to bounce.
|
||||
/// Note that this has no effect if the application is already focused.
|
||||
///
|
||||
/// The `is_critical` flag has the following effects:
|
||||
/// - `false`: the dock icon will only bounce once.
|
||||
/// - `true`: the dock icon will bounce until the application is focused.
|
||||
fn request_user_attention(&self, is_critical: bool);
|
||||
}
|
||||
|
||||
impl WindowExt for Window {
|
||||
@@ -28,6 +34,11 @@ impl WindowExt for Window {
|
||||
fn get_nsview(&self) -> *mut c_void {
|
||||
self.window.get_nsview()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn request_user_attention(&self, is_critical: bool) {
|
||||
self.window.request_user_attention(is_critical)
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to `NSApplicationActivationPolicy`.
|
||||
@@ -47,19 +58,6 @@ impl Default for ActivationPolicy {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ActivationPolicy> for NSApplicationActivationPolicy {
|
||||
fn from(activation_policy: ActivationPolicy) -> Self {
|
||||
match activation_policy {
|
||||
ActivationPolicy::Regular =>
|
||||
NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
|
||||
ActivationPolicy::Accessory =>
|
||||
NSApplicationActivationPolicy::NSApplicationActivationPolicyAccessory,
|
||||
ActivationPolicy::Prohibited =>
|
||||
NSApplicationActivationPolicy::NSApplicationActivationPolicyProhibited,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to MacOS.
|
||||
///
|
||||
/// **Note:** Properties dealing with the titlebar will be overwritten by the `with_decorations` method
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
||||
|
||||
use std::os::raw;
|
||||
use std::ptr;
|
||||
@@ -219,6 +219,8 @@ pub trait WindowBuilderExt {
|
||||
fn with_override_redirect(self, override_redirect: bool) -> WindowBuilder;
|
||||
/// Build window with `_NET_WM_WINDOW_TYPE` hint; defaults to `Normal`. Only relevant on X11.
|
||||
fn with_x11_window_type(self, x11_window_type: XWindowType) -> WindowBuilder;
|
||||
/// Build window with `_GTK_THEME_VARIANT` hint set to the specified value. Currently only relevant on X11.
|
||||
fn with_gtk_theme_variant(self, variant: String) -> WindowBuilder;
|
||||
/// Build window with resize increment hint. Only implemented on X11.
|
||||
fn with_resize_increments(self, increments: LogicalSize) -> WindowBuilder;
|
||||
/// Build window with base size hint. Only implemented on X11.
|
||||
@@ -269,6 +271,12 @@ impl WindowBuilderExt for WindowBuilder {
|
||||
self.platform_specific.base_size = Some(base_size.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_gtk_theme_variant(mut self, variant: String) -> WindowBuilder {
|
||||
self.platform_specific.gtk_theme_variant = Some(variant);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorId` that are specific to Linux.
|
||||
|
||||
@@ -810,11 +810,15 @@ fn key_translate_virt(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES
|
||||
"F13" => Some(F13),
|
||||
"F14" => Some(F14),
|
||||
"F15" => Some(F15),
|
||||
"F16" => None,
|
||||
"F17" => None,
|
||||
"F18" => None,
|
||||
"F19" => None,
|
||||
"F20" => None,
|
||||
"F16" => Some(F16),
|
||||
"F17" => Some(F17),
|
||||
"F18" => Some(F18),
|
||||
"F19" => Some(F19),
|
||||
"F20" => Some(F20),
|
||||
"F21" => Some(F21),
|
||||
"F22" => Some(F22),
|
||||
"F23" => Some(F23),
|
||||
"F24" => Some(F24),
|
||||
"Soft1" => None,
|
||||
"Soft2" => None,
|
||||
"Soft3" => None,
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::mem;
|
||||
use std::os::raw::*;
|
||||
|
||||
use objc::runtime::{Class, Object};
|
||||
use objc::runtime::Object;
|
||||
|
||||
pub type id = *mut Object;
|
||||
pub const nil: id = 0 as id;
|
||||
@@ -15,19 +14,11 @@ pub type Boolean = u32;
|
||||
|
||||
pub const kCFRunLoopRunHandledSource: i32 = 4;
|
||||
|
||||
pub const UIViewAutoresizingFlexibleWidth: NSUInteger = 1 << 1;
|
||||
pub const UIViewAutoresizingFlexibleHeight: NSUInteger = 1 << 4;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
pub type CGFloat = f32;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
pub type CGFloat = f64;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
pub type NSUInteger = u32;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
pub type NSUInteger = u64;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CGPoint {
|
||||
@@ -73,12 +64,24 @@ extern {
|
||||
|
||||
extern {
|
||||
pub fn setjmp(env: *mut c_void) -> c_int;
|
||||
pub fn longjmp(env: *mut c_void, val: c_int);
|
||||
pub fn longjmp(env: *mut c_void, val: c_int) -> !;
|
||||
}
|
||||
|
||||
// values taken from "setjmp.h" header in xcode iPhoneOS/iPhoneSimulator SDK
|
||||
#[cfg(any(target_arch = "x86_64"))]
|
||||
pub const JBLEN: usize = (9 * 2) + 3 + 16;
|
||||
#[cfg(any(target_arch = "x86"))]
|
||||
pub const JBLEN: usize = 18;
|
||||
#[cfg(target_arch = "arm")]
|
||||
pub const JBLEN: usize = 10 + 16 + 2;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub const JBLEN: usize = (14 + 8 + 2) * 2;
|
||||
|
||||
pub type JmpBuf = [c_int; JBLEN];
|
||||
|
||||
pub trait NSString: Sized {
|
||||
unsafe fn alloc(_: Self) -> id {
|
||||
msg_send![class("NSString"), alloc]
|
||||
msg_send![class!(NSString), alloc]
|
||||
}
|
||||
|
||||
unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id;
|
||||
@@ -105,10 +108,3 @@ impl NSString for id {
|
||||
msg_send![self, UTF8String]
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn class(name: &str) -> *mut Class {
|
||||
unsafe {
|
||||
mem::transmute(Class::get(name))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,8 +61,10 @@
|
||||
#![cfg(target_os = "ios")]
|
||||
|
||||
use std::{fmt, mem, ptr};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::os::raw::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::{BOOL, Class, Object, Sel, YES};
|
||||
@@ -90,6 +92,8 @@ use self::ffi::{
|
||||
CGPoint,
|
||||
CGRect,
|
||||
id,
|
||||
JBLEN,
|
||||
JmpBuf,
|
||||
kCFRunLoopDefaultMode,
|
||||
kCFRunLoopRunHandledSource,
|
||||
longjmp,
|
||||
@@ -97,14 +101,13 @@ use self::ffi::{
|
||||
NSString,
|
||||
setjmp,
|
||||
UIApplicationMain,
|
||||
UIViewAutoresizingFlexibleWidth,
|
||||
UIViewAutoresizingFlexibleHeight,
|
||||
};
|
||||
|
||||
static mut JMPBUF: [c_int; 27] = [0; 27];
|
||||
static mut JMPBUF: Option<Box<JmpBuf>> = None;
|
||||
|
||||
pub struct Window {
|
||||
delegate_state: *mut DelegateState,
|
||||
_events_queue: Arc<RefCell<VecDeque<Event>>>,
|
||||
delegate_state: Box<DelegateState>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Window {}
|
||||
@@ -112,7 +115,6 @@ unsafe impl Sync for Window {}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DelegateState {
|
||||
events_queue: VecDeque<Event>,
|
||||
window: id,
|
||||
controller: id,
|
||||
view: id,
|
||||
@@ -123,7 +125,6 @@ struct DelegateState {
|
||||
impl DelegateState {
|
||||
fn new(window: id, controller: id, view: id, size: LogicalSize, scale: f64) -> DelegateState {
|
||||
DelegateState {
|
||||
events_queue: VecDeque::new(),
|
||||
window,
|
||||
controller,
|
||||
view,
|
||||
@@ -170,7 +171,7 @@ impl fmt::Debug for MonitorId {
|
||||
impl MonitorId {
|
||||
#[inline]
|
||||
pub fn get_uiscreen(&self) -> id {
|
||||
let class = Class::get("UIScreen").expect("Failed to get class `UIScreen`");
|
||||
let class = class!(UIScreen);
|
||||
unsafe { msg_send![class, mainScreen] }
|
||||
}
|
||||
|
||||
@@ -199,7 +200,7 @@ impl MonitorId {
|
||||
}
|
||||
|
||||
pub struct EventsLoop {
|
||||
delegate_state: *mut DelegateState,
|
||||
events_queue: Arc<RefCell<VecDeque<Event>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -208,21 +209,11 @@ pub struct EventsLoopProxy;
|
||||
impl EventsLoop {
|
||||
pub fn new() -> EventsLoop {
|
||||
unsafe {
|
||||
if setjmp(mem::transmute(&mut JMPBUF)) != 0 {
|
||||
let app_class = Class::get("UIApplication").expect("Failed to get class `UIApplication`");
|
||||
let app: id = msg_send![app_class, sharedApplication];
|
||||
let delegate: id = msg_send![app, delegate];
|
||||
let state: *mut c_void = *(&*delegate).get_ivar("winitState");
|
||||
let delegate_state = state as *mut DelegateState;
|
||||
return EventsLoop { delegate_state };
|
||||
if !msg_send![class!(NSThread), isMainThread] {
|
||||
panic!("`EventsLoop` can only be created on the main thread on iOS");
|
||||
}
|
||||
}
|
||||
|
||||
create_view_class();
|
||||
create_delegate_class();
|
||||
start_app();
|
||||
|
||||
panic!("Couldn't create `UIApplication`!")
|
||||
EventsLoop { events_queue: Default::default() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -240,29 +231,30 @@ impl EventsLoop {
|
||||
pub fn poll_events<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
if let Some(event) = self.events_queue.borrow_mut().pop_front() {
|
||||
callback(event);
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let state = &mut *self.delegate_state;
|
||||
|
||||
if let Some(event) = state.events_queue.pop_front() {
|
||||
callback(event);
|
||||
return;
|
||||
}
|
||||
|
||||
// jump hack, so we won't quit on willTerminate event before processing it
|
||||
if setjmp(mem::transmute(&mut JMPBUF)) != 0 {
|
||||
if let Some(event) = state.events_queue.pop_front() {
|
||||
assert!(JMPBUF.is_some(), "`EventsLoop::poll_events` must be called after window creation on iOS");
|
||||
if setjmp(mem::transmute_copy(&mut JMPBUF)) != 0 {
|
||||
if let Some(event) = self.events_queue.borrow_mut().pop_front() {
|
||||
callback(event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// run runloop
|
||||
let seconds: CFTimeInterval = 0.000002;
|
||||
while CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, 1) == kCFRunLoopRunHandledSource {}
|
||||
}
|
||||
|
||||
if let Some(event) = state.events_queue.pop_front() {
|
||||
callback(event)
|
||||
}
|
||||
if let Some(event) = self.events_queue.borrow_mut().pop_front() {
|
||||
callback(event)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,8 +293,18 @@ pub struct WindowId;
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes;
|
||||
#[derive(Clone)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub root_view_class: &'static Class,
|
||||
}
|
||||
|
||||
impl Default for PlatformSpecificWindowBuilderAttributes {
|
||||
fn default() -> Self {
|
||||
PlatformSpecificWindowBuilderAttributes {
|
||||
root_view_class: class!(UIView),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: AFAIK transparency is enabled by default on iOS,
|
||||
// so to be consistent with other platforms we have to change that.
|
||||
@@ -310,19 +312,60 @@ impl Window {
|
||||
pub fn new(
|
||||
ev: &EventsLoop,
|
||||
_attributes: WindowAttributes,
|
||||
_pl_alltributes: PlatformSpecificWindowBuilderAttributes,
|
||||
pl_attributes: PlatformSpecificWindowBuilderAttributes,
|
||||
) -> Result<Window, CreationError> {
|
||||
Ok(Window { delegate_state: ev.delegate_state })
|
||||
unsafe {
|
||||
debug_assert!(mem::size_of_val(&JMPBUF) == mem::size_of::<Box<JmpBuf>>());
|
||||
assert!(mem::replace(&mut JMPBUF, Some(Box::new([0; JBLEN]))).is_none(), "Only one `Window` is supported on iOS");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
if setjmp(mem::transmute_copy(&mut JMPBUF)) != 0 {
|
||||
let app_class = class!(UIApplication);
|
||||
let app: id = msg_send![app_class, sharedApplication];
|
||||
let delegate: id = msg_send![app, delegate];
|
||||
let state: *mut c_void = *(&*delegate).get_ivar("winitState");
|
||||
let mut delegate_state = Box::from_raw(state as *mut DelegateState);
|
||||
let events_queue = &*ev.events_queue;
|
||||
(&mut *delegate).set_ivar("eventsQueue", mem::transmute::<_, *mut c_void>(events_queue));
|
||||
|
||||
// easiest? way to get access to PlatformSpecificWindowBuilderAttributes to configure the view
|
||||
let rect: CGRect = msg_send![MonitorId.get_uiscreen(), bounds];
|
||||
|
||||
let uiview_class = class!(UIView);
|
||||
let root_view_class = pl_attributes.root_view_class;
|
||||
let is_uiview: BOOL = msg_send![root_view_class, isSubclassOfClass:uiview_class];
|
||||
assert!(is_uiview == YES, "`root_view_class` must inherit from `UIView`");
|
||||
|
||||
delegate_state.view = msg_send![root_view_class, alloc];
|
||||
assert!(!delegate_state.view.is_null(), "Failed to create `UIView` instance");
|
||||
delegate_state.view = msg_send![delegate_state.view, initWithFrame:rect];
|
||||
assert!(!delegate_state.view.is_null(), "Failed to initialize `UIView` instance");
|
||||
|
||||
let _: () = msg_send![delegate_state.controller, setView:delegate_state.view];
|
||||
let _: () = msg_send![delegate_state.window, makeKeyAndVisible];
|
||||
|
||||
return Ok(Window {
|
||||
_events_queue: ev.events_queue.clone(),
|
||||
delegate_state,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
create_delegate_class();
|
||||
start_app();
|
||||
|
||||
panic!("Couldn't create `UIApplication`!")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_uiwindow(&self) -> id {
|
||||
unsafe { (*self.delegate_state).window }
|
||||
self.delegate_state.window
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_uiview(&self) -> id {
|
||||
unsafe { (*self.delegate_state).view }
|
||||
self.delegate_state.view
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -359,7 +402,7 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||
unsafe { Some((&*self.delegate_state).size) }
|
||||
Some(self.delegate_state.size)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -404,7 +447,7 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
unsafe { (&*self.delegate_state) }.scale
|
||||
self.delegate_state.scale
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -469,10 +512,9 @@ impl Window {
|
||||
|
||||
fn create_delegate_class() {
|
||||
extern fn did_finish_launching(this: &mut Object, _: Sel, _: id, _: id) -> BOOL {
|
||||
let screen_class = Class::get("UIScreen").expect("Failed to get class `UIScreen`");
|
||||
let window_class = Class::get("UIWindow").expect("Failed to get class `UIWindow`");
|
||||
let controller_class = Class::get("MainViewController").expect("Failed to get class `MainViewController`");
|
||||
let view_class = Class::get("MainView").expect("Failed to get class `MainView`");
|
||||
let screen_class = class!(UIScreen);
|
||||
let window_class = class!(UIWindow);
|
||||
let controller_class = class!(UIViewController);
|
||||
unsafe {
|
||||
let main_screen: id = msg_send![screen_class, mainScreen];
|
||||
let bounds: CGRect = msg_send![main_screen, bounds];
|
||||
@@ -486,30 +528,28 @@ fn create_delegate_class() {
|
||||
let view_controller: id = msg_send![controller_class, alloc];
|
||||
let view_controller: id = msg_send![view_controller, init];
|
||||
|
||||
let view: id = msg_send![view_class, alloc];
|
||||
let view: id = msg_send![view, initForGl:&bounds];
|
||||
|
||||
let _: () = msg_send![window, setRootViewController:view_controller];
|
||||
let _: () = msg_send![window, makeKeyAndVisible];
|
||||
|
||||
let state = Box::new(DelegateState::new(window, view_controller, view, size, scale as f64));
|
||||
let state = Box::new(DelegateState::new(window, view_controller, ptr::null_mut(), size, scale as f64));
|
||||
let state_ptr: *mut DelegateState = mem::transmute(state);
|
||||
this.set_ivar("winitState", state_ptr as *mut c_void);
|
||||
|
||||
// The `UIView` is setup in `Window::new` which gets `longjmp`'ed to here.
|
||||
// This makes it easier to configure the specific `UIView` type.
|
||||
let _: () = msg_send![this, performSelector:sel!(postLaunch:) withObject:nil afterDelay:0.0];
|
||||
}
|
||||
YES
|
||||
}
|
||||
|
||||
extern fn post_launch(_: &Object, _: Sel, _: id) {
|
||||
unsafe { longjmp(mem::transmute(&mut JMPBUF),1); }
|
||||
unsafe { longjmp(mem::transmute_copy(&mut JMPBUF), 1); }
|
||||
}
|
||||
|
||||
extern fn did_become_active(this: &Object, _: Sel, _: id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
state.events_queue.push_back(Event::WindowEvent {
|
||||
let events_queue: *mut c_void = *this.get_ivar("eventsQueue");
|
||||
let events_queue = &*(events_queue as *const RefCell<VecDeque<Event>>);
|
||||
events_queue.borrow_mut().push_back(Event::WindowEvent {
|
||||
window_id: RootEventId(WindowId),
|
||||
event: WindowEvent::Focused(true),
|
||||
});
|
||||
@@ -518,9 +558,9 @@ fn create_delegate_class() {
|
||||
|
||||
extern fn will_resign_active(this: &Object, _: Sel, _: id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
state.events_queue.push_back(Event::WindowEvent {
|
||||
let events_queue: *mut c_void = *this.get_ivar("eventsQueue");
|
||||
let events_queue = &*(events_queue as *const RefCell<VecDeque<Event>>);
|
||||
events_queue.borrow_mut().push_back(Event::WindowEvent {
|
||||
window_id: RootEventId(WindowId),
|
||||
event: WindowEvent::Focused(false),
|
||||
});
|
||||
@@ -529,38 +569,38 @@ fn create_delegate_class() {
|
||||
|
||||
extern fn will_enter_foreground(this: &Object, _: Sel, _: id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
state.events_queue.push_back(Event::Suspended(false));
|
||||
let events_queue: *mut c_void = *this.get_ivar("eventsQueue");
|
||||
let events_queue = &*(events_queue as *const RefCell<VecDeque<Event>>);
|
||||
events_queue.borrow_mut().push_back(Event::Suspended(false));
|
||||
}
|
||||
}
|
||||
|
||||
extern fn did_enter_background(this: &Object, _: Sel, _: id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
state.events_queue.push_back(Event::Suspended(true));
|
||||
let events_queue: *mut c_void = *this.get_ivar("eventsQueue");
|
||||
let events_queue = &*(events_queue as *const RefCell<VecDeque<Event>>);
|
||||
events_queue.borrow_mut().push_back(Event::Suspended(true));
|
||||
}
|
||||
}
|
||||
|
||||
extern fn will_terminate(this: &Object, _: Sel, _: id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
let events_queue: *mut c_void = *this.get_ivar("eventsQueue");
|
||||
let events_queue = &*(events_queue as *const RefCell<VecDeque<Event>>);
|
||||
// push event to the front to garantee that we'll process it
|
||||
// immidiatly after jump
|
||||
state.events_queue.push_front(Event::WindowEvent {
|
||||
events_queue.borrow_mut().push_front(Event::WindowEvent {
|
||||
window_id: RootEventId(WindowId),
|
||||
event: WindowEvent::Destroyed,
|
||||
});
|
||||
longjmp(mem::transmute(&mut JMPBUF),1);
|
||||
longjmp(mem::transmute_copy(&mut JMPBUF), 1);
|
||||
}
|
||||
}
|
||||
|
||||
extern fn handle_touches(this: &Object, _: Sel, touches: id, _:id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
let events_queue: *mut c_void = *this.get_ivar("eventsQueue");
|
||||
let events_queue = &*(events_queue as *const RefCell<VecDeque<Event>>);
|
||||
|
||||
let touches_enum: id = msg_send![touches, objectEnumerator];
|
||||
|
||||
@@ -573,7 +613,7 @@ fn create_delegate_class() {
|
||||
let touch_id = touch as u64;
|
||||
let phase: i32 = msg_send![touch, phase];
|
||||
|
||||
state.events_queue.push_back(Event::WindowEvent {
|
||||
events_queue.borrow_mut().push_back(Event::WindowEvent {
|
||||
window_id: RootEventId(WindowId),
|
||||
event: WindowEvent::Touch(Touch {
|
||||
device_id: DEVICE_ID,
|
||||
@@ -593,7 +633,7 @@ fn create_delegate_class() {
|
||||
}
|
||||
}
|
||||
|
||||
let ui_responder = Class::get("UIResponder").expect("Failed to get class `UIResponder`");
|
||||
let ui_responder = class!(UIResponder);
|
||||
let mut decl = ClassDecl::new("AppDelegate", ui_responder).expect("Failed to declare class `AppDelegate`");
|
||||
|
||||
unsafe {
|
||||
@@ -633,46 +673,12 @@ fn create_delegate_class() {
|
||||
post_launch as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_ivar::<*mut c_void>("winitState");
|
||||
decl.add_ivar::<*mut c_void>("eventsQueue");
|
||||
|
||||
decl.register();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: winit shouldn't contain GL-specfiic code
|
||||
pub fn create_view_class() {
|
||||
let superclass = Class::get("UIViewController").expect("Failed to get class `UIViewController`");
|
||||
let decl = ClassDecl::new("MainViewController", superclass).expect("Failed to declare class `MainViewController`");
|
||||
decl.register();
|
||||
|
||||
extern fn init_for_gl(this: &Object, _: Sel, frame: *const c_void) -> id {
|
||||
unsafe {
|
||||
let bounds = frame as *const CGRect;
|
||||
let view: id = msg_send![this, initWithFrame:(*bounds).clone()];
|
||||
|
||||
let mask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
let _: () = msg_send![view, setAutoresizingMask:mask];
|
||||
let _: () = msg_send![view, setAutoresizesSubviews:YES];
|
||||
|
||||
let layer: id = msg_send![view, layer];
|
||||
let _ : () = msg_send![layer, setOpaque:YES];
|
||||
|
||||
view
|
||||
}
|
||||
}
|
||||
|
||||
extern fn layer_class(_: &Class, _: Sel) -> *const Class {
|
||||
unsafe { mem::transmute(Class::get("CAEAGLLayer").expect("Failed to get class `CAEAGLLayer`")) }
|
||||
}
|
||||
|
||||
let superclass = Class::get("GLKView").expect("Failed to get class `GLKView`");
|
||||
let mut decl = ClassDecl::new("MainView", superclass).expect("Failed to declare class `MainView`");
|
||||
unsafe {
|
||||
decl.add_method(sel!(initForGl:), init_for_gl as extern fn(&Object, Sel, *const c_void) -> id);
|
||||
decl.add_class_method(sel!(layerClass), layer_class as extern fn(&Class, Sel) -> *const Class);
|
||||
decl.register();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn start_app() {
|
||||
unsafe {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::os::raw::{c_void, c_char, c_int};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::{env, mem};
|
||||
@@ -45,6 +45,7 @@ pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub class: Option<(String, String)>,
|
||||
pub override_redirect: bool,
|
||||
pub x11_window_type: x11::util::WindowType,
|
||||
pub gtk_theme_variant: Option<String>,
|
||||
}
|
||||
|
||||
lazy_static!(
|
||||
@@ -429,10 +430,7 @@ impl EventsLoop {
|
||||
};
|
||||
|
||||
let err_string = format!(
|
||||
r#"Failed to initialize any backend!
|
||||
Wayland status: {:#?}
|
||||
X11 status: {:#?}
|
||||
"#,
|
||||
"Failed to initialize any backend! Wayland status: {:?} X11 status: {:?}",
|
||||
wayland_err,
|
||||
x11_err,
|
||||
);
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
|
||||
use {ControlFlow, EventsLoopClosed, PhysicalPosition, PhysicalSize};
|
||||
|
||||
use super::WindowId;
|
||||
use super::window::WindowStore;
|
||||
use super::WindowId;
|
||||
|
||||
use sctk::Environment;
|
||||
use sctk::output::OutputMgr;
|
||||
use sctk::reexports::client::{Display, EventQueue, GlobalEvent, Proxy, ConnectError};
|
||||
use sctk::reexports::client::commons::Implementation;
|
||||
use sctk::reexports::client::protocol::{wl_keyboard, wl_output, wl_pointer, wl_registry, wl_seat,
|
||||
wl_touch};
|
||||
use sctk::reexports::client::protocol::{
|
||||
wl_keyboard, wl_output, wl_pointer, wl_registry, wl_seat, wl_touch,
|
||||
};
|
||||
use sctk::reexports::client::{ConnectError, Display, EventQueue, GlobalEvent, Proxy};
|
||||
use sctk::Environment;
|
||||
|
||||
use sctk::reexports::client::protocol::wl_display::RequestsTrait as DisplayRequests;
|
||||
use sctk::reexports::client::protocol::wl_surface::RequestsTrait;
|
||||
|
||||
use ModifiersState;
|
||||
|
||||
pub struct EventsLoopSink {
|
||||
buffer: VecDeque<::Event>,
|
||||
@@ -91,7 +94,7 @@ impl EventsLoopProxy {
|
||||
// Update the `EventsLoop`'s `pending_wakeup` flag.
|
||||
wakeup.store(true, Ordering::Relaxed);
|
||||
// Cause the `EventsLoop` to break from `dispatch` if it is currently blocked.
|
||||
let _ = display.sync();
|
||||
let _ = display.sync(|callback| callback.implement(|_, _| {}, ()));
|
||||
display.flush().map_err(|_| EventsLoopClosed)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -104,29 +107,39 @@ impl EventsLoop {
|
||||
pub fn new() -> Result<EventsLoop, ConnectError> {
|
||||
let (display, mut event_queue) = Display::connect_to_env()?;
|
||||
|
||||
let display = Arc::new(display);
|
||||
let pending_wakeup = Arc::new(AtomicBool::new(false));
|
||||
let sink = Arc::new(Mutex::new(EventsLoopSink::new()));
|
||||
let store = Arc::new(Mutex::new(WindowStore::new()));
|
||||
let seats = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
let env = Environment::from_registry_with_cb(
|
||||
display.get_registry().unwrap(),
|
||||
let mut seat_manager = SeatManager {
|
||||
sink: sink.clone(),
|
||||
store: store.clone(),
|
||||
seats: seats.clone(),
|
||||
events_loop_proxy: EventsLoopProxy {
|
||||
display: Arc::downgrade(&display),
|
||||
pending_wakeup: Arc::downgrade(&pending_wakeup),
|
||||
},
|
||||
};
|
||||
|
||||
let env = Environment::from_display_with_cb(
|
||||
&display,
|
||||
&mut event_queue,
|
||||
SeatManager {
|
||||
sink: sink.clone(),
|
||||
store: store.clone(),
|
||||
seats: seats.clone(),
|
||||
move |event, registry| {
|
||||
seat_manager.receive(event, registry)
|
||||
},
|
||||
).unwrap();
|
||||
|
||||
Ok(EventsLoop {
|
||||
display: Arc::new(display),
|
||||
display,
|
||||
evq: RefCell::new(event_queue),
|
||||
sink: sink,
|
||||
pending_wakeup: Arc::new(AtomicBool::new(false)),
|
||||
store: store,
|
||||
env: env,
|
||||
sink,
|
||||
pending_wakeup,
|
||||
store,
|
||||
env,
|
||||
cleanup_needed: Arc::new(Mutex::new(false)),
|
||||
seats: seats,
|
||||
seats,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -242,6 +255,9 @@ impl EventsLoop {
|
||||
*size = (w, h);
|
||||
} else if frame_refresh {
|
||||
frame.refresh();
|
||||
if !refresh {
|
||||
frame.surface().commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(dpi) = new_dpi {
|
||||
@@ -266,9 +282,10 @@ struct SeatManager {
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
store: Arc<Mutex<WindowStore>>,
|
||||
seats: Arc<Mutex<Vec<(u32, Proxy<wl_seat::WlSeat>)>>>,
|
||||
events_loop_proxy: EventsLoopProxy,
|
||||
}
|
||||
|
||||
impl Implementation<Proxy<wl_registry::WlRegistry>, GlobalEvent> for SeatManager {
|
||||
impl SeatManager {
|
||||
fn receive(&mut self, evt: GlobalEvent, registry: Proxy<wl_registry::WlRegistry>) {
|
||||
use self::wl_registry::RequestsTrait as RegistryRequests;
|
||||
use self::wl_seat::RequestsTrait as SeatRequests;
|
||||
@@ -280,16 +297,23 @@ impl Implementation<Proxy<wl_registry::WlRegistry>, GlobalEvent> for SeatManager
|
||||
} if interface == "wl_seat" =>
|
||||
{
|
||||
use std::cmp::min;
|
||||
|
||||
let mut seat_data = SeatData {
|
||||
sink: self.sink.clone(),
|
||||
store: self.store.clone(),
|
||||
pointer: None,
|
||||
keyboard: None,
|
||||
touch: None,
|
||||
events_loop_proxy: self.events_loop_proxy.clone(),
|
||||
modifiers_tracker: Arc::new(Mutex::new(ModifiersState::default())),
|
||||
};
|
||||
let seat = registry
|
||||
.bind::<wl_seat::WlSeat>(min(version, 5), id)
|
||||
.unwrap()
|
||||
.implement(SeatData {
|
||||
sink: self.sink.clone(),
|
||||
store: self.store.clone(),
|
||||
pointer: None,
|
||||
keyboard: None,
|
||||
touch: None,
|
||||
});
|
||||
.bind(min(version, 5), id, move |seat| {
|
||||
seat.implement(move |event, seat| {
|
||||
seat_data.receive(event, seat)
|
||||
}, ())
|
||||
})
|
||||
.unwrap();
|
||||
self.store.lock().unwrap().new_seat(&seat);
|
||||
self.seats.lock().unwrap().push((id, seat));
|
||||
}
|
||||
@@ -313,20 +337,22 @@ struct SeatData {
|
||||
pointer: Option<Proxy<wl_pointer::WlPointer>>,
|
||||
keyboard: Option<Proxy<wl_keyboard::WlKeyboard>>,
|
||||
touch: Option<Proxy<wl_touch::WlTouch>>,
|
||||
events_loop_proxy: EventsLoopProxy,
|
||||
modifiers_tracker: Arc<Mutex<ModifiersState>>,
|
||||
}
|
||||
|
||||
impl Implementation<Proxy<wl_seat::WlSeat>, wl_seat::Event> for SeatData {
|
||||
impl SeatData {
|
||||
fn receive(&mut self, evt: wl_seat::Event, seat: Proxy<wl_seat::WlSeat>) {
|
||||
use self::wl_seat::RequestsTrait as SeatRequests;
|
||||
match evt {
|
||||
wl_seat::Event::Name { .. } => (),
|
||||
wl_seat::Event::Capabilities { capabilities } => {
|
||||
// create pointer if applicable
|
||||
if capabilities.contains(wl_seat::Capability::Pointer) && self.pointer.is_none() {
|
||||
self.pointer = Some(super::pointer::implement_pointer(
|
||||
seat.get_pointer().unwrap(),
|
||||
&seat,
|
||||
self.sink.clone(),
|
||||
self.store.clone(),
|
||||
self.modifiers_tracker.clone(),
|
||||
))
|
||||
}
|
||||
// destroy pointer if applicable
|
||||
@@ -341,8 +367,10 @@ impl Implementation<Proxy<wl_seat::WlSeat>, wl_seat::Event> for SeatData {
|
||||
// create keyboard if applicable
|
||||
if capabilities.contains(wl_seat::Capability::Keyboard) && self.keyboard.is_none() {
|
||||
self.keyboard = Some(super::keyboard::init_keyboard(
|
||||
seat.get_keyboard().unwrap(),
|
||||
&seat,
|
||||
self.sink.clone(),
|
||||
self.events_loop_proxy.clone(),
|
||||
self.modifiers_tracker.clone(),
|
||||
))
|
||||
}
|
||||
// destroy keyboard if applicable
|
||||
@@ -357,7 +385,7 @@ impl Implementation<Proxy<wl_seat::WlSeat>, wl_seat::Event> for SeatData {
|
||||
// create touch if applicable
|
||||
if capabilities.contains(wl_seat::Capability::Touch) && self.touch.is_none() {
|
||||
self.touch = Some(super::touch::implement_touch(
|
||||
seat.get_touch().unwrap(),
|
||||
&seat,
|
||||
self.sink.clone(),
|
||||
self.store.clone(),
|
||||
))
|
||||
|
||||
@@ -1,81 +1,121 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use super::{make_wid, DeviceId, EventsLoopProxy, EventsLoopSink};
|
||||
use sctk::keyboard::{
|
||||
self, map_keyboard_auto_with_repeat, Event as KbEvent, KeyRepeatEvent, KeyRepeatKind,
|
||||
};
|
||||
use sctk::reexports::client::protocol::wl_keyboard;
|
||||
use sctk::reexports::client::Proxy;
|
||||
use sctk::reexports::client::protocol::wl_seat;
|
||||
use sctk::reexports::client::protocol::wl_seat::RequestsTrait as SeatRequests;
|
||||
|
||||
use {ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent};
|
||||
|
||||
use super::{make_wid, DeviceId, EventsLoopSink};
|
||||
use sctk::keyboard::{self, map_keyboard_auto, Event as KbEvent};
|
||||
use sctk::reexports::client::{NewProxy, Proxy};
|
||||
use sctk::reexports::client::protocol::wl_keyboard;
|
||||
|
||||
pub fn init_keyboard(
|
||||
keyboard: NewProxy<wl_keyboard::WlKeyboard>,
|
||||
seat: &Proxy<wl_seat::WlSeat>,
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
events_loop_proxy: EventsLoopProxy,
|
||||
modifiers_tracker: Arc<Mutex<ModifiersState>>,
|
||||
) -> Proxy<wl_keyboard::WlKeyboard> {
|
||||
// { variables to be captured by the closure
|
||||
let mut target = None;
|
||||
// { variables to be captured by the closures
|
||||
let target = Arc::new(Mutex::new(None));
|
||||
let my_sink = sink.clone();
|
||||
let repeat_sink = sink.clone();
|
||||
let repeat_target = target.clone();
|
||||
let my_modifiers = modifiers_tracker.clone();
|
||||
// }
|
||||
let ret = map_keyboard_auto(keyboard, move |evt: KbEvent, _| match evt {
|
||||
KbEvent::Enter { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
my_sink
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send_event(WindowEvent::Focused(true), wid);
|
||||
target = Some(wid);
|
||||
}
|
||||
KbEvent::Leave { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
my_sink
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send_event(WindowEvent::Focused(false), wid);
|
||||
target = None;
|
||||
}
|
||||
KbEvent::Key {
|
||||
modifiers,
|
||||
rawkey,
|
||||
keysym,
|
||||
state,
|
||||
utf8,
|
||||
..
|
||||
} => {
|
||||
if let Some(wid) = target {
|
||||
let state = match state {
|
||||
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
|
||||
wl_keyboard::KeyState::Released => ElementState::Released,
|
||||
};
|
||||
let vkcode = key_to_vkey(rawkey, keysym);
|
||||
let mut guard = my_sink.lock().unwrap();
|
||||
let ret = map_keyboard_auto_with_repeat(
|
||||
seat,
|
||||
KeyRepeatKind::System,
|
||||
move |evt: KbEvent, _| match evt {
|
||||
KbEvent::Enter { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
my_sink
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send_event(WindowEvent::Focused(true), wid);
|
||||
*target.lock().unwrap() = Some(wid);
|
||||
}
|
||||
KbEvent::Leave { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
my_sink
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send_event(WindowEvent::Focused(false), wid);
|
||||
*target.lock().unwrap() = None;
|
||||
}
|
||||
KbEvent::Key {
|
||||
rawkey,
|
||||
keysym,
|
||||
state,
|
||||
utf8,
|
||||
..
|
||||
} => {
|
||||
if let Some(wid) = *target.lock().unwrap() {
|
||||
let state = match state {
|
||||
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
|
||||
wl_keyboard::KeyState::Released => ElementState::Released,
|
||||
};
|
||||
let vkcode = key_to_vkey(rawkey, keysym);
|
||||
let mut guard = my_sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
WindowEvent::KeyboardInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: rawkey,
|
||||
virtual_keycode: vkcode,
|
||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
||||
},
|
||||
},
|
||||
wid,
|
||||
);
|
||||
// send char event only on key press, not release
|
||||
if let ElementState::Released = state {
|
||||
return;
|
||||
}
|
||||
if let Some(txt) = utf8 {
|
||||
for chr in txt.chars() {
|
||||
guard.send_event(WindowEvent::ReceivedCharacter(chr), wid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
KbEvent::RepeatInfo { .. } => { /* Handled by smithay client toolkit */ }
|
||||
KbEvent::Modifiers { modifiers: event_modifiers } => {
|
||||
*modifiers_tracker.lock().unwrap() = event_modifiers.into()
|
||||
}
|
||||
},
|
||||
move |repeat_event: KeyRepeatEvent, _| {
|
||||
if let Some(wid) = *repeat_target.lock().unwrap() {
|
||||
let state = ElementState::Pressed;
|
||||
let vkcode = key_to_vkey(repeat_event.rawkey, repeat_event.keysym);
|
||||
let mut guard = repeat_sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
WindowEvent::KeyboardInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: rawkey,
|
||||
scancode: repeat_event.rawkey,
|
||||
virtual_keycode: vkcode,
|
||||
modifiers: modifiers.into(),
|
||||
modifiers: my_modifiers.lock().unwrap().clone(),
|
||||
},
|
||||
},
|
||||
wid,
|
||||
);
|
||||
// send char event only on key press, not release
|
||||
if let ElementState::Released = state {
|
||||
return;
|
||||
}
|
||||
if let Some(txt) = utf8 {
|
||||
if let Some(txt) = repeat_event.utf8 {
|
||||
for chr in txt.chars() {
|
||||
guard.send_event(WindowEvent::ReceivedCharacter(chr), wid);
|
||||
}
|
||||
}
|
||||
events_loop_proxy.wakeup().unwrap();
|
||||
}
|
||||
}
|
||||
KbEvent::RepeatInfo { .. } => { /* TODO: handle repeat info */ }
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
match ret {
|
||||
Ok(keyboard) => keyboard,
|
||||
Err((_, keyboard)) => {
|
||||
Err(_) => {
|
||||
// This is a fallback impl if libxkbcommon was not available
|
||||
// This case should probably never happen, as most wayland
|
||||
// compositors _need_ libxkbcommon anyway...
|
||||
@@ -87,45 +127,47 @@ pub fn init_keyboard(
|
||||
let mut target = None;
|
||||
let my_sink = sink;
|
||||
// }
|
||||
keyboard.implement(move |evt, _| match evt {
|
||||
wl_keyboard::Event::Enter { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
my_sink
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send_event(WindowEvent::Focused(true), wid);
|
||||
target = Some(wid);
|
||||
}
|
||||
wl_keyboard::Event::Leave { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
my_sink
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send_event(WindowEvent::Focused(false), wid);
|
||||
target = None;
|
||||
}
|
||||
wl_keyboard::Event::Key { key, state, .. } => {
|
||||
if let Some(wid) = target {
|
||||
let state = match state {
|
||||
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
|
||||
wl_keyboard::KeyState::Released => ElementState::Released,
|
||||
};
|
||||
my_sink.lock().unwrap().send_event(
|
||||
WindowEvent::KeyboardInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: key,
|
||||
virtual_keycode: None,
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
},
|
||||
wid,
|
||||
);
|
||||
seat.get_keyboard(|keyboard| {
|
||||
keyboard.implement(move |evt, _| match evt {
|
||||
wl_keyboard::Event::Enter { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
my_sink
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send_event(WindowEvent::Focused(true), wid);
|
||||
target = Some(wid);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
})
|
||||
wl_keyboard::Event::Leave { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
my_sink
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send_event(WindowEvent::Focused(false), wid);
|
||||
target = None;
|
||||
}
|
||||
wl_keyboard::Event::Key { key, state, .. } => {
|
||||
if let Some(wid) = target {
|
||||
let state = match state {
|
||||
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
|
||||
wl_keyboard::KeyState::Released => ElementState::Released,
|
||||
};
|
||||
my_sink.lock().unwrap().send_event(
|
||||
WindowEvent::KeyboardInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: key,
|
||||
virtual_keycode: None,
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}, ())
|
||||
}).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,6 +235,15 @@ fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
|
||||
keysyms::XKB_KEY_F13 => Some(VirtualKeyCode::F13),
|
||||
keysyms::XKB_KEY_F14 => Some(VirtualKeyCode::F14),
|
||||
keysyms::XKB_KEY_F15 => Some(VirtualKeyCode::F15),
|
||||
keysyms::XKB_KEY_F16 => Some(VirtualKeyCode::F16),
|
||||
keysyms::XKB_KEY_F17 => Some(VirtualKeyCode::F17),
|
||||
keysyms::XKB_KEY_F18 => Some(VirtualKeyCode::F18),
|
||||
keysyms::XKB_KEY_F19 => Some(VirtualKeyCode::F19),
|
||||
keysyms::XKB_KEY_F20 => Some(VirtualKeyCode::F20),
|
||||
keysyms::XKB_KEY_F21 => Some(VirtualKeyCode::F21),
|
||||
keysyms::XKB_KEY_F22 => Some(VirtualKeyCode::F22),
|
||||
keysyms::XKB_KEY_F23 => Some(VirtualKeyCode::F23),
|
||||
keysyms::XKB_KEY_F24 => Some(VirtualKeyCode::F24),
|
||||
// flow control
|
||||
keysyms::XKB_KEY_Print => Some(VirtualKeyCode::Snapshot),
|
||||
keysyms::XKB_KEY_Scroll_Lock => Some(VirtualKeyCode::Scroll),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd",
|
||||
target_os = "openbsd"))]
|
||||
target_os = "netbsd", target_os = "openbsd"))]
|
||||
|
||||
pub use self::window::Window;
|
||||
pub use self::event_loop::{EventsLoop, EventsLoopProxy, EventsLoopSink, MonitorId};
|
||||
|
||||
@@ -7,184 +7,183 @@ use super::DeviceId;
|
||||
use super::event_loop::EventsLoopSink;
|
||||
use super::window::WindowStore;
|
||||
|
||||
use sctk::reexports::client::{NewProxy, Proxy};
|
||||
use sctk::reexports::client::Proxy;
|
||||
use sctk::reexports::client::protocol::wl_pointer::{self, Event as PtrEvent, WlPointer};
|
||||
use sctk::reexports::client::protocol::wl_seat;
|
||||
use sctk::reexports::client::protocol::wl_seat::RequestsTrait as SeatRequests;
|
||||
|
||||
pub fn implement_pointer(
|
||||
pointer: NewProxy<WlPointer>,
|
||||
seat: &Proxy<wl_seat::WlSeat>,
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
store: Arc<Mutex<WindowStore>>,
|
||||
modifiers_tracker: Arc<Mutex<ModifiersState>>,
|
||||
) -> Proxy<WlPointer> {
|
||||
let mut mouse_focus = None;
|
||||
let mut axis_buffer = None;
|
||||
let mut axis_discrete_buffer = None;
|
||||
let mut axis_state = TouchPhase::Ended;
|
||||
|
||||
pointer.implement(move |evt, pointer: Proxy<_>| {
|
||||
let mut sink = sink.lock().unwrap();
|
||||
let store = store.lock().unwrap();
|
||||
match evt {
|
||||
PtrEvent::Enter {
|
||||
surface,
|
||||
surface_x,
|
||||
surface_y,
|
||||
..
|
||||
} => {
|
||||
let wid = store.find_wid(&surface);
|
||||
if let Some(wid) = wid {
|
||||
mouse_focus = Some(wid);
|
||||
sink.send_event(
|
||||
WindowEvent::CursorEntered {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
sink.send_event(
|
||||
WindowEvent::CursorMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (surface_x, surface_y).into(),
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
PtrEvent::Leave { surface, .. } => {
|
||||
mouse_focus = None;
|
||||
let wid = store.find_wid(&surface);
|
||||
if let Some(wid) = wid {
|
||||
sink.send_event(
|
||||
WindowEvent::CursorLeft {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
PtrEvent::Motion {
|
||||
surface_x,
|
||||
surface_y,
|
||||
..
|
||||
} => {
|
||||
if let Some(wid) = mouse_focus {
|
||||
sink.send_event(
|
||||
WindowEvent::CursorMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (surface_x, surface_y).into(),
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
PtrEvent::Button { button, state, .. } => {
|
||||
if let Some(wid) = mouse_focus {
|
||||
let state = match state {
|
||||
wl_pointer::ButtonState::Pressed => ElementState::Pressed,
|
||||
wl_pointer::ButtonState::Released => ElementState::Released,
|
||||
};
|
||||
let button = match button {
|
||||
0x110 => MouseButton::Left,
|
||||
0x111 => MouseButton::Right,
|
||||
0x112 => MouseButton::Middle,
|
||||
// TODO figure out the translation ?
|
||||
_ => return,
|
||||
};
|
||||
sink.send_event(
|
||||
WindowEvent::MouseInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
state: state,
|
||||
button: button,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
PtrEvent::Axis { axis, value, .. } => {
|
||||
if let Some(wid) = mouse_focus {
|
||||
if pointer.version() < 5 {
|
||||
let (mut x, mut y) = (0.0, 0.0);
|
||||
// old seat compatibility
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32,
|
||||
}
|
||||
seat.get_pointer(|pointer| {
|
||||
pointer.implement(move |evt, pointer| {
|
||||
let mut sink = sink.lock().unwrap();
|
||||
let store = store.lock().unwrap();
|
||||
match evt {
|
||||
PtrEvent::Enter {
|
||||
surface,
|
||||
surface_x,
|
||||
surface_y,
|
||||
..
|
||||
} => {
|
||||
let wid = store.find_wid(&surface);
|
||||
if let Some(wid) = wid {
|
||||
mouse_focus = Some(wid);
|
||||
sink.send_event(
|
||||
WindowEvent::MouseWheel {
|
||||
WindowEvent::CursorEntered {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
|
||||
phase: TouchPhase::Moved,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
} else {
|
||||
let (mut x, mut y) = axis_buffer.unwrap_or((0.0, 0.0));
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32,
|
||||
}
|
||||
axis_buffer = Some((x, y));
|
||||
axis_state = match axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PtrEvent::Frame => {
|
||||
let axis_buffer = axis_buffer.take();
|
||||
let axis_discrete_buffer = axis_discrete_buffer.take();
|
||||
if let Some(wid) = mouse_focus {
|
||||
if let Some((x, y)) = axis_discrete_buffer {
|
||||
sink.send_event(
|
||||
WindowEvent::MouseWheel {
|
||||
WindowEvent::CursorMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::LineDelta(x as f32, y as f32),
|
||||
phase: axis_state,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
} else if let Some((x, y)) = axis_buffer {
|
||||
sink.send_event(
|
||||
WindowEvent::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
|
||||
phase: axis_state,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
position: (surface_x, surface_y).into(),
|
||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
PtrEvent::AxisSource { .. } => (),
|
||||
PtrEvent::AxisStop { .. } => {
|
||||
axis_state = TouchPhase::Ended;
|
||||
}
|
||||
PtrEvent::AxisDiscrete { axis, discrete } => {
|
||||
let (mut x, mut y) = axis_discrete_buffer.unwrap_or((0, 0));
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= discrete,
|
||||
wl_pointer::Axis::HorizontalScroll => x += discrete,
|
||||
PtrEvent::Leave { surface, .. } => {
|
||||
mouse_focus = None;
|
||||
let wid = store.find_wid(&surface);
|
||||
if let Some(wid) = wid {
|
||||
sink.send_event(
|
||||
WindowEvent::CursorLeft {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
axis_discrete_buffer = Some((x, y));
|
||||
axis_state = match axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started,
|
||||
PtrEvent::Motion {
|
||||
surface_x,
|
||||
surface_y,
|
||||
..
|
||||
} => {
|
||||
if let Some(wid) = mouse_focus {
|
||||
sink.send_event(
|
||||
WindowEvent::CursorMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (surface_x, surface_y).into(),
|
||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
PtrEvent::Button { button, state, .. } => {
|
||||
if let Some(wid) = mouse_focus {
|
||||
let state = match state {
|
||||
wl_pointer::ButtonState::Pressed => ElementState::Pressed,
|
||||
wl_pointer::ButtonState::Released => ElementState::Released,
|
||||
};
|
||||
let button = match button {
|
||||
0x110 => MouseButton::Left,
|
||||
0x111 => MouseButton::Right,
|
||||
0x112 => MouseButton::Middle,
|
||||
// TODO figure out the translation ?
|
||||
_ => return,
|
||||
};
|
||||
sink.send_event(
|
||||
WindowEvent::MouseInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
state: state,
|
||||
button: button,
|
||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
PtrEvent::Axis { axis, value, .. } => {
|
||||
if let Some(wid) = mouse_focus {
|
||||
if pointer.version() < 5 {
|
||||
let (mut x, mut y) = (0.0, 0.0);
|
||||
// old seat compatibility
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32,
|
||||
}
|
||||
sink.send_event(
|
||||
WindowEvent::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
|
||||
phase: TouchPhase::Moved,
|
||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
} else {
|
||||
let (mut x, mut y) = axis_buffer.unwrap_or((0.0, 0.0));
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32,
|
||||
}
|
||||
axis_buffer = Some((x, y));
|
||||
axis_state = match axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PtrEvent::Frame => {
|
||||
let axis_buffer = axis_buffer.take();
|
||||
let axis_discrete_buffer = axis_discrete_buffer.take();
|
||||
if let Some(wid) = mouse_focus {
|
||||
if let Some((x, y)) = axis_discrete_buffer {
|
||||
sink.send_event(
|
||||
WindowEvent::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::LineDelta(x as f32, y as f32),
|
||||
phase: axis_state,
|
||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
} else if let Some((x, y)) = axis_buffer {
|
||||
sink.send_event(
|
||||
WindowEvent::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
|
||||
phase: axis_state,
|
||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
PtrEvent::AxisSource { .. } => (),
|
||||
PtrEvent::AxisStop { .. } => {
|
||||
axis_state = TouchPhase::Ended;
|
||||
}
|
||||
PtrEvent::AxisDiscrete { axis, discrete } => {
|
||||
let (mut x, mut y) = axis_discrete_buffer.unwrap_or((0, 0));
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= discrete,
|
||||
wl_pointer::Axis::HorizontalScroll => x += discrete,
|
||||
}
|
||||
axis_discrete_buffer = Some((x, y));
|
||||
axis_state = match axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}, ())
|
||||
}).unwrap()
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@ use super::{DeviceId, WindowId};
|
||||
use super::event_loop::EventsLoopSink;
|
||||
use super::window::WindowStore;
|
||||
|
||||
use sctk::reexports::client::{NewProxy, Proxy};
|
||||
use sctk::reexports::client::Proxy;
|
||||
use sctk::reexports::client::protocol::wl_touch::{Event as TouchEvent, WlTouch};
|
||||
use sctk::reexports::client::protocol::wl_seat;
|
||||
use sctk::reexports::client::protocol::wl_seat::RequestsTrait as SeatRequests;
|
||||
|
||||
struct TouchPoint {
|
||||
wid: WindowId,
|
||||
@@ -16,78 +18,80 @@ struct TouchPoint {
|
||||
}
|
||||
|
||||
pub(crate) fn implement_touch(
|
||||
touch: NewProxy<WlTouch>,
|
||||
seat: &Proxy<wl_seat::WlSeat>,
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
store: Arc<Mutex<WindowStore>>,
|
||||
) -> Proxy<WlTouch> {
|
||||
let mut pending_ids = Vec::new();
|
||||
touch.implement(move |evt, _| {
|
||||
let mut sink = sink.lock().unwrap();
|
||||
let store = store.lock().unwrap();
|
||||
match evt {
|
||||
TouchEvent::Down {
|
||||
surface, id, x, y, ..
|
||||
} => {
|
||||
let wid = store.find_wid(&surface);
|
||||
if let Some(wid) = wid {
|
||||
sink.send_event(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Started,
|
||||
location: (x, y).into(),
|
||||
id: id as u64,
|
||||
}),
|
||||
wid,
|
||||
);
|
||||
pending_ids.push(TouchPoint {
|
||||
wid: wid,
|
||||
location: (x, y),
|
||||
id: id,
|
||||
});
|
||||
seat.get_touch(|touch| {
|
||||
touch.implement(move |evt, _| {
|
||||
let mut sink = sink.lock().unwrap();
|
||||
let store = store.lock().unwrap();
|
||||
match evt {
|
||||
TouchEvent::Down {
|
||||
surface, id, x, y, ..
|
||||
} => {
|
||||
let wid = store.find_wid(&surface);
|
||||
if let Some(wid) = wid {
|
||||
sink.send_event(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Started,
|
||||
location: (x, y).into(),
|
||||
id: id as u64,
|
||||
}),
|
||||
wid,
|
||||
);
|
||||
pending_ids.push(TouchPoint {
|
||||
wid: wid,
|
||||
location: (x, y),
|
||||
id: id,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
TouchEvent::Up { id, .. } => {
|
||||
let idx = pending_ids.iter().position(|p| p.id == id);
|
||||
if let Some(idx) = idx {
|
||||
let pt = pending_ids.remove(idx);
|
||||
TouchEvent::Up { id, .. } => {
|
||||
let idx = pending_ids.iter().position(|p| p.id == id);
|
||||
if let Some(idx) = idx {
|
||||
let pt = pending_ids.remove(idx);
|
||||
sink.send_event(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Ended,
|
||||
location: pt.location.into(),
|
||||
id: id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
TouchEvent::Motion { id, x, y, .. } => {
|
||||
let pt = pending_ids.iter_mut().find(|p| p.id == id);
|
||||
if let Some(pt) = pt {
|
||||
pt.location = (x, y);
|
||||
sink.send_event(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Moved,
|
||||
location: (x, y).into(),
|
||||
id: id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
TouchEvent::Frame => (),
|
||||
TouchEvent::Cancel => for pt in pending_ids.drain(..) {
|
||||
sink.send_event(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Ended,
|
||||
phase: TouchPhase::Cancelled,
|
||||
location: pt.location.into(),
|
||||
id: id as u64,
|
||||
id: pt.id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
TouchEvent::Motion { id, x, y, .. } => {
|
||||
let pt = pending_ids.iter_mut().find(|p| p.id == id);
|
||||
if let Some(pt) = pt {
|
||||
pt.location = (x, y);
|
||||
sink.send_event(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Moved,
|
||||
location: (x, y).into(),
|
||||
id: id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
TouchEvent::Frame => (),
|
||||
TouchEvent::Cancel => for pt in pending_ids.drain(..) {
|
||||
sink.send_event(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Cancelled,
|
||||
location: pt.location.into(),
|
||||
id: pt.id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
},
|
||||
}
|
||||
})
|
||||
}, ())
|
||||
}).unwrap()
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use dpi::{LogicalPosition, LogicalSize};
|
||||
use platform::MonitorId as PlatformMonitorId;
|
||||
use window::MonitorId as RootMonitorId;
|
||||
|
||||
use sctk::window::{BasicFrame, Event as WEvent, Window as SWindow};
|
||||
use sctk::window::{ConceptFrame, Event as WEvent, Window as SWindow};
|
||||
use sctk::reexports::client::{Display, Proxy};
|
||||
use sctk::reexports::client::protocol::{wl_seat, wl_surface, wl_output};
|
||||
use sctk::reexports::client::protocol::wl_compositor::RequestsTrait as CompositorRequests;
|
||||
@@ -18,7 +18,7 @@ use platform::platform::wayland::event_loop::{get_available_monitors, get_primar
|
||||
|
||||
pub struct Window {
|
||||
surface: Proxy<wl_surface::WlSurface>,
|
||||
frame: Arc<Mutex<SWindow<BasicFrame>>>,
|
||||
frame: Arc<Mutex<SWindow<ConceptFrame>>>,
|
||||
monitors: Arc<Mutex<MonitorList>>, // Monitors this window is currently on
|
||||
outputs: OutputMgr, // Access to info for all monitors
|
||||
size: Arc<Mutex<(u32, u32)>>,
|
||||
@@ -36,11 +36,11 @@ impl Window {
|
||||
// monitor tracking
|
||||
let monitor_list = Arc::new(Mutex::new(MonitorList::new()));
|
||||
|
||||
let surface = evlp.env.compositor.create_surface().unwrap().implement({
|
||||
let surface = evlp.env.compositor.create_surface(|surface| {
|
||||
let list = monitor_list.clone();
|
||||
let omgr = evlp.env.outputs.clone();
|
||||
let window_store = evlp.store.clone();
|
||||
move |event, surface: Proxy<wl_surface::WlSurface>| match event {
|
||||
surface.implement(move |event, surface| match event {
|
||||
wl_surface::Event::Enter { output } => {
|
||||
let dpi_change = list.lock().unwrap().add_output(MonitorId {
|
||||
proxy: output,
|
||||
@@ -64,19 +64,16 @@ impl Window {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}, ())
|
||||
}).unwrap();
|
||||
|
||||
let window_store = evlp.store.clone();
|
||||
let my_surface = surface.clone();
|
||||
let mut frame = SWindow::<BasicFrame>::init(
|
||||
let mut frame = SWindow::<ConceptFrame>::init_from_env(
|
||||
&evlp.env,
|
||||
surface.clone(),
|
||||
(width, height),
|
||||
&evlp.env.compositor,
|
||||
&evlp.env.subcompositor,
|
||||
&evlp.env.shm,
|
||||
&evlp.env.shell,
|
||||
move |event, ()| match event {
|
||||
move |event| match event {
|
||||
WEvent::Configure { new_size, .. } => {
|
||||
let mut store = window_store.lock().unwrap();
|
||||
for window in &mut store.windows {
|
||||
@@ -327,7 +324,7 @@ struct InternalWindow {
|
||||
need_frame_refresh: Arc<Mutex<bool>>,
|
||||
closed: bool,
|
||||
kill_switch: Arc<Mutex<bool>>,
|
||||
frame: Weak<Mutex<SWindow<BasicFrame>>>,
|
||||
frame: Weak<Mutex<SWindow<ConceptFrame>>>,
|
||||
current_dpi: i32,
|
||||
new_dpi: Option<i32>
|
||||
}
|
||||
@@ -385,7 +382,7 @@ impl WindowStore {
|
||||
|
||||
pub fn for_each<F>(&mut self, mut f: F)
|
||||
where
|
||||
F: FnMut(Option<(u32, u32)>, &mut (u32, u32), Option<i32>, bool, bool, bool, WindowId, Option<&mut SWindow<BasicFrame>>),
|
||||
F: FnMut(Option<(u32, u32)>, &mut (u32, u32), Option<i32>, bool, bool, bool, WindowId, Option<&mut SWindow<ConceptFrame>>),
|
||||
{
|
||||
for window in &mut self.windows {
|
||||
let opt_arc = window.frame.upgrade();
|
||||
|
||||
@@ -117,23 +117,23 @@ pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> {
|
||||
//ffi::XK_L4 => events::VirtualKeyCode::L4,
|
||||
ffi::XK_F15 => events::VirtualKeyCode::F15,
|
||||
//ffi::XK_L5 => events::VirtualKeyCode::L5,
|
||||
//ffi::XK_F16 => events::VirtualKeyCode::F16,
|
||||
ffi::XK_F16 => events::VirtualKeyCode::F16,
|
||||
//ffi::XK_L6 => events::VirtualKeyCode::L6,
|
||||
//ffi::XK_F17 => events::VirtualKeyCode::F17,
|
||||
ffi::XK_F17 => events::VirtualKeyCode::F17,
|
||||
//ffi::XK_L7 => events::VirtualKeyCode::L7,
|
||||
//ffi::XK_F18 => events::VirtualKeyCode::F18,
|
||||
ffi::XK_F18 => events::VirtualKeyCode::F18,
|
||||
//ffi::XK_L8 => events::VirtualKeyCode::L8,
|
||||
//ffi::XK_F19 => events::VirtualKeyCode::F19,
|
||||
ffi::XK_F19 => events::VirtualKeyCode::F19,
|
||||
//ffi::XK_L9 => events::VirtualKeyCode::L9,
|
||||
//ffi::XK_F20 => events::VirtualKeyCode::F20,
|
||||
ffi::XK_F20 => events::VirtualKeyCode::F20,
|
||||
//ffi::XK_L10 => events::VirtualKeyCode::L10,
|
||||
//ffi::XK_F21 => events::VirtualKeyCode::F21,
|
||||
ffi::XK_F21 => events::VirtualKeyCode::F21,
|
||||
//ffi::XK_R1 => events::VirtualKeyCode::R1,
|
||||
//ffi::XK_F22 => events::VirtualKeyCode::F22,
|
||||
ffi::XK_F22 => events::VirtualKeyCode::F22,
|
||||
//ffi::XK_R2 => events::VirtualKeyCode::R2,
|
||||
//ffi::XK_F23 => events::VirtualKeyCode::F23,
|
||||
ffi::XK_F23 => events::VirtualKeyCode::F23,
|
||||
//ffi::XK_R3 => events::VirtualKeyCode::R3,
|
||||
//ffi::XK_F24 => events::VirtualKeyCode::F24,
|
||||
ffi::XK_F24 => events::VirtualKeyCode::F24,
|
||||
//ffi::XK_R4 => events::VirtualKeyCode::R4,
|
||||
//ffi::XK_F25 => events::VirtualKeyCode::F25,
|
||||
//ffi::XK_R5 => events::VirtualKeyCode::R5,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
||||
|
||||
pub mod ffi;
|
||||
mod events;
|
||||
@@ -278,25 +278,25 @@ impl EventsLoop {
|
||||
}
|
||||
} else if client_msg.message_type == self.dnd.atoms.position {
|
||||
// This event occurs every time the mouse moves while a file's being dragged
|
||||
// over our window. We emit HoveredFile in response; while the Mac OS X backend
|
||||
// does that upon a drag entering, XDnD doesn't have access to the actual drop
|
||||
// over our window. We emit HoveredFile in response; while the macOS backend
|
||||
// does that upon a drag entering, XDND doesn't have access to the actual drop
|
||||
// data until this event. For parity with other platforms, we only emit
|
||||
// HoveredFile the first time, though if winit's API is later extended to
|
||||
// supply position updates with HoveredFile or another event, implementing
|
||||
// `HoveredFile` the first time, though if winit's API is later extended to
|
||||
// supply position updates with `HoveredFile` or another event, implementing
|
||||
// that here would be trivial.
|
||||
|
||||
let source_window = client_msg.data.get_long(0) as c_ulong;
|
||||
|
||||
// Equivalent to (x << shift) | y
|
||||
// where shift = mem::size_of::<c_short>() * 8
|
||||
// Equivalent to `(x << shift) | y`
|
||||
// where `shift = mem::size_of::<c_short>() * 8`
|
||||
// Note that coordinates are in "desktop space", not "window space"
|
||||
// (in x11 parlance, they're root window coordinates)
|
||||
// (in X11 parlance, they're root window coordinates)
|
||||
//let packed_coordinates = client_msg.data.get_long(2);
|
||||
//let shift = mem::size_of::<libc::c_short>() * 8;
|
||||
//let x = packed_coordinates >> shift;
|
||||
//let y = packed_coordinates & !(x << shift);
|
||||
|
||||
// By our own state flow, version should never be None at this point.
|
||||
// By our own state flow, `version` should never be `None` at this point.
|
||||
let version = self.dnd.version.unwrap_or(5);
|
||||
|
||||
// Action is specified in versions 2 and up, though we don't need it anyway.
|
||||
@@ -318,23 +318,21 @@ impl EventsLoop {
|
||||
// In version 0, time isn't specified
|
||||
ffi::CurrentTime
|
||||
};
|
||||
// This results in the SelectionNotify event below
|
||||
// This results in the `SelectionNotify` event below
|
||||
self.dnd.convert_selection(window, time);
|
||||
}
|
||||
self.dnd.send_status(window, source_window, DndState::Accepted)
|
||||
.expect("Failed to send XDnD status message.");
|
||||
.expect("Failed to send `XdndStatus` message.");
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
self.dnd.send_status(window, source_window, DndState::Rejected)
|
||||
.expect("Failed to send XDnD status message.");
|
||||
self.dnd.send_finished(window, source_window, DndState::Rejected)
|
||||
.expect("Failed to send XDnD finished message.");
|
||||
.expect("Failed to send `XdndStatus` message.");
|
||||
}
|
||||
self.dnd.reset();
|
||||
}
|
||||
} else if client_msg.message_type == self.dnd.atoms.drop {
|
||||
if let Some(source_window) = self.dnd.source_window {
|
||||
let (source_window, state) = if let Some(source_window) = self.dnd.source_window {
|
||||
if let Some(Ok(ref path_list)) = self.dnd.result {
|
||||
for path in path_list {
|
||||
callback(Event::WindowEvent {
|
||||
@@ -343,10 +341,16 @@ impl EventsLoop {
|
||||
});
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
self.dnd.send_finished(window, source_window, DndState::Accepted)
|
||||
.expect("Failed to send XDnD finished message.");
|
||||
}
|
||||
(source_window, DndState::Accepted)
|
||||
} else {
|
||||
// `source_window` won't be part of our DND state if we already rejected the drop in our
|
||||
// `XdndPosition` handler.
|
||||
let source_window = client_msg.data.get_long(0) as c_ulong;
|
||||
(source_window, DndState::Rejected)
|
||||
};
|
||||
unsafe {
|
||||
self.dnd.send_finished(window, source_window, state)
|
||||
.expect("Failed to send `XdndFinished` message.");
|
||||
}
|
||||
self.dnd.reset();
|
||||
} else if client_msg.message_type == self.dnd.atoms.leave {
|
||||
@@ -412,10 +416,10 @@ impl EventsLoop {
|
||||
let new_inner_size = (xev.width as u32, xev.height as u32);
|
||||
let new_inner_position = (xev.x as i32, xev.y as i32);
|
||||
|
||||
let monitor = window.get_current_monitor(); // This must be done *before* locking!
|
||||
let mut monitor = window.get_current_monitor(); // This must be done *before* locking!
|
||||
let mut shared_state_lock = window.shared_state.lock();
|
||||
|
||||
let (resized, moved) = {
|
||||
let (mut resized, moved) = {
|
||||
let resized = util::maybe_change(&mut shared_state_lock.size, new_inner_size);
|
||||
let moved = if is_synthetic {
|
||||
util::maybe_change(&mut shared_state_lock.inner_position, new_inner_position)
|
||||
@@ -435,32 +439,8 @@ impl EventsLoop {
|
||||
(resized, moved)
|
||||
};
|
||||
|
||||
// This is a hack to ensure that the DPI adjusted resize is actually applied on all WMs. KWin
|
||||
// doesn't need this, but Xfwm does.
|
||||
if let Some(adjusted_size) = shared_state_lock.dpi_adjusted {
|
||||
let rounded_size = (adjusted_size.0.round() as u32, adjusted_size.1.round() as u32);
|
||||
if new_inner_size == rounded_size {
|
||||
// When this finally happens, the event will not be synthetic.
|
||||
shared_state_lock.dpi_adjusted = None;
|
||||
} else {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XResizeWindow)(
|
||||
self.xconn.display,
|
||||
xwindow,
|
||||
rounded_size.0 as c_uint,
|
||||
rounded_size.1 as c_uint,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut events = Events::default();
|
||||
|
||||
if resized {
|
||||
let logical_size = LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor);
|
||||
events.resized = Some(WindowEvent::Resized(logical_size));
|
||||
}
|
||||
|
||||
let new_outer_position = if moved || shared_state_lock.position.is_none() {
|
||||
// We need to convert client area position to window position.
|
||||
let frame_extents = shared_state_lock.frame_extents
|
||||
@@ -497,9 +477,9 @@ impl EventsLoop {
|
||||
});
|
||||
let new_hidpi_factor = {
|
||||
let window_rect = util::AaRect::new(new_outer_position, new_inner_size);
|
||||
let monitor = self.xconn.get_monitor_for_window(Some(window_rect));
|
||||
monitor = self.xconn.get_monitor_for_window(Some(window_rect));
|
||||
let new_hidpi_factor = monitor.hidpi_factor;
|
||||
shared_state_lock.last_monitor = Some(monitor);
|
||||
shared_state_lock.last_monitor = Some(monitor.clone());
|
||||
new_hidpi_factor
|
||||
};
|
||||
if last_hidpi_factor != new_hidpi_factor {
|
||||
@@ -512,23 +492,50 @@ impl EventsLoop {
|
||||
);
|
||||
flusher.queue();
|
||||
shared_state_lock.dpi_adjusted = Some((new_width, new_height));
|
||||
// if the DPI factor changed, force a resize event to ensure the logical
|
||||
// size is computed with the right DPI factor
|
||||
resized = true;
|
||||
}
|
||||
}
|
||||
|
||||
// This is a hack to ensure that the DPI adjusted resize is actually applied on all WMs. KWin
|
||||
// doesn't need this, but Xfwm does.
|
||||
if let Some(adjusted_size) = shared_state_lock.dpi_adjusted {
|
||||
let rounded_size = (adjusted_size.0.round() as u32, adjusted_size.1.round() as u32);
|
||||
if new_inner_size == rounded_size {
|
||||
// When this finally happens, the event will not be synthetic.
|
||||
shared_state_lock.dpi_adjusted = None;
|
||||
} else {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XResizeWindow)(
|
||||
self.xconn.display,
|
||||
xwindow,
|
||||
rounded_size.0 as c_uint,
|
||||
rounded_size.1 as c_uint,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if resized {
|
||||
let logical_size = LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor);
|
||||
events.resized = Some(WindowEvent::Resized(logical_size));
|
||||
}
|
||||
|
||||
events
|
||||
});
|
||||
|
||||
if let Some(events) = events {
|
||||
let window_id = mkwid(xwindow);
|
||||
if let Some(event) = events.dpi_changed {
|
||||
callback(Event::WindowEvent { window_id, event });
|
||||
}
|
||||
if let Some(event) = events.resized {
|
||||
callback(Event::WindowEvent { window_id, event });
|
||||
}
|
||||
if let Some(event) = events.moved {
|
||||
callback(Event::WindowEvent { window_id, event });
|
||||
}
|
||||
if let Some(event) = events.dpi_changed {
|
||||
callback(Event::WindowEvent { window_id, event });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ impl From<bool> for StateOperation {
|
||||
}
|
||||
|
||||
/// X window type. Maps directly to
|
||||
/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/1.3/ar01s05.html).
|
||||
/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html).
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum WindowType {
|
||||
/// A desktop feature. This can include a single window containing desktop icons with the same dimensions as the
|
||||
@@ -41,6 +41,24 @@ pub enum WindowType {
|
||||
Splash,
|
||||
/// This is a dialog window.
|
||||
Dialog,
|
||||
/// A dropdown menu that usually appears when the user clicks on an item in a menu bar.
|
||||
/// This property is typically used on override-redirect windows.
|
||||
DropdownMenu,
|
||||
/// A popup menu that usually appears when the user right clicks on an object.
|
||||
/// This property is typically used on override-redirect windows.
|
||||
PopupMenu,
|
||||
/// A tooltip window. Usually used to show additional information when hovering over an object with the cursor.
|
||||
/// This property is typically used on override-redirect windows.
|
||||
Tooltip,
|
||||
/// The window is a notification.
|
||||
/// This property is typically used on override-redirect windows.
|
||||
Notification,
|
||||
/// This should be used on the windows that are popped up by combo boxes.
|
||||
/// This property is typically used on override-redirect windows.
|
||||
Combo,
|
||||
/// This indicates the the window is being dragged.
|
||||
/// This property is typically used on override-redirect windows.
|
||||
Dnd,
|
||||
/// This is a normal, top-level window.
|
||||
Normal,
|
||||
}
|
||||
@@ -62,6 +80,12 @@ impl WindowType {
|
||||
&Utility => b"_NET_WM_WINDOW_TYPE_UTILITY\0",
|
||||
&Splash => b"_NET_WM_WINDOW_TYPE_SPLASH\0",
|
||||
&Dialog => b"_NET_WM_WINDOW_TYPE_DIALOG\0",
|
||||
&DropdownMenu => b"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0",
|
||||
&PopupMenu => b"_NET_WM_WINDOW_TYPE_POPUP_MENU\0",
|
||||
&Tooltip => b"_NET_WM_WINDOW_TYPE_TOOLTIP\0",
|
||||
&Notification => b"_NET_WM_WINDOW_TYPE_NOTIFICATION\0",
|
||||
&Combo => b"_NET_WM_WINDOW_TYPE_COMBO\0",
|
||||
&Dnd => b"_NET_WM_WINDOW_TYPE_DND\0",
|
||||
&Normal => b"_NET_WM_WINDOW_TYPE_NORMAL\0",
|
||||
};
|
||||
unsafe { xconn.get_atom_unchecked(atom_name) }
|
||||
|
||||
@@ -272,6 +272,10 @@ impl UnownedWindow {
|
||||
window.set_window_type(pl_attribs.x11_window_type).queue();
|
||||
}
|
||||
|
||||
if let Some(variant) = pl_attribs.gtk_theme_variant {
|
||||
window.set_gtk_theme_variant(variant).queue();
|
||||
}
|
||||
|
||||
// set size hints
|
||||
{
|
||||
let mut min_dimensions = window_attrs.min_dimensions;
|
||||
@@ -457,6 +461,19 @@ impl UnownedWindow {
|
||||
)
|
||||
}
|
||||
|
||||
fn set_gtk_theme_variant(&self, variant: String) -> util::Flusher {
|
||||
let hint_atom = unsafe { self.xconn.get_atom_unchecked(b"_GTK_THEME_VARIANT\0") };
|
||||
let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") };
|
||||
let variant = CString::new(variant).expect("`_GTK_THEME_VARIANT` contained null byte");
|
||||
self.xconn.change_property(
|
||||
self.xwindow,
|
||||
hint_atom,
|
||||
utf8_atom,
|
||||
util::PropMode::Replace,
|
||||
variant.as_bytes(),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_urgent(&self, is_urgent: bool) {
|
||||
let mut wm_hints = self.xconn.get_wm_hints(self.xwindow).expect("`XGetWMHints` failed");
|
||||
@@ -583,7 +600,7 @@ impl UnownedWindow {
|
||||
wm_name_atom,
|
||||
utf8_atom,
|
||||
util::PropMode::Replace,
|
||||
title.as_bytes_with_nul(),
|
||||
title.as_bytes(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ impl EventsLoop {
|
||||
where F: FnMut(Event),
|
||||
{
|
||||
unsafe {
|
||||
if !msg_send![cocoa::base::class("NSThread"), isMainThread] {
|
||||
if !msg_send![class!(NSThread), isMainThread] {
|
||||
panic!("Events can only be polled from the main thread on macOS");
|
||||
}
|
||||
}
|
||||
@@ -221,7 +221,7 @@ impl EventsLoop {
|
||||
where F: FnMut(Event) -> ControlFlow
|
||||
{
|
||||
unsafe {
|
||||
if !msg_send![cocoa::base::class("NSThread"), isMainThread] {
|
||||
if !msg_send![class!(NSThread), isMainThread] {
|
||||
panic!("Events can only be polled from the main thread on macOS");
|
||||
}
|
||||
}
|
||||
@@ -315,6 +315,27 @@ impl EventsLoop {
|
||||
});
|
||||
|
||||
match event_type {
|
||||
// https://github.com/glfw/glfw/blob/50eccd298a2bbc272b4977bd162d3e4b55f15394/src/cocoa_window.m#L881
|
||||
appkit::NSKeyUp => {
|
||||
if let Some(key_window) = maybe_key_window() {
|
||||
if event_mods(ns_event).logo {
|
||||
let _: () = msg_send![*key_window.window, sendEvent:ns_event];
|
||||
}
|
||||
}
|
||||
None
|
||||
},
|
||||
// similar to above, but for `<Cmd-.>`, the keyDown is suppressed instead of the
|
||||
// KeyUp, and the above trick does not appear to work.
|
||||
appkit::NSKeyDown => {
|
||||
let modifiers = event_mods(ns_event);
|
||||
let keycode = NSEvent::keyCode(ns_event);
|
||||
if modifiers.logo && keycode == 47 {
|
||||
modifier_event(ns_event, NSEventModifierFlags::NSCommandKeyMask, false)
|
||||
.map(into_event)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
appkit::NSFlagsChanged => {
|
||||
let mut events = std::collections::VecDeque::new();
|
||||
|
||||
@@ -589,7 +610,7 @@ pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> {
|
||||
0x3d => events::VirtualKeyCode::RAlt,
|
||||
0x3e => events::VirtualKeyCode::RControl,
|
||||
//0x3f => Fn key,
|
||||
//0x40 => F17 Key,
|
||||
0x40 => events::VirtualKeyCode::F17,
|
||||
0x41 => events::VirtualKeyCode::Decimal,
|
||||
//0x42 -> unkown,
|
||||
0x43 => events::VirtualKeyCode::Multiply,
|
||||
@@ -604,8 +625,8 @@ pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> {
|
||||
0x4c => events::VirtualKeyCode::NumpadEnter,
|
||||
//0x4d => unkown,
|
||||
0x4e => events::VirtualKeyCode::Subtract,
|
||||
//0x4f => F18 key,
|
||||
//0x50 => F19 Key,
|
||||
0x4f => events::VirtualKeyCode::F18,
|
||||
0x50 => events::VirtualKeyCode::F19,
|
||||
0x51 => events::VirtualKeyCode::NumpadEquals,
|
||||
0x52 => events::VirtualKeyCode::Numpad0,
|
||||
0x53 => events::VirtualKeyCode::Numpad1,
|
||||
@@ -615,7 +636,7 @@ pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> {
|
||||
0x57 => events::VirtualKeyCode::Numpad5,
|
||||
0x58 => events::VirtualKeyCode::Numpad6,
|
||||
0x59 => events::VirtualKeyCode::Numpad7,
|
||||
//0x5a => F20 Key,
|
||||
0x5a => events::VirtualKeyCode::F20,
|
||||
0x5b => events::VirtualKeyCode::Numpad8,
|
||||
0x5c => events::VirtualKeyCode::Numpad9,
|
||||
//0x5d => unkown,
|
||||
@@ -631,7 +652,7 @@ pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> {
|
||||
0x67 => events::VirtualKeyCode::F11,
|
||||
//0x68 => unkown,
|
||||
0x69 => events::VirtualKeyCode::F13,
|
||||
//0x6a => F16 Key,
|
||||
0x6a => events::VirtualKeyCode::F16,
|
||||
0x6b => events::VirtualKeyCode::F14,
|
||||
//0x6c => unkown,
|
||||
0x6d => events::VirtualKeyCode::F10,
|
||||
@@ -659,6 +680,23 @@ pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn check_additional_virtual_key_codes(
|
||||
s: &Option<String>
|
||||
) -> Option<events::VirtualKeyCode> {
|
||||
if let &Some(ref s) = s {
|
||||
if let Some(ch) = s.encode_utf16().next() {
|
||||
return Some(match ch {
|
||||
0xf718 => events::VirtualKeyCode::F21,
|
||||
0xf719 => events::VirtualKeyCode::F22,
|
||||
0xf71a => events::VirtualKeyCode::F23,
|
||||
0xf71b => events::VirtualKeyCode::F24,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn event_mods(event: cocoa::base::id) -> ModifiersState {
|
||||
let flags = unsafe {
|
||||
NSEvent::modifierFlags(event)
|
||||
@@ -674,11 +712,15 @@ pub fn event_mods(event: cocoa::base::id) -> ModifiersState {
|
||||
unsafe fn modifier_event(
|
||||
ns_event: cocoa::base::id,
|
||||
keymask: NSEventModifierFlags,
|
||||
key_pressed: bool,
|
||||
was_key_pressed: bool,
|
||||
) -> Option<WindowEvent> {
|
||||
if !key_pressed && NSEvent::modifierFlags(ns_event).contains(keymask)
|
||||
|| key_pressed && !NSEvent::modifierFlags(ns_event).contains(keymask) {
|
||||
let state = ElementState::Released;
|
||||
if !was_key_pressed && NSEvent::modifierFlags(ns_event).contains(keymask)
|
||||
|| was_key_pressed && !NSEvent::modifierFlags(ns_event).contains(keymask) {
|
||||
let state = if was_key_pressed {
|
||||
ElementState::Released
|
||||
} else {
|
||||
ElementState::Pressed
|
||||
};
|
||||
let keycode = NSEvent::keyCode(ns_event);
|
||||
let scancode = keycode as u32;
|
||||
let virtual_keycode = to_virtual_key_code(keycode);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#![allow(dead_code, non_snake_case, non_upper_case_globals)]
|
||||
|
||||
use cocoa::base::{class, id};
|
||||
use cocoa::base::id;
|
||||
use cocoa::foundation::{NSInteger, NSUInteger};
|
||||
use objc;
|
||||
|
||||
@@ -35,7 +35,7 @@ unsafe impl objc::Encode for NSRange {
|
||||
|
||||
pub trait NSMutableAttributedString: Sized {
|
||||
unsafe fn alloc(_: Self) -> id {
|
||||
msg_send![class("NSMutableAttributedString"), alloc]
|
||||
msg_send![class!(NSMutableAttributedString), alloc]
|
||||
}
|
||||
|
||||
unsafe fn init(self) -> id; // *mut NSMutableAttributedString
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use cocoa::appkit::NSWindowStyleMask;
|
||||
use cocoa::base::{class, id, nil};
|
||||
use cocoa::base::{id, nil};
|
||||
use cocoa::foundation::{NSRect, NSUInteger};
|
||||
use core_graphics::display::CGDisplay;
|
||||
|
||||
@@ -26,13 +26,13 @@ pub unsafe fn set_style_mask(window: id, view: id, mask: NSWindowStyleMask) {
|
||||
}
|
||||
|
||||
pub unsafe fn create_input_context(view: id) -> IdRef {
|
||||
let input_context: id = msg_send![class("NSTextInputContext"), alloc];
|
||||
let input_context: id = msg_send![class!(NSTextInputContext), alloc];
|
||||
let input_context: id = msg_send![input_context, initWithClient:view];
|
||||
IdRef::new(input_context)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn open_emoji_picker() {
|
||||
let app: id = msg_send![class("NSApplication"), sharedApplication];
|
||||
let app: id = msg_send![class!(NSApplication), sharedApplication];
|
||||
let _: () = msg_send![app, orderFrontCharacterPalette:nil];
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ use std::collections::VecDeque;
|
||||
use std::os::raw::*;
|
||||
use std::sync::Weak;
|
||||
|
||||
use cocoa::base::{class, id, nil};
|
||||
use cocoa::base::{id, nil};
|
||||
use cocoa::appkit::{NSEvent, NSView, NSWindow};
|
||||
use cocoa::foundation::{NSPoint, NSRect, NSSize, NSString, NSUInteger};
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::{Class, Object, Protocol, Sel, BOOL};
|
||||
use objc::runtime::{Class, Object, Protocol, Sel, BOOL, YES};
|
||||
|
||||
use {ElementState, Event, KeyboardInput, MouseButton, WindowEvent, WindowId};
|
||||
use platform::platform::events_loop::{DEVICE_ID, event_mods, Shared, to_virtual_key_code};
|
||||
use platform::platform::events_loop::{DEVICE_ID, event_mods, Shared, to_virtual_key_code, check_additional_virtual_key_codes};
|
||||
use platform::platform::util;
|
||||
use platform::platform::ffi::*;
|
||||
use platform::platform::window::{get_window_id, IdRef};
|
||||
@@ -24,7 +24,7 @@ struct ViewState {
|
||||
shared: Weak<Shared>,
|
||||
ime_spot: Option<(f64, f64)>,
|
||||
raw_characters: Option<String>,
|
||||
last_insert: Option<String>,
|
||||
is_key_down: bool,
|
||||
}
|
||||
|
||||
pub fn new_view(window: id, shared: Weak<Shared>) -> IdRef {
|
||||
@@ -33,7 +33,7 @@ pub fn new_view(window: id, shared: Weak<Shared>) -> IdRef {
|
||||
shared,
|
||||
ime_spot: None,
|
||||
raw_characters: None,
|
||||
last_insert: None,
|
||||
is_key_down: false,
|
||||
};
|
||||
unsafe {
|
||||
// This is free'd in `dealloc`
|
||||
@@ -64,7 +64,7 @@ unsafe impl Sync for ViewClass {}
|
||||
|
||||
lazy_static! {
|
||||
static ref VIEW_CLASS: ViewClass = unsafe {
|
||||
let superclass = Class::get("NSView").unwrap();
|
||||
let superclass = class!(NSView);
|
||||
let mut decl = ClassDecl::new("WinitView", superclass).unwrap();
|
||||
decl.add_method(sel!(dealloc), dealloc as extern fn(&Object, Sel));
|
||||
decl.add_method(
|
||||
@@ -122,6 +122,7 @@ lazy_static! {
|
||||
decl.add_method(sel!(mouseDragged:), mouse_dragged as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(rightMouseDragged:), right_mouse_dragged as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(otherMouseDragged:), other_mouse_dragged as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(_wantsKeyDownForEvent:), wants_key_down_for_event as extern fn(&Object, Sel, id) -> BOOL);
|
||||
decl.add_ivar::<*mut c_void>("winitState");
|
||||
decl.add_ivar::<id>("markedText");
|
||||
let protocol = Protocol::get("NSTextInputClient").unwrap();
|
||||
@@ -191,7 +192,7 @@ extern fn set_marked_text(
|
||||
let marked_text_ref: &mut id = this.get_mut_ivar("markedText");
|
||||
let _: () = msg_send![(*marked_text_ref), release];
|
||||
let marked_text = NSMutableAttributedString::alloc(nil);
|
||||
let has_attr = msg_send![string, isKindOfClass:class("NSAttributedString")];
|
||||
let has_attr = msg_send![string, isKindOfClass:class!(NSAttributedString)];
|
||||
if has_attr {
|
||||
marked_text.initWithAttributedString(string);
|
||||
} else {
|
||||
@@ -214,7 +215,7 @@ extern fn unmark_text(this: &Object, _sel: Sel) {
|
||||
|
||||
extern fn valid_attributes_for_marked_text(_this: &Object, _sel: Sel) -> id {
|
||||
//println!("validAttributesForMarkedText");
|
||||
unsafe { msg_send![class("NSArray"), array] }
|
||||
unsafe { msg_send![class!(NSArray), array] }
|
||||
}
|
||||
|
||||
extern fn attributed_substring_for_proposed_range(
|
||||
@@ -265,7 +266,7 @@ extern fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range:
|
||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
let has_attr = msg_send![string, isKindOfClass:class("NSAttributedString")];
|
||||
let has_attr = msg_send![string, isKindOfClass:class!(NSAttributedString)];
|
||||
let characters = if has_attr {
|
||||
// This is a *mut NSAttributedString
|
||||
msg_send![string, string]
|
||||
@@ -279,10 +280,10 @@ extern fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range:
|
||||
characters.len(),
|
||||
);
|
||||
let string = str::from_utf8_unchecked(slice);
|
||||
state.last_insert = Some(string.to_owned());
|
||||
state.is_key_down = true;
|
||||
|
||||
// We don't need this now, but it's here if that changes.
|
||||
//let event: id = msg_send![class("NSApp"), currentEvent];
|
||||
//let event: id = msg_send![class!(NSApp), currentEvent];
|
||||
|
||||
let mut events = VecDeque::with_capacity(characters.len());
|
||||
for character in string.chars() {
|
||||
@@ -343,6 +344,18 @@ extern fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_characters(event: id) -> Option<String> {
|
||||
unsafe {
|
||||
let characters: id = msg_send![event, characters];
|
||||
let slice = slice::from_raw_parts(
|
||||
characters.UTF8String() as *const c_uchar,
|
||||
characters.len(),
|
||||
);
|
||||
let string = str::from_utf8_unchecked(slice);
|
||||
Some(string.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
extern fn key_down(this: &Object, _sel: Sel, event: id) {
|
||||
//println!("keyDown");
|
||||
unsafe {
|
||||
@@ -350,8 +363,16 @@ extern fn key_down(this: &Object, _sel: Sel, event: id) {
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
let window_id = WindowId(get_window_id(state.window));
|
||||
|
||||
state.raw_characters = get_characters(event);
|
||||
|
||||
let keycode: c_ushort = msg_send![event, keyCode];
|
||||
let virtual_keycode = to_virtual_key_code(keycode);
|
||||
// We are checking here for F21-F24 keys, since their keycode
|
||||
// can vary, but we know that they are encoded
|
||||
// in characters property.
|
||||
let virtual_keycode = to_virtual_key_code(keycode)
|
||||
.or_else(|| {
|
||||
check_additional_virtual_key_codes(&state.raw_characters)
|
||||
});
|
||||
let scancode = keycode as u32;
|
||||
let is_repeat = msg_send![event, isARepeat];
|
||||
|
||||
@@ -368,13 +389,14 @@ extern fn key_down(this: &Object, _sel: Sel, event: id) {
|
||||
},
|
||||
};
|
||||
|
||||
state.raw_characters = {
|
||||
let characters: id = msg_send![event, characters];
|
||||
let slice = slice::from_raw_parts(
|
||||
characters.UTF8String() as *const c_uchar,
|
||||
characters.len(),
|
||||
let characters: id = msg_send![event, characters];
|
||||
let slice = slice::from_raw_parts(
|
||||
characters.UTF8String() as *const c_uchar,
|
||||
characters.len(),
|
||||
);
|
||||
let string = str::from_utf8_unchecked(slice);
|
||||
let string = str::from_utf8_unchecked(slice);
|
||||
|
||||
state.raw_characters = {
|
||||
Some(string.to_owned())
|
||||
};
|
||||
|
||||
@@ -384,9 +406,8 @@ extern fn key_down(this: &Object, _sel: Sel, event: id) {
|
||||
.unwrap()
|
||||
.push_back(window_event);
|
||||
// Emit `ReceivedCharacter` for key repeats
|
||||
if is_repeat && state.last_insert.is_some() {
|
||||
let last_insert = state.last_insert.as_ref().unwrap();
|
||||
for character in last_insert.chars() {
|
||||
if is_repeat && state.is_key_down{
|
||||
for character in string.chars() {
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ReceivedCharacter(character),
|
||||
@@ -400,7 +421,7 @@ extern fn key_down(this: &Object, _sel: Sel, event: id) {
|
||||
// Some keys (and only *some*, with no known reason) don't trigger `insertText`, while others do...
|
||||
// So, we don't give repeats the opportunity to trigger that, since otherwise our hack will cause some
|
||||
// keys to generate twice as many characters.
|
||||
let array: id = msg_send![class("NSArray"), arrayWithObject:event];
|
||||
let array: id = msg_send![class!(NSArray), arrayWithObject:event];
|
||||
let (): _ = msg_send![this, interpretKeyEvents:array];
|
||||
}
|
||||
}
|
||||
@@ -413,10 +434,17 @@ extern 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.last_insert = None;
|
||||
state.is_key_down = false;
|
||||
|
||||
// We need characters here to check for additional keys such as
|
||||
// F21-F24.
|
||||
let characters = get_characters(event);
|
||||
|
||||
let keycode: c_ushort = msg_send![event, keyCode];
|
||||
let virtual_keycode = to_virtual_key_code(keycode);
|
||||
let virtual_keycode = to_virtual_key_code(keycode)
|
||||
.or_else(|| {
|
||||
check_additional_virtual_key_codes(&characters)
|
||||
});
|
||||
let scancode = keycode as u32;
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.window)),
|
||||
@@ -566,3 +594,8 @@ extern fn right_mouse_dragged(this: &Object, _sel: Sel, event: id) {
|
||||
extern fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_motion(this, event);
|
||||
}
|
||||
|
||||
// https://github.com/chromium/chromium/blob/a86a8a6bcfa438fa3ac2eba6f02b3ad1f8e0756f/ui/views/cocoa/bridged_content_view.mm#L816
|
||||
extern fn wants_key_down_for_event(_this: &Object, _se: Sel, _event: id) -> BOOL {
|
||||
YES
|
||||
}
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
use std;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::f64;
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::c_void;
|
||||
use std::sync::Weak;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::sync::atomic::{Ordering, AtomicBool};
|
||||
|
||||
use cocoa;
|
||||
use cocoa::appkit::{
|
||||
self,
|
||||
CGFloat,
|
||||
NSApp,
|
||||
NSApplication,
|
||||
NSColor,
|
||||
NSRequestUserAttentionType,
|
||||
NSScreen,
|
||||
NSView,
|
||||
NSWindow,
|
||||
NSWindowButton,
|
||||
NSWindowStyleMask,
|
||||
NSApplicationActivationPolicy,
|
||||
};
|
||||
use cocoa::base::{id, nil};
|
||||
use cocoa::foundation::{NSAutoreleasePool, NSDictionary, NSPoint, NSRect, NSSize, NSString};
|
||||
@@ -45,6 +49,7 @@ use window::MonitorId as RootMonitorId;
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Id(pub usize);
|
||||
|
||||
// TODO: It's possible for delegate methods to be called asynchronously, causing data races / `RefCell` panics.
|
||||
pub struct DelegateState {
|
||||
view: IdRef,
|
||||
window: IdRef,
|
||||
@@ -421,7 +426,7 @@ impl WindowDelegate {
|
||||
|
||||
INIT.call_once(|| unsafe {
|
||||
// Create new NSWindowDelegate
|
||||
let superclass = Class::get("NSObject").unwrap();
|
||||
let superclass = class!(NSObject);
|
||||
let mut decl = ClassDecl::new("WinitWindowDelegate", superclass).unwrap();
|
||||
|
||||
// Add callback methods
|
||||
@@ -526,7 +531,7 @@ pub struct Window2 {
|
||||
pub window: IdRef,
|
||||
pub delegate: WindowDelegate,
|
||||
pub input_context: IdRef,
|
||||
cursor_hidden: Cell<bool>,
|
||||
cursor_hidden: AtomicBool,
|
||||
}
|
||||
|
||||
unsafe impl Send for Window2 {}
|
||||
@@ -582,6 +587,19 @@ impl WindowExt for Window2 {
|
||||
fn get_nsview(&self) -> *mut c_void {
|
||||
*self.view as *mut c_void
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn request_user_attention(&self, is_critical: bool) {
|
||||
let request_type = if is_critical {
|
||||
NSRequestUserAttentionType::NSCriticalRequest
|
||||
} else {
|
||||
NSRequestUserAttentionType::NSInformationalRequest
|
||||
};
|
||||
|
||||
unsafe {
|
||||
NSApp().requestUserAttention_(request_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Window2 {
|
||||
@@ -591,7 +609,7 @@ impl Window2 {
|
||||
pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||
) -> Result<Window2, CreationError> {
|
||||
unsafe {
|
||||
if !msg_send![cocoa::base::class("NSThread"), isMainThread] {
|
||||
if !msg_send![class!(NSThread), isMainThread] {
|
||||
panic!("Windows can only be created on the main thread on macOS");
|
||||
}
|
||||
}
|
||||
@@ -716,7 +734,15 @@ impl Window2 {
|
||||
if app == nil {
|
||||
None
|
||||
} else {
|
||||
app.setActivationPolicy_(activation_policy.into());
|
||||
let ns_activation_policy = match activation_policy {
|
||||
ActivationPolicy::Regular =>
|
||||
NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
|
||||
ActivationPolicy::Accessory =>
|
||||
NSApplicationActivationPolicy::NSApplicationActivationPolicyAccessory,
|
||||
ActivationPolicy::Prohibited =>
|
||||
NSApplicationActivationPolicy::NSApplicationActivationPolicyProhibited,
|
||||
};
|
||||
app.setActivationPolicy_(ns_activation_policy);
|
||||
app.finishLaunching();
|
||||
Some(app)
|
||||
}
|
||||
@@ -728,7 +754,7 @@ impl Window2 {
|
||||
static INIT: std::sync::Once = std::sync::ONCE_INIT;
|
||||
|
||||
INIT.call_once(|| unsafe {
|
||||
let window_superclass = Class::get("NSWindow").unwrap();
|
||||
let window_superclass = class!(NSWindow);
|
||||
let mut decl = ClassDecl::new("WinitWindow", window_superclass).unwrap();
|
||||
decl.add_method(sel!(canBecomeMainWindow), yes as extern fn(&Object, Sel) -> BOOL);
|
||||
decl.add_method(sel!(canBecomeKeyWindow), yes as extern fn(&Object, Sel) -> BOOL);
|
||||
@@ -849,6 +875,16 @@ impl Window2 {
|
||||
let view = new_view(window, shared);
|
||||
view.non_nil().map(|view| {
|
||||
view.setWantsBestResolutionOpenGLSurface_(YES);
|
||||
|
||||
// On Mojave, views automatically become layer-backed shortly after being added to
|
||||
// a window. Changing the layer-backedness of a view breaks the association between
|
||||
// the view and its associated OpenGL context. To work around this, on Mojave we
|
||||
// explicitly make the view layer-backed up front so that AppKit doesn't do it
|
||||
// itself and break the association with its context.
|
||||
if f64::floor(appkit::NSAppKitVersionNumber) > appkit::NSAppKitVersionNumber10_12 {
|
||||
view.setWantsLayer(YES);
|
||||
}
|
||||
|
||||
window.setContentView_(*view);
|
||||
window.makeFirstResponder_(*view);
|
||||
view
|
||||
@@ -987,7 +1023,7 @@ impl Window2 {
|
||||
MouseCursor::ZoomOut => "arrowCursor",
|
||||
};
|
||||
let sel = Sel::register(cursor_name);
|
||||
let cls = Class::get("NSCursor").unwrap();
|
||||
let cls = class!(NSCursor);
|
||||
unsafe {
|
||||
use objc::Message;
|
||||
let cursor: id = cls.send_message(sel, ()).unwrap();
|
||||
@@ -1004,16 +1040,16 @@ impl Window2 {
|
||||
|
||||
#[inline]
|
||||
pub fn hide_cursor(&self, hide: bool) {
|
||||
let cursor_class = Class::get("NSCursor").unwrap();
|
||||
let cursor_class = class!(NSCursor);
|
||||
// macOS uses a "hide counter" like Windows does, so we avoid incrementing it more than once.
|
||||
// (otherwise, `hide_cursor(false)` would need to be called n times!)
|
||||
if hide != self.cursor_hidden.get() {
|
||||
if hide != self.cursor_hidden.load(Ordering::Acquire) {
|
||||
if hide {
|
||||
let _: () = unsafe { msg_send![cursor_class, hide] };
|
||||
} else {
|
||||
let _: () = unsafe { msg_send![cursor_class, unhide] };
|
||||
}
|
||||
self.cursor_hidden.replace(hide);
|
||||
self.cursor_hidden.store(hide, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ pub use self::platform::*;
|
||||
#[cfg(target_os = "windows")]
|
||||
#[path="windows/mod.rs"]
|
||||
mod platform;
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
||||
#[path="linux/mod.rs"]
|
||||
mod platform;
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -21,5 +21,6 @@ mod platform;
|
||||
|
||||
#[cfg(all(not(target_os = "ios"), not(target_os = "windows"), not(target_os = "linux"),
|
||||
not(target_os = "macos"), not(target_os = "android"), not(target_os = "dragonfly"),
|
||||
not(target_os = "freebsd"), not(target_os = "openbsd"), not(target_os = "emscripten")))]
|
||||
not(target_os = "freebsd"), not(target_os = "netbsd"), not(target_os = "openbsd"),
|
||||
not(target_os = "emscripten")))]
|
||||
compile_error!("The platform you're compiling for is not supported by winit");
|
||||
|
||||
@@ -8,7 +8,6 @@ use winapi::shared::minwindef::{BOOL, UINT, FALSE};
|
||||
use winapi::shared::windef::{
|
||||
DPI_AWARENESS_CONTEXT,
|
||||
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE,
|
||||
HDC,
|
||||
HMONITOR,
|
||||
HWND,
|
||||
};
|
||||
@@ -145,7 +144,11 @@ pub fn dpi_to_scale_factor(dpi: u32) -> f64 {
|
||||
dpi as f64 / BASE_DPI as f64
|
||||
}
|
||||
|
||||
pub unsafe fn get_window_dpi(hwnd: HWND, hdc: HDC) -> u32 {
|
||||
pub unsafe fn get_hwnd_dpi(hwnd: HWND) -> u32 {
|
||||
let hdc = winuser::GetDC(hwnd);
|
||||
if hdc.is_null() {
|
||||
panic!("[winit] `GetDC` returned null!");
|
||||
}
|
||||
if let Some(GetDpiForWindow) = *GET_DPI_FOR_WINDOW {
|
||||
// We are on Windows 10 Anniversary Update (1607) or later.
|
||||
match GetDpiForWindow(hwnd) {
|
||||
@@ -181,16 +184,6 @@ pub unsafe fn get_window_dpi(hwnd: HWND, hdc: HDC) -> u32 {
|
||||
}
|
||||
}
|
||||
|
||||
// Use this when you have both the HWND and HDC on hand (i.e. window methods)
|
||||
pub fn get_window_scale_factor(hwnd: HWND, hdc: HDC) -> f64 {
|
||||
dpi_to_scale_factor(unsafe { get_window_dpi(hwnd, hdc) })
|
||||
}
|
||||
|
||||
// Use this when you only have the HWND (i.e. event handling)
|
||||
pub fn get_hwnd_scale_factor(hwnd: HWND) -> f64 {
|
||||
let hdc = unsafe { winuser::GetDC(hwnd) };
|
||||
if hdc.is_null() {
|
||||
panic!("[winit] `GetDC` returned null!");
|
||||
}
|
||||
unsafe { get_window_scale_factor(hwnd, hdc) }
|
||||
dpi_to_scale_factor(unsafe { get_hwnd_dpi(hwnd) })
|
||||
}
|
||||
|
||||
203
src/platform/windows/drop_handler.rs
Normal file
203
src/platform/windows/drop_handler.rs
Normal file
@@ -0,0 +1,203 @@
|
||||
use std::ffi::OsString;
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::{mem, ptr};
|
||||
|
||||
use winapi::ctypes::c_void;
|
||||
use winapi::shared::guiddef::REFIID;
|
||||
use winapi::shared::minwindef::{DWORD, MAX_PATH, UINT, ULONG};
|
||||
use winapi::shared::windef::{HWND, POINTL};
|
||||
use winapi::shared::winerror::S_OK;
|
||||
use winapi::um::objidl::IDataObject;
|
||||
use winapi::um::oleidl::{IDropTarget, IDropTargetVtbl};
|
||||
use winapi::um::winnt::HRESULT;
|
||||
use winapi::um::{shellapi, unknwnbase};
|
||||
|
||||
use platform::platform::events_loop::send_event;
|
||||
use platform::platform::WindowId;
|
||||
|
||||
use {Event, WindowId as SuperWindowId};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FileDropHandlerData {
|
||||
pub interface: IDropTarget,
|
||||
refcount: AtomicUsize,
|
||||
window: HWND,
|
||||
}
|
||||
|
||||
pub struct FileDropHandler {
|
||||
pub data: *mut FileDropHandlerData,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl FileDropHandler {
|
||||
pub fn new(window: HWND) -> FileDropHandler {
|
||||
let data = Box::new(FileDropHandlerData {
|
||||
interface: IDropTarget {
|
||||
lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl,
|
||||
},
|
||||
refcount: AtomicUsize::new(1),
|
||||
window,
|
||||
});
|
||||
FileDropHandler {
|
||||
data: Box::into_raw(data),
|
||||
}
|
||||
}
|
||||
|
||||
// Implement IUnknown
|
||||
pub unsafe extern "system" fn QueryInterface(
|
||||
_this: *mut unknwnbase::IUnknown,
|
||||
_riid: REFIID,
|
||||
_ppvObject: *mut *mut c_void,
|
||||
) -> HRESULT {
|
||||
// This function doesn't appear to be required for an `IDropTarget`.
|
||||
// An implementation would be nice however.
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn AddRef(this: *mut unknwnbase::IUnknown) -> ULONG {
|
||||
let drop_handler_data = Self::from_interface(this);
|
||||
let count = drop_handler_data.refcount.fetch_add(1, Ordering::Release) + 1;
|
||||
count as ULONG
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn Release(this: *mut unknwnbase::IUnknown) -> ULONG {
|
||||
let drop_handler = Self::from_interface(this);
|
||||
let count = drop_handler.refcount.fetch_sub(1, Ordering::Release) - 1;
|
||||
if count == 0 {
|
||||
// Destroy the underlying data
|
||||
Box::from_raw(drop_handler as *mut FileDropHandlerData);
|
||||
}
|
||||
count as ULONG
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn DragEnter(
|
||||
this: *mut IDropTarget,
|
||||
pDataObj: *const IDataObject,
|
||||
_grfKeyState: DWORD,
|
||||
_pt: *const POINTL,
|
||||
_pdwEffect: *mut DWORD,
|
||||
) -> HRESULT {
|
||||
use events::WindowEvent::HoveredFile;
|
||||
let drop_handler = Self::from_interface(this);
|
||||
Self::iterate_filenames(pDataObj, |filename| {
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(drop_handler.window)),
|
||||
event: HoveredFile(filename),
|
||||
});
|
||||
});
|
||||
|
||||
S_OK
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn DragOver(
|
||||
_this: *mut IDropTarget,
|
||||
_grfKeyState: DWORD,
|
||||
_pt: *const POINTL,
|
||||
_pdwEffect: *mut DWORD,
|
||||
) -> HRESULT {
|
||||
S_OK
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT {
|
||||
use events::WindowEvent::HoveredFileCancelled;
|
||||
let drop_handler = Self::from_interface(this);
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(drop_handler.window)),
|
||||
event: HoveredFileCancelled,
|
||||
});
|
||||
|
||||
S_OK
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn Drop(
|
||||
this: *mut IDropTarget,
|
||||
pDataObj: *const IDataObject,
|
||||
_grfKeyState: DWORD,
|
||||
_pt: *const POINTL,
|
||||
_pdwEffect: *mut DWORD,
|
||||
) -> HRESULT {
|
||||
use events::WindowEvent::DroppedFile;
|
||||
let drop_handler = Self::from_interface(this);
|
||||
let hdrop = Self::iterate_filenames(pDataObj, |filename| {
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(drop_handler.window)),
|
||||
event: DroppedFile(filename),
|
||||
});
|
||||
});
|
||||
shellapi::DragFinish(hdrop);
|
||||
|
||||
S_OK
|
||||
}
|
||||
|
||||
unsafe fn from_interface<'a, InterfaceT>(this: *mut InterfaceT) -> &'a mut FileDropHandlerData {
|
||||
&mut *(this as *mut _)
|
||||
}
|
||||
|
||||
unsafe fn iterate_filenames<F>(data_obj: *const IDataObject, callback: F) -> shellapi::HDROP
|
||||
where
|
||||
F: Fn(PathBuf),
|
||||
{
|
||||
use winapi::ctypes::wchar_t;
|
||||
use winapi::shared::winerror::SUCCEEDED;
|
||||
use winapi::shared::wtypes::{CLIPFORMAT, DVASPECT_CONTENT};
|
||||
use winapi::um::objidl::{FORMATETC, TYMED_HGLOBAL};
|
||||
use winapi::um::shellapi::DragQueryFileW;
|
||||
use winapi::um::winuser::CF_HDROP;
|
||||
|
||||
let mut drop_format = FORMATETC {
|
||||
cfFormat: CF_HDROP as CLIPFORMAT,
|
||||
ptd: ptr::null(),
|
||||
dwAspect: DVASPECT_CONTENT,
|
||||
lindex: -1,
|
||||
tymed: TYMED_HGLOBAL,
|
||||
};
|
||||
|
||||
let mut medium = mem::uninitialized();
|
||||
if SUCCEEDED((*data_obj).GetData(&mut drop_format, &mut medium)) {
|
||||
let hglobal = (*medium.u).hGlobal();
|
||||
let hdrop = (*hglobal) as shellapi::HDROP;
|
||||
|
||||
// The second parameter (0xFFFFFFFF) instructs the function to return the item count
|
||||
let item_count = DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0);
|
||||
|
||||
let mut pathbuf: [wchar_t; MAX_PATH] = mem::uninitialized();
|
||||
|
||||
for i in 0..item_count {
|
||||
let character_count =
|
||||
DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(), MAX_PATH as UINT) as usize;
|
||||
|
||||
if character_count > 0 {
|
||||
callback(OsString::from_wide(&pathbuf[0..character_count]).into());
|
||||
}
|
||||
}
|
||||
|
||||
return hdrop;
|
||||
}
|
||||
|
||||
// The call to `GetData` must succeed and the file handle must be returned before this
|
||||
// point
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FileDropHandler {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
FileDropHandler::Release(self.data as *mut unknwnbase::IUnknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static DROP_TARGET_VTBL: IDropTargetVtbl = IDropTargetVtbl {
|
||||
parent: unknwnbase::IUnknownVtbl {
|
||||
QueryInterface: FileDropHandler::QueryInterface,
|
||||
AddRef: FileDropHandler::AddRef,
|
||||
Release: FileDropHandler::Release,
|
||||
},
|
||||
DragEnter: FileDropHandler::DragEnter,
|
||||
DragOver: FileDropHandler::DragOver,
|
||||
DragLeave: FileDropHandler::DragLeave,
|
||||
Drop: FileDropHandler::Drop,
|
||||
};
|
||||
@@ -148,7 +148,7 @@ pub fn vkey_to_winit_vkey(vkey: c_int) -> Option<VirtualKeyCode> {
|
||||
winuser::VK_F13 => Some(VirtualKeyCode::F13),
|
||||
winuser::VK_F14 => Some(VirtualKeyCode::F14),
|
||||
winuser::VK_F15 => Some(VirtualKeyCode::F15),
|
||||
/*winuser::VK_F16 => Some(VirtualKeyCode::F16),
|
||||
winuser::VK_F16 => Some(VirtualKeyCode::F16),
|
||||
winuser::VK_F17 => Some(VirtualKeyCode::F17),
|
||||
winuser::VK_F18 => Some(VirtualKeyCode::F18),
|
||||
winuser::VK_F19 => Some(VirtualKeyCode::F19),
|
||||
@@ -156,7 +156,7 @@ pub fn vkey_to_winit_vkey(vkey: c_int) -> Option<VirtualKeyCode> {
|
||||
winuser::VK_F21 => Some(VirtualKeyCode::F21),
|
||||
winuser::VK_F22 => Some(VirtualKeyCode::F22),
|
||||
winuser::VK_F23 => Some(VirtualKeyCode::F23),
|
||||
winuser::VK_F24 => Some(VirtualKeyCode::F24),*/
|
||||
winuser::VK_F24 => Some(VirtualKeyCode::F24),
|
||||
winuser::VK_NUMLOCK => Some(VirtualKeyCode::Numlock),
|
||||
winuser::VK_SCROLL => Some(VirtualKeyCode::Scroll),
|
||||
winuser::VK_BROWSER_BACK => Some(VirtualKeyCode::NavigateBackward),
|
||||
|
||||
@@ -15,10 +15,8 @@
|
||||
use std::{mem, ptr, thread};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsString;
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
use std::os::windows::io::AsRawHandle;
|
||||
use std::sync::{Arc, Barrier, Condvar, mpsc, Mutex};
|
||||
use std::sync::{Arc, Barrier, mpsc, Mutex};
|
||||
|
||||
use winapi::ctypes::c_int;
|
||||
use winapi::shared::minwindef::{
|
||||
@@ -29,13 +27,14 @@ use winapi::shared::minwindef::{
|
||||
LOWORD,
|
||||
LPARAM,
|
||||
LRESULT,
|
||||
MAX_PATH,
|
||||
UINT,
|
||||
WPARAM,
|
||||
};
|
||||
use winapi::shared::windef::{HWND, POINT, RECT};
|
||||
use winapi::shared::windowsx;
|
||||
use winapi::um::{winuser, shellapi, processthreadsapi};
|
||||
use winapi::shared::winerror::S_OK;
|
||||
use winapi::um::{winuser, processthreadsapi, ole2};
|
||||
use winapi::um::oleidl::LPDROPTARGET;
|
||||
use winapi::um::winnt::{LONG, LPCSTR, SHORT};
|
||||
|
||||
use {
|
||||
@@ -57,7 +56,9 @@ use platform::platform::dpi::{
|
||||
enable_non_client_dpi_scaling,
|
||||
get_hwnd_scale_factor,
|
||||
};
|
||||
use platform::platform::drop_handler::FileDropHandler;
|
||||
use platform::platform::event::{handle_extended_keys, process_key_params, vkey_to_winit_vkey};
|
||||
use platform::platform::icon::WinIcon;
|
||||
use platform::platform::raw_input::{get_raw_input_data, get_raw_mouse_button_state};
|
||||
use platform::platform::window::adjust_size;
|
||||
|
||||
@@ -94,6 +95,13 @@ pub struct WindowState {
|
||||
// This is different from the value in `SavedWindowInfo`! That one represents the DPI saved upon entering
|
||||
// fullscreen. This will always be the most recent DPI for the window.
|
||||
pub dpi_factor: f64,
|
||||
pub fullscreen: Option<::MonitorId>,
|
||||
pub window_icon: Option<WinIcon>,
|
||||
pub taskbar_icon: Option<WinIcon>,
|
||||
pub decorations: bool,
|
||||
pub always_on_top: bool,
|
||||
pub maximized: bool,
|
||||
pub resizable: bool,
|
||||
}
|
||||
|
||||
impl WindowState {
|
||||
@@ -130,10 +138,6 @@ pub struct EventsLoop {
|
||||
thread_id: DWORD,
|
||||
// Receiver for the events. The sender is in the background thread.
|
||||
receiver: mpsc::Receiver<Event>,
|
||||
// Variable that contains the block state of the win32 event loop thread during a WM_SIZE event.
|
||||
// The mutex's value is `true` when it's blocked, and should be set to false when it's done
|
||||
// blocking. That's done by the parent thread when it receives a Resized event.
|
||||
win32_block_loop: Arc<(Mutex<bool>, Condvar)>,
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
@@ -146,8 +150,6 @@ impl EventsLoop {
|
||||
|
||||
// The main events transfer channel.
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let win32_block_loop = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
let win32_block_loop_child = win32_block_loop.clone();
|
||||
|
||||
// Local barrier in order to block the `new()` function until the background thread has
|
||||
// an events queue.
|
||||
@@ -159,8 +161,8 @@ impl EventsLoop {
|
||||
*context_stash.borrow_mut() = Some(ThreadLocalData {
|
||||
sender: tx,
|
||||
windows: HashMap::with_capacity(4),
|
||||
win32_block_loop: win32_block_loop_child,
|
||||
mouse_buttons_down: 0
|
||||
file_drop_handlers: HashMap::with_capacity(4),
|
||||
mouse_buttons_down: 0,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -212,7 +214,6 @@ impl EventsLoop {
|
||||
EventsLoop {
|
||||
thread_id,
|
||||
receiver: rx,
|
||||
win32_block_loop,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,18 +225,8 @@ impl EventsLoop {
|
||||
Ok(e) => e,
|
||||
Err(_) => return
|
||||
};
|
||||
let is_resize = match event {
|
||||
Event::WindowEvent{ event: WindowEvent::Resized(..), .. } => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
callback(event);
|
||||
if is_resize {
|
||||
let (ref mutex, ref cvar) = *self.win32_block_loop;
|
||||
let mut block_thread = mutex.lock().unwrap();
|
||||
*block_thread = false;
|
||||
cvar.notify_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,18 +238,8 @@ impl EventsLoop {
|
||||
Ok(e) => e,
|
||||
Err(_) => return
|
||||
};
|
||||
let is_resize = match event {
|
||||
Event::WindowEvent{ event: WindowEvent::Resized(..), .. } => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
let flow = callback(event);
|
||||
if is_resize {
|
||||
let (ref mutex, ref cvar) = *self.win32_block_loop;
|
||||
let mut block_thread = mutex.lock().unwrap();
|
||||
*block_thread = false;
|
||||
cvar.notify_all();
|
||||
}
|
||||
match flow {
|
||||
ControlFlow::Continue => continue,
|
||||
ControlFlow::Break => break,
|
||||
@@ -326,6 +307,11 @@ impl EventsLoopProxy {
|
||||
///
|
||||
/// The `Inserted` can be used to inject a `WindowState` for the callback to use. The state is
|
||||
/// removed automatically if the callback receives a `WM_CLOSE` message for the window.
|
||||
///
|
||||
/// Note that if you are using this to change some property of a window and updating
|
||||
/// `WindowState` then you should call this within the lock of `WindowState`. Otherwise the
|
||||
/// events may be sent to the other thread in different order to the one in which you set
|
||||
/// `WindowState`, leaving them out of sync.
|
||||
pub fn execute_in_thread<F>(&self, function: F)
|
||||
where
|
||||
F: FnMut(Inserter) + Send + 'static,
|
||||
@@ -357,7 +343,7 @@ lazy_static! {
|
||||
}
|
||||
};
|
||||
// Message sent when we want to execute a closure in the thread.
|
||||
// WPARAM contains a Box<Box<FnMut()>> that must be retreived with `Box::from_raw`,
|
||||
// WPARAM contains a Box<Box<FnMut()>> that must be retrieved with `Box::from_raw`,
|
||||
// and LPARAM is unused.
|
||||
static ref EXEC_MSG_ID: u32 = {
|
||||
unsafe {
|
||||
@@ -386,12 +372,12 @@ thread_local!(static CONTEXT_STASH: RefCell<Option<ThreadLocalData>> = RefCell::
|
||||
struct ThreadLocalData {
|
||||
sender: mpsc::Sender<Event>,
|
||||
windows: HashMap<HWND, Arc<Mutex<WindowState>>>,
|
||||
win32_block_loop: Arc<(Mutex<bool>, Condvar)>,
|
||||
mouse_buttons_down: u32
|
||||
file_drop_handlers: HashMap<HWND, FileDropHandler>, // Each window has its own drop handler.
|
||||
mouse_buttons_down: u32,
|
||||
}
|
||||
|
||||
// Utility function that dispatches an event on the current thread.
|
||||
fn send_event(event: Event) {
|
||||
pub fn send_event(event: Event) {
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let context_stash = context_stash.borrow();
|
||||
|
||||
@@ -439,6 +425,30 @@ pub unsafe extern "system" fn callback(
|
||||
lparam: LPARAM,
|
||||
) -> LRESULT {
|
||||
match msg {
|
||||
winuser::WM_CREATE => {
|
||||
use winapi::shared::winerror::{OLE_E_WRONGCOMPOBJ, RPC_E_CHANGED_MODE};
|
||||
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`");
|
||||
}
|
||||
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
|
||||
let drop_handlers = &mut context_stash.as_mut().unwrap().file_drop_handlers;
|
||||
let new_handler = FileDropHandler::new(window);
|
||||
let handler_interface_ptr = &mut (*new_handler.data).interface as LPDROPTARGET;
|
||||
drop_handlers.insert(window, new_handler);
|
||||
|
||||
assert_eq!(ole2::RegisterDragDrop(window, handler_interface_ptr), S_OK);
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_NCCREATE => {
|
||||
enable_non_client_dpi_scaling(window);
|
||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
@@ -457,7 +467,10 @@ pub unsafe extern "system" fn callback(
|
||||
use events::WindowEvent::Destroyed;
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
context_stash.as_mut().unwrap().windows.remove(&window);
|
||||
ole2::RevokeDragDrop(window);
|
||||
let context_stash_mut = context_stash.as_mut().unwrap();
|
||||
context_stash_mut.file_drop_handlers.remove(&window);
|
||||
context_stash_mut.windows.remove(&window);
|
||||
});
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
@@ -514,24 +527,7 @@ pub unsafe extern "system" fn callback(
|
||||
event: Resized(logical_size),
|
||||
};
|
||||
|
||||
// If this window has been inserted into the window map, the resize event happened
|
||||
// during the event loop. If it hasn't, the event happened on window creation and
|
||||
// should be ignored.
|
||||
if cstash.windows.get(&window).is_some() {
|
||||
let (ref mutex, ref cvar) = *cstash.win32_block_loop;
|
||||
let mut block_thread = mutex.lock().unwrap();
|
||||
*block_thread = true;
|
||||
|
||||
// The event needs to be sent after the lock to ensure that `notify_all` is
|
||||
// called after `wait`.
|
||||
cstash.sender.send(event).ok();
|
||||
|
||||
while *block_thread {
|
||||
block_thread = cvar.wait(block_thread).unwrap();
|
||||
}
|
||||
} else {
|
||||
cstash.sender.send(event).ok();
|
||||
}
|
||||
cstash.sender.send(event).ok();
|
||||
});
|
||||
0
|
||||
},
|
||||
@@ -1026,24 +1022,7 @@ pub unsafe extern "system" fn callback(
|
||||
},
|
||||
|
||||
winuser::WM_DROPFILES => {
|
||||
use events::WindowEvent::DroppedFile;
|
||||
|
||||
let hdrop = wparam as shellapi::HDROP;
|
||||
let mut pathbuf: [u16; MAX_PATH] = mem::uninitialized();
|
||||
let num_drops = shellapi::DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0);
|
||||
|
||||
for i in 0..num_drops {
|
||||
let nch = shellapi::DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(),
|
||||
MAX_PATH as u32) as usize;
|
||||
if nch > 0 {
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: DroppedFile(OsString::from_wide(&pathbuf[0..nch]).into())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
shellapi::DragFinish(hdrop);
|
||||
// See `FileDropHandler` for implementation.
|
||||
0
|
||||
},
|
||||
|
||||
|
||||
@@ -22,11 +22,13 @@ pub enum IconType {
|
||||
Big = winuser::ICON_BIG as isize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WinIcon {
|
||||
pub handle: HICON,
|
||||
}
|
||||
|
||||
unsafe impl Send for WinIcon {}
|
||||
|
||||
impl WinIcon {
|
||||
#[allow(dead_code)]
|
||||
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, util::WinError> {
|
||||
|
||||
@@ -18,7 +18,7 @@ unsafe impl Send for PlatformSpecificWindowBuilderAttributes {}
|
||||
unsafe impl Sync for PlatformSpecificWindowBuilderAttributes {}
|
||||
|
||||
// Cursor name in UTF-16. Used to set cursor in `WM_SETCURSOR`.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Cursor(pub *const winapi::ctypes::wchar_t);
|
||||
unsafe impl Send for Cursor {}
|
||||
unsafe impl Sync for Cursor {}
|
||||
@@ -49,6 +49,7 @@ unsafe impl Send for WindowId {}
|
||||
unsafe impl Sync for WindowId {}
|
||||
|
||||
mod dpi;
|
||||
mod drop_handler;
|
||||
mod event;
|
||||
mod events_loop;
|
||||
mod icon;
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::ffi::OsStr;
|
||||
use std::{io, mem, ptr};
|
||||
use std::cell::Cell;
|
||||
use std::ffi::OsStr;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
use winapi::ctypes::c_int;
|
||||
use winapi::shared::minwindef::{BOOL, DWORD, FALSE, LPARAM, TRUE, UINT, WORD, WPARAM};
|
||||
use winapi::shared::windef::{HDC, HWND, LPPOINT, POINT, RECT};
|
||||
use winapi::shared::windef::{HWND, LPPOINT, POINT, RECT};
|
||||
use winapi::um::{combaseapi, dwmapi, libloaderapi, winuser};
|
||||
use winapi::um::objbase::{COINIT_MULTITHREADED};
|
||||
use winapi::um::objbase::COINIT_MULTITHREADED;
|
||||
use winapi::um::shobjidl_core::{CLSID_TaskbarList, ITaskbarList2};
|
||||
use winapi::um::wingdi::{CreateRectRgn, DeleteObject};
|
||||
use winapi::um::winnt::{LONG, LPCWSTR};
|
||||
|
||||
use {
|
||||
@@ -26,8 +27,9 @@ use {
|
||||
WindowAttributes,
|
||||
};
|
||||
use platform::platform::{Cursor, PlatformSpecificWindowBuilderAttributes, WindowId};
|
||||
use platform::platform::dpi::{dpi_to_scale_factor, get_window_dpi, get_window_scale_factor};
|
||||
use platform::platform::events_loop::{self, DESTROY_MSG_ID, EventsLoop, INITIAL_DPI_MSG_ID};
|
||||
use platform::platform::dpi::{dpi_to_scale_factor, get_hwnd_dpi};
|
||||
use platform::platform::events_loop::{self, EventsLoop, DESTROY_MSG_ID, INITIAL_DPI_MSG_ID};
|
||||
use platform::platform::events_loop::WindowState;
|
||||
use platform::platform::icon::{self, IconType, WinIcon};
|
||||
use platform::platform::monitor::get_available_monitors;
|
||||
use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input;
|
||||
@@ -40,25 +42,13 @@ pub struct Window {
|
||||
/// Main handle for the window.
|
||||
window: WindowWrapper,
|
||||
|
||||
decorations: Cell<bool>,
|
||||
maximized: Cell<bool>,
|
||||
resizable: Cell<bool>,
|
||||
fullscreen: RefCell<Option<::MonitorId>>,
|
||||
always_on_top: Cell<bool>,
|
||||
|
||||
/// The current window state.
|
||||
window_state: Arc<Mutex<events_loop::WindowState>>,
|
||||
|
||||
window_icon: Cell<Option<WinIcon>>,
|
||||
taskbar_icon: Cell<Option<WinIcon>>,
|
||||
window_state: Arc<Mutex<WindowState>>,
|
||||
|
||||
// The events loop proxy.
|
||||
events_loop_proxy: events_loop::EventsLoopProxy,
|
||||
}
|
||||
|
||||
unsafe impl Send for Window {}
|
||||
unsafe impl Sync for Window {}
|
||||
|
||||
// https://blogs.msdn.microsoft.com/oldnewthing/20131017-00/?p=2903
|
||||
// The idea here is that we use the AdjustWindowRectEx function to calculate how much additional
|
||||
// non-client area gets added due to the styles we passed. To make the math simple,
|
||||
@@ -283,32 +273,25 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
if resizable == self.resizable.get() {
|
||||
return;
|
||||
}
|
||||
if self.fullscreen.borrow().is_some() {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
if mem::replace(&mut window_state.resizable, resizable) != resizable {
|
||||
// If we're in fullscreen, update stored configuration but don't apply anything.
|
||||
self.resizable.replace(resizable);
|
||||
return;
|
||||
}
|
||||
if window_state.fullscreen.is_none() {
|
||||
let mut style = unsafe {
|
||||
winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE)
|
||||
};
|
||||
|
||||
let mut style = unsafe {
|
||||
winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE)
|
||||
};
|
||||
if resizable {
|
||||
style |= WS_RESIZABLE as LONG;
|
||||
} else {
|
||||
style &= !WS_RESIZABLE as LONG;
|
||||
}
|
||||
if resizable {
|
||||
style |= WS_RESIZABLE as LONG;
|
||||
} else {
|
||||
style &= !WS_RESIZABLE as LONG;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
winuser::SetWindowLongW(
|
||||
self.window.0,
|
||||
winuser::GWL_STYLE,
|
||||
style as _,
|
||||
);
|
||||
};
|
||||
self.resizable.replace(resizable);
|
||||
unsafe {
|
||||
winuser::SetWindowLongW(self.window.0, winuser::GWL_STYLE, style as _);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `hwnd` of this window.
|
||||
@@ -319,7 +302,7 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||
let cursor_id = match cursor {
|
||||
let cursor_id = Cursor(match cursor {
|
||||
MouseCursor::Arrow | MouseCursor::Default => winuser::IDC_ARROW,
|
||||
MouseCursor::Hand => winuser::IDC_HAND,
|
||||
MouseCursor::Crosshair => winuser::IDC_CROSS,
|
||||
@@ -339,10 +322,15 @@ impl Window {
|
||||
MouseCursor::Progress => winuser::IDC_APPSTARTING,
|
||||
MouseCursor::Help => winuser::IDC_HELP,
|
||||
_ => winuser::IDC_ARROW, // use arrow for the missing cases.
|
||||
};
|
||||
|
||||
let mut cur = self.window_state.lock().unwrap();
|
||||
cur.cursor = Cursor(cursor_id);
|
||||
});
|
||||
self.window_state.lock().unwrap().cursor = cursor_id;
|
||||
self.events_loop_proxy.execute_in_thread(move |_| unsafe {
|
||||
let cursor = winuser::LoadCursorW(
|
||||
ptr::null_mut(),
|
||||
cursor_id.0,
|
||||
);
|
||||
winuser::SetCursor(cursor);
|
||||
});
|
||||
}
|
||||
|
||||
unsafe fn cursor_is_grabbed(&self) -> Result<bool, String> {
|
||||
@@ -391,15 +379,12 @@ impl Window {
|
||||
#[inline]
|
||||
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
|
||||
let currently_grabbed = unsafe { self.cursor_is_grabbed() }?;
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
{
|
||||
let window_state_lock = window_state.lock().unwrap();
|
||||
if currently_grabbed == grab
|
||||
&& grab == window_state_lock.cursor_grabbed {
|
||||
return Ok(());
|
||||
}
|
||||
let window_state_lock = self.window_state.lock().unwrap();
|
||||
if currently_grabbed == grab && grab == window_state_lock.cursor_grabbed {
|
||||
return Ok(());
|
||||
}
|
||||
let window = self.window.clone();
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
let (tx, rx) = channel();
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
let result = unsafe { Self::grab_cursor_inner(&window, grab) };
|
||||
@@ -408,6 +393,7 @@ impl Window {
|
||||
}
|
||||
let _ = tx.send(result);
|
||||
});
|
||||
drop(window_state_lock);
|
||||
rx.recv().unwrap()
|
||||
}
|
||||
|
||||
@@ -421,24 +407,23 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn hide_cursor(&self, hide: bool) {
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
{
|
||||
let window_state_lock = window_state.lock().unwrap();
|
||||
// We don't want to increment/decrement the display count more than once!
|
||||
if hide == window_state_lock.cursor_hidden { return; }
|
||||
}
|
||||
let window_state_lock = self.window_state.lock().unwrap();
|
||||
// We don't want to increment/decrement the display count more than once!
|
||||
if hide == window_state_lock.cursor_hidden { return; }
|
||||
let (tx, rx) = channel();
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
unsafe { Self::hide_cursor_inner(hide) };
|
||||
window_state.lock().unwrap().cursor_hidden = hide;
|
||||
let _ = tx.send(());
|
||||
});
|
||||
drop(window_state_lock);
|
||||
rx.recv().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
get_window_scale_factor(self.window.0, self.window.1)
|
||||
self.window_state.lock().unwrap().dpi_factor
|
||||
}
|
||||
|
||||
fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), String> {
|
||||
@@ -468,32 +453,32 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
self.maximized.replace(maximized);
|
||||
// We only maximize if we're not in fullscreen.
|
||||
if self.fullscreen.borrow().is_some() { return; }
|
||||
|
||||
let window = self.window.clone();
|
||||
unsafe {
|
||||
// `ShowWindow` resizes the window, so it must be called from the main thread.
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
winuser::ShowWindow(
|
||||
window.0,
|
||||
if maximized {
|
||||
winuser::SW_MAXIMIZE
|
||||
} else {
|
||||
winuser::SW_RESTORE
|
||||
},
|
||||
);
|
||||
});
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
if mem::replace(&mut window_state.maximized, maximized) != maximized {
|
||||
// We only maximize if we're not in fullscreen.
|
||||
if window_state.fullscreen.is_none() {
|
||||
let window = self.window.clone();
|
||||
unsafe {
|
||||
// `ShowWindow` resizes the window, so it must be called from the main thread.
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
winuser::ShowWindow(
|
||||
window.0,
|
||||
if maximized {
|
||||
winuser::SW_MAXIMIZE
|
||||
} else {
|
||||
winuser::SW_RESTORE
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn set_fullscreen_style(&self) -> (LONG, LONG) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
|
||||
if self.fullscreen.borrow().is_none() || window_state.saved_window_info.is_none() {
|
||||
unsafe fn set_fullscreen_style(&self, window_state: &mut WindowState) -> (LONG, LONG) {
|
||||
if window_state.fullscreen.is_none() || window_state.saved_window_info.is_none() {
|
||||
let rect = util::get_window_rect(self.window.0).expect("`GetWindowRect` failed");
|
||||
let dpi_factor = Some(self.get_hidpi_factor());
|
||||
let dpi_factor = Some(window_state.dpi_factor);
|
||||
window_state.saved_window_info = Some(events_loop::SavedWindowInfo {
|
||||
style: winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE),
|
||||
ex_style: winuser::GetWindowLongW(self.window.0, winuser::GWL_EXSTYLE),
|
||||
@@ -507,16 +492,14 @@ impl Window {
|
||||
let mut placement: winuser::WINDOWPLACEMENT = mem::zeroed();
|
||||
placement.length = mem::size_of::<winuser::WINDOWPLACEMENT>() as u32;
|
||||
winuser::GetWindowPlacement(self.window.0, &mut placement);
|
||||
self.maximized.replace(placement.showCmd == (winuser::SW_SHOWMAXIMIZED as u32));
|
||||
window_state.maximized = placement.showCmd == (winuser::SW_SHOWMAXIMIZED as u32);
|
||||
let saved_window_info = window_state.saved_window_info.as_ref().unwrap();
|
||||
|
||||
(saved_window_info.style, saved_window_info.ex_style)
|
||||
}
|
||||
|
||||
unsafe fn restore_saved_window(&self) {
|
||||
unsafe fn restore_saved_window(&self, window_state_lock: &mut WindowState) {
|
||||
let (rect, mut style, ex_style) = {
|
||||
let mut window_state_lock = self.window_state.lock().unwrap();
|
||||
|
||||
// 'saved_window_info' can be None if the window has never been
|
||||
// in fullscreen mode before this method gets called.
|
||||
if window_state_lock.saved_window_info.is_none() {
|
||||
@@ -537,8 +520,8 @@ impl Window {
|
||||
let window = self.window.clone();
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
|
||||
let maximized = self.maximized.get();
|
||||
let resizable = self.resizable.get();
|
||||
let resizable = window_state_lock.resizable;
|
||||
let maximized = window_state_lock.maximized;
|
||||
|
||||
// We're restoring the window to its size and position from before being fullscreened.
|
||||
// `ShowWindow` resizes the window, so it must be called from the main thread.
|
||||
@@ -585,6 +568,7 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
|
||||
let mut window_state_lock = self.window_state.lock().unwrap();
|
||||
unsafe {
|
||||
match &monitor {
|
||||
&Some(RootMonitorId { ref inner }) => {
|
||||
@@ -593,8 +577,7 @@ impl Window {
|
||||
let window = self.window.clone();
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
|
||||
let (style, ex_style) = self.set_fullscreen_style();
|
||||
|
||||
let (style, ex_style) = self.set_fullscreen_style(&mut window_state_lock);
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
let _ = Self::grab_cursor_inner(&window, false);
|
||||
|
||||
@@ -634,27 +617,23 @@ impl Window {
|
||||
});
|
||||
}
|
||||
&None => {
|
||||
self.restore_saved_window();
|
||||
self.restore_saved_window(&mut window_state_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.fullscreen.replace(monitor);
|
||||
window_state_lock.fullscreen = monitor;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, decorations: bool) {
|
||||
if self.decorations.get() == decorations {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
if mem::replace(&mut window_state.decorations, decorations) != decorations {
|
||||
let style_flags = (winuser::WS_CAPTION | winuser::WS_THICKFRAME) as LONG;
|
||||
let ex_style_flags = (winuser::WS_EX_WINDOWEDGE) as LONG;
|
||||
|
||||
// if we are in fullscreen mode, we only change the saved window info
|
||||
if self.fullscreen.borrow().is_some() {
|
||||
{
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
// if we are in fullscreen mode, we only change the saved window info
|
||||
if window_state.fullscreen.is_some() {
|
||||
let saved = window_state.saved_window_info.as_mut().unwrap();
|
||||
|
||||
unsafe {
|
||||
@@ -677,81 +656,73 @@ impl Window {
|
||||
saved.ex_style as _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.decorations.replace(decorations);
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let mut rect: RECT = mem::zeroed();
|
||||
winuser::GetWindowRect(self.window.0, &mut rect);
|
||||
|
||||
let mut style = winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE);
|
||||
let mut ex_style = winuser::GetWindowLongW(self.window.0, winuser::GWL_EXSTYLE);
|
||||
unjust_window_rect(&mut rect, style as _, ex_style as _);
|
||||
|
||||
if decorations {
|
||||
style = style | style_flags;
|
||||
ex_style = ex_style | ex_style_flags;
|
||||
} else {
|
||||
style = style & !style_flags;
|
||||
ex_style = ex_style & !ex_style_flags;
|
||||
unsafe {
|
||||
let mut rect: RECT = mem::zeroed();
|
||||
winuser::GetWindowRect(self.window.0, &mut rect);
|
||||
|
||||
let mut style = winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE);
|
||||
let mut ex_style = winuser::GetWindowLongW(self.window.0, winuser::GWL_EXSTYLE);
|
||||
unjust_window_rect(&mut rect, style as _, ex_style as _);
|
||||
|
||||
if decorations {
|
||||
style = style | style_flags;
|
||||
ex_style = ex_style | ex_style_flags;
|
||||
} else {
|
||||
style = style & !style_flags;
|
||||
ex_style = ex_style & !ex_style_flags;
|
||||
}
|
||||
|
||||
let window = self.window.clone();
|
||||
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
winuser::SetWindowLongW(window.0, winuser::GWL_STYLE, style);
|
||||
winuser::SetWindowLongW(window.0, winuser::GWL_EXSTYLE, ex_style);
|
||||
winuser::AdjustWindowRectEx(&mut rect, style as _, 0, ex_style as _);
|
||||
|
||||
winuser::SetWindowPos(
|
||||
window.0,
|
||||
ptr::null_mut(),
|
||||
rect.left,
|
||||
rect.top,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top,
|
||||
winuser::SWP_ASYNCWINDOWPOS
|
||||
| winuser::SWP_NOZORDER
|
||||
| winuser::SWP_NOACTIVATE
|
||||
| winuser::SWP_FRAMECHANGED,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let window = self.window.clone();
|
||||
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
winuser::SetWindowLongW(window.0, winuser::GWL_STYLE, style);
|
||||
winuser::SetWindowLongW(window.0, winuser::GWL_EXSTYLE, ex_style);
|
||||
winuser::AdjustWindowRectEx(&mut rect, style as _, 0, ex_style as _);
|
||||
|
||||
winuser::SetWindowPos(
|
||||
window.0,
|
||||
ptr::null_mut(),
|
||||
rect.left,
|
||||
rect.top,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top,
|
||||
winuser::SWP_ASYNCWINDOWPOS
|
||||
| winuser::SWP_NOZORDER
|
||||
| winuser::SWP_NOACTIVATE
|
||||
| winuser::SWP_FRAMECHANGED,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
self.decorations.replace(decorations);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_always_on_top(&self, always_on_top: bool) {
|
||||
if self.always_on_top.get() == always_on_top {
|
||||
return;
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
if mem::replace(&mut window_state.always_on_top, always_on_top) != always_on_top {
|
||||
let window = self.window.clone();
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
let insert_after = if always_on_top {
|
||||
winuser::HWND_TOPMOST
|
||||
} else {
|
||||
winuser::HWND_NOTOPMOST
|
||||
};
|
||||
unsafe {
|
||||
winuser::SetWindowPos(
|
||||
window.0,
|
||||
insert_after,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOMOVE | winuser::SWP_NOSIZE,
|
||||
);
|
||||
winuser::UpdateWindow(window.0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let window = self.window.clone();
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
let insert_after = if always_on_top {
|
||||
winuser::HWND_TOPMOST
|
||||
} else {
|
||||
winuser::HWND_NOTOPMOST
|
||||
};
|
||||
unsafe {
|
||||
winuser::SetWindowPos(
|
||||
window.0,
|
||||
insert_after,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOMOVE | winuser::SWP_NOSIZE,
|
||||
);
|
||||
winuser::UpdateWindow(window.0);
|
||||
}
|
||||
});
|
||||
|
||||
self.always_on_top.replace(always_on_top);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -771,7 +742,7 @@ impl Window {
|
||||
} else {
|
||||
icon::unset_for_window(self.window.0, IconType::Small);
|
||||
}
|
||||
self.window_icon.replace(window_icon);
|
||||
self.window_state.lock().unwrap().window_icon = window_icon;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -784,7 +755,7 @@ impl Window {
|
||||
} else {
|
||||
icon::unset_for_window(self.window.0, IconType::Big);
|
||||
}
|
||||
self.taskbar_icon.replace(taskbar_icon);
|
||||
self.window_state.lock().unwrap().taskbar_icon = taskbar_icon;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -807,17 +778,27 @@ impl Drop for Window {
|
||||
/// A simple non-owning wrapper around a window.
|
||||
#[doc(hidden)]
|
||||
#[derive(Clone)]
|
||||
pub struct WindowWrapper(HWND, HDC);
|
||||
pub struct WindowWrapper(HWND);
|
||||
|
||||
// Send is not implemented for HWND and HDC, we have to wrap it and implement it manually.
|
||||
// Send and Sync are not implemented for HWND and HDC, we have to wrap it and implement them manually.
|
||||
// For more info see:
|
||||
// https://github.com/retep998/winapi-rs/issues/360
|
||||
// https://github.com/retep998/winapi-rs/issues/396
|
||||
unsafe impl Sync for WindowWrapper {}
|
||||
unsafe impl Send for WindowWrapper {}
|
||||
|
||||
pub unsafe fn adjust_size(physical_size: PhysicalSize, style: DWORD, ex_style: DWORD) -> (LONG, LONG) {
|
||||
pub unsafe fn adjust_size(
|
||||
physical_size: PhysicalSize,
|
||||
style: DWORD,
|
||||
ex_style: DWORD,
|
||||
) -> (LONG, LONG) {
|
||||
let (width, height): (u32, u32) = physical_size.into();
|
||||
let mut rect = RECT { left: 0, right: width as LONG, top: 0, bottom: height as LONG };
|
||||
let mut rect = RECT {
|
||||
left: 0,
|
||||
right: width as LONG,
|
||||
top: 0,
|
||||
bottom: height as LONG,
|
||||
};
|
||||
winuser::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
|
||||
(rect.right - rect.left, rect.bottom - rect.top)
|
||||
}
|
||||
@@ -923,6 +904,9 @@ unsafe fn init(
|
||||
if pl_attribs.no_redirection_bitmap {
|
||||
ex_style |= winuser::WS_EX_NOREDIRECTIONBITMAP;
|
||||
}
|
||||
if attributes.transparent && attributes.decorations {
|
||||
ex_style |= winuser::WS_EX_LAYERED;
|
||||
}
|
||||
|
||||
// adjusting the window coordinates using the style
|
||||
winuser::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
|
||||
@@ -978,13 +962,7 @@ unsafe fn init(
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
let hdc = winuser::GetDC(handle);
|
||||
if hdc.is_null() {
|
||||
return Err(CreationError::OsError(format!("GetDC function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
WindowWrapper(handle, hdc)
|
||||
WindowWrapper(handle)
|
||||
};
|
||||
|
||||
// Set up raw input
|
||||
@@ -998,7 +976,7 @@ unsafe fn init(
|
||||
}
|
||||
}
|
||||
|
||||
let dpi = get_window_dpi(real_window.0, real_window.1);
|
||||
let dpi = get_hwnd_dpi(real_window.0);
|
||||
let dpi_factor = dpi_to_scale_factor(dpi);
|
||||
if dpi_factor != guessed_dpi_factor {
|
||||
let (width, height): (u32, u32) = dimensions.into();
|
||||
@@ -1029,6 +1007,13 @@ unsafe fn init(
|
||||
mouse_in_window: false,
|
||||
saved_window_info: None,
|
||||
dpi_factor,
|
||||
fullscreen: attributes.fullscreen.clone(),
|
||||
window_icon,
|
||||
taskbar_icon,
|
||||
decorations: attributes.decorations,
|
||||
maximized: attributes.maximized,
|
||||
resizable: attributes.resizable,
|
||||
always_on_top: attributes.always_on_top,
|
||||
};
|
||||
// Creating a mutex to track the current window state
|
||||
Arc::new(Mutex::new(window_state))
|
||||
@@ -1036,26 +1021,34 @@ unsafe fn init(
|
||||
|
||||
// making the window transparent
|
||||
if attributes.transparent && !pl_attribs.no_redirection_bitmap {
|
||||
let region = CreateRectRgn(0, 0, -1, -1); // makes the window transparent
|
||||
|
||||
let bb = dwmapi::DWM_BLURBEHIND {
|
||||
dwFlags: 0x1, // FIXME: DWM_BB_ENABLE;
|
||||
dwFlags: dwmapi::DWM_BB_ENABLE | dwmapi::DWM_BB_BLURREGION,
|
||||
fEnable: 1,
|
||||
hRgnBlur: ptr::null_mut(),
|
||||
hRgnBlur: region,
|
||||
fTransitionOnMaximized: 0,
|
||||
};
|
||||
|
||||
dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb);
|
||||
DeleteObject(region as _);
|
||||
|
||||
if attributes.decorations {
|
||||
// HACK: When opaque (opacity 255), there is a trail whenever
|
||||
// the transparent window is moved. By reducing it to 254,
|
||||
// the window is rendered properly.
|
||||
let opacity = 254;
|
||||
|
||||
// The color key can be any value except for black (0x0).
|
||||
let color_key = 0x0030c100;
|
||||
|
||||
winuser::SetLayeredWindowAttributes(real_window.0, color_key, opacity, winuser::LWA_ALPHA);
|
||||
}
|
||||
}
|
||||
|
||||
let win = Window {
|
||||
window: real_window,
|
||||
window_state: window_state,
|
||||
decorations: Cell::new(attributes.decorations),
|
||||
maximized: Cell::new(attributes.maximized.clone()),
|
||||
resizable: Cell::new(attributes.resizable.clone()),
|
||||
fullscreen: RefCell::new(attributes.fullscreen.clone()),
|
||||
always_on_top: Cell::new(attributes.always_on_top),
|
||||
window_icon: Cell::new(window_icon),
|
||||
taskbar_icon: Cell::new(taskbar_icon),
|
||||
window_state,
|
||||
events_loop_proxy,
|
||||
};
|
||||
|
||||
@@ -1112,7 +1105,6 @@ unsafe fn register_window_class(
|
||||
class_name
|
||||
}
|
||||
|
||||
|
||||
struct ComInitialized(*mut ());
|
||||
impl Drop for ComInitialized {
|
||||
fn drop(&mut self) {
|
||||
|
||||
@@ -304,6 +304,10 @@ impl Window {
|
||||
///
|
||||
/// See the [`dpi`](dpi/index.html) module for more information.
|
||||
///
|
||||
/// Note that this value can change depending on user action (for example if the window is
|
||||
/// moved to another screen); as such, tracking `WindowEvent::HiDpiFactorChanged` events is
|
||||
/// the most robust way to track the DPI you need to use to draw.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **X11:** Can be overridden using the `WINIT_HIDPI_FACTOR` environment variable.
|
||||
|
||||
39
tests/serde_objects.rs
Normal file
39
tests/serde_objects.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
#![cfg(feature = "serde")]
|
||||
|
||||
extern crate serde;
|
||||
extern crate winit;
|
||||
|
||||
use winit::{ControlFlow, MouseCursor};
|
||||
use winit::{
|
||||
KeyboardInput, TouchPhase, ElementState, MouseButton, MouseScrollDelta, VirtualKeyCode,
|
||||
ModifiersState
|
||||
};
|
||||
use winit::dpi::{LogicalPosition, PhysicalPosition, LogicalSize, PhysicalSize};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
fn needs_serde<S: Serialize + Deserialize<'static>>() {}
|
||||
|
||||
#[test]
|
||||
fn root_serde() {
|
||||
needs_serde::<ControlFlow>();
|
||||
needs_serde::<MouseCursor>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn events_serde() {
|
||||
needs_serde::<KeyboardInput>();
|
||||
needs_serde::<TouchPhase>();
|
||||
needs_serde::<ElementState>();
|
||||
needs_serde::<MouseButton>();
|
||||
needs_serde::<MouseScrollDelta>();
|
||||
needs_serde::<VirtualKeyCode>();
|
||||
needs_serde::<ModifiersState>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dpi_serde() {
|
||||
needs_serde::<LogicalPosition>();
|
||||
needs_serde::<PhysicalPosition>();
|
||||
needs_serde::<LogicalSize>();
|
||||
needs_serde::<PhysicalSize>();
|
||||
}
|
||||
Reference in New Issue
Block a user