mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
63 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 | ||
|
|
c1ef1acfc0 | ||
|
|
040d3f5d8b | ||
|
|
ec393e4a90 | ||
|
|
1703d0417a | ||
|
|
fad72c0441 | ||
|
|
2f7321a076 | ||
|
|
85ee422acd | ||
|
|
089816d9ba | ||
|
|
c873c2db15 | ||
|
|
047c67baf3 | ||
|
|
8f394f117b | ||
|
|
fb7528c239 | ||
|
|
042f5fe4b3 | ||
|
|
289fb47a34 | ||
|
|
38bc6babb7 | ||
|
|
e7a8efcfa0 | ||
|
|
1b74822cfc | ||
|
|
f083dae328 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
Cargo.lock
|
||||
target/
|
||||
rls/
|
||||
.vscode/
|
||||
*~
|
||||
#*#
|
||||
|
||||
@@ -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:
|
||||
- |
|
||||
|
||||
87
CHANGELOG.md
87
CHANGELOG.md
@@ -1,5 +1,92 @@
|
||||
# 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.
|
||||
- Corrected incorrect `unreachable!` usage when guessing the DPI factor with no detected monitors.
|
||||
|
||||
# Version 0.16.1 (2018-07-02)
|
||||
|
||||
- Added logging through `log`. Logging will become more extensive over time.
|
||||
- On X11 and Windows, the window's DPI factor is guessed before creating the window. This *greatly* cuts back on unsightly auto-resizing that would occur immediately after window creation.
|
||||
- Fixed X11 backend compilation for environments where `c_char` is unsigned.
|
||||
|
||||
# Version 0.16.0 (2018-06-25)
|
||||
|
||||
- Windows additionally has `WindowBuilderExt::with_no_redirection_bitmap`.
|
||||
- **Breaking:** Removed `VirtualKeyCode::LMenu` and `VirtualKeyCode::RMenu`; Windows now generates `VirtualKeyCode::LAlt` and `VirtualKeyCode::RAlt` instead.
|
||||
- On X11, exiting fullscreen no longer leaves the window in the monitor's top left corner.
|
||||
- **Breaking:** `Window::hidpi_factor` has been renamed to `Window::get_hidpi_factor` for better consistency. `WindowEvent::HiDPIFactorChanged` has been renamed to `WindowEvent::HiDpiFactorChanged`. DPI factors are always represented as `f64` instead of `f32` now.
|
||||
- The Windows backend is now DPI aware. `WindowEvent::HiDpiFactorChanged` is implemented, and `MonitorId::get_hidpi_factor` and `Window::hidpi_factor` return accurate values.
|
||||
- Implemented `WindowEvent::HiDpiFactorChanged` on X11.
|
||||
- On macOS, `Window::set_cursor_position` is now relative to the client area.
|
||||
- On macOS, setting the maximum and minimum dimensions now applies to the client area dimensions rather than to the window dimensions.
|
||||
- On iOS, `MonitorId::get_dimensions` has been implemented and both `MonitorId::get_hidpi_factor` and `Window::get_hidpi_factor` return accurate values.
|
||||
- On Emscripten, `MonitorId::get_hidpi_factor` now returns the same value as `Window::get_hidpi_factor` (it previously would always return 1.0).
|
||||
- **Breaking:** The entire API for sizes, positions, etc. has changed. In the majority of cases, winit produces and consumes positions and sizes as `LogicalPosition` and `LogicalSize`, respectively. The notable exception is `MonitorId` methods, which deal in `PhysicalPosition` and `PhysicalSize`. See the documentation for specifics and explanations of the types. Additionally, winit automatically conserves logical size when the DPI factor changes.
|
||||
- **Breaking:** All deprecated methods have been removed. For `Window::platform_display` and `Window::platform_window`, switch to the appropriate platform-specific `WindowExt` methods. For `Window::get_inner_size_points` and `Window::get_inner_size_pixels`, use the `LogicalSize` returned by `Window::get_inner_size` and convert as needed.
|
||||
- HiDPI support for Wayland.
|
||||
- `EventsLoop::get_available_monitors` and `EventsLoop::get_primary_monitor` now have identical counterparts on `Window`, so this information can be acquired without an `EventsLoop` borrow.
|
||||
- `AvailableMonitorsIter` now implements `Debug`.
|
||||
- Fixed quirk on macOS where certain keys would generate characters at twice the normal rate when held down.
|
||||
- On X11, all event loops now share the same `XConnection`.
|
||||
- **Breaking:** `Window::set_cursor_state` and `CursorState` enum removed in favor of the more composable `Window::grab_cursor` and `Window::hide_cursor`. As a result, grabbing the cursor no longer automatically hides it; you must call both methods to retain the old behavior on Windows and macOS. `Cursor::NoneCursor` has been removed, as it's no longer useful.
|
||||
- **Breaking:** `Window::set_cursor_position` now returns `Result<(), String>`, thus allowing for `Box<Error>` conversion via `?`.
|
||||
|
||||
# Version 0.15.1 (2018-06-13)
|
||||
|
||||
- On X11, the `Moved` event is no longer sent when the window is resized without changing position.
|
||||
|
||||
35
Cargo.toml
35
Cargo.toml
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.15.1"
|
||||
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"]
|
||||
@@ -19,41 +19,48 @@ icon_loading = ["image"]
|
||||
[dependencies]
|
||||
lazy_static = "1"
|
||||
libc = "0.2"
|
||||
image = { version = "0.19", optional = true }
|
||||
log = "0.4"
|
||||
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"
|
||||
parking_lot = "0.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"
|
||||
|
||||
12
README.md
12
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://travis-ci.org/tomaka/winit)
|
||||
[](https://ci.appveyor.com/project/tomaka/winit/branch/master)
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.15"
|
||||
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
|
||||
|
||||
@@ -8,7 +8,7 @@ fn main() {
|
||||
let window = winit::WindowBuilder::new().build(&events_loop).unwrap();
|
||||
window.set_title("A fantastic window!");
|
||||
|
||||
let cursors = [MouseCursor::Default, MouseCursor::Crosshair, MouseCursor::Hand, MouseCursor::Arrow, MouseCursor::Move, MouseCursor::Text, MouseCursor::Wait, MouseCursor::Help, MouseCursor::Progress, MouseCursor::NotAllowed, MouseCursor::ContextMenu, MouseCursor::NoneCursor, MouseCursor::Cell, MouseCursor::VerticalText, MouseCursor::Alias, MouseCursor::Copy, MouseCursor::NoDrop, MouseCursor::Grab, MouseCursor::Grabbing, MouseCursor::AllScroll, MouseCursor::ZoomIn, MouseCursor::ZoomOut, MouseCursor::EResize, MouseCursor::NResize, MouseCursor::NeResize, MouseCursor::NwResize, MouseCursor::SResize, MouseCursor::SeResize, MouseCursor::SwResize, MouseCursor::WResize, MouseCursor::EwResize, MouseCursor::NsResize, MouseCursor::NeswResize, MouseCursor::NwseResize, MouseCursor::ColResize, MouseCursor::RowResize];
|
||||
let cursors = [MouseCursor::Default, MouseCursor::Crosshair, MouseCursor::Hand, MouseCursor::Arrow, MouseCursor::Move, MouseCursor::Text, MouseCursor::Wait, MouseCursor::Help, MouseCursor::Progress, MouseCursor::NotAllowed, MouseCursor::ContextMenu, MouseCursor::Cell, MouseCursor::VerticalText, MouseCursor::Alias, MouseCursor::Copy, MouseCursor::NoDrop, MouseCursor::Grab, MouseCursor::Grabbing, MouseCursor::AllScroll, MouseCursor::ZoomIn, MouseCursor::ZoomOut, MouseCursor::EResize, MouseCursor::NResize, MouseCursor::NeResize, MouseCursor::NwResize, MouseCursor::SResize, MouseCursor::SeResize, MouseCursor::SwResize, MouseCursor::WResize, MouseCursor::EwResize, MouseCursor::NsResize, MouseCursor::NeswResize, MouseCursor::NwseResize, MouseCursor::ColResize, MouseCursor::RowResize];
|
||||
let mut cursor_idx = 0;
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
|
||||
38
examples/cursor_grab.rs
Normal file
38
examples/cursor_grab.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
extern crate winit;
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let window = winit::WindowBuilder::new()
|
||||
.with_title("Super Cursor Grab'n'Hide Simulator 9000")
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
if let winit::Event::WindowEvent { event, .. } = event {
|
||||
use winit::WindowEvent::*;
|
||||
match event {
|
||||
CloseRequested => return winit::ControlFlow::Break,
|
||||
KeyboardInput {
|
||||
input: winit::KeyboardInput {
|
||||
state: winit::ElementState::Released,
|
||||
virtual_keycode: Some(key),
|
||||
modifiers,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
use winit::VirtualKeyCode::*;
|
||||
match key {
|
||||
Escape => return winit::ControlFlow::Break,
|
||||
G => window.grab_cursor(!modifiers.shift).unwrap(),
|
||||
H => window.hide_cursor(!modifiers.shift),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
winit::ControlFlow::Continue
|
||||
});
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
extern crate winit;
|
||||
|
||||
use winit::{ControlFlow, WindowEvent, ElementState, KeyboardInput};
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let window = winit::WindowBuilder::new().build(&events_loop).unwrap();
|
||||
window.set_title("winit - Cursor grabbing test");
|
||||
|
||||
let mut grabbed = false;
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
println!("{:?}", event);
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent { event, .. } => {
|
||||
match event {
|
||||
WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. } => {
|
||||
if grabbed {
|
||||
grabbed = false;
|
||||
window.set_cursor_state(winit::CursorState::Normal)
|
||||
.ok().expect("could not ungrab mouse cursor");
|
||||
} else {
|
||||
grabbed = true;
|
||||
window.set_cursor_state(winit::CursorState::Grab)
|
||||
.ok().expect("could not grab mouse cursor");
|
||||
}
|
||||
},
|
||||
|
||||
WindowEvent::CloseRequested => return ControlFlow::Break,
|
||||
|
||||
a @ WindowEvent::CursorMoved { .. } => {
|
||||
println!("{:?}", a);
|
||||
},
|
||||
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
ControlFlow::Continue
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
extern crate winit;
|
||||
|
||||
use winit::dpi::LogicalSize;
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
@@ -7,8 +9,8 @@ fn main() {
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
window.set_min_dimensions(Some((400, 200)));
|
||||
window.set_max_dimensions(Some((800, 400)));
|
||||
window.set_min_dimensions(Some(LogicalSize::new(400.0, 200.0)));
|
||||
window.set_max_dimensions(Some(LogicalSize::new(800.0, 400.0)));
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
println!("{:?}", event);
|
||||
|
||||
7
examples/monitor_list.rs
Normal file
7
examples/monitor_list.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
extern crate winit;
|
||||
|
||||
fn main() {
|
||||
let event_loop = winit::EventsLoop::new();
|
||||
let window = winit::WindowBuilder::new().build(&event_loop).unwrap();
|
||||
println!("{:#?}\nPrimary: {:#?}", window.get_available_monitors(), window.get_primary_monitor());
|
||||
}
|
||||
@@ -7,7 +7,7 @@ fn main() {
|
||||
|
||||
let window = winit::WindowBuilder::new()
|
||||
.with_title("Hit space to toggle resizability.")
|
||||
.with_dimensions(400, 200)
|
||||
.with_dimensions((400, 200).into())
|
||||
.with_resizable(resizable)
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
331
src/dpi.rs
Normal file
331
src/dpi.rs
Normal file
@@ -0,0 +1,331 @@
|
||||
|
||||
//! DPI is important, so read the docs for this module if you don't want to be confused.
|
||||
//!
|
||||
//! Originally, `winit` dealt entirely in physical pixels (excluding unintentional inconsistencies), but now all
|
||||
//! window-related functions both produce and consume logical pixels. Monitor-related functions still use physical
|
||||
//! pixels, as do any context-related functions in `glutin`.
|
||||
//!
|
||||
//! If you've never heard of these terms before, then you're not alone, and this documentation will explain the
|
||||
//! concepts.
|
||||
//!
|
||||
//! Modern screens have a defined physical resolution, most commonly 1920x1080. Indepedent of that is the amount of
|
||||
//! space the screen occupies, which is to say, the height and width in millimeters. The relationship between these two
|
||||
//! measurements is the *pixel density*. Mobile screens require a high pixel density, as they're held close to the
|
||||
//! eyes. Larger displays also require a higher pixel density, hence the growing presence of 1440p and 4K displays.
|
||||
//!
|
||||
//! So, this presents a problem. Let's say we want to render a square 100px button. It will occupy 100x100 of the
|
||||
//! screen's pixels, which in many cases, seems perfectly fine. However, because this size doesn't account for the
|
||||
//! screen's dimensions or pixel density, the button's size can vary quite a bit. On a 4K display, it would be unusably
|
||||
//! 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.
|
||||
//! 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:
|
||||
//! - 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.
|
||||
//! While users are free to select any option they want, they're only given a selection of "nice" DPI factors, i.e.
|
||||
//! 1.0, 1.25, 1.5... on Windows 7, the DPI factor is global and changing it requires logging out.
|
||||
//! - **macOS:** The buzzword is "retina displays", which have a DPI factor of 2.0. Otherwise, the DPI factor is 1.0.
|
||||
//! Intermediate DPI factors are never used, thus 1440p displays/etc. aren't properly supported. It's possible for any
|
||||
//! display to use that 2.0 DPI factor, given the use of the command line.
|
||||
//! - **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 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 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`.
|
||||
///
|
||||
/// All functions that take a DPI factor assert that this will return `true`. If you're sourcing DPI factors from
|
||||
/// anywhere other than winit, it's recommended to validate them using this function before passing them to winit;
|
||||
/// otherwise, you risk panics.
|
||||
#[inline]
|
||||
pub fn validate_hidpi_factor(dpi_factor: f64) -> bool {
|
||||
dpi_factor.is_sign_positive() && dpi_factor.is_normal()
|
||||
}
|
||||
|
||||
/// A position represented in logical pixels.
|
||||
///
|
||||
/// The position is stored as floats, so please be careful. Casting floats to integers truncates the fractional part,
|
||||
/// 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,
|
||||
}
|
||||
|
||||
impl LogicalPosition {
|
||||
#[inline]
|
||||
pub fn new(x: f64, y: f64) -> Self {
|
||||
LogicalPosition { x, y }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_physical<T: Into<PhysicalPosition>>(physical: T, dpi_factor: f64) -> Self {
|
||||
physical.into().to_logical(dpi_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_physical(&self, dpi_factor: f64) -> PhysicalPosition {
|
||||
assert!(validate_hidpi_factor(dpi_factor));
|
||||
let x = self.x * dpi_factor;
|
||||
let y = self.y * dpi_factor;
|
||||
PhysicalPosition::new(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(f64, f64)> for LogicalPosition {
|
||||
#[inline]
|
||||
fn from((x, y): (f64, f64)) -> Self {
|
||||
Self::new(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(i32, i32)> for LogicalPosition {
|
||||
#[inline]
|
||||
fn from((x, y): (i32, i32)) -> Self {
|
||||
Self::new(x as f64, y as f64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<(f64, f64)> for LogicalPosition {
|
||||
#[inline]
|
||||
fn into(self) -> (f64, f64) {
|
||||
(self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<(i32, i32)> for LogicalPosition {
|
||||
/// Note that this rounds instead of truncating.
|
||||
#[inline]
|
||||
fn into(self) -> (i32, i32) {
|
||||
(self.x.round() as _, self.y.round() as _)
|
||||
}
|
||||
}
|
||||
|
||||
/// A position represented in physical pixels.
|
||||
///
|
||||
/// The position is stored as floats, so please be careful. Casting floats to integers truncates the fractional part,
|
||||
/// 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,
|
||||
}
|
||||
|
||||
impl PhysicalPosition {
|
||||
#[inline]
|
||||
pub fn new(x: f64, y: f64) -> Self {
|
||||
PhysicalPosition { x, y }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_logical<T: Into<LogicalPosition>>(logical: T, dpi_factor: f64) -> Self {
|
||||
logical.into().to_physical(dpi_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_logical(&self, dpi_factor: f64) -> LogicalPosition {
|
||||
assert!(validate_hidpi_factor(dpi_factor));
|
||||
let x = self.x / dpi_factor;
|
||||
let y = self.y / dpi_factor;
|
||||
LogicalPosition::new(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(f64, f64)> for PhysicalPosition {
|
||||
#[inline]
|
||||
fn from((x, y): (f64, f64)) -> Self {
|
||||
Self::new(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(i32, i32)> for PhysicalPosition {
|
||||
#[inline]
|
||||
fn from((x, y): (i32, i32)) -> Self {
|
||||
Self::new(x as f64, y as f64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<(f64, f64)> for PhysicalPosition {
|
||||
#[inline]
|
||||
fn into(self) -> (f64, f64) {
|
||||
(self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<(i32, i32)> for PhysicalPosition {
|
||||
/// Note that this rounds instead of truncating.
|
||||
#[inline]
|
||||
fn into(self) -> (i32, i32) {
|
||||
(self.x.round() as _, self.y.round() as _)
|
||||
}
|
||||
}
|
||||
|
||||
/// A size represented in logical pixels.
|
||||
///
|
||||
/// The size is stored as floats, so please be careful. Casting floats to integers truncates the fractional part,
|
||||
/// 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,
|
||||
}
|
||||
|
||||
impl LogicalSize {
|
||||
#[inline]
|
||||
pub fn new(width: f64, height: f64) -> Self {
|
||||
LogicalSize { width, height }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_physical<T: Into<PhysicalSize>>(physical: T, dpi_factor: f64) -> Self {
|
||||
physical.into().to_logical(dpi_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_physical(&self, dpi_factor: f64) -> PhysicalSize {
|
||||
assert!(validate_hidpi_factor(dpi_factor));
|
||||
let width = self.width * dpi_factor;
|
||||
let height = self.height * dpi_factor;
|
||||
PhysicalSize::new(width, height)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(f64, f64)> for LogicalSize {
|
||||
#[inline]
|
||||
fn from((width, height): (f64, f64)) -> Self {
|
||||
Self::new(width, height)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(u32, u32)> for LogicalSize {
|
||||
#[inline]
|
||||
fn from((width, height): (u32, u32)) -> Self {
|
||||
Self::new(width as f64, height as f64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<(f64, f64)> for LogicalSize {
|
||||
#[inline]
|
||||
fn into(self) -> (f64, f64) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<(u32, u32)> for LogicalSize {
|
||||
/// Note that this rounds instead of truncating.
|
||||
#[inline]
|
||||
fn into(self) -> (u32, u32) {
|
||||
(self.width.round() as _, self.height.round() as _)
|
||||
}
|
||||
}
|
||||
|
||||
/// A size represented in physical pixels.
|
||||
///
|
||||
/// The size is stored as floats, so please be careful. Casting floats to integers truncates the fractional part,
|
||||
/// 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,
|
||||
}
|
||||
|
||||
impl PhysicalSize {
|
||||
#[inline]
|
||||
pub fn new(width: f64, height: f64) -> Self {
|
||||
PhysicalSize { width, height }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_logical<T: Into<LogicalSize>>(logical: T, dpi_factor: f64) -> Self {
|
||||
logical.into().to_physical(dpi_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_logical(&self, dpi_factor: f64) -> LogicalSize {
|
||||
assert!(validate_hidpi_factor(dpi_factor));
|
||||
let width = self.width / dpi_factor;
|
||||
let height = self.height / dpi_factor;
|
||||
LogicalSize::new(width, height)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(f64, f64)> for PhysicalSize {
|
||||
#[inline]
|
||||
fn from((width, height): (f64, f64)) -> Self {
|
||||
Self::new(width, height)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(u32, u32)> for PhysicalSize {
|
||||
#[inline]
|
||||
fn from((width, height): (u32, u32)) -> Self {
|
||||
Self::new(width as f64, height as f64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<(f64, f64)> for PhysicalSize {
|
||||
#[inline]
|
||||
fn into(self) -> (f64, f64) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<(u32, u32)> for PhysicalSize {
|
||||
/// Note that this rounds instead of truncating.
|
||||
#[inline]
|
||||
fn into(self) -> (u32, u32) {
|
||||
(self.width.round() as _, self.height.round() as _)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::path::PathBuf;
|
||||
use {WindowId, DeviceId};
|
||||
|
||||
use {DeviceId, LogicalPosition, LogicalSize, WindowId};
|
||||
|
||||
/// Describes a generic event.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Event {
|
||||
WindowEvent {
|
||||
window_id: WindowId,
|
||||
@@ -21,14 +22,13 @@ 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(u32, u32),
|
||||
Resized(LogicalSize),
|
||||
|
||||
/// The position of the window has changed. Contains the window's new position.
|
||||
Moved(i32, i32),
|
||||
Moved(LogicalPosition),
|
||||
|
||||
/// The window has been requested to close.
|
||||
CloseRequested,
|
||||
@@ -63,7 +63,7 @@ pub enum WindowEvent {
|
||||
/// (x,y) coords in pixels relative to the top-left corner of the window. Because the range of this data is
|
||||
/// limited by the display area and it may have been transformed by the OS to implement effects such as cursor
|
||||
/// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control.
|
||||
position: (f64, f64),
|
||||
position: LogicalPosition,
|
||||
modifiers: ModifiersState
|
||||
},
|
||||
|
||||
@@ -96,14 +96,16 @@ pub enum WindowEvent {
|
||||
/// Touch event has been received
|
||||
Touch(Touch),
|
||||
|
||||
/// DPI scaling factor of the window has changed.
|
||||
/// The DPI factor of the window has changed.
|
||||
///
|
||||
/// The following actions cause DPI changes:
|
||||
/// The following user actions can cause DPI changes:
|
||||
///
|
||||
/// * A user changes the resolution.
|
||||
/// * A user changes the desktop scaling value (e.g. in Control Panel on Windows).
|
||||
/// * A user moves the application window to a display with a different DPI.
|
||||
HiDPIFactorChanged(f32),
|
||||
/// * Changing the display's resolution.
|
||||
/// * Changing the display's DPI factor (e.g. in Control Panel on Windows).
|
||||
/// * Moving the window to a display with a different DPI factor.
|
||||
///
|
||||
/// For more information about DPI in general, see the [`dpi`](dpi/index.html) module.
|
||||
HiDpiFactorChanged(f64),
|
||||
}
|
||||
|
||||
/// Represents raw hardware events that are not associated with any particular window.
|
||||
@@ -114,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,
|
||||
@@ -145,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
|
||||
///
|
||||
@@ -171,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,
|
||||
@@ -193,11 +197,11 @@ 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,
|
||||
pub location: (f64,f64),
|
||||
pub location: LogicalPosition,
|
||||
/// unique identifier of a finger.
|
||||
pub id: u64
|
||||
}
|
||||
@@ -213,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,
|
||||
@@ -220,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,
|
||||
@@ -229,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.
|
||||
@@ -242,12 +249,13 @@ pub enum MouseScrollDelta {
|
||||
/// Scroll events are expressed as a PixelDelta if
|
||||
/// supported by the device (eg. a touchpad) and
|
||||
/// platform.
|
||||
PixelDelta(f32, f32)
|
||||
PixelDelta(LogicalPosition),
|
||||
}
|
||||
|
||||
/// 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,
|
||||
@@ -315,6 +323,15 @@ pub enum VirtualKeyCode {
|
||||
F13,
|
||||
F14,
|
||||
F15,
|
||||
F16,
|
||||
F17,
|
||||
F18,
|
||||
F19,
|
||||
F20,
|
||||
F21,
|
||||
F22,
|
||||
F23,
|
||||
F24,
|
||||
|
||||
/// Print Screen/SysRq.
|
||||
Snapshot,
|
||||
@@ -383,7 +400,6 @@ pub enum VirtualKeyCode {
|
||||
LAlt,
|
||||
LBracket,
|
||||
LControl,
|
||||
LMenu,
|
||||
LShift,
|
||||
LWin,
|
||||
Mail,
|
||||
@@ -408,7 +424,6 @@ pub enum VirtualKeyCode {
|
||||
RAlt,
|
||||
RBracket,
|
||||
RControl,
|
||||
RMenu,
|
||||
RShift,
|
||||
RWin,
|
||||
Semicolon,
|
||||
@@ -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() {
|
||||
|
||||
105
src/lib.rs
105
src/lib.rs
@@ -25,24 +25,28 @@
|
||||
//! 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.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use winit::{Event, WindowEvent};
|
||||
//! use winit::dpi::LogicalSize;
|
||||
//! # use winit::EventsLoop;
|
||||
//! # let mut events_loop = EventsLoop::new();
|
||||
//!
|
||||
//! loop {
|
||||
//! events_loop.poll_events(|event| {
|
||||
//! match event {
|
||||
//! Event::WindowEvent { event: WindowEvent::Resized(w, h), .. } => {
|
||||
//! println!("The window was resized to {}x{}", w, h);
|
||||
//! Event::WindowEvent {
|
||||
//! event: WindowEvent::Resized(LogicalSize { width, height }),
|
||||
//! ..
|
||||
//! } => {
|
||||
//! println!("The window was resized to {}x{}", width, height);
|
||||
//! },
|
||||
//! _ => ()
|
||||
//! }
|
||||
@@ -76,15 +80,21 @@
|
||||
//! # 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.
|
||||
//!
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate libc;
|
||||
#[macro_use]
|
||||
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;
|
||||
@@ -97,23 +107,25 @@ 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.
|
||||
pub use events::*;
|
||||
pub use window::{AvailableMonitorsIter, MonitorId};
|
||||
pub use icon::*;
|
||||
|
||||
mod platform;
|
||||
pub mod dpi;
|
||||
mod events;
|
||||
mod window;
|
||||
mod icon;
|
||||
mod platform;
|
||||
mod window;
|
||||
|
||||
pub mod os;
|
||||
|
||||
@@ -140,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()`.
|
||||
@@ -157,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()`
|
||||
@@ -175,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,
|
||||
@@ -227,6 +252,11 @@ impl EventsLoop {
|
||||
/// Calls `callback` every time an event is received. If no event is available, sleeps the
|
||||
/// current thread and waits for an event. If the callback returns `ControlFlow::Break` then
|
||||
/// `run_forever` will immediately return.
|
||||
///
|
||||
/// # Danger!
|
||||
///
|
||||
/// The callback is run after *every* event, so if its execution time is non-trivial the event queue may not empty
|
||||
/// at a sufficient rate. Rendering in the callback with vsync enabled **will** cause significant lag.
|
||||
#[inline]
|
||||
pub fn run_forever<F>(&mut self, callback: F)
|
||||
where F: FnMut(Event) -> ControlFlow
|
||||
@@ -249,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.
|
||||
///
|
||||
@@ -287,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 {
|
||||
@@ -317,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,
|
||||
@@ -343,7 +388,6 @@ pub enum MouseCursor {
|
||||
/// Cursor showing that something cannot be done.
|
||||
NotAllowed,
|
||||
ContextMenu,
|
||||
NoneCursor,
|
||||
Cell,
|
||||
VerticalText,
|
||||
Alias,
|
||||
@@ -379,47 +423,24 @@ impl Default for MouseCursor {
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes how winit handles the cursor.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum CursorState {
|
||||
/// Normal cursor behavior.
|
||||
Normal,
|
||||
|
||||
/// The cursor will be invisible when over the window.
|
||||
Hide,
|
||||
|
||||
/// Grabs the mouse cursor. The cursor's motion will be confined to this
|
||||
/// window and the window has exclusive access to further events regarding
|
||||
/// the cursor.
|
||||
///
|
||||
/// This is useful for first-person cameras for example.
|
||||
Grab,
|
||||
}
|
||||
|
||||
impl Default for CursorState {
|
||||
fn default() -> Self {
|
||||
CursorState::Normal
|
||||
}
|
||||
}
|
||||
|
||||
/// Attributes to use when creating a window.
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WindowAttributes {
|
||||
/// The dimensions of the window. If this is `None`, some platform-specific dimensions will be
|
||||
/// used.
|
||||
///
|
||||
/// The default is `None`.
|
||||
pub dimensions: Option<(u32, u32)>,
|
||||
pub dimensions: Option<LogicalSize>,
|
||||
|
||||
/// The minimum dimensions a window can be, If this is `None`, the window will have no minimum dimensions (aside from reserved).
|
||||
///
|
||||
/// The default is `None`.
|
||||
pub min_dimensions: Option<(u32, u32)>,
|
||||
pub min_dimensions: Option<LogicalSize>,
|
||||
|
||||
/// The maximum dimensions a window can be, If this is `None`, the maximum will have no maximum or will be set to the primary monitor's dimensions by the platform.
|
||||
///
|
||||
/// The default is `None`.
|
||||
pub max_dimensions: Option<(u32, u32)>,
|
||||
pub max_dimensions: Option<LogicalSize>,
|
||||
|
||||
/// Whether the window is resizable or not.
|
||||
///
|
||||
|
||||
59
src/os/ios.rs
Normal file
59
src/os/ios.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
#![cfg(target_os = "ios")]
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use {MonitorId, Window, WindowBuilder};
|
||||
|
||||
/// Additional methods on `Window` that are specific to iOS.
|
||||
pub trait WindowExt {
|
||||
/// Returns a pointer to the `UIWindow` that is used by this window.
|
||||
///
|
||||
/// The pointer will become invalid when the `Window` is destroyed.
|
||||
fn get_uiwindow(&self) -> *mut c_void;
|
||||
|
||||
/// Returns a pointer to the `UIView` that is used by this window.
|
||||
///
|
||||
/// The pointer will become invalid when the `Window` is destroyed.
|
||||
fn get_uiview(&self) -> *mut c_void;
|
||||
}
|
||||
|
||||
impl WindowExt for Window {
|
||||
#[inline]
|
||||
fn get_uiwindow(&self) -> *mut c_void {
|
||||
self.window.get_uiwindow() as _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_uiview(&self) -> *mut c_void {
|
||||
self.window.get_uiview() as _
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
fn get_uiscreen(&self) -> *mut c_void;
|
||||
}
|
||||
|
||||
impl MonitorIdExt for MonitorId {
|
||||
#[inline]
|
||||
fn get_uiscreen(&self) -> *mut c_void {
|
||||
self.inner.get_uiscreen() as _
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
#![cfg(target_os = "macos")]
|
||||
|
||||
use std::convert::From;
|
||||
use std::os::raw::c_void;
|
||||
use cocoa::appkit::NSApplicationActivationPolicy;
|
||||
use {MonitorId, Window, WindowBuilder};
|
||||
use {LogicalSize, MonitorId, Window, WindowBuilder};
|
||||
|
||||
/// Additional methods on `Window` that are specific to MacOS.
|
||||
pub trait WindowExt {
|
||||
@@ -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
|
||||
@@ -86,7 +84,7 @@ pub trait WindowBuilderExt {
|
||||
/// Makes the window content appear behind the titlebar.
|
||||
fn with_fullsize_content_view(self, fullsize_content_view: bool) -> WindowBuilder;
|
||||
/// Build window with `resizeIncrements` property. Values must not be 0.
|
||||
fn with_resize_increments(self, width_inc: u32, height_inc: u32) -> WindowBuilder;
|
||||
fn with_resize_increments(self, increments: LogicalSize) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExt for WindowBuilder {
|
||||
@@ -133,8 +131,8 @@ impl WindowBuilderExt for WindowBuilder {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_resize_increments(mut self, width_inc: u32, height_inc: u32) -> WindowBuilder {
|
||||
self.platform_specific.resize_increments = Some((width_inc, height_inc));
|
||||
fn with_resize_increments(mut self, increments: LogicalSize) -> WindowBuilder {
|
||||
self.platform_specific.resize_increments = Some(increments.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
//! Contains the follow modules:
|
||||
//!
|
||||
//! - `android`
|
||||
//! - `ios`
|
||||
//! - `macos`
|
||||
//! - `unix`
|
||||
//! - `windows`
|
||||
@@ -10,6 +11,7 @@
|
||||
//! However only the module corresponding to the platform you're compiling to will be available.
|
||||
//!
|
||||
pub mod android;
|
||||
pub mod ios;
|
||||
pub mod macos;
|
||||
pub mod unix;
|
||||
pub mod windows;
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
#![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::sync::Arc;
|
||||
use std::ptr;
|
||||
use EventsLoop;
|
||||
use MonitorId;
|
||||
use Window;
|
||||
use platform::EventsLoop as LinuxEventsLoop;
|
||||
use platform::Window as LinuxWindow;
|
||||
use WindowBuilder;
|
||||
use std::sync::Arc;
|
||||
|
||||
use {
|
||||
EventsLoop,
|
||||
LogicalSize,
|
||||
MonitorId,
|
||||
Window,
|
||||
WindowBuilder,
|
||||
};
|
||||
use platform::{
|
||||
EventsLoop as LinuxEventsLoop,
|
||||
Window as LinuxWindow,
|
||||
};
|
||||
use platform::x11::XConnection;
|
||||
use platform::x11::ffi::XVisualInfo;
|
||||
|
||||
@@ -72,6 +78,7 @@ impl EventsLoopExt for EventsLoop {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> {
|
||||
self.events_loop.x_connection().cloned()
|
||||
}
|
||||
@@ -93,6 +100,7 @@ pub trait WindowExt {
|
||||
|
||||
fn get_xlib_screen_id(&self) -> Option<raw::c_int>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>>;
|
||||
|
||||
/// Set window urgency hint (`XUrgencyHint`). Only relevant on X.
|
||||
@@ -155,6 +163,7 @@ impl WindowExt for Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.get_xlib_xconnection()),
|
||||
@@ -210,10 +219,12 @@ 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, width_inc: u32, height_inc: u32) -> WindowBuilder;
|
||||
fn with_resize_increments(self, increments: LogicalSize) -> WindowBuilder;
|
||||
/// Build window with base size hint. Only implemented on X11.
|
||||
fn with_base_size(self, base_width: u32, base_height: u32) -> WindowBuilder;
|
||||
fn with_base_size(self, base_size: LogicalSize) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExt for WindowBuilder {
|
||||
@@ -250,14 +261,20 @@ impl WindowBuilderExt for WindowBuilder {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_resize_increments(mut self, width_inc: u32, height_inc: u32) -> WindowBuilder {
|
||||
self.platform_specific.resize_increments = Some((width_inc, height_inc));
|
||||
fn with_resize_increments(mut self, increments: LogicalSize) -> WindowBuilder {
|
||||
self.platform_specific.resize_increments = Some(increments.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_base_size(mut self, base_width: u32, base_height: u32) -> WindowBuilder {
|
||||
self.platform_specific.base_size = Some((base_width, base_height));
|
||||
fn with_base_size(mut self, base_size: LogicalSize) -> 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,25 @@ use std::os::raw::c_void;
|
||||
use libc;
|
||||
use winapi::shared::windef::HWND;
|
||||
|
||||
use {DeviceId, Icon, MonitorId, Window, WindowBuilder};
|
||||
use {DeviceId, EventsLoop, Icon, MonitorId, Window, WindowBuilder};
|
||||
use platform::EventsLoop as WindowsEventsLoop;
|
||||
|
||||
/// Additional methods on `EventsLoop` that are specific to Windows.
|
||||
pub trait EventsLoopExt {
|
||||
/// By default, winit on Windows will attempt to enable process-wide DPI awareness. If that's
|
||||
/// undesirable, you can create an `EventsLoop` using this function instead.
|
||||
fn new_dpi_unaware() -> Self where Self: Sized;
|
||||
}
|
||||
|
||||
impl EventsLoopExt for EventsLoop {
|
||||
#[inline]
|
||||
fn new_dpi_unaware() -> Self {
|
||||
EventsLoop {
|
||||
events_loop: WindowsEventsLoop::with_dpi_awareness(false),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `Window` that are specific to Windows.
|
||||
pub trait WindowExt {
|
||||
@@ -37,6 +55,9 @@ pub trait WindowBuilderExt {
|
||||
|
||||
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
|
||||
fn with_taskbar_icon(self, taskbar_icon: Option<Icon>) -> WindowBuilder;
|
||||
|
||||
/// This sets `WS_EX_NOREDIRECTIONBITMAP`.
|
||||
fn with_no_redirection_bitmap(self, flag: bool) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExt for WindowBuilder {
|
||||
@@ -51,6 +72,12 @@ impl WindowBuilderExt for WindowBuilder {
|
||||
self.platform_specific.taskbar_icon = taskbar_icon;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_no_redirection_bitmap(mut self, flag: bool) -> WindowBuilder {
|
||||
self.platform_specific.no_redirection_bitmap = flag;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorId` that are specific to Windows.
|
||||
|
||||
@@ -2,24 +2,33 @@
|
||||
|
||||
extern crate android_glue;
|
||||
|
||||
use libc;
|
||||
use std::sync::mpsc::{Receiver, channel};
|
||||
mod ffi;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::os::raw::c_void;
|
||||
use {CreationError, Event, WindowEvent, MouseCursor};
|
||||
use std::sync::mpsc::{Receiver, channel};
|
||||
|
||||
use {
|
||||
CreationError,
|
||||
Event,
|
||||
LogicalPosition,
|
||||
LogicalSize,
|
||||
MouseCursor,
|
||||
PhysicalPosition,
|
||||
PhysicalSize,
|
||||
WindowAttributes,
|
||||
WindowEvent,
|
||||
WindowId as RootWindowId,
|
||||
};
|
||||
use CreationError::OsError;
|
||||
use WindowId as RootWindowId;
|
||||
use events::{Touch, TouchPhase};
|
||||
use window::MonitorId as RootMonitorId;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use CursorState;
|
||||
use WindowAttributes;
|
||||
|
||||
pub struct EventsLoop {
|
||||
event_rx: Receiver<android_glue::Event>,
|
||||
suspend_callback: RefCell<Option<Box<Fn(bool) -> ()>>>
|
||||
suspend_callback: RefCell<Option<Box<Fn(bool) -> ()>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -31,13 +40,13 @@ impl EventsLoop {
|
||||
android_glue::add_sender(tx);
|
||||
EventsLoop {
|
||||
event_rx: rx,
|
||||
suspend_callback: RefCell::new(None),
|
||||
suspend_callback: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
let mut rb = VecDeque::new();
|
||||
let mut rb = VecDeque::with_capacity(1);
|
||||
rb.push_back(MonitorId);
|
||||
rb
|
||||
}
|
||||
@@ -47,14 +56,17 @@ impl EventsLoop {
|
||||
MonitorId
|
||||
}
|
||||
|
||||
|
||||
pub fn poll_events<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
while let Ok(event) = self.event_rx.try_recv() {
|
||||
|
||||
let e = match event{
|
||||
android_glue::Event::EventMotion(motion) => {
|
||||
let dpi_factor = MonitorId.get_hidpi_factor();
|
||||
let location = LogicalPosition::from_physical(
|
||||
(motion.x as f64, motion.y as f64),
|
||||
dpi_factor,
|
||||
);
|
||||
Some(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId),
|
||||
event: WindowEvent::Touch(Touch {
|
||||
@@ -64,7 +76,7 @@ impl EventsLoop {
|
||||
android_glue::MotionAction::Up => TouchPhase::Ended,
|
||||
android_glue::MotionAction::Cancel => TouchPhase::Cancelled,
|
||||
},
|
||||
location: (motion.x as f64, motion.y as f64),
|
||||
location,
|
||||
id: motion.pointer_id as u64,
|
||||
device_id: DEVICE_ID,
|
||||
}),
|
||||
@@ -91,11 +103,12 @@ impl EventsLoop {
|
||||
if native_window.is_null() {
|
||||
None
|
||||
} else {
|
||||
let w = unsafe { ffi::ANativeWindow_getWidth(native_window as *const _) } as u32;
|
||||
let h = unsafe { ffi::ANativeWindow_getHeight(native_window as *const _) } as u32;
|
||||
let dpi_factor = MonitorId.get_hidpi_factor();
|
||||
let physical_size = MonitorId.get_dimensions();
|
||||
let size = LogicalSize::from_physical(physical_size, dpi_factor);
|
||||
Some(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId),
|
||||
event: WindowEvent::Resized(w, h),
|
||||
event: WindowEvent::Resized(size),
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -164,10 +177,29 @@ pub struct Window {
|
||||
native_window: *const c_void,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorId;
|
||||
|
||||
mod ffi;
|
||||
impl fmt::Debug for MonitorId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
#[derive(Debug)]
|
||||
struct MonitorId {
|
||||
name: Option<String>,
|
||||
dimensions: PhysicalSize,
|
||||
position: PhysicalPosition,
|
||||
hidpi_factor: f64,
|
||||
}
|
||||
|
||||
let monitor_id_proxy = MonitorId {
|
||||
name: self.get_name(),
|
||||
dimensions: self.get_dimensions(),
|
||||
position: self.get_position(),
|
||||
hidpi_factor: self.get_hidpi_factor(),
|
||||
};
|
||||
|
||||
monitor_id_proxy.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
#[inline]
|
||||
@@ -176,21 +208,24 @@ impl MonitorId {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||
unsafe {
|
||||
let window = android_glue::get_native_window();
|
||||
(ffi::ANativeWindow_getWidth(window) as u32, ffi::ANativeWindow_getHeight(window) as u32)
|
||||
(
|
||||
ffi::ANativeWindow_getWidth(window) as f64,
|
||||
ffi::ANativeWindow_getHeight(window) as f64,
|
||||
).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
pub fn get_position(&self) -> PhysicalPosition {
|
||||
// Android assumes single screen
|
||||
(0, 0)
|
||||
(0, 0).into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
1.0
|
||||
}
|
||||
}
|
||||
@@ -205,10 +240,6 @@ impl Window {
|
||||
_: PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window, CreationError>
|
||||
{
|
||||
// not implemented
|
||||
assert!(win_attribs.min_dimensions.is_none());
|
||||
assert!(win_attribs.max_dimensions.is_none());
|
||||
|
||||
let native_window = unsafe { android_glue::get_native_window() };
|
||||
if native_window.is_null() {
|
||||
return Err(OsError(format!("Android's native window is null")));
|
||||
@@ -228,98 +259,106 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn set_title(&self, _: &str) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show(&self) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide(&self) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||
// N/A
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||
// N/A
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_position(&self, _x: i32, _y: i32) {
|
||||
pub fn set_position(&self, _position: LogicalPosition) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
pub fn set_min_dimensions(&self, _dimensions: Option<LogicalSize>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<LogicalSize>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, _resizable: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||
if self.native_window.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some((
|
||||
unsafe { ffi::ANativeWindow_getWidth(self.native_window as *const _) } as u32,
|
||||
unsafe { ffi::ANativeWindow_getHeight(self.native_window as *const _) } as u32
|
||||
))
|
||||
let dpi_factor = self.get_hidpi_factor();
|
||||
let physical_size = self.get_current_monitor().get_dimensions();
|
||||
Some(LogicalSize::from_physical(physical_size, dpi_factor))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||
self.get_inner_size()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_inner_size(&self, _x: u32, _y: u32) {
|
||||
pub fn set_inner_size(&self, _size: LogicalSize) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
unimplemented!()
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
self.get_current_monitor().get_hidpi_factor()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _: MouseCursor) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, _state: CursorState) -> Result<(), String> {
|
||||
Ok(())
|
||||
pub fn grab_cursor(&self, _grab: bool) -> Result<(), String> {
|
||||
Err("Cursor grabbing is not possible on Android.".to_owned())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
1.0
|
||||
pub fn hide_cursor(&self, _hide: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
|
||||
Ok(())
|
||||
pub fn set_cursor_position(&self, _position: LogicalPosition) -> Result<(), String> {
|
||||
Err("Setting cursor position is not possible on Android.".to_owned())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, _maximized: bool) {
|
||||
// N/A
|
||||
// Android has single screen maximized apps so nothing to do
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) {
|
||||
// N/A
|
||||
// Android has single screen maximized apps so nothing to do
|
||||
}
|
||||
|
||||
@@ -339,15 +378,28 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_spot(&self, _x: i32, _y: i32) {
|
||||
pub fn set_ime_spot(&self, _spot: LogicalPosition) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
RootMonitorId{inner: MonitorId}
|
||||
RootMonitorId { inner: MonitorId }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
let mut rb = VecDeque::with_capacity(1);
|
||||
rb.push_back(MonitorId);
|
||||
rb
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
MonitorId
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#![allow(dead_code)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(dead_code, non_camel_case_types, non_snake_case)]
|
||||
|
||||
use std::os::raw::{c_int, c_char, c_void, c_ulong, c_double, c_long, c_ushort};
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -2,16 +2,22 @@
|
||||
|
||||
mod ffi;
|
||||
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_void, c_double, c_ulong, c_int};
|
||||
use std::ptr;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Mutex, Arc};
|
||||
use std::{mem, ptr, str};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::os::raw::{c_char, c_void, c_double, c_ulong, c_int};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Mutex, Arc};
|
||||
|
||||
use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
|
||||
use window::MonitorId as RootMonitorId;
|
||||
|
||||
const DOCUMENT_NAME: &'static str = "#document\0";
|
||||
|
||||
fn get_hidpi_factor() -> f64 {
|
||||
unsafe { ffi::emscripten_get_device_pixel_ratio() as f64 }
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes;
|
||||
|
||||
@@ -34,18 +40,18 @@ impl MonitorId {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
pub fn get_position(&self) -> PhysicalPosition {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
(0, 0)
|
||||
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||
(0, 0).into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
1.0
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
get_hidpi_factor()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,17 +96,19 @@ impl EventsLoop {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn interrupt(&self) {
|
||||
self.interrupted.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn create_proxy(&self) -> EventsLoopProxy {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
let mut list = VecDeque::new();
|
||||
let mut list = VecDeque::with_capacity(1);
|
||||
list.push_back(MonitorId);
|
||||
list
|
||||
}
|
||||
@@ -115,7 +123,7 @@ impl EventsLoop {
|
||||
{
|
||||
let ref mut window = *self.window.lock().unwrap();
|
||||
if let &mut Some(ref mut window) = window {
|
||||
while let Some(event) = window.events.borrow_mut().pop_front() {
|
||||
while let Some(event) = window.events.lock().unwrap().pop_front() {
|
||||
callback(event)
|
||||
}
|
||||
}
|
||||
@@ -142,9 +150,10 @@ impl EventsLoop {
|
||||
pub struct WindowId(usize);
|
||||
|
||||
pub struct Window2 {
|
||||
cursor_state: Mutex<::CursorState>,
|
||||
cursor_grabbed: Mutex<bool>,
|
||||
cursor_hidden: Mutex<bool>,
|
||||
is_fullscreen: bool,
|
||||
events: Box<RefCell<VecDeque<::Event>>>,
|
||||
events: Box<Mutex<VecDeque<::Event>>>,
|
||||
}
|
||||
|
||||
pub struct Window {
|
||||
@@ -176,7 +185,7 @@ extern "C" fn mouse_callback(
|
||||
event_queue: *mut c_void) -> ffi::EM_BOOL
|
||||
{
|
||||
unsafe {
|
||||
let queue: &RefCell<VecDeque<::Event>> = mem::transmute(event_queue);
|
||||
let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue);
|
||||
|
||||
let modifiers = ::ModifiersState {
|
||||
shift: (*event).shiftKey == ffi::EM_TRUE,
|
||||
@@ -187,15 +196,20 @@ extern "C" fn mouse_callback(
|
||||
|
||||
match event_type {
|
||||
ffi::EMSCRIPTEN_EVENT_MOUSEMOVE => {
|
||||
queue.borrow_mut().push_back(::Event::WindowEvent {
|
||||
let dpi_factor = get_hidpi_factor();
|
||||
let position = LogicalPosition::from_physical(
|
||||
((*event).canvasX as f64, (*event).canvasY as f64),
|
||||
dpi_factor,
|
||||
);
|
||||
queue.lock().unwrap().push_back(::Event::WindowEvent {
|
||||
window_id: ::WindowId(WindowId(0)),
|
||||
event: ::WindowEvent::CursorMoved {
|
||||
device_id: ::DeviceId(DeviceId),
|
||||
position: ((*event).canvasX as f64, (*event).canvasY as f64),
|
||||
position,
|
||||
modifiers: modifiers,
|
||||
}
|
||||
});
|
||||
queue.borrow_mut().push_back(::Event::DeviceEvent {
|
||||
queue.lock().unwrap().push_back(::Event::DeviceEvent {
|
||||
device_id: ::DeviceId(DeviceId),
|
||||
event: ::DeviceEvent::MouseMotion {
|
||||
delta: ((*event).movementX as f64, (*event).movementY as f64),
|
||||
@@ -215,7 +229,7 @@ extern "C" fn mouse_callback(
|
||||
ffi::EMSCRIPTEN_EVENT_MOUSEUP => ::ElementState::Released,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
queue.borrow_mut().push_back(::Event::WindowEvent {
|
||||
queue.lock().unwrap().push_back(::Event::WindowEvent {
|
||||
window_id: ::WindowId(WindowId(0)),
|
||||
event: ::WindowEvent::MouseInput {
|
||||
device_id: ::DeviceId(DeviceId),
|
||||
@@ -238,7 +252,7 @@ extern "C" fn keyboard_callback(
|
||||
event_queue: *mut c_void) -> ffi::EM_BOOL
|
||||
{
|
||||
unsafe {
|
||||
let queue: &RefCell<VecDeque<::Event>> = mem::transmute(event_queue);
|
||||
let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue);
|
||||
|
||||
let modifiers = ::ModifiersState {
|
||||
shift: (*event).shiftKey == ffi::EM_TRUE,
|
||||
@@ -249,7 +263,7 @@ extern "C" fn keyboard_callback(
|
||||
|
||||
match event_type {
|
||||
ffi::EMSCRIPTEN_EVENT_KEYDOWN => {
|
||||
queue.borrow_mut().push_back(::Event::WindowEvent {
|
||||
queue.lock().unwrap().push_back(::Event::WindowEvent {
|
||||
window_id: ::WindowId(WindowId(0)),
|
||||
event: ::WindowEvent::KeyboardInput {
|
||||
device_id: ::DeviceId(DeviceId),
|
||||
@@ -263,7 +277,7 @@ extern "C" fn keyboard_callback(
|
||||
});
|
||||
},
|
||||
ffi::EMSCRIPTEN_EVENT_KEYUP => {
|
||||
queue.borrow_mut().push_back(::Event::WindowEvent {
|
||||
queue.lock().unwrap().push_back(::Event::WindowEvent {
|
||||
window_id: ::WindowId(WindowId(0)),
|
||||
event: ::WindowEvent::KeyboardInput {
|
||||
device_id: ::DeviceId(DeviceId),
|
||||
@@ -289,7 +303,7 @@ extern fn touch_callback(
|
||||
event_queue: *mut c_void) -> ffi::EM_BOOL
|
||||
{
|
||||
unsafe {
|
||||
let queue: &RefCell<VecDeque<::Event>> = mem::transmute(event_queue);
|
||||
let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue);
|
||||
|
||||
let phase = match event_type {
|
||||
ffi::EMSCRIPTEN_EVENT_TOUCHSTART => ::TouchPhase::Started,
|
||||
@@ -302,13 +316,18 @@ extern fn touch_callback(
|
||||
for touch in 0..(*event).numTouches as usize {
|
||||
let touch = (*event).touches[touch];
|
||||
if touch.isChanged == ffi::EM_TRUE {
|
||||
queue.borrow_mut().push_back(::Event::WindowEvent {
|
||||
let dpi_factor = get_hidpi_factor();
|
||||
let location = LogicalPosition::from_physical(
|
||||
(touch.canvasX as f64, touch.canvasY as f64),
|
||||
dpi_factor,
|
||||
);
|
||||
queue.lock().unwrap().push_back(::Event::WindowEvent {
|
||||
window_id: ::WindowId(WindowId(0)),
|
||||
event: ::WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(DeviceId),
|
||||
phase,
|
||||
id: touch.identifier as u64,
|
||||
location: (touch.canvasX as f64, touch.canvasY as f64),
|
||||
location,
|
||||
}),
|
||||
});
|
||||
}
|
||||
@@ -356,8 +375,9 @@ impl Window {
|
||||
}
|
||||
|
||||
let w = Window2 {
|
||||
cursor_state: Mutex::new(::CursorState::Normal),
|
||||
events: Box::new(RefCell::new(VecDeque::new())),
|
||||
cursor_grabbed: Default::default(),
|
||||
cursor_hidden: Default::default(),
|
||||
events: Default::default(),
|
||||
is_fullscreen: attribs.fullscreen.is_some(),
|
||||
};
|
||||
|
||||
@@ -395,8 +415,8 @@ impl Window {
|
||||
em_try(ffi::emscripten_set_fullscreenchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, Some(fullscreen_callback)))
|
||||
.map_err(|e| ::CreationError::OsError(e))?;
|
||||
}
|
||||
} else if let Some((w, h)) = attribs.dimensions {
|
||||
window.set_inner_size(w, h);
|
||||
} else if let Some(size) = attribs.dimensions {
|
||||
window.set_inner_size(size);
|
||||
}
|
||||
|
||||
*events_loop.window.lock().unwrap() = Some(window.window.clone());
|
||||
@@ -413,22 +433,22 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
Some((0, 0))
|
||||
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||
Some((0, 0).into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
Some((0, 0))
|
||||
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||
Some((0, 0).into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_position(&self, _: i32, _: i32) {
|
||||
pub fn set_position(&self, _: LogicalPosition) {
|
||||
}
|
||||
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||
unsafe {
|
||||
use std::{mem, ptr};
|
||||
let mut width = 0;
|
||||
let mut height = 0;
|
||||
let mut fullscreen = 0;
|
||||
@@ -438,99 +458,109 @@ impl Window {
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some((width as u32, height as u32))
|
||||
let dpi_factor = self.get_hidpi_factor();
|
||||
let logical = LogicalSize::from_physical((width as u32, height as u32), dpi_factor);
|
||||
Some(logical)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||
self.get_inner_size()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_inner_size(&self, width: u32, height: u32) {
|
||||
pub fn set_inner_size(&self, size: LogicalSize) {
|
||||
unsafe {
|
||||
use std::ptr;
|
||||
ffi::emscripten_set_element_css_size(ptr::null(), width as c_double, height
|
||||
as c_double);
|
||||
let dpi_factor = self.get_hidpi_factor();
|
||||
let physical = PhysicalSize::from_logical(size, dpi_factor);
|
||||
let (width, height): (u32, u32) = physical.into();
|
||||
ffi::emscripten_set_element_css_size(
|
||||
ptr::null(),
|
||||
width as c_double,
|
||||
height as c_double,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
pub fn set_min_dimensions(&self, _dimensions: Option<LogicalSize>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<LogicalSize>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, _resizable: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show(&self) {}
|
||||
#[inline]
|
||||
pub fn hide(&self) {}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut ::libc::c_void {
|
||||
unimplemented!()
|
||||
pub fn show(&self) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_window(&self) -> *mut ::libc::c_void {
|
||||
unimplemented!()
|
||||
pub fn hide(&self) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _cursor: ::MouseCursor) {}
|
||||
pub fn set_cursor(&self, _cursor: ::MouseCursor) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, state: ::CursorState) -> Result<(), String> {
|
||||
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
|
||||
let mut grabbed_lock = self.window.cursor_grabbed.lock().unwrap();
|
||||
if grab == *grabbed_lock { return Ok(()); }
|
||||
unsafe {
|
||||
use ::CursorState::*;
|
||||
|
||||
let mut old_state = self.window.cursor_state.lock().unwrap();
|
||||
if state == *old_state {
|
||||
return Ok(());
|
||||
if grab {
|
||||
em_try(ffi::emscripten_set_pointerlockchange_callback(
|
||||
ptr::null(),
|
||||
0 as *mut c_void,
|
||||
ffi::EM_FALSE,
|
||||
Some(pointerlockchange_callback),
|
||||
))?;
|
||||
em_try(ffi::emscripten_request_pointerlock(ptr::null(), ffi::EM_TRUE))?;
|
||||
} else {
|
||||
em_try(ffi::emscripten_set_pointerlockchange_callback(
|
||||
ptr::null(),
|
||||
0 as *mut c_void,
|
||||
ffi::EM_FALSE,
|
||||
None,
|
||||
))?;
|
||||
em_try(ffi::emscripten_exit_pointerlock())?;
|
||||
}
|
||||
|
||||
// Set or unset grab callback
|
||||
match state {
|
||||
Hide | Normal => em_try(ffi::emscripten_set_pointerlockchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, None))?,
|
||||
Grab => em_try(ffi::emscripten_set_pointerlockchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, Some(pointerlockchange_callback)))?,
|
||||
}
|
||||
|
||||
// Go back to normal cursor state
|
||||
match *old_state {
|
||||
Hide => show_mouse(),
|
||||
Grab => em_try(ffi::emscripten_exit_pointerlock())?,
|
||||
Normal => (),
|
||||
}
|
||||
|
||||
// Set cursor from normal cursor state
|
||||
match state {
|
||||
Hide => ffi::emscripten_hide_mouse(),
|
||||
Grab => em_try(ffi::emscripten_request_pointerlock(ptr::null(), ffi::EM_TRUE))?,
|
||||
Normal => (),
|
||||
}
|
||||
|
||||
// Update
|
||||
*old_state = state;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
*grabbed_lock = grab;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
unsafe { ffi::emscripten_get_device_pixel_ratio() as f32 }
|
||||
pub fn hide_cursor(&self, hide: bool) {
|
||||
let mut hidden_lock = self.window.cursor_hidden.lock().unwrap();
|
||||
if hide == *hidden_lock { return; }
|
||||
if hide {
|
||||
unsafe { ffi::emscripten_hide_mouse() };
|
||||
} else {
|
||||
show_mouse();
|
||||
}
|
||||
*hidden_lock = hide;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
|
||||
Err(())
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
get_hidpi_factor()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, _position: LogicalPosition) -> Result<(), String> {
|
||||
Err("Setting cursor position is not possible on Emscripten.".to_owned())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -559,13 +589,25 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_spot(&self, _x: i32, _y: i32) {
|
||||
pub fn set_ime_spot(&self, _logical_spot: LogicalPosition) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> ::MonitorId {
|
||||
::MonitorId{inner: MonitorId}
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
RootMonitorId { inner: MonitorId }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
let mut list = VecDeque::with_capacity(1);
|
||||
list.push_back(MonitorId);
|
||||
list
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
MonitorId
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,7 +621,8 @@ impl Drop for Window {
|
||||
|
||||
unsafe {
|
||||
// Return back to normal cursor state
|
||||
let _ = self.set_cursor_state(::CursorState::Normal);
|
||||
self.hide_cursor(false);
|
||||
self.grab_cursor(false);
|
||||
|
||||
// Exit fullscreen if on
|
||||
if self.window.is_fullscreen {
|
||||
@@ -612,7 +655,6 @@ fn error_to_str(code: ffi::EMSCRIPTEN_RESULT) -> &'static str {
|
||||
}
|
||||
|
||||
fn key_translate(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES]) -> u8 {
|
||||
use std::str;
|
||||
let slice = &input[0..input.iter().take_while(|x| **x != 0).count()];
|
||||
let maybe_key = unsafe { str::from_utf8(mem::transmute::<_, &[u8]>(slice)) };
|
||||
let key = match maybe_key {
|
||||
@@ -629,7 +671,6 @@ fn key_translate(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES]) ->
|
||||
fn key_translate_virt(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES],
|
||||
location: c_ulong) -> Option<::VirtualKeyCode>
|
||||
{
|
||||
use std::str;
|
||||
let slice = &input[0..input.iter().take_while(|x| **x != 0).count()];
|
||||
let maybe_key = unsafe { str::from_utf8(mem::transmute::<_, &[u8]>(slice)) };
|
||||
let key = match maybe_key {
|
||||
@@ -769,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,20 +1,17 @@
|
||||
#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::*;
|
||||
|
||||
use libc;
|
||||
use objc::runtime::{ Object, Class };
|
||||
use objc::runtime::Object;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type id = *mut Object;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const nil: id = 0 as id;
|
||||
|
||||
pub type CFStringRef = *const libc::c_void;
|
||||
pub type CFStringRef = *const c_void;
|
||||
pub type CFTimeInterval = f64;
|
||||
pub type Boolean = u32;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const kCFRunLoopRunHandledSource: i32 = 4;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
@@ -22,11 +19,6 @@ 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 {
|
||||
@@ -38,14 +30,14 @@ pub struct CGPoint {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CGRect {
|
||||
pub origin: CGPoint,
|
||||
pub size: CGSize
|
||||
pub size: CGSize,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CGSize {
|
||||
pub width: CGFloat,
|
||||
pub height: CGFloat
|
||||
pub height: CGFloat,
|
||||
}
|
||||
|
||||
#[link(name = "UIKit", kind = "framework")]
|
||||
@@ -55,33 +47,51 @@ extern {
|
||||
pub static kCFRunLoopDefaultMode: CFStringRef;
|
||||
|
||||
// int UIApplicationMain ( int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName );
|
||||
pub fn UIApplicationMain(argc: libc::c_int, argv: *const libc::c_char, principalClassName: id, delegateClassName: id) -> libc::c_int;
|
||||
pub fn UIApplicationMain(
|
||||
argc: c_int,
|
||||
argv: *const c_char,
|
||||
principalClassName: id,
|
||||
delegateClassName: id,
|
||||
) -> c_int;
|
||||
|
||||
// SInt32 CFRunLoopRunInMode ( CFStringRef mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled );
|
||||
pub fn CFRunLoopRunInMode(mode: CFStringRef, seconds: CFTimeInterval, returnAfterSourceHandled: Boolean) -> i32;
|
||||
pub fn CFRunLoopRunInMode(
|
||||
mode: CFStringRef,
|
||||
seconds: CFTimeInterval,
|
||||
returnAfterSourceHandled: Boolean,
|
||||
) -> i32;
|
||||
}
|
||||
|
||||
extern {
|
||||
pub fn setjmp(env: *mut libc::c_void) -> libc::c_int;
|
||||
pub fn longjmp(env: *mut libc::c_void, val: libc::c_int);
|
||||
pub fn setjmp(env: *mut c_void) -> 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]
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn initWithUTF8String_(self, c_string: *const i8) -> id;
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id;
|
||||
unsafe fn stringByAppendingString_(self, other: id) -> id;
|
||||
unsafe fn init_str(self, string: &str) -> Self;
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn UTF8String(self) -> *const libc::c_char;
|
||||
unsafe fn UTF8String(self) -> *const c_char;
|
||||
}
|
||||
|
||||
impl NSString for id {
|
||||
unsafe fn initWithUTF8String_(self, c_string: *const i8) -> id {
|
||||
unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id {
|
||||
msg_send![self, initWithUTF8String:c_string as id]
|
||||
}
|
||||
|
||||
@@ -94,14 +104,7 @@ impl NSString for id {
|
||||
self.initWithUTF8String_(cstring.as_ptr())
|
||||
}
|
||||
|
||||
unsafe fn UTF8String(self) -> *const libc::c_char {
|
||||
unsafe fn UTF8String(self) -> *const c_char {
|
||||
msg_send![self, UTF8String]
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn class(name: &str) -> *mut Class {
|
||||
unsafe {
|
||||
::std::mem::transmute(Class::get(name))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
//!
|
||||
//! ```rust, ignore
|
||||
//! #[no_mangle]
|
||||
//! pub extern fn start_glutin_app() {
|
||||
//! pub extern fn start_winit_app() {
|
||||
//! start_inner()
|
||||
//! }
|
||||
//!
|
||||
@@ -29,13 +29,13 @@
|
||||
//!
|
||||
//! ```
|
||||
//!
|
||||
//! Compile project and then drag resulting .a into Xcode project. Add glutin.h to xcode.
|
||||
//! Compile project and then drag resulting .a into Xcode project. Add winit.h to xcode.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! void start_glutin_app();
|
||||
//! void start_winit_app();
|
||||
//! ```
|
||||
//!
|
||||
//! Use start_glutin_app inside your xcode's main function.
|
||||
//! Use start_winit_app inside your xcode's main function.
|
||||
//!
|
||||
//!
|
||||
//! # App lifecycle and events
|
||||
@@ -45,7 +45,7 @@
|
||||
//! [app lifecycle](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/).
|
||||
//!
|
||||
//!
|
||||
//! This is how those event are represented in glutin:
|
||||
//! This is how those event are represented in winit:
|
||||
//!
|
||||
//! - applicationDidBecomeActive is Focused(true)
|
||||
//! - applicationWillResignActive is Focused(false)
|
||||
@@ -60,100 +60,147 @@
|
||||
|
||||
#![cfg(target_os = "ios")]
|
||||
|
||||
use std::{fmt, mem, ptr};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::ptr;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
use std::os::raw::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
use libc;
|
||||
use libc::c_int;
|
||||
use objc::runtime::{Class, Object, Sel, BOOL, YES };
|
||||
use objc::declare::{ ClassDecl };
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::{BOOL, Class, Object, Sel, YES};
|
||||
|
||||
use { CreationError, CursorState, MouseCursor, WindowAttributes };
|
||||
use WindowId as RootEventId;
|
||||
use WindowEvent;
|
||||
use Event;
|
||||
use events::{ Touch, TouchPhase };
|
||||
use {
|
||||
CreationError,
|
||||
Event,
|
||||
LogicalPosition,
|
||||
LogicalSize,
|
||||
MouseCursor,
|
||||
PhysicalPosition,
|
||||
PhysicalSize,
|
||||
WindowAttributes,
|
||||
WindowEvent,
|
||||
WindowId as RootEventId,
|
||||
};
|
||||
use events::{Touch, TouchPhase};
|
||||
use window::MonitorId as RootMonitorId;
|
||||
|
||||
mod ffi;
|
||||
use self::ffi::{
|
||||
setjmp,
|
||||
UIApplicationMain,
|
||||
CFTimeInterval,
|
||||
CFRunLoopRunInMode,
|
||||
CGFloat,
|
||||
CGPoint,
|
||||
CGRect,
|
||||
id,
|
||||
JBLEN,
|
||||
JmpBuf,
|
||||
kCFRunLoopDefaultMode,
|
||||
kCFRunLoopRunHandledSource,
|
||||
id,
|
||||
longjmp,
|
||||
nil,
|
||||
NSString,
|
||||
CGFloat,
|
||||
longjmp,
|
||||
CGRect,
|
||||
CGPoint
|
||||
setjmp,
|
||||
UIApplicationMain,
|
||||
};
|
||||
|
||||
static mut jmpbuf: [c_int;27] = [0;27];
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MonitorId;
|
||||
static mut JMPBUF: Option<Box<JmpBuf>> = None;
|
||||
|
||||
pub struct Window {
|
||||
delegate_state: *mut DelegateState
|
||||
_events_queue: Arc<RefCell<VecDeque<Event>>>,
|
||||
delegate_state: Box<DelegateState>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WindowProxy;
|
||||
unsafe impl Send for Window {}
|
||||
unsafe impl Sync for Window {}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DelegateState {
|
||||
events_queue: VecDeque<Event>,
|
||||
window: id,
|
||||
controller: id,
|
||||
size: (u32,u32),
|
||||
scale: f32
|
||||
view: id,
|
||||
size: LogicalSize,
|
||||
scale: f64,
|
||||
}
|
||||
|
||||
|
||||
impl DelegateState {
|
||||
#[inline]
|
||||
fn new(window: id, controller:id, size: (u32,u32), scale: f32) -> DelegateState {
|
||||
fn new(window: id, controller: id, view: id, size: LogicalSize, scale: f64) -> DelegateState {
|
||||
DelegateState {
|
||||
events_queue: VecDeque::new(),
|
||||
window: window,
|
||||
controller: controller,
|
||||
size: size,
|
||||
scale: scale
|
||||
window,
|
||||
controller,
|
||||
view,
|
||||
size,
|
||||
scale,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DelegateState {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let _: () = msg_send![self.window, release];
|
||||
let _: () = msg_send![self.controller, release];
|
||||
let _: () = msg_send![self.view, release];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorId;
|
||||
|
||||
impl fmt::Debug for MonitorId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
#[derive(Debug)]
|
||||
struct MonitorId {
|
||||
name: Option<String>,
|
||||
dimensions: PhysicalSize,
|
||||
position: PhysicalPosition,
|
||||
hidpi_factor: f64,
|
||||
}
|
||||
|
||||
let monitor_id_proxy = MonitorId {
|
||||
name: self.get_name(),
|
||||
dimensions: self.get_dimensions(),
|
||||
position: self.get_position(),
|
||||
hidpi_factor: self.get_hidpi_factor(),
|
||||
};
|
||||
|
||||
monitor_id_proxy.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
#[inline]
|
||||
pub fn get_uiscreen(&self) -> id {
|
||||
let class = class!(UIScreen);
|
||||
unsafe { msg_send![class, mainScreen] }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
Some("Primary".to_string())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
unimplemented!()
|
||||
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||
let bounds: CGRect = unsafe { msg_send![self.get_uiscreen(), nativeBounds] };
|
||||
(bounds.size.width as f64, bounds.size.height as f64).into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
pub fn get_position(&self) -> PhysicalPosition {
|
||||
// iOS assumes single screen
|
||||
(0, 0)
|
||||
(0, 0).into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
1.0
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
let scale: CGFloat = unsafe { msg_send![self.get_uiscreen(), nativeScale] };
|
||||
scale as f64
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventsLoop {
|
||||
delegate_state: *mut DelegateState
|
||||
events_queue: Arc<RefCell<VecDeque<Event>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -162,30 +209,16 @@ pub struct EventsLoopProxy;
|
||||
impl EventsLoop {
|
||||
pub fn new() -> EventsLoop {
|
||||
unsafe {
|
||||
if setjmp(mem::transmute(&mut jmpbuf)) != 0 {
|
||||
let app: id = msg_send![Class::get("UIApplication").unwrap(), sharedApplication];
|
||||
let delegate: id = msg_send![app, delegate];
|
||||
let state: *mut c_void = *(&*delegate).get_ivar("glutinState");
|
||||
let state = state as *mut DelegateState;
|
||||
|
||||
let events_loop = EventsLoop {
|
||||
delegate_state: state
|
||||
};
|
||||
|
||||
return events_loop;
|
||||
if !msg_send![class!(NSThread), isMainThread] {
|
||||
panic!("`EventsLoop` can only be created on the main thread on iOS");
|
||||
}
|
||||
}
|
||||
|
||||
create_delegate_class();
|
||||
create_view_class();
|
||||
start_app();
|
||||
|
||||
panic!("Couldn't create UIApplication")
|
||||
EventsLoop { events_queue: Default::default() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
let mut rb = VecDeque::new();
|
||||
let mut rb = VecDeque::with_capacity(1);
|
||||
rb.push_back(MonitorId);
|
||||
rb
|
||||
}
|
||||
@@ -198,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,114 +293,177 @@ 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.
|
||||
impl Window {
|
||||
pub fn new(ev: &EventsLoop, _: WindowAttributes, _: PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window, CreationError>
|
||||
{
|
||||
Ok(Window {
|
||||
delegate_state: ev.delegate_state,
|
||||
})
|
||||
pub fn new(
|
||||
ev: &EventsLoop,
|
||||
_attributes: WindowAttributes,
|
||||
pl_attributes: PlatformSpecificWindowBuilderAttributes,
|
||||
) -> Result<Window, CreationError> {
|
||||
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 set_title(&self, _: &str) {
|
||||
pub fn get_uiwindow(&self) -> id {
|
||||
self.delegate_state.window
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_uiview(&self) -> id {
|
||||
self.delegate_state.view
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_title(&self, _title: &str) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show(&self) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide(&self) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||
// N/A
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||
// N/A
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_position(&self, _x: i32, _y: i32) {
|
||||
pub fn set_position(&self, _position: LogicalPosition) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
unsafe { Some((&*self.delegate_state).size) }
|
||||
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||
Some(self.delegate_state.size)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||
self.get_inner_size()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_inner_size(&self, _x: u32, _y: u32) {
|
||||
pub fn set_inner_size(&self, _size: LogicalSize) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
pub fn set_min_dimensions(&self, _dimensions: Option<LogicalSize>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<LogicalSize>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, _resizable: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
unimplemented!();
|
||||
pub fn set_cursor(&self, _cursor: MouseCursor) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
unimplemented!()
|
||||
pub fn grab_cursor(&self, _grab: bool) -> Result<(), String> {
|
||||
Err("Cursor grabbing is not possible on iOS.".to_owned())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
||||
pub fn hide_cursor(&self, _hide: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _: MouseCursor) {
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
self.delegate_state.scale
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, _: CursorState) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
unsafe { (&*self.delegate_state) }.scale
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn create_window_proxy(&self) -> WindowProxy {
|
||||
WindowProxy
|
||||
pub fn set_cursor_position(&self, _position: LogicalPosition) -> Result<(), String> {
|
||||
Err("Setting cursor position is not possible on iOS.".to_owned())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, _maximized: bool) {
|
||||
// N/A
|
||||
// iOS has single screen maximized apps so nothing to do
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) {
|
||||
// N/A
|
||||
// iOS has single screen maximized apps so nothing to do
|
||||
}
|
||||
|
||||
@@ -386,13 +483,25 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_spot(&self, _x: i32, _y: i32) {
|
||||
pub fn set_ime_spot(&self, _logical_spot: LogicalPosition) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
RootMonitorId{inner: MonitorId}
|
||||
RootMonitorId { inner: MonitorId }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
let mut rb = VecDeque::with_capacity(1);
|
||||
rb.push_back(MonitorId);
|
||||
rb
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
MonitorId
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -403,41 +512,44 @@ impl Window {
|
||||
|
||||
fn create_delegate_class() {
|
||||
extern fn did_finish_launching(this: &mut Object, _: Sel, _: id, _: id) -> BOOL {
|
||||
let screen_class = class!(UIScreen);
|
||||
let window_class = class!(UIWindow);
|
||||
let controller_class = class!(UIViewController);
|
||||
unsafe {
|
||||
let main_screen: id = msg_send![Class::get("UIScreen").unwrap(), mainScreen];
|
||||
let main_screen: id = msg_send![screen_class, mainScreen];
|
||||
let bounds: CGRect = msg_send![main_screen, bounds];
|
||||
let scale: CGFloat = msg_send![main_screen, nativeScale];
|
||||
|
||||
let window: id = msg_send![Class::get("UIWindow").unwrap(), alloc];
|
||||
let window: id = msg_send![window_class, alloc];
|
||||
let window: id = msg_send![window, initWithFrame:bounds.clone()];
|
||||
|
||||
let size = (bounds.size.width as u32, bounds.size.height as u32);
|
||||
let size = (bounds.size.width as f64, bounds.size.height as f64).into();
|
||||
|
||||
let view_controller: id = msg_send![Class::get("MainViewController").unwrap(), alloc];
|
||||
let view_controller: id = msg_send![controller_class, alloc];
|
||||
let view_controller: id = msg_send![view_controller, init];
|
||||
|
||||
let _: () = msg_send![window, setRootViewController:view_controller];
|
||||
let _: () = msg_send![window, makeKeyAndVisible];
|
||||
|
||||
let state = Box::new(DelegateState::new(window, view_controller, size, scale as f32));
|
||||
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("glutinState", state_ptr as *mut c_void);
|
||||
|
||||
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("glutinState");
|
||||
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),
|
||||
});
|
||||
@@ -446,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("glutinState");
|
||||
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),
|
||||
});
|
||||
@@ -457,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("glutinState");
|
||||
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("glutinState");
|
||||
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("glutinState");
|
||||
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("glutinState");
|
||||
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];
|
||||
|
||||
@@ -501,12 +613,12 @@ 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,
|
||||
id: touch_id,
|
||||
location: (location.x as f64, location.y as f64),
|
||||
location: (location.x as f64, location.y as f64).into(),
|
||||
phase: match phase {
|
||||
0 => TouchPhase::Started,
|
||||
1 => TouchPhase::Moved,
|
||||
@@ -521,8 +633,8 @@ fn create_delegate_class() {
|
||||
}
|
||||
}
|
||||
|
||||
let ui_responder = Class::get("UIResponder").unwrap();
|
||||
let mut decl = ClassDecl::new("AppDelegate", ui_responder).unwrap();
|
||||
let ui_responder = class!(UIResponder);
|
||||
let mut decl = ClassDecl::new("AppDelegate", ui_responder).expect("Failed to declare class `AppDelegate`");
|
||||
|
||||
unsafe {
|
||||
decl.add_method(sel!(application:didFinishLaunchingWithOptions:),
|
||||
@@ -560,19 +672,13 @@ fn create_delegate_class() {
|
||||
decl.add_method(sel!(postLaunch:),
|
||||
post_launch as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_ivar::<*mut c_void>("glutinState");
|
||||
decl.add_ivar::<*mut c_void>("winitState");
|
||||
decl.add_ivar::<*mut c_void>("eventsQueue");
|
||||
|
||||
decl.register();
|
||||
}
|
||||
}
|
||||
|
||||
fn create_view_class() {
|
||||
let ui_view_controller = Class::get("UIViewController").unwrap();
|
||||
let decl = ClassDecl::new("MainViewController", ui_view_controller).unwrap();
|
||||
|
||||
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};
|
||||
@@ -6,20 +6,18 @@ use std::ffi::CStr;
|
||||
use std::os::raw::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use sctk::reexports::client::ConnectError;
|
||||
|
||||
// `std::os::raw::c_void` and `libc::c_void` are NOT interchangeable!
|
||||
use libc;
|
||||
|
||||
use {
|
||||
CreationError,
|
||||
CursorState,
|
||||
EventsLoopClosed,
|
||||
Icon,
|
||||
MouseCursor,
|
||||
ControlFlow,
|
||||
WindowAttributes,
|
||||
};
|
||||
use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
|
||||
use window::MonitorId as RootMonitorId;
|
||||
use self::x11::{XConnection, XError};
|
||||
use self::x11::ffi::XVisualInfo;
|
||||
@@ -47,29 +45,30 @@ 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>,
|
||||
}
|
||||
|
||||
thread_local!(
|
||||
pub static X11_BACKEND: Result<Arc<XConnection>, XNotSupported> = {
|
||||
XConnection::new(Some(x_error_callback)).map(Arc::new)
|
||||
lazy_static!(
|
||||
pub static ref X11_BACKEND: Mutex<Result<Arc<XConnection>, XNotSupported>> = {
|
||||
Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new))
|
||||
};
|
||||
);
|
||||
|
||||
pub enum Window {
|
||||
X(x11::Window),
|
||||
Wayland(wayland::Window)
|
||||
Wayland(wayland::Window),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum WindowId {
|
||||
X(x11::WindowId),
|
||||
Wayland(wayland::WindowId)
|
||||
Wayland(wayland::WindowId),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum DeviceId {
|
||||
X(x11::DeviceId),
|
||||
Wayland(wayland::DeviceId)
|
||||
Wayland(wayland::DeviceId),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -96,7 +95,7 @@ impl MonitorId {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||
match self {
|
||||
&MonitorId::X(ref m) => m.get_dimensions(),
|
||||
&MonitorId::Wayland(ref m) => m.get_dimensions(),
|
||||
@@ -104,7 +103,7 @@ impl MonitorId {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
pub fn get_position(&self) -> PhysicalPosition {
|
||||
match self {
|
||||
&MonitorId::X(ref m) => m.get_position(),
|
||||
&MonitorId::Wayland(ref m) => m.get_position(),
|
||||
@@ -112,10 +111,10 @@ impl MonitorId {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
match self {
|
||||
&MonitorId::X(ref m) => m.get_hidpi_factor(),
|
||||
&MonitorId::Wayland(ref m) => m.get_hidpi_factor(),
|
||||
&MonitorId::Wayland(ref m) => m.get_hidpi_factor() as f64,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,7 +140,7 @@ impl Window {
|
||||
pub fn id(&self) -> WindowId {
|
||||
match self {
|
||||
&Window::X(ref w) => WindowId::X(w.id()),
|
||||
&Window::Wayland(ref w) => WindowId::Wayland(w.id())
|
||||
&Window::Wayland(ref w) => WindowId::Wayland(w.id()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +148,7 @@ impl Window {
|
||||
pub fn set_title(&self, title: &str) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_title(title),
|
||||
&Window::Wayland(ref w) => w.set_title(title)
|
||||
&Window::Wayland(ref w) => w.set_title(title),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +156,7 @@ impl Window {
|
||||
pub fn show(&self) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.show(),
|
||||
&Window::Wayland(ref w) => w.show()
|
||||
&Window::Wayland(ref w) => w.show(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,20 +164,20 @@ impl Window {
|
||||
pub fn hide(&self) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.hide(),
|
||||
&Window::Wayland(ref w) => w.hide()
|
||||
&Window::Wayland(ref w) => w.hide(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||
match self {
|
||||
&Window::X(ref w) => w.get_position(),
|
||||
&Window::Wayland(ref w) => w.get_position()
|
||||
&Window::Wayland(ref w) => w.get_position(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||
match self {
|
||||
&Window::X(ref m) => m.get_inner_position(),
|
||||
&Window::Wayland(ref m) => m.get_inner_position(),
|
||||
@@ -186,53 +185,53 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_position(&self, x: i32, y: i32) {
|
||||
pub fn set_position(&self, position: LogicalPosition) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_position(x, y),
|
||||
&Window::Wayland(ref w) => w.set_position(x, y)
|
||||
&Window::X(ref w) => w.set_position(position),
|
||||
&Window::Wayland(ref w) => w.set_position(position),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||
match self {
|
||||
&Window::X(ref w) => w.get_inner_size(),
|
||||
&Window::Wayland(ref w) => w.get_inner_size()
|
||||
&Window::Wayland(ref w) => w.get_inner_size(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||
match self {
|
||||
&Window::X(ref w) => w.get_outer_size(),
|
||||
&Window::Wayland(ref w) => w.get_outer_size()
|
||||
&Window::Wayland(ref w) => w.get_outer_size(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_inner_size(&self, x: u32, y: u32) {
|
||||
pub fn set_inner_size(&self, size: LogicalSize) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_inner_size(x, y),
|
||||
&Window::Wayland(ref w) => w.set_inner_size(x, y)
|
||||
&Window::X(ref w) => w.set_inner_size(size),
|
||||
&Window::Wayland(ref w) => w.set_inner_size(size),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_min_dimensions(dimensions),
|
||||
&Window::Wayland(ref w) => w.set_min_dimensions(dimensions)
|
||||
&Window::Wayland(ref w) => w.set_min_dimensions(dimensions),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_max_dimensions(dimensions),
|
||||
&Window::Wayland(ref w) => w.set_max_dimensions(dimensions)
|
||||
&Window::Wayland(ref w) => w.set_max_dimensions(dimensions),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
match self {
|
||||
@@ -250,42 +249,34 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_cursor_state(state),
|
||||
&Window::Wayland(ref w) => w.set_cursor_state(state)
|
||||
&Window::X(ref window) => window.grab_cursor(grab),
|
||||
&Window::Wayland(ref window) => window.grab_cursor(grab),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
pub fn hide_cursor(&self, hide: bool) {
|
||||
match self {
|
||||
&Window::X(ref window) => window.hide_cursor(hide),
|
||||
&Window::Wayland(ref window) => window.hide_cursor(hide),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
match self {
|
||||
&Window::X(ref w) => w.hidpi_factor(),
|
||||
&Window::Wayland(ref w) => w.hidpi_factor()
|
||||
&Window::X(ref w) => w.get_hidpi_factor(),
|
||||
&Window::Wayland(ref w) => w.hidpi_factor() as f64,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||
pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), String> {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_cursor_position(x, y),
|
||||
&Window::Wayland(ref w) => w.set_cursor_position(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
match self {
|
||||
&Window::X(ref w) => w.platform_display(),
|
||||
&Window::Wayland(ref w) => w.get_display().c_ptr() as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
match self {
|
||||
&Window::X(ref w) => w.platform_window(),
|
||||
&Window::Wayland(ref w) => w.get_surface().c_ptr() as *mut _
|
||||
&Window::X(ref w) => w.set_cursor_position(position),
|
||||
&Window::Wayland(ref w) => w.set_cursor_position(position),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,9 +321,9 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_spot(&self, x: i32, y: i32) {
|
||||
pub fn set_ime_spot(&self, position: LogicalPosition) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.send_xim_spot(x as i16, y as i16),
|
||||
&Window::X(ref w) => w.set_ime_spot(position),
|
||||
&Window::Wayland(_) => (),
|
||||
}
|
||||
}
|
||||
@@ -340,8 +331,30 @@ impl Window {
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
match self {
|
||||
&Window::X(ref w) => RootMonitorId{inner: MonitorId::X(w.get_current_monitor())},
|
||||
&Window::Wayland(ref w) => RootMonitorId{inner: MonitorId::Wayland(w.get_current_monitor())},
|
||||
&Window::X(ref window) => RootMonitorId { inner: MonitorId::X(window.get_current_monitor()) },
|
||||
&Window::Wayland(ref window) => RootMonitorId { inner: MonitorId::Wayland(window.get_current_monitor()) },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
match self {
|
||||
&Window::X(ref window) => window.get_available_monitors()
|
||||
.into_iter()
|
||||
.map(MonitorId::X)
|
||||
.collect(),
|
||||
&Window::Wayland(ref window) => window.get_available_monitors()
|
||||
.into_iter()
|
||||
.map(MonitorId::Wayland)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
match self {
|
||||
&Window::X(ref window) => MonitorId::X(window.get_primary_monitor()),
|
||||
&Window::Wayland(ref window) => MonitorId::Wayland(window.get_primary_monitor()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -350,29 +363,28 @@ unsafe extern "C" fn x_error_callback(
|
||||
display: *mut x11::ffi::Display,
|
||||
event: *mut x11::ffi::XErrorEvent,
|
||||
) -> c_int {
|
||||
X11_BACKEND.with(|result| {
|
||||
if let &Ok(ref xconn) = result {
|
||||
let mut buf: [c_char; 1024] = mem::uninitialized();
|
||||
(xconn.xlib.XGetErrorText)(
|
||||
display,
|
||||
(*event).error_code as c_int,
|
||||
buf.as_mut_ptr(),
|
||||
buf.len() as c_int,
|
||||
);
|
||||
let description = CStr::from_ptr(buf.as_ptr()).to_string_lossy();
|
||||
let xconn_lock = X11_BACKEND.lock();
|
||||
if let Ok(ref xconn) = *xconn_lock {
|
||||
let mut buf: [c_char; 1024] = mem::uninitialized();
|
||||
(xconn.xlib.XGetErrorText)(
|
||||
display,
|
||||
(*event).error_code as c_int,
|
||||
buf.as_mut_ptr(),
|
||||
buf.len() as c_int,
|
||||
);
|
||||
let description = CStr::from_ptr(buf.as_ptr()).to_string_lossy();
|
||||
|
||||
let error = XError {
|
||||
description: description.into_owned(),
|
||||
error_code: (*event).error_code,
|
||||
request_code: (*event).request_code,
|
||||
minor_code: (*event).minor_code,
|
||||
};
|
||||
let error = XError {
|
||||
description: description.into_owned(),
|
||||
error_code: (*event).error_code,
|
||||
request_code: (*event).request_code,
|
||||
minor_code: (*event).minor_code,
|
||||
};
|
||||
|
||||
eprintln!("[winit X11 error] {:#?}", error);
|
||||
eprintln!("[winit X11 error] {:#?}", error);
|
||||
|
||||
*xconn.latest_error.lock() = Some(error);
|
||||
}
|
||||
});
|
||||
*xconn.latest_error.lock() = Some(error);
|
||||
}
|
||||
// Fun fact: this return value is completely ignored.
|
||||
0
|
||||
}
|
||||
@@ -418,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,
|
||||
);
|
||||
@@ -434,27 +443,29 @@ r#"Failed to initialize any backend!
|
||||
}
|
||||
|
||||
pub fn new_x11() -> Result<EventsLoop, XNotSupported> {
|
||||
X11_BACKEND.with(|result| {
|
||||
result
|
||||
.as_ref()
|
||||
.map(Arc::clone)
|
||||
.map(x11::EventsLoop::new)
|
||||
.map(EventsLoop::X)
|
||||
.map_err(|err| err.clone())
|
||||
})
|
||||
X11_BACKEND
|
||||
.lock()
|
||||
.as_ref()
|
||||
.map(Arc::clone)
|
||||
.map(x11::EventsLoop::new)
|
||||
.map(EventsLoop::X)
|
||||
.map_err(|err| err.clone())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
match *self {
|
||||
EventsLoop::Wayland(ref evlp) => evlp.get_available_monitors()
|
||||
.into_iter()
|
||||
.map(MonitorId::Wayland)
|
||||
.collect(),
|
||||
EventsLoop::X(ref evlp) => x11::get_available_monitors(evlp.x_connection())
|
||||
.into_iter()
|
||||
.map(MonitorId::X)
|
||||
.collect(),
|
||||
EventsLoop::Wayland(ref evlp) => evlp
|
||||
.get_available_monitors()
|
||||
.into_iter()
|
||||
.map(MonitorId::Wayland)
|
||||
.collect(),
|
||||
EventsLoop::X(ref evlp) => evlp
|
||||
.x_connection()
|
||||
.get_available_monitors()
|
||||
.into_iter()
|
||||
.map(MonitorId::X)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,7 +473,7 @@ r#"Failed to initialize any backend!
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
match *self {
|
||||
EventsLoop::Wayland(ref evlp) => MonitorId::Wayland(evlp.get_primary_monitor()),
|
||||
EventsLoop::X(ref evlp) => MonitorId::X(x11::get_primary_monitor(evlp.x_connection())),
|
||||
EventsLoop::X(ref evlp) => MonitorId::X(evlp.x_connection().get_primary_monitor()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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};
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -199,27 +212,11 @@ impl EventsLoop {
|
||||
}
|
||||
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
self.env.outputs.with_all(|list| {
|
||||
if let Some(&(_, ref proxy, _)) = list.first() {
|
||||
MonitorId {
|
||||
proxy: proxy.clone(),
|
||||
mgr: self.env.outputs.clone(),
|
||||
}
|
||||
} else {
|
||||
panic!("No monitor is available.")
|
||||
}
|
||||
})
|
||||
get_primary_monitor(&self.env.outputs)
|
||||
}
|
||||
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
self.env.outputs.with_all(|list| {
|
||||
list.iter()
|
||||
.map(|&(_, ref proxy, _)| MonitorId {
|
||||
proxy: proxy.clone(),
|
||||
mgr: self.env.outputs.clone(),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
get_available_monitors(&self.env.outputs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,16 +245,24 @@ impl EventsLoop {
|
||||
}
|
||||
// process pending resize/refresh
|
||||
self.store.lock().unwrap().for_each(
|
||||
|newsize, refresh, frame_refresh, closed, wid, frame| {
|
||||
|newsize, size, new_dpi, refresh, frame_refresh, closed, wid, frame| {
|
||||
if let Some(frame) = frame {
|
||||
if let Some((w, h)) = newsize {
|
||||
frame.resize(w as u32, h as u32);
|
||||
frame.resize(w, h);
|
||||
frame.refresh();
|
||||
sink.send_event(::WindowEvent::Resized(w as u32, h as u32), wid);
|
||||
let logical_size = ::LogicalSize::new(w as f64, h as f64);
|
||||
sink.send_event(::WindowEvent::Resized(logical_size), wid);
|
||||
*size = (w, h);
|
||||
} else if frame_refresh {
|
||||
frame.refresh();
|
||||
if !refresh {
|
||||
frame.surface().commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(dpi) = new_dpi {
|
||||
sink.send_event(::WindowEvent::HiDpiFactorChanged(dpi as f64), wid);
|
||||
}
|
||||
if refresh {
|
||||
sink.send_event(::WindowEvent::Refresh, wid);
|
||||
}
|
||||
@@ -277,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;
|
||||
@@ -291,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));
|
||||
}
|
||||
@@ -324,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
|
||||
@@ -352,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
|
||||
@@ -368,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(),
|
||||
))
|
||||
@@ -434,9 +451,9 @@ impl fmt::Debug for MonitorId {
|
||||
struct MonitorId {
|
||||
name: Option<String>,
|
||||
native_identifier: u32,
|
||||
dimensions: (u32, u32),
|
||||
position: (i32, i32),
|
||||
hidpi_factor: f32,
|
||||
dimensions: PhysicalSize,
|
||||
position: PhysicalPosition,
|
||||
hidpi_factor: i32,
|
||||
}
|
||||
|
||||
let monitor_id_proxy = MonitorId {
|
||||
@@ -463,7 +480,7 @@ impl MonitorId {
|
||||
self.mgr.with_info(&self.proxy, |id, _| id).unwrap_or(0)
|
||||
}
|
||||
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||
match self.mgr.with_info(&self.proxy, |_, info| {
|
||||
info.modes
|
||||
.iter()
|
||||
@@ -472,19 +489,44 @@ impl MonitorId {
|
||||
}) {
|
||||
Some(Some((w, h))) => (w as u32, h as u32),
|
||||
_ => (0, 0),
|
||||
}
|
||||
}.into()
|
||||
}
|
||||
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
pub fn get_position(&self) -> PhysicalPosition {
|
||||
self.mgr
|
||||
.with_info(&self.proxy, |_, info| info.location)
|
||||
.unwrap_or((0, 0))
|
||||
.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
pub fn get_hidpi_factor(&self) -> i32 {
|
||||
self.mgr
|
||||
.with_info(&self.proxy, |_, info| info.scale_factor as f32)
|
||||
.unwrap_or(1.0)
|
||||
.with_info(&self.proxy, |_, info| info.scale_factor)
|
||||
.unwrap_or(1)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_primary_monitor(outputs: &OutputMgr) -> MonitorId {
|
||||
outputs.with_all(|list| {
|
||||
if let Some(&(_, ref proxy, _)) = list.first() {
|
||||
MonitorId {
|
||||
proxy: proxy.clone(),
|
||||
mgr: outputs.clone(),
|
||||
}
|
||||
} else {
|
||||
panic!("No monitor is available.")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_available_monitors(outputs: &OutputMgr) -> VecDeque<MonitorId> {
|
||||
outputs.with_all(|list| {
|
||||
list.iter()
|
||||
.map(|&(_, ref proxy, _)| MonitorId {
|
||||
proxy: proxy.clone(),
|
||||
mgr: outputs.clone(),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
@@ -247,7 +298,6 @@ fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
|
||||
keysyms::XKB_KEY_Alt_L => Some(VirtualKeyCode::LAlt),
|
||||
// => Some(VirtualKeyCode::LBracket),
|
||||
keysyms::XKB_KEY_Control_L => Some(VirtualKeyCode::LControl),
|
||||
// => Some(VirtualKeyCode::LMenu),
|
||||
keysyms::XKB_KEY_Shift_L => Some(VirtualKeyCode::LShift),
|
||||
// => Some(VirtualKeyCode::LWin),
|
||||
// => Some(VirtualKeyCode::Mail),
|
||||
@@ -270,7 +320,6 @@ fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
|
||||
keysyms::XKB_KEY_Alt_R => Some(VirtualKeyCode::RAlt),
|
||||
// => Some(VirtualKeyCode::RBracket),
|
||||
keysyms::XKB_KEY_Control_R => Some(VirtualKeyCode::RControl),
|
||||
// => Some(VirtualKeyCode::RMenu),
|
||||
keysyms::XKB_KEY_Shift_R => Some(VirtualKeyCode::RShift),
|
||||
// => Some(VirtualKeyCode::RWin),
|
||||
keysyms::XKB_KEY_semicolon => Some(VirtualKeyCode::Semicolon),
|
||||
|
||||
@@ -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),
|
||||
// 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),
|
||||
// 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 f32, y as f32),
|
||||
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 f32, y as f32),
|
||||
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,
|
||||
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 as u64,
|
||||
}),
|
||||
wid,
|
||||
);
|
||||
pending_ids.push(TouchPoint {
|
||||
wid: wid,
|
||||
location: (x, y),
|
||||
id: id,
|
||||
});
|
||||
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,
|
||||
location: pt.location,
|
||||
id: id as u64,
|
||||
phase: TouchPhase::Cancelled,
|
||||
location: pt.location.into(),
|
||||
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),
|
||||
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,
|
||||
id: pt.id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
},
|
||||
}
|
||||
})
|
||||
}, ())
|
||||
}).unwrap()
|
||||
}
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
|
||||
use {CreationError, CursorState, MouseCursor, WindowAttributes};
|
||||
use {CreationError, MouseCursor, WindowAttributes};
|
||||
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};
|
||||
use sctk::reexports::client::protocol::{wl_seat, wl_surface, wl_output};
|
||||
use sctk::reexports::client::protocol::wl_compositor::RequestsTrait as CompositorRequests;
|
||||
use sctk::reexports::client::protocol::wl_surface::RequestsTrait as SurfaceRequests;
|
||||
use sctk::output::OutputMgr;
|
||||
|
||||
use super::{make_wid, EventsLoop, MonitorId, WindowId};
|
||||
use platform::platform::wayland::event_loop::{get_available_monitors, get_primary_monitor};
|
||||
|
||||
pub struct Window {
|
||||
surface: Proxy<wl_surface::WlSurface>,
|
||||
frame: Arc<Mutex<SWindow<BasicFrame>>>,
|
||||
monitors: Arc<Mutex<Vec<MonitorId>>>,
|
||||
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)>>,
|
||||
kill_switch: (Arc<Mutex<bool>>, Arc<Mutex<bool>>),
|
||||
display: Arc<Display>,
|
||||
@@ -24,42 +29,56 @@ pub struct Window {
|
||||
|
||||
impl Window {
|
||||
pub fn new(evlp: &EventsLoop, attributes: WindowAttributes) -> Result<Window, CreationError> {
|
||||
let (width, height) = attributes.dimensions.unwrap_or((800, 600));
|
||||
let (width, height) = attributes.dimensions.map(Into::into).unwrap_or((800, 600));
|
||||
// Create the window
|
||||
let size = Arc::new(Mutex::new((width, height)));
|
||||
|
||||
// monitor tracking
|
||||
let monitor_list = Arc::new(Mutex::new(Vec::new()));
|
||||
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();
|
||||
move |event, _| match event {
|
||||
wl_surface::Event::Enter { output } => list.lock().unwrap().push(MonitorId {
|
||||
proxy: output,
|
||||
mgr: omgr.clone(),
|
||||
}),
|
||||
let window_store = evlp.store.clone();
|
||||
surface.implement(move |event, surface| match event {
|
||||
wl_surface::Event::Enter { output } => {
|
||||
let dpi_change = list.lock().unwrap().add_output(MonitorId {
|
||||
proxy: output,
|
||||
mgr: omgr.clone(),
|
||||
});
|
||||
if let Some(dpi) = dpi_change {
|
||||
if surface.version() >= 3 {
|
||||
// without version 3 we can't be dpi aware
|
||||
window_store.lock().unwrap().dpi_change(&surface, dpi);
|
||||
surface.set_buffer_scale(dpi);
|
||||
}
|
||||
}
|
||||
},
|
||||
wl_surface::Event::Leave { output } => {
|
||||
list.lock().unwrap().retain(|m| !m.proxy.equals(&output));
|
||||
let dpi_change = list.lock().unwrap().del_output(&output);
|
||||
if let Some(dpi) = dpi_change {
|
||||
if surface.version() >= 3 {
|
||||
// without version 3 we can't be dpi aware
|
||||
window_store.lock().unwrap().dpi_change(&surface, dpi);
|
||||
surface.set_buffer_scale(dpi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}, ())
|
||||
}).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 {
|
||||
if window.surface.equals(&my_surface) {
|
||||
window.newsize = new_size.map(|(w, h)| (w as i32, h as i32));
|
||||
window.newsize = new_size;
|
||||
window.need_refresh = true;
|
||||
*(window.need_frame_refresh.lock().unwrap()) = true;
|
||||
return;
|
||||
@@ -107,8 +126,8 @@ impl Window {
|
||||
frame.set_decorate(attributes.decorations);
|
||||
|
||||
// min-max dimensions
|
||||
frame.set_min_size(attributes.min_dimensions);
|
||||
frame.set_max_size(attributes.max_dimensions);
|
||||
frame.set_min_size(attributes.min_dimensions.map(Into::into));
|
||||
frame.set_max_size(attributes.max_dimensions.map(Into::into));
|
||||
|
||||
let kill_switch = Arc::new(Mutex::new(false));
|
||||
let need_frame_refresh = Arc::new(Mutex::new(true));
|
||||
@@ -117,11 +136,14 @@ impl Window {
|
||||
evlp.store.lock().unwrap().windows.push(InternalWindow {
|
||||
closed: false,
|
||||
newsize: None,
|
||||
size: size.clone(),
|
||||
need_refresh: false,
|
||||
need_frame_refresh: need_frame_refresh.clone(),
|
||||
surface: surface.clone(),
|
||||
kill_switch: kill_switch.clone(),
|
||||
frame: Arc::downgrade(&frame),
|
||||
current_dpi: 1,
|
||||
new_dpi: None,
|
||||
});
|
||||
evlp.evq.borrow_mut().sync_roundtrip().unwrap();
|
||||
|
||||
@@ -130,6 +152,7 @@ impl Window {
|
||||
surface: surface,
|
||||
frame: frame,
|
||||
monitors: monitor_list,
|
||||
outputs: evlp.env.outputs.clone(),
|
||||
size: size,
|
||||
kill_switch: (kill_switch, evlp.cleanup_needed.clone()),
|
||||
need_frame_refresh: need_frame_refresh,
|
||||
@@ -156,48 +179,49 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||
// Not possible with wayland
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||
// Not possible with wayland
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_position(&self, _x: i32, _y: i32) {
|
||||
pub fn set_position(&self, _pos: LogicalPosition) {
|
||||
// Not possible with wayland
|
||||
}
|
||||
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
Some(self.size.lock().unwrap().clone())
|
||||
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||
Some(self.size.lock().unwrap().clone().into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||
let (w, h) = self.size.lock().unwrap().clone();
|
||||
// let (w, h) = super::wayland_window::add_borders(w as i32, h as i32);
|
||||
Some((w as u32, h as u32))
|
||||
Some((w, h).into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
// NOTE: This will only resize the borders, the contents must be updated by the user
|
||||
pub fn set_inner_size(&self, x: u32, y: u32) {
|
||||
self.frame.lock().unwrap().resize(x, y);
|
||||
*(self.size.lock().unwrap()) = (x, y);
|
||||
pub fn set_inner_size(&self, size: LogicalSize) {
|
||||
let (w, h) = size.into();
|
||||
self.frame.lock().unwrap().resize(w, h);
|
||||
*(self.size.lock().unwrap()) = (w, h);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
self.frame.lock().unwrap().set_min_size(dimensions);
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||
self.frame.lock().unwrap().set_min_size(dimensions.map(Into::into));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
self.frame.lock().unwrap().set_max_size(dimensions);
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||
self.frame.lock().unwrap().set_max_size(dimensions.map(Into::into));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -206,30 +230,8 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _cursor: MouseCursor) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
use CursorState::{Grab, Hide, Normal};
|
||||
// TODO : not yet possible on wayland to grab cursor
|
||||
match state {
|
||||
Grab => Err("Cursor cannot be grabbed on wayland yet.".to_string()),
|
||||
Hide => Err("Cursor cannot be hidden on wayland yet.".to_string()),
|
||||
Normal => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
let mut factor: f32 = 1.0;
|
||||
let guard = self.monitors.lock().unwrap();
|
||||
for monitor_id in guard.iter() {
|
||||
let hidpif = monitor_id.get_hidpi_factor();
|
||||
factor = factor.max(hidpif);
|
||||
}
|
||||
factor
|
||||
pub fn hidpi_factor(&self) -> i32 {
|
||||
self.monitors.lock().unwrap().compute_hidpi_factor()
|
||||
}
|
||||
|
||||
pub fn set_decorations(&self, decorate: bool) {
|
||||
@@ -260,9 +262,23 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
|
||||
// TODO: not yet possible on wayland
|
||||
Err(())
|
||||
pub fn set_cursor(&self, _cursor: MouseCursor) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide_cursor(&self, _hide: bool) {
|
||||
// TODO: This isn't possible on Wayland yet
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn grab_cursor(&self, _grab: bool) -> Result<(), String> {
|
||||
Err("Cursor grabbing is not yet possible on Wayland.".to_owned())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, _pos: LogicalPosition) -> Result<(), String> {
|
||||
Err("Setting the cursor position is not yet possible on Wayland.".to_owned())
|
||||
}
|
||||
|
||||
pub fn get_display(&self) -> &Display {
|
||||
@@ -277,7 +293,15 @@ impl Window {
|
||||
// we don't know how much each monitor sees us so...
|
||||
// just return the most recent one ?
|
||||
let guard = self.monitors.lock().unwrap();
|
||||
guard.last().unwrap().clone()
|
||||
guard.monitors.last().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
get_available_monitors(&self.outputs)
|
||||
}
|
||||
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
get_primary_monitor(&self.outputs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,12 +318,15 @@ impl Drop for Window {
|
||||
|
||||
struct InternalWindow {
|
||||
surface: Proxy<wl_surface::WlSurface>,
|
||||
newsize: Option<(i32, i32)>,
|
||||
newsize: Option<(u32, u32)>,
|
||||
size: Arc<Mutex<(u32, u32)>>,
|
||||
need_refresh: bool,
|
||||
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>
|
||||
}
|
||||
|
||||
pub struct WindowStore {
|
||||
@@ -345,24 +372,84 @@ impl WindowStore {
|
||||
}
|
||||
}
|
||||
|
||||
fn dpi_change(&mut self, surface: &Proxy<wl_surface::WlSurface>, new: i32) {
|
||||
for window in &mut self.windows {
|
||||
if surface.equals(&window.surface) {
|
||||
window.new_dpi = Some(new);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_each<F>(&mut self, mut f: F)
|
||||
where
|
||||
F: FnMut(Option<(i32, 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();
|
||||
let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap());
|
||||
f(
|
||||
window.newsize.take(),
|
||||
&mut *(window.size.lock().unwrap()),
|
||||
window.new_dpi,
|
||||
window.need_refresh,
|
||||
::std::mem::replace(&mut *window.need_frame_refresh.lock().unwrap(), false),
|
||||
window.closed,
|
||||
make_wid(&window.surface),
|
||||
opt_mutex_lock.as_mut().map(|m| &mut **m),
|
||||
);
|
||||
if let Some(dpi) = window.new_dpi.take() {
|
||||
window.current_dpi = dpi;
|
||||
}
|
||||
window.need_refresh = false;
|
||||
// avoid re-spamming the event
|
||||
window.closed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Monitor list with some covenience method to compute DPI
|
||||
*/
|
||||
|
||||
struct MonitorList {
|
||||
monitors: Vec<MonitorId>
|
||||
}
|
||||
|
||||
impl MonitorList {
|
||||
fn new() -> MonitorList {
|
||||
MonitorList {
|
||||
monitors: Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_hidpi_factor(&self) -> i32 {
|
||||
let mut factor = 1;
|
||||
for monitor_id in &self.monitors {
|
||||
let monitor_dpi = monitor_id.get_hidpi_factor();
|
||||
if monitor_dpi > factor { factor = monitor_dpi; }
|
||||
}
|
||||
factor
|
||||
}
|
||||
|
||||
fn add_output(&mut self, monitor: MonitorId) -> Option<i32> {
|
||||
let old_dpi = self.compute_hidpi_factor();
|
||||
let monitor_dpi = monitor.get_hidpi_factor();
|
||||
self.monitors.push(monitor);
|
||||
if monitor_dpi > old_dpi {
|
||||
Some(monitor_dpi)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn del_output(&mut self, output: &Proxy<wl_output::WlOutput>) -> Option<i32> {
|
||||
let old_dpi = self.compute_hidpi_factor();
|
||||
self.monitors.retain(|m| !m.proxy.equals(output));
|
||||
let new_dpi = self.compute_hidpi_factor();
|
||||
if new_dpi != old_dpi {
|
||||
Some(new_dpi)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -9,13 +9,7 @@ mod dnd;
|
||||
mod ime;
|
||||
pub mod util;
|
||||
|
||||
pub use self::monitor::{
|
||||
MonitorId,
|
||||
get_available_monitors,
|
||||
get_monitor_for_window,
|
||||
get_primary_monitor,
|
||||
invalidate_cached_monitor_list,
|
||||
};
|
||||
pub use self::monitor::MonitorId;
|
||||
pub use self::window::UnownedWindow;
|
||||
pub use self::xdisplay::{XConnection, XNotSupported, XError};
|
||||
|
||||
@@ -29,7 +23,6 @@ use std::sync::{Arc, mpsc, Weak};
|
||||
use std::sync::atomic::{self, AtomicBool};
|
||||
|
||||
use libc::{self, setlocale, LC_CTYPE};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use {
|
||||
ControlFlow,
|
||||
@@ -38,6 +31,8 @@ use {
|
||||
Event,
|
||||
EventsLoopClosed,
|
||||
KeyboardInput,
|
||||
LogicalPosition,
|
||||
LogicalSize,
|
||||
WindowAttributes,
|
||||
WindowEvent,
|
||||
};
|
||||
@@ -92,7 +87,7 @@ impl EventsLoop {
|
||||
result.expect("Failed to set input method destruction callback")
|
||||
});
|
||||
|
||||
let randr_event_offset = monitor::select_input(&xconn, root)
|
||||
let randr_event_offset = xconn.select_xrandr_input(root)
|
||||
.expect("Failed to query XRandR extension");
|
||||
|
||||
let xi2ext = unsafe {
|
||||
@@ -283,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.
|
||||
@@ -323,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 {
|
||||
@@ -348,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 {
|
||||
@@ -394,6 +393,13 @@ impl EventsLoop {
|
||||
}
|
||||
|
||||
ffi::ConfigureNotify => {
|
||||
#[derive(Debug, Default)]
|
||||
struct Events {
|
||||
resized: Option<WindowEvent>,
|
||||
moved: Option<WindowEvent>,
|
||||
dpi_changed: Option<WindowEvent>,
|
||||
}
|
||||
|
||||
let xev: &ffi::XConfigureEvent = xev.as_ref();
|
||||
let xwindow = xev.window;
|
||||
let events = self.with_window(xwindow, |window| {
|
||||
@@ -406,12 +412,14 @@ impl EventsLoop {
|
||||
// that has a position relative to the parent window.
|
||||
let is_synthetic = xev.send_event == ffi::True;
|
||||
|
||||
// These are both in physical space.
|
||||
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 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)
|
||||
@@ -431,14 +439,9 @@ impl EventsLoop {
|
||||
(resized, moved)
|
||||
};
|
||||
|
||||
let capacity = resized as usize + moved as usize;
|
||||
let mut events = Vec::with_capacity(capacity);
|
||||
let mut events = Events::default();
|
||||
|
||||
if resized {
|
||||
events.push(WindowEvent::Resized(new_inner_size.0, new_inner_size.1));
|
||||
}
|
||||
|
||||
if moved || shared_state_lock.position.is_none() {
|
||||
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
|
||||
.as_ref()
|
||||
@@ -451,19 +454,87 @@ impl EventsLoop {
|
||||
let outer = frame_extents.inner_pos_to_outer(new_inner_position.0, new_inner_position.1);
|
||||
shared_state_lock.position = Some(outer);
|
||||
if moved {
|
||||
events.push(WindowEvent::Moved(outer.0, outer.1));
|
||||
let logical_position = LogicalPosition::from_physical(outer, monitor.hidpi_factor);
|
||||
events.moved = Some(WindowEvent::Moved(logical_position));
|
||||
}
|
||||
outer
|
||||
} else {
|
||||
shared_state_lock.position.unwrap()
|
||||
};
|
||||
|
||||
if is_synthetic {
|
||||
// If we don't use the existing adjusted value when available, then the user can screw up the
|
||||
// resizing by dragging across monitors *without* dropping the window.
|
||||
let (width, height) = shared_state_lock.dpi_adjusted
|
||||
.unwrap_or_else(|| (xev.width as f64, xev.height as f64));
|
||||
let last_hidpi_factor = shared_state_lock.guessed_dpi
|
||||
.take()
|
||||
.unwrap_or_else(|| {
|
||||
shared_state_lock.last_monitor
|
||||
.as_ref()
|
||||
.map(|last_monitor| last_monitor.hidpi_factor)
|
||||
.unwrap_or(1.0)
|
||||
});
|
||||
let new_hidpi_factor = {
|
||||
let window_rect = util::AaRect::new(new_outer_position, new_inner_size);
|
||||
monitor = self.xconn.get_monitor_for_window(Some(window_rect));
|
||||
let new_hidpi_factor = monitor.hidpi_factor;
|
||||
shared_state_lock.last_monitor = Some(monitor.clone());
|
||||
new_hidpi_factor
|
||||
};
|
||||
if last_hidpi_factor != new_hidpi_factor {
|
||||
events.dpi_changed = Some(WindowEvent::HiDpiFactorChanged(new_hidpi_factor));
|
||||
let (new_width, new_height, flusher) = window.adjust_for_dpi(
|
||||
last_hidpi_factor,
|
||||
new_hidpi_factor,
|
||||
width,
|
||||
height,
|
||||
);
|
||||
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 {
|
||||
for event in events {
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(xwindow),
|
||||
event,
|
||||
});
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -529,7 +600,7 @@ impl EventsLoop {
|
||||
|
||||
// Standard virtual core keyboard ID. XInput2 needs to be used to get a reliable
|
||||
// value, though this should only be an issue under multiseat configurations.
|
||||
let device = 3;
|
||||
let device = util::VIRTUAL_CORE_KEYBOARD;
|
||||
let device_id = mkdid(device);
|
||||
|
||||
// When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with
|
||||
@@ -694,14 +765,25 @@ impl EventsLoop {
|
||||
util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
|
||||
});
|
||||
if cursor_moved == Some(true) {
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: CursorMoved {
|
||||
device_id,
|
||||
position: new_cursor_pos,
|
||||
modifiers,
|
||||
},
|
||||
let dpi_factor = self.with_window(xev.event, |window| {
|
||||
window.get_hidpi_factor()
|
||||
});
|
||||
if let Some(dpi_factor) = dpi_factor {
|
||||
let position = LogicalPosition::from_physical(
|
||||
(xev.event_x as f64, xev.event_y as f64),
|
||||
dpi_factor,
|
||||
);
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: CursorMoved {
|
||||
device_id,
|
||||
position,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if cursor_moved.is_none() {
|
||||
return;
|
||||
}
|
||||
@@ -782,19 +864,29 @@ impl EventsLoop {
|
||||
event: CursorEntered { device_id },
|
||||
});
|
||||
|
||||
let new_cursor_pos = (xev.event_x, xev.event_y);
|
||||
|
||||
// The mods field on this event isn't actually populated, so query the
|
||||
// pointer device. In the future, we can likely remove this round-trip by
|
||||
// relying on Xkb for modifier values.
|
||||
let modifiers = self.xconn.query_pointer(xev.event, xev.deviceid)
|
||||
.expect("Failed to query pointer device").get_modifier_state();
|
||||
|
||||
callback(Event::WindowEvent { window_id, event: CursorMoved {
|
||||
device_id,
|
||||
position: new_cursor_pos,
|
||||
modifiers,
|
||||
}})
|
||||
let dpi_factor = self.with_window(xev.event, |window| {
|
||||
window.get_hidpi_factor()
|
||||
});
|
||||
if let Some(dpi_factor) = dpi_factor {
|
||||
let position = LogicalPosition::from_physical(
|
||||
(xev.event_x as f64, xev.event_y as f64),
|
||||
dpi_factor,
|
||||
);
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: CursorMoved {
|
||||
device_id,
|
||||
position,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
ffi::XI_Leave => {
|
||||
let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) };
|
||||
@@ -812,7 +904,12 @@ impl EventsLoop {
|
||||
ffi::XI_FocusIn => {
|
||||
let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
|
||||
|
||||
if !self.window_exists(xev.event) { return; }
|
||||
let dpi_factor = match self.with_window(xev.event, |window| {
|
||||
window.get_hidpi_factor()
|
||||
}) {
|
||||
Some(dpi_factor) => dpi_factor,
|
||||
None => return,
|
||||
};
|
||||
let window_id = mkwid(xev.event);
|
||||
|
||||
self.ime
|
||||
@@ -830,11 +927,15 @@ impl EventsLoop {
|
||||
.map(|device| device.attachment)
|
||||
.unwrap_or(2);
|
||||
|
||||
let position = LogicalPosition::from_physical(
|
||||
(xev.event_x as f64, xev.event_y as f64),
|
||||
dpi_factor,
|
||||
);
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: CursorMoved {
|
||||
device_id: mkdid(pointer_id),
|
||||
position: (xev.event_x, xev.event_y),
|
||||
position,
|
||||
modifiers: ModifiersState::from(xev.mods),
|
||||
}
|
||||
});
|
||||
@@ -861,15 +962,24 @@ impl EventsLoop {
|
||||
ffi::XI_TouchEnd => TouchPhase::Ended,
|
||||
_ => unreachable!()
|
||||
};
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Touch(Touch {
|
||||
device_id: mkdid(xev.deviceid),
|
||||
phase,
|
||||
location: (xev.event_x, xev.event_y),
|
||||
id: xev.detail as u64,
|
||||
},
|
||||
)})
|
||||
let dpi_factor = self.with_window(xev.event, |window| {
|
||||
window.get_hidpi_factor()
|
||||
});
|
||||
if let Some(dpi_factor) = dpi_factor {
|
||||
let location = LogicalPosition::from_physical(
|
||||
(xev.event_x as f64, xev.event_y as f64),
|
||||
dpi_factor,
|
||||
);
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Touch(Touch {
|
||||
device_id: mkdid(xev.deviceid),
|
||||
phase,
|
||||
location,
|
||||
id: xev.detail as u64,
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => {
|
||||
@@ -986,7 +1096,44 @@ impl EventsLoop {
|
||||
_ => {
|
||||
if event_type == self.randr_event_offset {
|
||||
// In the future, it would be quite easy to emit monitor hotplug events.
|
||||
monitor::invalidate_cached_monitor_list();
|
||||
let prev_list = monitor::invalidate_cached_monitor_list();
|
||||
if let Some(prev_list) = prev_list {
|
||||
let new_list = self.xconn.get_available_monitors();
|
||||
for new_monitor in new_list {
|
||||
prev_list
|
||||
.iter()
|
||||
.find(|prev_monitor| prev_monitor.name == new_monitor.name)
|
||||
.map(|prev_monitor| {
|
||||
if new_monitor.hidpi_factor != prev_monitor.hidpi_factor {
|
||||
for (window_id, window) in self.windows.borrow().iter() {
|
||||
if let Some(window) = window.upgrade() {
|
||||
// Check if the window is on this monitor
|
||||
let monitor = window.get_current_monitor();
|
||||
if monitor.name == new_monitor.name {
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(window_id.0),
|
||||
event: WindowEvent::HiDpiFactorChanged(
|
||||
new_monitor.hidpi_factor
|
||||
),
|
||||
});
|
||||
let (width, height) = match window.get_inner_size_physical() {
|
||||
Some(result) => result,
|
||||
None => continue,
|
||||
};
|
||||
let (_, _, flusher) = window.adjust_for_dpi(
|
||||
prev_monitor.hidpi_factor,
|
||||
new_monitor.hidpi_factor,
|
||||
width as f64,
|
||||
height as f64,
|
||||
);
|
||||
flusher.queue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1110,16 +1257,13 @@ pub struct WindowId(ffi::Window);
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId(c_int);
|
||||
|
||||
pub struct Window {
|
||||
pub window: Arc<UnownedWindow>,
|
||||
ime_sender: Mutex<ImeSender>,
|
||||
}
|
||||
pub struct Window(Arc<UnownedWindow>);
|
||||
|
||||
impl Deref for Window {
|
||||
type Target = UnownedWindow;
|
||||
#[inline]
|
||||
fn deref(&self) -> &UnownedWindow {
|
||||
&*self.window
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1130,35 +1274,19 @@ impl Window {
|
||||
pl_attribs: PlatformSpecificWindowBuilderAttributes
|
||||
) -> Result<Self, CreationError> {
|
||||
let window = Arc::new(UnownedWindow::new(&event_loop, attribs, pl_attribs)?);
|
||||
|
||||
event_loop.windows
|
||||
.borrow_mut()
|
||||
.insert(window.id(), Arc::downgrade(&window));
|
||||
|
||||
event_loop.ime
|
||||
.borrow_mut()
|
||||
.create_context(window.id().0)
|
||||
.expect("Failed to create input context");
|
||||
|
||||
Ok(Window {
|
||||
window,
|
||||
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn send_xim_spot(&self, x: i16, y: i16) {
|
||||
let _ = self.ime_sender
|
||||
.lock()
|
||||
.send((self.window.id().0, x, y));
|
||||
Ok(Window(window))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
let xconn = &self.window.xconn;
|
||||
let window = self.deref();
|
||||
let xconn = &window.xconn;
|
||||
unsafe {
|
||||
(xconn.xlib.XDestroyWindow)(xconn.display, self.window.id().0);
|
||||
(xconn.xlib.XDestroyWindow)(xconn.display, window.id().0);
|
||||
// If the window was somehow already destroyed, we'll get a `BadWindow` error, which we don't care about.
|
||||
let _ = xconn.check_errors();
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::os::raw::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use {PhysicalPosition, PhysicalSize};
|
||||
use super::{util, XConnection, XError};
|
||||
use super::ffi::{
|
||||
RRCrtcChangeNotifyMask,
|
||||
RROutputPropertyNotifyMask,
|
||||
@@ -11,7 +12,6 @@ use super::ffi::{
|
||||
Window,
|
||||
XRRScreenResources,
|
||||
};
|
||||
use super::{util, XConnection, XError};
|
||||
|
||||
// Used to test XRandR < 1.5 code path. This should always be committed as false.
|
||||
const FORCE_RANDR_COMPAT: bool = false;
|
||||
@@ -45,7 +45,7 @@ pub struct MonitorId {
|
||||
/// The actual id
|
||||
id: u32,
|
||||
/// The name of the monitor
|
||||
name: String,
|
||||
pub(crate) name: String,
|
||||
/// The size of the monitor
|
||||
dimensions: (u32, u32),
|
||||
/// The position of the monitor in the X screen
|
||||
@@ -53,14 +53,14 @@ pub struct MonitorId {
|
||||
/// If the monitor is the primary one
|
||||
primary: bool,
|
||||
/// The DPI scale factor
|
||||
pub(crate) hidpi_factor: f32,
|
||||
pub(crate) hidpi_factor: f64,
|
||||
/// Used to determine which windows are on this monitor
|
||||
pub(crate) rect: util::Rect,
|
||||
pub(crate) rect: util::AaRect,
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
fn from_repr(
|
||||
xconn: &Arc<XConnection>,
|
||||
xconn: &XConnection,
|
||||
resources: *mut XRRScreenResources,
|
||||
id: u32,
|
||||
repr: util::MonitorRepr,
|
||||
@@ -68,7 +68,7 @@ impl MonitorId {
|
||||
) -> Self {
|
||||
let (name, hidpi_factor) = unsafe { xconn.get_output_info(resources, &repr) };
|
||||
let (dimensions, position) = unsafe { (repr.get_dimensions(), repr.get_position()) };
|
||||
let rect = util::Rect::new(position, dimensions);
|
||||
let rect = util::AaRect::new(position, dimensions);
|
||||
MonitorId {
|
||||
id,
|
||||
name,
|
||||
@@ -89,182 +89,181 @@ impl MonitorId {
|
||||
self.id as u32
|
||||
}
|
||||
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
self.dimensions
|
||||
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||
self.dimensions.into()
|
||||
}
|
||||
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
self.position
|
||||
pub fn get_position(&self) -> PhysicalPosition {
|
||||
self.position.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
self.hidpi_factor
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_monitor_for_window(
|
||||
xconn: &Arc<XConnection>,
|
||||
window_rect: Option<util::Rect>,
|
||||
) -> MonitorId {
|
||||
let monitors = get_available_monitors(xconn);
|
||||
let default = monitors
|
||||
.get(0)
|
||||
.expect("[winit] Failed to find any monitors using XRandR.");
|
||||
impl XConnection {
|
||||
pub fn get_monitor_for_window(&self, window_rect: Option<util::AaRect>) -> MonitorId {
|
||||
let monitors = self.get_available_monitors();
|
||||
let default = monitors
|
||||
.get(0)
|
||||
.expect("[winit] Failed to find any monitors using XRandR.");
|
||||
|
||||
let window_rect = match window_rect {
|
||||
Some(rect) => rect,
|
||||
None => return default.to_owned(),
|
||||
};
|
||||
let window_rect = match window_rect {
|
||||
Some(rect) => rect,
|
||||
None => return default.to_owned(),
|
||||
};
|
||||
|
||||
let mut largest_overlap = 0;
|
||||
let mut matched_monitor = default;
|
||||
for monitor in &monitors {
|
||||
let overlapping_area = window_rect.get_overlapping_area(&monitor.rect);
|
||||
if overlapping_area > largest_overlap {
|
||||
largest_overlap = overlapping_area;
|
||||
matched_monitor = &monitor;
|
||||
let mut largest_overlap = 0;
|
||||
let mut matched_monitor = default;
|
||||
for monitor in &monitors {
|
||||
let overlapping_area = window_rect.get_overlapping_area(&monitor.rect);
|
||||
if overlapping_area > largest_overlap {
|
||||
largest_overlap = overlapping_area;
|
||||
matched_monitor = &monitor;
|
||||
}
|
||||
}
|
||||
|
||||
matched_monitor.to_owned()
|
||||
}
|
||||
|
||||
matched_monitor.to_owned()
|
||||
}
|
||||
|
||||
fn query_monitor_list(xconn: &Arc<XConnection>) -> Vec<MonitorId> {
|
||||
unsafe {
|
||||
let root = (xconn.xlib.XDefaultRootWindow)(xconn.display);
|
||||
// WARNING: this function is supposedly very slow, on the order of hundreds of ms.
|
||||
// Upon failure, `resources` will be null.
|
||||
let resources = (xconn.xrandr.XRRGetScreenResources)(xconn.display, root);
|
||||
if resources.is_null() {
|
||||
panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist.");
|
||||
}
|
||||
|
||||
let mut available;
|
||||
let mut has_primary = false;
|
||||
|
||||
if xconn.xrandr_1_5.is_some() && version_is_at_least(1, 5) && !FORCE_RANDR_COMPAT {
|
||||
// We're in XRandR >= 1.5, enumerate monitors. This supports things like MST and
|
||||
// videowalls.
|
||||
let xrandr_1_5 = xconn.xrandr_1_5.as_ref().unwrap();
|
||||
let mut monitor_count = 0;
|
||||
let monitors = (xrandr_1_5.XRRGetMonitors)(xconn.display, root, 1, &mut monitor_count);
|
||||
assert!(monitor_count >= 0);
|
||||
available = Vec::with_capacity(monitor_count as usize);
|
||||
for monitor_index in 0..monitor_count {
|
||||
let monitor = monitors.offset(monitor_index as isize);
|
||||
let is_primary = (*monitor).primary != 0;
|
||||
has_primary |= is_primary;
|
||||
available.push(MonitorId::from_repr(
|
||||
xconn,
|
||||
resources,
|
||||
monitor_index as u32,
|
||||
monitor.into(),
|
||||
is_primary,
|
||||
));
|
||||
fn query_monitor_list(&self) -> Vec<MonitorId> {
|
||||
unsafe {
|
||||
let root = (self.xlib.XDefaultRootWindow)(self.display);
|
||||
// WARNING: this function is supposedly very slow, on the order of hundreds of ms.
|
||||
// Upon failure, `resources` will be null.
|
||||
let resources = (self.xrandr.XRRGetScreenResources)(self.display, root);
|
||||
if resources.is_null() {
|
||||
panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist.");
|
||||
}
|
||||
(xrandr_1_5.XRRFreeMonitors)(monitors);
|
||||
} else {
|
||||
// We're in XRandR < 1.5, enumerate CRTCs. Everything will work except MST and
|
||||
// videowall setups will also show monitors that aren't in the logical groups the user
|
||||
// cares about.
|
||||
let primary = (xconn.xrandr.XRRGetOutputPrimary)(xconn.display, root);
|
||||
available = Vec::with_capacity((*resources).ncrtc as usize);
|
||||
for crtc_index in 0..(*resources).ncrtc {
|
||||
let crtc_id = *((*resources).crtcs.offset(crtc_index as isize));
|
||||
let crtc = (xconn.xrandr.XRRGetCrtcInfo)(xconn.display, resources, crtc_id);
|
||||
let is_active = (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0;
|
||||
if is_active {
|
||||
let crtc = util::MonitorRepr::from(crtc);
|
||||
let is_primary = crtc.get_output() == primary;
|
||||
|
||||
let mut available;
|
||||
let mut has_primary = false;
|
||||
|
||||
if self.xrandr_1_5.is_some() && version_is_at_least(1, 5) && !FORCE_RANDR_COMPAT {
|
||||
// We're in XRandR >= 1.5, enumerate monitors. This supports things like MST and
|
||||
// videowalls.
|
||||
let xrandr_1_5 = self.xrandr_1_5.as_ref().unwrap();
|
||||
let mut monitor_count = 0;
|
||||
let monitors = (xrandr_1_5.XRRGetMonitors)(self.display, root, 1, &mut monitor_count);
|
||||
assert!(monitor_count >= 0);
|
||||
available = Vec::with_capacity(monitor_count as usize);
|
||||
for monitor_index in 0..monitor_count {
|
||||
let monitor = monitors.offset(monitor_index as isize);
|
||||
let is_primary = (*monitor).primary != 0;
|
||||
has_primary |= is_primary;
|
||||
available.push(MonitorId::from_repr(
|
||||
xconn,
|
||||
self,
|
||||
resources,
|
||||
crtc_id as u32,
|
||||
crtc,
|
||||
monitor_index as u32,
|
||||
monitor.into(),
|
||||
is_primary,
|
||||
));
|
||||
}
|
||||
(xconn.xrandr.XRRFreeCrtcInfo)(crtc);
|
||||
(xrandr_1_5.XRRFreeMonitors)(monitors);
|
||||
} else {
|
||||
// We're in XRandR < 1.5, enumerate CRTCs. Everything will work except MST and
|
||||
// videowall setups will also show monitors that aren't in the logical groups the user
|
||||
// cares about.
|
||||
let primary = (self.xrandr.XRRGetOutputPrimary)(self.display, root);
|
||||
available = Vec::with_capacity((*resources).ncrtc as usize);
|
||||
for crtc_index in 0..(*resources).ncrtc {
|
||||
let crtc_id = *((*resources).crtcs.offset(crtc_index as isize));
|
||||
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
|
||||
let is_active = (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0;
|
||||
if is_active {
|
||||
let crtc = util::MonitorRepr::from(crtc);
|
||||
let is_primary = crtc.get_output() == primary;
|
||||
has_primary |= is_primary;
|
||||
available.push(MonitorId::from_repr(
|
||||
self,
|
||||
resources,
|
||||
crtc_id as u32,
|
||||
crtc,
|
||||
is_primary,
|
||||
));
|
||||
}
|
||||
(self.xrandr.XRRFreeCrtcInfo)(crtc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no monitors were detected as being primary, we just pick one ourselves!
|
||||
if !has_primary {
|
||||
if let Some(ref mut fallback) = available.first_mut() {
|
||||
// Setting this here will come in handy if we ever add an `is_primary` method.
|
||||
fallback.primary = true;
|
||||
// If no monitors were detected as being primary, we just pick one ourselves!
|
||||
if !has_primary {
|
||||
if let Some(ref mut fallback) = available.first_mut() {
|
||||
// Setting this here will come in handy if we ever add an `is_primary` method.
|
||||
fallback.primary = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(xconn.xrandr.XRRFreeScreenResources)(resources);
|
||||
available
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_available_monitors(xconn: &Arc<XConnection>) -> Vec<MonitorId> {
|
||||
let mut monitors_lock = MONITORS.lock();
|
||||
(*monitors_lock)
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.or_else(|| {
|
||||
let monitors = Some(query_monitor_list(xconn));
|
||||
if !DISABLE_MONITOR_LIST_CACHING {
|
||||
(*monitors_lock) = monitors.clone();
|
||||
}
|
||||
monitors
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(xconn: &Arc<XConnection>) -> MonitorId {
|
||||
get_available_monitors(xconn)
|
||||
.into_iter()
|
||||
.find(|monitor| monitor.primary)
|
||||
.expect("[winit] Failed to find any monitors using XRandR.")
|
||||
}
|
||||
|
||||
pub fn select_input(xconn: &Arc<XConnection>, root: Window) -> Result<c_int, XError> {
|
||||
{
|
||||
let mut version_lock = XRANDR_VERSION.lock();
|
||||
if version_lock.is_none() {
|
||||
let mut major = 0;
|
||||
let mut minor = 0;
|
||||
let has_extension = unsafe {
|
||||
(xconn.xrandr.XRRQueryVersion)(
|
||||
xconn.display,
|
||||
&mut major,
|
||||
&mut minor,
|
||||
)
|
||||
};
|
||||
if has_extension != True {
|
||||
panic!("[winit] XRandR extension not available.");
|
||||
}
|
||||
*version_lock = Some((major, minor));
|
||||
(self.xrandr.XRRFreeScreenResources)(resources);
|
||||
available
|
||||
}
|
||||
}
|
||||
|
||||
let mut event_offset = 0;
|
||||
let mut error_offset = 0;
|
||||
let status = unsafe {
|
||||
(xconn.xrandr.XRRQueryExtension)(
|
||||
xconn.display,
|
||||
&mut event_offset,
|
||||
&mut error_offset,
|
||||
)
|
||||
};
|
||||
|
||||
if status != True {
|
||||
xconn.check_errors()?;
|
||||
unreachable!("[winit] `XRRQueryExtension` failed but no error was received.");
|
||||
pub fn get_available_monitors(&self) -> Vec<MonitorId> {
|
||||
let mut monitors_lock = MONITORS.lock();
|
||||
(*monitors_lock)
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.or_else(|| {
|
||||
let monitors = Some(self.query_monitor_list());
|
||||
if !DISABLE_MONITOR_LIST_CACHING {
|
||||
(*monitors_lock) = monitors.clone();
|
||||
}
|
||||
monitors
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
let mask = RRCrtcChangeNotifyMask
|
||||
| RROutputPropertyNotifyMask
|
||||
| RRScreenChangeNotifyMask;
|
||||
unsafe { (xconn.xrandr.XRRSelectInput)(xconn.display, root, mask) };
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
self.get_available_monitors()
|
||||
.into_iter()
|
||||
.find(|monitor| monitor.primary)
|
||||
.expect("[winit] Failed to find any monitors using XRandR.")
|
||||
}
|
||||
|
||||
Ok(event_offset)
|
||||
pub fn select_xrandr_input(&self, root: Window) -> Result<c_int, XError> {
|
||||
{
|
||||
let mut version_lock = XRANDR_VERSION.lock();
|
||||
if version_lock.is_none() {
|
||||
let mut major = 0;
|
||||
let mut minor = 0;
|
||||
let has_extension = unsafe {
|
||||
(self.xrandr.XRRQueryVersion)(
|
||||
self.display,
|
||||
&mut major,
|
||||
&mut minor,
|
||||
)
|
||||
};
|
||||
if has_extension != True {
|
||||
panic!("[winit] XRandR extension not available.");
|
||||
}
|
||||
*version_lock = Some((major, minor));
|
||||
}
|
||||
}
|
||||
|
||||
let mut event_offset = 0;
|
||||
let mut error_offset = 0;
|
||||
let status = unsafe {
|
||||
(self.xrandr.XRRQueryExtension)(
|
||||
self.display,
|
||||
&mut event_offset,
|
||||
&mut error_offset,
|
||||
)
|
||||
};
|
||||
|
||||
if status != True {
|
||||
self.check_errors()?;
|
||||
unreachable!("[winit] `XRRQueryExtension` failed but no error was received.");
|
||||
}
|
||||
|
||||
let mask = RRCrtcChangeNotifyMask
|
||||
| RROutputPropertyNotifyMask
|
||||
| RRScreenChangeNotifyMask;
|
||||
unsafe { (self.xrandr.XRRSelectInput)(self.display, root, mask) };
|
||||
|
||||
Ok(event_offset)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ pub trait Formattable: Debug + Clone + Copy + PartialEq + PartialOrd {
|
||||
}
|
||||
|
||||
// You might be surprised by the absence of c_int, but not as surprised as X11 would be by the presence of it.
|
||||
impl Formattable for c_char { const FORMAT: Format = Format::Char; }
|
||||
impl Formattable for c_schar { const FORMAT: Format = Format::Char; }
|
||||
impl Formattable for c_uchar { const FORMAT: Format = Format::Char; }
|
||||
impl Formattable for c_short { const FORMAT: Format = Format::Short; }
|
||||
impl Formattable for c_ushort { const FORMAT: Format = Format::Short; }
|
||||
|
||||
@@ -1,35 +1,36 @@
|
||||
use std::cmp;
|
||||
|
||||
use super::*;
|
||||
use {LogicalPosition, LogicalSize};
|
||||
|
||||
// Friendly neighborhood axis-aligned rectangle
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Rect {
|
||||
left: i64,
|
||||
right: i64,
|
||||
top: i64,
|
||||
bottom: i64,
|
||||
pub struct AaRect {
|
||||
x: i64,
|
||||
y: i64,
|
||||
width: i64,
|
||||
height: i64,
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
impl AaRect {
|
||||
pub fn new((x, y): (i32, i32), (width, height): (u32, u32)) -> Self {
|
||||
let (x, y) = (x as i64, y as i64);
|
||||
let (width, height) = (width as i64, height as i64);
|
||||
Rect {
|
||||
left: x,
|
||||
right: x + width,
|
||||
top: y,
|
||||
bottom: y + height,
|
||||
}
|
||||
AaRect { x, y, width, height }
|
||||
}
|
||||
|
||||
pub fn contains_point(&self, x: i64, y: i64) -> bool {
|
||||
x >= self.x && x <= self.x + self.width && y >= self.y && y <= self.y + self.height
|
||||
}
|
||||
|
||||
pub fn get_overlapping_area(&self, other: &Self) -> i64 {
|
||||
let x_overlap = cmp::max(
|
||||
0,
|
||||
cmp::min(self.right, other.right) - cmp::max(self.left, other.left),
|
||||
cmp::min(self.x + self.width, other.x + other.width) - cmp::max(self.x, other.x),
|
||||
);
|
||||
let y_overlap = cmp::max(
|
||||
0,
|
||||
cmp::min(self.bottom, other.bottom) - cmp::max(self.top, other.top),
|
||||
cmp::min(self.y + self.height, other.y + other.height) - cmp::max(self.y, other.y),
|
||||
);
|
||||
x_overlap * y_overlap
|
||||
}
|
||||
@@ -78,6 +79,24 @@ impl FrameExtents {
|
||||
pub fn from_border(border: c_ulong) -> Self {
|
||||
Self::new(border, border, border, border)
|
||||
}
|
||||
|
||||
pub fn as_logical(&self, factor: f64) -> LogicalFrameExtents {
|
||||
let logicalize = |value: c_ulong| value as f64 / factor;
|
||||
LogicalFrameExtents {
|
||||
left: logicalize(self.left),
|
||||
right: logicalize(self.right),
|
||||
top: logicalize(self.top),
|
||||
bottom: logicalize(self.bottom),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LogicalFrameExtents {
|
||||
pub left: f64,
|
||||
pub right: f64,
|
||||
pub top: f64,
|
||||
pub bottom: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@@ -103,6 +122,16 @@ impl FrameExtentsHeuristic {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inner_pos_to_outer_logical(&self, mut logical: LogicalPosition, factor: f64) -> LogicalPosition {
|
||||
use self::FrameExtentsHeuristicPath::*;
|
||||
if self.heuristic_path != UnsupportedBordered {
|
||||
let frame_extents = self.frame_extents.as_logical(factor);
|
||||
logical.x -= frame_extents.left;
|
||||
logical.y -= frame_extents.top;
|
||||
}
|
||||
logical
|
||||
}
|
||||
|
||||
pub fn inner_size_to_outer(&self, width: u32, height: u32) -> (u32, u32) {
|
||||
(
|
||||
width.saturating_add(
|
||||
@@ -113,6 +142,13 @@ impl FrameExtentsHeuristic {
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn inner_size_to_outer_logical(&self, mut logical: LogicalSize, factor: f64) -> LogicalSize {
|
||||
let frame_extents = self.frame_extents.as_logical(factor);
|
||||
logical.width += frame_extents.left + frame_extents.right;
|
||||
logical.height += frame_extents.top + frame_extents.bottom;
|
||||
logical
|
||||
}
|
||||
}
|
||||
|
||||
impl XConnection {
|
||||
|
||||
@@ -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,12 +80,111 @@ 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) }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NormalHints<'a> {
|
||||
size_hints: XSmartPointer<'a, ffi::XSizeHints>,
|
||||
}
|
||||
|
||||
impl<'a> NormalHints<'a> {
|
||||
pub fn new(xconn: &'a XConnection) -> Self {
|
||||
NormalHints { size_hints: xconn.alloc_size_hints() }
|
||||
}
|
||||
|
||||
pub fn has_flag(&self, flag: c_long) -> bool {
|
||||
has_flag(self.size_hints.flags, flag)
|
||||
}
|
||||
|
||||
fn getter(&self, flag: c_long, field1: &c_int, field2: &c_int) -> Option<(u32, u32)> {
|
||||
if self.has_flag(flag) {
|
||||
Some((*field1 as _, *field2 as _))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> Option<(u32, u32)> {
|
||||
self.getter(ffi::PSize, &self.size_hints.width, &self.size_hints.height)
|
||||
}
|
||||
|
||||
// WARNING: This hint is obsolete
|
||||
pub fn set_size(&mut self, size: Option<(u32, u32)>) {
|
||||
if let Some((width, height)) = size {
|
||||
self.size_hints.flags |= ffi::PSize;
|
||||
self.size_hints.width = width as c_int;
|
||||
self.size_hints.height = height as c_int;
|
||||
} else {
|
||||
self.size_hints.flags &= !ffi::PSize;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_max_size(&self) -> Option<(u32, u32)> {
|
||||
self.getter(ffi::PMaxSize, &self.size_hints.max_width, &self.size_hints.max_height)
|
||||
}
|
||||
|
||||
pub fn set_max_size(&mut self, max_size: Option<(u32, u32)>) {
|
||||
if let Some((max_width, max_height)) = max_size {
|
||||
self.size_hints.flags |= ffi::PMaxSize;
|
||||
self.size_hints.max_width = max_width as c_int;
|
||||
self.size_hints.max_height = max_height as c_int;
|
||||
} else {
|
||||
self.size_hints.flags &= !ffi::PMaxSize;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_min_size(&self) -> Option<(u32, u32)> {
|
||||
self.getter(ffi::PMinSize, &self.size_hints.min_width, &self.size_hints.min_height)
|
||||
}
|
||||
|
||||
pub fn set_min_size(&mut self, min_size: Option<(u32, u32)>) {
|
||||
if let Some((min_width, min_height)) = min_size {
|
||||
self.size_hints.flags |= ffi::PMinSize;
|
||||
self.size_hints.min_width = min_width as c_int;
|
||||
self.size_hints.min_height = min_height as c_int;
|
||||
} else {
|
||||
self.size_hints.flags &= !ffi::PMinSize;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_resize_increments(&self) -> Option<(u32, u32)> {
|
||||
self.getter(ffi::PResizeInc, &self.size_hints.width_inc, &self.size_hints.height_inc)
|
||||
}
|
||||
|
||||
pub fn set_resize_increments(&mut self, resize_increments: Option<(u32, u32)>) {
|
||||
if let Some((width_inc, height_inc)) = resize_increments {
|
||||
self.size_hints.flags |= ffi::PResizeInc;
|
||||
self.size_hints.width_inc = width_inc as c_int;
|
||||
self.size_hints.height_inc = height_inc as c_int;
|
||||
} else {
|
||||
self.size_hints.flags &= !ffi::PResizeInc;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_base_size(&self) -> Option<(u32, u32)> {
|
||||
self.getter(ffi::PBaseSize, &self.size_hints.base_width, &self.size_hints.base_height)
|
||||
}
|
||||
|
||||
pub fn set_base_size(&mut self, base_size: Option<(u32, u32)>) {
|
||||
if let Some((base_width, base_height)) = base_size {
|
||||
self.size_hints.flags |= ffi::PBaseSize;
|
||||
self.size_hints.base_width = base_width as c_int;
|
||||
self.size_hints.base_height = base_height as c_int;
|
||||
} else {
|
||||
self.size_hints.flags &= !ffi::PBaseSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XConnection {
|
||||
pub fn get_wm_hints(&self, window: ffi::Window) -> Result<XSmartPointer<ffi::XWMHints>, XError> {
|
||||
let wm_hints = unsafe { (self.xlib.XGetWMHints)(self.display, window) };
|
||||
@@ -90,4 +207,29 @@ impl XConnection {
|
||||
}
|
||||
Flusher::new(self)
|
||||
}
|
||||
|
||||
pub fn get_normal_hints(&self, window: ffi::Window) -> Result<NormalHints, XError> {
|
||||
let size_hints = self.alloc_size_hints();
|
||||
let mut supplied_by_user: c_long = unsafe { mem::uninitialized() };
|
||||
unsafe {
|
||||
(self.xlib.XGetWMNormalHints)(
|
||||
self.display,
|
||||
window,
|
||||
size_hints.ptr,
|
||||
&mut supplied_by_user,
|
||||
);
|
||||
}
|
||||
self.check_errors().map(|_| NormalHints { size_hints })
|
||||
}
|
||||
|
||||
pub fn set_normal_hints(&self, window: ffi::Window, normal_hints: NormalHints) -> Flusher {
|
||||
unsafe {
|
||||
(self.xlib.XSetWMNormalHints)(
|
||||
self.display,
|
||||
window,
|
||||
normal_hints.size_hints.ptr,
|
||||
);
|
||||
}
|
||||
Flusher::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ use std::str;
|
||||
use super::*;
|
||||
use events::ModifiersState;
|
||||
|
||||
pub const VIRTUAL_CORE_POINTER: c_int = 2;
|
||||
pub const VIRTUAL_CORE_KEYBOARD: c_int = 3;
|
||||
|
||||
// A base buffer size of 1kB uses a negligible amount of RAM while preventing us from having to
|
||||
// re-allocate (and make another round-trip) in the *vast* majority of cases.
|
||||
// To test if `lookup_utf8` works correctly, set this to 1.
|
||||
@@ -24,8 +27,8 @@ pub struct PointerState<'a> {
|
||||
xconn: &'a XConnection,
|
||||
root: ffi::Window,
|
||||
child: ffi::Window,
|
||||
root_x: c_double,
|
||||
root_y: c_double,
|
||||
pub root_x: c_double,
|
||||
pub root_y: c_double,
|
||||
win_x: c_double,
|
||||
win_y: c_double,
|
||||
buttons: ffi::XIButtonState,
|
||||
|
||||
@@ -27,10 +27,16 @@ pub use self::wm::*;
|
||||
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::ops::BitAnd;
|
||||
use std::os::raw::*;
|
||||
|
||||
use super::{ffi, XConnection, XError};
|
||||
|
||||
pub fn reinterpret<'a, A, B>(a: &'a A) -> &'a B {
|
||||
let b_ptr = a as *const _ as *const B;
|
||||
unsafe { &*b_ptr }
|
||||
}
|
||||
|
||||
pub fn maybe_change<T: PartialEq>(field: &mut Option<T>, value: T) -> bool {
|
||||
let wrapped = Some(value);
|
||||
if *field != wrapped {
|
||||
@@ -41,6 +47,13 @@ pub fn maybe_change<T: PartialEq>(field: &mut Option<T>, value: T) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_flag<T>(bitset: T, flag: T) -> bool
|
||||
where T:
|
||||
Copy + PartialEq + BitAnd<T, Output = T>
|
||||
{
|
||||
bitset & flag == flag
|
||||
}
|
||||
|
||||
#[must_use = "This request was made asynchronously, and is still in the output buffer. You must explicitly choose to either `.flush()` (empty the output buffer, sending the request now) or `.queue()` (wait to send the request, allowing you to continue to add more requests without additional round-trips). For more information, see the documentation for `util::flush_requests`."]
|
||||
pub struct Flusher<'a> {
|
||||
xconn: &'a XConnection,
|
||||
|
||||
@@ -1,21 +1,49 @@
|
||||
use std::{env, slice};
|
||||
use std::str::FromStr;
|
||||
|
||||
use validate_hidpi_factor;
|
||||
use super::*;
|
||||
use super::ffi::{
|
||||
RROutput,
|
||||
XRRCrtcInfo,
|
||||
XRRMonitorInfo,
|
||||
XRRScreenResources,
|
||||
};
|
||||
|
||||
pub fn calc_dpi_factor(
|
||||
(width_px, height_px): (u32, u32),
|
||||
(width_mm, height_mm): (u64, u64),
|
||||
) -> f64 {
|
||||
// Override DPI if `WINIT_HIDPI_FACTOR` variable is set
|
||||
let dpi_override = env::var("WINIT_HIDPI_FACTOR")
|
||||
.ok()
|
||||
.and_then(|var| f64::from_str(&var).ok());
|
||||
if let Some(dpi_override) = dpi_override {
|
||||
if !validate_hidpi_factor(dpi_override) {
|
||||
panic!(
|
||||
"`WINIT_HIDPI_FACTOR` invalid; DPI factors must be normal floats greater than 0. Got `{}`",
|
||||
dpi_override,
|
||||
);
|
||||
}
|
||||
return dpi_override;
|
||||
}
|
||||
|
||||
// See http://xpra.org/trac/ticket/728 for more information.
|
||||
if width_mm == 0 || width_mm == 0 {
|
||||
warn!("XRandR reported that the display's 0mm in size, which is certifiably insane");
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
let ppmm = (
|
||||
(width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)
|
||||
).sqrt();
|
||||
// Quantize 1/12 step size
|
||||
let dpi_factor = ((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0);
|
||||
assert!(validate_hidpi_factor(dpi_factor));
|
||||
dpi_factor
|
||||
}
|
||||
|
||||
pub enum MonitorRepr {
|
||||
Monitor(*mut XRRMonitorInfo),
|
||||
Crtc(*mut XRRCrtcInfo),
|
||||
Monitor(*mut ffi::XRRMonitorInfo),
|
||||
Crtc(*mut ffi::XRRCrtcInfo),
|
||||
}
|
||||
|
||||
impl MonitorRepr {
|
||||
pub unsafe fn get_output(&self) -> RROutput {
|
||||
pub unsafe fn get_output(&self) -> ffi::RROutput {
|
||||
match *self {
|
||||
// Same member names, but different locations within the struct...
|
||||
MonitorRepr::Monitor(monitor) => *((*monitor).outputs.offset(0)),
|
||||
@@ -38,47 +66,20 @@ impl MonitorRepr {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<*mut XRRMonitorInfo> for MonitorRepr {
|
||||
fn from(monitor: *mut XRRMonitorInfo) -> Self {
|
||||
impl From<*mut ffi::XRRMonitorInfo> for MonitorRepr {
|
||||
fn from(monitor: *mut ffi::XRRMonitorInfo) -> Self {
|
||||
MonitorRepr::Monitor(monitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<*mut XRRCrtcInfo> for MonitorRepr {
|
||||
fn from(crtc: *mut XRRCrtcInfo) -> Self {
|
||||
impl From<*mut ffi::XRRCrtcInfo> for MonitorRepr {
|
||||
fn from(crtc: *mut ffi::XRRCrtcInfo) -> Self {
|
||||
MonitorRepr::Crtc(crtc)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calc_dpi_factor(
|
||||
(width_px, height_px): (u32, u32),
|
||||
(width_mm, height_mm): (u64, u64),
|
||||
) -> f64 {
|
||||
// Override DPI if `WINIT_HIDPI_FACTOR` variable is set
|
||||
if let Ok(dpi_factor_str) = env::var("WINIT_HIDPI_FACTOR") {
|
||||
if let Ok(dpi_factor) = f64::from_str(&dpi_factor_str) {
|
||||
if dpi_factor <= 0. {
|
||||
panic!("Expected `WINIT_HIDPI_FACTOR` to be bigger than 0, got '{}'", dpi_factor);
|
||||
}
|
||||
|
||||
return dpi_factor;
|
||||
}
|
||||
}
|
||||
|
||||
// See http://xpra.org/trac/ticket/728 for more information
|
||||
if width_mm == 0 || width_mm == 0 {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
let ppmm = (
|
||||
(width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)
|
||||
).sqrt();
|
||||
// Quantize 1/12 step size
|
||||
((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0)
|
||||
}
|
||||
|
||||
impl XConnection {
|
||||
pub unsafe fn get_output_info(&self, resources: *mut XRRScreenResources, repr: &MonitorRepr) -> (String, f32) {
|
||||
pub unsafe fn get_output_info(&self, resources: *mut ffi::XRRScreenResources, repr: &MonitorRepr) -> (String, f64) {
|
||||
let output_info = (self.xrandr.XRRGetOutputInfo)(
|
||||
self.display,
|
||||
resources,
|
||||
@@ -92,7 +93,7 @@ impl XConnection {
|
||||
let hidpi_factor = calc_dpi_factor(
|
||||
repr.get_dimensions(),
|
||||
((*output_info).mm_width as u64, (*output_info).mm_height as u64),
|
||||
) as f32;
|
||||
);
|
||||
(self.xrandr.XRRFreeOutputInfo)(output_info);
|
||||
(name, hidpi_factor)
|
||||
}
|
||||
|
||||
@@ -7,15 +7,15 @@ use std::sync::Arc;
|
||||
use libc;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use {CursorState, Icon, MouseCursor, WindowAttributes};
|
||||
use {Icon, MouseCursor, WindowAttributes};
|
||||
use CreationError::{self, OsError};
|
||||
use dpi::{LogicalPosition, LogicalSize};
|
||||
use platform::MonitorId as PlatformMonitorId;
|
||||
use platform::PlatformSpecificWindowBuilderAttributes;
|
||||
use platform::x11::MonitorId as X11MonitorId;
|
||||
use platform::x11::monitor::get_monitor_for_window;
|
||||
use window::MonitorId as RootMonitorId;
|
||||
|
||||
use super::{ffi, util, XConnection, XError, WindowId, EventsLoop};
|
||||
use super::{ffi, util, ImeSender, XConnection, XError, WindowId, EventsLoop};
|
||||
|
||||
unsafe extern "C" fn visibility_predicate(
|
||||
_display: *mut ffi::Display,
|
||||
@@ -29,17 +29,27 @@ unsafe extern "C" fn visibility_predicate(
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SharedState {
|
||||
pub multitouch: bool,
|
||||
pub cursor_pos: Option<(f64, f64)>,
|
||||
pub size: Option<(u32, u32)>,
|
||||
pub position: Option<(i32, i32)>,
|
||||
pub inner_position: Option<(i32, i32)>,
|
||||
pub inner_position_rel_parent: Option<(i32, i32)>,
|
||||
pub guessed_dpi: Option<f64>,
|
||||
pub last_monitor: Option<X11MonitorId>,
|
||||
pub dpi_adjusted: Option<(f64, f64)>,
|
||||
// Used to restore position after exiting fullscreen.
|
||||
pub restore_position: Option<(i32, i32)>,
|
||||
pub frame_extents: Option<util::FrameExtentsHeuristic>,
|
||||
pub min_dimensions: Option<(u32, u32)>,
|
||||
pub max_dimensions: Option<(u32, u32)>,
|
||||
pub min_dimensions: Option<LogicalSize>,
|
||||
pub max_dimensions: Option<LogicalSize>,
|
||||
}
|
||||
|
||||
impl SharedState {
|
||||
fn new(dpi_factor: f64) -> Mutex<Self> {
|
||||
let mut shared_state = SharedState::default();
|
||||
shared_state.guessed_dpi = Some(dpi_factor);
|
||||
Mutex::new(shared_state)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for UnownedWindow {}
|
||||
@@ -51,7 +61,9 @@ pub struct UnownedWindow {
|
||||
root: ffi::Window, // never changes
|
||||
screen_id: i32, // never changes
|
||||
cursor: Mutex<MouseCursor>,
|
||||
cursor_state: Mutex<CursorState>,
|
||||
cursor_grabbed: Mutex<bool>,
|
||||
cursor_hidden: Mutex<bool>,
|
||||
ime_sender: Mutex<ImeSender>,
|
||||
pub multitouch: bool, // never changes
|
||||
pub shared_state: Mutex<SharedState>,
|
||||
}
|
||||
@@ -65,18 +77,60 @@ impl UnownedWindow {
|
||||
let xconn = &event_loop.xconn;
|
||||
let root = event_loop.root;
|
||||
|
||||
let monitors = xconn.get_available_monitors();
|
||||
let dpi_factor = if !monitors.is_empty() {
|
||||
let mut dpi_factor = Some(monitors[0].get_hidpi_factor());
|
||||
for monitor in &monitors {
|
||||
if Some(monitor.get_hidpi_factor()) != dpi_factor {
|
||||
dpi_factor = None;
|
||||
}
|
||||
}
|
||||
dpi_factor.unwrap_or_else(|| {
|
||||
xconn.query_pointer(root, util::VIRTUAL_CORE_POINTER)
|
||||
.ok()
|
||||
.and_then(|pointer_state| {
|
||||
let (x, y) = (pointer_state.root_x as i64, pointer_state.root_y as i64);
|
||||
let mut dpi_factor = None;
|
||||
for monitor in &monitors {
|
||||
if monitor.rect.contains_point(x, y) {
|
||||
dpi_factor = Some(monitor.get_hidpi_factor());
|
||||
break;
|
||||
}
|
||||
}
|
||||
dpi_factor
|
||||
})
|
||||
.unwrap_or(1.0)
|
||||
})
|
||||
} else {
|
||||
return Err(OsError(format!("No monitors were detected.")));
|
||||
};
|
||||
|
||||
info!("Guessed window DPI factor: {}", dpi_factor);
|
||||
|
||||
let max_dimensions: Option<(u32, u32)> = window_attrs.max_dimensions.map(|size| {
|
||||
size.to_physical(dpi_factor).into()
|
||||
});
|
||||
let min_dimensions: Option<(u32, u32)> = window_attrs.min_dimensions.map(|size| {
|
||||
size.to_physical(dpi_factor).into()
|
||||
});
|
||||
|
||||
let dimensions = {
|
||||
// x11 only applies constraints when the window is actively resized
|
||||
// by the user, so we have to manually apply the initial constraints
|
||||
let mut dimensions = window_attrs.dimensions.unwrap_or((800, 600));
|
||||
if let Some(max) = window_attrs.max_dimensions {
|
||||
let mut dimensions: (u32, u32) = window_attrs.dimensions
|
||||
.or_else(|| Some((800, 600).into()))
|
||||
.map(|size| size.to_physical(dpi_factor))
|
||||
.map(Into::into)
|
||||
.unwrap();
|
||||
if let Some(max) = max_dimensions {
|
||||
dimensions.0 = cmp::min(dimensions.0, max.0);
|
||||
dimensions.1 = cmp::min(dimensions.1, max.1);
|
||||
}
|
||||
if let Some(min) = window_attrs.min_dimensions {
|
||||
if let Some(min) = min_dimensions {
|
||||
dimensions.0 = cmp::max(dimensions.0, min.0);
|
||||
dimensions.1 = cmp::max(dimensions.1, min.1);
|
||||
}
|
||||
debug!("Calculated physical dimensions: {}x{}", dimensions.0, dimensions.1);
|
||||
dimensions
|
||||
};
|
||||
|
||||
@@ -144,9 +198,11 @@ impl UnownedWindow {
|
||||
root,
|
||||
screen_id,
|
||||
cursor: Default::default(),
|
||||
cursor_state: Default::default(),
|
||||
cursor_grabbed: Default::default(),
|
||||
cursor_hidden: Default::default(),
|
||||
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
|
||||
multitouch: window_attrs.multitouch,
|
||||
shared_state: Default::default(),
|
||||
shared_state: SharedState::new(dpi_factor),
|
||||
};
|
||||
|
||||
// Title must be set before mapping. Some tiling window managers (i.e. i3) use the window
|
||||
@@ -216,48 +272,34 @@ 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
|
||||
{
|
||||
(*window.shared_state.lock()).min_dimensions = window_attrs.min_dimensions;
|
||||
(*window.shared_state.lock()).max_dimensions = window_attrs.max_dimensions;
|
||||
let mut min_dimensions = window_attrs.min_dimensions;
|
||||
let mut max_dimensions = window_attrs.max_dimensions;
|
||||
if !window_attrs.resizable && !util::wm_name_is_one_of(&["Xfwm4"]) {
|
||||
max_dimensions = Some(dimensions);
|
||||
min_dimensions = Some(dimensions);
|
||||
if !window_attrs.resizable {
|
||||
if util::wm_name_is_one_of(&["Xfwm4"]) {
|
||||
warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4");
|
||||
} else {
|
||||
max_dimensions = Some(dimensions.into());
|
||||
min_dimensions = Some(dimensions.into());
|
||||
|
||||
let mut shared_state_lock = window.shared_state.lock();
|
||||
shared_state_lock.min_dimensions = window_attrs.min_dimensions;
|
||||
shared_state_lock.max_dimensions = window_attrs.max_dimensions;
|
||||
}
|
||||
}
|
||||
|
||||
let mut size_hints = xconn.alloc_size_hints();
|
||||
(*size_hints).flags = ffi::PSize;
|
||||
(*size_hints).width = dimensions.0 as c_int;
|
||||
(*size_hints).height = dimensions.1 as c_int;
|
||||
if let Some((min_width, min_height)) = min_dimensions {
|
||||
(*size_hints).flags |= ffi::PMinSize;
|
||||
(*size_hints).min_width = min_width as c_int;
|
||||
(*size_hints).min_height = min_height as c_int;
|
||||
}
|
||||
if let Some((max_width, max_height)) = max_dimensions {
|
||||
(*size_hints).flags |= ffi::PMaxSize;
|
||||
(*size_hints).max_width = max_width as c_int;
|
||||
(*size_hints).max_height = max_height as c_int;
|
||||
}
|
||||
if let Some((width_inc, height_inc)) = pl_attribs.resize_increments {
|
||||
(*size_hints).flags |= ffi::PResizeInc;
|
||||
(*size_hints).width_inc = width_inc as c_int;
|
||||
(*size_hints).height_inc = height_inc as c_int;
|
||||
}
|
||||
if let Some((base_width, base_height)) = pl_attribs.base_size {
|
||||
(*size_hints).flags |= ffi::PBaseSize;
|
||||
(*size_hints).base_width = base_width as c_int;
|
||||
(*size_hints).base_height = base_height as c_int;
|
||||
}
|
||||
unsafe {
|
||||
(xconn.xlib.XSetWMNormalHints)(
|
||||
xconn.display,
|
||||
window.xwindow,
|
||||
size_hints.ptr,
|
||||
);
|
||||
}//.queue();
|
||||
let mut normal_hints = util::NormalHints::new(xconn);
|
||||
normal_hints.set_size(Some(dimensions));
|
||||
normal_hints.set_min_size(min_dimensions.map(Into::into));
|
||||
normal_hints.set_max_size(max_dimensions.map(Into::into));
|
||||
normal_hints.set_resize_increments(pl_attribs.resize_increments);
|
||||
normal_hints.set_base_size(pl_attribs.base_size);
|
||||
xconn.set_normal_hints(window.xwindow, normal_hints).queue();
|
||||
}
|
||||
|
||||
// Set window icons
|
||||
@@ -291,7 +333,7 @@ impl UnownedWindow {
|
||||
&mut supported_ptr,
|
||||
);
|
||||
if supported_ptr == ffi::False {
|
||||
return Err(OsError(format!("XkbSetDetectableAutoRepeat failed")));
|
||||
return Err(OsError(format!("`XkbSetDetectableAutoRepeat` failed")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,6 +357,15 @@ impl UnownedWindow {
|
||||
};
|
||||
xconn.select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask).queue();
|
||||
|
||||
{
|
||||
let result = event_loop.ime
|
||||
.borrow_mut()
|
||||
.create_context(window.xwindow);
|
||||
if let Err(err) = result {
|
||||
return Err(OsError(format!("Failed to create input context: {:?}", err)));
|
||||
}
|
||||
}
|
||||
|
||||
// These properties must be set after mapping
|
||||
if window_attrs.maximized {
|
||||
window.set_maximized_inner(window_attrs.maximized).queue();
|
||||
@@ -355,6 +406,16 @@ impl UnownedWindow {
|
||||
))
|
||||
}
|
||||
|
||||
fn logicalize_coords(&self, (x, y): (i32, i32)) -> LogicalPosition {
|
||||
let dpi = self.get_hidpi_factor();
|
||||
LogicalPosition::from_physical((x, y), dpi)
|
||||
}
|
||||
|
||||
fn logicalize_size(&self, (width, height): (u32, u32)) -> LogicalSize {
|
||||
let dpi = self.get_hidpi_factor();
|
||||
LogicalSize::from_physical((width, height), dpi)
|
||||
}
|
||||
|
||||
fn set_pid(&self) -> Option<util::Flusher> {
|
||||
let pid_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_PID\0") };
|
||||
let client_machine_atom = unsafe { self.xconn.get_atom_unchecked(b"WM_CLIENT_MACHINE\0") };
|
||||
@@ -400,6 +461,20 @@ 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");
|
||||
if is_urgent {
|
||||
@@ -439,17 +514,24 @@ impl UnownedWindow {
|
||||
fn set_fullscreen_inner(&self, monitor: Option<RootMonitorId>) -> util::Flusher {
|
||||
match monitor {
|
||||
None => {
|
||||
self.set_fullscreen_hint(false)
|
||||
let flusher = self.set_fullscreen_hint(false);
|
||||
if let Some(position) = self.shared_state.lock().restore_position.take() {
|
||||
self.set_position_inner(position.0, position.1).queue();
|
||||
}
|
||||
flusher
|
||||
},
|
||||
Some(RootMonitorId { inner: PlatformMonitorId::X(monitor) }) => {
|
||||
let screenpos = monitor.get_position();
|
||||
self.set_position(screenpos.0 as i32, screenpos.1 as i32);
|
||||
let window_position = self.get_position_physical();
|
||||
self.shared_state.lock().restore_position = window_position;
|
||||
let monitor_origin: (i32, i32) = monitor.get_position().into();
|
||||
self.set_position_inner(monitor_origin.0, monitor_origin.1).queue();
|
||||
self.set_fullscreen_hint(true)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
|
||||
self.set_fullscreen_inner(monitor)
|
||||
.flush()
|
||||
@@ -457,17 +539,36 @@ impl UnownedWindow {
|
||||
self.invalidate_cached_frame_extents();
|
||||
}
|
||||
|
||||
fn get_rect(&self) -> Option<util::Rect> {
|
||||
fn get_rect(&self) -> Option<util::AaRect> {
|
||||
// TODO: This might round-trip more times than needed.
|
||||
if let (Some(position), Some(size)) = (self.get_position(), self.get_outer_size()) {
|
||||
Some(util::Rect::new(position, size))
|
||||
if let (Some(position), Some(size)) = (self.get_position_physical(), self.get_outer_size_physical()) {
|
||||
Some(util::AaRect::new(position, size))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> X11MonitorId {
|
||||
get_monitor_for_window(&self.xconn, self.get_rect()).to_owned()
|
||||
let monitor = self.shared_state
|
||||
.lock()
|
||||
.last_monitor
|
||||
.as_ref()
|
||||
.cloned();
|
||||
monitor
|
||||
.unwrap_or_else(|| {
|
||||
let monitor = self.xconn.get_monitor_for_window(self.get_rect()).to_owned();
|
||||
self.shared_state.lock().last_monitor = Some(monitor.clone());
|
||||
monitor
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_available_monitors(&self) -> Vec<X11MonitorId> {
|
||||
self.xconn.get_available_monitors()
|
||||
}
|
||||
|
||||
pub fn get_primary_monitor(&self) -> X11MonitorId {
|
||||
self.xconn.get_primary_monitor()
|
||||
}
|
||||
|
||||
fn set_maximized_inner(&self, maximized: bool) -> util::Flusher {
|
||||
@@ -476,6 +577,7 @@ impl UnownedWindow {
|
||||
self.set_netwm(maximized.into(), (horz_atom as c_long, vert_atom as c_long, 0, 0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
self.set_maximized_inner(maximized)
|
||||
.flush()
|
||||
@@ -498,11 +600,12 @@ impl UnownedWindow {
|
||||
wm_name_atom,
|
||||
utf8_atom,
|
||||
util::PropMode::Replace,
|
||||
title.as_bytes_with_nul(),
|
||||
title.as_bytes(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_title(&self, title: &str) {
|
||||
self.set_title_inner(title)
|
||||
.flush()
|
||||
@@ -526,6 +629,7 @@ impl UnownedWindow {
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, decorations: bool) {
|
||||
self.set_decorations_inner(decorations)
|
||||
.flush()
|
||||
@@ -538,6 +642,7 @@ impl UnownedWindow {
|
||||
self.set_netwm(always_on_top.into(), (above_atom as c_long, 0, 0, 0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_always_on_top(&self, always_on_top: bool) {
|
||||
self.set_always_on_top_inner(always_on_top)
|
||||
.flush()
|
||||
@@ -568,6 +673,7 @@ impl UnownedWindow {
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, icon: Option<Icon>) {
|
||||
match icon {
|
||||
Some(icon) => self.set_icon_inner(icon),
|
||||
@@ -575,6 +681,7 @@ impl UnownedWindow {
|
||||
}.flush().expect("Failed to set icons");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show(&self) {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XMapRaised)(self.xconn.display, self.xwindow);
|
||||
@@ -583,6 +690,7 @@ impl UnownedWindow {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide(&self) {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow);
|
||||
@@ -596,31 +704,46 @@ impl UnownedWindow {
|
||||
(*self.shared_state.lock()).frame_extents = Some(extents);
|
||||
}
|
||||
|
||||
pub fn invalidate_cached_frame_extents(&self) {
|
||||
pub(crate) fn invalidate_cached_frame_extents(&self) {
|
||||
(*self.shared_state.lock()).frame_extents.take();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
pub(crate) fn get_position_physical(&self) -> Option<(i32, i32)> {
|
||||
let extents = (*self.shared_state.lock()).frame_extents.clone();
|
||||
if let Some(extents) = extents {
|
||||
self.get_inner_position().map(|(x, y)|
|
||||
extents.inner_pos_to_outer(x, y)
|
||||
)
|
||||
self.get_inner_position_physical()
|
||||
.map(|(x, y)| extents.inner_pos_to_outer(x, y))
|
||||
} else {
|
||||
self.update_cached_frame_extents();
|
||||
self.get_position_physical()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||
let extents = (*self.shared_state.lock()).frame_extents.clone();
|
||||
if let Some(extents) = extents {
|
||||
self.get_inner_position()
|
||||
.map(|logical| extents.inner_pos_to_outer_logical(logical, self.get_hidpi_factor()))
|
||||
} else {
|
||||
self.update_cached_frame_extents();
|
||||
self.get_position()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
self.xconn.translate_coords(self.xwindow, self.root )
|
||||
pub(crate) fn get_inner_position_physical(&self) -> Option<(i32, i32)> {
|
||||
self.xconn.translate_coords(self.xwindow, self.root)
|
||||
.ok()
|
||||
.map(|coords| (coords.x_rel_root, coords.y_rel_root))
|
||||
}
|
||||
|
||||
pub fn set_position(&self, mut x: i32, mut y: i32) {
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||
self.get_inner_position_physical()
|
||||
.map(|coords| self.logicalize_coords(coords))
|
||||
}
|
||||
|
||||
pub(crate) fn set_position_inner(&self, mut x: i32, mut y: i32) -> util::Flusher {
|
||||
// There are a few WMs that set client area position rather than window position, so
|
||||
// we'll translate for consistency.
|
||||
if util::wm_name_is_one_of(&["Enlightenment", "FVWM"]) {
|
||||
@@ -630,7 +753,7 @@ impl UnownedWindow {
|
||||
y += extents.frame_extents.top as i32;
|
||||
} else {
|
||||
self.update_cached_frame_extents();
|
||||
self.set_position(x, y)
|
||||
return self.set_position_inner(x, y);
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
@@ -640,32 +763,58 @@ impl UnownedWindow {
|
||||
x as c_int,
|
||||
y as c_int,
|
||||
);
|
||||
self.xconn.flush_requests()
|
||||
}.expect("Failed to call XMoveWindow");
|
||||
}
|
||||
util::Flusher::new(&self.xconn)
|
||||
}
|
||||
|
||||
pub(crate) fn set_position_physical(&self, x: i32, y: i32) {
|
||||
self.set_position_inner(x, y)
|
||||
.flush()
|
||||
.expect("Failed to call `XMoveWindow`");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
pub fn set_position(&self, logical_position: LogicalPosition) {
|
||||
let (x, y) = logical_position.to_physical(self.get_hidpi_factor()).into();
|
||||
self.set_position_physical(x, y);
|
||||
}
|
||||
|
||||
pub(crate) fn get_inner_size_physical(&self) -> Option<(u32, u32)> {
|
||||
self.xconn.get_geometry(self.xwindow)
|
||||
.ok()
|
||||
.map(|geo| (geo.width, geo.height))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
let extents = (*self.shared_state.lock()).frame_extents.clone();
|
||||
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||
self.get_inner_size_physical()
|
||||
.map(|size| self.logicalize_size(size))
|
||||
}
|
||||
|
||||
pub(crate) fn get_outer_size_physical(&self) -> Option<(u32, u32)> {
|
||||
let extents = self.shared_state.lock().frame_extents.clone();
|
||||
if let Some(extents) = extents {
|
||||
self.get_inner_size().map(|(w, h)|
|
||||
extents.inner_size_to_outer(w, h)
|
||||
)
|
||||
self.get_inner_size_physical()
|
||||
.map(|(w, h)| extents.inner_size_to_outer(w, h))
|
||||
} else {
|
||||
self.update_cached_frame_extents();
|
||||
self.get_outer_size_physical()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||
let extents = self.shared_state.lock().frame_extents.clone();
|
||||
if let Some(extents) = extents {
|
||||
self.get_inner_size()
|
||||
.map(|logical| extents.inner_size_to_outer_logical(logical, self.get_hidpi_factor()))
|
||||
} else {
|
||||
self.update_cached_frame_extents();
|
||||
self.get_outer_size()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_inner_size(&self, width: u32, height: u32) {
|
||||
pub(crate) fn set_inner_size_physical(&self, width: u32, height: u32) {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XResizeWindow)(
|
||||
self.xconn.display,
|
||||
@@ -674,62 +823,86 @@ impl UnownedWindow {
|
||||
height as c_uint,
|
||||
);
|
||||
self.xconn.flush_requests()
|
||||
}.expect("Failed to call XResizeWindow");
|
||||
}.expect("Failed to call `XResizeWindow`");
|
||||
}
|
||||
|
||||
unsafe fn update_normal_hints<F>(&self, callback: F) -> Result<(), XError>
|
||||
where F: FnOnce(*mut ffi::XSizeHints) -> ()
|
||||
#[inline]
|
||||
pub fn set_inner_size(&self, logical_size: LogicalSize) {
|
||||
let dpi_factor = self.get_hidpi_factor();
|
||||
let (width, height) = logical_size.to_physical(dpi_factor).into();
|
||||
self.set_inner_size_physical(width, height);
|
||||
}
|
||||
|
||||
fn update_normal_hints<F>(&self, callback: F) -> Result<(), XError>
|
||||
where F: FnOnce(&mut util::NormalHints) -> ()
|
||||
{
|
||||
let size_hints = self.xconn.alloc_size_hints();
|
||||
let mut flags: c_long = mem::uninitialized();
|
||||
(self.xconn.xlib.XGetWMNormalHints)(
|
||||
self.xconn.display,
|
||||
self.xwindow,
|
||||
size_hints.ptr,
|
||||
&mut flags,
|
||||
);
|
||||
self.xconn.check_errors()?;
|
||||
|
||||
callback(size_hints.ptr);
|
||||
|
||||
(self.xconn.xlib.XSetWMNormalHints)(
|
||||
self.xconn.display,
|
||||
self.xwindow,
|
||||
size_hints.ptr,
|
||||
);
|
||||
self.xconn.flush_requests()?;
|
||||
|
||||
Ok(())
|
||||
let mut normal_hints = self.xconn.get_normal_hints(self.xwindow)?;
|
||||
callback(&mut normal_hints);
|
||||
self.xconn.set_normal_hints(self.xwindow, normal_hints).flush()
|
||||
}
|
||||
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
(*self.shared_state.lock()).min_dimensions = dimensions;
|
||||
unsafe {
|
||||
self.update_normal_hints(|size_hints| {
|
||||
if let Some((width, height)) = dimensions {
|
||||
(*size_hints).flags |= ffi::PMinSize;
|
||||
(*size_hints).min_width = width as c_int;
|
||||
(*size_hints).min_height = height as c_int;
|
||||
} else {
|
||||
(*size_hints).flags &= !ffi::PMinSize;
|
||||
}
|
||||
})
|
||||
}.expect("Failed to call XSetWMNormalHints");
|
||||
pub(crate) fn set_min_dimensions_physical(&self, dimensions: Option<(u32, u32)>) {
|
||||
self.update_normal_hints(|normal_hints| normal_hints.set_min_size(dimensions))
|
||||
.expect("Failed to call `XSetWMNormalHints`");
|
||||
}
|
||||
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
(*self.shared_state.lock()).max_dimensions = dimensions;
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, logical_dimensions: Option<LogicalSize>) {
|
||||
self.shared_state.lock().min_dimensions = logical_dimensions;
|
||||
let physical_dimensions = logical_dimensions.map(|logical_dimensions| {
|
||||
logical_dimensions.to_physical(self.get_hidpi_factor()).into()
|
||||
});
|
||||
self.set_min_dimensions_physical(physical_dimensions);
|
||||
}
|
||||
|
||||
pub(crate) fn set_max_dimensions_physical(&self, dimensions: Option<(u32, u32)>) {
|
||||
self.update_normal_hints(|normal_hints| normal_hints.set_max_size(dimensions))
|
||||
.expect("Failed to call `XSetWMNormalHints`");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, logical_dimensions: Option<LogicalSize>) {
|
||||
self.shared_state.lock().max_dimensions = logical_dimensions;
|
||||
let physical_dimensions = logical_dimensions.map(|logical_dimensions| {
|
||||
logical_dimensions.to_physical(self.get_hidpi_factor()).into()
|
||||
});
|
||||
self.set_max_dimensions_physical(physical_dimensions);
|
||||
}
|
||||
|
||||
pub(crate) fn adjust_for_dpi(
|
||||
&self,
|
||||
old_dpi_factor: f64,
|
||||
new_dpi_factor: f64,
|
||||
width: f64,
|
||||
height: f64,
|
||||
) -> (f64, f64, util::Flusher) {
|
||||
let scale_factor = new_dpi_factor / old_dpi_factor;
|
||||
let new_width = width * scale_factor;
|
||||
let new_height = height * scale_factor;
|
||||
self.update_normal_hints(|normal_hints| {
|
||||
let dpi_adjuster = |(width, height): (u32, u32)| -> (u32, u32) {
|
||||
let new_width = width as f64 * scale_factor;
|
||||
let new_height = height as f64 * scale_factor;
|
||||
(new_width.round() as u32, new_height.round() as u32)
|
||||
};
|
||||
let max_size = normal_hints.get_max_size().map(&dpi_adjuster);
|
||||
let min_size = normal_hints.get_min_size().map(&dpi_adjuster);
|
||||
let resize_increments = normal_hints.get_resize_increments().map(&dpi_adjuster);
|
||||
let base_size = normal_hints.get_base_size().map(&dpi_adjuster);
|
||||
normal_hints.set_max_size(max_size);
|
||||
normal_hints.set_min_size(min_size);
|
||||
normal_hints.set_resize_increments(resize_increments);
|
||||
normal_hints.set_base_size(base_size);
|
||||
}).expect("Failed to update normal hints");
|
||||
unsafe {
|
||||
self.update_normal_hints(|size_hints| {
|
||||
if let Some((width, height)) = dimensions {
|
||||
(*size_hints).flags |= ffi::PMaxSize;
|
||||
(*size_hints).max_width = width as c_int;
|
||||
(*size_hints).max_height = height as c_int;
|
||||
} else {
|
||||
(*size_hints).flags &= !ffi::PMaxSize;
|
||||
}
|
||||
})
|
||||
}.expect("Failed to call XSetWMNormalHints");
|
||||
(self.xconn.xlib.XResizeWindow)(
|
||||
self.xconn.display,
|
||||
self.xwindow,
|
||||
new_width.round() as c_uint,
|
||||
new_height.round() as c_uint,
|
||||
);
|
||||
}
|
||||
(new_width, new_height, util::Flusher::new(&self.xconn))
|
||||
}
|
||||
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
@@ -737,26 +910,29 @@ impl UnownedWindow {
|
||||
// Making the window unresizable on Xfwm prevents further changes to `WM_NORMAL_HINTS` from being detected.
|
||||
// This makes it impossible for resizing to be re-enabled, and also breaks DPI scaling. As such, we choose
|
||||
// the lesser of two evils and do nothing.
|
||||
warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4");
|
||||
return;
|
||||
}
|
||||
if resizable {
|
||||
let min_dimensions = (*self.shared_state.lock()).min_dimensions;
|
||||
let max_dimensions = (*self.shared_state.lock()).max_dimensions;
|
||||
self.set_min_dimensions(min_dimensions);
|
||||
self.set_max_dimensions(max_dimensions);
|
||||
|
||||
let (logical_min, logical_max) = if resizable {
|
||||
let shared_state_lock = self.shared_state.lock();
|
||||
(shared_state_lock.min_dimensions, shared_state_lock.max_dimensions)
|
||||
} else {
|
||||
unsafe {
|
||||
self.update_normal_hints(|size_hints| {
|
||||
(*size_hints).flags |= ffi::PMinSize | ffi::PMaxSize;
|
||||
if let Some((width, height)) = self.get_inner_size() {
|
||||
(*size_hints).min_width = width as c_int;
|
||||
(*size_hints).min_height = height as c_int;
|
||||
(*size_hints).max_width = width as c_int;
|
||||
(*size_hints).max_height = height as c_int;
|
||||
}
|
||||
})
|
||||
}.expect("Failed to call XSetWMNormalHints");
|
||||
}
|
||||
let window_size = self.get_inner_size();
|
||||
(window_size.clone(), window_size)
|
||||
};
|
||||
|
||||
let dpi_factor = self.get_hidpi_factor();
|
||||
let min_dimensions = logical_min
|
||||
.map(|logical_size| logical_size.to_physical(dpi_factor))
|
||||
.map(Into::into);
|
||||
let max_dimensions = logical_max
|
||||
.map(|logical_size| logical_size.to_physical(dpi_factor))
|
||||
.map(Into::into);
|
||||
self.update_normal_hints(|normal_hints| {
|
||||
normal_hints.set_min_size(min_dimensions);
|
||||
normal_hints.set_max_size(max_dimensions);
|
||||
}).expect("Failed to call `XSetWMNormalHints`");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -774,21 +950,12 @@ impl UnownedWindow {
|
||||
Arc::clone(&self.xconn)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
self.xconn.display as _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_xlib_window(&self) -> c_ulong {
|
||||
self.xwindow
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
self.xwindow as _
|
||||
}
|
||||
|
||||
pub fn get_xcb_connection(&self) -> *mut c_void {
|
||||
unsafe {
|
||||
(self.xconn.xlib_xcb.XGetXCBConnection)(self.xconn.display) as *mut _
|
||||
@@ -870,9 +1037,6 @@ impl UnownedWindow {
|
||||
|
||||
MouseCursor::ZoomIn => load(b"zoom-in\0"),
|
||||
MouseCursor::ZoomOut => load(b"zoom-out\0"),
|
||||
|
||||
MouseCursor::NoneCursor => self.create_empty_cursor()
|
||||
.expect("Failed to create empty cursor"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -886,9 +1050,10 @@ impl UnownedWindow {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||
*self.cursor.lock() = cursor;
|
||||
if *self.cursor_state.lock() != CursorState::Hide {
|
||||
if !*self.cursor_hidden.lock() {
|
||||
self.update_cursor(self.get_cursor(cursor));
|
||||
}
|
||||
}
|
||||
@@ -931,74 +1096,82 @@ impl UnownedWindow {
|
||||
Some(cursor)
|
||||
}
|
||||
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
use CursorState::*;
|
||||
|
||||
let mut cursor_state_lock = self.cursor_state.lock();
|
||||
|
||||
match (state, *cursor_state_lock) {
|
||||
(Normal, Normal) | (Hide, Hide) | (Grab, Grab) => return Ok(()),
|
||||
_ => {},
|
||||
#[inline]
|
||||
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
|
||||
let mut grabbed_lock = self.cursor_grabbed.lock();
|
||||
if grab == *grabbed_lock { return Ok(()); }
|
||||
unsafe {
|
||||
// We ungrab before grabbing to prevent passive grabs from causing `AlreadyGrabbed`.
|
||||
// Therefore, this is common to both codepaths.
|
||||
(self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime);
|
||||
}
|
||||
let result = if grab {
|
||||
let result = unsafe {
|
||||
(self.xconn.xlib.XGrabPointer)(
|
||||
self.xconn.display,
|
||||
self.xwindow,
|
||||
ffi::True,
|
||||
(
|
||||
ffi::ButtonPressMask
|
||||
| ffi::ButtonReleaseMask
|
||||
| ffi::EnterWindowMask
|
||||
| ffi::LeaveWindowMask
|
||||
| ffi::PointerMotionMask
|
||||
| ffi::PointerMotionHintMask
|
||||
| ffi::Button1MotionMask
|
||||
| ffi::Button2MotionMask
|
||||
| ffi::Button3MotionMask
|
||||
| ffi::Button4MotionMask
|
||||
| ffi::Button5MotionMask
|
||||
| ffi::ButtonMotionMask
|
||||
| ffi::KeymapStateMask
|
||||
) as c_uint,
|
||||
ffi::GrabModeAsync,
|
||||
ffi::GrabModeAsync,
|
||||
self.xwindow,
|
||||
0,
|
||||
ffi::CurrentTime,
|
||||
)
|
||||
};
|
||||
|
||||
match *cursor_state_lock {
|
||||
Grab => {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime);
|
||||
self.xconn.flush_requests().expect("Failed to call XUngrabPointer");
|
||||
}
|
||||
},
|
||||
Normal => {},
|
||||
Hide => self.update_cursor(self.get_cursor(*self.cursor.lock())),
|
||||
}
|
||||
|
||||
match state {
|
||||
Normal => {
|
||||
*cursor_state_lock = state;
|
||||
Ok(())
|
||||
},
|
||||
Hide => {
|
||||
*cursor_state_lock = state;
|
||||
self.update_cursor(
|
||||
self.create_empty_cursor().expect("Failed to create empty cursor")
|
||||
);
|
||||
Ok(())
|
||||
},
|
||||
Grab => {
|
||||
unsafe {
|
||||
// Ungrab before grabbing to prevent passive grabs
|
||||
// from causing AlreadyGrabbed
|
||||
(self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime);
|
||||
|
||||
match (self.xconn.xlib.XGrabPointer)(
|
||||
self.xconn.display, self.xwindow, ffi::True,
|
||||
(ffi::ButtonPressMask | ffi::ButtonReleaseMask | ffi::EnterWindowMask |
|
||||
ffi::LeaveWindowMask | ffi::PointerMotionMask | ffi::PointerMotionHintMask |
|
||||
ffi::Button1MotionMask | ffi::Button2MotionMask | ffi::Button3MotionMask |
|
||||
ffi::Button4MotionMask | ffi::Button5MotionMask | ffi::ButtonMotionMask |
|
||||
ffi::KeymapStateMask) as c_uint,
|
||||
ffi::GrabModeAsync, ffi::GrabModeAsync,
|
||||
self.xwindow, 0, ffi::CurrentTime
|
||||
) {
|
||||
ffi::GrabSuccess => {
|
||||
*cursor_state_lock = state;
|
||||
Ok(())
|
||||
},
|
||||
ffi::AlreadyGrabbed | ffi::GrabInvalidTime |
|
||||
ffi::GrabNotViewable | ffi::GrabFrozen
|
||||
=> Err("cursor could not be grabbed".to_string()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
},
|
||||
match result {
|
||||
ffi::GrabSuccess => Ok(()),
|
||||
ffi::AlreadyGrabbed => Err("Cursor could not be grabbed: already grabbed by another client"),
|
||||
ffi::GrabInvalidTime => Err("Cursor could not be grabbed: invalid time"),
|
||||
ffi::GrabNotViewable => Err("Cursor could not be grabbed: grab location not viewable"),
|
||||
ffi::GrabFrozen => Err("Cursor could not be grabbed: frozen by another client"),
|
||||
_ => unreachable!(),
|
||||
}.map_err(|err| err.to_owned())
|
||||
} else {
|
||||
self.xconn.flush_requests()
|
||||
.map_err(|err| format!("Failed to call `XUngrabPointer`: {:?}", err))
|
||||
};
|
||||
if result.is_ok() {
|
||||
*grabbed_lock = grab;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
#[inline]
|
||||
pub fn hide_cursor(&self, hide: bool) {
|
||||
let mut hidden_lock = self.cursor_hidden.lock();
|
||||
if hide == *hidden_lock {return; }
|
||||
let cursor = if hide {
|
||||
self.create_empty_cursor().expect("Failed to create empty cursor")
|
||||
} else {
|
||||
self.get_cursor(*self.cursor.lock())
|
||||
};
|
||||
*hidden_lock = hide;
|
||||
drop(hidden_lock);
|
||||
self.update_cursor(cursor);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
self.get_current_monitor().hidpi_factor
|
||||
}
|
||||
|
||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||
pub fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), String> {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XWarpPointer)(
|
||||
self.xconn.display,
|
||||
@@ -1011,10 +1184,28 @@ impl UnownedWindow {
|
||||
x,
|
||||
y,
|
||||
);
|
||||
self.xconn.flush_requests().map_err(|_| ())
|
||||
self.xconn.flush_requests().map_err(|e| format!("`XWarpPointer` failed: {:?}", e))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, logical_position: LogicalPosition) -> Result<(), String> {
|
||||
let (x, y) = logical_position.to_physical(self.get_hidpi_factor()).into();
|
||||
self.set_cursor_position_physical(x, y)
|
||||
}
|
||||
|
||||
pub(crate) fn set_ime_spot_physical(&self, x: i32, y: i32) {
|
||||
let _ = self.ime_sender
|
||||
.lock()
|
||||
.send((self.xwindow, x as i16, y as i16));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_spot(&self, logical_spot: LogicalPosition) {
|
||||
let (x, y) = logical_spot.to_physical(self.get_hidpi_factor()).into();
|
||||
self.set_ime_spot_physical(x, y);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId { WindowId(self.xwindow) }
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -377,14 +398,16 @@ impl EventsLoop {
|
||||
} else {
|
||||
window.view.convertPoint_fromView_(window_point, cocoa::base::nil)
|
||||
};
|
||||
|
||||
let view_rect = NSView::frame(*window.view);
|
||||
let scale_factor = window.hidpi_factor();
|
||||
|
||||
let x = (scale_factor * view_point.x as f32) as f64;
|
||||
let y = (scale_factor * (view_rect.size.height - view_point.y) as f32) as f64;
|
||||
let window_event = WindowEvent::CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event_mods(ns_event) };
|
||||
let x = view_point.x as f64;
|
||||
let y = (view_rect.size.height - view_point.y) as f64;
|
||||
let window_event = WindowEvent::CursorMoved {
|
||||
device_id: DEVICE_ID,
|
||||
position: (x, y).into(),
|
||||
modifiers: event_mods(ns_event),
|
||||
};
|
||||
let event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event };
|
||||
|
||||
self.shared.pending_events.lock().unwrap().push_back(event);
|
||||
Some(into_event(WindowEvent::CursorEntered { device_id: DEVICE_ID }))
|
||||
},
|
||||
@@ -397,32 +420,30 @@ impl EventsLoop {
|
||||
// If the mouse movement was on one of our windows, use it.
|
||||
// Otherwise, if one of our windows is the key window (receiving input), use it.
|
||||
// Otherwise, return `None`.
|
||||
let window = match maybe_window.or_else(maybe_key_window) {
|
||||
Some(window) => window,
|
||||
match maybe_window.or_else(maybe_key_window) {
|
||||
Some(_window) => (),
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let scale_factor = window.hidpi_factor();
|
||||
}
|
||||
|
||||
let mut events = std::collections::VecDeque::with_capacity(3);
|
||||
|
||||
let delta_x = (scale_factor * ns_event.deltaX() as f32) as f64;
|
||||
let delta_x = ns_event.deltaX() as f64;
|
||||
if delta_x != 0.0 {
|
||||
let motion_event = DeviceEvent::Motion { axis: 0, value: delta_x };
|
||||
let event = Event::DeviceEvent{ device_id: DEVICE_ID, event: motion_event };
|
||||
let event = Event::DeviceEvent { device_id: DEVICE_ID, event: motion_event };
|
||||
events.push_back(event);
|
||||
}
|
||||
|
||||
let delta_y = (scale_factor * ns_event.deltaY() as f32) as f64;
|
||||
let delta_y = ns_event.deltaY() as f64;
|
||||
if delta_y != 0.0 {
|
||||
let motion_event = DeviceEvent::Motion { axis: 1, value: delta_y };
|
||||
let event = Event::DeviceEvent{ device_id: DEVICE_ID, event: motion_event };
|
||||
let event = Event::DeviceEvent { device_id: DEVICE_ID, event: motion_event };
|
||||
events.push_back(event);
|
||||
}
|
||||
|
||||
if delta_x != 0.0 || delta_y != 0.0 {
|
||||
let motion_event = DeviceEvent::MouseMotion { delta: (delta_x, delta_y) };
|
||||
let event = Event::DeviceEvent{ device_id: DEVICE_ID, event: motion_event };
|
||||
let event = Event::DeviceEvent { device_id: DEVICE_ID, event: motion_event };
|
||||
events.push_back(event);
|
||||
}
|
||||
|
||||
@@ -433,19 +454,22 @@ impl EventsLoop {
|
||||
|
||||
appkit::NSScrollWheel => {
|
||||
// If none of the windows received the scroll, return `None`.
|
||||
let window = match maybe_window {
|
||||
Some(window) => window,
|
||||
None => return None,
|
||||
};
|
||||
if maybe_window.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
use events::MouseScrollDelta::{LineDelta, PixelDelta};
|
||||
let scale_factor = window.hidpi_factor();
|
||||
let delta = if ns_event.hasPreciseScrollingDeltas() == cocoa::base::YES {
|
||||
PixelDelta(scale_factor * ns_event.scrollingDeltaX() as f32,
|
||||
scale_factor * ns_event.scrollingDeltaY() as f32)
|
||||
PixelDelta((
|
||||
ns_event.scrollingDeltaX() as f64,
|
||||
ns_event.scrollingDeltaY() as f64,
|
||||
).into())
|
||||
} else {
|
||||
LineDelta(scale_factor * ns_event.scrollingDeltaX() as f32,
|
||||
scale_factor * ns_event.scrollingDeltaY() as f32)
|
||||
// TODO: This is probably wrong
|
||||
LineDelta(
|
||||
ns_event.scrollingDeltaX() as f32,
|
||||
ns_event.scrollingDeltaY() as f32,
|
||||
)
|
||||
};
|
||||
let phase = match ns_event.phase() {
|
||||
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => TouchPhase::Started,
|
||||
@@ -456,11 +480,15 @@ impl EventsLoop {
|
||||
device_id: DEVICE_ID,
|
||||
event: DeviceEvent::MouseWheel {
|
||||
delta: if ns_event.hasPreciseScrollingDeltas() == cocoa::base::YES {
|
||||
PixelDelta(ns_event.scrollingDeltaX() as f32,
|
||||
ns_event.scrollingDeltaY() as f32)
|
||||
PixelDelta((
|
||||
ns_event.scrollingDeltaX() as f64,
|
||||
ns_event.scrollingDeltaY() as f64,
|
||||
).into())
|
||||
} else {
|
||||
LineDelta(ns_event.scrollingDeltaX() as f32,
|
||||
ns_event.scrollingDeltaY() as f32)
|
||||
LineDelta(
|
||||
ns_event.scrollingDeltaX() as f32,
|
||||
ns_event.scrollingDeltaY() as f32,
|
||||
)
|
||||
},
|
||||
}
|
||||
});
|
||||
@@ -582,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,
|
||||
@@ -597,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,
|
||||
@@ -608,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,
|
||||
@@ -624,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,
|
||||
@@ -652,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)
|
||||
@@ -667,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,30 +1,44 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
|
||||
use cocoa::appkit::NSScreen;
|
||||
use cocoa::base::{id, nil};
|
||||
use cocoa::foundation::{NSString, NSUInteger};
|
||||
use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds};
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
|
||||
use {PhysicalPosition, PhysicalSize};
|
||||
use super::EventsLoop;
|
||||
use super::window::IdRef;
|
||||
use super::window::{IdRef, Window2};
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct MonitorId(CGDirectDisplayID);
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
let mut monitors = VecDeque::new();
|
||||
if let Ok(displays) = CGDisplay::active_displays() {
|
||||
for d in displays {
|
||||
monitors.push_back(MonitorId(d));
|
||||
}
|
||||
fn get_available_monitors() -> VecDeque<MonitorId> {
|
||||
if let Ok(displays) = CGDisplay::active_displays() {
|
||||
let mut monitors = VecDeque::with_capacity(displays.len());
|
||||
for d in displays {
|
||||
monitors.push_back(MonitorId(d));
|
||||
}
|
||||
monitors
|
||||
} else {
|
||||
VecDeque::with_capacity(0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_primary_monitor() -> MonitorId {
|
||||
let id = MonitorId(CGDisplay::main().id);
|
||||
id
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
get_available_monitors()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
let id = MonitorId(CGDisplay::main().id);
|
||||
id
|
||||
get_primary_monitor()
|
||||
}
|
||||
|
||||
pub fn make_monitor_from_display(id: CGDirectDisplayID) -> MonitorId {
|
||||
@@ -33,15 +47,27 @@ impl EventsLoop {
|
||||
}
|
||||
}
|
||||
|
||||
impl Window2 {
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
get_available_monitors()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
get_primary_monitor()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for MonitorId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
#[derive(Debug)]
|
||||
struct MonitorId {
|
||||
name: Option<String>,
|
||||
native_identifier: u32,
|
||||
dimensions: (u32, u32),
|
||||
position: (i32, i32),
|
||||
hidpi_factor: f32,
|
||||
dimensions: PhysicalSize,
|
||||
position: PhysicalPosition,
|
||||
hidpi_factor: f64,
|
||||
}
|
||||
|
||||
let monitor_id_proxy = MonitorId {
|
||||
@@ -68,30 +94,32 @@ impl MonitorId {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||
let MonitorId(display_id) = *self;
|
||||
let display = CGDisplay::new(display_id);
|
||||
let dimension = {
|
||||
let height = display.pixels_high();
|
||||
let width = display.pixels_wide();
|
||||
(width as u32, height as u32)
|
||||
};
|
||||
dimension
|
||||
let height = display.pixels_high();
|
||||
let width = display.pixels_wide();
|
||||
PhysicalSize::from_logical(
|
||||
(width as f64, height as f64),
|
||||
self.get_hidpi_factor(),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
pub fn get_position(&self) -> PhysicalPosition {
|
||||
let bounds = unsafe { CGDisplayBounds(self.get_native_identifier()) };
|
||||
(bounds.origin.x as i32, bounds.origin.y as i32)
|
||||
PhysicalPosition::from_logical(
|
||||
(bounds.origin.x as f64, bounds.origin.y as f64),
|
||||
self.get_hidpi_factor(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
let screen = match self.get_nsscreen() {
|
||||
Some(screen) => screen,
|
||||
None => return 1.0, // default to 1.0 when we can't find the screen
|
||||
};
|
||||
|
||||
unsafe { NSScreen::backingScaleFactor(screen) as f32 }
|
||||
unsafe { NSScreen::backingScaleFactor(screen) as f64 }
|
||||
}
|
||||
|
||||
pub(crate) fn get_nsscreen(&self) -> Option<id> {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -14,8 +14,8 @@ pub const EMPTY_RANGE: ffi::NSRange = ffi::NSRange {
|
||||
// For consistency with other platforms, this will...
|
||||
// 1. translate the bottom-left window corner into the top-left window corner
|
||||
// 2. translate the coordinate from a bottom-left origin coordinate system to a top-left one
|
||||
pub fn bottom_left_to_top_left(rect: NSRect) -> i32 {
|
||||
(CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height)) as _
|
||||
pub fn bottom_left_to_top_left(rect: NSRect) -> f64 {
|
||||
CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height)
|
||||
}
|
||||
|
||||
pub unsafe fn set_style_mask(window: id, view: id, mask: NSWindowStyleMask) {
|
||||
@@ -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};
|
||||
@@ -22,9 +22,9 @@ use platform::platform::window::{get_window_id, IdRef};
|
||||
struct ViewState {
|
||||
window: id,
|
||||
shared: Weak<Shared>,
|
||||
ime_spot: Option<(i32, i32)>,
|
||||
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`
|
||||
@@ -43,7 +43,7 @@ pub fn new_view(window: id, shared: Weak<Shared>) -> IdRef {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_ime_spot(view: id, input_context: id, x: i32, y: i32) {
|
||||
pub fn set_ime_spot(view: id, input_context: id, x: f64, y: f64) {
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *(*view).get_mut_ivar("winitState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
@@ -51,8 +51,8 @@ pub fn set_ime_spot(view: id, input_context: id, x: i32, y: i32) {
|
||||
state.window,
|
||||
NSWindow::frame(state.window),
|
||||
);
|
||||
let base_x = content_rect.origin.x as i32;
|
||||
let base_y = (content_rect.origin.y + content_rect.size.height) as i32;
|
||||
let base_x = content_rect.origin.x as f64;
|
||||
let base_y = (content_rect.origin.y + content_rect.size.height) as f64;
|
||||
state.ime_spot = Some((base_x + x, base_y - y));
|
||||
let _: () = msg_send![input_context, invalidateCharacterCoordinates];
|
||||
}
|
||||
@@ -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(
|
||||
@@ -249,7 +250,7 @@ extern fn first_rect_for_character_range(
|
||||
);
|
||||
let x = content_rect.origin.x;
|
||||
let y = util::bottom_left_to_top_left(content_rect);
|
||||
(x as i32, y as i32)
|
||||
(x, y)
|
||||
});
|
||||
|
||||
NSRect::new(
|
||||
@@ -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),
|
||||
@@ -396,11 +417,14 @@ extern fn key_down(this: &Object, _sel: Sel, event: id) {
|
||||
.unwrap()
|
||||
.push_back(window_event);
|
||||
}
|
||||
} else {
|
||||
// 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 (): _ = msg_send![this, interpretKeyEvents:array];
|
||||
}
|
||||
}
|
||||
|
||||
let array: id = msg_send![class("NSArray"), arrayWithObject:event];
|
||||
let (): _ = msg_send![this, interpretKeyEvents:array];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,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)),
|
||||
@@ -527,15 +558,14 @@ fn mouse_motion(this: &Object, event: id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let scale_factor = NSWindow::backingScaleFactor(state.window) as f64;
|
||||
let x = scale_factor * view_point.x as f64;
|
||||
let y = scale_factor * (view_rect.size.height as f64 - view_point.y as f64);
|
||||
let x = view_point.x as f64;
|
||||
let y = view_rect.size.height as f64 - view_point.y as f64;
|
||||
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.window)),
|
||||
event: WindowEvent::CursorMoved {
|
||||
device_id: DEVICE_ID,
|
||||
position: (x, y),
|
||||
position: (x, y).into(),
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
};
|
||||
@@ -564,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,40 +1,56 @@
|
||||
use {CreationError, Event, WindowEvent, WindowId, MouseCursor, CursorState};
|
||||
use CreationError::OsError;
|
||||
use libc;
|
||||
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::sync::atomic::{Ordering, AtomicBool};
|
||||
|
||||
use WindowAttributes;
|
||||
use os::macos::ActivationPolicy;
|
||||
use os::macos::WindowExt;
|
||||
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};
|
||||
|
||||
use core_graphics::display::CGDisplay;
|
||||
|
||||
use objc;
|
||||
use objc::runtime::{Class, Object, Sel, BOOL, YES, NO};
|
||||
use objc::declare::ClassDecl;
|
||||
|
||||
use cocoa;
|
||||
use cocoa::appkit::{self, NSApplication, NSColor, NSScreen, NSView, NSWindow, NSWindowButton,
|
||||
NSWindowStyleMask};
|
||||
use cocoa::base::{id, nil};
|
||||
use cocoa::foundation::{NSDictionary, NSPoint, NSRect, NSSize, NSString, NSAutoreleasePool};
|
||||
|
||||
use core_graphics::display::CGDisplay;
|
||||
|
||||
use std;
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::c_void;
|
||||
use std::sync::Weak;
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
use super::events_loop::{EventsLoop, Shared};
|
||||
use platform::platform::ffi;
|
||||
use platform::platform::util;
|
||||
use {
|
||||
CreationError,
|
||||
Event,
|
||||
LogicalPosition,
|
||||
LogicalSize,
|
||||
MouseCursor,
|
||||
WindowAttributes,
|
||||
WindowEvent,
|
||||
WindowId,
|
||||
};
|
||||
use CreationError::OsError;
|
||||
use os::macos::{ActivationPolicy, WindowExt};
|
||||
use platform::platform::{ffi, util};
|
||||
use platform::platform::events_loop::{EventsLoop, Shared};
|
||||
use platform::platform::view::{new_view, set_ime_spot};
|
||||
|
||||
use window::MonitorId as RootMonitorId;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Id(pub usize);
|
||||
|
||||
struct DelegateState {
|
||||
// TODO: It's possible for delegate methods to be called asynchronously, causing data races / `RefCell` panics.
|
||||
pub struct DelegateState {
|
||||
view: IdRef,
|
||||
window: IdRef,
|
||||
shared: Weak<Shared>,
|
||||
@@ -47,8 +63,11 @@ struct DelegateState {
|
||||
// see comments of `window_did_fail_to_enter_fullscreen`
|
||||
handle_with_fullscreen: bool,
|
||||
|
||||
// During windowDidResize, we use this to only send Moved if the position changed.
|
||||
previous_position: Option<(i32, i32)>,
|
||||
// During `windowDidResize`, we use this to only send Moved if the position changed.
|
||||
previous_position: Option<(f64, f64)>,
|
||||
|
||||
// Used to prevent redundant events.
|
||||
previous_dpi_factor: f64,
|
||||
}
|
||||
|
||||
impl DelegateState {
|
||||
@@ -150,48 +169,44 @@ pub struct WindowDelegate {
|
||||
}
|
||||
|
||||
impl WindowDelegate {
|
||||
// Emits an event via the `EventsLoop`'s callback or stores it in the pending queue.
|
||||
pub fn emit_event(state: &mut DelegateState, window_event: WindowEvent) {
|
||||
let window_id = get_window_id(*state.window);
|
||||
let event = Event::WindowEvent {
|
||||
window_id: WindowId(window_id),
|
||||
event: window_event,
|
||||
};
|
||||
if let Some(shared) = state.shared.upgrade() {
|
||||
shared.call_user_callback_with_event_or_store_in_pending(event);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emit_resize_event(state: &mut DelegateState) {
|
||||
let rect = unsafe { NSView::frame(*state.view) };
|
||||
let size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64);
|
||||
WindowDelegate::emit_event(state, WindowEvent::Resized(size));
|
||||
}
|
||||
|
||||
pub fn emit_move_event(state: &mut DelegateState) {
|
||||
let rect = unsafe { NSWindow::frame(*state.window) };
|
||||
let x = rect.origin.x as f64;
|
||||
let y = util::bottom_left_to_top_left(rect);
|
||||
let moved = state.previous_position != Some((x, y));
|
||||
if moved {
|
||||
state.previous_position = Some((x, y));
|
||||
WindowDelegate::emit_event(state, WindowEvent::Moved((x, y).into()));
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the delegate class, initiailizing it neccessary
|
||||
fn class() -> *const Class {
|
||||
use std::os::raw::c_void;
|
||||
|
||||
// Emits an event via the `EventsLoop`'s callback or stores it in the pending queue.
|
||||
unsafe fn emit_event(state: &mut DelegateState, window_event: WindowEvent) {
|
||||
let window_id = get_window_id(*state.window);
|
||||
let event = Event::WindowEvent {
|
||||
window_id: WindowId(window_id),
|
||||
event: window_event,
|
||||
};
|
||||
|
||||
if let Some(shared) = state.shared.upgrade() {
|
||||
shared.call_user_callback_with_event_or_store_in_pending(event);
|
||||
}
|
||||
}
|
||||
|
||||
// Called when the window is resized or when the window was moved to a different screen.
|
||||
unsafe fn emit_resize_event(state: &mut DelegateState) {
|
||||
let rect = NSView::frame(*state.view);
|
||||
let scale_factor = NSWindow::backingScaleFactor(*state.window) as f32;
|
||||
let width = (scale_factor * rect.size.width as f32) as u32;
|
||||
let height = (scale_factor * rect.size.height as f32) as u32;
|
||||
emit_event(state, WindowEvent::Resized(width, height));
|
||||
}
|
||||
|
||||
unsafe fn emit_move_event(state: &mut DelegateState) {
|
||||
let frame_rect = NSWindow::frame(*state.window);
|
||||
let x = frame_rect.origin.x as _;
|
||||
let y = util::bottom_left_to_top_left(frame_rect);
|
||||
let moved = state.previous_position != Some((x, y));
|
||||
if moved {
|
||||
state.previous_position = Some((x, y));
|
||||
emit_event(state, WindowEvent::Moved(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
extern fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
emit_event(state, WindowEvent::CloseRequested);
|
||||
WindowDelegate::emit_event(state, WindowEvent::CloseRequested);
|
||||
}
|
||||
NO
|
||||
}
|
||||
@@ -201,7 +216,7 @@ impl WindowDelegate {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
|
||||
emit_event(state, WindowEvent::Destroyed);
|
||||
WindowDelegate::emit_event(state, WindowEvent::Destroyed);
|
||||
|
||||
// Remove the window from the shared state.
|
||||
if let Some(shared) = state.shared.upgrade() {
|
||||
@@ -215,8 +230,8 @@ impl WindowDelegate {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
emit_resize_event(state);
|
||||
emit_move_event(state);
|
||||
WindowDelegate::emit_resize_event(state);
|
||||
WindowDelegate::emit_move_event(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +240,7 @@ impl WindowDelegate {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
emit_move_event(state);
|
||||
WindowDelegate::emit_move_event(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,18 +248,26 @@ impl WindowDelegate {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
emit_resize_event(state);
|
||||
let scale_factor = NSWindow::backingScaleFactor(*state.window) as f32;
|
||||
emit_event(state, WindowEvent::HiDPIFactorChanged(scale_factor));
|
||||
let dpi_factor = NSWindow::backingScaleFactor(*state.window) as f64;
|
||||
if state.previous_dpi_factor != dpi_factor {
|
||||
state.previous_dpi_factor = dpi_factor;
|
||||
WindowDelegate::emit_event(state, WindowEvent::HiDpiFactorChanged(dpi_factor));
|
||||
WindowDelegate::emit_resize_event(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This will always be called before `window_did_change_screen`.
|
||||
extern fn window_did_change_backing_properties(this: &Object, _:Sel, _:id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
let scale_factor = NSWindow::backingScaleFactor(*state.window) as f32;
|
||||
emit_event(state, WindowEvent::HiDPIFactorChanged(scale_factor));
|
||||
let dpi_factor = NSWindow::backingScaleFactor(*state.window) as f64;
|
||||
if state.previous_dpi_factor != dpi_factor {
|
||||
state.previous_dpi_factor = dpi_factor;
|
||||
WindowDelegate::emit_event(state, WindowEvent::HiDpiFactorChanged(dpi_factor));
|
||||
WindowDelegate::emit_resize_event(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +277,7 @@ impl WindowDelegate {
|
||||
// lost focus
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
emit_event(state, WindowEvent::Focused(true));
|
||||
WindowDelegate::emit_event(state, WindowEvent::Focused(true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +285,7 @@ impl WindowDelegate {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
emit_event(state, WindowEvent::Focused(false));
|
||||
WindowDelegate::emit_event(state, WindowEvent::Focused(false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +308,7 @@ impl WindowDelegate {
|
||||
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
emit_event(state, WindowEvent::HoveredFile(PathBuf::from(path)));
|
||||
WindowDelegate::emit_event(state, WindowEvent::HoveredFile(PathBuf::from(path)));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -314,7 +337,7 @@ impl WindowDelegate {
|
||||
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
emit_event(state, WindowEvent::DroppedFile(PathBuf::from(path)));
|
||||
WindowDelegate::emit_event(state, WindowEvent::DroppedFile(PathBuf::from(path)));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -329,7 +352,7 @@ impl WindowDelegate {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
emit_event(state, WindowEvent::HoveredFileCancelled);
|
||||
WindowDelegate::emit_event(state, WindowEvent::HoveredFileCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,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
|
||||
@@ -500,7 +523,7 @@ pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub titlebar_hidden: bool,
|
||||
pub titlebar_buttons_hidden: bool,
|
||||
pub fullsize_content_view: bool,
|
||||
pub resize_increments: Option<(u32, u32)>,
|
||||
pub resize_increments: Option<LogicalSize>,
|
||||
}
|
||||
|
||||
pub struct Window2 {
|
||||
@@ -508,6 +531,7 @@ pub struct Window2 {
|
||||
pub window: IdRef,
|
||||
pub delegate: WindowDelegate,
|
||||
pub input_context: IdRef,
|
||||
cursor_hidden: AtomicBool,
|
||||
}
|
||||
|
||||
unsafe impl Send for Window2 {}
|
||||
@@ -563,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 {
|
||||
@@ -572,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");
|
||||
}
|
||||
}
|
||||
@@ -617,12 +654,11 @@ impl Window2 {
|
||||
|
||||
app.activateIgnoringOtherApps_(YES);
|
||||
|
||||
if let Some((width, height)) = win_attribs.min_dimensions {
|
||||
nswindow_set_min_dimensions(window.0, width.into(), height.into());
|
||||
if let Some(dimensions) = win_attribs.min_dimensions {
|
||||
nswindow_set_min_dimensions(window.0, dimensions);
|
||||
}
|
||||
|
||||
if let Some((width, height)) = win_attribs.max_dimensions {
|
||||
nswindow_set_max_dimensions(window.0, width.into(), height.into());
|
||||
if let Some(dimensions) = win_attribs.max_dimensions {
|
||||
nswindow_set_max_dimensions(window.0, dimensions);
|
||||
}
|
||||
|
||||
use cocoa::foundation::NSArray;
|
||||
@@ -631,7 +667,9 @@ impl Window2 {
|
||||
registerForDraggedTypes:NSArray::arrayWithObject(nil, appkit::NSFilenamesPboardType)];
|
||||
}
|
||||
|
||||
let ds = DelegateState {
|
||||
let dpi_factor = unsafe { NSWindow::backingScaleFactor(*window) as f64 };
|
||||
|
||||
let mut delegate_state = DelegateState {
|
||||
view: view.clone(),
|
||||
window: window.clone(),
|
||||
shared,
|
||||
@@ -640,14 +678,21 @@ impl Window2 {
|
||||
save_style_mask: Cell::new(None),
|
||||
handle_with_fullscreen: win_attribs.fullscreen.is_some(),
|
||||
previous_position: None,
|
||||
previous_dpi_factor: dpi_factor,
|
||||
};
|
||||
ds.win_attribs.borrow_mut().fullscreen = None;
|
||||
delegate_state.win_attribs.borrow_mut().fullscreen = None;
|
||||
|
||||
if dpi_factor != 1.0 {
|
||||
WindowDelegate::emit_event(&mut delegate_state, WindowEvent::HiDpiFactorChanged(dpi_factor));
|
||||
WindowDelegate::emit_resize_event(&mut delegate_state);
|
||||
}
|
||||
|
||||
let window = Window2 {
|
||||
view: view,
|
||||
window: window,
|
||||
delegate: WindowDelegate::new(ds),
|
||||
delegate: WindowDelegate::new(delegate_state),
|
||||
input_context,
|
||||
cursor_hidden: Default::default(),
|
||||
};
|
||||
|
||||
// Set fullscreen mode after we setup everything
|
||||
@@ -689,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)
|
||||
}
|
||||
@@ -701,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);
|
||||
@@ -729,8 +782,10 @@ impl Window2 {
|
||||
let frame = match screen {
|
||||
Some(screen) => appkit::NSScreen::frame(screen),
|
||||
None => {
|
||||
let (width, height) = attrs.dimensions.unwrap_or((800, 600));
|
||||
NSRect::new(NSPoint::new(0., 0.), NSSize::new(width as f64, height as f64))
|
||||
let (width, height) = attrs.dimensions
|
||||
.map(|logical| (logical.width, logical.height))
|
||||
.unwrap_or((800.0, 600.0));
|
||||
NSRect::new(NSPoint::new(0.0, 0.0), NSSize::new(width, height))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -799,9 +854,10 @@ impl Window2 {
|
||||
let _: () = msg_send![*window, setLevel:ffi::NSWindowLevel::NSFloatingWindowLevel];
|
||||
}
|
||||
|
||||
if let Some((x, y)) = pl_attrs.resize_increments {
|
||||
if x >= 1 && y >= 1 {
|
||||
let size = NSSize::new(x as _, y as _);
|
||||
if let Some(increments) = pl_attrs.resize_increments {
|
||||
let (x, y) = (increments.width, increments.height);
|
||||
if x >= 1.0 && y >= 1.0 {
|
||||
let size = NSSize::new(x as CGFloat, y as CGFloat);
|
||||
window.setResizeIncrements_(size);
|
||||
}
|
||||
}
|
||||
@@ -819,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
|
||||
@@ -843,15 +909,15 @@ impl Window2 {
|
||||
unsafe { NSWindow::orderOut_(*self.window, nil); }
|
||||
}
|
||||
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||
let frame_rect = unsafe { NSWindow::frame(*self.window) };
|
||||
Some((
|
||||
frame_rect.origin.x as i32,
|
||||
frame_rect.origin.x as f64,
|
||||
util::bottom_left_to_top_left(frame_rect),
|
||||
))
|
||||
).into())
|
||||
}
|
||||
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||
let content_rect = unsafe {
|
||||
NSWindow::contentRectForFrameRect_(
|
||||
*self.window,
|
||||
@@ -859,18 +925,18 @@ impl Window2 {
|
||||
)
|
||||
};
|
||||
Some((
|
||||
content_rect.origin.x as i32,
|
||||
content_rect.origin.x as f64,
|
||||
util::bottom_left_to_top_left(content_rect),
|
||||
))
|
||||
).into())
|
||||
}
|
||||
|
||||
pub fn set_position(&self, x: i32, y: i32) {
|
||||
pub fn set_position(&self, position: LogicalPosition) {
|
||||
let dummy = NSRect::new(
|
||||
NSPoint::new(
|
||||
x as f64,
|
||||
position.x,
|
||||
// While it's true that we're setting the top-left position, it still needs to be
|
||||
// in a bottom-left coordinate system.
|
||||
CGDisplay::main().pixels_high() as f64 - y as f64,
|
||||
CGDisplay::main().pixels_high() as f64 - position.y,
|
||||
),
|
||||
NSSize::new(0f64, 0f64),
|
||||
);
|
||||
@@ -880,42 +946,35 @@ impl Window2 {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
let factor = self.hidpi_factor() as f64; // API convention is that size is in physical pixels
|
||||
unsafe {
|
||||
let view_frame = NSView::frame(*self.view);
|
||||
Some(((view_frame.size.width*factor) as u32, (view_frame.size.height*factor) as u32))
|
||||
}
|
||||
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||
let view_frame = unsafe { NSView::frame(*self.view) };
|
||||
Some((view_frame.size.width as f64, view_frame.size.height as f64).into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
let factor = self.hidpi_factor() as f64; // API convention is that size is in physical pixels
|
||||
unsafe {
|
||||
let window_frame = NSWindow::frame(*self.window);
|
||||
Some(((window_frame.size.width*factor) as u32, (window_frame.size.height*factor) as u32))
|
||||
}
|
||||
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||
let view_frame = unsafe { NSWindow::frame(*self.window) };
|
||||
Some((view_frame.size.width as f64, view_frame.size.height as f64).into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_inner_size(&self, width: u32, height: u32) {
|
||||
let factor = self.hidpi_factor() as f64; // API convention is that size is in physical pixels
|
||||
pub fn set_inner_size(&self, size: LogicalSize) {
|
||||
unsafe {
|
||||
NSWindow::setContentSize_(*self.window, NSSize::new((width as f64)/factor, (height as f64)/factor));
|
||||
NSWindow::setContentSize_(*self.window, NSSize::new(size.width as CGFloat, size.height as CGFloat));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||
unsafe {
|
||||
let (width, height) = dimensions.unwrap_or((0, 0));
|
||||
nswindow_set_min_dimensions(self.window.0, width.into(), height.into());
|
||||
let dimensions = dimensions.unwrap_or_else(|| (0, 0).into());
|
||||
nswindow_set_min_dimensions(self.window.0, dimensions);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||
unsafe {
|
||||
let (width, height) = dimensions.unwrap_or((!0, !0));
|
||||
nswindow_set_max_dimensions(self.window.0, width.into(), height.into());
|
||||
let dimensions = dimensions.unwrap_or_else(|| (!0, !0).into());
|
||||
nswindow_set_max_dimensions(self.window.0, dimensions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -934,16 +993,6 @@ impl Window2 {
|
||||
} // Otherwise, we don't change the mask until we exit fullscreen.
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
*self.window as *mut libc::c_void
|
||||
}
|
||||
|
||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||
let cursor_name = match cursor {
|
||||
MouseCursor::Arrow | MouseCursor::Default => "arrowCursor",
|
||||
@@ -968,13 +1017,13 @@ impl Window2 {
|
||||
MouseCursor::SeResize | MouseCursor::SwResize |
|
||||
MouseCursor::NwseResize | MouseCursor::NeswResize |
|
||||
|
||||
MouseCursor::Cell | MouseCursor::NoneCursor |
|
||||
MouseCursor::Cell |
|
||||
MouseCursor::Wait | MouseCursor::Progress | MouseCursor::Help |
|
||||
MouseCursor::Move | MouseCursor::AllScroll | MouseCursor::ZoomIn |
|
||||
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();
|
||||
@@ -982,46 +1031,47 @@ impl Window2 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
let cls = Class::get("NSCursor").unwrap();
|
||||
#[inline]
|
||||
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
|
||||
// TODO: Do this for real https://stackoverflow.com/a/40922095/5435443
|
||||
CGDisplay::associate_mouse_and_mouse_cursor_position(!grab)
|
||||
.map_err(|status| format!("Failed to grab cursor: `CGError` {:?}", status))
|
||||
}
|
||||
|
||||
// TODO: Check for errors.
|
||||
match state {
|
||||
CursorState::Normal => {
|
||||
let _: () = unsafe { msg_send![cls, unhide] };
|
||||
let _ = CGDisplay::associate_mouse_and_mouse_cursor_position(true);
|
||||
Ok(())
|
||||
},
|
||||
CursorState::Hide => {
|
||||
let _: () = unsafe { msg_send![cls, hide] };
|
||||
Ok(())
|
||||
},
|
||||
CursorState::Grab => {
|
||||
let _: () = unsafe { msg_send![cls, hide] };
|
||||
let _ = CGDisplay::associate_mouse_and_mouse_cursor_position(false);
|
||||
Ok(())
|
||||
#[inline]
|
||||
pub fn hide_cursor(&self, hide: bool) {
|
||||
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.load(Ordering::Acquire) {
|
||||
if hide {
|
||||
let _: () = unsafe { msg_send![cursor_class, hide] };
|
||||
} else {
|
||||
let _: () = unsafe { msg_send![cursor_class, unhide] };
|
||||
}
|
||||
self.cursor_hidden.store(hide, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
unsafe {
|
||||
NSWindow::backingScaleFactor(*self.window) as f32
|
||||
NSWindow::backingScaleFactor(*self.window) as f64
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||
let (window_x, window_y) = self.get_position().unwrap_or((0, 0));
|
||||
let (cursor_x, cursor_y) = (window_x + x, window_y + y);
|
||||
|
||||
// TODO: Check for errors.
|
||||
let _ = CGDisplay::warp_mouse_cursor_position(appkit::CGPoint {
|
||||
x: cursor_x as appkit::CGFloat,
|
||||
y: cursor_y as appkit::CGFloat,
|
||||
});
|
||||
let _ = CGDisplay::associate_mouse_and_mouse_cursor_position(true);
|
||||
pub fn set_cursor_position(&self, cursor_position: LogicalPosition) -> Result<(), String> {
|
||||
let window_position = self.get_inner_position()
|
||||
.ok_or("`get_inner_position` failed".to_owned())?;
|
||||
let point = appkit::CGPoint {
|
||||
x: (cursor_position.x + window_position.x) as CGFloat,
|
||||
y: (cursor_position.y + window_position.y) as CGFloat,
|
||||
};
|
||||
CGDisplay::warp_mouse_cursor_position(point)
|
||||
.map_err(|e| format!("`CGWarpMouseCursorPosition` failed: {:?}", e))?;
|
||||
CGDisplay::associate_mouse_and_mouse_cursor_position(true)
|
||||
.map_err(|e| format!("`CGAssociateMouseAndMouseCursorPosition` failed: {:?}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1131,8 +1181,8 @@ impl Window2 {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_spot(&self, x: i32, y: i32) {
|
||||
set_ime_spot(*self.view, *self.input_context, x, y);
|
||||
pub fn set_ime_spot(&self, logical_spot: LogicalPosition) {
|
||||
set_ime_spot(*self.view, *self.input_context, logical_spot.x, logical_spot.y);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1149,51 +1199,50 @@ pub fn get_window_id(window_cocoa_id: id) -> Id {
|
||||
Id(window_cocoa_id as *const objc::runtime::Object as usize)
|
||||
}
|
||||
|
||||
unsafe fn nswindow_set_min_dimensions<V: NSWindow + Copy>(
|
||||
window: V, min_width: f64, min_height: f64)
|
||||
{
|
||||
unsafe fn nswindow_set_min_dimensions<V: NSWindow + Copy>(window: V, mut min_size: LogicalSize) {
|
||||
let mut current_rect = NSWindow::frame(window);
|
||||
let content_rect = NSWindow::contentRectForFrameRect_(window, NSWindow::frame(window));
|
||||
// Convert from client area size to window size
|
||||
min_size.width += (current_rect.size.width - content_rect.size.width) as f64; // this tends to be 0
|
||||
min_size.height += (current_rect.size.height - content_rect.size.height) as f64;
|
||||
window.setMinSize_(NSSize {
|
||||
width: min_width,
|
||||
height: min_height,
|
||||
width: min_size.width as CGFloat,
|
||||
height: min_size.height as CGFloat,
|
||||
});
|
||||
// If necessary, resize the window to match constraint
|
||||
let mut current_rect = NSWindow::frame(window);
|
||||
if current_rect.size.width < min_width {
|
||||
current_rect.size.width = min_width;
|
||||
if current_rect.size.width < min_size.width {
|
||||
current_rect.size.width = min_size.width;
|
||||
window.setFrame_display_(current_rect, 0)
|
||||
}
|
||||
if current_rect.size.height < min_height {
|
||||
// The origin point of a rectangle is at its bottom left in Cocoa. To
|
||||
// ensure the window's top-left point remains the same:
|
||||
current_rect.origin.y +=
|
||||
current_rect.size.height - min_height;
|
||||
|
||||
current_rect.size.height = min_height;
|
||||
if current_rect.size.height < min_size.height {
|
||||
// The origin point of a rectangle is at its bottom left in Cocoa.
|
||||
// To ensure the window's top-left point remains the same:
|
||||
current_rect.origin.y += current_rect.size.height - min_size.height;
|
||||
current_rect.size.height = min_size.height;
|
||||
window.setFrame_display_(current_rect, 0)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn nswindow_set_max_dimensions<V: NSWindow + Copy>(
|
||||
window: V, max_width: f64, max_height: f64)
|
||||
{
|
||||
unsafe fn nswindow_set_max_dimensions<V: NSWindow + Copy>(window: V, mut max_size: LogicalSize) {
|
||||
let mut current_rect = NSWindow::frame(window);
|
||||
let content_rect = NSWindow::contentRectForFrameRect_(window, NSWindow::frame(window));
|
||||
// Convert from client area size to window size
|
||||
max_size.width += (current_rect.size.width - content_rect.size.width) as f64; // this tends to be 0
|
||||
max_size.height += (current_rect.size.height - content_rect.size.height) as f64;
|
||||
window.setMaxSize_(NSSize {
|
||||
width: max_width,
|
||||
height: max_height,
|
||||
width: max_size.width as CGFloat,
|
||||
height: max_size.height as CGFloat,
|
||||
});
|
||||
// If necessary, resize the window to match constraint
|
||||
let mut current_rect = NSWindow::frame(window);
|
||||
if current_rect.size.width > max_width {
|
||||
current_rect.size.width = max_width;
|
||||
if current_rect.size.width > max_size.width {
|
||||
current_rect.size.width = max_size.width;
|
||||
window.setFrame_display_(current_rect, 0)
|
||||
}
|
||||
if current_rect.size.height > max_height {
|
||||
// The origin point of a rectangle is at its bottom left in
|
||||
// Cocoa. To ensure the window's top-left point remains the
|
||||
// same:
|
||||
current_rect.origin.y +=
|
||||
current_rect.size.height - max_height;
|
||||
|
||||
current_rect.size.height = max_height;
|
||||
if current_rect.size.height > max_size.height {
|
||||
// The origin point of a rectangle is at its bottom left in Cocoa.
|
||||
// To ensure the window's top-left point remains the same:
|
||||
current_rect.origin.y += current_rect.size.height - max_size.height;
|
||||
current_rect.size.height = max_size.height;
|
||||
window.setFrame_display_(current_rect, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
189
src/platform/windows/dpi.rs
Normal file
189
src/platform/windows/dpi.rs
Normal file
@@ -0,0 +1,189 @@
|
||||
#![allow(non_snake_case, unused_unsafe)]
|
||||
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
use std::sync::{Once, ONCE_INIT};
|
||||
|
||||
use winapi::shared::minwindef::{BOOL, UINT, FALSE};
|
||||
use winapi::shared::windef::{
|
||||
DPI_AWARENESS_CONTEXT,
|
||||
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE,
|
||||
HMONITOR,
|
||||
HWND,
|
||||
};
|
||||
use winapi::shared::winerror::S_OK;
|
||||
use winapi::um::libloaderapi::{GetProcAddress, LoadLibraryA};
|
||||
use winapi::um::shellscalingapi::{
|
||||
MDT_EFFECTIVE_DPI,
|
||||
MONITOR_DPI_TYPE,
|
||||
PROCESS_DPI_AWARENESS,
|
||||
PROCESS_PER_MONITOR_DPI_AWARE,
|
||||
};
|
||||
use winapi::um::wingdi::{GetDeviceCaps, LOGPIXELSX};
|
||||
use winapi::um::winnt::{HRESULT, LPCSTR};
|
||||
use winapi::um::winuser::{self, MONITOR_DEFAULTTONEAREST};
|
||||
|
||||
const DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2: DPI_AWARENESS_CONTEXT = -4isize as _;
|
||||
|
||||
type SetProcessDPIAware = unsafe extern "system" fn () -> BOOL;
|
||||
type SetProcessDpiAwareness = unsafe extern "system" fn (
|
||||
value: PROCESS_DPI_AWARENESS,
|
||||
) -> HRESULT;
|
||||
type SetProcessDpiAwarenessContext = unsafe extern "system" fn (
|
||||
value: DPI_AWARENESS_CONTEXT,
|
||||
) -> BOOL;
|
||||
type GetDpiForWindow = unsafe extern "system" fn (hwnd: HWND) -> UINT;
|
||||
type GetDpiForMonitor = unsafe extern "system" fn (
|
||||
hmonitor: HMONITOR,
|
||||
dpi_type: MONITOR_DPI_TYPE,
|
||||
dpi_x: *mut UINT,
|
||||
dpi_y: *mut UINT,
|
||||
) -> HRESULT;
|
||||
type EnableNonClientDpiScaling = unsafe extern "system" fn (hwnd: HWND) -> BOOL;
|
||||
|
||||
// Helper function to dynamically load function pointer.
|
||||
// `library` and `function` must be zero-terminated.
|
||||
fn get_function_impl(library: &str, function: &str) -> Option<*const c_void> {
|
||||
assert_eq!(library.chars().last(), Some('\0'));
|
||||
assert_eq!(function.chars().last(), Some('\0'));
|
||||
|
||||
// Library names we will use are ASCII so we can use the A version to avoid string conversion.
|
||||
let module = unsafe { LoadLibraryA(library.as_ptr() as LPCSTR) };
|
||||
if module.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let function_ptr = unsafe { GetProcAddress(module, function.as_ptr() as LPCSTR) };
|
||||
if function_ptr.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(function_ptr as _)
|
||||
}
|
||||
|
||||
macro_rules! get_function {
|
||||
($lib:expr, $func:ident) => {
|
||||
get_function_impl(concat!($lib, '\0'), concat!(stringify!($func), '\0'))
|
||||
.map(|f| unsafe { mem::transmute::<*const _, $func>(f) })
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref GET_DPI_FOR_WINDOW: Option<GetDpiForWindow> = get_function!(
|
||||
"user32.dll",
|
||||
GetDpiForWindow
|
||||
);
|
||||
static ref GET_DPI_FOR_MONITOR: Option<GetDpiForMonitor> = get_function!(
|
||||
"shcore.dll",
|
||||
GetDpiForMonitor
|
||||
);
|
||||
static ref ENABLE_NON_CLIENT_DPI_SCALING: Option<EnableNonClientDpiScaling> = get_function!(
|
||||
"user32.dll",
|
||||
EnableNonClientDpiScaling
|
||||
);
|
||||
}
|
||||
|
||||
pub fn become_dpi_aware(enable: bool) {
|
||||
if !enable { return; }
|
||||
static ENABLE_DPI_AWARENESS: Once = ONCE_INIT;
|
||||
ENABLE_DPI_AWARENESS.call_once(|| { unsafe {
|
||||
if let Some(SetProcessDpiAwarenessContext) = get_function!(
|
||||
"user32.dll",
|
||||
SetProcessDpiAwarenessContext
|
||||
) {
|
||||
// We are on Windows 10 Anniversary Update (1607) or later.
|
||||
if SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
|
||||
== FALSE {
|
||||
// V2 only works with Windows 10 Creators Update (1703). Try using the older
|
||||
// V1 if we can't set V2.
|
||||
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
|
||||
}
|
||||
} else if let Some(SetProcessDpiAwareness) = get_function!(
|
||||
"shcore.dll",
|
||||
SetProcessDpiAwareness
|
||||
) {
|
||||
// We are on Windows 8.1 or later.
|
||||
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
|
||||
} else if let Some(SetProcessDPIAware) = get_function!(
|
||||
"user32.dll",
|
||||
SetProcessDPIAware
|
||||
) {
|
||||
// We are on Vista or later.
|
||||
SetProcessDPIAware();
|
||||
}
|
||||
} });
|
||||
}
|
||||
|
||||
pub fn enable_non_client_dpi_scaling(hwnd: HWND) {
|
||||
unsafe {
|
||||
if let Some(EnableNonClientDpiScaling) = *ENABLE_NON_CLIENT_DPI_SCALING {
|
||||
EnableNonClientDpiScaling(hwnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_monitor_dpi(hmonitor: HMONITOR) -> Option<u32> {
|
||||
unsafe {
|
||||
if let Some(GetDpiForMonitor) = *GET_DPI_FOR_MONITOR {
|
||||
// We are on Windows 8.1 or later.
|
||||
let mut dpi_x = 0;
|
||||
let mut dpi_y = 0;
|
||||
if GetDpiForMonitor(hmonitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) == S_OK {
|
||||
// MSDN says that "the values of *dpiX and *dpiY are identical. You only need to
|
||||
// record one of the values to determine the DPI and respond appropriately".
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx
|
||||
return Some(dpi_x as u32)
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub const BASE_DPI: u32 = 96;
|
||||
pub fn dpi_to_scale_factor(dpi: u32) -> f64 {
|
||||
dpi as f64 / BASE_DPI as f64
|
||||
}
|
||||
|
||||
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) {
|
||||
0 => BASE_DPI, // 0 is returned if hwnd is invalid
|
||||
dpi => dpi as u32,
|
||||
}
|
||||
} else if let Some(GetDpiForMonitor) = *GET_DPI_FOR_MONITOR {
|
||||
// We are on Windows 8.1 or later.
|
||||
let monitor = winuser::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||
if monitor.is_null() {
|
||||
return BASE_DPI;
|
||||
}
|
||||
|
||||
let mut dpi_x = 0;
|
||||
let mut dpi_y = 0;
|
||||
if GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) == S_OK {
|
||||
dpi_x as u32
|
||||
} else {
|
||||
BASE_DPI
|
||||
}
|
||||
} else {
|
||||
// We are on Vista or later.
|
||||
if winuser::IsProcessDPIAware() != FALSE {
|
||||
// If the process is DPI aware, then scaling must be handled by the application using
|
||||
// this DPI value.
|
||||
GetDeviceCaps(hdc, LOGPIXELSX) as u32
|
||||
} else {
|
||||
// If the process is DPI unaware, then scaling is performed by the OS; we thus return
|
||||
// 96 (scale factor 1.0) to prevent the window from being re-scaled by both the
|
||||
// application and the WM.
|
||||
BASE_DPI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_hwnd_scale_factor(hwnd: HWND) -> f64 {
|
||||
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,
|
||||
};
|
||||
@@ -45,8 +45,8 @@ pub fn vkey_to_winit_vkey(vkey: c_int) -> Option<VirtualKeyCode> {
|
||||
winuser::VK_RSHIFT => Some(VirtualKeyCode::RShift),
|
||||
winuser::VK_LCONTROL => Some(VirtualKeyCode::LControl),
|
||||
winuser::VK_RCONTROL => Some(VirtualKeyCode::RControl),
|
||||
winuser::VK_LMENU => Some(VirtualKeyCode::LMenu),
|
||||
winuser::VK_RMENU => Some(VirtualKeyCode::RMenu),
|
||||
winuser::VK_LMENU => Some(VirtualKeyCode::LAlt),
|
||||
winuser::VK_RMENU => Some(VirtualKeyCode::RAlt),
|
||||
winuser::VK_PAUSE => Some(VirtualKeyCode::Pause),
|
||||
winuser::VK_CAPITAL => Some(VirtualKeyCode::Capital),
|
||||
winuser::VK_KANA => Some(VirtualKeyCode::Kana),
|
||||
@@ -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),
|
||||
|
||||
@@ -12,42 +12,56 @@
|
||||
//! The closure passed to the `execute_in_thread` method takes an `Inserter` that you can use to
|
||||
//! add a `WindowState` entry to a list of window to be used by the callback.
|
||||
|
||||
use std::{mem, ptr, thread};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsString;
|
||||
use std::mem;
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
use std::os::windows::io::AsRawHandle;
|
||||
use std::ptr;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Barrier;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::Condvar;
|
||||
use std::thread;
|
||||
use std::sync::{Arc, Barrier, mpsc, Mutex};
|
||||
|
||||
use winapi::shared::minwindef::{LOWORD, HIWORD, DWORD, WPARAM, LPARAM, INT, UINT, LRESULT, MAX_PATH};
|
||||
use winapi::ctypes::c_int;
|
||||
use winapi::shared::minwindef::{
|
||||
BOOL,
|
||||
DWORD,
|
||||
HIWORD,
|
||||
INT,
|
||||
LOWORD,
|
||||
LPARAM,
|
||||
LRESULT,
|
||||
UINT,
|
||||
WPARAM,
|
||||
};
|
||||
use winapi::shared::windef::{HWND, POINT, RECT};
|
||||
use winapi::shared::windowsx;
|
||||
use winapi::um::{winuser, shellapi, processthreadsapi};
|
||||
use winapi::um::winnt::{LONG, SHORT};
|
||||
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 events::DeviceEvent;
|
||||
use {
|
||||
ControlFlow,
|
||||
Event,
|
||||
EventsLoopClosed,
|
||||
KeyboardInput,
|
||||
LogicalPosition,
|
||||
LogicalSize,
|
||||
PhysicalSize,
|
||||
WindowEvent,
|
||||
WindowId as SuperWindowId,
|
||||
};
|
||||
use events::{DeviceEvent, Touch, TouchPhase};
|
||||
use platform::platform::{event, Cursor, WindowId, DEVICE_ID, wrap_device_id, util};
|
||||
use platform::platform::dpi::{
|
||||
become_dpi_aware,
|
||||
dpi_to_scale_factor,
|
||||
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::raw_input::*;
|
||||
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;
|
||||
|
||||
use ControlFlow;
|
||||
use CursorState;
|
||||
use Event;
|
||||
use EventsLoopClosed;
|
||||
use KeyboardInput;
|
||||
use WindowAttributes;
|
||||
use WindowEvent;
|
||||
use WindowId as SuperWindowId;
|
||||
use events::{Touch, TouchPhase};
|
||||
|
||||
/// Contains saved window info for switching between fullscreen
|
||||
#[derive(Clone)]
|
||||
pub struct SavedWindowInfo {
|
||||
@@ -57,6 +71,11 @@ pub struct SavedWindowInfo {
|
||||
pub ex_style: LONG,
|
||||
/// Window position and size
|
||||
pub rect: RECT,
|
||||
// Since a window can be fullscreened to a different monitor, a DPI change can be triggered. This could result in
|
||||
// the window being automitcally resized to smaller/larger than it was supposed to be restored to, so we thus must
|
||||
// check if the post-fullscreen DPI matches the pre-fullscreen DPI.
|
||||
pub is_fullscreen: bool,
|
||||
pub dpi_factor: Option<f64>,
|
||||
}
|
||||
|
||||
/// Contains information about states and the window that the callback is going to use.
|
||||
@@ -64,14 +83,38 @@ pub struct SavedWindowInfo {
|
||||
pub struct WindowState {
|
||||
/// Cursor to set at the next `WM_SETCURSOR` event received.
|
||||
pub cursor: Cursor,
|
||||
/// Cursor state to set at the next `WM_SETCURSOR` event received.
|
||||
pub cursor_state: CursorState,
|
||||
pub cursor_grabbed: bool,
|
||||
pub cursor_hidden: bool,
|
||||
/// Used by `WM_GETMINMAXINFO`.
|
||||
pub attributes: WindowAttributes,
|
||||
pub max_size: Option<PhysicalSize>,
|
||||
pub min_size: Option<PhysicalSize>,
|
||||
/// Will contain `true` if the mouse is hovering the window.
|
||||
pub mouse_in_window: bool,
|
||||
/// Saved window info for fullscreen restored
|
||||
pub saved_window_info: Option<SavedWindowInfo>,
|
||||
// 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 {
|
||||
pub fn update_min_max(&mut self, old_dpi_factor: f64, new_dpi_factor: f64) {
|
||||
let scale_factor = new_dpi_factor / old_dpi_factor;
|
||||
let dpi_adjuster = |mut physical_size: PhysicalSize| -> PhysicalSize {
|
||||
physical_size.width *= scale_factor;
|
||||
physical_size.height *= scale_factor;
|
||||
physical_size
|
||||
};
|
||||
self.max_size = self.max_size.map(&dpi_adjuster);
|
||||
self.min_size = self.min_size.map(&dpi_adjuster);
|
||||
}
|
||||
}
|
||||
|
||||
/// Dummy object that allows inserting a window's state.
|
||||
@@ -95,18 +138,18 @@ 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 {
|
||||
pub fn new() -> EventsLoop {
|
||||
Self::with_dpi_awareness(true)
|
||||
}
|
||||
|
||||
pub fn with_dpi_awareness(dpi_aware: bool) -> EventsLoop {
|
||||
become_dpi_aware(dpi_aware);
|
||||
|
||||
// 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.
|
||||
@@ -118,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,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -171,7 +214,6 @@ impl EventsLoop {
|
||||
EventsLoop {
|
||||
thread_id,
|
||||
receiver: rx,
|
||||
win32_block_loop
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,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,
|
||||
@@ -285,26 +307,30 @@ 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,
|
||||
{
|
||||
unsafe {
|
||||
// We are using double-boxing here because it make casting back much easier
|
||||
let boxed = Box::new(function) as Box<FnMut(_)>;
|
||||
let boxed2 = Box::new(boxed);
|
||||
let raw = Box::into_raw(boxed2);
|
||||
// We are using double-boxing here because it make casting back much easier
|
||||
let double_box = Box::new(Box::new(function) as Box<FnMut(_)>);
|
||||
let raw = Box::into_raw(double_box);
|
||||
|
||||
let res = winuser::PostThreadMessageA(
|
||||
let res = unsafe {
|
||||
winuser::PostThreadMessageA(
|
||||
self.thread_id,
|
||||
*EXEC_MSG_ID,
|
||||
raw as *mut () as usize as WPARAM,
|
||||
0,
|
||||
);
|
||||
// PostThreadMessage can only fail if the thread ID is invalid (which shouldn't happen
|
||||
// as the events loop is still alive) or if the queue is full.
|
||||
assert!(res != 0, "PostThreadMessage failed; is the messages queue full?");
|
||||
}
|
||||
)
|
||||
};
|
||||
// PostThreadMessage can only fail if the thread ID is invalid (which shouldn't happen as
|
||||
// the events loop is still alive) or if the queue is full.
|
||||
assert!(res != 0, "PostThreadMessage failed; is the messages queue full?");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,22 +339,29 @@ lazy_static! {
|
||||
// WPARAM and LPARAM are unused.
|
||||
static ref WAKEUP_MSG_ID: u32 = {
|
||||
unsafe {
|
||||
winuser::RegisterWindowMessageA("Winit::WakeupMsg\0".as_ptr() as *const i8)
|
||||
winuser::RegisterWindowMessageA("Winit::WakeupMsg\0".as_ptr() as LPCSTR)
|
||||
}
|
||||
};
|
||||
// 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 {
|
||||
winuser::RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr() as *const i8)
|
||||
winuser::RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr() as LPCSTR)
|
||||
}
|
||||
};
|
||||
// Message sent by a `Window` when it wants to be destroyed by the main thread.
|
||||
// WPARAM and LPARAM are unused.
|
||||
pub static ref DESTROY_MSG_ID: u32 = {
|
||||
unsafe {
|
||||
winuser::RegisterWindowMessageA("Winit::DestroyMsg\0".as_ptr() as *const i8)
|
||||
winuser::RegisterWindowMessageA("Winit::DestroyMsg\0".as_ptr() as LPCSTR)
|
||||
}
|
||||
};
|
||||
// Message sent by a `Window` after creation if it has a DPI != 96.
|
||||
// WPARAM is the the DPI (u32). LOWORD of LPARAM is width, and HIWORD is height.
|
||||
pub static ref INITIAL_DPI_MSG_ID: u32 = {
|
||||
unsafe {
|
||||
winuser::RegisterWindowMessageA("Winit::InitialDpiMsg\0".as_ptr() as LPCSTR)
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -339,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();
|
||||
|
||||
@@ -385,11 +418,42 @@ unsafe fn release_mouse() {
|
||||
//
|
||||
// Returning 0 tells the Win32 API that the message has been processed.
|
||||
// FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary
|
||||
pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
wparam: WPARAM, lparam: LPARAM)
|
||||
-> LRESULT
|
||||
{
|
||||
pub unsafe extern "system" fn callback(
|
||||
window: HWND,
|
||||
msg: UINT,
|
||||
wparam: WPARAM,
|
||||
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)
|
||||
},
|
||||
|
||||
winuser::WM_CLOSE => {
|
||||
use events::WindowEvent::CloseRequested;
|
||||
send_event(Event::WindowEvent {
|
||||
@@ -403,7 +467,10 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
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)),
|
||||
@@ -427,9 +494,14 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
|
||||
let windowpos = lparam as *const winuser::WINDOWPOS;
|
||||
if (*windowpos).flags & winuser::SWP_NOMOVE != winuser::SWP_NOMOVE {
|
||||
let dpi_factor = get_hwnd_scale_factor(window);
|
||||
let logical_position = LogicalPosition::from_physical(
|
||||
((*windowpos).x, (*windowpos).y),
|
||||
dpi_factor,
|
||||
);
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: Moved((*windowpos).x, (*windowpos).y),
|
||||
event: Moved(logical_position),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -448,29 +520,14 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
let cstash = context_stash.as_mut().unwrap();
|
||||
|
||||
let dpi_factor = get_hwnd_scale_factor(window);
|
||||
let logical_size = LogicalSize::from_physical((w, h), dpi_factor);
|
||||
let event = Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: Resized(w, h),
|
||||
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
|
||||
},
|
||||
@@ -528,10 +585,12 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
|
||||
let x = windowsx::GET_X_LPARAM(lparam) as f64;
|
||||
let y = windowsx::GET_Y_LPARAM(lparam) as f64;
|
||||
let dpi_factor = get_hwnd_scale_factor(window);
|
||||
let position = LogicalPosition::from_physical((x, y), dpi_factor);
|
||||
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event::get_key_mods() },
|
||||
event: CursorMoved { device_id: DEVICE_ID, position, modifiers: event::get_key_mods() },
|
||||
});
|
||||
|
||||
0
|
||||
@@ -869,10 +928,17 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
let mut inputs = Vec::with_capacity( pcount );
|
||||
inputs.set_len( pcount );
|
||||
let htouch = lparam as winuser::HTOUCHINPUT;
|
||||
if winuser::GetTouchInputInfo( htouch, pcount as UINT,
|
||||
inputs.as_mut_ptr(),
|
||||
mem::size_of::<winuser::TOUCHINPUT>() as INT ) > 0 {
|
||||
if winuser::GetTouchInputInfo(
|
||||
htouch,
|
||||
pcount as UINT,
|
||||
inputs.as_mut_ptr(),
|
||||
mem::size_of::<winuser::TOUCHINPUT>() as INT,
|
||||
) > 0 {
|
||||
let dpi_factor = get_hwnd_scale_factor(window);
|
||||
for input in &inputs {
|
||||
let x = (input.x as f64) / 100f64;
|
||||
let y = (input.y as f64) / 100f64;
|
||||
let location = LogicalPosition::from_physical((x, y), dpi_factor);
|
||||
send_event( Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: WindowEvent::Touch(Touch {
|
||||
@@ -886,8 +952,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
} else {
|
||||
continue;
|
||||
},
|
||||
location: ((input.x as f64) / 100f64,
|
||||
(input.y as f64) / 100f64),
|
||||
location,
|
||||
id: input.dwID as u64,
|
||||
device_id: DEVICE_ID,
|
||||
})
|
||||
@@ -907,11 +972,14 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
|
||||
let x = windowsx::GET_X_LPARAM(lparam) as f64;
|
||||
let y = windowsx::GET_Y_LPARAM(lparam) as f64;
|
||||
let dpi_factor = get_hwnd_scale_factor(window);
|
||||
let position = LogicalPosition::from_physical((x, y), dpi_factor);
|
||||
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event::get_key_mods() },
|
||||
event: CursorMoved { device_id: DEVICE_ID, position, modifiers: event::get_key_mods() },
|
||||
});
|
||||
|
||||
0
|
||||
},
|
||||
|
||||
@@ -954,24 +1022,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
},
|
||||
|
||||
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
|
||||
},
|
||||
|
||||
@@ -985,18 +1036,15 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
if let Some(wstash) = cstash.windows.get(&window) {
|
||||
let window_state = wstash.lock().unwrap();
|
||||
|
||||
if window_state.attributes.min_dimensions.is_some() ||
|
||||
window_state.attributes.max_dimensions.is_some() {
|
||||
|
||||
if window_state.min_size.is_some() || window_state.max_size.is_some() {
|
||||
let style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD;
|
||||
let ex_style = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD;
|
||||
|
||||
if let Some(min_dimensions) = window_state.attributes.min_dimensions {
|
||||
let (width, height) = adjust_size(min_dimensions, style, ex_style);
|
||||
if let Some(min_size) = window_state.min_size {
|
||||
let (width, height) = adjust_size(min_size, style, ex_style);
|
||||
(*mmi).ptMinTrackSize = POINT { x: width as i32, y: height as i32 };
|
||||
}
|
||||
if let Some(max_dimensions) = window_state.attributes.max_dimensions {
|
||||
let (width, height) = adjust_size(max_dimensions, style, ex_style);
|
||||
if let Some(max_size) = window_state.max_size {
|
||||
let (width, height) = adjust_size(max_size, style, ex_style);
|
||||
(*mmi).ptMaxTrackSize = POINT { x: width as i32, y: height as i32 };
|
||||
}
|
||||
}
|
||||
@@ -1007,10 +1055,115 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
0
|
||||
},
|
||||
|
||||
// Only sent on Windows 8.1 or newer. On Windows 7 and older user has to log out to change
|
||||
// DPI, therefore all applications are closed while DPI is changing.
|
||||
winuser::WM_DPICHANGED => {
|
||||
use events::WindowEvent::HiDpiFactorChanged;
|
||||
|
||||
// This message actually provides two DPI values - x and y. However MSDN says that
|
||||
// "you only need to use either the X-axis or the Y-axis value when scaling your
|
||||
// application since they are the same".
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/dn312083(v=vs.85).aspx
|
||||
let new_dpi_x = u32::from(LOWORD(wparam as DWORD));
|
||||
let new_dpi_factor = dpi_to_scale_factor(new_dpi_x);
|
||||
|
||||
let suppress_resize = CONTEXT_STASH.with(|context_stash| {
|
||||
context_stash
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.and_then(|cstash| cstash.windows.get(&window))
|
||||
.map(|window_state_mutex| {
|
||||
let mut window_state = window_state_mutex.lock().unwrap();
|
||||
let suppress_resize = window_state.saved_window_info
|
||||
.as_mut()
|
||||
.map(|saved_window_info| {
|
||||
let dpi_changed = if !saved_window_info.is_fullscreen {
|
||||
saved_window_info.dpi_factor.take() != Some(new_dpi_factor)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
!dpi_changed || saved_window_info.is_fullscreen
|
||||
})
|
||||
.unwrap_or(false);
|
||||
// Now we adjust the min/max dimensions for the new DPI.
|
||||
if !suppress_resize {
|
||||
let old_dpi_factor = window_state.dpi_factor;
|
||||
window_state.update_min_max(old_dpi_factor, new_dpi_factor);
|
||||
}
|
||||
window_state.dpi_factor = new_dpi_factor;
|
||||
suppress_resize
|
||||
})
|
||||
.unwrap_or(false)
|
||||
});
|
||||
|
||||
// This prevents us from re-applying DPI adjustment to the restored size after exiting
|
||||
// fullscreen (the restored size is already DPI adjusted).
|
||||
if !suppress_resize {
|
||||
// Resize window to the size suggested by Windows.
|
||||
let rect = &*(lparam as *const RECT);
|
||||
winuser::SetWindowPos(
|
||||
window,
|
||||
ptr::null_mut(),
|
||||
rect.left,
|
||||
rect.top,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top,
|
||||
winuser::SWP_NOZORDER | winuser::SWP_NOACTIVATE,
|
||||
);
|
||||
}
|
||||
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: HiDpiFactorChanged(new_dpi_factor),
|
||||
});
|
||||
|
||||
0
|
||||
},
|
||||
|
||||
_ => {
|
||||
if msg == *DESTROY_MSG_ID {
|
||||
winuser::DestroyWindow(window);
|
||||
0
|
||||
} else if msg == *INITIAL_DPI_MSG_ID {
|
||||
use events::WindowEvent::HiDpiFactorChanged;
|
||||
let scale_factor = dpi_to_scale_factor(wparam as u32);
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: HiDpiFactorChanged(scale_factor),
|
||||
});
|
||||
// Automatically resize for actual DPI
|
||||
let width = LOWORD(lparam as DWORD) as u32;
|
||||
let height = HIWORD(lparam as DWORD) as u32;
|
||||
let (adjusted_width, adjusted_height): (u32, u32) = PhysicalSize::from_logical(
|
||||
(width, height),
|
||||
scale_factor,
|
||||
).into();
|
||||
// We're not done yet! `SetWindowPos` needs the window size, not the client area size.
|
||||
let mut rect = RECT {
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: adjusted_height as LONG,
|
||||
right: adjusted_width as LONG,
|
||||
};
|
||||
let dw_style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD;
|
||||
let b_menu = !winuser::GetMenu(window).is_null() as BOOL;
|
||||
let dw_style_ex = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD;
|
||||
winuser::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
|
||||
let outer_x = (rect.right - rect.left).abs() as c_int;
|
||||
let outer_y = (rect.top - rect.bottom).abs() as c_int;
|
||||
winuser::SetWindowPos(
|
||||
window,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
0,
|
||||
outer_x,
|
||||
outer_y,
|
||||
winuser::SWP_NOMOVE
|
||||
| winuser::SWP_NOREPOSITION
|
||||
| winuser::SWP_NOZORDER
|
||||
| winuser::SWP_NOACTIVATE,
|
||||
);
|
||||
0
|
||||
} else {
|
||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -11,13 +11,14 @@ pub use self::window::Window;
|
||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub parent: Option<HWND>,
|
||||
pub taskbar_icon: Option<::Icon>,
|
||||
pub no_redirection_bitmap: bool,
|
||||
}
|
||||
|
||||
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 {}
|
||||
@@ -47,6 +48,8 @@ pub struct WindowId(HWND);
|
||||
unsafe impl Send for WindowId {}
|
||||
unsafe impl Sync for WindowId {}
|
||||
|
||||
mod dpi;
|
||||
mod drop_handler;
|
||||
mod event;
|
||||
mod events_loop;
|
||||
mod icon;
|
||||
|
||||
@@ -1,38 +1,33 @@
|
||||
use winapi::ctypes::wchar_t;
|
||||
use winapi::shared::minwindef::{DWORD, LPARAM, BOOL, TRUE};
|
||||
use winapi::shared::windef::{HMONITOR, HDC, LPRECT, HWND};
|
||||
use winapi::shared::minwindef::{BOOL, DWORD, LPARAM, TRUE};
|
||||
use winapi::shared::windef::{HDC, HMONITOR, HWND, LPRECT, POINT};
|
||||
use winapi::um::winnt::LONG;
|
||||
use winapi::um::winuser;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::{mem, ptr};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use super::{EventsLoop, util};
|
||||
use dpi::{PhysicalPosition, PhysicalSize};
|
||||
use platform::platform::dpi::{dpi_to_scale_factor, get_monitor_dpi};
|
||||
use platform::platform::window::Window;
|
||||
|
||||
/// Win32 implementation of the main `MonitorId` object.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MonitorId {
|
||||
/// The system name of the adapter.
|
||||
adapter_name: [wchar_t; 32],
|
||||
|
||||
/// Monitor handle.
|
||||
hmonitor: HMonitor,
|
||||
|
||||
/// The system name of the monitor.
|
||||
monitor_name: String,
|
||||
|
||||
/// True if this is the primary monitor.
|
||||
primary: bool,
|
||||
|
||||
/// The position of the monitor in pixels on the desktop.
|
||||
///
|
||||
/// A window that is positioned at these coordinates will overlap the monitor.
|
||||
position: (i32, i32),
|
||||
|
||||
/// The current resolution in pixels on the monitor.
|
||||
dimensions: (u32, u32),
|
||||
|
||||
/// DPI scaling factor.
|
||||
hidpi_factor: f32,
|
||||
/// DPI scale factor.
|
||||
hidpi_factor: f64,
|
||||
}
|
||||
|
||||
// Send is not implemented for HMONITOR, we have to wrap it and implement it manually.
|
||||
@@ -44,122 +39,135 @@ struct HMonitor(HMONITOR);
|
||||
|
||||
unsafe impl Send for HMonitor {}
|
||||
|
||||
unsafe extern "system" fn monitor_enum_proc(hmonitor: HMONITOR, _: HDC, place: LPRECT, data: LPARAM) -> BOOL {
|
||||
unsafe extern "system" fn monitor_enum_proc(
|
||||
hmonitor: HMONITOR,
|
||||
_hdc: HDC,
|
||||
_place: LPRECT,
|
||||
data: LPARAM,
|
||||
) -> BOOL {
|
||||
let monitors = data as *mut VecDeque<MonitorId>;
|
||||
(*monitors).push_back(MonitorId::from_hmonitor(hmonitor));
|
||||
TRUE // continue enumeration
|
||||
}
|
||||
|
||||
let place = *place;
|
||||
let position = (place.left as i32, place.top as i32);
|
||||
let dimensions = ((place.right - place.left) as u32, (place.bottom - place.top) as u32);
|
||||
|
||||
let mut monitor_info: winuser::MONITORINFOEXW = mem::zeroed();
|
||||
monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
|
||||
if winuser::GetMonitorInfoW(hmonitor, &mut monitor_info as *mut winuser::MONITORINFOEXW as *mut winuser::MONITORINFO) == 0 {
|
||||
// Some error occurred, just skip this monitor and go on.
|
||||
return TRUE;
|
||||
pub fn get_available_monitors() -> VecDeque<MonitorId> {
|
||||
let mut monitors: VecDeque<MonitorId> = VecDeque::new();
|
||||
unsafe {
|
||||
winuser::EnumDisplayMonitors(
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
Some(monitor_enum_proc),
|
||||
&mut monitors as *mut _ as LPARAM,
|
||||
);
|
||||
}
|
||||
monitors
|
||||
}
|
||||
|
||||
(*monitors).push_back(MonitorId {
|
||||
adapter_name: monitor_info.szDevice,
|
||||
hmonitor: HMonitor(hmonitor),
|
||||
monitor_name: util::wchar_to_string(&monitor_info.szDevice),
|
||||
primary: monitor_info.dwFlags & winuser::MONITORINFOF_PRIMARY != 0,
|
||||
position,
|
||||
dimensions,
|
||||
hidpi_factor: 1.0,
|
||||
});
|
||||
|
||||
// TRUE means continue enumeration.
|
||||
TRUE
|
||||
pub fn get_primary_monitor() -> MonitorId {
|
||||
const ORIGIN: POINT = POINT { x: 0, y: 0 };
|
||||
let hmonitor = unsafe {
|
||||
winuser::MonitorFromPoint(ORIGIN, winuser::MONITOR_DEFAULTTOPRIMARY)
|
||||
};
|
||||
MonitorId::from_hmonitor(hmonitor)
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
// TODO: Investigate opportunities for caching
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
unsafe {
|
||||
let mut result: VecDeque<MonitorId> = VecDeque::new();
|
||||
winuser::EnumDisplayMonitors(ptr::null_mut(), ptr::null_mut(), Some(monitor_enum_proc), &mut result as *mut _ as LPARAM);
|
||||
result
|
||||
}
|
||||
get_available_monitors()
|
||||
}
|
||||
|
||||
pub fn get_current_monitor(handle: HWND) -> MonitorId {
|
||||
unsafe {
|
||||
let mut monitor_info: winuser::MONITORINFOEXW = mem::zeroed();
|
||||
monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
|
||||
|
||||
let hmonitor = winuser::MonitorFromWindow(handle, winuser::MONITOR_DEFAULTTONEAREST);
|
||||
|
||||
winuser::GetMonitorInfoW(
|
||||
hmonitor,
|
||||
&mut monitor_info as *mut winuser::MONITORINFOEXW as *mut winuser::MONITORINFO,
|
||||
);
|
||||
|
||||
let place = monitor_info.rcMonitor;
|
||||
let position = (place.left as i32, place.top as i32);
|
||||
let dimensions = (
|
||||
(place.right - place.left) as u32,
|
||||
(place.bottom - place.top) as u32,
|
||||
);
|
||||
|
||||
MonitorId {
|
||||
adapter_name: monitor_info.szDevice,
|
||||
hmonitor: super::monitor::HMonitor(hmonitor),
|
||||
monitor_name: util::wchar_to_string(&monitor_info.szDevice),
|
||||
primary: monitor_info.dwFlags & winuser::MONITORINFOF_PRIMARY != 0,
|
||||
position,
|
||||
dimensions,
|
||||
hidpi_factor: 1.0,
|
||||
}
|
||||
}
|
||||
pub fn get_current_monitor(hwnd: HWND) -> MonitorId {
|
||||
let hmonitor = unsafe {
|
||||
winuser::MonitorFromWindow(hwnd, winuser::MONITOR_DEFAULTTONEAREST)
|
||||
};
|
||||
MonitorId::from_hmonitor(hmonitor)
|
||||
}
|
||||
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
// we simply get all available monitors and return the one with the `MONITORINFOF_PRIMARY` flag
|
||||
// TODO: it is possible to query the win32 API for the primary monitor, this should be done
|
||||
// instead
|
||||
for monitor in self.get_available_monitors().into_iter() {
|
||||
if monitor.primary {
|
||||
return monitor;
|
||||
}
|
||||
}
|
||||
get_primary_monitor()
|
||||
}
|
||||
}
|
||||
|
||||
panic!("Failed to find the primary monitor")
|
||||
impl Window {
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
get_available_monitors()
|
||||
}
|
||||
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
get_primary_monitor()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_monitor_info(hmonitor: HMONITOR) -> Result<winuser::MONITORINFOEXW, util::WinError> {
|
||||
let mut monitor_info: winuser::MONITORINFOEXW = unsafe { mem::uninitialized() };
|
||||
monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
|
||||
let status = unsafe {
|
||||
winuser::GetMonitorInfoW(
|
||||
hmonitor,
|
||||
&mut monitor_info as *mut winuser::MONITORINFOEXW as *mut winuser::MONITORINFO,
|
||||
)
|
||||
};
|
||||
if status == 0 {
|
||||
Err(util::WinError::from_last_error())
|
||||
} else {
|
||||
Ok(monitor_info)
|
||||
}
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
/// See the docs if the crate root file.
|
||||
pub(crate) fn from_hmonitor(hmonitor: HMONITOR) -> Self {
|
||||
let monitor_info = get_monitor_info(hmonitor).expect("`GetMonitorInfoW` failed");
|
||||
let place = monitor_info.rcMonitor;
|
||||
let dimensions = (
|
||||
(place.right - place.left) as u32,
|
||||
(place.bottom - place.top) as u32,
|
||||
);
|
||||
MonitorId {
|
||||
hmonitor: HMonitor(hmonitor),
|
||||
monitor_name: util::wchar_ptr_to_string(monitor_info.szDevice.as_ptr()),
|
||||
primary: util::has_flag(monitor_info.dwFlags, winuser::MONITORINFOF_PRIMARY),
|
||||
position: (place.left as i32, place.top as i32),
|
||||
dimensions,
|
||||
hidpi_factor: dpi_to_scale_factor(get_monitor_dpi(hmonitor).unwrap_or(96)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn contains_point(&self, point: &POINT) -> bool {
|
||||
let left = self.position.0 as LONG;
|
||||
let right = left + self.dimensions.0 as LONG;
|
||||
let top = self.position.1 as LONG;
|
||||
let bottom = top + self.dimensions.1 as LONG;
|
||||
point.x >= left && point.x <= right && point.y >= top && point.y <= bottom
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
Some(self.monitor_name.clone())
|
||||
}
|
||||
|
||||
/// See the docs of the crate root file.
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> String {
|
||||
self.monitor_name.clone()
|
||||
}
|
||||
|
||||
/// See the docs of the crate root file.
|
||||
#[inline]
|
||||
pub fn get_hmonitor(&self) -> HMONITOR {
|
||||
self.hmonitor.0
|
||||
}
|
||||
|
||||
/// See the docs of the crate root file.
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
// TODO: retrieve the dimensions every time this is called
|
||||
self.dimensions
|
||||
}
|
||||
|
||||
/// A window that is positioned at these coordinates will overlap the monitor.
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
self.position
|
||||
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||
self.dimensions.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
pub fn get_position(&self) -> PhysicalPosition {
|
||||
self.position.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
self.hidpi_factor
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::{self, mem, ptr};
|
||||
use std::{self, mem, ptr, slice};
|
||||
use std::ops::BitAnd;
|
||||
|
||||
use winapi::ctypes::wchar_t;
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
use winapi::shared::windef::RECT;
|
||||
use winapi::shared::minwindef::{BOOL, DWORD};
|
||||
use winapi::shared::windef::{HWND, POINT, RECT};
|
||||
use winapi::um::errhandlingapi::GetLastError;
|
||||
use winapi::um::winbase::{
|
||||
FormatMessageW,
|
||||
@@ -19,6 +19,7 @@ use winapi::um::winnt::{
|
||||
LANG_NEUTRAL,
|
||||
SUBLANG_DEFAULT,
|
||||
};
|
||||
use winapi::um::winuser;
|
||||
|
||||
pub fn has_flag<T>(bitset: T, flag: T) -> bool
|
||||
where T:
|
||||
@@ -28,9 +29,30 @@ where T:
|
||||
}
|
||||
|
||||
pub fn wchar_to_string(wchar: &[wchar_t]) -> String {
|
||||
String::from_utf16_lossy(wchar)
|
||||
.trim_right_matches(0 as char)
|
||||
.to_string()
|
||||
String::from_utf16_lossy(wchar).to_string()
|
||||
}
|
||||
|
||||
pub fn wchar_ptr_to_string(wchar: *const wchar_t) -> String {
|
||||
let len = unsafe { lstrlenW(wchar) } as usize;
|
||||
let wchar_slice = unsafe { slice::from_raw_parts(wchar, len) };
|
||||
wchar_to_string(wchar_slice)
|
||||
}
|
||||
|
||||
pub unsafe fn status_map<T, F: FnMut(&mut T) -> BOOL>(mut fun: F) -> Option<T> {
|
||||
let mut data: T = mem::uninitialized();
|
||||
if fun(&mut data) != 0 {
|
||||
Some(data)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cursor_pos() -> Option<POINT> {
|
||||
unsafe { status_map(|cursor_pos| winuser::GetCursorPos(cursor_pos)) }
|
||||
}
|
||||
|
||||
pub fn get_window_rect(hwnd: HWND) -> Option<RECT> {
|
||||
unsafe { status_map(|rect| winuser::GetWindowRect(hwnd, rect)) }
|
||||
}
|
||||
|
||||
// This won't be needed anymore if we just add a derive to winapi.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
228
src/window.rs
228
src/window.rs
@@ -1,16 +1,19 @@
|
||||
use std::collections::vec_deque::IntoIter as VecDequeIter;
|
||||
|
||||
use CreationError;
|
||||
use CursorState;
|
||||
use EventsLoop;
|
||||
use Icon;
|
||||
use MouseCursor;
|
||||
use Window;
|
||||
use WindowBuilder;
|
||||
use WindowId;
|
||||
|
||||
use libc;
|
||||
use platform;
|
||||
use {
|
||||
CreationError,
|
||||
EventsLoop,
|
||||
Icon,
|
||||
LogicalPosition,
|
||||
LogicalSize,
|
||||
MouseCursor,
|
||||
PhysicalPosition,
|
||||
PhysicalSize,
|
||||
platform,
|
||||
Window,
|
||||
WindowBuilder,
|
||||
WindowId,
|
||||
};
|
||||
|
||||
impl WindowBuilder {
|
||||
/// Initializes a new `WindowBuilder` with default values.
|
||||
@@ -23,29 +26,23 @@ impl WindowBuilder {
|
||||
}
|
||||
|
||||
/// Requests the window to be of specific dimensions.
|
||||
///
|
||||
/// Width and height are in pixels.
|
||||
#[inline]
|
||||
pub fn with_dimensions(mut self, width: u32, height: u32) -> WindowBuilder {
|
||||
self.window.dimensions = Some((width, height));
|
||||
pub fn with_dimensions(mut self, size: LogicalSize) -> WindowBuilder {
|
||||
self.window.dimensions = Some(size);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a minimum dimension size for the window
|
||||
///
|
||||
/// Width and height are in pixels.
|
||||
#[inline]
|
||||
pub fn with_min_dimensions(mut self, width: u32, height: u32) -> WindowBuilder {
|
||||
self.window.min_dimensions = Some((width, height));
|
||||
pub fn with_min_dimensions(mut self, min_size: LogicalSize) -> WindowBuilder {
|
||||
self.window.min_dimensions = Some(min_size);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a maximum dimension size for the window
|
||||
///
|
||||
/// Width and height are in pixels.
|
||||
#[inline]
|
||||
pub fn with_max_dimensions(mut self, width: u32, height: u32) -> WindowBuilder {
|
||||
self.window.max_dimensions = Some((width, height));
|
||||
pub fn with_max_dimensions(mut self, max_size: LogicalSize) -> WindowBuilder {
|
||||
self.window.max_dimensions = Some(max_size);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -144,14 +141,15 @@ impl WindowBuilder {
|
||||
///
|
||||
/// Error should be very rare and only occur in case of permission denied, incompatible system,
|
||||
/// out of memory, etc.
|
||||
#[inline]
|
||||
pub fn build(mut self, events_loop: &EventsLoop) -> Result<Window, CreationError> {
|
||||
self.window.dimensions = Some(self.window.dimensions.unwrap_or_else(|| {
|
||||
if let Some(ref monitor) = self.window.fullscreen {
|
||||
// resizing the window to the dimensions of the monitor when fullscreen
|
||||
monitor.get_dimensions()
|
||||
LogicalSize::from_physical(monitor.get_dimensions(), 1.0)
|
||||
} else {
|
||||
// default dimensions
|
||||
(1024, 768)
|
||||
(1024, 768).into()
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -219,7 +217,7 @@ impl Window {
|
||||
///
|
||||
/// Returns `None` if the window no longer exists.
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||
self.window.get_position()
|
||||
}
|
||||
|
||||
@@ -228,7 +226,7 @@ impl Window {
|
||||
///
|
||||
/// The same conditions that apply to `get_position` apply to this method.
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||
self.window.get_inner_position()
|
||||
}
|
||||
|
||||
@@ -238,61 +236,30 @@ impl Window {
|
||||
///
|
||||
/// This is a no-op if the window has already been closed.
|
||||
#[inline]
|
||||
pub fn set_position(&self, x: i32, y: i32) {
|
||||
self.window.set_position(x, y)
|
||||
pub fn set_position(&self, position: LogicalPosition) {
|
||||
self.window.set_position(position)
|
||||
}
|
||||
|
||||
/// Returns the size in pixels of the client area of the window.
|
||||
/// Returns the logical size of the window's client area.
|
||||
///
|
||||
/// The client area is the content of the window, excluding the title bar and borders.
|
||||
/// These are the dimensions that need to be supplied to `glViewport`.
|
||||
///
|
||||
/// Converting the returned `LogicalSize` to `PhysicalSize` produces the size your framebuffer should be.
|
||||
///
|
||||
/// Returns `None` if the window no longer exists.
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||
self.window.get_inner_size()
|
||||
}
|
||||
|
||||
/// Returns the size in points of the client area of the window.
|
||||
/// Returns the logical size of the entire window.
|
||||
///
|
||||
/// The client area is the content of the window, excluding the title bar and borders.
|
||||
/// To get the dimensions of the frame buffer when calling `glViewport`, multiply with hidpi factor.
|
||||
///
|
||||
/// Returns `None` if the window no longer exists.
|
||||
///
|
||||
/// DEPRECATED
|
||||
#[inline]
|
||||
#[deprecated]
|
||||
pub fn get_inner_size_points(&self) -> Option<(u32, u32)> {
|
||||
self.window.get_inner_size().map(|(x, y)| {
|
||||
let hidpi = self.hidpi_factor();
|
||||
((x as f32 / hidpi) as u32, (y as f32 / hidpi) as u32)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the size in pixels of the client area of the window.
|
||||
///
|
||||
/// The client area is the content of the window, excluding the title bar and borders.
|
||||
/// These are the dimensions of the frame buffer, and the dimensions that you should use
|
||||
/// when you call `glViewport`.
|
||||
///
|
||||
/// Returns `None` if the window no longer exists.
|
||||
///
|
||||
/// DEPRECATED
|
||||
#[inline]
|
||||
#[deprecated]
|
||||
pub fn get_inner_size_pixels(&self) -> Option<(u32, u32)> {
|
||||
self.window.get_inner_size()
|
||||
}
|
||||
|
||||
/// Returns the size in pixels of the window.
|
||||
///
|
||||
/// These dimensions include title bar and borders. If you don't want these, you should use
|
||||
/// use `get_inner_size` instead.
|
||||
/// These dimensions include the title bar and borders. If you don't want that (and you usually don't),
|
||||
/// use `get_inner_size` instead.
|
||||
///
|
||||
/// Returns `None` if the window no longer exists.
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||
self.window.get_outer_size()
|
||||
}
|
||||
|
||||
@@ -302,23 +269,19 @@ impl Window {
|
||||
///
|
||||
/// This is a no-op if the window has already been closed.
|
||||
#[inline]
|
||||
pub fn set_inner_size(&self, x: u32, y: u32) {
|
||||
self.window.set_inner_size(x, y)
|
||||
pub fn set_inner_size(&self, size: LogicalSize) {
|
||||
self.window.set_inner_size(size)
|
||||
}
|
||||
|
||||
/// Sets a minimum dimension size for the window.
|
||||
///
|
||||
/// Width and height are in pixels.
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||
self.window.set_min_dimensions(dimensions)
|
||||
}
|
||||
|
||||
/// Sets a maximum dimension size for the window.
|
||||
///
|
||||
/// Width and height are in pixels.
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||
self.window.set_max_dimensions(dimensions)
|
||||
}
|
||||
|
||||
@@ -337,54 +300,61 @@ impl Window {
|
||||
self.window.set_resizable(resizable)
|
||||
}
|
||||
|
||||
/// DEPRECATED. Gets the native platform specific display for this window.
|
||||
/// This is typically only required when integrating with
|
||||
/// other libraries that need this information.
|
||||
#[deprecated]
|
||||
/// Returns the DPI factor that can be used to map logical pixels to physical pixels, and vice versa.
|
||||
///
|
||||
/// 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.
|
||||
/// - **Android:** Always returns 1.0.
|
||||
#[inline]
|
||||
pub unsafe fn platform_display(&self) -> *mut libc::c_void {
|
||||
self.window.platform_display()
|
||||
}
|
||||
|
||||
/// DEPRECATED. Gets the native platform specific window handle. This is
|
||||
/// typically only required when integrating with other libraries
|
||||
/// that need this information.
|
||||
#[deprecated]
|
||||
#[inline]
|
||||
pub unsafe fn platform_window(&self) -> *mut libc::c_void {
|
||||
self.window.platform_window()
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
self.window.get_hidpi_factor()
|
||||
}
|
||||
|
||||
/// Modifies the mouse cursor of the window.
|
||||
/// Has no effect on Android.
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||
self.window.set_cursor(cursor);
|
||||
}
|
||||
|
||||
/// Returns the ratio between the backing framebuffer resolution and the
|
||||
/// window size in screen pixels. This is typically one for a normal display
|
||||
/// and two for a retina display.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// On X11 the DPI factor can be overridden using the `WINIT_HIDPI_FACTOR` environment
|
||||
/// variable.
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
self.window.hidpi_factor()
|
||||
}
|
||||
|
||||
/// Changes the position of the cursor in window coordinates.
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||
self.window.set_cursor_position(x, y)
|
||||
pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), String> {
|
||||
self.window.set_cursor_position(position)
|
||||
}
|
||||
|
||||
/// Sets how winit handles the cursor. See the documentation of `CursorState` for details.
|
||||
/// Grabs the cursor, preventing it from leaving the window.
|
||||
///
|
||||
/// Has no effect on Android.
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// On macOS, this presently merely locks the cursor in a fixed location, which looks visually awkward.
|
||||
///
|
||||
/// This has no effect on Android or iOS.
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
self.window.set_cursor_state(state)
|
||||
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
|
||||
self.window.grab_cursor(grab)
|
||||
}
|
||||
|
||||
/// Hides the cursor, making it invisible but still usable.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// On Windows and X11, the cursor is only hidden within the confines of the window.
|
||||
///
|
||||
/// On macOS, the cursor is hidden as long as the window has input focus, even if the cursor is outside of the
|
||||
/// window.
|
||||
///
|
||||
/// This has no effect on Android or iOS.
|
||||
#[inline]
|
||||
pub fn hide_cursor(&self, hide: bool) {
|
||||
self.window.hide_cursor(hide)
|
||||
}
|
||||
|
||||
/// Sets the window to maximized or back
|
||||
@@ -426,15 +396,33 @@ impl Window {
|
||||
|
||||
/// Sets location of IME candidate box in client area coordinates relative to the top left.
|
||||
#[inline]
|
||||
pub fn set_ime_spot(&self, x: i32, y: i32) {
|
||||
self.window.set_ime_spot(x, y)
|
||||
pub fn set_ime_spot(&self, position: LogicalPosition) {
|
||||
self.window.set_ime_spot(position)
|
||||
}
|
||||
|
||||
/// Returns the monitor on which the window currently resides
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> MonitorId {
|
||||
self.window.get_current_monitor()
|
||||
}
|
||||
|
||||
/// Returns the list of all the monitors available on the system.
|
||||
///
|
||||
/// This is the same as `EventsLoop::get_available_monitors`, and is provided for convenience.
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> AvailableMonitorsIter {
|
||||
let data = self.window.get_available_monitors();
|
||||
AvailableMonitorsIter { data: data.into_iter() }
|
||||
}
|
||||
|
||||
/// Returns the primary monitor of the system.
|
||||
///
|
||||
/// This is the same as `EventsLoop::get_primary_monitor`, and is provided for convenience.
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
MonitorId { inner: self.window.get_primary_monitor() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId(self.window.id())
|
||||
@@ -444,6 +432,7 @@ impl Window {
|
||||
/// An iterator for the list of available monitors.
|
||||
// Implementation note: we retrieve the list once, then serve each element by one by one.
|
||||
// This may change in the future.
|
||||
#[derive(Debug)]
|
||||
pub struct AvailableMonitorsIter {
|
||||
pub(crate) data: VecDequeIter<platform::MonitorId>,
|
||||
}
|
||||
@@ -477,26 +466,29 @@ impl MonitorId {
|
||||
self.inner.get_name()
|
||||
}
|
||||
|
||||
/// Returns the number of pixels currently displayed on the monitor.
|
||||
/// Returns the monitor's resolution.
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||
self.inner.get_dimensions()
|
||||
}
|
||||
|
||||
/// Returns the top-left corner position of the monitor relative to the larger full
|
||||
/// screen area.
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
pub fn get_position(&self) -> PhysicalPosition {
|
||||
self.inner.get_position()
|
||||
}
|
||||
|
||||
/// Returns the ratio between the monitor's physical pixels and logical pixels.
|
||||
/// Returns the DPI factor that can be used to map logical pixels to physical pixels, and vice versa.
|
||||
///
|
||||
/// See the [`dpi`](dpi/index.html) module for more information.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// On X11 the DPI factor can be overridden using the `WINIT_HIDPI_FACTOR` environment
|
||||
/// variable.
|
||||
///
|
||||
/// - **X11:** Can be overridden using the `WINIT_HIDPI_FACTOR` environment variable.
|
||||
/// - **Android:** Always returns 1.0.
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
self.inner.get_hidpi_factor()
|
||||
}
|
||||
}
|
||||
|
||||
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