mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-27 15:13:13 -04:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
23c384bd30 | ||
|
|
ced1616e51 | ||
|
|
233ac4aed2 | ||
|
|
be5a2b0e87 | ||
|
|
2b4b64f499 | ||
|
|
262490d074 | ||
|
|
8891cfd85e | ||
|
|
2cc8fa1eac | ||
|
|
79aebf06dc | ||
|
|
19dd961752 | ||
|
|
bf413ecb83 | ||
|
|
fd1a3eda1c | ||
|
|
0e2488db32 | ||
|
|
58a00bffbb | ||
|
|
bbfe57400d | ||
|
|
4372f6fdac | ||
|
|
30f798b246 | ||
|
|
282770f11a | ||
|
|
17373a4e91 | ||
|
|
a34147b602 | ||
|
|
cebd15bfd1 | ||
|
|
f51f7c0ca8 | ||
|
|
f6d26df64d | ||
|
|
fddfb2e2d6 | ||
|
|
dec728cfa2 | ||
|
|
8440091a4e | ||
|
|
2464a135b3 | ||
|
|
87fa120ebb | ||
|
|
d86f53a02c | ||
|
|
15a4fec3d9 | ||
|
|
1819be1173 | ||
|
|
ffa9b51d27 | ||
|
|
b4a8c08f43 | ||
|
|
741bcc4672 |
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
- [ ] Tested on all platforms changed
|
||||
- [ ] Added an entry to `CHANGELOG.md` if knowledge of this change could be valuable to users
|
||||
- [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
|
||||
- [ ] Created an example program if it would help users understand this functionality
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,6 @@
|
||||
Cargo.lock
|
||||
target/
|
||||
rls/
|
||||
.vscode/
|
||||
*~
|
||||
#*#
|
||||
|
||||
15
.travis.yml
15
.travis.yml
@@ -19,6 +19,12 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
packages: *i686_packages
|
||||
- env: TARGET=i686-unknown-linux-gnu
|
||||
os: linux
|
||||
rust: 1.24.1
|
||||
addons:
|
||||
apt:
|
||||
packages: *i686_packages
|
||||
|
||||
# Linux 64bit
|
||||
- env: TARGET=x86_64-unknown-linux-gnu
|
||||
@@ -27,6 +33,9 @@ matrix:
|
||||
- env: TARGET=x86_64-unknown-linux-gnu
|
||||
os: linux
|
||||
rust: stable
|
||||
- env: TARGET=x86_64-unknown-linux-gnu
|
||||
os: linux
|
||||
rust: 1.24.1
|
||||
|
||||
# macOS
|
||||
- env: TARGET=x86_64-apple-darwin
|
||||
@@ -35,6 +44,9 @@ matrix:
|
||||
- env: TARGET=x86_64-apple-darwin
|
||||
os: osx
|
||||
rust: stable
|
||||
- env: TARGET=x86_64-apple-darwin
|
||||
os: osx
|
||||
rust: 1.24.1
|
||||
|
||||
# iOS
|
||||
- env: TARGET=x86_64-apple-ios
|
||||
@@ -43,6 +55,9 @@ matrix:
|
||||
- env: TARGET=x86_64-apple-ios
|
||||
os: osx
|
||||
rust: stable
|
||||
- env: TARGET=x86_64-apple-ios
|
||||
os: osx
|
||||
rust: 1.24.1
|
||||
|
||||
install:
|
||||
- rustup self update
|
||||
|
||||
89
CHANGELOG.md
89
CHANGELOG.md
@@ -1,5 +1,94 @@
|
||||
# Unreleased
|
||||
|
||||
# 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.
|
||||
- `MouseCursor` and `CursorState` now implement `Default`.
|
||||
- `WindowBuilder::with_resizable` implemented for Windows, X11, Wayland, and macOS.
|
||||
- `Window::set_resizable` implemented for Windows, X11, Wayland, and macOS.
|
||||
- On X11, if the monitor's width or height in millimeters is reported as 0, the DPI is now 1.0 instead of +inf.
|
||||
- On X11, the environment variable `WINIT_HIDPI_FACTOR` has been added for overriding DPI factor.
|
||||
- On X11, enabling transparency no longer causes the window contents to flicker when resizing.
|
||||
- On X11, `with_override_redirect` now actually enables override redirect.
|
||||
- macOS now generates `VirtualKeyCode::LAlt` and `VirtualKeyCode::RAlt` instead of `None` for both.
|
||||
- On macOS, `VirtualKeyCode::RWin` and `VirtualKeyCode::LWin` are no longer switched.
|
||||
- On macOS, windows without decorations can once again be resized.
|
||||
- Fixed race conditions when creating an `EventsLoop` on X11, most commonly manifesting as "[xcb] Unknown sequence number while processing queue".
|
||||
- On macOS, `CursorMoved` and `MouseInput` events are only generated if they occurs within the window's client area.
|
||||
- On macOS, resizing the window no longer generates a spurious `MouseInput` event.
|
||||
|
||||
# Version 0.15.0 (2018-05-22)
|
||||
|
||||
- `Icon::to_cardinals` is no longer public, since it was never supposed to be.
|
||||
- Wayland: improve diagnostics if initialization fails
|
||||
- Fix some system event key doesn't work when focused, do not block keyevent forward to system on macOS
|
||||
- On X11, the scroll wheel position is now correctly reset on i3 and other WMs that have the same quirk.
|
||||
- On X11, `Window::get_current_monitor` now reliably returns the correct monitor.
|
||||
- On X11, `Window::hidpi_factor` returns values from XRandR rather than the inaccurate values previously queried from the core protocol.
|
||||
- On X11, the primary monitor is detected correctly even when using versions of XRandR less than 1.5.
|
||||
- `MonitorId` now implements `Debug`.
|
||||
- Fixed bug on macOS where using `with_decorations(false)` would cause `set_decorations(true)` to produce a transparent titlebar with no title.
|
||||
- Implemented `MonitorId::get_position` on macOS.
|
||||
- On macOS, `Window::get_current_monitor` now returns accurate values.
|
||||
- Added `WindowBuilderExt::with_resize_increments` to macOS.
|
||||
- **Breaking:** On X11, `WindowBuilderExt::with_resize_increments` and `WindowBuilderExt::with_base_size` now take `u32` values rather than `i32`.
|
||||
- macOS keyboard handling has been overhauled, allowing for the use of dead keys, IME, etc. Right modifier keys are also no longer reported as being left.
|
||||
- Added the `Window::set_ime_spot(x: i32, y: i32)` method, which is implemented on X11 and macOS.
|
||||
- **Breaking**: `os::unix::WindowExt::send_xim_spot(x: i16, y: i16)` no longer exists. Switch to the new `Window::set_ime_spot(x: i32, y: i32)`, which has equivalent functionality.
|
||||
- Fixed detection of `Pause` and `Scroll` keys on Windows.
|
||||
- On Windows, alt-tabbing while the cursor is grabbed no longer makes it impossible to re-grab the cursor.
|
||||
- On Windows, using `CursorState::Hide` when the cursor is grabbed now ungrabs the cursor first.
|
||||
- Implemented `MouseCursor::NoneCursor` on Windows.
|
||||
- Added `WindowBuilder::with_always_on_top` and `Window::set_always_on_top`. Implemented on Windows, macOS, and X11.
|
||||
- On X11, `WindowBuilderExt` now has `with_class`, `with_override_redirect`, and `with_x11_window_type` to allow for more control over window creation. `WindowExt` additionally has `set_urgent`.
|
||||
- More hints are set by default on X11, including `_NET_WM_PID` and `WM_CLIENT_MACHINE`. Note that prior to this, the `WM_CLASS` hint was automatically set to whatever value was passed to `with_title`. It's now set to the executable name to better conform to expectations and the specification; if this is undesirable, you must explicitly use `WindowBuilderExt::with_class`.
|
||||
|
||||
# Version 0.14.0 (2018-05-09)
|
||||
|
||||
- Created the `Copy`, `Paste` and `Cut` `VirtualKeyCode`s and added support for them on X11 and Wayland
|
||||
|
||||
48
Cargo.toml
48
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.14.0"
|
||||
version = "0.17.0"
|
||||
authors = ["The winit contributors, Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
keywords = ["windowing"]
|
||||
@@ -10,46 +10,52 @@ repository = "https://github.com/tomaka/winit"
|
||||
documentation = "https://docs.rs/winit"
|
||||
categories = ["gui"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["icon_loading"]
|
||||
|
||||
[features]
|
||||
icon_loading = ["image"]
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "1"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
image = { version = "0.19", optional = true }
|
||||
|
||||
[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.14"
|
||||
core-foundation = "0.5"
|
||||
core-graphics = "0.13"
|
||||
objc = "0.2.3"
|
||||
cocoa = "0.17"
|
||||
core-foundation = "0.6"
|
||||
core-graphics = "0.16"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
||||
version = "0.3"
|
||||
version = "0.3.5"
|
||||
features = [
|
||||
"combaseapi",
|
||||
"dwmapi",
|
||||
"hidusage",
|
||||
"libloaderapi",
|
||||
"objbase",
|
||||
"processthreadsapi",
|
||||
"shellapi",
|
||||
"shellscalingapi",
|
||||
"shobjidl_core",
|
||||
"unknwnbase",
|
||||
"windowsx",
|
||||
"wingdi",
|
||||
"winnt",
|
||||
"winuser",
|
||||
"wingdi",
|
||||
"shellapi",
|
||||
"dwmapi",
|
||||
"processthreadsapi",
|
||||
"libloaderapi",
|
||||
"windowsx",
|
||||
"hidusage",
|
||||
"combaseapi",
|
||||
"objbase",
|
||||
"unknwnbase",
|
||||
]
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies]
|
||||
wayland-client = { version = "0.20.4", features = [ "dlopen", "egl", "cursor"] }
|
||||
smithay-client-toolkit = "0.2.1"
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
|
||||
wayland-client = { version = "0.20.10", features = [ "dlopen", "egl", "cursor"] }
|
||||
smithay-client-toolkit = "0.2.6"
|
||||
x11-dl = "2.17.5"
|
||||
parking_lot = "0.5"
|
||||
parking_lot = "0.6"
|
||||
percent-encoding = "1.0"
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
|
||||
[](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.14"
|
||||
winit = "0.17"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
|
||||
13
appveyor.yml
13
appveyor.yml
@@ -1,12 +1,19 @@
|
||||
environment:
|
||||
matrix:
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
CHANNEL: nightly
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
CHANNEL: stable
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
CHANNEL: 1.24.1
|
||||
- TARGET: i686-pc-windows-msvc
|
||||
CHANNEL: nightly
|
||||
- TARGET: i686-pc-windows-gnu
|
||||
CHANNEL: nightly
|
||||
install:
|
||||
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe"
|
||||
- rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
|
||||
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin
|
||||
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
||||
- rustup-init -yv --default-toolchain %CHANNEL% --default-host %TARGET%
|
||||
- SET PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
||||
- SET PATH=%PATH%;C:\MinGW\bin
|
||||
- rustc -V
|
||||
- cargo -V
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
38
examples/resizable.rs
Normal file
38
examples/resizable.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
extern crate winit;
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let mut resizable = false;
|
||||
|
||||
let window = winit::WindowBuilder::new()
|
||||
.with_title("Hit space to toggle resizability.")
|
||||
.with_dimensions((400, 200).into())
|
||||
.with_resizable(resizable)
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
match event {
|
||||
winit::Event::WindowEvent { event, .. } => match event {
|
||||
winit::WindowEvent::CloseRequested => return winit::ControlFlow::Break,
|
||||
winit::WindowEvent::KeyboardInput {
|
||||
input:
|
||||
winit::KeyboardInput {
|
||||
virtual_keycode: Some(winit::VirtualKeyCode::Space),
|
||||
state: winit::ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
resizable = !resizable;
|
||||
println!("Resizable: {}", resizable);
|
||||
window.set_resizable(resizable);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
};
|
||||
winit::ControlFlow::Continue
|
||||
});
|
||||
}
|
||||
317
src/dpi.rs
Normal file
317
src/dpi.rs
Normal file
@@ -0,0 +1,317 @@
|
||||
|
||||
//! 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 `WindowEvent::HiDpiFactorChanged` event of your windows. This event is sent any
|
||||
//! time the DPI factor changes, be it 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 `WindowEvent::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 very much at the discretion of the user.
|
||||
//! - **iOS:** DPI factors are both constant and device-specific on iOS.
|
||||
//! - **Android:** This feature isn't yet implemented on Android, so the DPI factor will always be returned as 1.0.
|
||||
//!
|
||||
//! The window's logical size is conserved across DPI changes, resulting in the physical size changing instead. This
|
||||
//! may be surprising on X11, but is quite standard elsewhere. Physical size changes produce a
|
||||
//! [`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.
|
||||
|
||||
/// 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)]
|
||||
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)]
|
||||
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)]
|
||||
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)]
|
||||
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,5 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
use {WindowId, DeviceId};
|
||||
|
||||
use {DeviceId, LogicalPosition, LogicalSize, WindowId};
|
||||
|
||||
/// Describes a generic event.
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -23,12 +24,11 @@ pub enum Event {
|
||||
/// Describes an event from a `Window`.
|
||||
#[derive(Clone, Debug)]
|
||||
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.
|
||||
@@ -197,7 +199,7 @@ pub enum TouchPhase {
|
||||
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
|
||||
}
|
||||
@@ -242,7 +244,7 @@ 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.
|
||||
@@ -383,7 +385,6 @@ pub enum VirtualKeyCode {
|
||||
LAlt,
|
||||
LBracket,
|
||||
LControl,
|
||||
LMenu,
|
||||
LShift,
|
||||
LWin,
|
||||
Mail,
|
||||
@@ -408,7 +409,6 @@ pub enum VirtualKeyCode {
|
||||
RAlt,
|
||||
RBracket,
|
||||
RControl,
|
||||
RMenu,
|
||||
RShift,
|
||||
RWin,
|
||||
Semicolon,
|
||||
|
||||
10
src/icon.rs
10
src/icon.rs
@@ -104,12 +104,16 @@ impl Icon {
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
/// Loads an `Icon` from the path of an image on the filesystem.
|
||||
///
|
||||
/// Requires the `icon_loading` feature.
|
||||
pub fn from_path<P: AsRef<Path>>(path: P) -> image::ImageResult<Self> {
|
||||
image::open(path).map(Into::into)
|
||||
}
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
/// Loads an `Icon` from anything implementing `BufRead` and `Seek`.
|
||||
///
|
||||
/// Requires the `icon_loading` feature.
|
||||
pub fn from_reader<R: BufRead + Seek>(
|
||||
reader: R,
|
||||
format: image::ImageFormat,
|
||||
@@ -120,12 +124,16 @@ impl Icon {
|
||||
#[cfg(feature = "icon_loading")]
|
||||
/// Loads an `Icon` from the unprocessed bytes of an image file.
|
||||
/// Uses heuristics to determine format.
|
||||
///
|
||||
/// Requires the `icon_loading` feature.
|
||||
pub fn from_bytes(bytes: &[u8]) -> image::ImageResult<Self> {
|
||||
image::load_from_memory(bytes).map(Into::into)
|
||||
}
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
/// Loads an `Icon` from the unprocessed bytes of an image.
|
||||
///
|
||||
/// Requires the `icon_loading` feature.
|
||||
pub fn from_bytes_with_format(
|
||||
bytes: &[u8],
|
||||
format: image::ImageFormat,
|
||||
@@ -135,6 +143,7 @@ impl Icon {
|
||||
}
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
/// Requires the `icon_loading` feature.
|
||||
impl From<image::DynamicImage> for Icon {
|
||||
fn from(image: image::DynamicImage) -> Self {
|
||||
use image::{GenericImage, Pixel};
|
||||
@@ -148,6 +157,7 @@ impl From<image::DynamicImage> for Icon {
|
||||
}
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
/// Requires the `icon_loading` feature.
|
||||
impl From<image::RgbaImage> for Icon {
|
||||
fn from(buf: image::RgbaImage) -> Self {
|
||||
let (width, height) = buf.dimensions();
|
||||
|
||||
82
src/lib.rs
82
src/lib.rs
@@ -15,7 +15,7 @@
|
||||
//! - Calling `Window::new(&events_loop)`.
|
||||
//! - Calling `let builder = WindowBuilder::new()` then `builder.build(&events_loop)`.
|
||||
//!
|
||||
//! The first way is the simpliest way and will give you default values for everything.
|
||||
//! The first way is the simplest way and will give you default values for everything.
|
||||
//!
|
||||
//! The second way allows you to customize the way your window will look and behave by modifying
|
||||
//! the fields of the `WindowBuilder` object before you create the window.
|
||||
@@ -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,19 +80,20 @@
|
||||
//! # 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.
|
||||
//!
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "windows"))]
|
||||
#[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(target_os = "windows")]
|
||||
#[macro_use]
|
||||
extern crate winapi;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
#[macro_use]
|
||||
@@ -99,23 +104,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;
|
||||
|
||||
@@ -159,7 +166,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()`
|
||||
@@ -229,6 +236,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
|
||||
@@ -345,7 +357,6 @@ pub enum MouseCursor {
|
||||
/// Cursor showing that something cannot be done.
|
||||
NotAllowed,
|
||||
ContextMenu,
|
||||
NoneCursor,
|
||||
Cell,
|
||||
VerticalText,
|
||||
Alias,
|
||||
@@ -375,41 +386,35 @@ pub enum MouseCursor {
|
||||
RowResize,
|
||||
}
|
||||
|
||||
/// 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 MouseCursor {
|
||||
fn default() -> Self {
|
||||
MouseCursor::Default
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// The default is `true`.
|
||||
pub resizable: bool,
|
||||
|
||||
/// Whether the window should be set as fullscreen upon creation.
|
||||
///
|
||||
@@ -442,6 +447,11 @@ pub struct WindowAttributes {
|
||||
/// The default is `true`.
|
||||
pub decorations: bool,
|
||||
|
||||
/// Whether the window should always be on top of other windows.
|
||||
///
|
||||
/// The default is `false`.
|
||||
pub always_on_top: bool,
|
||||
|
||||
/// The window icon.
|
||||
///
|
||||
/// The default is `None`.
|
||||
@@ -459,12 +469,14 @@ impl Default for WindowAttributes {
|
||||
dimensions: None,
|
||||
min_dimensions: None,
|
||||
max_dimensions: None,
|
||||
resizable: true,
|
||||
title: "winit window".to_owned(),
|
||||
maximized: false,
|
||||
fullscreen: None,
|
||||
visible: true,
|
||||
transparent: false,
|
||||
decorations: true,
|
||||
always_on_top: false,
|
||||
window_icon: None,
|
||||
multitouch: false,
|
||||
}
|
||||
|
||||
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 _
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
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 {
|
||||
@@ -71,64 +71,72 @@ impl From<ActivationPolicy> for NSApplicationActivationPolicy {
|
||||
/// - `with_titlebar_buttons_hidden`
|
||||
/// - `with_fullsize_content_view`
|
||||
pub trait WindowBuilderExt {
|
||||
/// Sets the activation policy for the window being built.
|
||||
fn with_activation_policy(self, activation_policy: ActivationPolicy) -> WindowBuilder;
|
||||
/// Enables click-and-drag behavior for the entire window, not just the titlebar.
|
||||
fn with_movable_by_window_background(self, movable_by_window_background: bool) -> WindowBuilder;
|
||||
/// Makes the titlebar transparent and allows the content to appear behind it.
|
||||
fn with_titlebar_transparent(self, titlebar_transparent: bool) -> WindowBuilder;
|
||||
/// Hides the window title.
|
||||
fn with_title_hidden(self, title_hidden: bool) -> WindowBuilder;
|
||||
/// Hides the window titlebar.
|
||||
fn with_titlebar_hidden(self, titlebar_hidden: bool) -> WindowBuilder;
|
||||
/// Hides the window titlebar buttons.
|
||||
fn with_titlebar_buttons_hidden(self, titlebar_buttons_hidden: bool) -> WindowBuilder;
|
||||
/// 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, increments: LogicalSize) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExt for WindowBuilder {
|
||||
/// Sets the activation policy for the window being built
|
||||
#[inline]
|
||||
fn with_activation_policy(mut self, activation_policy: ActivationPolicy) -> WindowBuilder {
|
||||
self.platform_specific.activation_policy = activation_policy;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables click-and-drag behavior for the entire window, not just the titlebar
|
||||
#[inline]
|
||||
fn with_movable_by_window_background(mut self, movable_by_window_background: bool) -> WindowBuilder {
|
||||
self.platform_specific.movable_by_window_background = movable_by_window_background;
|
||||
self
|
||||
}
|
||||
|
||||
/// Makes the titlebar transparent and allows the content to appear behind it
|
||||
#[inline]
|
||||
fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> WindowBuilder {
|
||||
self.platform_specific.titlebar_transparent = titlebar_transparent;
|
||||
self
|
||||
}
|
||||
|
||||
/// Hides the window titlebar
|
||||
#[inline]
|
||||
fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> WindowBuilder {
|
||||
self.platform_specific.titlebar_hidden = titlebar_hidden;
|
||||
self
|
||||
}
|
||||
|
||||
/// Hides the window titlebar buttons
|
||||
#[inline]
|
||||
fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> WindowBuilder {
|
||||
self.platform_specific.titlebar_buttons_hidden = titlebar_buttons_hidden;
|
||||
self
|
||||
}
|
||||
|
||||
/// Hides the window title
|
||||
#[inline]
|
||||
fn with_title_hidden(mut self, title_hidden: bool) -> WindowBuilder {
|
||||
self.platform_specific.title_hidden = title_hidden;
|
||||
self
|
||||
}
|
||||
|
||||
/// Makes the window content appear behind the titlebar
|
||||
#[inline]
|
||||
fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> WindowBuilder {
|
||||
self.platform_specific.fullsize_content_view = fullsize_content_view;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_resize_increments(mut self, increments: LogicalSize) -> WindowBuilder {
|
||||
self.platform_specific.resize_increments = Some(increments.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorId` that are specific to MacOS.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -17,6 +23,7 @@ use platform::x11::ffi::XVisualInfo;
|
||||
pub use platform::x11;
|
||||
|
||||
pub use platform::XNotSupported;
|
||||
pub use platform::x11::util::WindowType as XWindowType;
|
||||
|
||||
/// Additional methods on `EventsLoop` that are specific to Linux.
|
||||
pub trait EventsLoopExt {
|
||||
@@ -71,6 +78,7 @@ impl EventsLoopExt for EventsLoop {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> {
|
||||
self.events_loop.x_connection().cloned()
|
||||
}
|
||||
@@ -92,9 +100,11 @@ pub trait WindowExt {
|
||||
|
||||
fn get_xlib_screen_id(&self) -> Option<raw::c_int>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>>;
|
||||
|
||||
fn send_xim_spot(&self, x: i16, y: i16);
|
||||
/// Set window urgency hint (`XUrgencyHint`). Only relevant on X.
|
||||
fn set_urgent(&self, is_urgent: bool);
|
||||
|
||||
/// This function returns the underlying `xcb_connection_t` of an xlib `Display`.
|
||||
///
|
||||
@@ -144,6 +154,7 @@ impl WindowExt for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_xlib_screen_id(&self) -> Option<raw::c_int> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.get_xlib_screen_id()),
|
||||
@@ -151,6 +162,8 @@ 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()),
|
||||
@@ -158,6 +171,7 @@ impl WindowExt for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_xcb_connection(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.get_xcb_connection()),
|
||||
@@ -165,9 +179,10 @@ impl WindowExt for Window {
|
||||
}
|
||||
}
|
||||
|
||||
fn send_xim_spot(&self, x: i16, y: i16) {
|
||||
#[inline]
|
||||
fn set_urgent(&self, is_urgent: bool) {
|
||||
if let LinuxWindow::X(ref w) = self.window {
|
||||
w.send_xim_spot(x, y);
|
||||
w.set_urgent(is_urgent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,10 +213,16 @@ pub trait WindowBuilderExt {
|
||||
fn with_x11_visual<T>(self, visual_infos: *const T) -> WindowBuilder;
|
||||
fn with_x11_screen(self, screen_id: i32) -> WindowBuilder;
|
||||
|
||||
/// Build window with `WM_CLASS` hint; defaults to the name of the binary. Only relevant on X11.
|
||||
fn with_class(self, class: String, instance: String) -> WindowBuilder;
|
||||
/// Build window with override-redirect flag; defaults to false. Only relevant on X11.
|
||||
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 resize increment hint. Only implemented on X11.
|
||||
fn with_resize_increments(self, width_inc: i32, height_inc: i32) -> 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: i32, base_height: i32) -> WindowBuilder;
|
||||
fn with_base_size(self, base_size: LogicalSize) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExt for WindowBuilder {
|
||||
@@ -220,14 +241,32 @@ impl WindowBuilderExt for WindowBuilder {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_resize_increments(mut self, width_inc: i32, height_inc: i32) -> WindowBuilder {
|
||||
self.platform_specific.resize_increments = Some((width_inc, height_inc));
|
||||
fn with_class(mut self, instance: String, class: String) -> WindowBuilder {
|
||||
self.platform_specific.class = Some((instance, class));
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_base_size(mut self, base_width: i32, base_height: i32) -> WindowBuilder {
|
||||
self.platform_specific.base_size = Some((base_width, base_height));
|
||||
fn with_override_redirect(mut self, override_redirect: bool) -> WindowBuilder {
|
||||
self.platform_specific.override_redirect = override_redirect;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_x11_window_type(mut self, x11_window_type: XWindowType) -> WindowBuilder {
|
||||
self.platform_specific.x11_window_type = x11_window_type;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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_size: LogicalSize) -> WindowBuilder {
|
||||
self.platform_specific.base_size = Some(base_size.into());
|
||||
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),
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -167,7 +180,26 @@ pub struct Window {
|
||||
#[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,93 +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 get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
pub fn set_resizable(&self, _resizable: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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
|
||||
}
|
||||
|
||||
@@ -323,16 +367,39 @@ impl Window {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_always_on_top(&self, _always_on_top: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
RootMonitorId{inner: MonitorId}
|
||||
pub fn set_ime_spot(&self, _spot: LogicalPosition) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
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;
|
||||
|
||||
@@ -24,7 +30,7 @@ pub struct DeviceId;
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificHeadlessBuilderAttributes;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MonitorId;
|
||||
|
||||
impl MonitorId {
|
||||
@@ -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,94 +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)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn show(&self) {}
|
||||
#[inline]
|
||||
pub fn hide(&self) {}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut ::libc::c_void {
|
||||
unimplemented!()
|
||||
pub fn set_min_dimensions(&self, _dimensions: Option<LogicalSize>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_window(&self) -> *mut ::libc::c_void {
|
||||
unimplemented!()
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<LogicalSize>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _cursor: ::MouseCursor) {}
|
||||
pub fn set_resizable(&self, _resizable: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, state: ::CursorState) -> Result<(), String> {
|
||||
pub fn show(&self) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide(&self) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _cursor: ::MouseCursor) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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]
|
||||
@@ -543,14 +578,36 @@ impl Window {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_always_on_top(&self, _always_on_top: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> ::MonitorId {
|
||||
::MonitorId{inner: MonitorId}
|
||||
pub fn set_ime_spot(&self, _logical_spot: LogicalPosition) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,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 {
|
||||
@@ -597,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 {
|
||||
@@ -614,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 {
|
||||
|
||||
@@ -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(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,109 +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)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
unimplemented!();
|
||||
pub fn set_min_dimensions(&self, _dimensions: Option<LogicalSize>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
unimplemented!()
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<LogicalSize>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
||||
pub fn set_resizable(&self, _resizable: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _: MouseCursor) {
|
||||
pub fn set_cursor(&self, _cursor: MouseCursor) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, _: CursorState) -> Result<(), String> {
|
||||
Ok(())
|
||||
pub fn grab_cursor(&self, _grab: bool) -> Result<(), String> {
|
||||
Err("Cursor grabbing is not possible on iOS.".to_owned())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
unsafe { (&*self.delegate_state) }.scale
|
||||
pub fn hide_cursor(&self, _hide: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
|
||||
unimplemented!();
|
||||
pub fn get_hidpi_factor(&self) -> f64 {
|
||||
self.delegate_state.scale
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
|
||||
@@ -370,14 +472,36 @@ impl Window {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_always_on_top(&self, _always_on_top: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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]
|
||||
@@ -388,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),
|
||||
});
|
||||
@@ -431,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),
|
||||
});
|
||||
@@ -442,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];
|
||||
|
||||
@@ -486,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,
|
||||
@@ -506,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:),
|
||||
@@ -545,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,18 +6,18 @@ use std::ffi::CStr;
|
||||
use std::os::raw::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
// `std::os::raw::c_void` and `libc::c_void` are NOT interchangeable!
|
||||
use libc;
|
||||
use parking_lot::Mutex;
|
||||
use sctk::reexports::client::ConnectError;
|
||||
|
||||
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;
|
||||
@@ -40,34 +40,37 @@ const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND";
|
||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub visual_infos: Option<XVisualInfo>,
|
||||
pub screen_id: Option<i32>,
|
||||
pub resize_increments: Option<(i32, i32)>,
|
||||
pub base_size: Option<(i32, i32)>,
|
||||
pub resize_increments: Option<(u32, u32)>,
|
||||
pub base_size: Option<(u32, u32)>,
|
||||
pub class: Option<(String, String)>,
|
||||
pub override_redirect: bool,
|
||||
pub x11_window_type: x11::util::WindowType,
|
||||
}
|
||||
|
||||
lazy_static!(
|
||||
pub static ref X11_BACKEND: Result<Arc<XConnection>, XNotSupported> = {
|
||||
XConnection::new(Some(x_error_callback)).map(Arc::new)
|
||||
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(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MonitorId {
|
||||
X(x11::MonitorId),
|
||||
Wayland(wayland::MonitorId),
|
||||
@@ -91,7 +94,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(),
|
||||
@@ -99,7 +102,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(),
|
||||
@@ -107,10 +110,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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,7 +139,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()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +147,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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +155,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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,20 +163,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(),
|
||||
@@ -181,50 +184,58 @@ 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 {
|
||||
&Window::X(ref w) => w.set_resizable(resizable),
|
||||
&Window::Wayland(ref w) => w.set_resizable(resizable),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,42 +248,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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,6 +303,14 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_always_on_top(&self, always_on_top: bool) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_always_on_top(always_on_top),
|
||||
&Window::Wayland(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, window_icon: Option<Icon>) {
|
||||
match self {
|
||||
@@ -308,11 +319,41 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_spot(&self, position: LogicalPosition) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_ime_spot(position),
|
||||
&Window::Wayland(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -321,7 +362,8 @@ unsafe extern "C" fn x_error_callback(
|
||||
display: *mut x11::ffi::Display,
|
||||
event: *mut x11::ffi::XErrorEvent,
|
||||
) -> c_int {
|
||||
if let Ok(ref xconn) = *X11_BACKEND {
|
||||
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,
|
||||
@@ -338,6 +380,8 @@ unsafe extern "C" fn x_error_callback(
|
||||
minor_code: (*event).minor_code,
|
||||
};
|
||||
|
||||
eprintln!("[winit X11 error] {:#?}", error);
|
||||
|
||||
*xconn.latest_error.lock() = Some(error);
|
||||
}
|
||||
// Fun fact: this return value is completely ignored.
|
||||
@@ -395,30 +439,35 @@ r#"Failed to initialize any backend!
|
||||
panic!(err_string);
|
||||
}
|
||||
|
||||
pub fn new_wayland() -> Result<EventsLoop, ()> {
|
||||
pub fn new_wayland() -> Result<EventsLoop, ConnectError> {
|
||||
wayland::EventsLoop::new()
|
||||
.map(EventsLoop::Wayland)
|
||||
.ok_or(())
|
||||
}
|
||||
|
||||
pub fn new_x11() -> Result<EventsLoop, XNotSupported> {
|
||||
match *X11_BACKEND {
|
||||
Ok(ref x) => Ok(EventsLoop::X(x11::EventsLoop::new(x.clone()))),
|
||||
Err(ref 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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,7 +475,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,16 +1,17 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use {ControlFlow, EventsLoopClosed};
|
||||
use {ControlFlow, EventsLoopClosed, PhysicalPosition, PhysicalSize};
|
||||
|
||||
use super::WindowId;
|
||||
use super::window::WindowStore;
|
||||
|
||||
use sctk::Environment;
|
||||
use sctk::output::OutputMgr;
|
||||
use sctk::reexports::client::{Display, EventQueue, GlobalEvent, Proxy};
|
||||
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};
|
||||
@@ -100,11 +101,8 @@ impl EventsLoopProxy {
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new() -> Option<EventsLoop> {
|
||||
let (display, mut event_queue) = match Display::connect_to_env() {
|
||||
Ok(ret) => ret,
|
||||
Err(_) => return None,
|
||||
};
|
||||
pub fn new() -> Result<EventsLoop, ConnectError> {
|
||||
let (display, mut event_queue) = Display::connect_to_env()?;
|
||||
|
||||
let sink = Arc::new(Mutex::new(EventsLoopSink::new()));
|
||||
let store = Arc::new(Mutex::new(WindowStore::new()));
|
||||
@@ -120,7 +118,7 @@ impl EventsLoop {
|
||||
},
|
||||
).unwrap();
|
||||
|
||||
Some(EventsLoop {
|
||||
Ok(EventsLoop {
|
||||
display: Arc::new(display),
|
||||
evq: RefCell::new(event_queue),
|
||||
sink: sink,
|
||||
@@ -201,27 +199,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,16 +232,21 @@ 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 let Some(dpi) = new_dpi {
|
||||
sink.send_event(::WindowEvent::HiDpiFactorChanged(dpi as f64), wid);
|
||||
}
|
||||
if refresh {
|
||||
sink.send_event(::WindowEvent::Refresh, wid);
|
||||
}
|
||||
@@ -430,6 +417,29 @@ impl Clone for MonitorId {
|
||||
}
|
||||
}
|
||||
|
||||
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: PhysicalSize,
|
||||
position: PhysicalPosition,
|
||||
hidpi_factor: i32,
|
||||
}
|
||||
|
||||
let monitor_id_proxy = MonitorId {
|
||||
name: self.get_name(),
|
||||
native_identifier: self.get_native_identifier(),
|
||||
dimensions: self.get_dimensions(),
|
||||
position: self.get_position(),
|
||||
hidpi_factor: self.get_hidpi_factor(),
|
||||
};
|
||||
|
||||
monitor_id_proxy.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
self.mgr.with_info(&self.proxy, |_, info| {
|
||||
@@ -442,7 +452,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()
|
||||
@@ -451,19 +461,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()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -247,7 +247,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 +269,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};
|
||||
|
||||
@@ -42,7 +42,7 @@ pub fn implement_pointer(
|
||||
sink.send_event(
|
||||
WindowEvent::CursorMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (surface_x, surface_y),
|
||||
position: (surface_x, surface_y).into(),
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
@@ -71,7 +71,7 @@ pub fn implement_pointer(
|
||||
sink.send_event(
|
||||
WindowEvent::CursorMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (surface_x, surface_y),
|
||||
position: (surface_x, surface_y).into(),
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
@@ -117,7 +117,7 @@ pub fn implement_pointer(
|
||||
sink.send_event(
|
||||
WindowEvent::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
|
||||
phase: TouchPhase::Moved,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
@@ -158,7 +158,7 @@ pub fn implement_pointer(
|
||||
sink.send_event(
|
||||
WindowEvent::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
|
||||
phase: axis_state,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
|
||||
@@ -34,7 +34,7 @@ pub(crate) fn implement_touch(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Started,
|
||||
location: (x, y),
|
||||
location: (x, y).into(),
|
||||
id: id as u64,
|
||||
}),
|
||||
wid,
|
||||
@@ -54,7 +54,7 @@ pub(crate) fn implement_touch(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Ended,
|
||||
location: pt.location,
|
||||
location: pt.location.into(),
|
||||
id: id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
@@ -69,7 +69,7 @@ pub(crate) fn implement_touch(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Moved,
|
||||
location: (x, y),
|
||||
location: (x, y).into(),
|
||||
id: id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
@@ -82,7 +82,7 @@ pub(crate) fn implement_touch(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Cancelled,
|
||||
location: pt.location,
|
||||
location: pt.location.into(),
|
||||
id: pt.id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
|
||||
@@ -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::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>>>,
|
||||
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,23 +29,40 @@ 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 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();
|
||||
move |event, surface: Proxy<wl_surface::WlSurface>| 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -59,7 +81,7 @@ impl Window {
|
||||
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;
|
||||
@@ -101,12 +123,14 @@ impl Window {
|
||||
frame.set_maximized();
|
||||
}
|
||||
|
||||
frame.set_resizable(attributes.resizable);
|
||||
|
||||
// set decorations
|
||||
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));
|
||||
@@ -115,11 +139,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();
|
||||
|
||||
@@ -128,6 +155,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,
|
||||
@@ -154,75 +182,59 @@ 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]
|
||||
pub fn set_cursor(&self, _cursor: MouseCursor) {
|
||||
// TODO
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
self.frame.lock().unwrap().set_resizable(resizable);
|
||||
}
|
||||
|
||||
#[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) {
|
||||
@@ -253,9 +265,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 {
|
||||
@@ -270,7 +296,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,12 +321,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>>>,
|
||||
current_dpi: i32,
|
||||
new_dpi: Option<i32>
|
||||
}
|
||||
|
||||
pub struct WindowStore {
|
||||
@@ -338,24 +375,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<BasicFrame>>),
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ impl DndAtoms {
|
||||
b"text/uri-list\0".as_ptr() as *mut c_char,
|
||||
b"None\0".as_ptr() as *mut c_char,
|
||||
];
|
||||
let atoms = unsafe { util::get_atoms(xconn, &names) }?;
|
||||
let atoms = unsafe { xconn.get_atoms(&names) }?;
|
||||
Ok(DndAtoms {
|
||||
aware: atoms[0],
|
||||
enter: atoms[1],
|
||||
@@ -127,13 +127,12 @@ impl Dnd {
|
||||
DndState::Accepted => (1, self.atoms.action_private as c_long),
|
||||
DndState::Rejected => (0, self.atoms.none as c_long),
|
||||
};
|
||||
util::send_client_msg(
|
||||
&self.xconn,
|
||||
self.xconn.send_client_msg(
|
||||
target_window,
|
||||
target_window,
|
||||
self.atoms.status,
|
||||
None,
|
||||
(this_window as c_long, accepted, 0, 0, action),
|
||||
[this_window as c_long, accepted, 0, 0, action],
|
||||
).flush()
|
||||
}
|
||||
|
||||
@@ -147,13 +146,12 @@ impl Dnd {
|
||||
DndState::Accepted => (1, self.atoms.action_private as c_long),
|
||||
DndState::Rejected => (0, self.atoms.none as c_long),
|
||||
};
|
||||
util::send_client_msg(
|
||||
&self.xconn,
|
||||
self.xconn.send_client_msg(
|
||||
target_window,
|
||||
target_window,
|
||||
self.atoms.finished,
|
||||
None,
|
||||
(this_window as c_long, accepted, action, 0, 0),
|
||||
[this_window as c_long, accepted, action, 0, 0],
|
||||
).flush()
|
||||
}
|
||||
|
||||
@@ -161,8 +159,7 @@ impl Dnd {
|
||||
&self,
|
||||
source_window: c_ulong,
|
||||
) -> Result<Vec<ffi::Atom>, util::GetPropertyError> {
|
||||
util::get_property(
|
||||
&self.xconn,
|
||||
self.xconn.get_property(
|
||||
source_window,
|
||||
self.atoms.type_list,
|
||||
ffi::XA_ATOM,
|
||||
@@ -184,8 +181,7 @@ impl Dnd {
|
||||
&self,
|
||||
window: c_ulong,
|
||||
) -> Result<Vec<c_uchar>, util::GetPropertyError> {
|
||||
util::get_property(
|
||||
&self.xconn,
|
||||
self.xconn.get_property(
|
||||
window,
|
||||
self.atoms.selection,
|
||||
self.atoms.uri_list,
|
||||
|
||||
@@ -5,12 +5,20 @@ use std::sync::Arc;
|
||||
use std::os::raw::c_char;
|
||||
use std::ffi::{CStr, CString, IntoStringError};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use super::{ffi, util, XConnection, XError};
|
||||
|
||||
lazy_static! {
|
||||
static ref GLOBAL_LOCK: Mutex<()> = Default::default();
|
||||
}
|
||||
|
||||
unsafe fn open_im(
|
||||
xconn: &Arc<XConnection>,
|
||||
locale_modifiers: &CStr,
|
||||
) -> Option<ffi::XIM> {
|
||||
let _lock = GLOBAL_LOCK.lock();
|
||||
|
||||
// XSetLocaleModifiers returns...
|
||||
// * The current locale modifiers if it's given a NULL pointer.
|
||||
// * The new locale modifiers if we succeeded in setting them.
|
||||
@@ -85,13 +93,11 @@ enum GetXimServersError {
|
||||
// modifiers, since we don't want a user who's looking at logs to ask "am I supposed to set
|
||||
// XMODIFIERS to `@server=ibus`?!?"
|
||||
unsafe fn get_xim_servers(xconn: &Arc<XConnection>) -> Result<Vec<String>, GetXimServersError> {
|
||||
let servers_atom = util::get_atom(&xconn, b"XIM_SERVERS\0")
|
||||
.map_err(GetXimServersError::XError)?;
|
||||
let servers_atom = xconn.get_atom_unchecked(b"XIM_SERVERS\0");
|
||||
|
||||
let root = (xconn.xlib.XDefaultRootWindow)(xconn.display);
|
||||
|
||||
let mut atoms: Vec<ffi::Atom> = util::get_property(
|
||||
&xconn,
|
||||
let mut atoms: Vec<ffi::Atom> = xconn.get_property(
|
||||
root,
|
||||
servers_atom,
|
||||
ffi::XA_ATOM,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,110 +1,85 @@
|
||||
use std::sync::Arc;
|
||||
use std::slice;
|
||||
use std::os::raw::*;
|
||||
|
||||
use super::XConnection;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
#[derive(Clone)]
|
||||
use {PhysicalPosition, PhysicalSize};
|
||||
use super::{util, XConnection, XError};
|
||||
use super::ffi::{
|
||||
RRCrtcChangeNotifyMask,
|
||||
RROutputPropertyNotifyMask,
|
||||
RRScreenChangeNotifyMask,
|
||||
True,
|
||||
Window,
|
||||
XRRScreenResources,
|
||||
};
|
||||
|
||||
// Used to test XRandR < 1.5 code path. This should always be committed as false.
|
||||
const FORCE_RANDR_COMPAT: bool = false;
|
||||
// Also used for testing. This should always be committed as false.
|
||||
const DISABLE_MONITOR_LIST_CACHING: bool = false;
|
||||
|
||||
lazy_static! {
|
||||
static ref XRANDR_VERSION: Mutex<Option<(c_int, c_int)>> = Mutex::default();
|
||||
static ref MONITORS: Mutex<Option<Vec<MonitorId>>> = Mutex::default();
|
||||
}
|
||||
|
||||
fn version_is_at_least(major: c_int, minor: c_int) -> bool {
|
||||
if let Some((avail_major, avail_minor)) = *XRANDR_VERSION.lock() {
|
||||
if avail_major == major {
|
||||
avail_minor >= minor
|
||||
} else {
|
||||
avail_major > major
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invalidate_cached_monitor_list() -> Option<Vec<MonitorId>> {
|
||||
// We update this lazily.
|
||||
(*MONITORS.lock()).take()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
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
|
||||
position: (i32, i32),
|
||||
/// If the monitor is the primary one
|
||||
primary: bool,
|
||||
/// The DPI scaling factor
|
||||
hidpi_factor: f32,
|
||||
}
|
||||
|
||||
pub fn get_available_monitors(x: &Arc<XConnection>) -> Vec<MonitorId> {
|
||||
let mut available = Vec::new();
|
||||
unsafe {
|
||||
let root = (x.xlib.XDefaultRootWindow)(x.display);
|
||||
let resources = (x.xrandr.XRRGetScreenResources)(x.display, root);
|
||||
|
||||
if let Some(ref xrandr_1_5) = x.xrandr_1_5 {
|
||||
// We're in XRandR >= 1.5, enumerate Monitors to handle things like MST and videowalls
|
||||
let mut nmonitors = 0;
|
||||
let monitors = (xrandr_1_5.XRRGetMonitors)(x.display, root, 1, &mut nmonitors);
|
||||
for i in 0..nmonitors {
|
||||
let monitor = *(monitors.offset(i as isize));
|
||||
let output = (xrandr_1_5.XRRGetOutputInfo)(x.display, resources, *(monitor.outputs.offset(0)));
|
||||
let nameslice = slice::from_raw_parts((*output).name as *mut u8, (*output).nameLen as usize);
|
||||
let name = String::from_utf8_lossy(nameslice).into_owned();
|
||||
let hidpi_factor = {
|
||||
let x_mm = (*output).mm_width as f32;
|
||||
let y_mm = (*output).mm_height as f32;
|
||||
let x_px = monitor.width as f32;
|
||||
let y_px = monitor.height as f32;
|
||||
let ppmm = ((x_px * y_px) / (x_mm * y_mm)).sqrt();
|
||||
|
||||
// Quantize 1/12 step size
|
||||
((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0)
|
||||
};
|
||||
(xrandr_1_5.XRRFreeOutputInfo)(output);
|
||||
available.push(MonitorId{
|
||||
id: i as u32,
|
||||
name,
|
||||
hidpi_factor,
|
||||
dimensions: (monitor.width as u32, monitor.height as u32),
|
||||
position: (monitor.x as i32, monitor.y as i32),
|
||||
primary: (monitor.primary != 0),
|
||||
});
|
||||
}
|
||||
(xrandr_1_5.XRRFreeMonitors)(monitors);
|
||||
} else {
|
||||
// We're in XRandR < 1.5, enumerate CRTCs. Everything will work but MST and
|
||||
// videowall setups will show more monitors than the logical groups the user
|
||||
// cares about
|
||||
for i in 0..(*resources).ncrtc {
|
||||
let crtcid = *((*resources).crtcs.offset(i as isize));
|
||||
let crtc = (x.xrandr.XRRGetCrtcInfo)(x.display, resources, crtcid);
|
||||
if (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0 {
|
||||
let output = (x.xrandr.XRRGetOutputInfo)(x.display, resources, *((*crtc).outputs.offset(0)));
|
||||
let nameslice = slice::from_raw_parts((*output).name as *mut u8, (*output).nameLen as usize);
|
||||
let name = String::from_utf8_lossy(nameslice).into_owned();
|
||||
|
||||
let hidpi_factor = {
|
||||
let x_mm = (*output).mm_width as f32;
|
||||
let y_mm = (*output).mm_height as f32;
|
||||
let x_px = (*crtc).width as f32;
|
||||
let y_px = (*crtc).height as f32;
|
||||
let ppmm = ((x_px * y_px) / (x_mm * y_mm)).sqrt();
|
||||
|
||||
// Quantize 1/12 step size
|
||||
((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0)
|
||||
};
|
||||
|
||||
(x.xrandr.XRRFreeOutputInfo)(output);
|
||||
available.push(MonitorId{
|
||||
id: crtcid as u32,
|
||||
name,
|
||||
hidpi_factor,
|
||||
dimensions: ((*crtc).width as u32, (*crtc).height as u32),
|
||||
position: ((*crtc).x as i32, (*crtc).y as i32),
|
||||
primary: true,
|
||||
});
|
||||
}
|
||||
(x.xrandr.XRRFreeCrtcInfo)(crtc);
|
||||
}
|
||||
}
|
||||
(x.xrandr.XRRFreeScreenResources)(resources);
|
||||
}
|
||||
available
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(x: &Arc<XConnection>) -> MonitorId {
|
||||
get_available_monitors(x).into_iter().find(|m| m.primary)
|
||||
// 'no primary' case is better handled picking some existing monitor
|
||||
.or_else(|| get_available_monitors(x).into_iter().next())
|
||||
.expect("[winit] Failed to find any x11 monitor")
|
||||
/// The DPI scale factor
|
||||
pub(crate) hidpi_factor: f64,
|
||||
/// Used to determine which windows are on this monitor
|
||||
pub(crate) rect: util::AaRect,
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
fn from_repr(
|
||||
xconn: &XConnection,
|
||||
resources: *mut XRRScreenResources,
|
||||
id: u32,
|
||||
repr: util::MonitorRepr,
|
||||
primary: bool,
|
||||
) -> 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::AaRect::new(position, dimensions);
|
||||
MonitorId {
|
||||
id,
|
||||
name,
|
||||
hidpi_factor,
|
||||
dimensions,
|
||||
position,
|
||||
primary,
|
||||
rect,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
Some(self.name.clone())
|
||||
}
|
||||
@@ -114,16 +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
|
||||
}
|
||||
}
|
||||
|
||||
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 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()
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
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(
|
||||
self,
|
||||
resources,
|
||||
monitor_index as u32,
|
||||
monitor.into(),
|
||||
is_primary,
|
||||
));
|
||||
}
|
||||
(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;
|
||||
}
|
||||
}
|
||||
|
||||
(self.xrandr.XRRFreeScreenResources)(resources);
|
||||
available
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
#[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.")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt::Debug;
|
||||
use std::os::raw::*;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
@@ -12,48 +13,60 @@ lazy_static! {
|
||||
static ref ATOM_CACHE: Mutex<AtomCache> = Mutex::new(HashMap::with_capacity(2048));
|
||||
}
|
||||
|
||||
pub unsafe fn get_atom(xconn: &Arc<XConnection>, name: &[u8]) -> Result<ffi::Atom, XError> {
|
||||
let name = CStr::from_bytes_with_nul_unchecked(name); // I trust you. Don't let me down.
|
||||
let mut atom_cache_lock = ATOM_CACHE.lock();
|
||||
let cached_atom = (*atom_cache_lock).get(name).cloned();
|
||||
if let Some(atom) = cached_atom {
|
||||
Ok(atom)
|
||||
} else {
|
||||
let atom = (xconn.xlib.XInternAtom)(
|
||||
xconn.display,
|
||||
name.as_ptr() as *const c_char,
|
||||
impl XConnection {
|
||||
pub fn get_atom<T: AsRef<CStr> + Debug>(&self, name: T) -> ffi::Atom {
|
||||
let name = name.as_ref();
|
||||
let mut atom_cache_lock = ATOM_CACHE.lock();
|
||||
let cached_atom = (*atom_cache_lock).get(name).cloned();
|
||||
if let Some(atom) = cached_atom {
|
||||
atom
|
||||
} else {
|
||||
let atom = unsafe { (self.xlib.XInternAtom)(
|
||||
self.display,
|
||||
name.as_ptr() as *const c_char,
|
||||
ffi::False,
|
||||
) };
|
||||
if atom == 0 {
|
||||
let msg = format!(
|
||||
"`XInternAtom` failed, which really shouldn't happen. Atom: {:?}, Error: {:#?}",
|
||||
name,
|
||||
self.check_errors(),
|
||||
);
|
||||
panic!(msg);
|
||||
}
|
||||
/*println!(
|
||||
"XInternAtom name:{:?} atom:{:?}",
|
||||
name,
|
||||
atom,
|
||||
);*/
|
||||
(*atom_cache_lock).insert(name.to_owned(), atom);
|
||||
atom
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_atom_unchecked(&self, name: &[u8]) -> ffi::Atom {
|
||||
debug_assert!(CStr::from_bytes_with_nul(name).is_ok());
|
||||
let name = CStr::from_bytes_with_nul_unchecked(name);
|
||||
self.get_atom(name)
|
||||
}
|
||||
|
||||
// Note: this doesn't use caching, for the sake of simplicity.
|
||||
// If you're dealing with this many atoms, you'll usually want to cache them locally anyway.
|
||||
pub unsafe fn get_atoms(&self, names: &[*mut c_char]) -> Result<Vec<ffi::Atom>, XError> {
|
||||
let mut atoms = Vec::with_capacity(names.len());
|
||||
(self.xlib.XInternAtoms)(
|
||||
self.display,
|
||||
names.as_ptr() as *mut _,
|
||||
names.len() as c_int,
|
||||
ffi::False,
|
||||
atoms.as_mut_ptr(),
|
||||
);
|
||||
self.check_errors()?;
|
||||
atoms.set_len(names.len());
|
||||
/*println!(
|
||||
"XInternAtom name:{:?} atom:{:?}",
|
||||
name,
|
||||
atom,
|
||||
"XInternAtoms atoms:{:?}",
|
||||
atoms,
|
||||
);*/
|
||||
xconn.check_errors()?;
|
||||
(*atom_cache_lock).insert(name.to_owned(), atom);
|
||||
Ok(atom)
|
||||
Ok(atoms)
|
||||
}
|
||||
}
|
||||
|
||||
// Note: this doesn't use caching, for the sake of simplicity.
|
||||
// If you're dealing with this many atoms, you'll usually want to cache them locally anyway.
|
||||
pub unsafe fn get_atoms(
|
||||
xconn: &Arc<XConnection>,
|
||||
names: &[*mut c_char],
|
||||
) -> Result<Vec<ffi::Atom>, XError> {
|
||||
let mut atoms = Vec::with_capacity(names.len());
|
||||
(xconn.xlib.XInternAtoms)(
|
||||
xconn.display,
|
||||
names.as_ptr() as *mut _,
|
||||
names.len() as c_int,
|
||||
ffi::False,
|
||||
atoms.as_mut_ptr(),
|
||||
);
|
||||
xconn.check_errors()?;
|
||||
atoms.set_len(names.len());
|
||||
/*println!(
|
||||
"XInternAtoms atoms:{:?}",
|
||||
atoms,
|
||||
);*/
|
||||
Ok(atoms)
|
||||
}
|
||||
|
||||
95
src/platform/linux/x11/util/client_msg.rs
Normal file
95
src/platform/linux/x11/util/client_msg.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use super::*;
|
||||
|
||||
pub type ClientMsgPayload = [c_long; 5];
|
||||
|
||||
impl XConnection {
|
||||
pub fn send_event<T: Into<ffi::XEvent>>(
|
||||
&self,
|
||||
target_window: c_ulong,
|
||||
event_mask: Option<c_long>,
|
||||
event: T,
|
||||
) -> Flusher {
|
||||
let event_mask = event_mask.unwrap_or(ffi::NoEventMask);
|
||||
unsafe {
|
||||
(self.xlib.XSendEvent)(
|
||||
self.display,
|
||||
target_window,
|
||||
ffi::False,
|
||||
event_mask,
|
||||
&mut event.into(),
|
||||
);
|
||||
}
|
||||
Flusher::new(self)
|
||||
}
|
||||
|
||||
pub fn send_client_msg(
|
||||
&self,
|
||||
window: c_ulong, // The window this is "about"; not necessarily this window
|
||||
target_window: c_ulong, // The window we're sending to
|
||||
message_type: ffi::Atom,
|
||||
event_mask: Option<c_long>,
|
||||
data: ClientMsgPayload,
|
||||
) -> Flusher {
|
||||
let mut event: ffi::XClientMessageEvent = unsafe { mem::uninitialized() };
|
||||
event.type_ = ffi::ClientMessage;
|
||||
event.display = self.display;
|
||||
event.window = window;
|
||||
event.message_type = message_type;
|
||||
event.format = c_long::FORMAT as c_int;
|
||||
event.data = unsafe { mem::transmute(data) };
|
||||
self.send_event(target_window, event_mask, event)
|
||||
}
|
||||
|
||||
// Prepare yourself for the ultimate in unsafety!
|
||||
// You should favor `send_client_msg` whenever possible, but some protocols (i.e. startup notification) require you
|
||||
// to send more than one message worth of data.
|
||||
pub fn send_client_msg_multi<T: Formattable>(
|
||||
&self,
|
||||
window: c_ulong, // The window this is "about"; not necessarily this window
|
||||
target_window: c_ulong, // The window we're sending to
|
||||
message_type: ffi::Atom,
|
||||
event_mask: Option<c_long>,
|
||||
data: &[T],
|
||||
) -> Flusher {
|
||||
let format = T::FORMAT;
|
||||
let size_of_t = mem::size_of::<T>();
|
||||
debug_assert_eq!(size_of_t, format.get_actual_size());
|
||||
let mut event: ffi::XClientMessageEvent = unsafe { mem::uninitialized() };
|
||||
event.type_ = ffi::ClientMessage;
|
||||
event.display = self.display;
|
||||
event.window = window;
|
||||
event.message_type = message_type;
|
||||
event.format = format as c_int;
|
||||
|
||||
let t_per_payload = format.get_payload_size() / size_of_t;
|
||||
assert!(t_per_payload > 0);
|
||||
let payload_count = data.len() / t_per_payload;
|
||||
let payload_remainder = data.len() % t_per_payload;
|
||||
let payload_ptr = data.as_ptr() as *const ClientMsgPayload;
|
||||
|
||||
let mut payload_index = 0;
|
||||
while payload_index < payload_count {
|
||||
let payload = unsafe { payload_ptr.offset(payload_index as isize) };
|
||||
payload_index += 1;
|
||||
event.data = unsafe { mem::transmute(*payload) };
|
||||
self.send_event(target_window, event_mask, &event).queue();
|
||||
}
|
||||
|
||||
if payload_remainder > 0 {
|
||||
let mut payload: ClientMsgPayload = [0; 5];
|
||||
let t_payload = payload.as_mut_ptr() as *mut T;
|
||||
let invalid_payload = unsafe { payload_ptr.offset(payload_index as isize) };
|
||||
let invalid_t_payload = invalid_payload as *const T;
|
||||
let mut t_index = 0;
|
||||
while t_index < payload_remainder {
|
||||
let valid_t = unsafe { invalid_t_payload.offset(t_index as isize) };
|
||||
unsafe { (*t_payload.offset(t_index as isize)) = (*valid_t).clone() };
|
||||
t_index += 1;
|
||||
}
|
||||
event.data = unsafe { mem::transmute(payload) };
|
||||
self.send_event(target_window, event_mask, &event).queue();
|
||||
}
|
||||
|
||||
Flusher::new(self)
|
||||
}
|
||||
}
|
||||
58
src/platform/linux/x11/util/format.rs
Normal file
58
src/platform/linux/x11/util/format.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use std::fmt::Debug;
|
||||
use std::mem;
|
||||
use std::os::raw::*;
|
||||
|
||||
// This isn't actually the number of the bits in the format.
|
||||
// X11 does a match on this value to determine which type to call sizeof on.
|
||||
// Thus, we use 32 for c_long, since 32 maps to c_long which maps to 64.
|
||||
// ...if that sounds confusing, then you know why this enum is here.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Format {
|
||||
Char = 8,
|
||||
Short = 16,
|
||||
Long = 32,
|
||||
}
|
||||
|
||||
impl Format {
|
||||
pub fn from_format(format: usize) -> Option<Self> {
|
||||
match format {
|
||||
8 => Some(Format::Char),
|
||||
16 => Some(Format::Short),
|
||||
32 => Some(Format::Long),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_same_size_as<T>(&self) -> bool {
|
||||
mem::size_of::<T>() == self.get_actual_size()
|
||||
}
|
||||
|
||||
pub fn get_actual_size(&self) -> usize {
|
||||
match self {
|
||||
&Format::Char => mem::size_of::<c_char>(),
|
||||
&Format::Short => mem::size_of::<c_short>(),
|
||||
&Format::Long => mem::size_of::<c_long>(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_payload_size(&self) -> usize {
|
||||
match self {
|
||||
// Due to the wonders of X11, half the space goes unused if you're not using longs (on 64-bit).
|
||||
&Format::Char => mem::size_of::<c_char>() * 20,
|
||||
&Format::Short => mem::size_of::<c_short>() * 10,
|
||||
&Format::Long => mem::size_of::<c_long>() * 5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Formattable: Debug + Clone + Copy + PartialEq + PartialOrd {
|
||||
const FORMAT: Format;
|
||||
}
|
||||
|
||||
// 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_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; }
|
||||
impl Formattable for c_long { const FORMAT: Format = Format::Long; }
|
||||
impl Formattable for c_ulong { const FORMAT: Format = Format::Long; }
|
||||
@@ -1,4 +1,40 @@
|
||||
use std::cmp;
|
||||
|
||||
use super::*;
|
||||
use {LogicalPosition, LogicalSize};
|
||||
|
||||
// Friendly neighborhood axis-aligned rectangle
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AaRect {
|
||||
x: i64,
|
||||
y: i64,
|
||||
width: i64,
|
||||
height: i64,
|
||||
}
|
||||
|
||||
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);
|
||||
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.x + self.width, other.x + other.width) - cmp::max(self.x, other.x),
|
||||
);
|
||||
let y_overlap = cmp::max(
|
||||
0,
|
||||
cmp::min(self.y + self.height, other.y + other.height) - cmp::max(self.y, other.y),
|
||||
);
|
||||
x_overlap * y_overlap
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TranslatedCoords {
|
||||
@@ -7,30 +43,6 @@ pub struct TranslatedCoords {
|
||||
pub child: ffi::Window,
|
||||
}
|
||||
|
||||
// This is adequate for get_inner_position
|
||||
pub unsafe fn translate_coords(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
root: ffi::Window,
|
||||
) -> Result<TranslatedCoords, XError> {
|
||||
let mut translated_coords: TranslatedCoords = mem::uninitialized();
|
||||
|
||||
(xconn.xlib.XTranslateCoordinates)(
|
||||
xconn.display,
|
||||
window,
|
||||
root,
|
||||
0,
|
||||
0,
|
||||
&mut translated_coords.x_rel_root,
|
||||
&mut translated_coords.y_rel_root,
|
||||
&mut translated_coords.child,
|
||||
);
|
||||
|
||||
//println!("XTranslateCoordinates coords:{:?}", translated_coords);
|
||||
|
||||
xconn.check_errors().map(|_| translated_coords)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Geometry {
|
||||
pub root: ffi::Window,
|
||||
@@ -51,30 +63,6 @@ pub struct Geometry {
|
||||
pub depth: c_uint,
|
||||
}
|
||||
|
||||
// This is adequate for get_inner_size
|
||||
pub unsafe fn get_geometry(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
) -> Result<Geometry, XError> {
|
||||
let mut geometry: Geometry = mem::uninitialized();
|
||||
|
||||
let _status = (xconn.xlib.XGetGeometry)(
|
||||
xconn.display,
|
||||
window,
|
||||
&mut geometry.root,
|
||||
&mut geometry.x_rel_parent,
|
||||
&mut geometry.y_rel_parent,
|
||||
&mut geometry.width,
|
||||
&mut geometry.height,
|
||||
&mut geometry.border,
|
||||
&mut geometry.depth,
|
||||
);
|
||||
|
||||
//println!("XGetGeometry geo:{:?}", geometry);
|
||||
|
||||
xconn.check_errors().map(|_| geometry)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FrameExtents {
|
||||
pub left: c_ulong,
|
||||
@@ -91,109 +79,24 @@ impl FrameExtents {
|
||||
pub fn from_border(border: c_ulong) -> Self {
|
||||
Self::new(border, border, border, border)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_frame_extents(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
) -> Option<self::FrameExtents> {
|
||||
let extents_atom = unsafe { self::get_atom(xconn, b"_NET_FRAME_EXTENTS\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_FRAME_EXTENTS)");
|
||||
|
||||
if !self::hint_is_supported(extents_atom) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Of the WMs tested, xmonad, i3, dwm, IceWM (1.3.x and earlier), and blackbox don't
|
||||
// support this. As this is part of EWMH (Extended Window Manager Hints), it's likely to
|
||||
// be unsupported by many smaller WMs.
|
||||
let extents: Option<Vec<c_ulong>> = unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
window,
|
||||
extents_atom,
|
||||
ffi::XA_CARDINAL,
|
||||
)
|
||||
}.ok();
|
||||
|
||||
extents.and_then(|extents| {
|
||||
if extents.len() >= 4 {
|
||||
Some(self::FrameExtents {
|
||||
left: extents[0],
|
||||
right: extents[1],
|
||||
top: extents[2],
|
||||
bottom: extents[3],
|
||||
})
|
||||
} else {
|
||||
None
|
||||
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),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_top_level(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
root: ffi::Window,
|
||||
) -> Option<bool> {
|
||||
let client_list_atom = unsafe { self::get_atom(xconn, b"_NET_CLIENT_LIST\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_CLIENT_LIST)");
|
||||
|
||||
if !self::hint_is_supported(client_list_atom) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let client_list: Option<Vec<ffi::Window>> = unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
root,
|
||||
client_list_atom,
|
||||
ffi::XA_WINDOW,
|
||||
)
|
||||
}.ok();
|
||||
|
||||
client_list.map(|client_list| client_list.contains(&window))
|
||||
}
|
||||
|
||||
unsafe fn get_parent_window(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
) -> Result<ffi::Window, XError> {
|
||||
let mut root: ffi::Window = mem::uninitialized();
|
||||
let mut parent: ffi::Window = mem::uninitialized();
|
||||
let mut children: *mut ffi::Window = ptr::null_mut();
|
||||
let mut nchildren: c_uint = mem::uninitialized();
|
||||
|
||||
let _status = (xconn.xlib.XQueryTree)(
|
||||
xconn.display,
|
||||
window,
|
||||
&mut root,
|
||||
&mut parent,
|
||||
&mut children,
|
||||
&mut nchildren,
|
||||
);
|
||||
|
||||
// The list of children isn't used
|
||||
if children != ptr::null_mut() {
|
||||
(xconn.xlib.XFree)(children as *mut _);
|
||||
}
|
||||
|
||||
xconn.check_errors().map(|_| parent)
|
||||
}
|
||||
|
||||
fn climb_hierarchy(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
root: ffi::Window,
|
||||
) -> Result<ffi::Window, XError> {
|
||||
let mut outer_window = window;
|
||||
loop {
|
||||
let candidate = unsafe { get_parent_window(xconn, outer_window) }?;
|
||||
if candidate == root {
|
||||
break;
|
||||
}
|
||||
outer_window = candidate;
|
||||
}
|
||||
Ok(outer_window)
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LogicalFrameExtents {
|
||||
pub left: f64,
|
||||
pub right: f64,
|
||||
pub top: f64,
|
||||
pub bottom: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@@ -219,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(
|
||||
@@ -229,122 +142,246 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_frame_extents_heuristic(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
root: ffi::Window,
|
||||
) -> FrameExtentsHeuristic {
|
||||
use self::FrameExtentsHeuristicPath::*;
|
||||
impl XConnection {
|
||||
// This is adequate for get_inner_position
|
||||
pub fn translate_coords(&self, window: ffi::Window, root: ffi::Window) -> Result<TranslatedCoords, XError> {
|
||||
let mut translated_coords: TranslatedCoords = unsafe { mem::uninitialized() };
|
||||
unsafe {
|
||||
(self.xlib.XTranslateCoordinates)(
|
||||
self.display,
|
||||
window,
|
||||
root,
|
||||
0,
|
||||
0,
|
||||
&mut translated_coords.x_rel_root,
|
||||
&mut translated_coords.y_rel_root,
|
||||
&mut translated_coords.child,
|
||||
);
|
||||
}
|
||||
//println!("XTranslateCoordinates coords:{:?}", translated_coords);
|
||||
self.check_errors().map(|_| translated_coords)
|
||||
}
|
||||
|
||||
// Position relative to root window.
|
||||
// With rare exceptions, this is the position of a nested window. Cases where the window
|
||||
// isn't nested are outlined in the comments throghout this function, but in addition to
|
||||
// that, fullscreen windows often aren't nested.
|
||||
let (inner_y_rel_root, child) = {
|
||||
let coords = unsafe { translate_coords(xconn, window, root) }
|
||||
.expect("Failed to translate window coordinates");
|
||||
(
|
||||
coords.y_rel_root,
|
||||
coords.child,
|
||||
)
|
||||
};
|
||||
// This is adequate for get_inner_size
|
||||
pub fn get_geometry(&self, window: ffi::Window) -> Result<Geometry, XError> {
|
||||
let mut geometry: Geometry = unsafe { mem::uninitialized() };
|
||||
let _status = unsafe {
|
||||
(self.xlib.XGetGeometry)(
|
||||
self.display,
|
||||
window,
|
||||
&mut geometry.root,
|
||||
&mut geometry.x_rel_parent,
|
||||
&mut geometry.y_rel_parent,
|
||||
&mut geometry.width,
|
||||
&mut geometry.height,
|
||||
&mut geometry.border,
|
||||
&mut geometry.depth,
|
||||
)
|
||||
};
|
||||
//println!("XGetGeometry geo:{:?}", geometry);
|
||||
self.check_errors().map(|_| geometry)
|
||||
}
|
||||
|
||||
let (width, height, border) = {
|
||||
let inner_geometry = unsafe { get_geometry(xconn, window) }
|
||||
.expect("Failed to get inner window geometry");
|
||||
(
|
||||
inner_geometry.width,
|
||||
inner_geometry.height,
|
||||
inner_geometry.border,
|
||||
)
|
||||
};
|
||||
fn get_frame_extents(&self, window: ffi::Window) -> Option<FrameExtents> {
|
||||
let extents_atom = unsafe { self.get_atom_unchecked(b"_NET_FRAME_EXTENTS\0") };
|
||||
|
||||
// The first condition is only false for un-nested windows, but isn't always false for
|
||||
// un-nested windows. Mutter/Muffin/Budgie and Marco present a mysterious discrepancy:
|
||||
// when y is on the range [0, 2] and if the window has been unfocused since being
|
||||
// undecorated (or was undecorated upon construction), the first condition is true,
|
||||
// requiring us to rely on the second condition.
|
||||
let nested = !(window == child || is_top_level(xconn, child, root) == Some(true));
|
||||
|
||||
// Hopefully the WM supports EWMH, allowing us to get exact info on the window frames.
|
||||
if let Some(mut frame_extents) = get_frame_extents(xconn, window) {
|
||||
// Mutter/Muffin/Budgie and Marco preserve their decorated frame extents when
|
||||
// decorations are disabled, but since the window becomes un-nested, it's easy to
|
||||
// catch.
|
||||
if !nested {
|
||||
frame_extents = FrameExtents::new(0, 0, 0, 0);
|
||||
if !hint_is_supported(extents_atom) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// The difference between the nested window's position and the outermost window's
|
||||
// position is equivalent to the frame size. In most scenarios, this is equivalent to
|
||||
// manually climbing the hierarchy as is done in the case below. Here's a list of
|
||||
// known discrepancies:
|
||||
// * Mutter/Muffin/Budgie gives decorated windows a margin of 9px (only 7px on top) in
|
||||
// addition to a 1px semi-transparent border. The margin can be easily observed by
|
||||
// using a screenshot tool to get a screenshot of a selected window, and is
|
||||
// presumably used for drawing drop shadows. Getting window geometry information
|
||||
// via hierarchy-climbing results in this margin being included in both the
|
||||
// position and outer size, so a window positioned at (0, 0) would be reported as
|
||||
// having a position (-10, -8).
|
||||
// * Compiz has a drop shadow margin just like Mutter/Muffin/Budgie, though it's 10px
|
||||
// on all sides, and there's no additional border.
|
||||
// * Enlightenment otherwise gets a y position equivalent to inner_y_rel_root.
|
||||
// Without decorations, there's no difference. This is presumably related to
|
||||
// Enlightenment's fairly unique concept of window position; it interprets
|
||||
// positions given to XMoveWindow as a client area position rather than a position
|
||||
// of the overall window.
|
||||
// Of the WMs tested, xmonad, i3, dwm, IceWM (1.3.x and earlier), and blackbox don't
|
||||
// support this. As this is part of EWMH (Extended Window Manager Hints), it's likely to
|
||||
// be unsupported by many smaller WMs.
|
||||
let extents: Option<Vec<c_ulong>> = self.get_property(
|
||||
window,
|
||||
extents_atom,
|
||||
ffi::XA_CARDINAL,
|
||||
).ok();
|
||||
|
||||
FrameExtentsHeuristic {
|
||||
frame_extents,
|
||||
heuristic_path: Supported,
|
||||
extents.and_then(|extents| {
|
||||
if extents.len() >= 4 {
|
||||
Some(FrameExtents {
|
||||
left: extents[0],
|
||||
right: extents[1],
|
||||
top: extents[2],
|
||||
bottom: extents[3],
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_top_level(&self, window: ffi::Window, root: ffi::Window) -> Option<bool> {
|
||||
let client_list_atom = unsafe { self.get_atom_unchecked(b"_NET_CLIENT_LIST\0") };
|
||||
|
||||
if !hint_is_supported(client_list_atom) {
|
||||
return None;
|
||||
}
|
||||
} else if nested {
|
||||
// If the position value we have is for a nested window used as the client area, we'll
|
||||
// just climb up the hierarchy and get the geometry of the outermost window we're
|
||||
// nested in.
|
||||
let outer_window = climb_hierarchy(xconn, window, root)
|
||||
.expect("Failed to climb window hierarchy");
|
||||
|
||||
let (outer_y, outer_width, outer_height) = {
|
||||
let outer_geometry = unsafe { get_geometry(xconn, outer_window) }
|
||||
.expect("Failed to get outer window geometry");
|
||||
let client_list: Option<Vec<ffi::Window>> = self.get_property(
|
||||
root,
|
||||
client_list_atom,
|
||||
ffi::XA_WINDOW,
|
||||
).ok();
|
||||
|
||||
client_list.map(|client_list| client_list.contains(&window))
|
||||
}
|
||||
|
||||
fn get_parent_window(&self, window: ffi::Window) -> Result<ffi::Window, XError> {
|
||||
let parent = unsafe {
|
||||
let mut root: ffi::Window = mem::uninitialized();
|
||||
let mut parent: ffi::Window = mem::uninitialized();
|
||||
let mut children: *mut ffi::Window = ptr::null_mut();
|
||||
let mut nchildren: c_uint = mem::uninitialized();
|
||||
|
||||
// What's filled into `parent` if `window` is the root window?
|
||||
let _status = (self.xlib.XQueryTree)(
|
||||
self.display,
|
||||
window,
|
||||
&mut root,
|
||||
&mut parent,
|
||||
&mut children,
|
||||
&mut nchildren,
|
||||
);
|
||||
|
||||
// The list of children isn't used
|
||||
if children != ptr::null_mut() {
|
||||
(self.xlib.XFree)(children as *mut _);
|
||||
}
|
||||
|
||||
parent
|
||||
};
|
||||
self.check_errors().map(|_| parent)
|
||||
}
|
||||
|
||||
fn climb_hierarchy(&self, window: ffi::Window, root: ffi::Window) -> Result<ffi::Window, XError> {
|
||||
let mut outer_window = window;
|
||||
loop {
|
||||
let candidate = self.get_parent_window(outer_window)?;
|
||||
if candidate == root {
|
||||
break;
|
||||
}
|
||||
outer_window = candidate;
|
||||
}
|
||||
Ok(outer_window)
|
||||
}
|
||||
|
||||
pub fn get_frame_extents_heuristic(&self, window: ffi::Window, root: ffi::Window) -> FrameExtentsHeuristic {
|
||||
use self::FrameExtentsHeuristicPath::*;
|
||||
|
||||
// Position relative to root window.
|
||||
// With rare exceptions, this is the position of a nested window. Cases where the window
|
||||
// isn't nested are outlined in the comments throghout this function, but in addition to
|
||||
// that, fullscreen windows often aren't nested.
|
||||
let (inner_y_rel_root, child) = {
|
||||
let coords = self.translate_coords(window, root).expect("Failed to translate window coordinates");
|
||||
(
|
||||
outer_geometry.y_rel_parent,
|
||||
outer_geometry.width,
|
||||
outer_geometry.height,
|
||||
coords.y_rel_root,
|
||||
coords.child,
|
||||
)
|
||||
};
|
||||
|
||||
// Since we have the geometry of the outermost window and the geometry of the client
|
||||
// area, we can figure out what's in between.
|
||||
let diff_x = outer_width.saturating_sub(width);
|
||||
let diff_y = outer_height.saturating_sub(height);
|
||||
let offset_y = inner_y_rel_root.saturating_sub(outer_y) as c_uint;
|
||||
let (width, height, border) = {
|
||||
let inner_geometry = self.get_geometry(window).expect("Failed to get inner window geometry");
|
||||
(
|
||||
inner_geometry.width,
|
||||
inner_geometry.height,
|
||||
inner_geometry.border,
|
||||
)
|
||||
};
|
||||
|
||||
let left = diff_x / 2;
|
||||
let right = left;
|
||||
let top = offset_y;
|
||||
let bottom = diff_y.saturating_sub(offset_y);
|
||||
// The first condition is only false for un-nested windows, but isn't always false for
|
||||
// un-nested windows. Mutter/Muffin/Budgie and Marco present a mysterious discrepancy:
|
||||
// when y is on the range [0, 2] and if the window has been unfocused since being
|
||||
// undecorated (or was undecorated upon construction), the first condition is true,
|
||||
// requiring us to rely on the second condition.
|
||||
let nested = !(window == child || self.is_top_level(child, root) == Some(true));
|
||||
|
||||
let frame_extents = FrameExtents::new(
|
||||
left.into(),
|
||||
right.into(),
|
||||
top.into(),
|
||||
bottom.into(),
|
||||
);
|
||||
FrameExtentsHeuristic {
|
||||
frame_extents,
|
||||
heuristic_path: UnsupportedNested,
|
||||
}
|
||||
} else {
|
||||
// This is the case for xmonad and dwm, AKA the only WMs tested that supplied a
|
||||
// border value. This is convenient, since we can use it to get an accurate frame.
|
||||
let frame_extents = FrameExtents::from_border(border.into());
|
||||
FrameExtentsHeuristic {
|
||||
frame_extents,
|
||||
heuristic_path: UnsupportedBordered,
|
||||
// Hopefully the WM supports EWMH, allowing us to get exact info on the window frames.
|
||||
if let Some(mut frame_extents) = self.get_frame_extents(window) {
|
||||
// Mutter/Muffin/Budgie and Marco preserve their decorated frame extents when
|
||||
// decorations are disabled, but since the window becomes un-nested, it's easy to
|
||||
// catch.
|
||||
if !nested {
|
||||
frame_extents = FrameExtents::new(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
// The difference between the nested window's position and the outermost window's
|
||||
// position is equivalent to the frame size. In most scenarios, this is equivalent to
|
||||
// manually climbing the hierarchy as is done in the case below. Here's a list of
|
||||
// known discrepancies:
|
||||
// * Mutter/Muffin/Budgie gives decorated windows a margin of 9px (only 7px on top) in
|
||||
// addition to a 1px semi-transparent border. The margin can be easily observed by
|
||||
// using a screenshot tool to get a screenshot of a selected window, and is
|
||||
// presumably used for drawing drop shadows. Getting window geometry information
|
||||
// via hierarchy-climbing results in this margin being included in both the
|
||||
// position and outer size, so a window positioned at (0, 0) would be reported as
|
||||
// having a position (-10, -8).
|
||||
// * Compiz has a drop shadow margin just like Mutter/Muffin/Budgie, though it's 10px
|
||||
// on all sides, and there's no additional border.
|
||||
// * Enlightenment otherwise gets a y position equivalent to inner_y_rel_root.
|
||||
// Without decorations, there's no difference. This is presumably related to
|
||||
// Enlightenment's fairly unique concept of window position; it interprets
|
||||
// positions given to XMoveWindow as a client area position rather than a position
|
||||
// of the overall window.
|
||||
|
||||
FrameExtentsHeuristic {
|
||||
frame_extents,
|
||||
heuristic_path: Supported,
|
||||
}
|
||||
} else if nested {
|
||||
// If the position value we have is for a nested window used as the client area, we'll
|
||||
// just climb up the hierarchy and get the geometry of the outermost window we're
|
||||
// nested in.
|
||||
let outer_window = self.climb_hierarchy(window, root).expect("Failed to climb window hierarchy");
|
||||
let (outer_y, outer_width, outer_height) = {
|
||||
let outer_geometry = self.get_geometry(outer_window).expect("Failed to get outer window geometry");
|
||||
(
|
||||
outer_geometry.y_rel_parent,
|
||||
outer_geometry.width,
|
||||
outer_geometry.height,
|
||||
)
|
||||
};
|
||||
|
||||
// Since we have the geometry of the outermost window and the geometry of the client
|
||||
// area, we can figure out what's in between.
|
||||
let diff_x = outer_width.saturating_sub(width);
|
||||
let diff_y = outer_height.saturating_sub(height);
|
||||
let offset_y = inner_y_rel_root.saturating_sub(outer_y) as c_uint;
|
||||
|
||||
let left = diff_x / 2;
|
||||
let right = left;
|
||||
let top = offset_y;
|
||||
let bottom = diff_y.saturating_sub(offset_y);
|
||||
|
||||
let frame_extents = FrameExtents::new(
|
||||
left.into(),
|
||||
right.into(),
|
||||
top.into(),
|
||||
bottom.into(),
|
||||
);
|
||||
FrameExtentsHeuristic {
|
||||
frame_extents,
|
||||
heuristic_path: UnsupportedNested,
|
||||
}
|
||||
} else {
|
||||
// This is the case for xmonad and dwm, AKA the only WMs tested that supplied a
|
||||
// border value. This is convenient, since we can use it to get an accurate frame.
|
||||
let frame_extents = FrameExtents::from_border(border.into());
|
||||
FrameExtentsHeuristic {
|
||||
frame_extents,
|
||||
heuristic_path: UnsupportedBordered,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub const MWM_HINTS_DECORATIONS: c_ulong = 2;
|
||||
@@ -6,15 +8,204 @@ pub const MWM_HINTS_DECORATIONS: c_ulong = 2;
|
||||
pub enum StateOperation {
|
||||
Remove = 0, // _NET_WM_STATE_REMOVE
|
||||
Add = 1, // _NET_WM_STATE_ADD
|
||||
_Toggle = 2, // _NET_WM_STATE_TOGGLE
|
||||
Toggle = 2, // _NET_WM_STATE_TOGGLE
|
||||
}
|
||||
|
||||
impl From<bool> for StateOperation {
|
||||
fn from(b: bool) -> Self {
|
||||
if b {
|
||||
fn from(op: bool) -> Self {
|
||||
if op {
|
||||
StateOperation::Add
|
||||
} else {
|
||||
StateOperation::Remove
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// X window type. Maps directly to
|
||||
/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/1.3/ar01s05.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
|
||||
/// screen, allowing the desktop environment to have full control of the desktop, without the need for proxying
|
||||
/// root window clicks.
|
||||
Desktop,
|
||||
/// A dock or panel feature. Typically a Window Manager would keep such windows on top of all other windows.
|
||||
Dock,
|
||||
/// Toolbar windows. "Torn off" from the main application.
|
||||
Toolbar,
|
||||
/// Pinnable menu windows. "Torn off" from the main application.
|
||||
Menu,
|
||||
/// A small persistent utility window, such as a palette or toolbox.
|
||||
Utility,
|
||||
/// The window is a splash screen displayed as an application is starting up.
|
||||
Splash,
|
||||
/// This is a dialog window.
|
||||
Dialog,
|
||||
/// This is a normal, top-level window.
|
||||
Normal,
|
||||
}
|
||||
|
||||
impl Default for WindowType {
|
||||
fn default() -> Self {
|
||||
WindowType::Normal
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowType {
|
||||
pub(crate) fn as_atom(&self, xconn: &Arc<XConnection>) -> ffi::Atom {
|
||||
use self::WindowType::*;
|
||||
let atom_name: &[u8] = match self {
|
||||
&Desktop => b"_NET_WM_WINDOW_TYPE_DESKTOP\0",
|
||||
&Dock => b"_NET_WM_WINDOW_TYPE_DOCK\0",
|
||||
&Toolbar => b"_NET_WM_WINDOW_TYPE_TOOLBAR\0",
|
||||
&Menu => b"_NET_WM_WINDOW_TYPE_MENU\0",
|
||||
&Utility => b"_NET_WM_WINDOW_TYPE_UTILITY\0",
|
||||
&Splash => b"_NET_WM_WINDOW_TYPE_SPLASH\0",
|
||||
&Dialog => b"_NET_WM_WINDOW_TYPE_DIALOG\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) };
|
||||
self.check_errors()?;
|
||||
let wm_hints = if wm_hints.is_null() {
|
||||
self.alloc_wm_hints()
|
||||
} else {
|
||||
XSmartPointer::new(self, wm_hints).unwrap()
|
||||
};
|
||||
Ok(wm_hints)
|
||||
}
|
||||
|
||||
pub fn set_wm_hints(&self, window: ffi::Window, wm_hints: XSmartPointer<ffi::XWMHints>) -> Flusher {
|
||||
unsafe {
|
||||
(self.xlib.XSetWMHints)(
|
||||
self.display,
|
||||
window,
|
||||
wm_hints.ptr,
|
||||
);
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ impl Pixel {
|
||||
}
|
||||
|
||||
impl Icon {
|
||||
pub fn to_cardinals(&self) -> Vec<Cardinal> {
|
||||
pub(crate) fn to_cardinals(&self) -> Vec<Cardinal> {
|
||||
assert_eq!(self.rgba.len() % PIXEL_SIZE, 0);
|
||||
let pixel_count = self.rgba.len() / PIXEL_SIZE;
|
||||
assert_eq!(pixel_count, (self.width * self.height) as usize);
|
||||
|
||||
@@ -1,44 +1,15 @@
|
||||
use std::str;
|
||||
|
||||
use super::*;
|
||||
use events::ModifiersState;
|
||||
|
||||
pub unsafe fn select_xinput_events(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: c_ulong,
|
||||
device_id: c_int,
|
||||
mask: i32,
|
||||
) -> Flusher {
|
||||
let mut event_mask = ffi::XIEventMask {
|
||||
deviceid: device_id,
|
||||
mask: &mask as *const _ as *mut c_uchar,
|
||||
mask_len: mem::size_of_val(&mask) as c_int,
|
||||
};
|
||||
(xconn.xinput2.XISelectEvents)(
|
||||
xconn.display,
|
||||
window,
|
||||
&mut event_mask as *mut ffi::XIEventMask,
|
||||
1, // number of masks to read from pointer above
|
||||
);
|
||||
Flusher::new(xconn)
|
||||
}
|
||||
pub const VIRTUAL_CORE_POINTER: c_int = 2;
|
||||
pub const VIRTUAL_CORE_KEYBOARD: c_int = 3;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn select_xkb_events(
|
||||
xconn: &Arc<XConnection>,
|
||||
device_id: c_uint,
|
||||
mask: c_ulong,
|
||||
) -> Option<Flusher> {
|
||||
let status = (xconn.xlib.XkbSelectEvents)(
|
||||
xconn.display,
|
||||
device_id,
|
||||
mask,
|
||||
mask,
|
||||
);
|
||||
if status == ffi::True {
|
||||
Some(Flusher::new(xconn))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
// 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.
|
||||
const TEXT_BUFFER_SIZE: usize = 1024;
|
||||
|
||||
impl From<ffi::XIModifierState> for ModifiersState {
|
||||
fn from(mods: ffi::XIModifierState) -> Self {
|
||||
@@ -53,17 +24,17 @@ impl From<ffi::XIModifierState> for ModifiersState {
|
||||
}
|
||||
|
||||
pub struct PointerState<'a> {
|
||||
xconn: &'a Arc<XConnection>,
|
||||
_root: ffi::Window,
|
||||
_child: ffi::Window,
|
||||
_root_x: c_double,
|
||||
_root_y: c_double,
|
||||
_win_x: c_double,
|
||||
_win_y: c_double,
|
||||
_buttons: ffi::XIButtonState,
|
||||
xconn: &'a XConnection,
|
||||
root: ffi::Window,
|
||||
child: ffi::Window,
|
||||
pub root_x: c_double,
|
||||
pub root_y: c_double,
|
||||
win_x: c_double,
|
||||
win_y: c_double,
|
||||
buttons: ffi::XIButtonState,
|
||||
modifiers: ffi::XIModifierState,
|
||||
_group: ffi::XIGroupState,
|
||||
_relative_to_window: bool,
|
||||
group: ffi::XIGroupState,
|
||||
relative_to_window: bool,
|
||||
}
|
||||
|
||||
impl<'a> PointerState<'a> {
|
||||
@@ -74,114 +45,115 @@ impl<'a> PointerState<'a> {
|
||||
|
||||
impl<'a> Drop for PointerState<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// This is why you need to read the docs carefully...
|
||||
(self.xconn.xlib.XFree)(self._buttons.mask as _);
|
||||
if !self.buttons.mask.is_null() {
|
||||
unsafe {
|
||||
// This is why you need to read the docs carefully...
|
||||
(self.xconn.xlib.XFree)(self.buttons.mask as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn query_pointer(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
device_id: c_int,
|
||||
) -> Result<PointerState, XError> {
|
||||
let mut root_return = mem::uninitialized();
|
||||
let mut child_return = mem::uninitialized();
|
||||
let mut root_x_return = mem::uninitialized();
|
||||
let mut root_y_return = mem::uninitialized();
|
||||
let mut win_x_return = mem::uninitialized();
|
||||
let mut win_y_return = mem::uninitialized();
|
||||
let mut buttons_return = mem::uninitialized();
|
||||
let mut modifiers_return = mem::uninitialized();
|
||||
let mut group_return = mem::uninitialized();
|
||||
impl XConnection {
|
||||
pub fn select_xinput_events(&self, window: c_ulong, device_id: c_int, mask: i32) -> Flusher {
|
||||
let mut event_mask = ffi::XIEventMask {
|
||||
deviceid: device_id,
|
||||
mask: &mask as *const _ as *mut c_uchar,
|
||||
mask_len: mem::size_of_val(&mask) as c_int,
|
||||
};
|
||||
unsafe {
|
||||
(self.xinput2.XISelectEvents)(
|
||||
self.display,
|
||||
window,
|
||||
&mut event_mask as *mut ffi::XIEventMask,
|
||||
1, // number of masks to read from pointer above
|
||||
);
|
||||
}
|
||||
Flusher::new(self)
|
||||
}
|
||||
|
||||
let relative_to_window = (xconn.xinput2.XIQueryPointer)(
|
||||
xconn.display,
|
||||
device_id,
|
||||
window,
|
||||
&mut root_return,
|
||||
&mut child_return,
|
||||
&mut root_x_return,
|
||||
&mut root_y_return,
|
||||
&mut win_x_return,
|
||||
&mut win_y_return,
|
||||
&mut buttons_return,
|
||||
&mut modifiers_return,
|
||||
&mut group_return,
|
||||
) == ffi::True;
|
||||
#[allow(dead_code)]
|
||||
pub fn select_xkb_events(&self, device_id: c_uint, mask: c_ulong) -> Option<Flusher> {
|
||||
let status = unsafe {
|
||||
(self.xlib.XkbSelectEvents)(
|
||||
self.display,
|
||||
device_id,
|
||||
mask,
|
||||
mask,
|
||||
)
|
||||
};
|
||||
if status == ffi::True {
|
||||
Some(Flusher::new(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
xconn.check_errors()?;
|
||||
pub fn query_pointer(&self, window: ffi::Window, device_id: c_int) -> Result<PointerState, XError> {
|
||||
unsafe {
|
||||
let mut pointer_state: PointerState = mem::uninitialized();
|
||||
pointer_state.xconn = self;
|
||||
pointer_state.relative_to_window = (self.xinput2.XIQueryPointer)(
|
||||
self.display,
|
||||
device_id,
|
||||
window,
|
||||
&mut pointer_state.root,
|
||||
&mut pointer_state.child,
|
||||
&mut pointer_state.root_x,
|
||||
&mut pointer_state.root_y,
|
||||
&mut pointer_state.win_x,
|
||||
&mut pointer_state.win_y,
|
||||
&mut pointer_state.buttons,
|
||||
&mut pointer_state.modifiers,
|
||||
&mut pointer_state.group,
|
||||
) == ffi::True;
|
||||
if let Err(err) = self.check_errors() {
|
||||
// Running the destrutor would be bad news for us...
|
||||
mem::forget(pointer_state);
|
||||
Err(err)
|
||||
} else {
|
||||
Ok(pointer_state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(PointerState {
|
||||
xconn,
|
||||
_root: root_return,
|
||||
_child: child_return,
|
||||
_root_x: root_x_return,
|
||||
_root_y: root_y_return,
|
||||
_win_x: win_x_return,
|
||||
_win_y: win_y_return,
|
||||
_buttons: buttons_return,
|
||||
modifiers: modifiers_return,
|
||||
_group: group_return,
|
||||
_relative_to_window: relative_to_window,
|
||||
})
|
||||
}
|
||||
fn lookup_utf8_inner(
|
||||
&self,
|
||||
ic: ffi::XIC,
|
||||
key_event: &mut ffi::XKeyEvent,
|
||||
buffer: &mut [u8],
|
||||
) -> (ffi::KeySym, ffi::Status, c_int) {
|
||||
let mut keysym: ffi::KeySym = 0;
|
||||
let mut status: ffi::Status = 0;
|
||||
let count = unsafe {
|
||||
(self.xlib.Xutf8LookupString)(
|
||||
ic,
|
||||
key_event,
|
||||
buffer.as_mut_ptr() as *mut c_char,
|
||||
buffer.len() as c_int,
|
||||
&mut keysym,
|
||||
&mut status,
|
||||
)
|
||||
};
|
||||
(keysym, status, count)
|
||||
}
|
||||
|
||||
unsafe fn lookup_utf8_inner(
|
||||
xconn: &Arc<XConnection>,
|
||||
ic: ffi::XIC,
|
||||
key_event: &mut ffi::XKeyEvent,
|
||||
buffer: &mut [u8],
|
||||
) -> (ffi::KeySym, ffi::Status, c_int) {
|
||||
let mut keysym: ffi::KeySym = 0;
|
||||
let mut status: ffi::Status = 0;
|
||||
let count = (xconn.xlib.Xutf8LookupString)(
|
||||
ic,
|
||||
key_event,
|
||||
buffer.as_mut_ptr() as *mut c_char,
|
||||
buffer.len() as c_int,
|
||||
&mut keysym,
|
||||
&mut status,
|
||||
);
|
||||
(keysym, status, count)
|
||||
}
|
||||
|
||||
// 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.
|
||||
const TEXT_BUFFER_SIZE: usize = 1024;
|
||||
|
||||
pub unsafe fn lookup_utf8(
|
||||
xconn: &Arc<XConnection>,
|
||||
ic: ffi::XIC,
|
||||
key_event: &mut ffi::XKeyEvent,
|
||||
) -> String {
|
||||
let mut buffer: [u8; TEXT_BUFFER_SIZE] = mem::uninitialized();
|
||||
let (_, status, count) = lookup_utf8_inner(
|
||||
xconn,
|
||||
ic,
|
||||
key_event,
|
||||
&mut buffer,
|
||||
);
|
||||
|
||||
// The buffer overflowed, so we'll make a new one on the heap.
|
||||
if status == ffi::XBufferOverflow {
|
||||
let mut buffer = Vec::with_capacity(count as usize);
|
||||
buffer.set_len(count as usize);
|
||||
let (_, _, new_count) = lookup_utf8_inner(
|
||||
xconn,
|
||||
ic,
|
||||
key_event,
|
||||
&mut buffer,
|
||||
);
|
||||
debug_assert_eq!(count, new_count);
|
||||
str::from_utf8(&buffer[..count as usize])
|
||||
.unwrap_or("")
|
||||
.to_string()
|
||||
} else {
|
||||
str::from_utf8(&buffer[..count as usize])
|
||||
.unwrap_or("")
|
||||
.to_string()
|
||||
pub fn lookup_utf8(&self, ic: ffi::XIC, key_event: &mut ffi::XKeyEvent) -> String {
|
||||
let mut buffer: [u8; TEXT_BUFFER_SIZE] = unsafe { mem::uninitialized() };
|
||||
let (_, status, count) = self.lookup_utf8_inner(ic, key_event, &mut buffer);
|
||||
// The buffer overflowed, so we'll make a new one on the heap.
|
||||
if status == ffi::XBufferOverflow {
|
||||
let mut buffer = Vec::with_capacity(count as usize);
|
||||
unsafe { buffer.set_len(count as usize) };
|
||||
let (_, _, new_count) = self.lookup_utf8_inner(ic, key_event, &mut buffer);
|
||||
debug_assert_eq!(count, new_count);
|
||||
str::from_utf8(&buffer[..count as usize])
|
||||
.unwrap_or("")
|
||||
.to_string()
|
||||
} else {
|
||||
str::from_utf8(&buffer[..count as usize])
|
||||
.unwrap_or("")
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
62
src/platform/linux/x11/util/memory.rs
Normal file
62
src/platform/linux/x11/util/memory.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct XSmartPointer<'a, T> {
|
||||
xconn: &'a XConnection,
|
||||
pub ptr: *mut T,
|
||||
}
|
||||
|
||||
impl<'a, T> XSmartPointer<'a, T> {
|
||||
// You're responsible for only passing things to this that should be XFree'd.
|
||||
// Returns None if ptr is null.
|
||||
pub fn new(xconn: &'a XConnection, ptr: *mut T) -> Option<Self> {
|
||||
if !ptr.is_null() {
|
||||
Some(XSmartPointer {
|
||||
xconn,
|
||||
ptr,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for XSmartPointer<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for XSmartPointer<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for XSmartPointer<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XFree)(self.ptr as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XConnection {
|
||||
pub fn alloc_class_hint(&self) -> XSmartPointer<ffi::XClassHint> {
|
||||
XSmartPointer::new(self, unsafe { (self.xlib.XAllocClassHint)() })
|
||||
.expect("`XAllocClassHint` returned null; out of memory")
|
||||
}
|
||||
|
||||
pub fn alloc_size_hints(&self) -> XSmartPointer<ffi::XSizeHints> {
|
||||
XSmartPointer::new(self, unsafe { (self.xlib.XAllocSizeHints)() })
|
||||
.expect("`XAllocSizeHints` returned null; out of memory")
|
||||
}
|
||||
|
||||
pub fn alloc_wm_hints(&self) -> XSmartPointer<ffi::XWMHints> {
|
||||
XSmartPointer::new(self, unsafe { (self.xlib.XAllocWMHints)() })
|
||||
.expect("`XAllocWMHints` returned null; out of memory")
|
||||
}
|
||||
}
|
||||
@@ -2,178 +2,103 @@
|
||||
// *results may vary
|
||||
|
||||
mod atom;
|
||||
mod client_msg;
|
||||
mod format;
|
||||
mod geometry;
|
||||
mod hint;
|
||||
mod icon;
|
||||
mod input;
|
||||
mod memory;
|
||||
mod randr;
|
||||
mod window_property;
|
||||
mod wm;
|
||||
|
||||
pub use self::atom::*;
|
||||
pub use self::client_msg::*;
|
||||
pub use self::format::*;
|
||||
pub use self::geometry::*;
|
||||
pub use self::hint::*;
|
||||
pub use self::icon::*;
|
||||
pub use self::input::*;
|
||||
pub use self::memory::*;
|
||||
pub use self::randr::*;
|
||||
pub use self::window_property::*;
|
||||
pub use self::wm::*;
|
||||
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::ops::BitAnd;
|
||||
use std::os::raw::*;
|
||||
|
||||
use super::{ffi, XConnection, XError};
|
||||
|
||||
// This isn't actually the number of the bits in the format.
|
||||
// X11 does a match on this value to determine which type to call sizeof on.
|
||||
// Thus, we use 32 for c_long, since 32 maps to c_long which maps to 64.
|
||||
// ...if that sounds confusing, then you know why this enum is here.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Format {
|
||||
Char = 8,
|
||||
Short = 16,
|
||||
Long = 32,
|
||||
pub fn reinterpret<'a, A, B>(a: &'a A) -> &'a B {
|
||||
let b_ptr = a as *const _ as *const B;
|
||||
unsafe { &*b_ptr }
|
||||
}
|
||||
|
||||
impl Format {
|
||||
pub fn from_format(format: usize) -> Option<Self> {
|
||||
match format {
|
||||
8 => Some(Format::Char),
|
||||
16 => Some(Format::Short),
|
||||
32 => Some(Format::Long),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_same_size_as<T>(&self) -> bool {
|
||||
mem::size_of::<T>() == self.get_actual_size()
|
||||
}
|
||||
|
||||
pub fn get_actual_size(&self) -> usize {
|
||||
match self {
|
||||
&Format::Char => mem::size_of::<c_char>(),
|
||||
&Format::Short => mem::size_of::<c_short>(),
|
||||
&Format::Long => mem::size_of::<c_long>(),
|
||||
}
|
||||
pub fn maybe_change<T: PartialEq>(field: &mut Option<T>, value: T) -> bool {
|
||||
let wrapped = Some(value);
|
||||
if *field != wrapped {
|
||||
*field = wrapped;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub struct XSmartPointer<'a, T> {
|
||||
xconn: &'a Arc<XConnection>,
|
||||
pub ptr: *mut T,
|
||||
}
|
||||
|
||||
impl<'a, T> XSmartPointer<'a, T> {
|
||||
// You're responsible for only passing things to this that should be XFree'd.
|
||||
// Returns None if ptr is null.
|
||||
pub fn new(xconn: &'a Arc<XConnection>, ptr: *mut T) -> Option<Self> {
|
||||
if !ptr.is_null() {
|
||||
Some(XSmartPointer {
|
||||
xconn,
|
||||
ptr,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for XSmartPointer<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for XSmartPointer<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for XSmartPointer<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XFree)(self.ptr as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is impoartant, so pay attention!
|
||||
// Xlib has an output buffer, and tries to hide the async nature of X from you.
|
||||
// This buffer contains the requests you make, and is flushed under various circumstances:
|
||||
// 1. XPending, XNextEvent, and XWindowEvent flush "as needed"
|
||||
// 2. XFlush explicitly flushes
|
||||
// 3. XSync flushes and blocks until all requests are responded to
|
||||
// 4. Calls that have a return dependent on a response (i.e. XGetWindowProperty) sync internally.
|
||||
// When in doubt, check the X11 source; if a function calls _XReply, it flushes and waits.
|
||||
// All util functions that abstract an async function will return a Flusher.
|
||||
pub unsafe fn flush_requests(xconn: &Arc<XConnection>) -> Result<(), XError> {
|
||||
(xconn.xlib.XFlush)(xconn.display);
|
||||
//println!("XFlush");
|
||||
// This isn't necessarily a useful time to check for errors (since our request hasn't
|
||||
// necessarily been processed yet)
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
pub unsafe fn sync_with_server(xconn: &Arc<XConnection>) -> Result<(), XError> {
|
||||
(xconn.xlib.XSync)(xconn.display, ffi::False);
|
||||
//println!("XSync");
|
||||
xconn.check_errors()
|
||||
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 Arc<XConnection>,
|
||||
xconn: &'a XConnection,
|
||||
}
|
||||
|
||||
impl<'a> Flusher<'a> {
|
||||
pub fn new(xconn: &'a Arc<XConnection>) -> Self {
|
||||
pub fn new(xconn: &'a XConnection) -> Self {
|
||||
Flusher { xconn }
|
||||
}
|
||||
|
||||
// "I want this request sent now!"
|
||||
pub fn flush(self) -> Result<(), XError> {
|
||||
unsafe { flush_requests(self.xconn) }
|
||||
self.xconn.flush_requests()
|
||||
}
|
||||
|
||||
// "I want the response now too!"
|
||||
pub fn sync(self) -> Result<(), XError> {
|
||||
self.xconn.sync_with_server()
|
||||
}
|
||||
|
||||
// "I'm aware that this request hasn't been sent, and I'm okay with waiting."
|
||||
pub fn queue(self) {}
|
||||
}
|
||||
|
||||
pub unsafe fn send_client_msg(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: c_ulong, // The window this is "about"; not necessarily this window
|
||||
target_window: c_ulong, // The window we're sending to
|
||||
message_type: ffi::Atom,
|
||||
event_mask: Option<c_long>,
|
||||
data: (c_long, c_long, c_long, c_long, c_long),
|
||||
) -> Flusher {
|
||||
let mut event: ffi::XClientMessageEvent = mem::uninitialized();
|
||||
event.type_ = ffi::ClientMessage;
|
||||
event.display = xconn.display;
|
||||
event.window = window;
|
||||
event.message_type = message_type;
|
||||
event.format = Format::Long as c_int;
|
||||
event.data = ffi::ClientMessageData::new();
|
||||
event.data.set_long(0, data.0);
|
||||
event.data.set_long(1, data.1);
|
||||
event.data.set_long(2, data.2);
|
||||
event.data.set_long(3, data.3);
|
||||
event.data.set_long(4, data.4);
|
||||
impl XConnection {
|
||||
// This is impoartant, so pay attention!
|
||||
// Xlib has an output buffer, and tries to hide the async nature of X from you.
|
||||
// This buffer contains the requests you make, and is flushed under various circumstances:
|
||||
// 1. `XPending`, `XNextEvent`, and `XWindowEvent` flush "as needed"
|
||||
// 2. `XFlush` explicitly flushes
|
||||
// 3. `XSync` flushes and blocks until all requests are responded to
|
||||
// 4. Calls that have a return dependent on a response (i.e. `XGetWindowProperty`) sync internally.
|
||||
// When in doubt, check the X11 source; if a function calls `_XReply`, it flushes and waits.
|
||||
// All util functions that abstract an async function will return a `Flusher`.
|
||||
pub fn flush_requests(&self) -> Result<(), XError> {
|
||||
unsafe { (self.xlib.XFlush)(self.display) };
|
||||
//println!("XFlush");
|
||||
// This isn't necessarily a useful time to check for errors (since our request hasn't
|
||||
// necessarily been processed yet)
|
||||
self.check_errors()
|
||||
}
|
||||
|
||||
let event_mask = event_mask.unwrap_or(ffi::NoEventMask);
|
||||
|
||||
(xconn.xlib.XSendEvent)(
|
||||
xconn.display,
|
||||
target_window,
|
||||
ffi::False,
|
||||
event_mask,
|
||||
&mut event.into(),
|
||||
);
|
||||
|
||||
Flusher::new(xconn)
|
||||
pub fn sync_with_server(&self) -> Result<(), XError> {
|
||||
unsafe { (self.xlib.XSync)(self.display, ffi::False) };
|
||||
//println!("XSync");
|
||||
self.check_errors()
|
||||
}
|
||||
}
|
||||
|
||||
100
src/platform/linux/x11/util/randr.rs
Normal file
100
src/platform/linux/x11/util/randr.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
use std::{env, slice};
|
||||
use std::str::FromStr;
|
||||
|
||||
use validate_hidpi_factor;
|
||||
use super::*;
|
||||
|
||||
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 ffi::XRRMonitorInfo),
|
||||
Crtc(*mut ffi::XRRCrtcInfo),
|
||||
}
|
||||
|
||||
impl MonitorRepr {
|
||||
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)),
|
||||
MonitorRepr::Crtc(crtc) => *((*crtc).outputs.offset(0)),
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_dimensions(&self) -> (u32, u32) {
|
||||
match *self {
|
||||
MonitorRepr::Monitor(monitor) => ((*monitor).width as u32, (*monitor).height as u32),
|
||||
MonitorRepr::Crtc(crtc) => ((*crtc).width as u32, (*crtc).height as u32),
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_position(&self) -> (i32, i32) {
|
||||
match *self {
|
||||
MonitorRepr::Monitor(monitor) => ((*monitor).x as i32, (*monitor).y as i32),
|
||||
MonitorRepr::Crtc(crtc) => ((*crtc).x as i32, (*crtc).y as i32),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<*mut ffi::XRRMonitorInfo> for MonitorRepr {
|
||||
fn from(monitor: *mut ffi::XRRMonitorInfo) -> Self {
|
||||
MonitorRepr::Monitor(monitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<*mut ffi::XRRCrtcInfo> for MonitorRepr {
|
||||
fn from(crtc: *mut ffi::XRRCrtcInfo) -> Self {
|
||||
MonitorRepr::Crtc(crtc)
|
||||
}
|
||||
}
|
||||
|
||||
impl XConnection {
|
||||
pub unsafe fn get_output_info(&self, resources: *mut ffi::XRRScreenResources, repr: &MonitorRepr) -> (String, f64) {
|
||||
let output_info = (self.xrandr.XRRGetOutputInfo)(
|
||||
self.display,
|
||||
resources,
|
||||
repr.get_output(),
|
||||
);
|
||||
let name_slice = slice::from_raw_parts(
|
||||
(*output_info).name as *mut u8,
|
||||
(*output_info).nameLen as usize,
|
||||
);
|
||||
let name = String::from_utf8_lossy(name_slice).into();
|
||||
let hidpi_factor = calc_dpi_factor(
|
||||
repr.get_dimensions(),
|
||||
((*output_info).mm_width as u64, (*output_info).mm_height as u64),
|
||||
);
|
||||
(self.xrandr.XRRFreeOutputInfo)(output_info);
|
||||
(name, hidpi_factor)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
use std;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -25,139 +24,121 @@ impl GetPropertyError {
|
||||
}
|
||||
|
||||
// Number of 32-bit chunks to retrieve per iteration of get_property's inner loop.
|
||||
// To test if get_property works correctly, set this to 1.
|
||||
// To test if `get_property` works correctly, set this to 1.
|
||||
const PROPERTY_BUFFER_SIZE: c_long = 1024; // 4k of RAM ought to be enough for anyone!
|
||||
|
||||
pub unsafe fn get_property<T: Debug + Clone>(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: c_ulong,
|
||||
property: ffi::Atom,
|
||||
property_type: ffi::Atom,
|
||||
) -> Result<Vec<T>, GetPropertyError> {
|
||||
let mut data = Vec::new();
|
||||
let mut offset = 0;
|
||||
|
||||
let mut done = false;
|
||||
while !done {
|
||||
let mut actual_type: ffi::Atom = mem::uninitialized();
|
||||
let mut actual_format: c_int = mem::uninitialized();
|
||||
let mut quantity_returned: c_ulong = mem::uninitialized();
|
||||
let mut bytes_after: c_ulong = mem::uninitialized();
|
||||
let mut buf: *mut c_uchar = ptr::null_mut();
|
||||
(xconn.xlib.XGetWindowProperty)(
|
||||
xconn.display,
|
||||
window,
|
||||
property,
|
||||
// This offset is in terms of 32-bit chunks.
|
||||
offset,
|
||||
// This is the quanity of 32-bit chunks to receive at once.
|
||||
PROPERTY_BUFFER_SIZE,
|
||||
ffi::False,
|
||||
property_type,
|
||||
&mut actual_type,
|
||||
&mut actual_format,
|
||||
// This is the quantity of items we retrieved in our format, NOT of 32-bit chunks!
|
||||
&mut quantity_returned,
|
||||
// ...and this is a quantity of bytes. So, this function deals in 3 different units.
|
||||
&mut bytes_after,
|
||||
&mut buf,
|
||||
);
|
||||
|
||||
if let Err(e) = xconn.check_errors() {
|
||||
return Err(GetPropertyError::XError(e));
|
||||
}
|
||||
|
||||
if actual_type != property_type {
|
||||
return Err(GetPropertyError::TypeMismatch(actual_type));
|
||||
}
|
||||
|
||||
let format_mismatch = Format::from_format(actual_format as _)
|
||||
.map(|actual_format| !actual_format.is_same_size_as::<T>())
|
||||
// This won't actually be reached; the XError condition above is triggered first.
|
||||
.unwrap_or(true);
|
||||
|
||||
if format_mismatch {
|
||||
return Err(GetPropertyError::FormatMismatch(actual_format));
|
||||
}
|
||||
|
||||
if !buf.is_null() {
|
||||
offset += PROPERTY_BUFFER_SIZE;
|
||||
let new_data = std::slice::from_raw_parts(
|
||||
buf as *mut T,
|
||||
quantity_returned as usize,
|
||||
);
|
||||
/*println!(
|
||||
"XGetWindowProperty prop:{:?} fmt:{:02} len:{:02} off:{:02} out:{:02}, buf:{:?}",
|
||||
property,
|
||||
mem::size_of::<T>() * 8,
|
||||
data.len(),
|
||||
offset,
|
||||
quantity_returned,
|
||||
new_data,
|
||||
);*/
|
||||
data.extend_from_slice(&new_data);
|
||||
// Fun fact: XGetWindowProperty allocates one extra byte at the end.
|
||||
(xconn.xlib.XFree)(buf as _); // Don't try to access new_data after this.
|
||||
} else {
|
||||
return Err(GetPropertyError::NothingAllocated);
|
||||
}
|
||||
|
||||
done = bytes_after == 0;
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PropMode {
|
||||
Replace = ffi::PropModeReplace as isize,
|
||||
_Prepend = ffi::PropModePrepend as isize,
|
||||
_Append = ffi::PropModeAppend as isize,
|
||||
Prepend = ffi::PropModePrepend as isize,
|
||||
Append = ffi::PropModeAppend as isize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InvalidFormat {
|
||||
format_used: Format,
|
||||
size_passed: usize,
|
||||
size_expected: usize,
|
||||
}
|
||||
impl XConnection {
|
||||
pub fn get_property<T: Formattable>(
|
||||
&self,
|
||||
window: c_ulong,
|
||||
property: ffi::Atom,
|
||||
property_type: ffi::Atom,
|
||||
) -> Result<Vec<T>, GetPropertyError> {
|
||||
let mut data = Vec::new();
|
||||
let mut offset = 0;
|
||||
|
||||
pub unsafe fn change_property<'a, T: Debug>(
|
||||
xconn: &'a Arc<XConnection>,
|
||||
window: c_ulong,
|
||||
property: ffi::Atom,
|
||||
property_type: ffi::Atom,
|
||||
format: Format,
|
||||
mode: PropMode,
|
||||
new_value: &[T],
|
||||
) -> Flusher<'a> {
|
||||
if !format.is_same_size_as::<T>() {
|
||||
panic!(format!(
|
||||
"[winit developer error] Incorrect usage of `util::change_property`: {:#?}",
|
||||
InvalidFormat {
|
||||
format_used: format,
|
||||
size_passed: mem::size_of::<T>() * 8,
|
||||
size_expected: format.get_actual_size() * 8,
|
||||
},
|
||||
));
|
||||
let mut done = false;
|
||||
while !done {
|
||||
unsafe {
|
||||
let mut actual_type: ffi::Atom = mem::uninitialized();
|
||||
let mut actual_format: c_int = mem::uninitialized();
|
||||
let mut quantity_returned: c_ulong = mem::uninitialized();
|
||||
let mut bytes_after: c_ulong = mem::uninitialized();
|
||||
let mut buf: *mut c_uchar = ptr::null_mut();
|
||||
(self.xlib.XGetWindowProperty)(
|
||||
self.display,
|
||||
window,
|
||||
property,
|
||||
// This offset is in terms of 32-bit chunks.
|
||||
offset,
|
||||
// This is the quanity of 32-bit chunks to receive at once.
|
||||
PROPERTY_BUFFER_SIZE,
|
||||
ffi::False,
|
||||
property_type,
|
||||
&mut actual_type,
|
||||
&mut actual_format,
|
||||
// This is the quantity of items we retrieved in our format, NOT of 32-bit chunks!
|
||||
&mut quantity_returned,
|
||||
// ...and this is a quantity of bytes. So, this function deals in 3 different units.
|
||||
&mut bytes_after,
|
||||
&mut buf,
|
||||
);
|
||||
|
||||
if let Err(e) = self.check_errors() {
|
||||
return Err(GetPropertyError::XError(e));
|
||||
}
|
||||
|
||||
if actual_type != property_type {
|
||||
return Err(GetPropertyError::TypeMismatch(actual_type));
|
||||
}
|
||||
|
||||
let format_mismatch = Format::from_format(actual_format as _) != Some(T::FORMAT);
|
||||
if format_mismatch {
|
||||
return Err(GetPropertyError::FormatMismatch(actual_format));
|
||||
}
|
||||
|
||||
if !buf.is_null() {
|
||||
offset += PROPERTY_BUFFER_SIZE;
|
||||
let new_data = std::slice::from_raw_parts(
|
||||
buf as *mut T,
|
||||
quantity_returned as usize,
|
||||
);
|
||||
/*println!(
|
||||
"XGetWindowProperty prop:{:?} fmt:{:02} len:{:02} off:{:02} out:{:02}, buf:{:?}",
|
||||
property,
|
||||
mem::size_of::<T>() * 8,
|
||||
data.len(),
|
||||
offset,
|
||||
quantity_returned,
|
||||
new_data,
|
||||
);*/
|
||||
data.extend_from_slice(&new_data);
|
||||
// Fun fact: XGetWindowProperty allocates one extra byte at the end.
|
||||
(self.xlib.XFree)(buf as _); // Don't try to access new_data after this.
|
||||
} else {
|
||||
return Err(GetPropertyError::NothingAllocated);
|
||||
}
|
||||
|
||||
done = bytes_after == 0;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
(xconn.xlib.XChangeProperty)(
|
||||
xconn.display,
|
||||
window,
|
||||
property,
|
||||
property_type,
|
||||
format as c_int,
|
||||
mode as c_int,
|
||||
new_value.as_ptr() as *const c_uchar,
|
||||
new_value.len() as c_int,
|
||||
);
|
||||
|
||||
/*println!(
|
||||
"XChangeProperty prop:{:?} val:{:?}",
|
||||
property,
|
||||
new_value,
|
||||
);*/
|
||||
|
||||
Flusher::new(xconn)
|
||||
pub fn change_property<'a, T: Formattable>(
|
||||
&'a self,
|
||||
window: c_ulong,
|
||||
property: ffi::Atom,
|
||||
property_type: ffi::Atom,
|
||||
mode: PropMode,
|
||||
new_value: &[T],
|
||||
) -> Flusher<'a> {
|
||||
debug_assert_eq!(mem::size_of::<T>(), T::FORMAT.get_actual_size());
|
||||
unsafe {
|
||||
(self.xlib.XChangeProperty)(
|
||||
self.display,
|
||||
window,
|
||||
property,
|
||||
property_type,
|
||||
T::FORMAT as c_int,
|
||||
mode as c_int,
|
||||
new_value.as_ptr() as *const c_uchar,
|
||||
new_value.len() as c_int,
|
||||
);
|
||||
}
|
||||
/*println!(
|
||||
"XChangeProperty prop:{:?} val:{:?}",
|
||||
property,
|
||||
new_value,
|
||||
);*/
|
||||
Flusher::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,139 +20,122 @@ pub fn wm_name_is_one_of(names: &[&str]) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_cached_wm_info(xconn: &Arc<XConnection>, root: ffi::Window) {
|
||||
*SUPPORTED_HINTS.lock() = self::get_supported_hints(xconn, root);
|
||||
*WM_NAME.lock() = self::get_wm_name(xconn, root);
|
||||
}
|
||||
impl XConnection {
|
||||
pub fn update_cached_wm_info(&self, root: ffi::Window) {
|
||||
*SUPPORTED_HINTS.lock() = self.get_supported_hints(root);
|
||||
*WM_NAME.lock() = self.get_wm_name(root);
|
||||
}
|
||||
|
||||
fn get_supported_hints(xconn: &Arc<XConnection>, root: ffi::Window) -> Vec<ffi::Atom> {
|
||||
let supported_atom = unsafe { self::get_atom(xconn, b"_NET_SUPPORTED\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_SUPPORTED)");
|
||||
unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
fn get_supported_hints(&self, root: ffi::Window) -> Vec<ffi::Atom> {
|
||||
let supported_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTED\0") };
|
||||
self.get_property(
|
||||
root,
|
||||
supported_atom,
|
||||
ffi::XA_ATOM,
|
||||
)
|
||||
}.unwrap_or_else(|_| Vec::with_capacity(0))
|
||||
}
|
||||
).unwrap_or_else(|_| Vec::with_capacity(0))
|
||||
}
|
||||
|
||||
fn get_wm_name(xconn: &Arc<XConnection>, root: ffi::Window) -> Option<String> {
|
||||
let check_atom = unsafe { self::get_atom(xconn, b"_NET_SUPPORTING_WM_CHECK\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_SUPPORTING_WM_CHECK)");
|
||||
let wm_name_atom = unsafe { self::get_atom(xconn, b"_NET_WM_NAME\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_WM_NAME)");
|
||||
fn get_wm_name(&self, root: ffi::Window) -> Option<String> {
|
||||
let check_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTING_WM_CHECK\0") };
|
||||
let wm_name_atom = unsafe { self.get_atom_unchecked(b"_NET_WM_NAME\0") };
|
||||
|
||||
// Mutter/Muffin/Budgie doesn't have _NET_SUPPORTING_WM_CHECK in its _NET_SUPPORTED, despite
|
||||
// it working and being supported. This has been reported upstream, but due to the
|
||||
// inavailability of time machines, we'll just try to get _NET_SUPPORTING_WM_CHECK
|
||||
// regardless of whether or not the WM claims to support it.
|
||||
//
|
||||
// Blackbox 0.70 also incorrectly reports not supporting this, though that appears to be fixed
|
||||
// in 0.72.
|
||||
/*if !supported_hints.contains(&check_atom) {
|
||||
return None;
|
||||
}*/
|
||||
// Mutter/Muffin/Budgie doesn't have _NET_SUPPORTING_WM_CHECK in its _NET_SUPPORTED, despite
|
||||
// it working and being supported. This has been reported upstream, but due to the
|
||||
// inavailability of time machines, we'll just try to get _NET_SUPPORTING_WM_CHECK
|
||||
// regardless of whether or not the WM claims to support it.
|
||||
//
|
||||
// Blackbox 0.70 also incorrectly reports not supporting this, though that appears to be fixed
|
||||
// in 0.72.
|
||||
/*if !supported_hints.contains(&check_atom) {
|
||||
return None;
|
||||
}*/
|
||||
|
||||
// IceWM (1.3.x and earlier) doesn't report supporting _NET_WM_NAME, but will nonetheless
|
||||
// provide us with a value for it. Note that the unofficial 1.4 fork of IceWM works fine.
|
||||
/*if !supported_hints.contains(&wm_name_atom) {
|
||||
return None;
|
||||
}*/
|
||||
// IceWM (1.3.x and earlier) doesn't report supporting _NET_WM_NAME, but will nonetheless
|
||||
// provide us with a value for it. Note that the unofficial 1.4 fork of IceWM works fine.
|
||||
/*if !supported_hints.contains(&wm_name_atom) {
|
||||
return None;
|
||||
}*/
|
||||
|
||||
// Of the WMs tested, only xmonad and dwm fail to provide a WM name.
|
||||
// Of the WMs tested, only xmonad and dwm fail to provide a WM name.
|
||||
|
||||
// Querying this property on the root window will give us the ID of a child window created by
|
||||
// the WM.
|
||||
let root_window_wm_check = {
|
||||
let result = unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
// Querying this property on the root window will give us the ID of a child window created by
|
||||
// the WM.
|
||||
let root_window_wm_check = {
|
||||
let result = self.get_property(
|
||||
root,
|
||||
check_atom,
|
||||
ffi::XA_WINDOW,
|
||||
)
|
||||
);
|
||||
|
||||
let wm_check = result
|
||||
.ok()
|
||||
.and_then(|wm_check| wm_check.get(0).cloned());
|
||||
|
||||
if let Some(wm_check) = wm_check {
|
||||
wm_check
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let wm_check = result
|
||||
.ok()
|
||||
.and_then(|wm_check| wm_check.get(0).cloned());
|
||||
|
||||
if let Some(wm_check) = wm_check {
|
||||
wm_check
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// Querying the same property on the child window we were given, we should get this child
|
||||
// window's ID again.
|
||||
let child_window_wm_check = {
|
||||
let result = unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
// Querying the same property on the child window we were given, we should get this child
|
||||
// window's ID again.
|
||||
let child_window_wm_check = {
|
||||
let result = self.get_property(
|
||||
root_window_wm_check,
|
||||
check_atom,
|
||||
ffi::XA_WINDOW,
|
||||
)
|
||||
);
|
||||
|
||||
let wm_check = result
|
||||
.ok()
|
||||
.and_then(|wm_check| wm_check.get(0).cloned());
|
||||
|
||||
if let Some(wm_check) = wm_check {
|
||||
wm_check
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let wm_check = result
|
||||
.ok()
|
||||
.and_then(|wm_check| wm_check.get(0).cloned());
|
||||
|
||||
if let Some(wm_check) = wm_check {
|
||||
wm_check
|
||||
} else {
|
||||
// These values should be the same.
|
||||
if root_window_wm_check != child_window_wm_check {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// These values should be the same.
|
||||
if root_window_wm_check != child_window_wm_check {
|
||||
return None;
|
||||
}
|
||||
// All of that work gives us a window ID that we can get the WM name from.
|
||||
let wm_name = {
|
||||
let utf8_string_atom = unsafe { self.get_atom_unchecked(b"UTF8_STRING\0") };
|
||||
|
||||
// All of that work gives us a window ID that we can get the WM name from.
|
||||
let wm_name = {
|
||||
let utf8_string_atom = unsafe { self::get_atom(xconn, b"UTF8_STRING\0") }
|
||||
.expect("Failed to call XInternAtom (UTF8_STRING)");
|
||||
|
||||
let result = unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
let result = self.get_property(
|
||||
root_window_wm_check,
|
||||
wm_name_atom,
|
||||
utf8_string_atom,
|
||||
)
|
||||
};
|
||||
);
|
||||
|
||||
// IceWM requires this. IceWM was also the only WM tested that returns a null-terminated
|
||||
// string. For more fun trivia, IceWM is also unique in including version and uname
|
||||
// information in this string (this means you'll have to be careful if you want to match
|
||||
// against it, though).
|
||||
// The unofficial 1.4 fork of IceWM still includes the extra details, but properly
|
||||
// returns a UTF8 string that isn't null-terminated.
|
||||
let no_utf8 = if let Err(ref err) = result {
|
||||
err.is_actual_property_type(ffi::XA_STRING)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
// IceWM requires this. IceWM was also the only WM tested that returns a null-terminated
|
||||
// string. For more fun trivia, IceWM is also unique in including version and uname
|
||||
// information in this string (this means you'll have to be careful if you want to match
|
||||
// against it, though).
|
||||
// The unofficial 1.4 fork of IceWM still includes the extra details, but properly
|
||||
// returns a UTF8 string that isn't null-terminated.
|
||||
let no_utf8 = if let Err(ref err) = result {
|
||||
err.is_actual_property_type(ffi::XA_STRING)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if no_utf8 {
|
||||
unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
if no_utf8 {
|
||||
self.get_property(
|
||||
root_window_wm_check,
|
||||
wm_name_atom,
|
||||
ffi::XA_STRING,
|
||||
)
|
||||
} else {
|
||||
result
|
||||
}
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}.ok();
|
||||
}.ok();
|
||||
|
||||
wm_name.and_then(|wm_name| String::from_utf8(wm_name).ok())
|
||||
wm_name.and_then(|wm_name| String::from_utf8(wm_name).ok())
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,14 @@
|
||||
use {ControlFlow, EventsLoopClosed};
|
||||
use cocoa::{self, appkit, foundation};
|
||||
use cocoa::appkit::{NSApplication, NSEvent, NSEventMask, NSEventModifierFlags, NSEventPhase, NSView, NSWindow};
|
||||
use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent, DeviceEvent, ModifiersState, KeyboardInput};
|
||||
use events::{self, ElementState, Event, TouchPhase, WindowEvent, DeviceEvent, ModifiersState, KeyboardInput};
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
use super::window::Window2;
|
||||
use std;
|
||||
use std::os::raw::*;
|
||||
use super::DeviceId;
|
||||
|
||||
|
||||
pub struct EventsLoop {
|
||||
modifiers: Modifiers,
|
||||
pub shared: Arc<Shared>,
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -294,10 +294,7 @@ impl EventsLoop {
|
||||
|
||||
// FIXME: Document this. Why do we do this? Seems like it passes on events to window/app.
|
||||
// If we don't do this, window does not become main for some reason.
|
||||
match event_type {
|
||||
appkit::NSKeyDown => (),
|
||||
_ => appkit::NSApp().sendEvent_(ns_event),
|
||||
}
|
||||
appkit::NSApp().sendEvent_(ns_event);
|
||||
|
||||
let windows = self.shared.windows.lock().unwrap();
|
||||
let maybe_window = windows.iter()
|
||||
@@ -318,137 +315,53 @@ impl EventsLoop {
|
||||
});
|
||||
|
||||
match event_type {
|
||||
|
||||
appkit::NSKeyDown => {
|
||||
let mut events = std::collections::VecDeque::new();
|
||||
let received_c_str = foundation::NSString::UTF8String(ns_event.characters());
|
||||
let received_str = std::ffi::CStr::from_ptr(received_c_str);
|
||||
|
||||
let vkey = to_virtual_key_code(NSEvent::keyCode(ns_event));
|
||||
let state = ElementState::Pressed;
|
||||
let code = NSEvent::keyCode(ns_event) as u32;
|
||||
let window_event = WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: code,
|
||||
virtual_keycode: vkey,
|
||||
modifiers: event_mods(ns_event),
|
||||
},
|
||||
};
|
||||
for received_char in std::str::from_utf8(received_str.to_bytes()).unwrap().chars() {
|
||||
let window_event = WindowEvent::ReceivedCharacter(received_char);
|
||||
events.push_back(into_event(window_event));
|
||||
}
|
||||
self.shared.pending_events.lock().unwrap().extend(events.into_iter());
|
||||
Some(into_event(window_event))
|
||||
},
|
||||
|
||||
appkit::NSKeyUp => {
|
||||
let vkey = to_virtual_key_code(NSEvent::keyCode(ns_event));
|
||||
|
||||
let state = ElementState::Released;
|
||||
let code = NSEvent::keyCode(ns_event) as u32;
|
||||
let window_event = WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: code,
|
||||
virtual_keycode: vkey,
|
||||
modifiers: event_mods(ns_event),
|
||||
},
|
||||
};
|
||||
Some(into_event(window_event))
|
||||
},
|
||||
|
||||
appkit::NSFlagsChanged => {
|
||||
unsafe fn modifier_event(event: cocoa::base::id,
|
||||
keymask: NSEventModifierFlags,
|
||||
key: events::VirtualKeyCode,
|
||||
key_pressed: bool) -> Option<WindowEvent>
|
||||
{
|
||||
if !key_pressed && NSEvent::modifierFlags(event).contains(keymask) {
|
||||
let state = ElementState::Pressed;
|
||||
let code = NSEvent::keyCode(event) as u32;
|
||||
let window_event = WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: code,
|
||||
virtual_keycode: Some(key),
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
};
|
||||
Some(window_event)
|
||||
|
||||
} else if key_pressed && !NSEvent::modifierFlags(event).contains(keymask) {
|
||||
let state = ElementState::Released;
|
||||
let code = NSEvent::keyCode(event) as u32;
|
||||
let window_event = WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: code,
|
||||
virtual_keycode: Some(key),
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
};
|
||||
Some(window_event)
|
||||
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
let mut events = std::collections::VecDeque::new();
|
||||
if let Some(window_event) = modifier_event(ns_event,
|
||||
NSEventModifierFlags::NSShiftKeyMask,
|
||||
events::VirtualKeyCode::LShift,
|
||||
self.modifiers.shift_pressed)
|
||||
{
|
||||
|
||||
if let Some(window_event) = modifier_event(
|
||||
ns_event,
|
||||
NSEventModifierFlags::NSShiftKeyMask,
|
||||
self.modifiers.shift_pressed,
|
||||
) {
|
||||
self.modifiers.shift_pressed = !self.modifiers.shift_pressed;
|
||||
events.push_back(into_event(window_event));
|
||||
}
|
||||
|
||||
if let Some(window_event) = modifier_event(ns_event,
|
||||
NSEventModifierFlags::NSControlKeyMask,
|
||||
events::VirtualKeyCode::LControl,
|
||||
self.modifiers.ctrl_pressed)
|
||||
{
|
||||
if let Some(window_event) = modifier_event(
|
||||
ns_event,
|
||||
NSEventModifierFlags::NSControlKeyMask,
|
||||
self.modifiers.ctrl_pressed,
|
||||
) {
|
||||
self.modifiers.ctrl_pressed = !self.modifiers.ctrl_pressed;
|
||||
events.push_back(into_event(window_event));
|
||||
}
|
||||
|
||||
if let Some(window_event) = modifier_event(ns_event,
|
||||
NSEventModifierFlags::NSCommandKeyMask,
|
||||
events::VirtualKeyCode::LWin,
|
||||
self.modifiers.win_pressed)
|
||||
{
|
||||
if let Some(window_event) = modifier_event(
|
||||
ns_event,
|
||||
NSEventModifierFlags::NSCommandKeyMask,
|
||||
self.modifiers.win_pressed,
|
||||
) {
|
||||
self.modifiers.win_pressed = !self.modifiers.win_pressed;
|
||||
events.push_back(into_event(window_event));
|
||||
}
|
||||
|
||||
if let Some(window_event) = modifier_event(ns_event,
|
||||
NSEventModifierFlags::NSAlternateKeyMask,
|
||||
events::VirtualKeyCode::LAlt,
|
||||
self.modifiers.alt_pressed)
|
||||
{
|
||||
if let Some(window_event) = modifier_event(
|
||||
ns_event,
|
||||
NSEventModifierFlags::NSAlternateKeyMask,
|
||||
self.modifiers.alt_pressed,
|
||||
) {
|
||||
self.modifiers.alt_pressed = !self.modifiers.alt_pressed;
|
||||
events.push_back(into_event(window_event));
|
||||
}
|
||||
|
||||
let event = events.pop_front();
|
||||
self.shared.pending_events.lock().unwrap().extend(events.into_iter());
|
||||
self.shared.pending_events
|
||||
.lock()
|
||||
.unwrap()
|
||||
.extend(events.into_iter());
|
||||
event
|
||||
},
|
||||
|
||||
appkit::NSLeftMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Left, modifiers: event_mods(ns_event) })) },
|
||||
appkit::NSLeftMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Left, modifiers: event_mods(ns_event) })) },
|
||||
appkit::NSRightMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Right, modifiers: event_mods(ns_event) })) },
|
||||
appkit::NSRightMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Right, modifiers: event_mods(ns_event) })) },
|
||||
appkit::NSOtherMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Middle, modifiers: event_mods(ns_event) })) },
|
||||
appkit::NSOtherMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Middle, modifiers: event_mods(ns_event) })) },
|
||||
|
||||
appkit::NSMouseEntered => {
|
||||
let window = match maybe_window.or_else(maybe_key_window) {
|
||||
Some(window) => window,
|
||||
@@ -464,14 +377,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 }))
|
||||
},
|
||||
@@ -484,50 +399,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 window_point = ns_event.locationInWindow();
|
||||
let view_point = if ns_window == cocoa::base::nil {
|
||||
let ns_size = foundation::NSSize::new(0.0, 0.0);
|
||||
let ns_rect = foundation::NSRect::new(window_point, ns_size);
|
||||
let window_rect = window.window.convertRectFromScreen_(ns_rect);
|
||||
window.view.convertPoint_fromView_(window_rect.origin, cocoa::base::nil)
|
||||
} else {
|
||||
window.view.convertPoint_fromView_(window_point, cocoa::base::nil)
|
||||
};
|
||||
let view_rect = NSView::frame(*window.view);
|
||||
let scale_factor = window.hidpi_factor();
|
||||
|
||||
let mut events = std::collections::VecDeque::new();
|
||||
|
||||
{
|
||||
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 event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event };
|
||||
events.push_back(event);
|
||||
}
|
||||
|
||||
let delta_x = (scale_factor * ns_event.deltaX() as f32) as f64;
|
||||
let mut events = std::collections::VecDeque::with_capacity(3);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -538,19 +433,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,
|
||||
@@ -561,11 +459,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,
|
||||
)
|
||||
},
|
||||
}
|
||||
});
|
||||
@@ -621,8 +523,7 @@ impl Proxy {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn to_virtual_key_code(code: u16) -> Option<events::VirtualKeyCode> {
|
||||
pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> {
|
||||
Some(match code {
|
||||
0x00 => events::VirtualKeyCode::A,
|
||||
0x01 => events::VirtualKeyCode::S,
|
||||
@@ -678,14 +579,14 @@ fn to_virtual_key_code(code: u16) -> Option<events::VirtualKeyCode> {
|
||||
0x33 => events::VirtualKeyCode::Back,
|
||||
//0x34 => unkown,
|
||||
0x35 => events::VirtualKeyCode::Escape,
|
||||
0x36 => events::VirtualKeyCode::RWin,
|
||||
0x37 => events::VirtualKeyCode::LWin,
|
||||
0x36 => events::VirtualKeyCode::LWin,
|
||||
0x37 => events::VirtualKeyCode::RWin,
|
||||
0x38 => events::VirtualKeyCode::LShift,
|
||||
//0x39 => Caps lock,
|
||||
//0x3a => Left alt,
|
||||
0x3a => events::VirtualKeyCode::LAlt,
|
||||
0x3b => events::VirtualKeyCode::LControl,
|
||||
0x3c => events::VirtualKeyCode::RShift,
|
||||
//0x3d => Right alt,
|
||||
0x3d => events::VirtualKeyCode::RAlt,
|
||||
0x3e => events::VirtualKeyCode::RControl,
|
||||
//0x3f => Fn key,
|
||||
//0x40 => F17 Key,
|
||||
@@ -758,7 +659,7 @@ fn to_virtual_key_code(code: u16) -> Option<events::VirtualKeyCode> {
|
||||
})
|
||||
}
|
||||
|
||||
fn event_mods(event: cocoa::base::id) -> ModifiersState {
|
||||
pub fn event_mods(event: cocoa::base::id) -> ModifiersState {
|
||||
let flags = unsafe {
|
||||
NSEvent::modifierFlags(event)
|
||||
};
|
||||
@@ -770,5 +671,34 @@ fn event_mods(event: cocoa::base::id) -> ModifiersState {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn modifier_event(
|
||||
ns_event: cocoa::base::id,
|
||||
keymask: NSEventModifierFlags,
|
||||
was_key_pressed: bool,
|
||||
) -> Option<WindowEvent> {
|
||||
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);
|
||||
Some(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state,
|
||||
scancode,
|
||||
virtual_keycode,
|
||||
modifiers: event_mods(ns_event),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
||||
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
|
||||
pub const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
|
||||
|
||||
107
src/platform/macos/ffi.rs
Normal file
107
src/platform/macos/ffi.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
// TODO: Upstream these
|
||||
|
||||
#![allow(dead_code, non_snake_case, non_upper_case_globals)]
|
||||
|
||||
use cocoa::base::id;
|
||||
use cocoa::foundation::{NSInteger, NSUInteger};
|
||||
use objc;
|
||||
|
||||
pub const NSNotFound: NSInteger = NSInteger::max_value();
|
||||
|
||||
#[repr(C)]
|
||||
pub struct NSRange {
|
||||
pub location: NSUInteger,
|
||||
pub length: NSUInteger,
|
||||
}
|
||||
|
||||
impl NSRange {
|
||||
#[inline]
|
||||
pub fn new(location: NSUInteger, length: NSUInteger) -> NSRange {
|
||||
NSRange { location, length }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl objc::Encode for NSRange {
|
||||
fn encode() -> objc::Encoding {
|
||||
let encoding = format!(
|
||||
// TODO: Verify that this is correct
|
||||
"{{NSRange={}{}}}",
|
||||
NSUInteger::encode().as_str(),
|
||||
NSUInteger::encode().as_str(),
|
||||
);
|
||||
unsafe { objc::Encoding::from_str(&encoding) }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NSMutableAttributedString: Sized {
|
||||
unsafe fn alloc(_: Self) -> id {
|
||||
msg_send![class!(NSMutableAttributedString), alloc]
|
||||
}
|
||||
|
||||
unsafe fn init(self) -> id; // *mut NSMutableAttributedString
|
||||
unsafe fn initWithString(self, string: id) -> id;
|
||||
unsafe fn initWithAttributedString(self, string: id) -> id;
|
||||
|
||||
unsafe fn string(self) -> id; // *mut NSString
|
||||
unsafe fn mutableString(self) -> id; // *mut NSMutableString
|
||||
unsafe fn length(self) -> NSUInteger;
|
||||
}
|
||||
|
||||
impl NSMutableAttributedString for id {
|
||||
unsafe fn init(self) -> id {
|
||||
msg_send![self, init]
|
||||
}
|
||||
|
||||
unsafe fn initWithString(self, string: id) -> id {
|
||||
msg_send![self, initWithString:string]
|
||||
}
|
||||
|
||||
unsafe fn initWithAttributedString(self, string: id) -> id {
|
||||
msg_send![self, initWithAttributedString:string]
|
||||
}
|
||||
|
||||
unsafe fn string(self) -> id {
|
||||
msg_send![self, string]
|
||||
}
|
||||
|
||||
unsafe fn mutableString(self) -> id {
|
||||
msg_send![self, mutableString]
|
||||
}
|
||||
|
||||
unsafe fn length(self) -> NSUInteger {
|
||||
msg_send![self, length]
|
||||
}
|
||||
}
|
||||
|
||||
pub const kCGBaseWindowLevelKey: NSInteger = 0;
|
||||
pub const kCGMinimumWindowLevelKey: NSInteger = 1;
|
||||
pub const kCGDesktopWindowLevelKey: NSInteger = 2;
|
||||
pub const kCGBackstopMenuLevelKey: NSInteger = 3;
|
||||
pub const kCGNormalWindowLevelKey: NSInteger = 4;
|
||||
pub const kCGFloatingWindowLevelKey: NSInteger = 5;
|
||||
pub const kCGTornOffMenuWindowLevelKey: NSInteger = 6;
|
||||
pub const kCGDockWindowLevelKey: NSInteger = 7;
|
||||
pub const kCGMainMenuWindowLevelKey: NSInteger = 8;
|
||||
pub const kCGStatusWindowLevelKey: NSInteger = 9;
|
||||
pub const kCGModalPanelWindowLevelKey: NSInteger = 10;
|
||||
pub const kCGPopUpMenuWindowLevelKey: NSInteger = 11;
|
||||
pub const kCGDraggingWindowLevelKey: NSInteger = 12;
|
||||
pub const kCGScreenSaverWindowLevelKey: NSInteger = 13;
|
||||
pub const kCGMaximumWindowLevelKey: NSInteger = 14;
|
||||
pub const kCGOverlayWindowLevelKey: NSInteger = 15;
|
||||
pub const kCGHelpWindowLevelKey: NSInteger = 16;
|
||||
pub const kCGUtilityWindowLevelKey: NSInteger = 17;
|
||||
pub const kCGDesktopIconWindowLevelKey: NSInteger = 18;
|
||||
pub const kCGCursorWindowLevelKey: NSInteger = 19;
|
||||
pub const kCGNumberOfWindowLevelKeys: NSInteger = 20;
|
||||
|
||||
pub enum NSWindowLevel {
|
||||
NSNormalWindowLevel = kCGBaseWindowLevelKey as _,
|
||||
NSFloatingWindowLevel = kCGFloatingWindowLevelKey as _,
|
||||
NSTornOffMenuWindowLevel = kCGTornOffMenuWindowLevelKey as _,
|
||||
NSModalPanelWindowLevel = kCGModalPanelWindowLevelKey as _,
|
||||
NSMainMenuWindowLevel = kCGMainMenuWindowLevelKey as _,
|
||||
NSStatusWindowLevel = kCGStatusWindowLevelKey as _,
|
||||
NSPopUpMenuWindowLevel = kCGPopUpMenuWindowLevelKey as _,
|
||||
NSScreenSaverWindowLevel = kCGScreenSaverWindowLevelKey as _,
|
||||
}
|
||||
@@ -38,5 +38,8 @@ impl Window {
|
||||
}
|
||||
|
||||
mod events_loop;
|
||||
mod ffi;
|
||||
mod monitor;
|
||||
mod util;
|
||||
mod view;
|
||||
mod window;
|
||||
|
||||
@@ -1,29 +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};
|
||||
use std::collections::VecDeque;
|
||||
use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds};
|
||||
|
||||
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 {
|
||||
@@ -32,6 +47,41 @@ 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: PhysicalSize,
|
||||
position: PhysicalPosition,
|
||||
hidpi_factor: f64,
|
||||
}
|
||||
|
||||
let monitor_id_proxy = MonitorId {
|
||||
name: self.get_name(),
|
||||
native_identifier: self.get_native_identifier(),
|
||||
dimensions: self.get_dimensions(),
|
||||
position: self.get_position(),
|
||||
hidpi_factor: self.get_hidpi_factor(),
|
||||
};
|
||||
|
||||
monitor_id_proxy.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
let MonitorId(display_id) = *self;
|
||||
@@ -44,29 +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) {
|
||||
unimplemented!()
|
||||
pub fn get_position(&self) -> PhysicalPosition {
|
||||
let bounds = unsafe { CGDisplayBounds(self.get_native_identifier()) };
|
||||
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> {
|
||||
|
||||
38
src/platform/macos/util.rs
Normal file
38
src/platform/macos/util.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use cocoa::appkit::NSWindowStyleMask;
|
||||
use cocoa::base::{id, nil};
|
||||
use cocoa::foundation::{NSRect, NSUInteger};
|
||||
use core_graphics::display::CGDisplay;
|
||||
|
||||
use platform::platform::ffi;
|
||||
use platform::platform::window::IdRef;
|
||||
|
||||
pub const EMPTY_RANGE: ffi::NSRange = ffi::NSRange {
|
||||
location: ffi::NSNotFound as NSUInteger,
|
||||
length: 0,
|
||||
};
|
||||
|
||||
// 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) -> 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) {
|
||||
use cocoa::appkit::NSWindow;
|
||||
window.setStyleMask_(mask);
|
||||
// If we don't do this, key handling will break. Therefore, never call `setStyleMask` directly!
|
||||
window.makeFirstResponder_(view);
|
||||
}
|
||||
|
||||
pub unsafe fn create_input_context(view: id) -> IdRef {
|
||||
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 _: () = msg_send![app, orderFrontCharacterPalette:nil];
|
||||
}
|
||||
568
src/platform/macos/view.rs
Normal file
568
src/platform/macos/view.rs
Normal file
@@ -0,0 +1,568 @@
|
||||
// This is a pretty close port of the implementation in GLFW:
|
||||
// https://github.com/glfw/glfw/blob/7ef34eb06de54dd9186d3d21a401b2ef819b59e7/src/cocoa_window.m
|
||||
|
||||
use std::{slice, str};
|
||||
use std::boxed::Box;
|
||||
use std::collections::VecDeque;
|
||||
use std::os::raw::*;
|
||||
use std::sync::Weak;
|
||||
|
||||
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 {ElementState, Event, KeyboardInput, MouseButton, WindowEvent, WindowId};
|
||||
use platform::platform::events_loop::{DEVICE_ID, event_mods, Shared, to_virtual_key_code};
|
||||
use platform::platform::util;
|
||||
use platform::platform::ffi::*;
|
||||
use platform::platform::window::{get_window_id, IdRef};
|
||||
|
||||
struct ViewState {
|
||||
window: id,
|
||||
shared: Weak<Shared>,
|
||||
ime_spot: Option<(f64, f64)>,
|
||||
raw_characters: Option<String>,
|
||||
last_insert: Option<String>,
|
||||
}
|
||||
|
||||
pub fn new_view(window: id, shared: Weak<Shared>) -> IdRef {
|
||||
let state = ViewState {
|
||||
window,
|
||||
shared,
|
||||
ime_spot: None,
|
||||
raw_characters: None,
|
||||
last_insert: None,
|
||||
};
|
||||
unsafe {
|
||||
// This is free'd in `dealloc`
|
||||
let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void;
|
||||
let view: id = msg_send![VIEW_CLASS.0, alloc];
|
||||
IdRef::new(msg_send![view, initWithWinit:state_ptr])
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
let content_rect = NSWindow::contentRectForFrameRect_(
|
||||
state.window,
|
||||
NSWindow::frame(state.window),
|
||||
);
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
struct ViewClass(*const Class);
|
||||
unsafe impl Send for ViewClass {}
|
||||
unsafe impl Sync for ViewClass {}
|
||||
|
||||
lazy_static! {
|
||||
static ref VIEW_CLASS: ViewClass = unsafe {
|
||||
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(
|
||||
sel!(initWithWinit:),
|
||||
init_with_winit as extern fn(&Object, Sel, *mut c_void) -> id,
|
||||
);
|
||||
decl.add_method(sel!(hasMarkedText), has_marked_text as extern fn(&Object, Sel) -> BOOL);
|
||||
decl.add_method(
|
||||
sel!(markedRange),
|
||||
marked_range as extern fn(&Object, Sel) -> NSRange,
|
||||
);
|
||||
decl.add_method(sel!(selectedRange), selected_range as extern fn(&Object, Sel) -> NSRange);
|
||||
decl.add_method(
|
||||
sel!(setMarkedText:selectedRange:replacementRange:),
|
||||
set_marked_text as extern fn(&mut Object, Sel, id, NSRange, NSRange),
|
||||
);
|
||||
decl.add_method(sel!(unmarkText), unmark_text as extern fn(&Object, Sel));
|
||||
decl.add_method(
|
||||
sel!(validAttributesForMarkedText),
|
||||
valid_attributes_for_marked_text as extern fn(&Object, Sel) -> id,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(attributedSubstringForProposedRange:actualRange:),
|
||||
attributed_substring_for_proposed_range
|
||||
as extern fn(&Object, Sel, NSRange, *mut c_void) -> id,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(insertText:replacementRange:),
|
||||
insert_text as extern fn(&Object, Sel, id, NSRange),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(characterIndexForPoint:),
|
||||
character_index_for_point as extern fn(&Object, Sel, NSPoint) -> NSUInteger,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(firstRectForCharacterRange:actualRange:),
|
||||
first_rect_for_character_range
|
||||
as extern fn(&Object, Sel, NSRange, *mut c_void) -> NSRect,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(doCommandBySelector:),
|
||||
do_command_by_selector as extern fn(&Object, Sel, Sel),
|
||||
);
|
||||
decl.add_method(sel!(keyDown:), key_down as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(keyUp:), key_up as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(insertTab:), insert_tab as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(insertBackTab:), insert_back_tab as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(mouseDown:), mouse_down as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(mouseUp:), mouse_up as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(rightMouseDown:), right_mouse_down as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(rightMouseUp:), right_mouse_up as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(otherMouseDown:), other_mouse_down as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(otherMouseUp:), other_mouse_up as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(mouseMoved:), mouse_moved as extern fn(&Object, Sel, id));
|
||||
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_ivar::<*mut c_void>("winitState");
|
||||
decl.add_ivar::<id>("markedText");
|
||||
let protocol = Protocol::get("NSTextInputClient").unwrap();
|
||||
decl.add_protocol(&protocol);
|
||||
ViewClass(decl.register())
|
||||
};
|
||||
}
|
||||
|
||||
extern fn dealloc(this: &Object, _sel: Sel) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let marked_text: id = *this.get_ivar("markedText");
|
||||
let _: () = msg_send![marked_text, release];
|
||||
Box::from_raw(state as *mut ViewState);
|
||||
}
|
||||
}
|
||||
|
||||
extern fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> id {
|
||||
unsafe {
|
||||
let this: id = msg_send![this, init];
|
||||
if this != nil {
|
||||
(*this).set_ivar("winitState", state);
|
||||
let marked_text = <id as NSMutableAttributedString>::init(
|
||||
NSMutableAttributedString::alloc(nil),
|
||||
);
|
||||
(*this).set_ivar("markedText", marked_text);
|
||||
}
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
extern fn has_marked_text(this: &Object, _sel: Sel) -> BOOL {
|
||||
//println!("hasMarkedText");
|
||||
unsafe {
|
||||
let marked_text: id = *this.get_ivar("markedText");
|
||||
(marked_text.length() > 0) as i8
|
||||
}
|
||||
}
|
||||
|
||||
extern fn marked_range(this: &Object, _sel: Sel) -> NSRange {
|
||||
//println!("markedRange");
|
||||
unsafe {
|
||||
let marked_text: id = *this.get_ivar("markedText");
|
||||
let length = marked_text.length();
|
||||
if length > 0 {
|
||||
NSRange::new(0, length - 1)
|
||||
} else {
|
||||
util::EMPTY_RANGE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern fn selected_range(_this: &Object, _sel: Sel) -> NSRange {
|
||||
//println!("selectedRange");
|
||||
util::EMPTY_RANGE
|
||||
}
|
||||
|
||||
extern fn set_marked_text(
|
||||
this: &mut Object,
|
||||
_sel: Sel,
|
||||
string: id,
|
||||
_selected_range: NSRange,
|
||||
_replacement_range: NSRange,
|
||||
) {
|
||||
//println!("setMarkedText");
|
||||
unsafe {
|
||||
let marked_text_ref: &mut id = this.get_mut_ivar("markedText");
|
||||
let _: () = msg_send![(*marked_text_ref), release];
|
||||
let marked_text = NSMutableAttributedString::alloc(nil);
|
||||
let has_attr = msg_send![string, isKindOfClass:class!(NSAttributedString)];
|
||||
if has_attr {
|
||||
marked_text.initWithAttributedString(string);
|
||||
} else {
|
||||
marked_text.initWithString(string);
|
||||
};
|
||||
*marked_text_ref = marked_text;
|
||||
}
|
||||
}
|
||||
|
||||
extern fn unmark_text(this: &Object, _sel: Sel) {
|
||||
//println!("unmarkText");
|
||||
unsafe {
|
||||
let marked_text: id = *this.get_ivar("markedText");
|
||||
let mutable_string = marked_text.mutableString();
|
||||
let _: () = msg_send![mutable_string, setString:""];
|
||||
let input_context: id = msg_send![this, inputContext];
|
||||
let _: () = msg_send![input_context, discardMarkedText];
|
||||
}
|
||||
}
|
||||
|
||||
extern fn valid_attributes_for_marked_text(_this: &Object, _sel: Sel) -> id {
|
||||
//println!("validAttributesForMarkedText");
|
||||
unsafe { msg_send![class!(NSArray), array] }
|
||||
}
|
||||
|
||||
extern fn attributed_substring_for_proposed_range(
|
||||
_this: &Object,
|
||||
_sel: Sel,
|
||||
_range: NSRange,
|
||||
_actual_range: *mut c_void, // *mut NSRange
|
||||
) -> id {
|
||||
//println!("attributedSubstringForProposedRange");
|
||||
nil
|
||||
}
|
||||
|
||||
extern fn character_index_for_point(_this: &Object, _sel: Sel, _point: NSPoint) -> NSUInteger {
|
||||
//println!("characterIndexForPoint");
|
||||
0
|
||||
}
|
||||
|
||||
extern fn first_rect_for_character_range(
|
||||
this: &Object,
|
||||
_sel: Sel,
|
||||
_range: NSRange,
|
||||
_actual_range: *mut c_void, // *mut NSRange
|
||||
) -> NSRect {
|
||||
//println!("firstRectForCharacterRange");
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
let (x, y) = state.ime_spot.unwrap_or_else(|| {
|
||||
let content_rect = NSWindow::contentRectForFrameRect_(
|
||||
state.window,
|
||||
NSWindow::frame(state.window),
|
||||
);
|
||||
let x = content_rect.origin.x;
|
||||
let y = util::bottom_left_to_top_left(content_rect);
|
||||
(x, y)
|
||||
});
|
||||
|
||||
NSRect::new(
|
||||
NSPoint::new(x as _, y as _),
|
||||
NSSize::new(0.0, 0.0),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extern fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange) {
|
||||
//println!("insertText");
|
||||
unsafe {
|
||||
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 characters = if has_attr {
|
||||
// This is a *mut NSAttributedString
|
||||
msg_send![string, string]
|
||||
} else {
|
||||
// This is already a *mut NSString
|
||||
string
|
||||
};
|
||||
|
||||
let slice = slice::from_raw_parts(
|
||||
characters.UTF8String() as *const c_uchar,
|
||||
characters.len(),
|
||||
);
|
||||
let string = str::from_utf8_unchecked(slice);
|
||||
state.last_insert = Some(string.to_owned());
|
||||
|
||||
// We don't need this now, but it's here if that changes.
|
||||
//let event: id = msg_send![class!(NSApp), currentEvent];
|
||||
|
||||
let mut events = VecDeque::with_capacity(characters.len());
|
||||
for character in string.chars() {
|
||||
events.push_back(Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.window)),
|
||||
event: WindowEvent::ReceivedCharacter(character),
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(shared) = state.shared.upgrade() {
|
||||
shared.pending_events
|
||||
.lock()
|
||||
.unwrap()
|
||||
.append(&mut events);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
|
||||
//println!("doCommandBySelector");
|
||||
// Basically, we're sent this message whenever a keyboard event that doesn't generate a "human readable" character
|
||||
// happens, i.e. newlines, tabs, and Ctrl+C.
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
let shared = if let Some(shared) = state.shared.upgrade() {
|
||||
shared
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut events = VecDeque::with_capacity(1);
|
||||
if command == sel!(insertNewline:) {
|
||||
// The `else` condition would emit the same character, but I'm keeping this here both...
|
||||
// 1) as a reminder for how `doCommandBySelector` works
|
||||
// 2) to make our use of carriage return explicit
|
||||
events.push_back(Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.window)),
|
||||
event: WindowEvent::ReceivedCharacter('\r'),
|
||||
});
|
||||
} else {
|
||||
let raw_characters = state.raw_characters.take();
|
||||
if let Some(raw_characters) = raw_characters {
|
||||
for character in raw_characters.chars() {
|
||||
events.push_back(Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.window)),
|
||||
event: WindowEvent::ReceivedCharacter(character),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
shared.pending_events
|
||||
.lock()
|
||||
.unwrap()
|
||||
.append(&mut events);
|
||||
}
|
||||
}
|
||||
|
||||
extern fn key_down(this: &Object, _sel: Sel, event: id) {
|
||||
//println!("keyDown");
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
let window_id = WindowId(get_window_id(state.window));
|
||||
|
||||
let keycode: c_ushort = msg_send![event, keyCode];
|
||||
let virtual_keycode = to_virtual_key_code(keycode);
|
||||
let scancode = keycode as u32;
|
||||
let is_repeat = msg_send![event, isARepeat];
|
||||
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: ElementState::Pressed,
|
||||
scancode,
|
||||
virtual_keycode,
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
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 string = str::from_utf8_unchecked(slice);
|
||||
Some(string.to_owned())
|
||||
};
|
||||
|
||||
if let Some(shared) = state.shared.upgrade() {
|
||||
shared.pending_events
|
||||
.lock()
|
||||
.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() {
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ReceivedCharacter(character),
|
||||
};
|
||||
shared.pending_events
|
||||
.lock()
|
||||
.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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern fn key_up(this: &Object, _sel: Sel, event: id) {
|
||||
//println!("keyUp");
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
state.last_insert = None;
|
||||
|
||||
let keycode: c_ushort = msg_send![event, keyCode];
|
||||
let virtual_keycode = to_virtual_key_code(keycode);
|
||||
let scancode = keycode as u32;
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.window)),
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: ElementState::Released,
|
||||
scancode,
|
||||
virtual_keycode,
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(shared) = state.shared.upgrade() {
|
||||
shared.pending_events
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push_back(window_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern fn insert_tab(this: &Object, _sel: Sel, _sender: id) {
|
||||
unsafe {
|
||||
let window: id = msg_send![this, window];
|
||||
let first_responder: id = msg_send![window, firstResponder];
|
||||
let this_ptr = this as *const _ as *mut _;
|
||||
if first_responder == this_ptr {
|
||||
let (): _ = msg_send![window, selectNextKeyView:this];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) {
|
||||
unsafe {
|
||||
let window: id = msg_send![this, window];
|
||||
let first_responder: id = msg_send![window, firstResponder];
|
||||
let this_ptr = this as *const _ as *mut _;
|
||||
if first_responder == this_ptr {
|
||||
let (): _ = msg_send![window, selectPreviousKeyView:this];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: ElementState) {
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.window)),
|
||||
event: WindowEvent::MouseInput {
|
||||
device_id: DEVICE_ID,
|
||||
state: button_state,
|
||||
button,
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(shared) = state.shared.upgrade() {
|
||||
shared.pending_events
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push_back(window_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern fn mouse_down(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_click(this, event, MouseButton::Left, ElementState::Pressed);
|
||||
}
|
||||
|
||||
extern fn mouse_up(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_click(this, event, MouseButton::Left, ElementState::Released);
|
||||
}
|
||||
|
||||
extern fn right_mouse_down(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_click(this, event, MouseButton::Right, ElementState::Pressed);
|
||||
}
|
||||
|
||||
extern fn right_mouse_up(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_click(this, event, MouseButton::Right, ElementState::Released);
|
||||
}
|
||||
|
||||
extern fn other_mouse_down(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_click(this, event, MouseButton::Middle, ElementState::Pressed);
|
||||
}
|
||||
|
||||
extern fn other_mouse_up(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_click(this, event, MouseButton::Middle, ElementState::Released);
|
||||
}
|
||||
|
||||
fn mouse_motion(this: &Object, event: id) {
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
// We have to do this to have access to the `NSView` trait...
|
||||
let view: id = this as *const _ as *mut _;
|
||||
|
||||
let window_point = event.locationInWindow();
|
||||
let view_point = view.convertPoint_fromView_(window_point, nil);
|
||||
let view_rect = NSView::frame(view);
|
||||
|
||||
if view_point.x.is_sign_negative()
|
||||
|| view_point.y.is_sign_negative()
|
||||
|| view_point.x > view_rect.size.width
|
||||
|| view_point.y > view_rect.size.height {
|
||||
// Point is outside of the client area (view)
|
||||
return;
|
||||
}
|
||||
|
||||
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).into(),
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(shared) = state.shared.upgrade() {
|
||||
shared.pending_events
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push_back(window_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern fn mouse_moved(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_motion(this, event);
|
||||
}
|
||||
|
||||
extern fn mouse_dragged(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_motion(this, event);
|
||||
}
|
||||
|
||||
extern fn right_mouse_dragged(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_motion(this, event);
|
||||
}
|
||||
|
||||
extern fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_motion(this, event);
|
||||
}
|
||||
@@ -1,37 +1,53 @@
|
||||
use {CreationError, Event, WindowEvent, WindowId, MouseCursor, CursorState};
|
||||
use CreationError::OsError;
|
||||
use libc;
|
||||
use std;
|
||||
use std::cell::{Cell, RefCell};
|
||||
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;
|
||||
use cocoa::appkit::{
|
||||
self,
|
||||
CGFloat,
|
||||
NSApplication,
|
||||
NSColor,
|
||||
NSScreen,
|
||||
NSView,
|
||||
NSWindow,
|
||||
NSWindowButton,
|
||||
NSWindowStyleMask,
|
||||
};
|
||||
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 {
|
||||
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>,
|
||||
@@ -44,8 +60,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 {
|
||||
@@ -55,16 +74,17 @@ impl DelegateState {
|
||||
// resizable temporality
|
||||
let curr_mask = self.window.styleMask();
|
||||
|
||||
if !curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask) {
|
||||
self.window
|
||||
.setStyleMask_(NSWindowStyleMask::NSResizableWindowMask);
|
||||
let required = NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask;
|
||||
let needs_temp_mask = !curr_mask.contains(required);
|
||||
if needs_temp_mask {
|
||||
util::set_style_mask(*self.window, *self.view, required);
|
||||
}
|
||||
|
||||
let is_zoomed: BOOL = msg_send![*self.window, isZoomed];
|
||||
|
||||
// Roll back temp styles
|
||||
if !curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask) {
|
||||
self.window.setStyleMask_(curr_mask);
|
||||
if needs_temp_mask {
|
||||
util::set_style_mask(*self.window, *self.view, curr_mask);
|
||||
}
|
||||
|
||||
is_zoomed != 0
|
||||
@@ -74,13 +94,20 @@ impl DelegateState {
|
||||
fn restore_state_from_fullscreen(&mut self) {
|
||||
let maximized = unsafe {
|
||||
let mut win_attribs = self.win_attribs.borrow_mut();
|
||||
|
||||
win_attribs.fullscreen = None;
|
||||
let save_style_opt = self.save_style_mask.take();
|
||||
|
||||
if let Some(save_style) = save_style_opt {
|
||||
self.window.setStyleMask_(save_style);
|
||||
}
|
||||
let mask = {
|
||||
let base_mask = self.save_style_mask
|
||||
.take()
|
||||
.unwrap_or_else(|| self.window.styleMask());
|
||||
if win_attribs.resizable {
|
||||
base_mask | NSWindowStyleMask::NSResizableWindowMask
|
||||
} else {
|
||||
base_mask & !NSWindowStyleMask::NSResizableWindowMask
|
||||
}
|
||||
};
|
||||
|
||||
util::set_style_mask(*self.window, *self.view, mask);
|
||||
|
||||
win_attribs.maximized
|
||||
};
|
||||
@@ -105,16 +132,17 @@ impl DelegateState {
|
||||
let mut win_attribs = self.win_attribs.borrow_mut();
|
||||
win_attribs.maximized = maximized;
|
||||
|
||||
let curr_mask = unsafe { self.window.styleMask() };
|
||||
if win_attribs.fullscreen.is_some() {
|
||||
// Handle it in window_did_exit_fullscreen
|
||||
return;
|
||||
} else if win_attribs.decorations {
|
||||
// Just use the native zoom if not borderless
|
||||
} else if curr_mask.contains(NSWindowStyleMask::NSResizableWindowMask) {
|
||||
// Just use the native zoom if resizable
|
||||
unsafe {
|
||||
self.window.zoom_(nil);
|
||||
}
|
||||
} else {
|
||||
// if it is borderless, we set the frame directly
|
||||
// if it's not resizable, we set the frame directly
|
||||
unsafe {
|
||||
let new_rect = if maximized {
|
||||
let screen = NSScreen::mainScreen(nil);
|
||||
@@ -138,48 +166,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 = Window2::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
|
||||
}
|
||||
@@ -189,7 +213,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() {
|
||||
@@ -203,8 +227,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +237,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,18 +245,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,7 +274,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,7 +282,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +305,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)));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -302,7 +334,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)));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -317,7 +349,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,7 +358,7 @@ impl WindowDelegate {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
state.win_attribs.borrow_mut().fullscreen = Some(get_current_monitor());
|
||||
state.win_attribs.borrow_mut().fullscreen = Some(get_current_monitor(*state.window));
|
||||
|
||||
state.handle_with_fullscreen = false;
|
||||
}
|
||||
@@ -391,7 +423,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
|
||||
@@ -415,13 +447,13 @@ impl WindowDelegate {
|
||||
// callbacks for drag and drop events
|
||||
decl.add_method(sel!(draggingEntered:),
|
||||
dragging_entered as extern fn(&Object, Sel, id) -> BOOL);
|
||||
decl.add_method(sel!(prepareForDragOperation:),
|
||||
decl.add_method(sel!(prepareForDragOperation:),
|
||||
prepare_for_drag_operation as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(performDragOperation:),
|
||||
decl.add_method(sel!(performDragOperation:),
|
||||
perform_drag_operation as extern fn(&Object, Sel, id) -> BOOL);
|
||||
decl.add_method(sel!(concludeDragOperation:),
|
||||
decl.add_method(sel!(concludeDragOperation:),
|
||||
conclude_drag_operation as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(draggingExited:),
|
||||
decl.add_method(sel!(draggingExited:),
|
||||
dragging_exited as extern fn(&Object, Sel, id));
|
||||
|
||||
// callbacks for fullscreen events
|
||||
@@ -488,29 +520,27 @@ pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub titlebar_hidden: bool,
|
||||
pub titlebar_buttons_hidden: bool,
|
||||
pub fullsize_content_view: bool,
|
||||
pub resize_increments: Option<LogicalSize>,
|
||||
}
|
||||
|
||||
pub struct Window2 {
|
||||
pub view: IdRef,
|
||||
pub window: IdRef,
|
||||
pub delegate: WindowDelegate,
|
||||
pub input_context: IdRef,
|
||||
cursor_hidden: AtomicBool,
|
||||
}
|
||||
|
||||
unsafe impl Send for Window2 {}
|
||||
unsafe impl Sync for Window2 {}
|
||||
|
||||
/// Helpper funciton to convert NSScreen::mainScreen to MonitorId
|
||||
unsafe fn get_current_monitor() -> RootMonitorId {
|
||||
let screen = NSScreen::mainScreen(nil);
|
||||
unsafe fn get_current_monitor(window: id) -> RootMonitorId {
|
||||
let screen: id = msg_send![window, screen];
|
||||
let desc = NSScreen::deviceDescription(screen);
|
||||
let key = IdRef::new(NSString::alloc(nil).init_str("NSScreenNumber"));
|
||||
|
||||
let value = NSDictionary::valueForKey_(desc, *key);
|
||||
let display_id = msg_send![value, unsignedIntegerValue];
|
||||
|
||||
RootMonitorId {
|
||||
inner: EventsLoop::make_monitor_from_display(display_id),
|
||||
}
|
||||
RootMonitorId { inner: EventsLoop::make_monitor_from_display(display_id) }
|
||||
}
|
||||
|
||||
impl Drop for Window2 {
|
||||
@@ -559,42 +589,47 @@ impl WindowExt for Window2 {
|
||||
impl Window2 {
|
||||
pub fn new(
|
||||
shared: Weak<Shared>,
|
||||
win_attribs: WindowAttributes,
|
||||
mut win_attribs: WindowAttributes,
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
// Might as well save some RAM...
|
||||
win_attribs.window_icon.take();
|
||||
|
||||
let autoreleasepool = unsafe {
|
||||
NSAutoreleasePool::new(nil)
|
||||
};
|
||||
|
||||
let app = match Window2::create_app(pl_attribs.activation_policy) {
|
||||
Some(app) => app,
|
||||
None => {
|
||||
None => {
|
||||
let _: () = unsafe { msg_send![autoreleasepool, drain] };
|
||||
return Err(OsError(format!("Couldn't create NSApplication")));
|
||||
},
|
||||
};
|
||||
|
||||
let window = match Window2::create_window(&win_attribs, &pl_attribs)
|
||||
{
|
||||
Some(window) => window,
|
||||
None => {
|
||||
let window = match Window2::create_window(&win_attribs, &pl_attribs) {
|
||||
Some(res) => res,
|
||||
None => {
|
||||
let _: () = unsafe { msg_send![autoreleasepool, drain] };
|
||||
return Err(OsError(format!("Couldn't create NSWindow")));
|
||||
},
|
||||
};
|
||||
let view = match Window2::create_view(*window) {
|
||||
let view = match Window2::create_view(*window, Weak::clone(&shared)) {
|
||||
Some(view) => view,
|
||||
None => {
|
||||
None => {
|
||||
let _: () = unsafe { msg_send![autoreleasepool, drain] };
|
||||
return Err(OsError(format!("Couldn't create NSView")));
|
||||
},
|
||||
};
|
||||
|
||||
let input_context = unsafe { util::create_input_context(*view) };
|
||||
|
||||
unsafe {
|
||||
if win_attribs.transparent {
|
||||
(*window as id).setOpaque_(NO);
|
||||
@@ -603,12 +638,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;
|
||||
@@ -617,7 +651,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,
|
||||
@@ -626,19 +662,27 @@ 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
|
||||
if let Some(ref monitor) = win_attribs.fullscreen {
|
||||
unsafe {
|
||||
if monitor.inner != get_current_monitor().inner {
|
||||
if monitor.inner != get_current_monitor(*window.window).inner {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
@@ -686,7 +730,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);
|
||||
@@ -714,15 +758,18 @@ 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))
|
||||
}
|
||||
};
|
||||
|
||||
let mut masks = if !attrs.decorations && !screen.is_some() {
|
||||
// unresizable Window2 without a titlebar or borders
|
||||
// Resizable Window2 without a titlebar or borders
|
||||
// if decorations is set to false, ignore pl_attrs
|
||||
NSWindowStyleMask::NSBorderlessWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask
|
||||
} else if pl_attrs.titlebar_hidden {
|
||||
// if the titlebar is hidden, ignore other pl_attrs
|
||||
NSWindowStyleMask::NSBorderlessWindowMask |
|
||||
@@ -735,8 +782,12 @@ impl Window2 {
|
||||
NSWindowStyleMask::NSTitledWindowMask
|
||||
};
|
||||
|
||||
if !attrs.resizable {
|
||||
masks &= !NSWindowStyleMask::NSResizableWindowMask;
|
||||
}
|
||||
|
||||
if pl_attrs.fullsize_content_view {
|
||||
masks = masks | NSWindowStyleMask::NSFullSizeContentViewWindowMask;
|
||||
masks |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
|
||||
}
|
||||
|
||||
let winit_window = Window2::class();
|
||||
@@ -775,9 +826,16 @@ impl Window2 {
|
||||
window.setMovableByWindowBackground_(YES);
|
||||
}
|
||||
|
||||
if !attrs.decorations {
|
||||
window.setTitleVisibility_(appkit::NSWindowTitleVisibility::NSWindowTitleHidden);
|
||||
window.setTitlebarAppearsTransparent_(YES);
|
||||
if attrs.always_on_top {
|
||||
let _: () = msg_send![*window, setLevel:ffi::NSWindowLevel::NSFloatingWindowLevel];
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
window.center();
|
||||
@@ -788,12 +846,13 @@ impl Window2 {
|
||||
}
|
||||
}
|
||||
|
||||
fn create_view(window: id) -> Option<IdRef> {
|
||||
fn create_view(window: id, shared: Weak<Shared>) -> Option<IdRef> {
|
||||
unsafe {
|
||||
let view = IdRef::new(NSView::init(NSView::alloc(nil)));
|
||||
let view = new_view(window, shared);
|
||||
view.non_nil().map(|view| {
|
||||
view.setWantsBestResolutionOpenGLSurface_(YES);
|
||||
window.setContentView_(*view);
|
||||
window.makeFirstResponder_(*view);
|
||||
view
|
||||
})
|
||||
}
|
||||
@@ -816,22 +875,15 @@ impl Window2 {
|
||||
unsafe { NSWindow::orderOut_(*self.window, nil); }
|
||||
}
|
||||
|
||||
// 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
|
||||
fn bottom_left_to_top_left(rect: NSRect) -> i32 {
|
||||
(CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height)) as _
|
||||
}
|
||||
|
||||
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,
|
||||
Self::bottom_left_to_top_left(frame_rect),
|
||||
))
|
||||
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,
|
||||
@@ -839,18 +891,18 @@ impl Window2 {
|
||||
)
|
||||
};
|
||||
Some((
|
||||
content_rect.origin.x as i32,
|
||||
Self::bottom_left_to_top_left(content_rect),
|
||||
))
|
||||
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),
|
||||
);
|
||||
@@ -860,53 +912,51 @@ 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
|
||||
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<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, size: LogicalSize) {
|
||||
unsafe {
|
||||
let view_frame = NSView::frame(*self.view);
|
||||
Some(((view_frame.size.width*factor) as u32, (view_frame.size.height*factor) as u32))
|
||||
NSWindow::setContentSize_(*self.window, NSSize::new(size.width as CGFloat, size.height as CGFloat));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||
unsafe {
|
||||
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<LogicalSize>) {
|
||||
unsafe {
|
||||
let dimensions = dimensions.unwrap_or_else(|| (!0, !0).into());
|
||||
nswindow_set_max_dimensions(self.window.0, dimensions);
|
||||
}
|
||||
}
|
||||
|
||||
#[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))
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
unsafe {
|
||||
NSWindow::setContentSize_(*self.window, NSSize::new((width as f64)/factor, (height as f64)/factor));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
unsafe {
|
||||
let (width, height) = dimensions.unwrap_or((0, 0));
|
||||
nswindow_set_min_dimensions(self.window.0, width.into(), height.into());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
unsafe {
|
||||
let (width, height) = dimensions.unwrap_or((!0, !0));
|
||||
nswindow_set_max_dimensions(self.window.0, width.into(), height.into());
|
||||
}
|
||||
}
|
||||
|
||||
#[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_resizable(&self, resizable: bool) {
|
||||
let mut win_attribs = self.delegate.state.win_attribs.borrow_mut();
|
||||
win_attribs.resizable = resizable;
|
||||
if win_attribs.fullscreen.is_none() {
|
||||
let mut mask = unsafe { self.window.styleMask() };
|
||||
if resizable {
|
||||
mask |= NSWindowStyleMask::NSResizableWindowMask;
|
||||
} else {
|
||||
mask &= !NSWindowStyleMask::NSResizableWindowMask;
|
||||
}
|
||||
unsafe { util::set_style_mask(*self.window, *self.view, mask) };
|
||||
} // Otherwise, we don't change the mask until we exit fullscreen.
|
||||
}
|
||||
|
||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||
@@ -933,13 +983,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();
|
||||
@@ -947,46 +997,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(())
|
||||
}
|
||||
@@ -1027,12 +1078,9 @@ impl Window2 {
|
||||
// It will clean up at window_did_exit_fullscreen.
|
||||
if current.is_none() {
|
||||
let curr_mask = state.window.styleMask();
|
||||
|
||||
if !curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask) {
|
||||
state.window.setStyleMask_(
|
||||
NSWindowStyleMask::NSTitledWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask,
|
||||
);
|
||||
let required = NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask;
|
||||
if !curr_mask.contains(required) {
|
||||
util::set_style_mask(*self.window, *self.view, required);
|
||||
state.save_style_mask.set(Some(curr_mask));
|
||||
}
|
||||
}
|
||||
@@ -1059,33 +1107,54 @@ impl Window2 {
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let new_mask = if decorations {
|
||||
let mut new_mask = if decorations {
|
||||
NSWindowStyleMask::NSClosableWindowMask
|
||||
| NSWindowStyleMask::NSMiniaturizableWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask
|
||||
| NSWindowStyleMask::NSTitledWindowMask
|
||||
} else {
|
||||
NSWindowStyleMask::NSBorderlessWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask
|
||||
};
|
||||
if !win_attribs.resizable {
|
||||
new_mask &= !NSWindowStyleMask::NSResizableWindowMask;
|
||||
}
|
||||
util::set_style_mask(*state.window, *state.view, new_mask);
|
||||
}
|
||||
}
|
||||
|
||||
state.window.setStyleMask_(new_mask);
|
||||
#[inline]
|
||||
pub fn set_always_on_top(&self, always_on_top: bool) {
|
||||
unsafe {
|
||||
let level = if always_on_top {
|
||||
ffi::NSWindowLevel::NSFloatingWindowLevel
|
||||
} else {
|
||||
ffi::NSWindowLevel::NSNormalWindowLevel
|
||||
};
|
||||
let _: () = msg_send![*self.window, setLevel:level];
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
|
||||
// macOS doesn't have window icons. Though, there is `setRepresentedFilename`, but that's
|
||||
// semantically distinct and should only be used when the window is in some representing a
|
||||
// specific file/directory. For instance, Terminal.app uses this for the CWD. Anyway, that
|
||||
// should eventually be implemented as `WindowBuilderExt::with_represented_file` or
|
||||
// something, and doesn't have anything to do with this.
|
||||
// semantically distinct and should only be used when the window is in some way
|
||||
// representing a specific file/directory. For instance, Terminal.app uses this for the
|
||||
// CWD. Anyway, that should eventually be implemented as
|
||||
// `WindowBuilderExt::with_represented_file` or something, and doesn't have anything to do
|
||||
// with `set_window_icon`.
|
||||
// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/WinPanel/Tasks/SettingWindowTitle.html
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_spot(&self, logical_spot: LogicalPosition) {
|
||||
set_ime_spot(*self.view, *self.input_context, logical_spot.x, logical_spot.y);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
unsafe {
|
||||
self::get_current_monitor()
|
||||
self::get_current_monitor(*self.window)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1096,51 +1165,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)
|
||||
}
|
||||
}
|
||||
@@ -1169,8 +1237,7 @@ impl Drop for IdRef {
|
||||
fn drop(&mut self) {
|
||||
if self.0 != nil {
|
||||
unsafe {
|
||||
let autoreleasepool =
|
||||
NSAutoreleasePool::new(nil);
|
||||
let autoreleasepool = NSAutoreleasePool::new(nil);
|
||||
let _ : () = msg_send![self.0, release];
|
||||
let _ : () = msg_send![autoreleasepool, release];
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ pub use self::platform::*;
|
||||
#[cfg(target_os = "windows")]
|
||||
#[path="windows/mod.rs"]
|
||||
mod platform;
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
||||
#[path="linux/mod.rs"]
|
||||
mod platform;
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -21,5 +21,6 @@ mod platform;
|
||||
|
||||
#[cfg(all(not(target_os = "ios"), not(target_os = "windows"), not(target_os = "linux"),
|
||||
not(target_os = "macos"), not(target_os = "android"), not(target_os = "dragonfly"),
|
||||
not(target_os = "freebsd"), not(target_os = "openbsd"), not(target_os = "emscripten")))]
|
||||
not(target_os = "freebsd"), not(target_os = "netbsd"), not(target_os = "openbsd"),
|
||||
not(target_os = "emscripten")))]
|
||||
compile_error!("The platform you're compiling for is not supported by winit");
|
||||
|
||||
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) })
|
||||
}
|
||||
@@ -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),
|
||||
@@ -205,31 +205,49 @@ pub fn vkey_to_winit_vkey(vkey: c_int) -> Option<VirtualKeyCode> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vkey_left_right(vkey: c_int, scancode: UINT, extended: bool) -> c_int {
|
||||
match vkey {
|
||||
winuser::VK_SHIFT => unsafe { winuser::MapVirtualKeyA(
|
||||
scancode,
|
||||
winuser::MAPVK_VSC_TO_VK_EX,
|
||||
) as _ },
|
||||
winuser::VK_CONTROL => if extended {
|
||||
winuser::VK_RCONTROL
|
||||
} else {
|
||||
winuser::VK_LCONTROL
|
||||
},
|
||||
winuser::VK_MENU => if extended {
|
||||
winuser::VK_RMENU
|
||||
} else {
|
||||
winuser::VK_LMENU
|
||||
},
|
||||
_ => vkey,
|
||||
}
|
||||
pub fn handle_extended_keys(vkey: c_int, mut scancode: UINT, extended: bool) -> Option<(c_int, UINT)> {
|
||||
// Welcome to hell https://blog.molecular-matters.com/2011/09/05/properly-handling-keyboard-input/
|
||||
let vkey = match vkey {
|
||||
winuser::VK_SHIFT => unsafe { winuser::MapVirtualKeyA(
|
||||
scancode,
|
||||
winuser::MAPVK_VSC_TO_VK_EX,
|
||||
) as _ },
|
||||
winuser::VK_CONTROL => if extended {
|
||||
winuser::VK_RCONTROL
|
||||
} else {
|
||||
winuser::VK_LCONTROL
|
||||
},
|
||||
winuser::VK_MENU => if extended {
|
||||
winuser::VK_RMENU
|
||||
} else {
|
||||
winuser::VK_LMENU
|
||||
},
|
||||
_ => match scancode {
|
||||
// This is only triggered when using raw input. Without this check, we get two events whenever VK_PAUSE is
|
||||
// pressed, the first one having scancode 0x1D but vkey VK_PAUSE...
|
||||
0x1D if vkey == winuser::VK_PAUSE => return None,
|
||||
// ...and the second having scancode 0x45 but an unmatched vkey!
|
||||
0x45 => winuser::VK_PAUSE,
|
||||
// VK_PAUSE and VK_SCROLL have the same scancode when using modifiers, alongside incorrect vkey values.
|
||||
0x46 => {
|
||||
if extended {
|
||||
scancode = 0x45;
|
||||
winuser::VK_PAUSE
|
||||
} else {
|
||||
winuser::VK_SCROLL
|
||||
}
|
||||
},
|
||||
_ => vkey,
|
||||
},
|
||||
};
|
||||
Some((vkey, scancode))
|
||||
}
|
||||
|
||||
pub fn vkeycode_to_element(wparam: WPARAM, lparam: LPARAM) -> (ScanCode, Option<VirtualKeyCode>) {
|
||||
pub fn process_key_params(wparam: WPARAM, lparam: LPARAM) -> Option<(ScanCode, Option<VirtualKeyCode>)> {
|
||||
let scancode = ((lparam >> 16) & 0xff) as UINT;
|
||||
let extended = (lparam & 0x01000000) != 0;
|
||||
let vkey = vkey_left_right(wparam as _, scancode, extended);
|
||||
(scancode, vkey_to_winit_vkey(vkey))
|
||||
handle_extended_keys(wparam as _, scancode, extended)
|
||||
.map(|(vkey, scancode)| (scancode, vkey_to_winit_vkey(vkey)))
|
||||
}
|
||||
|
||||
// This is needed as windows doesn't properly distinguish
|
||||
|
||||
@@ -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, Condvar, 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,
|
||||
MAX_PATH,
|
||||
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::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::event::{vkey_to_winit_vkey, vkey_left_right};
|
||||
use platform::platform::raw_input::*;
|
||||
use platform::platform::dpi::{
|
||||
become_dpi_aware,
|
||||
dpi_to_scale_factor,
|
||||
enable_non_client_dpi_scaling,
|
||||
get_hwnd_scale_factor,
|
||||
};
|
||||
use platform::platform::event::{handle_extended_keys, process_key_params, vkey_to_winit_vkey};
|
||||
use platform::platform::icon::WinIcon;
|
||||
use platform::platform::raw_input::{get_raw_input_data, get_raw_mouse_button_state};
|
||||
use platform::platform::window::adjust_size;
|
||||
|
||||
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.
|
||||
@@ -98,11 +141,17 @@ pub struct EventsLoop {
|
||||
// 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)>
|
||||
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()));
|
||||
@@ -171,7 +220,7 @@ impl EventsLoop {
|
||||
EventsLoop {
|
||||
thread_id,
|
||||
receiver: rx,
|
||||
win32_block_loop
|
||||
win32_block_loop,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,26 +334,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 +366,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)
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -385,11 +445,18 @@ 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_NCCREATE => {
|
||||
enable_non_client_dpi_scaling(window);
|
||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
},
|
||||
|
||||
winuser::WM_CLOSE => {
|
||||
use events::WindowEvent::CloseRequested;
|
||||
send_event(Event::WindowEvent {
|
||||
@@ -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,9 +520,11 @@ 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
|
||||
@@ -528,10 +602,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
|
||||
@@ -586,26 +662,27 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
if msg == winuser::WM_SYSKEYDOWN && wparam as i32 == winuser::VK_F4 {
|
||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
} else {
|
||||
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: Pressed,
|
||||
scancode: scancode,
|
||||
virtual_keycode: vkey,
|
||||
modifiers: event::get_key_mods(),
|
||||
}
|
||||
}
|
||||
});
|
||||
// Windows doesn't emit a delete character by default, but in order to make it
|
||||
// consistent with the other platforms we'll emit a delete character here.
|
||||
if vkey == Some(VirtualKeyCode::Delete) {
|
||||
if let Some((scancode, vkey)) = process_key_params(wparam, lparam) {
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: WindowEvent::ReceivedCharacter('\u{7F}'),
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: Pressed,
|
||||
scancode: scancode,
|
||||
virtual_keycode: vkey,
|
||||
modifiers: event::get_key_mods(),
|
||||
}
|
||||
}
|
||||
});
|
||||
// Windows doesn't emit a delete character by default, but in order to make it
|
||||
// consistent with the other platforms we'll emit a delete character here.
|
||||
if vkey == Some(VirtualKeyCode::Delete) {
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: WindowEvent::ReceivedCharacter('\u{7F}'),
|
||||
});
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
@@ -613,19 +690,20 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
|
||||
winuser::WM_KEYUP | winuser::WM_SYSKEYUP => {
|
||||
use events::ElementState::Released;
|
||||
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: Released,
|
||||
scancode: scancode,
|
||||
virtual_keycode: vkey,
|
||||
modifiers: event::get_key_mods(),
|
||||
},
|
||||
}
|
||||
});
|
||||
if let Some((scancode, vkey)) = process_key_params(wparam, lparam) {
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: Released,
|
||||
scancode: scancode,
|
||||
virtual_keycode: vkey,
|
||||
modifiers: event::get_key_mods(),
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
0
|
||||
},
|
||||
|
||||
@@ -836,23 +914,25 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
};
|
||||
|
||||
let scancode = keyboard.MakeCode as _;
|
||||
let extended = util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _);
|
||||
let vkey = vkey_left_right(
|
||||
let extended = util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _)
|
||||
| util::has_flag(keyboard.Flags, winuser::RI_KEY_E1 as _);
|
||||
if let Some((vkey, scancode)) = handle_extended_keys(
|
||||
keyboard.VKey as _,
|
||||
scancode,
|
||||
extended,
|
||||
);
|
||||
let virtual_keycode = vkey_to_winit_vkey(vkey);
|
||||
) {
|
||||
let virtual_keycode = vkey_to_winit_vkey(vkey);
|
||||
|
||||
send_event(Event::DeviceEvent {
|
||||
device_id,
|
||||
event: Key(KeyboardInput {
|
||||
scancode,
|
||||
state,
|
||||
virtual_keycode,
|
||||
modifiers: event::get_key_mods(),
|
||||
}),
|
||||
});
|
||||
send_event(Event::DeviceEvent {
|
||||
device_id,
|
||||
event: Key(KeyboardInput {
|
||||
scancode,
|
||||
state,
|
||||
virtual_keycode,
|
||||
modifiers: event::get_key_mods(),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -865,10 +945,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 {
|
||||
@@ -882,8 +969,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,
|
||||
})
|
||||
@@ -903,11 +989,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
|
||||
},
|
||||
|
||||
@@ -922,30 +1011,24 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
|
||||
winuser::WM_SETCURSOR => {
|
||||
let call_def_window_proc = CONTEXT_STASH.with(|context_stash| {
|
||||
let cstash = context_stash.borrow();
|
||||
let mut call_def_window_proc = false;
|
||||
if let Some(cstash) = cstash.as_ref() {
|
||||
if let Some(w_stash) = cstash.windows.get(&window) {
|
||||
if let Ok(window_state) = w_stash.lock() {
|
||||
if window_state.mouse_in_window {
|
||||
match window_state.cursor_state {
|
||||
CursorState::Normal => {
|
||||
winuser::SetCursor(winuser::LoadCursorW(
|
||||
ptr::null_mut(),
|
||||
window_state.cursor));
|
||||
},
|
||||
CursorState::Grab | CursorState::Hide => {
|
||||
winuser::SetCursor(ptr::null_mut());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
call_def_window_proc = true;
|
||||
}
|
||||
context_stash
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.and_then(|cstash| cstash.windows.get(&window))
|
||||
.map(|window_state_mutex| {
|
||||
let window_state = window_state_mutex.lock().unwrap();
|
||||
if window_state.mouse_in_window {
|
||||
let cursor = winuser::LoadCursorW(
|
||||
ptr::null_mut(),
|
||||
window_state.cursor.0,
|
||||
);
|
||||
winuser::SetCursor(cursor);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
call_def_window_proc
|
||||
})
|
||||
.unwrap_or(true)
|
||||
});
|
||||
|
||||
if call_def_window_proc {
|
||||
@@ -987,18 +1070,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 };
|
||||
}
|
||||
}
|
||||
@@ -1009,10 +1089,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,17 @@ 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 {}
|
||||
|
||||
// TODO: document what this means
|
||||
pub type Cursor = *const winapi::ctypes::wchar_t;
|
||||
// Cursor name in UTF-16. Used to set cursor in `WM_SETCURSOR`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cursor(pub *const winapi::ctypes::wchar_t);
|
||||
unsafe impl Send for Cursor {}
|
||||
unsafe impl Sync for Cursor {}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId(u32);
|
||||
@@ -44,6 +48,7 @@ pub struct WindowId(HWND);
|
||||
unsafe impl Send for WindowId {}
|
||||
unsafe impl Sync for WindowId {}
|
||||
|
||||
mod dpi;
|
||||
mod event;
|
||||
mod events_loop;
|
||||
mod icon;
|
||||
|
||||
@@ -1,172 +1,173 @@
|
||||
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(Clone)]
|
||||
#[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.
|
||||
// For more info see:
|
||||
// https://github.com/retep998/winapi-rs/issues/360
|
||||
// https://github.com/retep998/winapi-rs/issues/396
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
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
|
||||
}
|
||||
|
||||
/// This is a Win32-only function for `MonitorId` that returns the system name of the adapter
|
||||
/// device.
|
||||
#[inline]
|
||||
pub fn get_adapter_name(&self) -> &[wchar_t] {
|
||||
&self.adapter_name
|
||||
}
|
||||
|
||||
/// 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,8 +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::minwindef::{BOOL, DWORD};
|
||||
use winapi::shared::windef::{HWND, POINT, RECT};
|
||||
use winapi::um::errhandlingapi::GetLastError;
|
||||
use winapi::um::winbase::{
|
||||
FormatMessageW,
|
||||
@@ -18,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:
|
||||
@@ -27,9 +29,39 @@ 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.
|
||||
pub fn rect_eq(a: &RECT, b: &RECT) -> bool {
|
||||
let left_eq = a.left == b.left;
|
||||
let right_eq = a.right == b.right;
|
||||
let top_eq = a.top == b.top;
|
||||
let bottom_eq = a.bottom == b.bottom;
|
||||
left_eq && right_eq && top_eq && bottom_eq
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
266
src/window.rs
266
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,39 @@ 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
|
||||
}
|
||||
|
||||
/// Sets whether the window is resizable or not
|
||||
///
|
||||
/// Note that making the window unresizable doesn't exempt you from handling `Resized`, as that event can still be
|
||||
/// triggered by DPI scaling, entering fullscreen mode, etc.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// This only has an effect on desktop platforms.
|
||||
///
|
||||
/// Due to a bug in XFCE, this has no effect on Xfwm.
|
||||
#[inline]
|
||||
pub fn with_resizable(mut self, resizable: bool) -> WindowBuilder {
|
||||
self.window.resizable = resizable;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -92,6 +105,13 @@ impl WindowBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether or not the window will always be on top of other windows.
|
||||
#[inline]
|
||||
pub fn with_always_on_top(mut self, always_on_top: bool) -> WindowBuilder {
|
||||
self.window.always_on_top = always_on_top;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the window icon. On Windows and X11, this is typically the small icon in the top-left
|
||||
/// corner of the titlebar.
|
||||
///
|
||||
@@ -121,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()
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -196,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()
|
||||
}
|
||||
|
||||
@@ -205,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()
|
||||
}
|
||||
|
||||
@@ -215,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()
|
||||
}
|
||||
|
||||
@@ -279,70 +269,92 @@ 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)
|
||||
}
|
||||
|
||||
/// 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]
|
||||
/// Sets whether the window is resizable or not.
|
||||
///
|
||||
/// Note that making the window unresizable doesn't exempt you from handling `Resized`, as that event can still be
|
||||
/// triggered by DPI scaling, entering fullscreen mode, etc.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// This only has an effect on desktop platforms.
|
||||
///
|
||||
/// Due to a bug in XFCE, this has no effect on Xfwm.
|
||||
#[inline]
|
||||
pub unsafe fn platform_display(&self) -> *mut libc::c_void {
|
||||
self.window.platform_display()
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
self.window.set_resizable(resizable)
|
||||
}
|
||||
|
||||
/// DEPRECATED. Gets the native platform specific window handle. 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_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.
|
||||
#[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
|
||||
@@ -363,6 +375,12 @@ impl Window {
|
||||
self.window.set_decorations(decorations)
|
||||
}
|
||||
|
||||
/// Change whether or not the window will always be on top of other windows.
|
||||
#[inline]
|
||||
pub fn set_always_on_top(&self, always_on_top: bool) {
|
||||
self.window.set_always_on_top(always_on_top)
|
||||
}
|
||||
|
||||
/// Sets the window icon. On Windows and X11, this is typically the small icon in the top-left
|
||||
/// corner of the titlebar.
|
||||
///
|
||||
@@ -376,11 +394,35 @@ impl Window {
|
||||
self.window.set_window_icon(window_icon)
|
||||
}
|
||||
|
||||
/// Sets location of IME candidate box in client area coordinates relative to the top left.
|
||||
#[inline]
|
||||
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())
|
||||
@@ -390,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>,
|
||||
}
|
||||
@@ -409,7 +452,7 @@ impl Iterator for AvailableMonitorsIter {
|
||||
}
|
||||
|
||||
/// Identifier for a monitor.
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MonitorId {
|
||||
pub(crate) inner: platform::MonitorId
|
||||
}
|
||||
@@ -423,22 +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
|
||||
///
|
||||
/// - **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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user