mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-27 07:03:15 -04:00
Compare commits
267 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4641433c6a | ||
|
|
eadd9a19b2 | ||
|
|
42f0671531 | ||
|
|
eae7cb247c | ||
|
|
0474dc9861 | ||
|
|
8fd49a4dbe | ||
|
|
10688915eb | ||
|
|
19cd53193b | ||
|
|
2477d8ce46 | ||
|
|
8b0b4ab248 | ||
|
|
24333b806a | ||
|
|
c327960f3e | ||
|
|
bdc01fee1a | ||
|
|
457c0b7208 | ||
|
|
09c809003b | ||
|
|
f08bf44670 | ||
|
|
e36fd1788d | ||
|
|
4005bf11e4 | ||
|
|
9d036a6faa | ||
|
|
591e0d9b8e | ||
|
|
580321b56f | ||
|
|
1c4973d5b7 | ||
|
|
7cd440135a | ||
|
|
f3ab8af813 | ||
|
|
be6d2ed3b9 | ||
|
|
0b922ad9c0 | ||
|
|
b40b14f37f | ||
|
|
7a1946589c | ||
|
|
bbcd3019e8 | ||
|
|
d667a395b6 | ||
|
|
76118af341 | ||
|
|
ce7a426bb5 | ||
|
|
5dcde83b4c | ||
|
|
559681b0ed | ||
|
|
51181b4347 | ||
|
|
e6fefd5e93 | ||
|
|
f3d43016ad | ||
|
|
4c62d71950 | ||
|
|
f279b2f229 | ||
|
|
c62296dc2b | ||
|
|
7daf27f389 | ||
|
|
4abcc164cd | ||
|
|
4cce65274f | ||
|
|
ff17eff00f | ||
|
|
9b5254adeb | ||
|
|
b49abbbf17 | ||
|
|
107a1e7332 | ||
|
|
150d2706f9 | ||
|
|
7d38ed2fab | ||
|
|
1609808e27 | ||
|
|
7e1c70964d | ||
|
|
0e81251f3a | ||
|
|
0ea4f93f05 | ||
|
|
dddd9de151 | ||
|
|
124b16fcde | ||
|
|
198d9ff230 | ||
|
|
23881459bc | ||
|
|
a8d5a9e1ab | ||
|
|
f89dc9e903 | ||
|
|
011720848a | ||
|
|
d92666c188 | ||
|
|
463f316cb8 | ||
|
|
b36a8e010f | ||
|
|
23e4293179 | ||
|
|
8f18dab061 | ||
|
|
9698d0a8d8 | ||
|
|
d18db208ff | ||
|
|
d2dd82c146 | ||
|
|
8348e8225b | ||
|
|
663d615379 | ||
|
|
0f14e63b34 | ||
|
|
fae10c6072 | ||
|
|
cfd087d9a5 | ||
|
|
c61f9b75f8 | ||
|
|
bbf13561b5 | ||
|
|
61d25be3e0 | ||
|
|
37a10e6741 | ||
|
|
62e45fa75d | ||
|
|
01d1178d7b | ||
|
|
b3c5ee6219 | ||
|
|
36058ab8e6 | ||
|
|
159364bec3 | ||
|
|
58181dbff9 | ||
|
|
760e588627 | ||
|
|
d10312c6b1 | ||
|
|
4e4db1749d | ||
|
|
229029f2da | ||
|
|
48902297b7 | ||
|
|
eff3440482 | ||
|
|
80d9a6371d | ||
|
|
3d8c94bae5 | ||
|
|
3f33cd1929 | ||
|
|
1db92063d9 | ||
|
|
5af88d97e8 | ||
|
|
b337d8f99b | ||
|
|
515595153d | ||
|
|
df7e349c70 | ||
|
|
9c116a1bae | ||
|
|
15fbc0dff4 | ||
|
|
6a2a7036d4 | ||
|
|
3edc8e0cd9 | ||
|
|
52a78d61bf | ||
|
|
f81a0741f5 | ||
|
|
192bd798e3 | ||
|
|
52a7b07c79 | ||
|
|
1d0b5bcfbd | ||
|
|
769d4fe897 | ||
|
|
ae7802c8c2 | ||
|
|
c82fcd203f | ||
|
|
59c33d2c6a | ||
|
|
342d5d8587 | ||
|
|
0ada6c15ea | ||
|
|
2a4b381384 | ||
|
|
fe1deb83ae | ||
|
|
5b57b73fe8 | ||
|
|
3d1c18ded9 | ||
|
|
e65cacbc86 | ||
|
|
1b22e39fb2 | ||
|
|
7dc6fcdedc | ||
|
|
f7a8bcddb8 | ||
|
|
60b575a7c1 | ||
|
|
b3ef9c8b22 | ||
|
|
9693f7caa9 | ||
|
|
1382adbf11 | ||
|
|
b35c4a5ee5 | ||
|
|
eff04394c9 | ||
|
|
1d97a2a506 | ||
|
|
a4052b8693 | ||
|
|
17de3f1d15 | ||
|
|
be9d4e7e03 | ||
|
|
3b8de1af34 | ||
|
|
f81d6ddf93 | ||
|
|
200ef9c307 | ||
|
|
63fdc3f903 | ||
|
|
8e3e60b1c3 | ||
|
|
24f5b0b591 | ||
|
|
4202c35786 | ||
|
|
786666aca8 | ||
|
|
657860a233 | ||
|
|
d2034b1700 | ||
|
|
13bd116891 | ||
|
|
a582df443b | ||
|
|
718e0f8551 | ||
|
|
d868510cc8 | ||
|
|
8f03fb7a34 | ||
|
|
a14bae8742 | ||
|
|
c508d68d1d | ||
|
|
15c4641758 | ||
|
|
6820e2a826 | ||
|
|
ed761bef7d | ||
|
|
491bc891e8 | ||
|
|
c57ec33c00 | ||
|
|
28cdce99c0 | ||
|
|
45c5568c89 | ||
|
|
d65d9950f2 | ||
|
|
06e01e4cb3 | ||
|
|
5f00028f6b | ||
|
|
4ef7c71c66 | ||
|
|
3b7dbd01d0 | ||
|
|
8f04d6d4d0 | ||
|
|
3ccee6c2f1 | ||
|
|
772acc742e | ||
|
|
a15b818827 | ||
|
|
32eea41b1b | ||
|
|
cc89d56e43 | ||
|
|
c86cf416d5 | ||
|
|
8d5b23d56c | ||
|
|
28eddb64a9 | ||
|
|
2066909845 | ||
|
|
8add21b04f | ||
|
|
00197b3d04 | ||
|
|
58b800c344 | ||
|
|
506e830cb0 | ||
|
|
2171918023 | ||
|
|
b09e3b2568 | ||
|
|
2e079fe9a2 | ||
|
|
e1e21ded28 | ||
|
|
a1d2ee6ecf | ||
|
|
7a1a2667b1 | ||
|
|
9cd370fa4c | ||
|
|
d6b9faacc9 | ||
|
|
9462a51f32 | ||
|
|
e1fba7d92f | ||
|
|
8e13f85fac | ||
|
|
f2ee78bcf5 | ||
|
|
aea61a74fb | ||
|
|
117beed0b5 | ||
|
|
aad82eb987 | ||
|
|
5ebeb8ab5f | ||
|
|
90a81cca29 | ||
|
|
601599eb0a | ||
|
|
0371b6573f | ||
|
|
ac0d6c890a | ||
|
|
75856e0e39 | ||
|
|
3d9e8da9ec | ||
|
|
e196f80e98 | ||
|
|
ee0dbb8546 | ||
|
|
886eab5c7e | ||
|
|
51036e6ba5 | ||
|
|
61ba6dce7f | ||
|
|
05cd9f2114 | ||
|
|
09d086749d | ||
|
|
9d2a32b415 | ||
|
|
db0f012574 | ||
|
|
3c710de65d | ||
|
|
9a17c2eb86 | ||
|
|
5e5debc48f | ||
|
|
e915454a9d | ||
|
|
66a12d5332 | ||
|
|
3a89843767 | ||
|
|
eaa92a4282 | ||
|
|
789598fa84 | ||
|
|
7a19ef1907 | ||
|
|
9804cad7dd | ||
|
|
a08347eef0 | ||
|
|
fe61d81d41 | ||
|
|
04ccad1dbc | ||
|
|
df1276d72a | ||
|
|
24d6f8da49 | ||
|
|
cd71271f0d | ||
|
|
4b42af910b | ||
|
|
0af3c04900 | ||
|
|
0237526999 | ||
|
|
c5b9bd3612 | ||
|
|
db9e80bdb6 | ||
|
|
f2dd2f0752 | ||
|
|
38856b1c60 | ||
|
|
2b55b2e0ef | ||
|
|
8f0ef514b1 | ||
|
|
647a1727d0 | ||
|
|
339318f295 | ||
|
|
9ca2f83784 | ||
|
|
f6587aed39 | ||
|
|
c8e791b402 | ||
|
|
06a5ec35b3 | ||
|
|
f9f1000d8c | ||
|
|
fdb1c56366 | ||
|
|
1523548d3e | ||
|
|
7298df74bc | ||
|
|
148c751f32 | ||
|
|
96a90d0ec6 | ||
|
|
a17f7328c9 | ||
|
|
b8c6afb96a | ||
|
|
622b3b248e | ||
|
|
4a50fa6c44 | ||
|
|
c2c27c1b37 | ||
|
|
c33bedce5e | ||
|
|
ce8682be2e | ||
|
|
55b5054d50 | ||
|
|
4ec5078bdb | ||
|
|
ad8d2b0470 | ||
|
|
f034017ab2 | ||
|
|
5645941a14 | ||
|
|
4f2515f91e | ||
|
|
46057d7122 | ||
|
|
6c5bc52a5f | ||
|
|
f3db0ba641 | ||
|
|
8288d419fd | ||
|
|
c92ac695af | ||
|
|
8f9f2352cf | ||
|
|
0b75a6b60c | ||
|
|
15aafc2908 | ||
|
|
689d0521f5 | ||
|
|
4087627b12 | ||
|
|
6c99546035 | ||
|
|
c4cdb9aa5c | ||
|
|
22bc119cd7 |
56
.circleci/config.yml
Normal file
56
.circleci/config.yml
Normal file
@@ -0,0 +1,56 @@
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
|
||||
android-test:
|
||||
working_directory: ~/winit
|
||||
docker:
|
||||
- image: tomaka/cargo-apk
|
||||
steps:
|
||||
- run: apt-get -qq update && apt-get install -y git
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: android-test-cache-{{ checksum "Cargo.toml" }}
|
||||
- run: cargo apk build --example window
|
||||
- save_cache:
|
||||
key: android-test-cache-{{ checksum "Cargo.toml" }}
|
||||
paths:
|
||||
- target
|
||||
|
||||
asmjs-test:
|
||||
working_directory: ~/winit
|
||||
docker:
|
||||
- image: tomaka/rustc-emscripten
|
||||
steps:
|
||||
- run: apt-get -qq update && apt-get install -y git
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: asmjs-test-cache-{{ checksum "Cargo.toml" }}
|
||||
- run: cargo build --example window --target asmjs-unknown-emscripten
|
||||
- save_cache:
|
||||
key: asmjs-test-cache-{{ checksum "Cargo.toml" }}
|
||||
paths:
|
||||
- target
|
||||
|
||||
wasm-test:
|
||||
working_directory: ~/winit
|
||||
docker:
|
||||
- image: tomaka/rustc-emscripten
|
||||
steps:
|
||||
- run: apt-get -qq update && apt-get install -y git
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: wasm-test-cache-{{ checksum "Cargo.toml" }}
|
||||
- run: cargo build --example window --target wasm32-unknown-emscripten
|
||||
- save_cache:
|
||||
key: wasm-test-cache-{{ checksum "Cargo.toml" }}
|
||||
paths:
|
||||
- target
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build-test-and-deploy:
|
||||
jobs:
|
||||
- android-test
|
||||
- asmjs-test
|
||||
- wasm-test
|
||||
65
.travis.yml
65
.travis.yml
@@ -1,30 +1,57 @@
|
||||
language: rust
|
||||
|
||||
rust:
|
||||
- nightly
|
||||
- stable
|
||||
|
||||
cache: cargo
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libxxf86vm-dev
|
||||
matrix:
|
||||
include:
|
||||
# Linux 32bit
|
||||
- env: TARGET=i686-unknown-linux-gnu
|
||||
os: linux
|
||||
rust: nightly
|
||||
addons:
|
||||
apt:
|
||||
# Cross compiler and cross compiled C libraries
|
||||
packages: &i686_packages
|
||||
- gcc-multilib
|
||||
- env: TARGET=i686-unknown-linux-gnu
|
||||
os: linux
|
||||
rust: stable
|
||||
addons:
|
||||
apt:
|
||||
packages: *i686_packages
|
||||
|
||||
# Linux 64bit
|
||||
- env: TARGET=x86_64-unknown-linux-gnu
|
||||
os: linux
|
||||
rust: nightly
|
||||
- env: TARGET=x86_64-unknown-linux-gnu
|
||||
os: linux
|
||||
rust: stable
|
||||
|
||||
# macOS
|
||||
- env: TARGET=x86_64-apple-darwin
|
||||
os: osx
|
||||
rust: nightly
|
||||
- env: TARGET=x86_64-apple-darwin
|
||||
os: osx
|
||||
rust: stable
|
||||
|
||||
# iOS
|
||||
- env: TARGET=x86_64-apple-ios
|
||||
os: osx
|
||||
rust: nightly
|
||||
- env: TARGET=x86_64-apple-ios
|
||||
os: osx
|
||||
rust: stable
|
||||
|
||||
install:
|
||||
- |
|
||||
if [ $TRAVIS_OS_NAME = osx ]; then
|
||||
rustup target add x86_64-apple-ios
|
||||
fi
|
||||
- rustup self update
|
||||
- rustup target add $TARGET; true
|
||||
|
||||
script:
|
||||
- cargo build --verbose
|
||||
- if [ $TRAVIS_OS_NAME = osx ]; then cargo build --target x86_64-apple-ios --verbose; fi
|
||||
- cargo test --verbose
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
- cargo build --target $TARGET --verbose
|
||||
# Running iOS apps on OSX requires the simulator so we skip that for now
|
||||
- if [ "$TARGET" != "x86_64-apple-ios" ]; then cargo test --target $TARGET --verbose; fi
|
||||
|
||||
after_success:
|
||||
- |
|
||||
|
||||
126
CHANGELOG.md
Normal file
126
CHANGELOG.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Unreleased
|
||||
|
||||
# Version 0.13.0 (2018-04-25)
|
||||
|
||||
- Implement `WindowBuilder::with_maximized`, `Window::set_fullscreen`, `Window::set_maximized` and `Window::set_decorations` for MacOS.
|
||||
- Implement `WindowBuilder::with_maximized`, `Window::set_fullscreen`, `Window::set_maximized` and `Window::set_decorations` for Windows.
|
||||
- On Windows, `WindowBuilder::with_fullscreen` no longer changing monitor display resolution.
|
||||
- Overhauled X11 window geometry calculations. `get_position` and `set_position` are more universally accurate across different window managers, and `get_outer_size` actually works now.
|
||||
- Fixed SIGSEGV/SIGILL crashes on macOS caused by stabilization of the `!` (never) type.
|
||||
- Implement `WindowEvent::HiDPIFactorChanged` for macOS
|
||||
- On X11, input methods now work completely out of the box, no longer requiring application developers to manually call `setlocale`. Additionally, when input methods are started, stopped, or restarted on the server end, it's correctly handled.
|
||||
- Implemented `Refresh` event on Windows.
|
||||
- Properly calculate the minimum and maximum window size on Windows, including window decorations.
|
||||
- Map more `MouseCursor` variants to cursor icons on Windows.
|
||||
- Corrected `get_position` on macOS to return outer frame position, not content area position.
|
||||
- Corrected `set_position` on macOS to set outer frame position, not content area position.
|
||||
- Added `get_inner_position` method to `Window`, which gets the position of the window's client area. This is implemented on all applicable platforms (all desktop platforms other than Wayland, where this isn't possible).
|
||||
- **Breaking:** the `Closed` event has been replaced by `CloseRequested` and `Destroyed`. To migrate, you typically just need to replace all usages of `Closed` with `CloseRequested`; see example programs for more info. The exception is iOS, where `Closed` must be replaced by `Destroyed`.
|
||||
|
||||
# Version 0.12.0 (2018-04-06)
|
||||
|
||||
- Added subclass to macos windows so they can be made resizable even with no decorations.
|
||||
- Dead keys now work properly on X11, no longer resulting in a panic.
|
||||
- On X11, input method creation first tries to use the value from the user's `XMODIFIERS` environment variable, so application developers should no longer need to manually call `XSetLocaleModifiers`. If that fails, fallbacks are tried, which should prevent input method initialization from ever outright failing.
|
||||
- Fixed thread safety issues with input methods on X11.
|
||||
- Add support for `Touch` for win32 backend.
|
||||
- Fixed `Window::get_inner_size` and friends to return the size in pixels instead of points when using HIDPI displays on OSX.
|
||||
|
||||
# Version 0.11.3 (2018-03-28)
|
||||
|
||||
- Added `set_min_dimensions` and `set_max_dimensions` methods to `Window`, and implemented on Windows, X11, Wayland, and OSX.
|
||||
- On X11, dropping a `Window` actually closes it now, and clicking the window's × button (or otherwise having the WM signal to close it) will result in the window closing.
|
||||
- Added `WindowBuilderExt` methods for macos: `with_titlebar_transparent`,
|
||||
`with_title_hidden`, `with_titlebar_buttons_hidden`,
|
||||
`with_fullsize_content_view`.
|
||||
- Mapped X11 numpad keycodes (arrows, Home, End, PageUp, PageDown, Insert and Delete) to corresponding virtual keycodes
|
||||
|
||||
# Version 0.11.2 (2018-03-06)
|
||||
|
||||
- Impl `Hash`, `PartialEq`, and `Eq` for `events::ModifiersState`.
|
||||
- Implement `MonitorId::get_hidpi_factor` for MacOS.
|
||||
- Added method `os::macos::MonitorIdExt::get_nsscreen() -> *mut c_void` that gets a `NSScreen` object matching the monitor ID.
|
||||
- Send `Awakened` event on Android when event loop is woken up.
|
||||
|
||||
# Version 0.11.1 (2018-02-19)
|
||||
|
||||
- Fixed windows not receiving mouse events when click-dragging the mouse outside the client area of a window, on Windows platforms.
|
||||
- Added method `os::android::EventsLoopExt:set_suspend_callback(Option<Box<Fn(bool) -> ()>>)` that allows glutin to register a callback when a suspend event happens
|
||||
|
||||
# Version 0.11.0 (2018-02-09)
|
||||
|
||||
- Implement `MonitorId::get_dimensions` for Android.
|
||||
- Added method `os::macos::WindowBuilderExt::with_movable_by_window_background(bool)` that allows to move a window without a titlebar - `with_decorations(false)`
|
||||
- Implement `Window::set_fullscreen`, `Window::set_maximized` and `Window::set_decorations` for Wayland.
|
||||
- Added `Caret` as VirtualKeyCode and support OSX ^-Key with german input.
|
||||
|
||||
# Version 0.10.1 (2018-02-05)
|
||||
|
||||
*Yanked*
|
||||
|
||||
# Version 0.10.0 (2017-12-27)
|
||||
|
||||
- Add support for `Touch` for emscripten backend.
|
||||
- Added support for `DroppedFile`, `HoveredFile`, and `HoveredFileCancelled` to X11 backend.
|
||||
- **Breaking:** `unix::WindowExt` no longer returns pointers for things that aren't actually pointers; `get_xlib_window` now returns `Option<std::os::raw::c_ulong>` and `get_xlib_screen_id` returns `Option<std::os::raw::c_int>`. Additionally, methods that previously returned `libc::c_void` have been changed to return `std::os::raw::c_void`, which are not interchangeable types, so users wanting the former will need to explicitly cast.
|
||||
- Added `set_decorations` method to `Window` to allow decorations to be toggled after the window is built. Presently only implemented on X11.
|
||||
- Raised the minimum supported version of Rust to 1.20 on MacOS due to usage of associated constants in new versions of cocoa and core-graphics.
|
||||
- Added `modifiers` field to `MouseInput`, `MouseWheel`, and `CursorMoved` events to track the modifiers state (`ModifiersState`).
|
||||
- Fixed the emscripten backend to return the size of the canvas instead of the size of the window.
|
||||
|
||||
# Version 0.9.0 (2017-12-01)
|
||||
|
||||
- Added event `WindowEvent::HiDPIFactorChanged`.
|
||||
- Added method `MonitorId::get_hidpi_factor`.
|
||||
- Deprecated `get_inner_size_pixels` and `get_inner_size_points` methods of `Window` in favor of
|
||||
`get_inner_size`.
|
||||
- **Breaking:** `EventsLoop` is `!Send` and `!Sync` because of platform-dependant constraints,
|
||||
but `Window`, `WindowId`, `DeviceId` and `MonitorId` guaranteed to be `Send`.
|
||||
- `MonitorId::get_position` now returns `(i32, i32)` instead of `(u32, u32)`.
|
||||
- Rewrite of the wayland backend to use wayland-client-0.11
|
||||
- Support for dead keys on wayland for keyboard utf8 input
|
||||
- Monitor enumeration on Windows is now implemented using `EnumDisplayMonitors` instead of
|
||||
`EnumDisplayDevices`. This changes the value returned by `MonitorId::get_name()`.
|
||||
- On Windows added `MonitorIdExt::hmonitor` method
|
||||
- Impl `Clone` for `EventsLoopProxy`
|
||||
- `EventsLoop::get_primary_monitor()` on X11 will fallback to any available monitor if no primary is found
|
||||
- Support for touch event on wayland
|
||||
- `WindowEvent`s `MouseMoved`, `MouseEntered`, and `MouseLeft` have been renamed to
|
||||
`CursorMoved`, `CursorEntered`, and `CursorLeft`.
|
||||
- New `DeviceEvent`s added, `MouseMotion` and `MouseWheel`.
|
||||
- Send `CursorMoved` event after `CursorEntered` and `Focused` events.
|
||||
- Add support for `ModifiersState`, `MouseMove`, `MouseInput`, `MouseMotion` for emscripten backend.
|
||||
|
||||
# Version 0.8.3 (2017-10-11)
|
||||
|
||||
- Fixed issue of calls to `set_inner_size` blocking on Windows.
|
||||
- Mapped `ISO_Left_Tab` to `VirtualKeyCode::Tab` to make the key work with modifiers
|
||||
- Fixed the X11 backed on 32bit targets
|
||||
|
||||
# Version 0.8.2 (2017-09-28)
|
||||
|
||||
- Uniformize keyboard scancode values accross Wayland and X11 (#297).
|
||||
- Internal rework of the wayland event loop
|
||||
- Added method `os::linux::WindowExt::is_ready`
|
||||
|
||||
# Version 0.8.1 (2017-09-22)
|
||||
|
||||
- Added various methods to `os::linux::EventsLoopExt`, plus some hidden items necessary to make
|
||||
glutin work.
|
||||
|
||||
# Version 0.8.0 (2017-09-21)
|
||||
|
||||
- Added `Window::set_maximized`, `WindowAttributes::maximized` and `WindowBuilder::with_maximized`.
|
||||
- Added `Window::set_fullscreen`.
|
||||
- Changed `with_fullscreen` to take a `Option<MonitorId>` instead of a `MonitorId`.
|
||||
- Removed `MonitorId::get_native_identifer()` in favor of platform-specific traits in the `os`
|
||||
module.
|
||||
- Changed `get_available_monitors()` and `get_primary_monitor()` to be methods of `EventsLoop`
|
||||
instead of stand-alone methods.
|
||||
- Changed `EventsLoop` to be tied to a specific X11 or Wayland connection.
|
||||
- Added a `os::linux::EventsLoopExt` trait that makes it possible to configure the connection.
|
||||
- Fixed the emscripten code, which now compiles.
|
||||
- Changed the X11 fullscreen code to use `xrandr` instead of `xxf86vm`.
|
||||
- Fixed the Wayland backend to produce `Refresh` event after window creation.
|
||||
- Changed the `Suspended` event to be outside of `WindowEvent`.
|
||||
- Fixed the X11 backend sometimes reporting the wrong virtual key (#273).
|
||||
45
Cargo.toml
45
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.6.4"
|
||||
version = "0.13.0"
|
||||
authors = ["The winit contributors, Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
keywords = ["windowing"]
|
||||
@@ -11,9 +11,8 @@ documentation = "https://docs.rs/winit"
|
||||
categories = ["gui"]
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "0.2.2"
|
||||
lazy_static = "1"
|
||||
libc = "0.2"
|
||||
shared_library = "0.1.5"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies.android_glue]
|
||||
version = "0.2"
|
||||
@@ -23,21 +22,31 @@ objc = "0.2"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
objc = "0.2"
|
||||
cgl = "0.2"
|
||||
cocoa = "=0.5.2"
|
||||
core-foundation = "0.2"
|
||||
core-graphics = "0.4"
|
||||
cocoa = "0.14"
|
||||
core-foundation = "0.5"
|
||||
core-graphics = "0.13"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = "0.2"
|
||||
shell32-sys = "0.1"
|
||||
gdi32-sys = "0.1"
|
||||
user32-sys = "~0.1.2"
|
||||
kernel32-sys = "0.2"
|
||||
dwmapi-sys = "0.1"
|
||||
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
||||
version = "0.3"
|
||||
features = [
|
||||
"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.8.6", features = ["dlopen"] }
|
||||
wayland-kbd = "0.8.0"
|
||||
wayland-window = "0.5.0"
|
||||
x11-dl = "2.8"
|
||||
wayland-client = { version = "0.12.0", features = ["dlopen"] }
|
||||
wayland-protocols = { version = "0.12.0", features = ["unstable_protocols"] }
|
||||
wayland-kbd = "0.13.0"
|
||||
wayland-window = "0.13.0"
|
||||
x11-dl = "2.17"
|
||||
percent-encoding = "1.0"
|
||||
|
||||
30
README.md
30
README.md
@@ -9,7 +9,7 @@
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.5"
|
||||
winit = "0.13"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
@@ -17,7 +17,7 @@ winit = "0.5"
|
||||
## Usage
|
||||
|
||||
Winit is a window creation and management library. It can create windows and lets you handle
|
||||
events (for example: the window being resized, a key being pressed, a mouse mouvement, etc.)
|
||||
events (for example: the window being resized, a key being pressed, a mouse movement, etc.)
|
||||
produced by window.
|
||||
|
||||
Winit is designed to be a low-level brick in a hierarchy of libraries. Consequently, in order to
|
||||
@@ -28,13 +28,29 @@ another library.
|
||||
extern crate winit;
|
||||
|
||||
fn main() {
|
||||
let window = winit::Window::new().unwrap();
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
let window = winit::Window::new(&events_loop).unwrap();
|
||||
|
||||
for event in window.wait_events() {
|
||||
events_loop.run_forever(|event| {
|
||||
match event {
|
||||
winit::Event::Closed => break,
|
||||
_ => ()
|
||||
winit::Event::WindowEvent {
|
||||
event: winit::WindowEvent::CloseRequested,
|
||||
..
|
||||
} => winit::ControlFlow::Break,
|
||||
_ => winit::ControlFlow::Continue,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Platform-specific usage
|
||||
|
||||
#### Emscripten and WebAssembly
|
||||
|
||||
Building a binary will yield a `.js` file. In order to use it in an HTML file, you need to:
|
||||
|
||||
- Put a `<canvas id="my_id"></canvas>` element somewhere. A canvas corresponds to a winit "window".
|
||||
- Write a Javascript code that creates a global variable named `Module`. Set `Module.canvas` to
|
||||
the element of the `<canvas>` element (in the example you would retrieve it via `document.getElementById("my_id")`).
|
||||
More information [here](https://kripken.github.io/emscripten-site/docs/api_reference/module.html).
|
||||
- Make sure that you insert the `.js` file generated by Rust after the `Module` variable is created.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
extern crate winit;
|
||||
|
||||
use winit::{Event, ElementState, MouseCursor, WindowEvent};
|
||||
use winit::{Event, ElementState, MouseCursor, WindowEvent, KeyboardInput, ControlFlow};
|
||||
|
||||
fn main() {
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let window = winit::WindowBuilder::new().build(&events_loop).unwrap();
|
||||
window.set_title("A fantastic window!");
|
||||
@@ -13,7 +13,7 @@ fn main() {
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
match event {
|
||||
Event::WindowEvent { event: WindowEvent::KeyboardInput(ElementState::Pressed, _, _, _), .. } => {
|
||||
Event::WindowEvent { event: WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. }, .. } => {
|
||||
println!("Setting cursor to \"{:?}\"", cursors[cursor_idx]);
|
||||
window.set_cursor(cursors[cursor_idx]);
|
||||
if cursor_idx < cursors.len() - 1 {
|
||||
@@ -22,10 +22,11 @@ fn main() {
|
||||
cursor_idx = 0;
|
||||
}
|
||||
},
|
||||
Event::WindowEvent { event: WindowEvent::Closed, .. } => {
|
||||
events_loop.interrupt()
|
||||
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
|
||||
return ControlFlow::Break;
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
ControlFlow::Continue
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
extern crate winit;
|
||||
|
||||
use std::io::{self, Write};
|
||||
use winit::{ControlFlow, Event, WindowEvent};
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
// enumerating monitors
|
||||
let monitor = {
|
||||
for (num, monitor) in winit::get_available_monitors().enumerate() {
|
||||
for (num, monitor) in events_loop.get_available_monitors().enumerate() {
|
||||
println!("Monitor #{}: {:?}", num, monitor.get_name());
|
||||
}
|
||||
|
||||
@@ -15,32 +18,62 @@ fn main() {
|
||||
let mut num = String::new();
|
||||
io::stdin().read_line(&mut num).unwrap();
|
||||
let num = num.trim().parse().ok().expect("Please enter a number");
|
||||
let monitor = winit::get_available_monitors().nth(num).expect("Please enter a valid ID");
|
||||
let monitor = events_loop.get_available_monitors().nth(num).expect("Please enter a valid ID");
|
||||
|
||||
println!("Using {:?}", monitor.get_name());
|
||||
|
||||
monitor
|
||||
};
|
||||
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
|
||||
let _window = winit::WindowBuilder::new()
|
||||
let window = winit::WindowBuilder::new()
|
||||
.with_title("Hello world!")
|
||||
.with_fullscreen(monitor)
|
||||
.with_fullscreen(Some(monitor))
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut is_fullscreen = true;
|
||||
let mut is_maximized = false;
|
||||
let mut decorations = true;
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
println!("{:?}", event);
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent { event, .. } => {
|
||||
match event {
|
||||
winit::WindowEvent::Closed => events_loop.interrupt(),
|
||||
winit::WindowEvent::KeyboardInput(_, _, Some(winit::VirtualKeyCode::Escape), _) => events_loop.interrupt(),
|
||||
_ => ()
|
||||
}
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => return ControlFlow::Break,
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
winit::KeyboardInput {
|
||||
virtual_keycode: Some(virtual_code),
|
||||
state,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match (virtual_code, state) {
|
||||
(winit::VirtualKeyCode::Escape, _) => return ControlFlow::Break,
|
||||
(winit::VirtualKeyCode::F, winit::ElementState::Pressed) => {
|
||||
is_fullscreen = !is_fullscreen;
|
||||
if !is_fullscreen {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
window.set_fullscreen(Some(window.get_current_monitor()));
|
||||
}
|
||||
}
|
||||
(winit::VirtualKeyCode::M, winit::ElementState::Pressed) => {
|
||||
is_maximized = !is_maximized;
|
||||
window.set_maximized(is_maximized);
|
||||
}
|
||||
(winit::VirtualKeyCode::D, winit::ElementState::Pressed) => {
|
||||
decorations = !decorations;
|
||||
window.set_decorations(decorations);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
ControlFlow::Continue
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
extern crate winit;
|
||||
|
||||
use winit::{WindowEvent, ElementState};
|
||||
use winit::{ControlFlow, WindowEvent, ElementState, KeyboardInput};
|
||||
|
||||
fn main() {
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let window = winit::WindowBuilder::new().build(&events_loop).unwrap();
|
||||
window.set_title("winit - Cursor grabbing test");
|
||||
@@ -16,7 +16,7 @@ fn main() {
|
||||
match event {
|
||||
winit::Event::WindowEvent { event, .. } => {
|
||||
match event {
|
||||
WindowEvent::KeyboardInput(ElementState::Pressed, _, _, _) => {
|
||||
WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. } => {
|
||||
if grabbed {
|
||||
grabbed = false;
|
||||
window.set_cursor_state(winit::CursorState::Normal)
|
||||
@@ -28,15 +28,18 @@ fn main() {
|
||||
}
|
||||
},
|
||||
|
||||
WindowEvent::Closed => events_loop.interrupt(),
|
||||
WindowEvent::CloseRequested => return ControlFlow::Break,
|
||||
|
||||
a @ WindowEvent::MouseMoved(_, _) => {
|
||||
a @ WindowEvent::CursorMoved { .. } => {
|
||||
println!("{:?}", a);
|
||||
},
|
||||
|
||||
_ => (),
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
ControlFlow::Continue
|
||||
});
|
||||
}
|
||||
|
||||
74
examples/handling_close.rs
Normal file
74
examples/handling_close.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
extern crate winit;
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let _window = winit::WindowBuilder::new()
|
||||
.with_title("Your faithful window")
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut close_requested = false;
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
use winit::WindowEvent::*;
|
||||
use winit::ElementState::Released;
|
||||
use winit::VirtualKeyCode::{N, Y};
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent { event, .. } => match event {
|
||||
CloseRequested => {
|
||||
// `CloseRequested` is sent when the close button on the window is pressed (or
|
||||
// through whatever other mechanisms the window manager provides for closing a
|
||||
// window). If you don't handle this event, the close button won't actually do
|
||||
// anything.
|
||||
|
||||
// A common thing to do here is prompt the user if they have unsaved work.
|
||||
// Creating a proper dialog box for that is far beyond the scope of this
|
||||
// example, so here we'll just respond to the Y and N keys.
|
||||
println!("Are you ready to bid your window farewell? [Y/N]");
|
||||
close_requested = true;
|
||||
|
||||
// In applications where you can safely close the window without further
|
||||
// action from the user, this is generally where you'd handle cleanup before
|
||||
// closing the window. How to close the window is detailed in the handler for
|
||||
// the Y key.
|
||||
}
|
||||
KeyboardInput {
|
||||
input:
|
||||
winit::KeyboardInput {
|
||||
virtual_keycode: Some(virtual_code),
|
||||
state: Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match virtual_code {
|
||||
Y => {
|
||||
if close_requested {
|
||||
// This is where you'll want to do any cleanup you need.
|
||||
println!("Buh-bye!");
|
||||
|
||||
// For a single-window application like this, you'd normally just
|
||||
// break out of the event loop here. If you wanted to keep running the
|
||||
// event loop (i.e. if it's a multi-window application), you need to
|
||||
// drop the window. That closes it, and results in `Destroyed` being
|
||||
// sent.
|
||||
return winit::ControlFlow::Break;
|
||||
}
|
||||
}
|
||||
N => {
|
||||
if close_requested {
|
||||
println!("Your window will continue to stay by your side.");
|
||||
close_requested = false;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
winit::ControlFlow::Continue
|
||||
});
|
||||
}
|
||||
@@ -1,20 +1,21 @@
|
||||
extern crate winit;
|
||||
|
||||
fn main() {
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let _window = winit::WindowBuilder::new()
|
||||
.with_min_dimensions(400, 200)
|
||||
.with_max_dimensions(800, 400)
|
||||
let window = winit::WindowBuilder::new()
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
window.set_min_dimensions(Some((400, 200)));
|
||||
window.set_max_dimensions(Some((800, 400)));
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
println!("{:?}", event);
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => events_loop.interrupt(),
|
||||
_ => ()
|
||||
winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => winit::ControlFlow::Break,
|
||||
_ => winit::ControlFlow::Continue,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
extern crate winit;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn main() {
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let window1 = winit::Window::new(&events_loop).unwrap();
|
||||
let window2 = winit::Window::new(&events_loop).unwrap();
|
||||
let window3 = winit::Window::new(&events_loop).unwrap();
|
||||
|
||||
let mut num_windows = 3;
|
||||
let mut windows = HashMap::new();
|
||||
for _ in 0..3 {
|
||||
let window = winit::Window::new(&events_loop).unwrap();
|
||||
windows.insert(window.id(), window);
|
||||
}
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
match event {
|
||||
winit::Event::WindowEvent { event: winit::WindowEvent::Closed, window_id } => {
|
||||
if window_id == window1.id() {
|
||||
println!("Window 1 has been closed")
|
||||
} else if window_id == window2.id() {
|
||||
println!("Window 2 has been closed")
|
||||
} else if window_id == window3.id() {
|
||||
println!("Window 3 has been closed");
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
winit::Event::WindowEvent {
|
||||
event: winit::WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} => {
|
||||
println!("Window {:?} has received the signal to close", window_id);
|
||||
|
||||
num_windows -= 1;
|
||||
if num_windows == 0 {
|
||||
events_loop.interrupt();
|
||||
// This drops the window, causing it to close.
|
||||
windows.remove(&window_id);
|
||||
|
||||
if windows.is_empty() {
|
||||
return winit::ControlFlow::Break;
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
winit::ControlFlow::Continue
|
||||
})
|
||||
}
|
||||
|
||||
29
examples/proxy.rs
Normal file
29
examples/proxy.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
extern crate winit;
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let _window = winit::WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
let proxy = events_loop.create_proxy();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
// Wake up the `events_loop` once every second.
|
||||
loop {
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
proxy.wakeup().unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
println!("{:?}", event);
|
||||
match event {
|
||||
winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } =>
|
||||
winit::ControlFlow::Break,
|
||||
_ => winit::ControlFlow::Continue,
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
extern crate winit;
|
||||
|
||||
fn main() {
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let window = winit::WindowBuilder::new().with_decorations(false)
|
||||
.with_transparency(true)
|
||||
@@ -13,8 +13,8 @@ fn main() {
|
||||
println!("{:?}", event);
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => events_loop.interrupt(),
|
||||
_ => ()
|
||||
winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => winit::ControlFlow::Break,
|
||||
_ => winit::ControlFlow::Continue,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
extern crate winit;
|
||||
|
||||
fn main() {
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let window = winit::WindowBuilder::new()
|
||||
let _window = winit::WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
@@ -12,8 +12,11 @@ fn main() {
|
||||
println!("{:?}", event);
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => events_loop.interrupt(),
|
||||
_ => ()
|
||||
winit::Event::WindowEvent {
|
||||
event: winit::WindowEvent::CloseRequested,
|
||||
..
|
||||
} => winit::ControlFlow::Break,
|
||||
_ => winit::ControlFlow::Continue,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
|
||||
//! This temporary module generates types that wrap around the old API (winit v5 and below) and
|
||||
//! expose the new API (winit v6 and above).
|
||||
//!
|
||||
//! This is temporary so that existing backends can smoothly transition. After all implementations
|
||||
//! have finished transitionning, this module should disappear.
|
||||
|
||||
macro_rules! gen_api_transition {
|
||||
() => {
|
||||
pub struct EventsLoop {
|
||||
windows: ::std::sync::Mutex<Vec<::std::sync::Arc<Window>>>,
|
||||
interrupted: ::std::sync::atomic::AtomicBool,
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new() -> EventsLoop {
|
||||
EventsLoop {
|
||||
windows: ::std::sync::Mutex::new(vec![]),
|
||||
interrupted: ::std::sync::atomic::AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interrupt(&self) {
|
||||
self.interrupted.store(true, ::std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn poll_events<F>(&self, mut callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
let mut windows = self.windows.lock().unwrap();
|
||||
for window in windows.iter() {
|
||||
for event in window.poll_events() {
|
||||
callback(::Event::WindowEvent {
|
||||
window_id: ::WindowId(WindowId(&**window as *const Window as usize)),
|
||||
event: event,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&self, mut callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
self.interrupted.store(false, ::std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
// Yeah that's a very bad implementation.
|
||||
loop {
|
||||
self.poll_events(|e| callback(e));
|
||||
::std::thread::sleep_ms(5);
|
||||
if self.interrupted.load(::std::sync::atomic::Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(usize);
|
||||
|
||||
pub struct Window2 {
|
||||
pub window: ::std::sync::Arc<Window>,
|
||||
events_loop: ::std::sync::Weak<EventsLoop>,
|
||||
}
|
||||
|
||||
impl ::std::ops::Deref for Window2 {
|
||||
type Target = Window;
|
||||
#[inline]
|
||||
fn deref(&self) -> &Window {
|
||||
&*self.window
|
||||
}
|
||||
}
|
||||
|
||||
impl Window2 {
|
||||
pub fn new(events_loop: ::std::sync::Arc<EventsLoop>, window: &::WindowAttributes,
|
||||
pl_attribs: &PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window2, CreationError>
|
||||
{
|
||||
let win = ::std::sync::Arc::new(try!(Window::new(window, pl_attribs)));
|
||||
events_loop.windows.lock().unwrap().push(win.clone());
|
||||
Ok(Window2 {
|
||||
window: win,
|
||||
events_loop: ::std::sync::Arc::downgrade(&events_loop),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId(&*self.window as *const Window as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window2 {
|
||||
fn drop(&mut self) {
|
||||
if let Some(ev) = self.events_loop.upgrade() {
|
||||
let mut windows = ev.windows.lock().unwrap();
|
||||
windows.retain(|w| &**w as *const Window != &*self.window as *const _);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
162
src/events.rs
162
src/events.rs
@@ -1,18 +1,28 @@
|
||||
use std::path::PathBuf;
|
||||
use WindowId;
|
||||
use {WindowId, DeviceId};
|
||||
|
||||
/// Describes a generic event.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Event {
|
||||
WindowEvent {
|
||||
window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
}
|
||||
},
|
||||
DeviceEvent {
|
||||
device_id: DeviceId,
|
||||
event: DeviceEvent,
|
||||
},
|
||||
Awakened,
|
||||
|
||||
/// The application has been suspended or resumed.
|
||||
///
|
||||
/// The parameter is true if app was suspended, and false if it has been resumed.
|
||||
Suspended(bool),
|
||||
}
|
||||
|
||||
/// Describes an event from a `Window`.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum WindowEvent {
|
||||
// TODO: remove ; can break the lib internally so be careful
|
||||
Awakened,
|
||||
|
||||
/// The size of the window has changed.
|
||||
Resized(u32, u32),
|
||||
@@ -20,12 +30,21 @@ pub enum WindowEvent {
|
||||
/// The position of the window has changed.
|
||||
Moved(i32, i32),
|
||||
|
||||
/// The window has been closed.
|
||||
Closed,
|
||||
/// The window has been requested to close.
|
||||
CloseRequested,
|
||||
|
||||
/// The window has been destroyed.
|
||||
Destroyed,
|
||||
|
||||
/// A file has been dropped into the window.
|
||||
DroppedFile(PathBuf),
|
||||
|
||||
/// A file is being hovered over the window.
|
||||
HoveredFile(PathBuf),
|
||||
|
||||
/// A file was hovered, but has exited the window.
|
||||
HoveredFileCancelled,
|
||||
|
||||
/// The window received a unicode character.
|
||||
ReceivedCharacter(char),
|
||||
|
||||
@@ -35,44 +54,122 @@ pub enum WindowEvent {
|
||||
Focused(bool),
|
||||
|
||||
/// An event from the keyboard has been received.
|
||||
KeyboardInput(ElementState, ScanCode, Option<VirtualKeyCode>, ModifiersState),
|
||||
KeyboardInput { device_id: DeviceId, input: KeyboardInput },
|
||||
|
||||
/// The cursor has moved on the window.
|
||||
///
|
||||
/// The parameter are the (x,y) coords in pixels relative to the top-left corner of the window.
|
||||
MouseMoved(i32, i32),
|
||||
CursorMoved {
|
||||
device_id: DeviceId,
|
||||
|
||||
/// (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),
|
||||
modifiers: ModifiersState
|
||||
},
|
||||
|
||||
/// The cursor has entered the window.
|
||||
MouseEntered,
|
||||
CursorEntered { device_id: DeviceId },
|
||||
|
||||
/// The cursor has left the window.
|
||||
MouseLeft,
|
||||
CursorLeft { device_id: DeviceId },
|
||||
|
||||
/// A mouse wheel movement or touchpad scroll occurred.
|
||||
MouseWheel(MouseScrollDelta, TouchPhase),
|
||||
MouseWheel { device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase, modifiers: ModifiersState },
|
||||
|
||||
/// An mouse button press has been received.
|
||||
MouseInput { device_id: DeviceId, state: ElementState, button: MouseButton, modifiers: ModifiersState },
|
||||
|
||||
/// An event from the mouse has been received.
|
||||
MouseInput(ElementState, MouseButton),
|
||||
|
||||
/// Touchpad pressure event.
|
||||
///
|
||||
/// At the moment, only supported on Apple forcetouch-capable macbooks.
|
||||
/// The parameters are: pressure level (value between 0 and 1 representing how hard the touchpad
|
||||
/// is being pressed) and stage (integer representing the click level).
|
||||
TouchpadPressure(f32, i64),
|
||||
TouchpadPressure { device_id: DeviceId, pressure: f32, stage: i64 },
|
||||
|
||||
/// Motion on some analog axis. May report data redundant to other, more specific events.
|
||||
AxisMotion { device_id: DeviceId, axis: AxisId, value: f64 },
|
||||
|
||||
/// The window needs to be redrawn.
|
||||
Refresh,
|
||||
|
||||
/// App has been suspended or resumed.
|
||||
///
|
||||
/// The parameter is true if app was suspended, and false if it has been resumed.
|
||||
Suspended(bool),
|
||||
|
||||
/// Touch event has been received
|
||||
Touch(Touch)
|
||||
Touch(Touch),
|
||||
|
||||
/// DPI scaling factor of the window has changed.
|
||||
///
|
||||
/// The following actions 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),
|
||||
}
|
||||
|
||||
/// Represents raw hardware events that are not associated with any particular window.
|
||||
///
|
||||
/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person
|
||||
/// game controls. Many physical actions, such as mouse movement, can produce both device and window events. Because
|
||||
/// window events typically arise from virtual devices (corresponding to GUI cursors and keyboard focus) the device IDs
|
||||
/// may not match.
|
||||
///
|
||||
/// Note that these events are delivered regardless of input focus.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum DeviceEvent {
|
||||
Added,
|
||||
Removed,
|
||||
|
||||
/// Change in physical position of a pointing device.
|
||||
///
|
||||
/// This represents raw, unfiltered physical motion. Not to be confused with `WindowEvent::CursorMoved`.
|
||||
MouseMotion {
|
||||
/// (x, y) change in position in unspecified units.
|
||||
///
|
||||
/// Different devices may use different units.
|
||||
delta: (f64, f64),
|
||||
},
|
||||
|
||||
/// Physical scroll event
|
||||
MouseWheel {
|
||||
delta: MouseScrollDelta,
|
||||
},
|
||||
|
||||
/// Motion on some analog axis. This event will be reported for all arbitrary input devices
|
||||
/// that winit supports on this platform, including mouse devices. If the device is a mouse
|
||||
/// device then this will be reported alongside the MouseMotion event.
|
||||
Motion { axis: AxisId, value: f64 },
|
||||
|
||||
Button { button: ButtonId, state: ElementState },
|
||||
Key(KeyboardInput),
|
||||
Text { codepoint: char },
|
||||
}
|
||||
|
||||
/// Describes a keyboard input event.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct KeyboardInput {
|
||||
/// Identifies the physical key pressed
|
||||
///
|
||||
/// This should not change if the user adjusts the host's keyboard map. Use when the physical location of the
|
||||
/// key is more important than the key's host GUI semantics, such as for movement controls in a first-person
|
||||
/// game.
|
||||
pub scancode: ScanCode,
|
||||
|
||||
pub state: ElementState,
|
||||
|
||||
/// Identifies the semantic meaning of the key
|
||||
///
|
||||
/// Use when the semantics of the key are more important than the physical location of the key, such as when
|
||||
/// implementing appropriate behavior for "page up."
|
||||
pub virtual_keycode: Option<VirtualKeyCode>,
|
||||
|
||||
/// Modifier keys active at the time of this input.
|
||||
///
|
||||
/// This is tracked internally to avoid tracking errors arising from modifier key state changes when events from
|
||||
/// this device are not being delivered to the application, e.g. due to keyboard focus being elsewhere.
|
||||
pub modifiers: ModifiersState
|
||||
}
|
||||
|
||||
/// Describes touch-screen input state.
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum TouchPhase {
|
||||
Started,
|
||||
@@ -81,7 +178,6 @@ pub enum TouchPhase {
|
||||
Cancelled
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
/// Represents touch event
|
||||
///
|
||||
/// Every time user touches screen new Start event with some finger id is generated.
|
||||
@@ -97,21 +193,32 @@ pub enum TouchPhase {
|
||||
/// as previously received End event is a new finger and has nothing to do with an old one.
|
||||
///
|
||||
/// Touch may be cancelled if for example window lost focus.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Touch {
|
||||
pub device_id: DeviceId,
|
||||
pub phase: TouchPhase,
|
||||
pub location: (f64,f64),
|
||||
/// unique identifier of a finger.
|
||||
pub id: u64
|
||||
}
|
||||
|
||||
pub type ScanCode = u8;
|
||||
/// Hardware-dependent keyboard scan code.
|
||||
pub type ScanCode = u32;
|
||||
|
||||
/// Identifier for a specific analog axis on some device.
|
||||
pub type AxisId = u32;
|
||||
|
||||
/// Identifier for a specific button on some device.
|
||||
pub type ButtonId = u32;
|
||||
|
||||
/// Describes the input state of a key.
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum ElementState {
|
||||
Pressed,
|
||||
Released,
|
||||
}
|
||||
|
||||
/// Describes a button of a mouse controller.
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum MouseButton {
|
||||
Left,
|
||||
@@ -120,6 +227,7 @@ pub enum MouseButton {
|
||||
Other(u8),
|
||||
}
|
||||
|
||||
/// Describes a difference in the mouse scroll wheel state.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum MouseScrollDelta {
|
||||
/// Amount in lines or rows to scroll in the horizontal
|
||||
@@ -137,7 +245,9 @@ pub enum MouseScrollDelta {
|
||||
PixelDelta(f32, f32)
|
||||
}
|
||||
|
||||
/// Symbolic name for a keyboard key.
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
#[repr(u32)]
|
||||
pub enum VirtualKeyCode {
|
||||
/// The '1' key over the letters.
|
||||
Key1,
|
||||
@@ -237,6 +347,8 @@ pub enum VirtualKeyCode {
|
||||
/// The "Compose" key on Linux.
|
||||
Compose,
|
||||
|
||||
Caret,
|
||||
|
||||
Numlock,
|
||||
Numpad0,
|
||||
Numpad1,
|
||||
@@ -324,7 +436,7 @@ pub enum VirtualKeyCode {
|
||||
/// Represents the current state of the keyboard modifiers
|
||||
///
|
||||
/// Each field of this struct represents a modifier and is `true` if this modifier is active.
|
||||
#[derive(Default, Debug, Clone, Copy)]
|
||||
#[derive(Default, Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct ModifiersState {
|
||||
/// The "shift" key
|
||||
pub shift: bool,
|
||||
|
||||
222
src/lib.rs
222
src/lib.rs
@@ -1,4 +1,4 @@
|
||||
//! Winit allows you to build a window on as many platforms as possible.
|
||||
//! Winit allows you to build a window on as many platforms as possible.
|
||||
//!
|
||||
//! # Building a window
|
||||
//!
|
||||
@@ -34,10 +34,9 @@
|
||||
//! screen, such as video games.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use winit::Event;
|
||||
//! use winit::WindowEvent;
|
||||
//! use winit::{Event, WindowEvent};
|
||||
//! # use winit::EventsLoop;
|
||||
//! # let events_loop = EventsLoop::new();
|
||||
//! # let mut events_loop = EventsLoop::new();
|
||||
//!
|
||||
//! loop {
|
||||
//! events_loop.poll_events(|event| {
|
||||
@@ -52,21 +51,20 @@
|
||||
//! ```
|
||||
//!
|
||||
//! The second way is to call `events_loop.run_forever(...)`. As its name tells, it will run
|
||||
//! forever unless it is stopped by calling `events_loop.interrupt()`.
|
||||
//! forever unless it is stopped by returning `ControlFlow::Break`.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use winit::Event;
|
||||
//! use winit::WindowEvent;
|
||||
//! use winit::{ControlFlow, Event, WindowEvent};
|
||||
//! # use winit::EventsLoop;
|
||||
//! # let events_loop = EventsLoop::new();
|
||||
//! # let mut events_loop = EventsLoop::new();
|
||||
//!
|
||||
//! events_loop.run_forever(|event| {
|
||||
//! match event {
|
||||
//! Event::WindowEvent { event: WindowEvent::Closed, .. } => {
|
||||
//! println!("The window was closed ; stopping");
|
||||
//! events_loop.interrupt();
|
||||
//! Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
|
||||
//! println!("The close button was pressed; stopping");
|
||||
//! ControlFlow::Break
|
||||
//! },
|
||||
//! _ => ()
|
||||
//! _ => ControlFlow::Continue,
|
||||
//! }
|
||||
//! });
|
||||
//! ```
|
||||
@@ -82,32 +80,19 @@
|
||||
//! 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"))]
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
#[macro_use]
|
||||
extern crate shared_library;
|
||||
|
||||
extern crate libc;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[macro_use]
|
||||
extern crate winapi;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate kernel32;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate shell32;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate gdi32;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate user32;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate dwmapi;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
#[macro_use]
|
||||
extern crate objc;
|
||||
#[cfg(target_os = "macos")]
|
||||
extern crate cgl;
|
||||
#[cfg(target_os = "macos")]
|
||||
extern crate cocoa;
|
||||
#[cfg(target_os = "macos")]
|
||||
extern crate core_foundation;
|
||||
@@ -115,18 +100,14 @@ extern crate core_foundation;
|
||||
extern crate core_graphics;
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
extern crate x11_dl;
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
extern crate percent_encoding;
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
|
||||
#[macro_use(wayland_env,declare_handler)]
|
||||
#[macro_use]
|
||||
extern crate wayland_client;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use events::*;
|
||||
pub use window::{AvailableMonitorsIter, MonitorId, get_available_monitors, get_primary_monitor};
|
||||
pub use native_monitor::NativeMonitorId;
|
||||
|
||||
#[macro_use]
|
||||
mod api_transition;
|
||||
pub use window::{AvailableMonitorsIter, MonitorId};
|
||||
|
||||
mod platform;
|
||||
mod events;
|
||||
@@ -139,25 +120,22 @@ pub mod os;
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use winit::Event;
|
||||
/// use winit::EventsLoop;
|
||||
/// use winit::Window;
|
||||
/// use winit::WindowEvent;
|
||||
/// use winit::{Event, EventsLoop, Window, WindowEvent, ControlFlow};
|
||||
///
|
||||
/// let events_loop = EventsLoop::new();
|
||||
/// let mut events_loop = EventsLoop::new();
|
||||
/// let window = Window::new(&events_loop).unwrap();
|
||||
///
|
||||
/// events_loop.run_forever(|event| {
|
||||
/// match event {
|
||||
/// Event::WindowEvent { event: WindowEvent::Closed, .. } => {
|
||||
/// events_loop.interrupt();
|
||||
/// Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
|
||||
/// ControlFlow::Break
|
||||
/// },
|
||||
/// _ => ()
|
||||
/// _ => ControlFlow::Continue,
|
||||
/// }
|
||||
/// });
|
||||
/// ```
|
||||
pub struct Window {
|
||||
window: platform::Window2,
|
||||
window: platform::Window,
|
||||
}
|
||||
|
||||
/// Identifier of a window. Unique for each window.
|
||||
@@ -169,42 +147,131 @@ pub struct Window {
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(platform::WindowId);
|
||||
|
||||
/// Provides a way to retreive events from the windows that were registered to it.
|
||||
// TODO: document usage in multiple threads
|
||||
/// Identifier of an input device.
|
||||
///
|
||||
/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which
|
||||
/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or
|
||||
/// physical. Virtual devices typically aggregate inputs from multiple physical devices.
|
||||
#[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
|
||||
/// the events loop.
|
||||
///
|
||||
/// An `EventsLoop` can be seen more or less as a "context". Calling `EventsLoop::new()`
|
||||
/// initializes everything that will be required to create windows. For example on Linux creating
|
||||
/// an events loop opens a connection to the X or Wayland server.
|
||||
///
|
||||
/// To wake up an `EventsLoop` from a another thread, see the `EventsLoopProxy` docs.
|
||||
///
|
||||
/// Note that the `EventsLoop` cannot be shared accross threads (due to platform-dependant logic
|
||||
/// forbiding it), as such it is neither `Send` nor `Sync`. If you need cross-thread access, the
|
||||
/// `Window` created from this `EventsLoop` _can_ be sent to an other thread, and the
|
||||
/// `EventsLoopProxy` allows you to wakeup an `EventsLoop` from an other thread.
|
||||
pub struct EventsLoop {
|
||||
events_loop: Arc<platform::EventsLoop>,
|
||||
events_loop: platform::EventsLoop,
|
||||
_marker: ::std::marker::PhantomData<*mut ()> // Not Send nor Sync
|
||||
}
|
||||
|
||||
/// Returned by the user callback given to the `EventsLoop::run_forever` method.
|
||||
///
|
||||
/// Indicates whether the `run_forever` method should continue or complete.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ControlFlow {
|
||||
/// Continue looping and waiting for events.
|
||||
Continue,
|
||||
/// Break from the event loop.
|
||||
Break,
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
/// Builds a new events loop.
|
||||
///
|
||||
/// Usage will result in display backend initialisation, this can be controlled on linux
|
||||
/// using an environment variable `WINIT_UNIX_BACKEND`. Legal values are `x11` and `wayland`.
|
||||
/// If it is not set, winit will try to connect to a wayland connection, and if it fails will
|
||||
/// fallback on x11. If this variable is set with any other value, winit will panic.
|
||||
pub fn new() -> EventsLoop {
|
||||
EventsLoop {
|
||||
events_loop: Arc::new(platform::EventsLoop::new()),
|
||||
events_loop: platform::EventsLoop::new(),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the list of all the monitors available on the system.
|
||||
///
|
||||
// Note: should be replaced with `-> impl Iterator` once stable.
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> AvailableMonitorsIter {
|
||||
let data = self.events_loop.get_available_monitors();
|
||||
AvailableMonitorsIter{ data: data.into_iter() }
|
||||
}
|
||||
|
||||
/// Returns the primary monitor of the system.
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
MonitorId { inner: self.events_loop.get_primary_monitor() }
|
||||
}
|
||||
|
||||
/// Fetches all the events that are pending, calls the callback function for each of them,
|
||||
/// and returns.
|
||||
#[inline]
|
||||
pub fn poll_events<F>(&self, callback: F)
|
||||
pub fn poll_events<F>(&mut self, callback: F)
|
||||
where F: FnMut(Event)
|
||||
{
|
||||
self.events_loop.poll_events(callback)
|
||||
}
|
||||
|
||||
/// Runs forever until `interrupt()` is called. Whenever an event happens, calls the callback.
|
||||
/// 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.
|
||||
#[inline]
|
||||
pub fn run_forever<F>(&self, callback: F)
|
||||
where F: FnMut(Event)
|
||||
pub fn run_forever<F>(&mut self, callback: F)
|
||||
where F: FnMut(Event) -> ControlFlow
|
||||
{
|
||||
self.events_loop.run_forever(callback)
|
||||
}
|
||||
|
||||
/// If we called `run_forever()`, stops the process of waiting for events.
|
||||
// TODO: what if we're waiting from multiple threads?
|
||||
#[inline]
|
||||
pub fn interrupt(&self) {
|
||||
self.events_loop.interrupt()
|
||||
/// Creates an `EventsLoopProxy` that can be used to wake up the `EventsLoop` from another
|
||||
/// thread.
|
||||
pub fn create_proxy(&self) -> EventsLoopProxy {
|
||||
EventsLoopProxy {
|
||||
events_loop_proxy: self.events_loop.create_proxy(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to wake up the `EventsLoop` from another thread.
|
||||
#[derive(Clone)]
|
||||
pub struct EventsLoopProxy {
|
||||
events_loop_proxy: platform::EventsLoopProxy,
|
||||
}
|
||||
|
||||
impl EventsLoopProxy {
|
||||
/// Wake up the `EventsLoop` from which this proxy was created.
|
||||
///
|
||||
/// This causes the `EventsLoop` to emit an `Awakened` event.
|
||||
///
|
||||
/// Returns an `Err` if the associated `EventsLoop` no longer exists.
|
||||
pub fn wakeup(&self) -> Result<(), EventsLoopClosed> {
|
||||
self.events_loop_proxy.wakeup()
|
||||
}
|
||||
}
|
||||
|
||||
/// The error that is returned when an `EventsLoopProxy` attempts to wake up an `EventsLoop` that
|
||||
/// no longer exists.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct EventsLoopClosed;
|
||||
|
||||
impl std::fmt::Display for EventsLoopClosed {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{}", std::error::Error::description(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for EventsLoopClosed {
|
||||
fn description(&self) -> &str {
|
||||
"Tried to wake up a closed `EventsLoop`"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,7 +286,7 @@ pub struct WindowBuilder {
|
||||
}
|
||||
|
||||
/// Error that can happen while creating a window or a headless renderer.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CreationError {
|
||||
OsError(String),
|
||||
/// TODO: remove this error
|
||||
@@ -247,6 +314,7 @@ impl std::error::Error for CreationError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes the appearance of the mouse cursor.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum MouseCursor {
|
||||
/// The platform-dependent default cursor.
|
||||
@@ -303,7 +371,7 @@ pub enum MouseCursor {
|
||||
RowResize,
|
||||
}
|
||||
|
||||
/// Describes how glutin handles the cursor.
|
||||
/// Describes how winit handles the cursor.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum CursorState {
|
||||
/// Normal cursor behavior.
|
||||
@@ -339,16 +407,21 @@ pub struct WindowAttributes {
|
||||
/// The default is `None`.
|
||||
pub max_dimensions: Option<(u32, u32)>,
|
||||
|
||||
/// If `Some`, the window will be in fullscreen mode with the given monitor.
|
||||
/// Whether the window should be set as fullscreen upon creation.
|
||||
///
|
||||
/// The default is `None`.
|
||||
pub monitor: Option<platform::MonitorId>,
|
||||
pub fullscreen: Option<MonitorId>,
|
||||
|
||||
/// The title of the window in the title bar.
|
||||
///
|
||||
/// The default is `"glutin window"`.
|
||||
/// The default is `"winit window"`.
|
||||
pub title: String,
|
||||
|
||||
/// Whether the window should be maximized upon creation.
|
||||
///
|
||||
/// The default is `false`.
|
||||
pub maximized: bool,
|
||||
|
||||
/// Whether the window should be immediately visible upon creation.
|
||||
///
|
||||
/// The default is `true`.
|
||||
@@ -365,8 +438,8 @@ pub struct WindowAttributes {
|
||||
/// The default is `true`.
|
||||
pub decorations: bool,
|
||||
|
||||
/// [iOS only] Enable multitouch, see [UIView#multipleTouchEnabled]
|
||||
/// (https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/occ/instp/UIView/multipleTouchEnabled)
|
||||
/// [iOS only] Enable multitouch,
|
||||
/// see [multipleTouchEnabled](https://developer.apple.com/documentation/uikit/uiview/1622519-multipletouchenabled)
|
||||
pub multitouch: bool,
|
||||
}
|
||||
|
||||
@@ -377,8 +450,9 @@ impl Default for WindowAttributes {
|
||||
dimensions: None,
|
||||
min_dimensions: None,
|
||||
max_dimensions: None,
|
||||
monitor: None,
|
||||
title: "glutin window".to_owned(),
|
||||
title: "winit window".to_owned(),
|
||||
maximized: false,
|
||||
fullscreen: None,
|
||||
visible: true,
|
||||
transparent: false,
|
||||
decorations: true,
|
||||
@@ -386,19 +460,3 @@ impl Default for WindowAttributes {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod native_monitor {
|
||||
/// Native platform identifier for a monitor. Different platforms use fundamentally different types
|
||||
/// to represent a monitor ID.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub enum NativeMonitorId {
|
||||
/// Cocoa and X11 use a numeric identifier to represent a monitor.
|
||||
Numeric(u32),
|
||||
|
||||
/// Win32 uses a Unicode string to represent a monitor.
|
||||
Name(String),
|
||||
|
||||
/// Other platforms (Android) don't support monitor identification.
|
||||
Unavailable
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
#![cfg(any(target_os = "android"))]
|
||||
|
||||
use std::os::raw::c_void;
|
||||
use EventsLoop;
|
||||
use Window;
|
||||
use WindowBuilder;
|
||||
|
||||
/// Additional methods on `EventsLoop` that are specific to Android.
|
||||
pub trait EventsLoopExt {
|
||||
/// Makes it possible for glutin to register a callback when a suspend event happens on Android
|
||||
fn set_suspend_callback(&self, cb: Option<Box<Fn(bool) -> ()>>);
|
||||
}
|
||||
|
||||
impl EventsLoopExt for EventsLoop {
|
||||
fn set_suspend_callback(&self, cb: Option<Box<Fn(bool) -> ()>>) {
|
||||
self.events_loop.set_suspend_callback(cb);
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `Window` that are specific to Android.
|
||||
pub trait WindowExt {
|
||||
fn get_native_window(&self) -> *const c_void;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use std::convert::From;
|
||||
use std::os::raw::c_void;
|
||||
use cocoa::appkit::NSApplicationActivationPolicy;
|
||||
use {Window, WindowBuilder};
|
||||
use {MonitorId, Window, WindowBuilder};
|
||||
|
||||
/// Additional methods on `Window` that are specific to MacOS.
|
||||
pub trait WindowExt {
|
||||
@@ -61,8 +61,23 @@ impl From<ActivationPolicy> for NSApplicationActivationPolicy {
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to MacOS.
|
||||
///
|
||||
/// **Note:** Properties dealing with the titlebar will be overwritten by the `with_decorations` method
|
||||
/// on the base `WindowBuilder`:
|
||||
///
|
||||
/// - `with_titlebar_transparent`
|
||||
/// - `with_title_hidden`
|
||||
/// - `with_titlebar_hidden`
|
||||
/// - `with_titlebar_buttons_hidden`
|
||||
/// - `with_fullsize_content_view`
|
||||
pub trait WindowBuilderExt {
|
||||
fn with_activation_policy(self, activation_policy: ActivationPolicy) -> WindowBuilder;
|
||||
fn with_movable_by_window_background(self, movable_by_window_background: bool) -> WindowBuilder;
|
||||
fn with_titlebar_transparent(self, titlebar_transparent: bool) -> WindowBuilder;
|
||||
fn with_title_hidden(self, title_hidden: bool) -> WindowBuilder;
|
||||
fn with_titlebar_hidden(self, titlebar_hidden: bool) -> WindowBuilder;
|
||||
fn with_titlebar_buttons_hidden(self, titlebar_buttons_hidden: bool) -> WindowBuilder;
|
||||
fn with_fullsize_content_view(self, fullsize_content_view: bool) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExt for WindowBuilder {
|
||||
@@ -72,4 +87,65 @@ impl WindowBuilderExt for 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
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorId` that are specific to MacOS.
|
||||
pub trait MonitorIdExt {
|
||||
/// Returns the identifier of the monitor for Cocoa.
|
||||
fn native_id(&self) -> u32;
|
||||
/// Returns a pointer to the NSScreen representing this monitor.
|
||||
fn get_nsscreen(&self) -> Option<*mut c_void>;
|
||||
}
|
||||
|
||||
impl MonitorIdExt for MonitorId {
|
||||
#[inline]
|
||||
fn native_id(&self) -> u32 {
|
||||
self.inner.get_native_identifier()
|
||||
}
|
||||
|
||||
fn get_nsscreen(&self) -> Option<*mut c_void> {
|
||||
self.inner.get_nsscreen().map(|s| s as *mut c_void)
|
||||
}
|
||||
}
|
||||
|
||||
167
src/os/unix.rs
167
src/os/unix.rs
@@ -1,91 +1,135 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
|
||||
use std::os::raw;
|
||||
use std::sync::Arc;
|
||||
use std::ptr;
|
||||
use libc;
|
||||
use EventsLoop;
|
||||
use MonitorId;
|
||||
use Window;
|
||||
use platform::Window2 as LinuxWindow;
|
||||
use platform::{UnixBackend, UNIX_BACKEND};
|
||||
use platform::EventsLoop as LinuxEventsLoop;
|
||||
use platform::Window as LinuxWindow;
|
||||
use WindowBuilder;
|
||||
use platform::x11::XConnection;
|
||||
use platform::x11::ffi::XVisualInfo;
|
||||
|
||||
use wayland_client::protocol::wl_display::WlDisplay;
|
||||
use wayland_client::protocol::wl_surface::WlSurface;
|
||||
|
||||
// TODO: stupid hack so that glutin can do its work
|
||||
#[doc(hidden)]
|
||||
pub use platform::x11;
|
||||
|
||||
// TODO: do not expose XConnection
|
||||
pub fn get_x11_xconnection() -> Option<Arc<XConnection>> {
|
||||
match *UNIX_BACKEND {
|
||||
UnixBackend::X(ref connec) => Some(connec.clone()),
|
||||
_ => None,
|
||||
pub use platform::XNotSupported;
|
||||
|
||||
/// Additional methods on `EventsLoop` that are specific to Linux.
|
||||
pub trait EventsLoopExt {
|
||||
/// Builds a new `EventsLoop` that is forced to use X11.
|
||||
fn new_x11() -> Result<Self, XNotSupported>
|
||||
where Self: Sized;
|
||||
|
||||
/// Builds a new `EventsLoop` that is forced to use Wayland.
|
||||
fn new_wayland() -> Self
|
||||
where Self: Sized;
|
||||
|
||||
/// True if the `EventsLoop` uses Wayland.
|
||||
fn is_wayland(&self) -> bool;
|
||||
|
||||
/// True if the `EventsLoop` uses X11.
|
||||
fn is_x11(&self) -> bool;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>>;
|
||||
}
|
||||
|
||||
impl EventsLoopExt for EventsLoop {
|
||||
#[inline]
|
||||
fn new_x11() -> Result<Self, XNotSupported> {
|
||||
LinuxEventsLoop::new_x11().map(|ev|
|
||||
EventsLoop {
|
||||
events_loop: ev,
|
||||
_marker: ::std::marker::PhantomData,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_wayland() -> Self {
|
||||
EventsLoop {
|
||||
events_loop: match LinuxEventsLoop::new_wayland() {
|
||||
Ok(e) => e,
|
||||
Err(_) => panic!() // TODO: propagate
|
||||
},
|
||||
_marker: ::std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_wayland(&self) -> bool {
|
||||
self.events_loop.is_wayland()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_x11(&self) -> bool {
|
||||
!self.events_loop.is_wayland()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> {
|
||||
self.events_loop.x_connection().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `Window` that are specific to Unix.
|
||||
pub trait WindowExt {
|
||||
/// Returns a pointer to the `Window` object of xlib that is used by this window.
|
||||
/// Returns the ID of the `Window` xlib object that is used by this window.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
|
||||
///
|
||||
/// The pointer will become invalid when the glutin `Window` is destroyed.
|
||||
fn get_xlib_window(&self) -> Option<*mut libc::c_void>;
|
||||
fn get_xlib_window(&self) -> Option<raw::c_ulong>;
|
||||
|
||||
/// Returns a pointer to the `Display` object of xlib that is used by this window.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
|
||||
///
|
||||
/// The pointer will become invalid when the glutin `Window` is destroyed.
|
||||
fn get_xlib_display(&self) -> Option<*mut libc::c_void>;
|
||||
fn get_xlib_display(&self) -> Option<*mut raw::c_void>;
|
||||
|
||||
fn get_xlib_screen_id(&self) -> Option<*mut libc::c_void>;
|
||||
fn get_xlib_screen_id(&self) -> Option<raw::c_int>;
|
||||
|
||||
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>>;
|
||||
|
||||
|
||||
fn send_xim_spot(&self, x: i16, y: i16);
|
||||
|
||||
/// This function returns the underlying `xcb_connection_t` of an xlib `Display`.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
|
||||
///
|
||||
/// The pointer will become invalid when the glutin `Window` is destroyed.
|
||||
fn get_xcb_connection(&self) -> Option<*mut libc::c_void>;
|
||||
fn get_xcb_connection(&self) -> Option<*mut raw::c_void>;
|
||||
|
||||
/// Returns a pointer to the `wl_surface` object of wayland that is used by this window.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
|
||||
///
|
||||
/// The pointer will become invalid when the glutin `Window` is destroyed.
|
||||
fn get_wayland_surface(&self) -> Option<*mut libc::c_void>;
|
||||
fn get_wayland_surface(&self) -> Option<*mut raw::c_void>;
|
||||
|
||||
/// Returns a pointer to the `wl_display` object of wayland that is used by this window.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
|
||||
///
|
||||
/// The pointer will become invalid when the glutin `Window` is destroyed.
|
||||
fn get_wayland_display(&self) -> Option<*mut libc::c_void>;
|
||||
fn get_wayland_display(&self) -> Option<*mut raw::c_void>;
|
||||
|
||||
/// Returns a reference to the `WlSurface` object of wayland that is used by this window.
|
||||
/// Check if the window is ready for drawing
|
||||
///
|
||||
/// For use with the `wayland-client` crate.
|
||||
/// It is a remnant of a previous implementation detail for the
|
||||
/// wayland backend, and is no longer relevant.
|
||||
///
|
||||
/// **This function is not part of winit's public API.**
|
||||
///
|
||||
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
|
||||
fn get_wayland_client_surface(&self) -> Option<&WlSurface>;
|
||||
|
||||
/// Returns a pointer to the `WlDisplay` object of wayland that is used by this window.
|
||||
///
|
||||
/// For use with the `wayland-client` crate.
|
||||
///
|
||||
/// **This function is not part of winit's public API.**
|
||||
///
|
||||
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
|
||||
fn get_wayland_client_display(&self) -> Option<&WlDisplay>;
|
||||
/// Always return true.
|
||||
#[deprecated]
|
||||
fn is_ready(&self) -> bool;
|
||||
}
|
||||
|
||||
impl WindowExt for Window {
|
||||
#[inline]
|
||||
fn get_xlib_window(&self) -> Option<*mut libc::c_void> {
|
||||
fn get_xlib_window(&self) -> Option<raw::c_ulong> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.get_xlib_window()),
|
||||
_ => None
|
||||
@@ -93,14 +137,14 @@ impl WindowExt for Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_xlib_display(&self) -> Option<*mut libc::c_void> {
|
||||
fn get_xlib_display(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.get_xlib_display()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_xlib_screen_id(&self) -> Option<*mut libc::c_void> {
|
||||
fn get_xlib_screen_id(&self) -> Option<raw::c_int> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.get_xlib_screen_id()),
|
||||
_ => None
|
||||
@@ -114,41 +158,41 @@ impl WindowExt for Window {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_xcb_connection(&self) -> Option<*mut libc::c_void> {
|
||||
fn get_xcb_connection(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.get_xcb_connection()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_wayland_surface(&self) -> Option<*mut libc::c_void> {
|
||||
use wayland_client::Proxy;
|
||||
self.get_wayland_client_surface().map(|p| p.ptr() as *mut _)
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
fn get_wayland_display(&self) -> Option<*mut libc::c_void> {
|
||||
use wayland_client::Proxy;
|
||||
self.get_wayland_client_display().map(|p| p.ptr() as *mut _)
|
||||
fn send_xim_spot(&self, x: i16, y: i16) {
|
||||
if let LinuxWindow::X(ref w) = self.window {
|
||||
w.send_xim_spot(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_wayland_client_surface(&self) -> Option<&WlSurface> {
|
||||
fn get_wayland_surface(&self) -> Option<*mut raw::c_void> {
|
||||
use wayland_client::Proxy;
|
||||
match self.window {
|
||||
LinuxWindow::Wayland(ref w) => Some(w.get_surface()),
|
||||
LinuxWindow::Wayland(ref w) => Some(w.get_surface().ptr() as *mut _),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_wayland_client_display(&self) -> Option<&WlDisplay> {
|
||||
fn get_wayland_display(&self) -> Option<*mut raw::c_void> {
|
||||
use wayland_client::Proxy;
|
||||
match self.window {
|
||||
LinuxWindow::Wayland(ref w) => Some(w.get_display()),
|
||||
LinuxWindow::Wayland(ref w) => Some(w.get_display().ptr() as *mut _),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_ready(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to Unix.
|
||||
@@ -172,3 +216,16 @@ impl WindowBuilderExt for WindowBuilder {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorId` that are specific to Linux.
|
||||
pub trait MonitorIdExt {
|
||||
/// Returns the inner identifier of the monitor.
|
||||
fn native_id(&self) -> u32;
|
||||
}
|
||||
|
||||
impl MonitorIdExt for MonitorId {
|
||||
#[inline]
|
||||
fn native_id(&self) -> u32 {
|
||||
self.inner.get_native_identifier()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,58 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use std::os::raw::c_void;
|
||||
use libc;
|
||||
use MonitorId;
|
||||
use Window;
|
||||
use WindowBuilder;
|
||||
use winapi;
|
||||
use winapi::shared::windef::HWND;
|
||||
|
||||
/// Additional methods on `Window` that are specific to Windows.
|
||||
pub trait WindowExt {
|
||||
/// Returns a pointer to the `Window` object of xlib that is used by this window.
|
||||
/// Returns the native handle that is used by this window.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
|
||||
///
|
||||
/// The pointer will become invalid when the glutin `Window` is destroyed.
|
||||
/// The pointer will become invalid when the native window was destroyed.
|
||||
fn get_hwnd(&self) -> *mut libc::c_void;
|
||||
}
|
||||
|
||||
impl WindowExt for Window {
|
||||
#[inline]
|
||||
fn get_hwnd(&self) -> *mut libc::c_void {
|
||||
self.window.platform_window()
|
||||
self.window.hwnd() as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to Windows.
|
||||
pub trait WindowBuilderExt {
|
||||
fn with_parent_window(self, parent: winapi::HWND) -> WindowBuilder;
|
||||
fn with_parent_window(self, parent: HWND) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExt for WindowBuilder {
|
||||
/// Sets a parent to the window to be created
|
||||
/// Sets a parent to the window to be created.
|
||||
#[inline]
|
||||
fn with_parent_window(mut self, parent: winapi::HWND) -> WindowBuilder {
|
||||
fn with_parent_window(mut self, parent: HWND) -> WindowBuilder {
|
||||
self.platform_specific.parent = Some(parent);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorId` that are specific to Windows.
|
||||
pub trait MonitorIdExt {
|
||||
/// Returns the name of the monitor adapter specific to the Win32 API.
|
||||
fn native_id(&self) -> String;
|
||||
|
||||
/// Returns the handle of the monitor - `HMONITOR`.
|
||||
fn hmonitor(&self) -> *mut c_void;
|
||||
}
|
||||
|
||||
impl MonitorIdExt for MonitorId {
|
||||
#[inline]
|
||||
fn native_id(&self) -> String {
|
||||
self.inner.get_native_identifier()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hmonitor(&self) -> *mut c_void {
|
||||
self.inner.get_hmonitor() as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,25 +3,165 @@
|
||||
extern crate android_glue;
|
||||
|
||||
use libc;
|
||||
use std::ffi::{CString};
|
||||
use std::sync::mpsc::{Receiver, channel};
|
||||
use std::os::raw::c_void;
|
||||
use {CreationError, WindowEvent as Event, MouseCursor};
|
||||
use {CreationError, Event, WindowEvent, MouseCursor};
|
||||
use CreationError::OsError;
|
||||
use events::ElementState::{Pressed, Released};
|
||||
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;
|
||||
use native_monitor::NativeMonitorId;
|
||||
|
||||
gen_api_transition!();
|
||||
pub struct EventsLoop {
|
||||
event_rx: Receiver<android_glue::Event>,
|
||||
suspend_callback: RefCell<Option<Box<Fn(bool) -> ()>>>
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventsLoopProxy;
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new() -> EventsLoop {
|
||||
let (tx, rx) = channel();
|
||||
android_glue::add_sender(tx);
|
||||
EventsLoop {
|
||||
event_rx: rx,
|
||||
suspend_callback: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
let mut rb = VecDeque::new();
|
||||
rb.push_back(MonitorId);
|
||||
rb
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
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) => {
|
||||
Some(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId),
|
||||
event: WindowEvent::Touch(Touch {
|
||||
phase: match motion.action {
|
||||
android_glue::MotionAction::Down => TouchPhase::Started,
|
||||
android_glue::MotionAction::Move => TouchPhase::Moved,
|
||||
android_glue::MotionAction::Up => TouchPhase::Ended,
|
||||
android_glue::MotionAction::Cancel => TouchPhase::Cancelled,
|
||||
},
|
||||
location: (motion.x as f64, motion.y as f64),
|
||||
id: motion.pointer_id as u64,
|
||||
device_id: DEVICE_ID,
|
||||
}),
|
||||
})
|
||||
},
|
||||
android_glue::Event::InitWindow => {
|
||||
// The activity went to foreground.
|
||||
if let Some(cb) = self.suspend_callback.borrow().as_ref() {
|
||||
(*cb)(false);
|
||||
}
|
||||
Some(Event::Suspended(false))
|
||||
},
|
||||
android_glue::Event::TermWindow => {
|
||||
// The activity went to background.
|
||||
if let Some(cb) = self.suspend_callback.borrow().as_ref() {
|
||||
(*cb)(true);
|
||||
}
|
||||
Some(Event::Suspended(true))
|
||||
},
|
||||
android_glue::Event::WindowResized |
|
||||
android_glue::Event::ConfigChanged => {
|
||||
// Activity Orientation changed or resized.
|
||||
let native_window = unsafe { android_glue::get_native_window() };
|
||||
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;
|
||||
Some(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId),
|
||||
event: WindowEvent::Resized(w, h),
|
||||
})
|
||||
}
|
||||
},
|
||||
android_glue::Event::WindowRedrawNeeded => {
|
||||
// The activity needs to be redrawn.
|
||||
Some(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId),
|
||||
event: WindowEvent::Refresh,
|
||||
})
|
||||
}
|
||||
android_glue::Event::Wake => {
|
||||
Some(Event::Awakened)
|
||||
}
|
||||
_ => {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(event) = e {
|
||||
callback(event);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn set_suspend_callback(&self, cb: Option<Box<Fn(bool) -> ()>>) {
|
||||
*self.suspend_callback.borrow_mut() = cb;
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(::Event) -> ::ControlFlow,
|
||||
{
|
||||
// Yeah that's a very bad implementation.
|
||||
loop {
|
||||
let mut control_flow = ::ControlFlow::Continue;
|
||||
self.poll_events(|e| {
|
||||
if let ::ControlFlow::Break = callback(e) {
|
||||
control_flow = ::ControlFlow::Break;
|
||||
}
|
||||
});
|
||||
if let ::ControlFlow::Break = control_flow {
|
||||
break;
|
||||
}
|
||||
::std::thread::sleep(::std::time::Duration::from_millis(5));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventsLoopProxy {
|
||||
EventsLoopProxy
|
||||
}
|
||||
}
|
||||
|
||||
impl EventsLoopProxy {
|
||||
pub fn wakeup(&self) -> Result<(), ::EventsLoopClosed> {
|
||||
android_glue::wake_event_loop();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
|
||||
pub struct Window {
|
||||
native_window: *const c_void,
|
||||
event_rx: Receiver<android_glue::Event>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -29,18 +169,6 @@ pub struct MonitorId;
|
||||
|
||||
mod ffi;
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors() -> VecDeque<MonitorId> {
|
||||
let mut rb = VecDeque::new();
|
||||
rb.push_back(MonitorId);
|
||||
rb
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor() -> MonitorId {
|
||||
MonitorId
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
#[inline]
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
@@ -48,13 +176,22 @@ impl MonitorId {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> NativeMonitorId {
|
||||
NativeMonitorId::Unavailable
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
unsafe {
|
||||
let window = android_glue::get_native_window();
|
||||
(ffi::ANativeWindow_getWidth(window) as u32, ffi::ANativeWindow_getHeight(window) as u32)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
unimplemented!()
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
// Android assumes single screen
|
||||
(0, 0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
1.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,78 +200,11 @@ pub struct PlatformSpecificWindowBuilderAttributes;
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificHeadlessBuilderAttributes;
|
||||
|
||||
pub struct PollEventsIterator<'a> {
|
||||
window: &'a Window,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for PollEventsIterator<'a> {
|
||||
type Item = Event;
|
||||
|
||||
fn next(&mut self) -> Option<Event> {
|
||||
match self.window.event_rx.try_recv() {
|
||||
Ok(android_glue::Event::EventMotion(motion)) => {
|
||||
Some(Event::Touch(Touch {
|
||||
phase: match motion.action {
|
||||
android_glue::MotionAction::Down => TouchPhase::Started,
|
||||
android_glue::MotionAction::Move => TouchPhase::Moved,
|
||||
android_glue::MotionAction::Up => TouchPhase::Ended,
|
||||
android_glue::MotionAction::Cancel => TouchPhase::Cancelled,
|
||||
},
|
||||
location: (motion.x as f64, motion.y as f64),
|
||||
id: motion.pointer_id as u64,
|
||||
}))
|
||||
},
|
||||
Ok(android_glue::Event::InitWindow) => {
|
||||
// The activity went to foreground.
|
||||
Some(Event::Suspended(false))
|
||||
},
|
||||
Ok(android_glue::Event::TermWindow) => {
|
||||
// The activity went to background.
|
||||
Some(Event::Suspended(true))
|
||||
},
|
||||
Ok(android_glue::Event::WindowResized) |
|
||||
Ok(android_glue::Event::ConfigChanged) => {
|
||||
// Activity Orientation changed or resized.
|
||||
self.window.get_inner_size().map(|s| Event::Resized(s.0, s.1))
|
||||
},
|
||||
Ok(android_glue::Event::WindowRedrawNeeded) => {
|
||||
// The activity needs to be redrawn.
|
||||
Some(Event::Refresh)
|
||||
}
|
||||
_ => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WaitEventsIterator<'a> {
|
||||
window: &'a Window,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for WaitEventsIterator<'a> {
|
||||
type Item = Event;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Event> {
|
||||
loop {
|
||||
// calling poll_events()
|
||||
if let Some(ev) = self.window.poll_events().next() {
|
||||
return Some(ev);
|
||||
}
|
||||
|
||||
// TODO: Implement a proper way of sleeping on the event queue
|
||||
// timer::sleep(Duration::milliseconds(16));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(win_attribs: &WindowAttributes, _: &PlatformSpecificWindowBuilderAttributes)
|
||||
pub fn new(_: &EventsLoop, win_attribs: &WindowAttributes,
|
||||
_: &PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window, CreationError>
|
||||
{
|
||||
use std::{mem, ptr};
|
||||
|
||||
// not implemented
|
||||
assert!(win_attribs.min_dimensions.is_none());
|
||||
assert!(win_attribs.max_dimensions.is_none());
|
||||
@@ -144,13 +214,10 @@ impl Window {
|
||||
return Err(OsError(format!("Android's native window is null")));
|
||||
}
|
||||
|
||||
let (tx, rx) = channel();
|
||||
android_glue::add_sender(tx);
|
||||
android_glue::set_multitouch(win_attribs.multitouch);
|
||||
|
||||
Ok(Window {
|
||||
native_window: native_window as *const _,
|
||||
event_rx: rx,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -159,11 +226,6 @@ impl Window {
|
||||
self.native_window
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_closed(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_title(&self, _: &str) {
|
||||
}
|
||||
@@ -181,20 +243,29 @@ impl Window {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_position(&self, _x: i32, _y: i32) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
let native_window = unsafe { android_glue::get_native_window() };
|
||||
pub fn set_min_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
if native_window.is_null() {
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
if self.native_window.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some((
|
||||
unsafe { ffi::ANativeWindow_getWidth(native_window as *const _) } as u32,
|
||||
unsafe { ffi::ANativeWindow_getHeight(native_window as *const _) } as u32
|
||||
unsafe { ffi::ANativeWindow_getWidth(self.native_window as *const _) } as u32,
|
||||
unsafe { ffi::ANativeWindow_getHeight(self.native_window as *const _) } as u32
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -208,25 +279,6 @@ impl Window {
|
||||
pub fn set_inner_size(&self, _x: u32, _y: u32) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn create_window_proxy(&self) -> WindowProxy {
|
||||
WindowProxy
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn poll_events(&self) -> PollEventsIterator {
|
||||
PollEventsIterator {
|
||||
window: self
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn wait_events(&self) -> WaitEventsIterator {
|
||||
WaitEventsIterator {
|
||||
window: self
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
unimplemented!();
|
||||
@@ -237,16 +289,12 @@ impl Window {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _: MouseCursor) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
pub fn set_cursor_state(&self, _state: CursorState) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -256,20 +304,37 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||
unimplemented!();
|
||||
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, _maximized: bool) {
|
||||
// Android has single screen maximized apps so nothing to do
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) {
|
||||
// Android has single screen maximized apps so nothing to do
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, _decorations: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
RootMonitorId{inner: MonitorId}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Window {}
|
||||
unsafe impl Sync for Window {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WindowProxy;
|
||||
|
||||
impl WindowProxy {
|
||||
#[inline]
|
||||
pub fn wakeup_event_loop(&self) {
|
||||
android_glue::wake_event_loop();
|
||||
}
|
||||
}
|
||||
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
||||
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
|
||||
|
||||
316
src/platform/emscripten/ffi.rs
Normal file
316
src/platform/emscripten/ffi.rs
Normal file
@@ -0,0 +1,316 @@
|
||||
#![allow(dead_code)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use std::os::raw::{c_int, c_char, c_void, c_ulong, c_double, c_long, c_ushort};
|
||||
#[cfg(test)]
|
||||
use std::mem;
|
||||
|
||||
pub type EM_BOOL = c_int;
|
||||
pub type EM_UTF8 = c_char;
|
||||
pub type EMSCRIPTEN_RESULT = c_int;
|
||||
|
||||
pub const EM_TRUE: EM_BOOL = 1;
|
||||
pub const EM_FALSE: EM_BOOL = 0;
|
||||
|
||||
// values for EMSCRIPTEN_RESULT
|
||||
pub const EMSCRIPTEN_RESULT_SUCCESS: c_int = 0;
|
||||
pub const EMSCRIPTEN_RESULT_DEFERRED: c_int = 1;
|
||||
pub const EMSCRIPTEN_RESULT_NOT_SUPPORTED: c_int = -1;
|
||||
pub const EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED: c_int = -2;
|
||||
pub const EMSCRIPTEN_RESULT_INVALID_TARGET: c_int = -3;
|
||||
pub const EMSCRIPTEN_RESULT_UNKNOWN_TARGET: c_int = -4;
|
||||
pub const EMSCRIPTEN_RESULT_INVALID_PARAM: c_int = -5;
|
||||
pub const EMSCRIPTEN_RESULT_FAILED: c_int = -6;
|
||||
pub const EMSCRIPTEN_RESULT_NO_DATA: c_int = -7;
|
||||
|
||||
// values for EMSCRIPTEN EVENT
|
||||
pub const EMSCRIPTEN_EVENT_KEYPRESS: c_int = 1;
|
||||
pub const EMSCRIPTEN_EVENT_KEYDOWN: c_int = 2;
|
||||
pub const EMSCRIPTEN_EVENT_KEYUP: c_int = 3;
|
||||
pub const EMSCRIPTEN_EVENT_CLICK: c_int = 4;
|
||||
pub const EMSCRIPTEN_EVENT_MOUSEDOWN: c_int = 5;
|
||||
pub const EMSCRIPTEN_EVENT_MOUSEUP: c_int = 6;
|
||||
pub const EMSCRIPTEN_EVENT_DBLCLICK: c_int = 7;
|
||||
pub const EMSCRIPTEN_EVENT_MOUSEMOVE: c_int = 8;
|
||||
pub const EMSCRIPTEN_EVENT_WHEEL: c_int = 9;
|
||||
pub const EMSCRIPTEN_EVENT_RESIZE: c_int = 10;
|
||||
pub const EMSCRIPTEN_EVENT_SCROLL: c_int = 11;
|
||||
pub const EMSCRIPTEN_EVENT_BLUR: c_int = 12;
|
||||
pub const EMSCRIPTEN_EVENT_FOCUS: c_int = 13;
|
||||
pub const EMSCRIPTEN_EVENT_FOCUSIN: c_int = 14;
|
||||
pub const EMSCRIPTEN_EVENT_FOCUSOUT: c_int = 15;
|
||||
pub const EMSCRIPTEN_EVENT_DEVICEORIENTATION: c_int = 16;
|
||||
pub const EMSCRIPTEN_EVENT_DEVICEMOTION: c_int = 17;
|
||||
pub const EMSCRIPTEN_EVENT_ORIENTATIONCHANGE: c_int = 18;
|
||||
pub const EMSCRIPTEN_EVENT_FULLSCREENCHANGE: c_int = 19;
|
||||
pub const EMSCRIPTEN_EVENT_POINTERLOCKCHANGE: c_int = 20;
|
||||
pub const EMSCRIPTEN_EVENT_VISIBILITYCHANGE: c_int = 21;
|
||||
pub const EMSCRIPTEN_EVENT_TOUCHSTART: c_int = 22;
|
||||
pub const EMSCRIPTEN_EVENT_TOUCHEND: c_int = 23;
|
||||
pub const EMSCRIPTEN_EVENT_TOUCHMOVE: c_int = 24;
|
||||
pub const EMSCRIPTEN_EVENT_TOUCHCANCEL: c_int = 25;
|
||||
pub const EMSCRIPTEN_EVENT_GAMEPADCONNECTED: c_int = 26;
|
||||
pub const EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED: c_int = 27;
|
||||
pub const EMSCRIPTEN_EVENT_BEFOREUNLOAD: c_int = 28;
|
||||
pub const EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE: c_int = 29;
|
||||
pub const EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE: c_int = 30;
|
||||
pub const EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST: c_int = 31;
|
||||
pub const EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED: c_int = 32;
|
||||
pub const EMSCRIPTEN_EVENT_MOUSEENTER: c_int = 33;
|
||||
pub const EMSCRIPTEN_EVENT_MOUSELEAVE: c_int = 34;
|
||||
pub const EMSCRIPTEN_EVENT_MOUSEOVER: c_int = 35;
|
||||
pub const EMSCRIPTEN_EVENT_MOUSEOUT: c_int = 36;
|
||||
pub const EMSCRIPTEN_EVENT_CANVASRESIZED: c_int = 37;
|
||||
pub const EMSCRIPTEN_EVENT_POINTERLOCKERROR: c_int = 38;
|
||||
|
||||
pub const EM_HTML5_SHORT_STRING_LEN_BYTES: usize = 32;
|
||||
|
||||
pub const DOM_KEY_LOCATION_STANDARD: c_ulong = 0x00;
|
||||
pub const DOM_KEY_LOCATION_LEFT: c_ulong = 0x01;
|
||||
pub const DOM_KEY_LOCATION_RIGHT: c_ulong = 0x02;
|
||||
pub const DOM_KEY_LOCATION_NUMPAD: c_ulong = 0x03;
|
||||
|
||||
pub type em_callback_func = Option<unsafe extern "C" fn()>;
|
||||
|
||||
pub type em_key_callback_func = Option<unsafe extern "C" fn(
|
||||
eventType: c_int,
|
||||
keyEvent: *const EmscriptenKeyboardEvent,
|
||||
userData: *mut c_void) -> EM_BOOL>;
|
||||
|
||||
pub type em_mouse_callback_func = Option<unsafe extern "C" fn(
|
||||
eventType: c_int,
|
||||
mouseEvent: *const EmscriptenMouseEvent,
|
||||
userData: *mut c_void) -> EM_BOOL>;
|
||||
|
||||
pub type em_pointerlockchange_callback_func = Option<unsafe extern "C" fn(
|
||||
eventType: c_int,
|
||||
pointerlockChangeEvent: *const EmscriptenPointerlockChangeEvent,
|
||||
userData: *mut c_void) -> EM_BOOL>;
|
||||
|
||||
pub type em_fullscreenchange_callback_func = Option<unsafe extern "C" fn(
|
||||
eventType: c_int,
|
||||
fullscreenChangeEvent: *const EmscriptenFullscreenChangeEvent,
|
||||
userData: *mut c_void) -> EM_BOOL>;
|
||||
|
||||
pub type em_touch_callback_func = Option<unsafe extern "C" fn(
|
||||
eventType: c_int,
|
||||
touchEvent: *const EmscriptenTouchEvent,
|
||||
userData: *mut c_void) -> EM_BOOL>;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct EmscriptenFullscreenChangeEvent {
|
||||
pub isFullscreen: c_int,
|
||||
pub fullscreenEnabled: c_int,
|
||||
pub nodeName: [c_char; 128usize],
|
||||
pub id: [c_char; 128usize],
|
||||
pub elementWidth: c_int,
|
||||
pub elementHeight: c_int,
|
||||
pub screenWidth: c_int,
|
||||
pub screenHeight: c_int,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_EmscriptenFullscreenChangeEvent() {
|
||||
assert_eq!(mem::size_of::<EmscriptenFullscreenChangeEvent>(), 280usize);
|
||||
assert_eq!(mem::align_of::<EmscriptenFullscreenChangeEvent>(), 4usize);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy)]
|
||||
pub struct EmscriptenKeyboardEvent {
|
||||
pub key: [c_char; 32usize],
|
||||
pub code: [c_char; 32usize],
|
||||
pub location: c_ulong,
|
||||
pub ctrlKey: c_int,
|
||||
pub shiftKey: c_int,
|
||||
pub altKey: c_int,
|
||||
pub metaKey: c_int,
|
||||
pub repeat: c_int,
|
||||
pub locale: [c_char; 32usize],
|
||||
pub charValue: [c_char; 32usize],
|
||||
pub charCode: c_ulong,
|
||||
pub keyCode: c_ulong,
|
||||
pub which: c_ulong,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_EmscriptenKeyboardEvent() {
|
||||
assert_eq!(mem::size_of::<EmscriptenKeyboardEvent>(), 184usize);
|
||||
assert_eq!(mem::align_of::<EmscriptenKeyboardEvent>(), 8usize);
|
||||
}
|
||||
impl Clone for EmscriptenKeyboardEvent {
|
||||
fn clone(&self) -> Self { *self }
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct EmscriptenMouseEvent {
|
||||
pub timestamp: f64,
|
||||
pub screenX: c_long,
|
||||
pub screenY: c_long,
|
||||
pub clientX: c_long,
|
||||
pub clientY: c_long,
|
||||
pub ctrlKey: c_int,
|
||||
pub shiftKey: c_int,
|
||||
pub altKey: c_int,
|
||||
pub metaKey: c_int,
|
||||
pub button: c_ushort,
|
||||
pub buttons: c_ushort,
|
||||
pub movementX: c_long,
|
||||
pub movementY: c_long,
|
||||
pub targetX: c_long,
|
||||
pub targetY: c_long,
|
||||
pub canvasX: c_long,
|
||||
pub canvasY: c_long,
|
||||
pub padding: c_long,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_EmscriptenMouseEvent() {
|
||||
assert_eq!(mem::size_of::<EmscriptenMouseEvent>(), 120usize);
|
||||
assert_eq!(mem::align_of::<EmscriptenMouseEvent>(), 8usize);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct EmscriptenTouchPoint {
|
||||
pub identifier: c_long,
|
||||
pub screenX: c_long,
|
||||
pub screenY: c_long,
|
||||
pub clientX: c_long,
|
||||
pub clientY: c_long,
|
||||
pub pageX: c_long,
|
||||
pub pageY: c_long,
|
||||
pub isChanged: c_int,
|
||||
pub onTarget: c_int,
|
||||
pub targetX: c_long,
|
||||
pub targetY: c_long,
|
||||
pub canvasX: c_long,
|
||||
pub canvasY: c_long,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_EmscriptenTouchPoint() {
|
||||
assert_eq!(mem::size_of::<EmscriptenTouchPoint>(), 96usize);
|
||||
assert_eq!(mem::align_of::<EmscriptenTouchPoint>(), 8usize);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct EmscriptenTouchEvent {
|
||||
pub numTouches: c_int,
|
||||
pub ctrlKey: c_int,
|
||||
pub shiftKey: c_int,
|
||||
pub altKey: c_int,
|
||||
pub metaKey: c_int,
|
||||
pub touches: [EmscriptenTouchPoint; 32usize],
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_EmscriptenTouchEvent() {
|
||||
assert_eq!(mem::size_of::<EmscriptenTouchEvent>(), 3096usize);
|
||||
assert_eq!(mem::align_of::<EmscriptenTouchEvent>(), 8usize);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct EmscriptenPointerlockChangeEvent {
|
||||
pub isActive: c_int,
|
||||
pub nodeName: [c_char; 128usize],
|
||||
pub id: [c_char; 128usize],
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_EmscriptenPointerlockChangeEvent() {
|
||||
assert_eq!(mem::size_of::<EmscriptenPointerlockChangeEvent>(), 260usize);
|
||||
assert_eq!(mem::align_of::<EmscriptenPointerlockChangeEvent>(), 4usize);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
pub fn emscripten_set_canvas_size(
|
||||
width: c_int, height: c_int)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_get_canvas_size(
|
||||
width: *mut c_int, height: *mut c_int,
|
||||
is_fullscreen: *mut c_int)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_element_css_size(
|
||||
target: *const c_char, width: c_double,
|
||||
height: c_double) -> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_get_element_css_size(
|
||||
target: *const c_char, width: *mut c_double,
|
||||
height: *mut c_double) -> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_request_pointerlock(
|
||||
target: *const c_char, deferUntilInEventHandler: EM_BOOL)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_exit_pointerlock() -> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_request_fullscreen(
|
||||
target: *const c_char, deferUntilInEventHandler: EM_BOOL)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_exit_fullscreen() -> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_keydown_callback(
|
||||
target: *const c_char, userData: *mut c_void,
|
||||
useCapture: EM_BOOL, callback: em_key_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_keyup_callback(
|
||||
target: *const c_char, userData: *mut c_void,
|
||||
useCapture: EM_BOOL, callback: em_key_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_mousemove_callback(
|
||||
target: *const c_char, user_data: *mut c_void,
|
||||
use_capture: EM_BOOL, callback: em_mouse_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_mousedown_callback(
|
||||
target: *const c_char, user_data: *mut c_void,
|
||||
use_capture: EM_BOOL, callback: em_mouse_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_mouseup_callback(
|
||||
target: *const c_char, user_data: *mut c_void,
|
||||
use_capture: EM_BOOL, callback: em_mouse_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_hide_mouse();
|
||||
|
||||
pub fn emscripten_get_device_pixel_ratio() -> f64;
|
||||
|
||||
pub fn emscripten_set_pointerlockchange_callback(
|
||||
target: *const c_char, userData: *mut c_void, useCapture: EM_BOOL,
|
||||
callback: em_pointerlockchange_callback_func) -> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_fullscreenchange_callback(
|
||||
target: *const c_char, userData: *mut c_void, useCapture: EM_BOOL,
|
||||
callback: em_fullscreenchange_callback_func) -> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_asm_const(code: *const c_char);
|
||||
|
||||
pub fn emscripten_set_main_loop(
|
||||
func: em_callback_func, fps: c_int, simulate_infinite_loop: EM_BOOL);
|
||||
|
||||
pub fn emscripten_cancel_main_loop();
|
||||
|
||||
pub fn emscripten_set_touchstart_callback(
|
||||
target: *const c_char, userData: *mut c_void,
|
||||
useCapture: c_int, callback: em_touch_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_touchend_callback(
|
||||
target: *const c_char, userData: *mut c_void,
|
||||
useCapture: c_int, callback: em_touch_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_touchmove_callback(
|
||||
target: *const c_char, userData: *mut c_void,
|
||||
useCapture: c_int, callback: em_touch_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_touchcancel_callback(
|
||||
target: *const c_char, userData: *mut c_void,
|
||||
useCapture: c_int, callback: em_touch_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
}
|
||||
1058
src/platform/emscripten/mod.rs
Normal file
1058
src/platform/emscripten/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -66,7 +66,7 @@ extern {
|
||||
pub fn longjmp(env: *mut libc::c_void, val: libc::c_int);
|
||||
}
|
||||
|
||||
pub trait NSString {
|
||||
pub trait NSString: Sized {
|
||||
unsafe fn alloc(_: Self) -> id {
|
||||
msg_send![class("NSString"), alloc]
|
||||
}
|
||||
|
||||
@@ -51,12 +51,12 @@
|
||||
//! - applicationWillResignActive is Focused(false)
|
||||
//! - applicationDidEnterBackground is Suspended(true)
|
||||
//! - applicationWillEnterForeground is Suspended(false)
|
||||
//! - applicationWillTerminate is Closed
|
||||
//! - applicationWillTerminate is Destroyed
|
||||
//!
|
||||
//! Keep in mind that after Closed event is received every attempt to draw with
|
||||
//! Keep in mind that after Destroyed event is received every attempt to draw with
|
||||
//! opengl will result in segfault.
|
||||
//!
|
||||
//! Also note that app will not receive Closed event if suspended, it will be SIGKILL'ed
|
||||
//! Also note that app will not receive Destroyed event if suspended, it will be SIGKILL'ed
|
||||
|
||||
#![cfg(target_os = "ios")]
|
||||
|
||||
@@ -70,10 +70,12 @@ use libc::c_int;
|
||||
use objc::runtime::{Class, Object, Sel, BOOL, YES };
|
||||
use objc::declare::{ ClassDecl };
|
||||
|
||||
use native_monitor::NativeMonitorId;
|
||||
use { CreationError, CursorState, MouseCursor, WindowAttributes };
|
||||
use WindowEvent as Event;
|
||||
use WindowId as RootEventId;
|
||||
use WindowEvent;
|
||||
use Event;
|
||||
use events::{ Touch, TouchPhase };
|
||||
use window::MonitorId as RootMonitorId;
|
||||
|
||||
mod ffi;
|
||||
use self::ffi::{
|
||||
@@ -104,14 +106,6 @@ pub struct Window {
|
||||
#[derive(Clone)]
|
||||
pub struct WindowProxy;
|
||||
|
||||
pub struct PollEventsIterator<'a> {
|
||||
window: &'a Window,
|
||||
}
|
||||
|
||||
pub struct WaitEventsIterator<'a> {
|
||||
window: &'a Window,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DelegateState {
|
||||
events_queue: VecDeque<Event>,
|
||||
@@ -135,44 +129,38 @@ impl DelegateState {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors() -> VecDeque<MonitorId> {
|
||||
let mut rb = VecDeque::new();
|
||||
rb.push_back(MonitorId);
|
||||
rb
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor() -> MonitorId {
|
||||
MonitorId
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
#[inline]
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
Some("Primary".to_string())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> NativeMonitorId {
|
||||
NativeMonitorId::Unavailable
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
// iOS assumes single screen
|
||||
(0, 0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
1.0
|
||||
}
|
||||
}
|
||||
|
||||
gen_api_transition!();
|
||||
pub struct EventsLoop {
|
||||
delegate_state: *mut DelegateState
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes;
|
||||
#[derive(Clone)]
|
||||
pub struct EventsLoopProxy;
|
||||
|
||||
impl Window {
|
||||
|
||||
pub fn new(_: &WindowAttributes, _: &PlatformSpecificWindowBuilderAttributes) -> Result<Window, CreationError>
|
||||
{
|
||||
impl EventsLoop {
|
||||
pub fn new() -> EventsLoop {
|
||||
unsafe {
|
||||
if setjmp(mem::transmute(&mut jmpbuf)) != 0 {
|
||||
let app: id = msg_send![Class::get("UIApplication").unwrap(), sharedApplication];
|
||||
@@ -180,187 +168,109 @@ impl Window {
|
||||
let state: *mut c_void = *(&*delegate).get_ivar("glutinState");
|
||||
let state = state as *mut DelegateState;
|
||||
|
||||
let window = Window {
|
||||
let events_loop = EventsLoop {
|
||||
delegate_state: state
|
||||
};
|
||||
|
||||
return Ok(window)
|
||||
return events_loop;
|
||||
}
|
||||
}
|
||||
|
||||
Window::create_delegate_class();
|
||||
Window::create_view_class();
|
||||
Window::start_app();
|
||||
create_delegate_class();
|
||||
create_view_class();
|
||||
start_app();
|
||||
|
||||
Err(CreationError::OsError(format!("Couldn't create UIApplication")))
|
||||
}
|
||||
|
||||
fn create_delegate_class() {
|
||||
extern fn did_finish_launching(this: &mut Object, _: Sel, _: id, _: id) -> BOOL {
|
||||
unsafe {
|
||||
let main_screen: id = msg_send![Class::get("UIScreen").unwrap(), 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, initWithFrame:bounds.clone()];
|
||||
|
||||
let size = (bounds.size.width as u32, bounds.size.height as u32);
|
||||
|
||||
let view_controller: id = msg_send![Class::get("MainViewController").unwrap(), 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_ptr: *mut DelegateState = mem::transmute(state);
|
||||
this.set_ivar("glutinState", state_ptr as *mut c_void);
|
||||
|
||||
|
||||
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); }
|
||||
}
|
||||
|
||||
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::Focused(true));
|
||||
}
|
||||
}
|
||||
|
||||
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::Focused(false));
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
// push event to the front to garantee that we'll process it
|
||||
// immidiatly after jump
|
||||
state.events_queue.push_front(Event::Closed);
|
||||
longjmp(mem::transmute(&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 touches_enum: id = msg_send![touches, objectEnumerator];
|
||||
|
||||
loop {
|
||||
let touch: id = msg_send![touches_enum, nextObject];
|
||||
if touch == nil {
|
||||
break
|
||||
}
|
||||
let location: CGPoint = msg_send![touch, locationInView:nil];
|
||||
let touch_id = touch as u64;
|
||||
let phase: i32 = msg_send![touch, phase];
|
||||
|
||||
state.events_queue.push_back(Event::Touch(Touch {
|
||||
id: touch_id,
|
||||
location: (location.x as f64, location.y as f64),
|
||||
phase: match phase {
|
||||
0 => TouchPhase::Started,
|
||||
1 => TouchPhase::Moved,
|
||||
// 2 is UITouchPhaseStationary and is not expected here
|
||||
3 => TouchPhase::Ended,
|
||||
4 => TouchPhase::Cancelled,
|
||||
_ => panic!("unexpected touch phase: {:?}", phase)
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ui_responder = Class::get("UIResponder").unwrap();
|
||||
let mut decl = ClassDecl::new("AppDelegate", ui_responder).unwrap();
|
||||
|
||||
unsafe {
|
||||
decl.add_method(sel!(application:didFinishLaunchingWithOptions:),
|
||||
did_finish_launching as extern fn(&mut Object, Sel, id, id) -> BOOL);
|
||||
|
||||
decl.add_method(sel!(applicationDidBecomeActive:),
|
||||
did_become_active as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_method(sel!(applicationWillResignActive:),
|
||||
will_resign_active as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_method(sel!(applicationWillEnterForeground:),
|
||||
will_enter_foreground as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_method(sel!(applicationDidEnterBackground:),
|
||||
did_enter_background as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_method(sel!(applicationWillTerminate:),
|
||||
will_terminate as extern fn(&Object, Sel, id));
|
||||
|
||||
|
||||
decl.add_method(sel!(touchesBegan:withEvent:),
|
||||
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
|
||||
|
||||
decl.add_method(sel!(touchesMoved:withEvent:),
|
||||
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
|
||||
|
||||
decl.add_method(sel!(touchesEnded:withEvent:),
|
||||
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
|
||||
|
||||
decl.add_method(sel!(touchesCancelled:withEvent:),
|
||||
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
|
||||
|
||||
|
||||
decl.add_method(sel!(postLaunch:),
|
||||
post_launch as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_ivar::<*mut c_void>("glutinState");
|
||||
|
||||
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();
|
||||
panic!("Couldn't create UIApplication")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn start_app() {
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
let mut rb = VecDeque::new();
|
||||
rb.push_back(MonitorId);
|
||||
rb
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
MonitorId
|
||||
}
|
||||
|
||||
pub fn poll_events<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
unsafe {
|
||||
UIApplicationMain(0, ptr::null(), nil, NSString::alloc(nil).init_str("AppDelegate"));
|
||||
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() {
|
||||
callback(event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// run runloop
|
||||
let seconds: CFTimeInterval = 0.000002;
|
||||
while CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, 1) == kCFRunLoopRunHandledSource {}
|
||||
|
||||
if let Some(event) = state.events_queue.pop_front() {
|
||||
callback(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(::Event) -> ::ControlFlow,
|
||||
{
|
||||
// Yeah that's a very bad implementation.
|
||||
loop {
|
||||
let mut control_flow = ::ControlFlow::Continue;
|
||||
self.poll_events(|e| {
|
||||
if let ::ControlFlow::Break = callback(e) {
|
||||
control_flow = ::ControlFlow::Break;
|
||||
}
|
||||
});
|
||||
if let ::ControlFlow::Break = control_flow {
|
||||
break;
|
||||
}
|
||||
::std::thread::sleep(::std::time::Duration::from_millis(5));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventsLoopProxy {
|
||||
EventsLoopProxy
|
||||
}
|
||||
}
|
||||
|
||||
impl EventsLoopProxy {
|
||||
pub fn wakeup(&self) -> Result<(), ::EventsLoopClosed> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes;
|
||||
|
||||
impl Window {
|
||||
pub fn new(ev: &EventsLoop, _: &WindowAttributes, _: &PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window, CreationError>
|
||||
{
|
||||
Ok(Window {
|
||||
delegate_state: ev.delegate_state,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_title(&self, _: &str) {
|
||||
}
|
||||
@@ -378,6 +288,11 @@ impl Window {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_position(&self, _x: i32, _y: i32) {
|
||||
}
|
||||
@@ -397,18 +312,10 @@ impl Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn poll_events(&self) -> PollEventsIterator {
|
||||
PollEventsIterator {
|
||||
window: self
|
||||
}
|
||||
}
|
||||
pub fn set_min_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn wait_events(&self) -> WaitEventsIterator {
|
||||
WaitEventsIterator {
|
||||
window: self
|
||||
}
|
||||
}
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
@@ -448,50 +355,210 @@ impl Window {
|
||||
WindowProxy
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl WindowProxy {
|
||||
#[inline]
|
||||
pub fn wakeup_event_loop(&self) {
|
||||
unimplemented!()
|
||||
pub fn set_maximized(&self, _maximized: bool) {
|
||||
// iOS has single screen maximized apps so nothing to do
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) {
|
||||
// iOS has single screen maximized apps so nothing to do
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, _decorations: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
RootMonitorId{inner: MonitorId}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a> Iterator for WaitEventsIterator<'a> {
|
||||
type Item = Event;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Event> {
|
||||
loop {
|
||||
if let Some(ev) = self.window.poll_events().next() {
|
||||
return Some(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for PollEventsIterator<'a> {
|
||||
type Item = Event;
|
||||
|
||||
fn next(&mut self) -> Option<Event> {
|
||||
fn create_delegate_class() {
|
||||
extern fn did_finish_launching(this: &mut Object, _: Sel, _: id, _: id) -> BOOL {
|
||||
unsafe {
|
||||
let state = &mut *self.window.delegate_state;
|
||||
let main_screen: id = msg_send![Class::get("UIScreen").unwrap(), mainScreen];
|
||||
let bounds: CGRect = msg_send![main_screen, bounds];
|
||||
let scale: CGFloat = msg_send![main_screen, nativeScale];
|
||||
|
||||
if let Some(event) = state.events_queue.pop_front() {
|
||||
return Some(event)
|
||||
}
|
||||
let window: id = msg_send![Class::get("UIWindow").unwrap(), alloc];
|
||||
let window: id = msg_send![window, initWithFrame:bounds.clone()];
|
||||
|
||||
// jump hack, so we won't quit on willTerminate event before processing it
|
||||
if setjmp(mem::transmute(&mut jmpbuf)) != 0 {
|
||||
return state.events_queue.pop_front()
|
||||
}
|
||||
let size = (bounds.size.width as u32, bounds.size.height as u32);
|
||||
|
||||
// run runloop
|
||||
let seconds: CFTimeInterval = 0.000002;
|
||||
while CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, 1) == kCFRunLoopRunHandledSource {}
|
||||
let view_controller: id = msg_send![Class::get("MainViewController").unwrap(), alloc];
|
||||
let view_controller: id = msg_send![view_controller, init];
|
||||
|
||||
state.events_queue.pop_front()
|
||||
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_ptr: *mut DelegateState = mem::transmute(state);
|
||||
this.set_ivar("glutinState", state_ptr as *mut c_void);
|
||||
|
||||
|
||||
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); }
|
||||
}
|
||||
|
||||
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 {
|
||||
window_id: RootEventId(WindowId),
|
||||
event: WindowEvent::Focused(true),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
window_id: RootEventId(WindowId),
|
||||
event: WindowEvent::Focused(false),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
// push event to the front to garantee that we'll process it
|
||||
// immidiatly after jump
|
||||
state.events_queue.push_front(Event::WindowEvent {
|
||||
window_id: RootEventId(WindowId),
|
||||
event: WindowEvent::Destroyed,
|
||||
});
|
||||
longjmp(mem::transmute(&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 touches_enum: id = msg_send![touches, objectEnumerator];
|
||||
|
||||
loop {
|
||||
let touch: id = msg_send![touches_enum, nextObject];
|
||||
if touch == nil {
|
||||
break
|
||||
}
|
||||
let location: CGPoint = msg_send![touch, locationInView:nil];
|
||||
let touch_id = touch as u64;
|
||||
let phase: i32 = msg_send![touch, phase];
|
||||
|
||||
state.events_queue.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),
|
||||
phase: match phase {
|
||||
0 => TouchPhase::Started,
|
||||
1 => TouchPhase::Moved,
|
||||
// 2 is UITouchPhaseStationary and is not expected here
|
||||
3 => TouchPhase::Ended,
|
||||
4 => TouchPhase::Cancelled,
|
||||
_ => panic!("unexpected touch phase: {:?}", phase)
|
||||
}
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ui_responder = Class::get("UIResponder").unwrap();
|
||||
let mut decl = ClassDecl::new("AppDelegate", ui_responder).unwrap();
|
||||
|
||||
unsafe {
|
||||
decl.add_method(sel!(application:didFinishLaunchingWithOptions:),
|
||||
did_finish_launching as extern fn(&mut Object, Sel, id, id) -> BOOL);
|
||||
|
||||
decl.add_method(sel!(applicationDidBecomeActive:),
|
||||
did_become_active as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_method(sel!(applicationWillResignActive:),
|
||||
will_resign_active as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_method(sel!(applicationWillEnterForeground:),
|
||||
will_enter_foreground as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_method(sel!(applicationDidEnterBackground:),
|
||||
did_enter_background as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_method(sel!(applicationWillTerminate:),
|
||||
will_terminate as extern fn(&Object, Sel, id));
|
||||
|
||||
|
||||
decl.add_method(sel!(touchesBegan:withEvent:),
|
||||
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
|
||||
|
||||
decl.add_method(sel!(touchesMoved:withEvent:),
|
||||
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
|
||||
|
||||
decl.add_method(sel!(touchesEnded:withEvent:),
|
||||
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
|
||||
|
||||
decl.add_method(sel!(touchesCancelled:withEvent:),
|
||||
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
|
||||
|
||||
|
||||
decl.add_method(sel!(postLaunch:),
|
||||
post_launch as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_ivar::<*mut c_void>("glutinState");
|
||||
|
||||
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 {
|
||||
UIApplicationMain(0, ptr::null(), nil, NSString::alloc(nil).init_str("AppDelegate"));
|
||||
}
|
||||
}
|
||||
|
||||
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
||||
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
|
||||
|
||||
@@ -2,94 +2,64 @@
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Arc;
|
||||
use std::env;
|
||||
|
||||
use CreationError;
|
||||
use CursorState;
|
||||
use MouseCursor;
|
||||
use {CreationError, CursorState, EventsLoopClosed, MouseCursor, ControlFlow};
|
||||
use libc;
|
||||
|
||||
use self::x11::XConnection;
|
||||
use self::x11::XError;
|
||||
use self::x11::XNotSupported;
|
||||
use self::x11::ffi::XVisualInfo;
|
||||
|
||||
pub use self::x11::XNotSupported;
|
||||
use window::MonitorId as RootMonitorId;
|
||||
|
||||
mod dlopen;
|
||||
pub mod wayland;
|
||||
pub mod x11;
|
||||
|
||||
/// Environment variable specifying which backend should be used on unix platform.
|
||||
///
|
||||
/// Legal values are x11 and wayland. If this variable is set only the named backend
|
||||
/// will be tried by winit. If it is not set, winit will try to connect to a wayland connection,
|
||||
/// and if it fails will fallback on x11.
|
||||
///
|
||||
/// If this variable is set with any other value, winit will panic.
|
||||
const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND";
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub visual_infos: Option<XVisualInfo>,
|
||||
pub screen_id: Option<i32>,
|
||||
}
|
||||
|
||||
pub enum UnixBackend {
|
||||
X(Arc<XConnection>),
|
||||
Wayland(Arc<wayland::WaylandContext>),
|
||||
Error(XNotSupported),
|
||||
}
|
||||
|
||||
lazy_static!(
|
||||
pub static ref UNIX_BACKEND: UnixBackend = {
|
||||
if let Some(ctxt) = wayland::WaylandContext::init() {
|
||||
UnixBackend::Wayland(Arc::new(ctxt))
|
||||
} else {
|
||||
match XConnection::new(Some(x_error_callback)) {
|
||||
Ok(x) => UnixBackend::X(Arc::new(x)),
|
||||
Err(e) => UnixBackend::Error(e),
|
||||
}
|
||||
}
|
||||
pub static ref X11_BACKEND: Result<Arc<XConnection>, XNotSupported> = {
|
||||
XConnection::new(Some(x_error_callback)).map(Arc::new)
|
||||
};
|
||||
);
|
||||
|
||||
|
||||
pub enum Window2 {
|
||||
#[doc(hidden)]
|
||||
X(x11::Window2),
|
||||
#[doc(hidden)]
|
||||
pub enum Window {
|
||||
X(x11::Window),
|
||||
Wayland(wayland::Window)
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum WindowId {
|
||||
#[doc(hidden)]
|
||||
X(x11::WindowId),
|
||||
#[doc(hidden)]
|
||||
Wayland(wayland::WindowId)
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum DeviceId {
|
||||
X(x11::DeviceId),
|
||||
Wayland(wayland::DeviceId)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum MonitorId {
|
||||
#[doc(hidden)]
|
||||
X(x11::MonitorId),
|
||||
#[doc(hidden)]
|
||||
Wayland(wayland::MonitorId),
|
||||
#[doc(hidden)]
|
||||
None,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors() -> VecDeque<MonitorId> {
|
||||
match *UNIX_BACKEND {
|
||||
UnixBackend::Wayland(ref ctxt) => wayland::get_available_monitors(ctxt)
|
||||
.into_iter()
|
||||
.map(MonitorId::Wayland)
|
||||
.collect(),
|
||||
UnixBackend::X(ref connec) => x11::get_available_monitors(connec)
|
||||
.into_iter()
|
||||
.map(MonitorId::X)
|
||||
.collect(),
|
||||
UnixBackend::Error(_) => { let mut d = VecDeque::new(); d.push_back(MonitorId::None); d},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor() -> MonitorId {
|
||||
match *UNIX_BACKEND {
|
||||
UnixBackend::Wayland(ref ctxt) => MonitorId::Wayland(wayland::get_primary_monitor(ctxt)),
|
||||
UnixBackend::X(ref connec) => MonitorId::X(x11::get_primary_monitor(connec)),
|
||||
UnixBackend::Error(_) => MonitorId::None,
|
||||
}
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
@@ -98,16 +68,14 @@ impl MonitorId {
|
||||
match self {
|
||||
&MonitorId::X(ref m) => m.get_name(),
|
||||
&MonitorId::Wayland(ref m) => m.get_name(),
|
||||
&MonitorId::None => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> ::native_monitor::NativeMonitorId {
|
||||
pub fn get_native_identifier(&self) -> u32 {
|
||||
match self {
|
||||
&MonitorId::X(ref m) => m.get_native_identifier(),
|
||||
&MonitorId::Wayland(ref m) => m.get_native_identifier(),
|
||||
&MonitorId::None => unimplemented!() // FIXME:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,139 +84,169 @@ impl MonitorId {
|
||||
match self {
|
||||
&MonitorId::X(ref m) => m.get_dimensions(),
|
||||
&MonitorId::Wayland(ref m) => m.get_dimensions(),
|
||||
&MonitorId::None => (800, 600), // FIXME:
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
match self {
|
||||
&MonitorId::X(ref m) => m.get_position(),
|
||||
&MonitorId::Wayland(ref m) => m.get_position(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
match self {
|
||||
&MonitorId::X(ref m) => m.get_hidpi_factor(),
|
||||
&MonitorId::Wayland(ref m) => m.get_hidpi_factor(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Window2 {
|
||||
impl Window {
|
||||
#[inline]
|
||||
pub fn new(events_loop: ::std::sync::Arc<EventsLoop>, window: &::WindowAttributes,
|
||||
pub fn new(events_loop: &EventsLoop,
|
||||
window: &::WindowAttributes,
|
||||
pl_attribs: &PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window2, CreationError>
|
||||
-> Result<Self, CreationError>
|
||||
{
|
||||
match *UNIX_BACKEND {
|
||||
UnixBackend::Wayland(ref ctxt) => {
|
||||
if let EventsLoop::Wayland(ref evlp) = *events_loop {
|
||||
wayland::Window::new(evlp, ctxt.clone(), window).map(Window2::Wayland)
|
||||
} else {
|
||||
// It is not possible to instanciate an EventsLoop not matching its backend
|
||||
unreachable!()
|
||||
}
|
||||
match *events_loop {
|
||||
EventsLoop::Wayland(ref evlp) => {
|
||||
wayland::Window::new(evlp, window).map(Window::Wayland)
|
||||
},
|
||||
|
||||
UnixBackend::X(ref connec) => {
|
||||
x11::Window2::new(events_loop, connec, window, pl_attribs).map(Window2::X)
|
||||
EventsLoop::X(ref el) => {
|
||||
x11::Window::new(el, window, pl_attribs).map(Window::X)
|
||||
},
|
||||
UnixBackend::Error(_) => {
|
||||
// If the Backend is Error(), it is not possible to instanciate an EventsLoop at all,
|
||||
// thus this function cannot be called!
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
match self {
|
||||
&Window2::X(ref w) => WindowId::X(w.id()),
|
||||
&Window2::Wayland(ref w) => WindowId::Wayland(w.id())
|
||||
&Window::X(ref w) => WindowId::X(w.id()),
|
||||
&Window::Wayland(ref w) => WindowId::Wayland(w.id())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_title(&self, title: &str) {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.set_title(title),
|
||||
&Window2::Wayland(ref w) => w.set_title(title)
|
||||
&Window::X(ref w) => w.set_title(title),
|
||||
&Window::Wayland(ref w) => w.set_title(title)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show(&self) {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.show(),
|
||||
&Window2::Wayland(ref w) => w.show()
|
||||
&Window::X(ref w) => w.show(),
|
||||
&Window::Wayland(ref w) => w.show()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide(&self) {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.hide(),
|
||||
&Window2::Wayland(ref w) => w.hide()
|
||||
&Window::X(ref w) => w.hide(),
|
||||
&Window::Wayland(ref w) => w.hide()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.get_position(),
|
||||
&Window2::Wayland(ref w) => w.get_position()
|
||||
&Window::X(ref w) => w.get_position(),
|
||||
&Window::Wayland(ref w) => w.get_position()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
match self {
|
||||
&Window::X(ref m) => m.get_inner_position(),
|
||||
&Window::Wayland(ref m) => m.get_inner_position(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_position(&self, x: i32, y: i32) {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.set_position(x, y),
|
||||
&Window2::Wayland(ref w) => w.set_position(x, y)
|
||||
&Window::X(ref w) => w.set_position(x, y),
|
||||
&Window::Wayland(ref w) => w.set_position(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.get_inner_size(),
|
||||
&Window2::Wayland(ref w) => w.get_inner_size()
|
||||
&Window::X(ref w) => w.get_inner_size(),
|
||||
&Window::Wayland(ref w) => w.get_inner_size()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.get_outer_size(),
|
||||
&Window2::Wayland(ref w) => w.get_outer_size()
|
||||
&Window::X(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) {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.set_inner_size(x, y),
|
||||
&Window2::Wayland(ref w) => w.set_inner_size(x, y)
|
||||
&Window::X(ref w) => w.set_inner_size(x, y),
|
||||
&Window::Wayland(ref w) => w.set_inner_size(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
match self {
|
||||
&Window::X(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)>) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_max_dimensions(dimensions),
|
||||
&Window::Wayland(ref w) => w.set_max_dimensions(dimensions)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.set_cursor(cursor),
|
||||
&Window2::Wayland(ref w) => w.set_cursor(cursor)
|
||||
&Window::X(ref w) => w.set_cursor(cursor),
|
||||
&Window::Wayland(ref w) => w.set_cursor(cursor)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.set_cursor_state(state),
|
||||
&Window2::Wayland(ref w) => w.set_cursor_state(state)
|
||||
&Window::X(ref w) => w.set_cursor_state(state),
|
||||
&Window::Wayland(ref w) => w.set_cursor_state(state)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.hidpi_factor(),
|
||||
&Window2::Wayland(ref w) => w.hidpi_factor()
|
||||
&Window::X(ref w) => w.hidpi_factor(),
|
||||
&Window::Wayland(ref w) => w.hidpi_factor()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.set_cursor_position(x, y),
|
||||
&Window2::Wayland(ref w) => w.set_cursor_position(x, y)
|
||||
&Window::X(ref w) => w.set_cursor_position(x, y),
|
||||
&Window::Wayland(ref w) => w.set_cursor_position(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,8 +254,8 @@ impl Window2 {
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
use wayland_client::Proxy;
|
||||
match self {
|
||||
&Window2::X(ref w) => w.platform_display(),
|
||||
&Window2::Wayland(ref w) => w.get_display().ptr() as *mut _
|
||||
&Window::X(ref w) => w.platform_display(),
|
||||
&Window::Wayland(ref w) => w.get_display().ptr() as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,8 +263,40 @@ impl Window2 {
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
use wayland_client::Proxy;
|
||||
match self {
|
||||
&Window2::X(ref w) => w.platform_window(),
|
||||
&Window2::Wayland(ref w) => w.get_surface().ptr() as *mut _
|
||||
&Window::X(ref w) => w.platform_window(),
|
||||
&Window::Wayland(ref w) => w.get_surface().ptr() as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_maximized(maximized),
|
||||
&Window::Wayland(ref w) => w.set_maximized(maximized),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_fullscreen(monitor),
|
||||
&Window::Wayland(ref w) => w.set_fullscreen(monitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, decorations: bool) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_decorations(decorations),
|
||||
&Window::Wayland(ref w) => w.set_decorations(decorations)
|
||||
}
|
||||
}
|
||||
|
||||
#[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())},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,7 +306,7 @@ unsafe extern "C" fn x_error_callback(dpy: *mut x11::ffi::Display, event: *mut x
|
||||
{
|
||||
use std::ffi::CStr;
|
||||
|
||||
if let UnixBackend::X(ref x) = *UNIX_BACKEND {
|
||||
if let Ok(ref x) = *X11_BACKEND {
|
||||
let mut buff: Vec<u8> = Vec::with_capacity(1024);
|
||||
(x.xlib.XGetErrorText)(dpy, (*event).error_code as i32, buff.as_mut_ptr() as *mut libc::c_char, buff.capacity() as i32);
|
||||
let description = CStr::from_ptr(buff.as_mut_ptr() as *const libc::c_char).to_string_lossy();
|
||||
@@ -295,51 +325,127 @@ unsafe extern "C" fn x_error_callback(dpy: *mut x11::ffi::Display, event: *mut x
|
||||
}
|
||||
|
||||
pub enum EventsLoop {
|
||||
#[doc(hidden)]
|
||||
Wayland(wayland::EventsLoop),
|
||||
#[doc(hidden)]
|
||||
X(x11::EventsLoop)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum EventsLoopProxy {
|
||||
X(x11::EventsLoopProxy),
|
||||
Wayland(wayland::EventsLoopProxy),
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new() -> EventsLoop {
|
||||
match *UNIX_BACKEND {
|
||||
UnixBackend::Wayland(ref ctxt) => {
|
||||
EventsLoop::Wayland(wayland::EventsLoop::new(ctxt.clone()))
|
||||
},
|
||||
|
||||
UnixBackend::X(_) => {
|
||||
EventsLoop::X(x11::EventsLoop::new())
|
||||
},
|
||||
|
||||
UnixBackend::Error(_) => {
|
||||
panic!("Attempted to create an EventsLoop while no backend was available.")
|
||||
if let Ok(env_var) = env::var(BACKEND_PREFERENCE_ENV_VAR) {
|
||||
match env_var.as_str() {
|
||||
"x11" => {
|
||||
return EventsLoop::new_x11().unwrap(); // TODO: propagate
|
||||
},
|
||||
"wayland" => {
|
||||
match EventsLoop::new_wayland() {
|
||||
Ok(e) => return e,
|
||||
Err(_) => panic!() // TODO: propagate
|
||||
}
|
||||
},
|
||||
_ => panic!("Unknown environment variable value for {}, try one of `x11`,`wayland`",
|
||||
BACKEND_PREFERENCE_ENV_VAR),
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(el) = EventsLoop::new_wayland() {
|
||||
return el;
|
||||
}
|
||||
|
||||
if let Ok(el) = EventsLoop::new_x11() {
|
||||
return el;
|
||||
}
|
||||
|
||||
panic!("No backend is available")
|
||||
}
|
||||
|
||||
pub fn interrupt(&self) {
|
||||
match *self {
|
||||
EventsLoop::Wayland(ref evlp) => evlp.interrupt(),
|
||||
EventsLoop::X(ref evlp) => evlp.interrupt()
|
||||
pub fn new_wayland() -> Result<EventsLoop, ()> {
|
||||
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()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_events<F>(&self, callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
match *self {
|
||||
EventsLoop::Wayland(ref evlp) => evlp.poll_events(callback),
|
||||
EventsLoop::X(ref evlp) => evlp.poll_events(callback)
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&self, callback: F)
|
||||
#[inline]
|
||||
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())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventsLoopProxy {
|
||||
match *self {
|
||||
EventsLoop::Wayland(ref evlp) => EventsLoopProxy::Wayland(evlp.create_proxy()),
|
||||
EventsLoop::X(ref evlp) => EventsLoopProxy::X(evlp.create_proxy()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_events<F>(&mut self, callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
match *self {
|
||||
EventsLoop::Wayland(ref evlp) => evlp.run_forever(callback),
|
||||
EventsLoop::X(ref evlp) => evlp.run_forever(callback)
|
||||
EventsLoop::Wayland(ref mut evlp) => evlp.poll_events(callback),
|
||||
EventsLoop::X(ref mut evlp) => evlp.poll_events(callback)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&mut self, callback: F)
|
||||
where F: FnMut(::Event) -> ControlFlow
|
||||
{
|
||||
match *self {
|
||||
EventsLoop::Wayland(ref mut evlp) => evlp.run_forever(callback),
|
||||
EventsLoop::X(ref mut evlp) => evlp.run_forever(callback)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_wayland(&self) -> bool {
|
||||
match *self {
|
||||
EventsLoop::Wayland(_) => true,
|
||||
EventsLoop::X(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn x_connection(&self) -> Option<&Arc<XConnection>> {
|
||||
match *self {
|
||||
EventsLoop::Wayland(_) => None,
|
||||
EventsLoop::X(ref ev) => Some(ev.x_connection()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventsLoopProxy {
|
||||
pub fn wakeup(&self) -> Result<(), EventsLoopClosed> {
|
||||
match *self {
|
||||
EventsLoopProxy::Wayland(ref proxy) => proxy.wakeup(),
|
||||
EventsLoopProxy::X(ref proxy) => proxy.wakeup(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,297 +0,0 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use wayland_client::{EnvHandler, default_connect, EventQueue, EventQueueHandle, Init, Proxy};
|
||||
use wayland_client::protocol::{wl_compositor, wl_seat, wl_shell, wl_shm, wl_subcompositor,
|
||||
wl_display, wl_registry, wl_output, wl_surface};
|
||||
|
||||
use super::wayland_window;
|
||||
|
||||
/*
|
||||
* Registry and globals handling
|
||||
*/
|
||||
|
||||
wayland_env!(InnerEnv,
|
||||
compositor: wl_compositor::WlCompositor,
|
||||
shell: wl_shell::WlShell,
|
||||
shm: wl_shm::WlShm,
|
||||
subcompositor: wl_subcompositor::WlSubcompositor
|
||||
);
|
||||
|
||||
struct WaylandEnv {
|
||||
registry: wl_registry::WlRegistry,
|
||||
inner: EnvHandler<InnerEnv>,
|
||||
monitors: Vec<OutputInfo>,
|
||||
my_id: usize,
|
||||
}
|
||||
|
||||
struct OutputInfo {
|
||||
output: wl_output::WlOutput,
|
||||
id: u32,
|
||||
scale: f32,
|
||||
pix_size: (u32, u32),
|
||||
name: String
|
||||
}
|
||||
|
||||
impl OutputInfo {
|
||||
fn new(output: wl_output::WlOutput, id: u32) -> OutputInfo {
|
||||
OutputInfo {
|
||||
output: output,
|
||||
id: id,
|
||||
scale: 1.0,
|
||||
pix_size: (0, 0),
|
||||
name: "".into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WaylandEnv {
|
||||
fn new(registry: wl_registry::WlRegistry) -> WaylandEnv {
|
||||
WaylandEnv {
|
||||
registry: registry,
|
||||
inner: EnvHandler::new(),
|
||||
monitors: Vec::new(),
|
||||
my_id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_seat(&self) -> Option<wl_seat::WlSeat> {
|
||||
for &(name, ref interface, version) in self.inner.globals() {
|
||||
if interface == "wl_seat" {
|
||||
if version < 5 {
|
||||
panic!("Winit requires at least version 5 of the wl_seat global.");
|
||||
}
|
||||
let seat = self.registry.bind::<wl_seat::WlSeat>(5, name);
|
||||
return Some(seat)
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Init for WaylandEnv {
|
||||
fn init(&mut self, evqh: &mut EventQueueHandle, index: usize) {
|
||||
evqh.register::<_, WaylandEnv>(&self.registry, index);
|
||||
self.my_id = index
|
||||
}
|
||||
}
|
||||
|
||||
impl wl_registry::Handler for WaylandEnv {
|
||||
fn global(&mut self,
|
||||
evqh: &mut EventQueueHandle,
|
||||
registry: &wl_registry::WlRegistry,
|
||||
name: u32,
|
||||
interface: String,
|
||||
version: u32)
|
||||
{
|
||||
if interface == "wl_output" {
|
||||
// intercept outputs
|
||||
// this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69)
|
||||
let output = self.registry.bind::<wl_output::WlOutput>(1, name);
|
||||
evqh.register::<_, WaylandEnv>(&output, self.my_id);
|
||||
self.monitors.push(OutputInfo::new(output, name));
|
||||
}
|
||||
self.inner.global(evqh, registry, name, interface, version);
|
||||
}
|
||||
|
||||
fn global_remove(&mut self,
|
||||
evqh: &mut EventQueueHandle,
|
||||
registry: &wl_registry::WlRegistry,
|
||||
name: u32)
|
||||
{
|
||||
// prune old monitors
|
||||
self.monitors.retain(|m| m.id != name);
|
||||
self.inner.global_remove(evqh, registry, name);
|
||||
}
|
||||
}
|
||||
|
||||
declare_handler!(WaylandEnv, wl_registry::Handler, wl_registry::WlRegistry);
|
||||
|
||||
impl wl_output::Handler for WaylandEnv {
|
||||
fn geometry(&mut self,
|
||||
_: &mut EventQueueHandle,
|
||||
proxy: &wl_output::WlOutput,
|
||||
_x: i32, _y: i32,
|
||||
_physical_width: i32, _physical_height: i32,
|
||||
_subpixel: wl_output::Subpixel,
|
||||
make: String, model: String,
|
||||
_transform: wl_output::Transform)
|
||||
{
|
||||
for m in self.monitors.iter_mut().filter(|m| m.output.equals(proxy)) {
|
||||
m.name = format!("{} ({})", model, make);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fn mode(&mut self,
|
||||
_: &mut EventQueueHandle,
|
||||
proxy: &wl_output::WlOutput,
|
||||
flags: wl_output::Mode,
|
||||
width: i32, height: i32,
|
||||
_refresh: i32)
|
||||
{
|
||||
if flags.contains(wl_output::Current) {
|
||||
for m in self.monitors.iter_mut().filter(|m| m.output.equals(proxy)) {
|
||||
m.pix_size = (width as u32, height as u32);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fn scale(&mut self,
|
||||
_: &mut EventQueueHandle,
|
||||
proxy: &wl_output::WlOutput,
|
||||
factor: i32)
|
||||
{
|
||||
for m in self.monitors.iter_mut().filter(|m| m.output.equals(proxy)) {
|
||||
m.scale = factor as f32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare_handler!(WaylandEnv, wl_output::Handler, wl_output::WlOutput);
|
||||
|
||||
/*
|
||||
* Main context struct
|
||||
*/
|
||||
|
||||
pub struct WaylandContext {
|
||||
pub display: wl_display::WlDisplay,
|
||||
evq: Mutex<EventQueue>,
|
||||
env_id: usize,
|
||||
}
|
||||
|
||||
impl WaylandContext {
|
||||
pub fn init() -> Option<WaylandContext> {
|
||||
// attempt to connect to the wayland server
|
||||
// this handles both "no libwayland" and "no compositor" cases
|
||||
let (display, mut event_queue) = match default_connect() {
|
||||
Ok(ret) => ret,
|
||||
Err(_) => return None
|
||||
};
|
||||
|
||||
// this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69)
|
||||
let registry = display.get_registry();
|
||||
let env_id = event_queue.add_handler_with_init(WaylandEnv::new(registry));
|
||||
// two syncs fully initialize
|
||||
event_queue.sync_roundtrip().expect("Wayland connection unexpectedly lost");
|
||||
event_queue.sync_roundtrip().expect("Wayland connection unexpectedly lost");
|
||||
|
||||
Some(WaylandContext {
|
||||
evq: Mutex::new(event_queue),
|
||||
display: display,
|
||||
env_id: env_id
|
||||
})
|
||||
}
|
||||
|
||||
pub fn dispatch_pending(&self) {
|
||||
let mut guard = self.evq.lock().unwrap();
|
||||
guard.dispatch_pending().expect("Wayland connection unexpectedly lost");
|
||||
}
|
||||
|
||||
pub fn dispatch(&self) {
|
||||
let mut guard = self.evq.lock().unwrap();
|
||||
guard.dispatch().expect("Wayland connection unexpectedly lost");
|
||||
}
|
||||
|
||||
pub fn flush(&self) {
|
||||
let _ = self.display.flush();
|
||||
}
|
||||
|
||||
pub fn get_seat(&self) -> Option<wl_seat::WlSeat> {
|
||||
let mut guard = self.evq.lock().unwrap();
|
||||
let state = guard.state();
|
||||
state.get_handler::<WaylandEnv>(self.env_id).get_seat()
|
||||
}
|
||||
|
||||
pub fn with_output<F>(&self, id: MonitorId, f: F) where F: FnOnce(&wl_output::WlOutput) {
|
||||
let mut guard = self.evq.lock().unwrap();
|
||||
let state = guard.state();
|
||||
let env = state.get_handler::<WaylandEnv>(self.env_id);
|
||||
for m in env.monitors.iter().filter(|m| m.id == id.id) {
|
||||
f(&m.output);
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_window<H: wayland_window::Handler>(&self)
|
||||
-> (Arc<wl_surface::WlSurface>, wayland_window::DecoratedSurface<H>)
|
||||
{
|
||||
let mut guard = self.evq.lock().unwrap();
|
||||
let mut state = guard.state();
|
||||
let env = state.get_mut_handler::<WaylandEnv>(self.env_id);
|
||||
// this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69)
|
||||
let surface = Arc::new(env.inner.compositor.create_surface());
|
||||
let decorated = wayland_window::DecoratedSurface::new(
|
||||
&*surface, 800, 600,
|
||||
&env.inner.compositor,
|
||||
&env.inner.subcompositor,
|
||||
&env.inner.shm,
|
||||
&env.inner.shell,
|
||||
env.get_seat(),
|
||||
false
|
||||
).expect("Failed to create a tmpfile buffer.");
|
||||
(surface, decorated)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Monitors API
|
||||
*/
|
||||
|
||||
pub fn get_primary_monitor(ctxt: &Arc<WaylandContext>) -> MonitorId {
|
||||
let mut guard = ctxt.evq.lock().unwrap();
|
||||
let state = guard.state();
|
||||
let env = state.get_handler::<WaylandEnv>(ctxt.env_id);
|
||||
if let Some(ref monitor) = env.monitors.iter().next() {
|
||||
MonitorId {
|
||||
id: monitor.id,
|
||||
ctxt: ctxt.clone()
|
||||
}
|
||||
} else {
|
||||
panic!("No monitor is available.")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_available_monitors(ctxt: &Arc<WaylandContext>) -> VecDeque<MonitorId> {
|
||||
let mut guard = ctxt.evq.lock().unwrap();
|
||||
let state = guard.state();
|
||||
let env = state.get_handler::<WaylandEnv>(ctxt.env_id);
|
||||
env.monitors.iter()
|
||||
.map(|m| MonitorId { id: m.id, ctxt: ctxt.clone() })
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorId {
|
||||
id: u32,
|
||||
ctxt: Arc<WaylandContext>
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
let mut guard = self.ctxt.evq.lock().unwrap();
|
||||
let state = guard.state();
|
||||
let env = state.get_handler::<WaylandEnv>(self.ctxt.env_id);
|
||||
for m in env.monitors.iter().filter(|m| m.id == self.id) {
|
||||
return Some(m.name.clone())
|
||||
}
|
||||
// if we reach here, this monitor does not exist any more
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> ::native_monitor::NativeMonitorId {
|
||||
::native_monitor::NativeMonitorId::Unavailable
|
||||
}
|
||||
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
let mut guard = self.ctxt.evq.lock().unwrap();
|
||||
let state = guard.state();
|
||||
let env = state.get_handler::<WaylandEnv>(self.ctxt.env_id);
|
||||
for m in env.monitors.iter().filter(|m| m.id == self.id) {
|
||||
return m.pix_size
|
||||
}
|
||||
// if we reach here, this monitor does not exist any more
|
||||
(0,0)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,68 +1,131 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use {VirtualKeyCode, ElementState, WindowEvent as Event};
|
||||
use {VirtualKeyCode, ElementState, WindowEvent as Event, KeyboardInput, ModifiersState};
|
||||
|
||||
use events::ModifiersState;
|
||||
|
||||
use super::{wayland_kbd, EventsLoopSink, WindowId};
|
||||
use wayland_client::EventQueueHandle;
|
||||
use super::{EventsLoopSink, WindowId, make_wid, DeviceId};
|
||||
use super::wayland_kbd::{MappedKeyboardImplementation, register_kbd};
|
||||
use wayland_client::protocol::wl_keyboard;
|
||||
use wayland_client::EventQueueHandle;
|
||||
|
||||
pub struct KbdHandler {
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
pub target: Option<WindowId>
|
||||
}
|
||||
pub fn init_keyboard(evq: &mut EventQueueHandle, keyboard: &wl_keyboard::WlKeyboard, sink: &Arc<Mutex<EventsLoopSink>>) {
|
||||
let idata = KeyboardIData {
|
||||
sink: sink.clone(),
|
||||
target: None
|
||||
};
|
||||
|
||||
impl KbdHandler {
|
||||
pub fn new(sink: Arc<Mutex<EventsLoopSink>>) -> KbdHandler {
|
||||
KbdHandler { sink: sink, target: None }
|
||||
if register_kbd(evq, keyboard, mapped_keyboard_impl(), idata).is_err() {
|
||||
// initializing libxkbcommon failed :(
|
||||
// fallback implementation
|
||||
let idata = KeyboardIData {
|
||||
sink: sink.clone(),
|
||||
target: None
|
||||
};
|
||||
evq.register(keyboard, raw_keyboard_impl(), idata);
|
||||
}
|
||||
}
|
||||
|
||||
impl wayland_kbd::Handler for KbdHandler {
|
||||
fn key(&mut self,
|
||||
_evqh: &mut EventQueueHandle,
|
||||
_proxy: &wl_keyboard::WlKeyboard,
|
||||
_serial: u32,
|
||||
_time: u32,
|
||||
mods: &wayland_kbd::ModifiersState,
|
||||
rawkey: u32,
|
||||
keysym: u32,
|
||||
state: wl_keyboard::KeyState,
|
||||
utf8: Option<String>)
|
||||
{
|
||||
if let Some(wid) = self.target {
|
||||
let state = match state {
|
||||
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
|
||||
wl_keyboard::KeyState::Released => ElementState::Released,
|
||||
};
|
||||
let vkcode = key_to_vkey(rawkey, keysym);
|
||||
let mut guard = self.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::KeyboardInput(
|
||||
state,
|
||||
rawkey as u8,
|
||||
vkcode,
|
||||
ModifiersState {
|
||||
shift: mods.shift,
|
||||
ctrl: mods.ctrl,
|
||||
alt: mods.alt,
|
||||
logo: mods.logo
|
||||
struct KeyboardIData {
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
target: Option<WindowId>
|
||||
}
|
||||
|
||||
fn mapped_keyboard_impl() -> MappedKeyboardImplementation<KeyboardIData> {
|
||||
MappedKeyboardImplementation {
|
||||
enter: |_, idata, _, _, surface, _, _, _| {
|
||||
let wid = make_wid(surface);
|
||||
idata.sink.lock().unwrap().send_event(Event::Focused(true), wid);
|
||||
idata.target = Some(wid);
|
||||
},
|
||||
leave: |_, idata, _, _, surface| {
|
||||
let wid = make_wid(surface);
|
||||
idata.sink.lock().unwrap().send_event(Event::Focused(false), wid);
|
||||
idata.target = None;
|
||||
},
|
||||
key: |_, idata, _, _, _, mods, rawkey, keysym, state, utf8| {
|
||||
if let Some(wid) = idata.target {
|
||||
let state = match state {
|
||||
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
|
||||
wl_keyboard::KeyState::Released => ElementState::Released,
|
||||
};
|
||||
let vkcode = key_to_vkey(rawkey, keysym);
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::KeyboardInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: rawkey,
|
||||
virtual_keycode: vkcode,
|
||||
modifiers: ModifiersState {
|
||||
shift: mods.shift,
|
||||
ctrl: mods.ctrl,
|
||||
alt: mods.alt,
|
||||
logo: mods.logo
|
||||
},
|
||||
},
|
||||
},
|
||||
wid
|
||||
);
|
||||
// send char event only on key press, not release
|
||||
if let ElementState::Released = state { return }
|
||||
if let Some(txt) = utf8 {
|
||||
for chr in txt.chars() {
|
||||
guard.send_event(Event::ReceivedCharacter(chr), wid);
|
||||
}
|
||||
),
|
||||
wid
|
||||
);
|
||||
// send char event only on key press, not release
|
||||
if let ElementState::Released = state { return }
|
||||
if let Some(txt) = utf8 {
|
||||
for chr in txt.chars() {
|
||||
guard.send_event(Event::ReceivedCharacter(chr), wid);
|
||||
}
|
||||
}
|
||||
},
|
||||
repeat_info: |_, _idata, _, _rate, _delay| {
|
||||
// TODO: handle repeat info
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This is fallback impl if libxkbcommon was not available
|
||||
// This case should probably never happen, as most wayland
|
||||
// compositors _need_ libxkbcommon anyway...
|
||||
//
|
||||
// In this case, we don't have the keymap information (it is
|
||||
// supposed to be serialized by the compositor using libxkbcommon)
|
||||
fn raw_keyboard_impl() -> wl_keyboard::Implementation<KeyboardIData> {
|
||||
wl_keyboard::Implementation {
|
||||
enter: |_, idata, _, _, surface, _| {
|
||||
let wid = make_wid(surface);
|
||||
idata.sink.lock().unwrap().send_event(Event::Focused(true), wid);
|
||||
idata.target = Some(wid);
|
||||
},
|
||||
leave: |_, idata, _, _, surface| {
|
||||
let wid = make_wid(surface);
|
||||
idata.sink.lock().unwrap().send_event(Event::Focused(false), wid);
|
||||
idata.target = None;
|
||||
},
|
||||
key: |_, idata, _, _, _, key, state| {
|
||||
if let Some(wid) = idata.target {
|
||||
let state = match state {
|
||||
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
|
||||
wl_keyboard::KeyState::Released => ElementState::Released,
|
||||
};
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::KeyboardInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: key,
|
||||
virtual_keycode: None,
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
},
|
||||
wid
|
||||
);
|
||||
}
|
||||
},
|
||||
repeat_info: |_, _idata, _, _rate, _delay| {},
|
||||
keymap: |_, _, _, _, _, _| {},
|
||||
modifiers: |_, _, _, _, _, _, _, _| {}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_to_vkey(rawkey: u32, keysym: u32) -> Option<VirtualKeyCode> {
|
||||
match rawkey {
|
||||
1 => Some(VirtualKeyCode::Escape),
|
||||
@@ -213,6 +276,7 @@ fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
|
||||
// => Some(VirtualKeyCode::Subtract),
|
||||
// => Some(VirtualKeyCode::Sysrq),
|
||||
keysyms::XKB_KEY_Tab => Some(VirtualKeyCode::Tab),
|
||||
keysyms::XKB_KEY_ISO_Left_Tab => Some(VirtualKeyCode::Tab),
|
||||
// => Some(VirtualKeyCode::Underline),
|
||||
// => Some(VirtualKeyCode::Unlabeled),
|
||||
keysyms::XKB_KEY_XF86AudioLowerVolume => Some(VirtualKeyCode::VolumeDown),
|
||||
|
||||
@@ -1,17 +1,28 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
|
||||
pub use self::window::{Window, WindowId};
|
||||
pub use self::event_loop::EventsLoop;
|
||||
pub use self::context::{WaylandContext, MonitorId, get_available_monitors,
|
||||
get_primary_monitor};
|
||||
|
||||
use self::window::{make_wid, DecoratedHandler};
|
||||
use self::event_loop::EventsLoopSink;
|
||||
pub use self::window::Window;
|
||||
pub use self::event_loop::{EventsLoop, EventsLoopProxy, EventsLoopSink, MonitorId};
|
||||
|
||||
extern crate wayland_kbd;
|
||||
extern crate wayland_window;
|
||||
extern crate wayland_protocols;
|
||||
|
||||
use wayland_client::protocol::wl_surface;
|
||||
use wayland_client::Proxy;
|
||||
|
||||
mod context;
|
||||
mod event_loop;
|
||||
mod pointer;
|
||||
mod touch;
|
||||
mod keyboard;
|
||||
mod window;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(usize);
|
||||
|
||||
#[inline]
|
||||
fn make_wid(s: &wl_surface::WlSurface) -> WindowId {
|
||||
WindowId(s.ptr() as usize)
|
||||
}
|
||||
194
src/platform/linux/wayland/pointer.rs
Normal file
194
src/platform/linux/wayland/pointer.rs
Normal file
@@ -0,0 +1,194 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase};
|
||||
use events::ModifiersState;
|
||||
|
||||
use super::{WindowId, DeviceId};
|
||||
use super::event_loop::EventsLoopSink;
|
||||
use super::window::WindowStore;
|
||||
|
||||
use wayland_client::{Proxy, StateToken};
|
||||
use wayland_client::protocol::wl_pointer;
|
||||
|
||||
pub struct PointerIData {
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
windows_token: StateToken<WindowStore>,
|
||||
mouse_focus: Option<WindowId>,
|
||||
axis_buffer: Option<(f32, f32)>,
|
||||
axis_discrete_buffer: Option<(i32, i32)>,
|
||||
axis_state: TouchPhase,
|
||||
}
|
||||
|
||||
impl PointerIData {
|
||||
pub fn new(sink: &Arc<Mutex<EventsLoopSink>>, token: StateToken<WindowStore>)
|
||||
-> PointerIData
|
||||
{
|
||||
PointerIData {
|
||||
sink: sink.clone(),
|
||||
windows_token: token,
|
||||
mouse_focus: None,
|
||||
axis_buffer: None,
|
||||
axis_discrete_buffer: None,
|
||||
axis_state: TouchPhase::Cancelled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pointer_implementation() -> wl_pointer::Implementation<PointerIData> {
|
||||
wl_pointer::Implementation {
|
||||
enter: |evqh, idata, _, _, surface, x, y| {
|
||||
let wid = evqh.state().get(&idata.windows_token).find_wid(surface);
|
||||
if let Some(wid) = wid {
|
||||
idata.mouse_focus = Some(wid);
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::CursorEntered {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
guard.send_event(
|
||||
Event::CursorMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (x, y),
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
},
|
||||
leave: |evqh, idata, _, _, surface| {
|
||||
idata.mouse_focus = None;
|
||||
let wid = evqh.state().get(&idata.windows_token).find_wid(surface);
|
||||
if let Some(wid) = wid {
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::CursorLeft {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
},
|
||||
motion: |_, idata, _, _, x, y| {
|
||||
if let Some(wid) = idata.mouse_focus {
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::CursorMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (x, y),
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid
|
||||
);
|
||||
}
|
||||
},
|
||||
button: |_, idata, _, _, _, button, state| {
|
||||
if let Some(wid) = idata.mouse_focus {
|
||||
let state = match state {
|
||||
wl_pointer::ButtonState::Pressed => ElementState::Pressed,
|
||||
wl_pointer::ButtonState::Released => ElementState::Released
|
||||
};
|
||||
let button = match button {
|
||||
0x110 => MouseButton::Left,
|
||||
0x111 => MouseButton::Right,
|
||||
0x112 => MouseButton::Middle,
|
||||
// TODO figure out the translation ?
|
||||
_ => return
|
||||
};
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
state: state,
|
||||
button: button,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid
|
||||
);
|
||||
}
|
||||
},
|
||||
axis: |_, idata, pointer, _, axis, value| {
|
||||
if let Some(wid) = idata.mouse_focus {
|
||||
if pointer.version() < 5 {
|
||||
let (mut x, mut y) = (0.0, 0.0);
|
||||
// old seat compatibility
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32
|
||||
}
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
phase: TouchPhase::Moved,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid
|
||||
);
|
||||
} else {
|
||||
let (mut x, mut y) = idata.axis_buffer.unwrap_or((0.0, 0.0));
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32
|
||||
}
|
||||
idata.axis_buffer = Some((x,y));
|
||||
idata.axis_state = match idata.axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
frame: |_, idata, _| {
|
||||
let axis_buffer = idata.axis_buffer.take();
|
||||
let axis_discrete_buffer = idata.axis_discrete_buffer.take();
|
||||
if let Some(wid) = idata.mouse_focus {
|
||||
if let Some((x, y)) = axis_discrete_buffer {
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::LineDelta(x as f32, y as f32),
|
||||
phase: idata.axis_state,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid
|
||||
);
|
||||
} else if let Some((x, y)) = axis_buffer {
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
phase: idata.axis_state,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
axis_source: |_, _, _, _| {},
|
||||
axis_stop: |_, idata, _, _, _| {
|
||||
idata.axis_state = TouchPhase::Ended;
|
||||
},
|
||||
axis_discrete: |_, idata, _, axis, discrete| {
|
||||
let (mut x, mut y) = idata.axis_discrete_buffer.unwrap_or((0,0));
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= discrete,
|
||||
wl_pointer::Axis::HorizontalScroll => x += discrete
|
||||
}
|
||||
idata.axis_discrete_buffer = Some((x,y));
|
||||
idata.axis_state = match idata.axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
106
src/platform/linux/wayland/touch.rs
Normal file
106
src/platform/linux/wayland/touch.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use {WindowEvent as Event, TouchPhase};
|
||||
|
||||
use super::{WindowId, DeviceId};
|
||||
use super::event_loop::EventsLoopSink;
|
||||
use super::window::WindowStore;
|
||||
|
||||
use wayland_client::StateToken;
|
||||
use wayland_client::protocol::wl_touch;
|
||||
|
||||
pub struct TouchIData {
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
windows_token: StateToken<WindowStore>,
|
||||
pending_ids: Vec<TouchPoint>,
|
||||
}
|
||||
|
||||
struct TouchPoint {
|
||||
wid: WindowId,
|
||||
location: (f64, f64),
|
||||
id: i32
|
||||
}
|
||||
|
||||
impl TouchIData {
|
||||
pub fn new(sink: &Arc<Mutex<EventsLoopSink>>, token: StateToken<WindowStore>)
|
||||
-> TouchIData
|
||||
{
|
||||
TouchIData {
|
||||
sink: sink.clone(),
|
||||
windows_token: token,
|
||||
pending_ids: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn touch_implementation() -> wl_touch::Implementation<TouchIData> {
|
||||
wl_touch::Implementation {
|
||||
down: |evqh, idata, _, _serial, _time, surface, touch_id, x, y| {
|
||||
let wid = evqh.state().get(&idata.windows_token).find_wid(surface);
|
||||
if let Some(wid) = wid {
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Started,
|
||||
location: (x, y),
|
||||
id: touch_id as u64
|
||||
}),
|
||||
wid,
|
||||
);
|
||||
idata.pending_ids.push(TouchPoint {
|
||||
wid: wid,
|
||||
location: (x, y),
|
||||
id: touch_id
|
||||
});
|
||||
}
|
||||
},
|
||||
up: |_, idata, _, _serial, _time, touch_id| {
|
||||
let idx = idata.pending_ids.iter().position(|p| p.id == touch_id);
|
||||
if let Some(idx) = idx {
|
||||
let pt = idata.pending_ids.remove(idx);
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Ended,
|
||||
location: pt.location,
|
||||
id: touch_id as u64
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
}
|
||||
},
|
||||
motion: |_, idata, _, _time, touch_id, x, y| {
|
||||
let pt = idata.pending_ids.iter_mut().find(|p| p.id == touch_id);
|
||||
if let Some(pt) = pt {
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
pt.location = (x, y);
|
||||
guard.send_event(
|
||||
Event::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Moved,
|
||||
location: (x, y),
|
||||
id: touch_id as u64
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
}
|
||||
},
|
||||
frame: |_, _, _| {},
|
||||
cancel: |_, idata, _| {
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
for pt in idata.pending_ids.drain(..) {
|
||||
guard.send_event(
|
||||
Event::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Cancelled,
|
||||
location: pt.location,
|
||||
id: pt.id as u64
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,87 +1,91 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
|
||||
use wayland_client::{EventQueue, EventQueueHandle, Proxy};
|
||||
use wayland_client::protocol::{wl_display,wl_surface,wl_shell_surface};
|
||||
use wayland_client::protocol::{wl_display,wl_surface};
|
||||
use wayland_client::{Proxy, StateToken};
|
||||
|
||||
use {CreationError, MouseCursor, CursorState, WindowAttributes};
|
||||
use platform::MonitorId as PlatformMonitorId;
|
||||
use window::MonitorId as RootMonitorId;
|
||||
|
||||
use super::{WaylandContext, EventsLoop};
|
||||
use super::wayland_window;
|
||||
use super::wayland_window::DecoratedSurface;
|
||||
use super::{EventsLoop, WindowId, make_wid, MonitorId};
|
||||
use super::wayland_window::{Frame, FrameImplementation, State as FrameState};
|
||||
use super::event_loop::StateContext;
|
||||
|
||||
pub struct Window {
|
||||
// the global wayland context
|
||||
ctxt: Arc<WaylandContext>,
|
||||
// the EventQueue of our EventsLoop
|
||||
evq: Arc<Mutex<EventQueue>>,
|
||||
// signal to advertize the EventsLoop when we are destroyed
|
||||
cleanup_signal: Arc<AtomicBool>,
|
||||
// our wayland surface
|
||||
surface: Arc<wl_surface::WlSurface>,
|
||||
// our current inner dimensions
|
||||
size: Mutex<(u32, u32)>,
|
||||
// the id of our DecoratedHandler in the EventQueue
|
||||
decorated_id: usize
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(usize);
|
||||
|
||||
#[inline]
|
||||
pub fn make_wid(s: &wl_surface::WlSurface) -> WindowId {
|
||||
WindowId(s.ptr() as usize)
|
||||
surface: wl_surface::WlSurface,
|
||||
frame: Arc<Mutex<Frame>>,
|
||||
monitors: Arc<Mutex<MonitorList>>,
|
||||
size: Arc<Mutex<(u32, u32)>>,
|
||||
kill_switch: (Arc<Mutex<bool>>, Arc<Mutex<bool>>),
|
||||
display: Arc<wl_display::WlDisplay>,
|
||||
need_frame_refresh: Arc<Mutex<bool>>
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(evlp: &EventsLoop, ctxt: Arc<WaylandContext>, attributes: &WindowAttributes) -> Result<Window, CreationError>
|
||||
pub fn new(evlp: &EventsLoop, attributes: &WindowAttributes) -> Result<Window, CreationError>
|
||||
{
|
||||
let (width, height) = attributes.dimensions.unwrap_or((800,600));
|
||||
|
||||
let (surface, decorated) = ctxt.create_window::<DecoratedHandler>();
|
||||
|
||||
// init DecoratedSurface
|
||||
let (evq, cleanup_signal) = evlp.get_window_init();
|
||||
let decorated_id = {
|
||||
let mut evq_guard = evq.lock().unwrap();
|
||||
let decorated_id = evq_guard.add_handler_with_init(decorated);
|
||||
{
|
||||
// initialize the DecoratedHandler
|
||||
let mut state = evq_guard.state();
|
||||
let decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(decorated_id);
|
||||
*(decorated.handler()) = Some(DecoratedHandler::new());
|
||||
|
||||
// set fullscreen if necessary
|
||||
if let Some(PlatformMonitorId::Wayland(ref monitor_id)) = attributes.monitor {
|
||||
ctxt.with_output(monitor_id.clone(), |output| {
|
||||
decorated.set_fullscreen(
|
||||
wl_shell_surface::FullscreenMethod::Default,
|
||||
0,
|
||||
Some(output)
|
||||
)
|
||||
});
|
||||
} else if attributes.decorations {
|
||||
decorated.set_decorate(true);
|
||||
}
|
||||
// Finally, set the decorations size
|
||||
decorated.resize(width as i32, height as i32);
|
||||
// Create the decorated surface
|
||||
let size = Arc::new(Mutex::new((width, height)));
|
||||
let store_token = evlp.store.clone();
|
||||
let (surface, mut frame) = evlp.create_window(
|
||||
width, height, decorated_impl(),
|
||||
|surface| FrameIData {
|
||||
surface: surface.clone().unwrap(),
|
||||
store_token: store_token.clone()
|
||||
}
|
||||
decorated_id
|
||||
};
|
||||
let me = Window {
|
||||
ctxt: ctxt,
|
||||
evq: evq,
|
||||
cleanup_signal: cleanup_signal,
|
||||
);
|
||||
// Check for fullscreen requirements
|
||||
if let Some(RootMonitorId { inner: PlatformMonitorId::Wayland(ref monitor_id) }) = attributes.fullscreen {
|
||||
let info = monitor_id.info.lock().unwrap();
|
||||
frame.set_state(FrameState::Fullscreen(Some(&info.output)));
|
||||
} else if attributes.maximized {
|
||||
frame.set_state(FrameState::Maximized);
|
||||
}
|
||||
|
||||
// set decorations
|
||||
frame.set_decorate(attributes.decorations);
|
||||
|
||||
// min-max dimensions
|
||||
frame.set_min_size(attributes.min_dimensions.map(|(w, h)| (w as i32, h as i32)));
|
||||
frame.set_max_size(attributes.max_dimensions.map(|(w, h)| (w as i32, h as i32)));
|
||||
|
||||
// setup the monitor tracking
|
||||
let monitor_list = Arc::new(Mutex::new(MonitorList::default()));
|
||||
{
|
||||
let mut evq = evlp.evq.borrow_mut();
|
||||
let idata = (evlp.ctxt_token.clone(), monitor_list.clone());
|
||||
evq.register(&surface, surface_impl(), idata);
|
||||
}
|
||||
|
||||
let kill_switch = Arc::new(Mutex::new(false));
|
||||
let need_frame_refresh = Arc::new(Mutex::new(true));
|
||||
let frame = Arc::new(Mutex::new(frame));
|
||||
|
||||
{
|
||||
let mut evq = evlp.evq.borrow_mut();
|
||||
evq.state().get_mut(&store_token).windows.push(InternalWindow {
|
||||
closed: false,
|
||||
newsize: None,
|
||||
need_refresh: false,
|
||||
need_frame_refresh: need_frame_refresh.clone(),
|
||||
surface: surface.clone().unwrap(),
|
||||
kill_switch: kill_switch.clone(),
|
||||
frame: Arc::downgrade(&frame)
|
||||
});
|
||||
evq.sync_roundtrip().unwrap();
|
||||
}
|
||||
|
||||
Ok(Window {
|
||||
display: evlp.display.clone(),
|
||||
surface: surface,
|
||||
size: Mutex::new((width, height)),
|
||||
decorated_id: decorated_id
|
||||
};
|
||||
|
||||
// register ourselves to the EventsLoop
|
||||
evlp.register_window(me.decorated_id, me.surface.clone());
|
||||
|
||||
Ok(me)
|
||||
frame: frame,
|
||||
monitors: monitor_list,
|
||||
size: size,
|
||||
kill_switch: (kill_switch, evlp.cleanup_needed.clone()),
|
||||
need_frame_refresh: need_frame_refresh
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -90,10 +94,7 @@ impl Window {
|
||||
}
|
||||
|
||||
pub fn set_title(&self, title: &str) {
|
||||
let mut guard = self.evq.lock().unwrap();
|
||||
let mut state = guard.state();
|
||||
let decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(self.decorated_id);
|
||||
decorated.set_title(title.into())
|
||||
self.frame.lock().unwrap().set_title(title.into());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -112,6 +113,12 @@ impl Window {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
// Not possible with wayland
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_position(&self, _x: i32, _y: i32) {
|
||||
// Not possible with wayland
|
||||
@@ -131,10 +138,18 @@ impl Window {
|
||||
#[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) {
|
||||
let mut guard = self.evq.lock().unwrap();
|
||||
let mut state = guard.state();
|
||||
let mut decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(self.decorated_id);
|
||||
decorated.resize(x as i32, y as i32);
|
||||
self.frame.lock().unwrap().resize(x as i32, y as i32);
|
||||
*(self.size.lock().unwrap()) = (x, y);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
self.frame.lock().unwrap().set_min_size(dimensions.map(|(w, h)| (w as i32, h as i32)));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
self.frame.lock().unwrap().set_max_size(dimensions.map(|(w, h)| (w as i32, h as i32)));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -155,8 +170,35 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
// TODO
|
||||
1.0
|
||||
let mut factor = 1.0;
|
||||
let guard = self.monitors.lock().unwrap();
|
||||
for monitor_id in &guard.monitors {
|
||||
let info = monitor_id.info.lock().unwrap();
|
||||
if info.scale > factor { factor = info.scale; }
|
||||
}
|
||||
factor
|
||||
}
|
||||
|
||||
pub fn set_decorations(&self, decorate: bool) {
|
||||
self.frame.lock().unwrap().set_decorate(decorate);
|
||||
*(self.need_frame_refresh.lock().unwrap()) = true;
|
||||
}
|
||||
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
if maximized {
|
||||
self.frame.lock().unwrap().set_state(FrameState::Maximized);
|
||||
} else {
|
||||
self.frame.lock().unwrap().set_state(FrameState::Regular);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
|
||||
if let Some(RootMonitorId { inner: PlatformMonitorId::Wayland(ref monitor_id) }) = monitor {
|
||||
let info = monitor_id.info.lock().unwrap();
|
||||
self.frame.lock().unwrap().set_state(FrameState::Fullscreen(Some(&info.output)));
|
||||
} else {
|
||||
self.frame.lock().unwrap().set_state(FrameState::Regular);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -164,42 +206,159 @@ impl Window {
|
||||
// TODO: not yet possible on wayland
|
||||
Err(())
|
||||
}
|
||||
|
||||
|
||||
pub fn get_display(&self) -> &wl_display::WlDisplay {
|
||||
&self.ctxt.display
|
||||
&*self.display
|
||||
}
|
||||
|
||||
|
||||
pub fn get_surface(&self) -> &wl_surface::WlSurface {
|
||||
&self.surface
|
||||
}
|
||||
|
||||
pub fn get_current_monitor(&self) -> MonitorId {
|
||||
// we don't know how much each monitor sees us so...
|
||||
// just return the most recent one ?
|
||||
let guard = self.monitors.lock().unwrap();
|
||||
guard.monitors.last().unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
self.surface.destroy();
|
||||
self.cleanup_signal.store(true, ::std::sync::atomic::Ordering::Relaxed);
|
||||
*(self.kill_switch.0.lock().unwrap()) = true;
|
||||
*(self.kill_switch.1.lock().unwrap()) = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DecoratedHandler {
|
||||
newsize: Option<(u32, u32)>
|
||||
/*
|
||||
* Internal store for windows
|
||||
*/
|
||||
|
||||
struct InternalWindow {
|
||||
surface: wl_surface::WlSurface,
|
||||
newsize: Option<(i32, i32)>,
|
||||
need_refresh: bool,
|
||||
need_frame_refresh: Arc<Mutex<bool>>,
|
||||
closed: bool,
|
||||
kill_switch: Arc<Mutex<bool>>,
|
||||
frame: Weak<Mutex<Frame>>
|
||||
}
|
||||
|
||||
impl DecoratedHandler {
|
||||
fn new() -> DecoratedHandler { DecoratedHandler { newsize: None }}
|
||||
pub struct WindowStore {
|
||||
windows: Vec<InternalWindow>
|
||||
}
|
||||
|
||||
pub fn take_newsize(&mut self) -> Option<(u32, u32)> {
|
||||
self.newsize.take()
|
||||
impl WindowStore {
|
||||
pub fn new() -> WindowStore {
|
||||
WindowStore { windows: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl wayland_window::Handler for DecoratedHandler {
|
||||
fn configure(&mut self,
|
||||
_: &mut EventQueueHandle,
|
||||
_: wl_shell_surface::Resize,
|
||||
width: i32, height: i32)
|
||||
pub fn find_wid(&self, surface: &wl_surface::WlSurface) -> Option<WindowId> {
|
||||
for window in &self.windows {
|
||||
if surface.equals(&window.surface) {
|
||||
return Some(make_wid(surface));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn cleanup(&mut self) -> Vec<WindowId> {
|
||||
let mut pruned = Vec::new();
|
||||
self.windows.retain(|w| {
|
||||
if *w.kill_switch.lock().unwrap() {
|
||||
// window is dead, cleanup
|
||||
pruned.push(make_wid(&w.surface));
|
||||
w.surface.destroy();
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
pruned
|
||||
}
|
||||
|
||||
pub fn for_each<F>(&mut self, mut f: F)
|
||||
where F: FnMut(Option<(i32, i32)>, bool, bool, bool, WindowId, Option<&mut Frame>)
|
||||
{
|
||||
use std::cmp::max;
|
||||
self.newsize = Some((max(width,1) as u32, max(height,1) as u32));
|
||||
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(),
|
||||
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)
|
||||
);
|
||||
window.need_refresh = false;
|
||||
// avoid re-spamming the event
|
||||
window.closed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Protocol implementation
|
||||
*/
|
||||
|
||||
struct FrameIData {
|
||||
store_token: StateToken<WindowStore>,
|
||||
surface: wl_surface::WlSurface
|
||||
}
|
||||
|
||||
fn decorated_impl() -> FrameImplementation<FrameIData> {
|
||||
FrameImplementation {
|
||||
configure: |evqh, idata, _, newsize| {
|
||||
let store = evqh.state().get_mut(&idata.store_token);
|
||||
for window in &mut store.windows {
|
||||
if window.surface.equals(&idata.surface) {
|
||||
window.newsize = newsize;
|
||||
window.need_refresh = true;
|
||||
*(window.need_frame_refresh.lock().unwrap()) = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
close: |evqh, idata| {
|
||||
let store = evqh.state().get_mut(&idata.store_token);
|
||||
for window in &mut store.windows {
|
||||
if window.surface.equals(&idata.surface) {
|
||||
window.closed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
refresh: |evqh, idata| {
|
||||
let store = evqh.state().get_mut(&idata.store_token);
|
||||
for window in &mut store.windows {
|
||||
if window.surface.equals(&idata.surface) {
|
||||
*(window.need_frame_refresh.lock().unwrap()) = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct MonitorList {
|
||||
monitors: Vec<MonitorId>
|
||||
}
|
||||
|
||||
fn surface_impl() -> wl_surface::Implementation<(StateToken<StateContext>, Arc<Mutex<MonitorList>>)> {
|
||||
wl_surface::Implementation {
|
||||
enter: |evqh, &mut (ref token, ref list), _, output| {
|
||||
let mut guard = list.lock().unwrap();
|
||||
let ctxt = evqh.state().get(token);
|
||||
let monitor = ctxt.monitor_id_for(output);
|
||||
guard.monitors.push(monitor);
|
||||
},
|
||||
leave: |evqh, &mut (ref token, ref list), _, output| {
|
||||
let mut guard = list.lock().unwrap();
|
||||
let ctxt = evqh.state().get(token);
|
||||
let monitor = ctxt.monitor_id_for(output);
|
||||
guard.monitors.retain(|m| !Arc::ptr_eq(&m.info, &monitor.info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
240
src/platform/linux/x11/dnd.rs
Normal file
240
src/platform/linux/x11/dnd.rs
Normal file
@@ -0,0 +1,240 @@
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::Utf8Error;
|
||||
|
||||
use libc::{c_char, c_int, c_long, c_uchar, c_ulong};
|
||||
use percent_encoding::percent_decode;
|
||||
|
||||
use super::{ffi, util, XConnection, XError};
|
||||
|
||||
const DND_ATOMS_LEN: usize = 12;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DndAtoms {
|
||||
pub aware: ffi::Atom,
|
||||
pub enter: ffi::Atom,
|
||||
pub leave: ffi::Atom,
|
||||
pub drop: ffi::Atom,
|
||||
pub position: ffi::Atom,
|
||||
pub status: ffi::Atom,
|
||||
pub action_private: ffi::Atom,
|
||||
pub selection: ffi::Atom,
|
||||
pub finished: ffi::Atom,
|
||||
pub type_list: ffi::Atom,
|
||||
pub uri_list: ffi::Atom,
|
||||
pub none: ffi::Atom,
|
||||
}
|
||||
|
||||
impl DndAtoms {
|
||||
pub fn new(xconn: &Arc<XConnection>) -> Result<Self, XError> {
|
||||
let mut atoms = Vec::with_capacity(DND_ATOMS_LEN);
|
||||
|
||||
let mut names = [
|
||||
b"XdndAware\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndEnter\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndLeave\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndDrop\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndPosition\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndStatus\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndActionPrivate\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndSelection\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndFinished\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndTypeList\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"text/uri-list\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"None\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
];
|
||||
|
||||
unsafe {
|
||||
(xconn.xlib.XInternAtoms)(
|
||||
xconn.display,
|
||||
names.as_mut_ptr(),
|
||||
DND_ATOMS_LEN as c_int,
|
||||
ffi::False,
|
||||
atoms.as_mut_ptr(),
|
||||
);
|
||||
}
|
||||
xconn.check_errors()?;
|
||||
unsafe {
|
||||
atoms.set_len(DND_ATOMS_LEN);
|
||||
}
|
||||
Ok(DndAtoms {
|
||||
aware: atoms[0],
|
||||
enter: atoms[1],
|
||||
leave: atoms[2],
|
||||
drop: atoms[3],
|
||||
position: atoms[4],
|
||||
status: atoms[5],
|
||||
action_private: atoms[6],
|
||||
selection: atoms[7],
|
||||
finished: atoms[8],
|
||||
type_list: atoms[9],
|
||||
uri_list: atoms[10],
|
||||
none: atoms[11],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum DndState {
|
||||
Accepted,
|
||||
Rejected,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DndDataParseError {
|
||||
EmptyData,
|
||||
InvalidUtf8(Utf8Error),
|
||||
HostnameSpecified(String),
|
||||
UnexpectedProtocol(String),
|
||||
UnresolvablePath(io::Error),
|
||||
}
|
||||
|
||||
impl From<Utf8Error> for DndDataParseError {
|
||||
fn from(e: Utf8Error) -> Self {
|
||||
DndDataParseError::InvalidUtf8(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for DndDataParseError {
|
||||
fn from(e: io::Error) -> Self {
|
||||
DndDataParseError::UnresolvablePath(e)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Dnd {
|
||||
xconn: Arc<XConnection>,
|
||||
pub atoms: DndAtoms,
|
||||
// Populated by XdndEnter event handler
|
||||
pub version: Option<c_long>,
|
||||
pub type_list: Option<Vec<c_ulong>>,
|
||||
// Populated by XdndPosition event handler
|
||||
pub source_window: Option<c_ulong>,
|
||||
// Populated by SelectionNotify event handler (triggered by XdndPosition event handler)
|
||||
pub result: Option<Result<Vec<PathBuf>, DndDataParseError>>,
|
||||
}
|
||||
|
||||
impl Dnd {
|
||||
pub fn new(xconn: Arc<XConnection>) -> Result<Self, XError> {
|
||||
let atoms = DndAtoms::new(&xconn)?;
|
||||
Ok(Dnd {
|
||||
xconn,
|
||||
atoms,
|
||||
version: None,
|
||||
type_list: None,
|
||||
source_window: None,
|
||||
result: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.version = None;
|
||||
self.type_list = None;
|
||||
self.source_window = None;
|
||||
self.result = None;
|
||||
}
|
||||
|
||||
pub unsafe fn send_status(
|
||||
&self,
|
||||
this_window: c_ulong,
|
||||
target_window: c_ulong,
|
||||
state: DndState,
|
||||
) -> Result<(), XError> {
|
||||
let (accepted, action) = match state {
|
||||
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,
|
||||
target_window,
|
||||
target_window,
|
||||
self.atoms.status,
|
||||
None,
|
||||
(this_window as c_long, accepted, 0, 0, action),
|
||||
)
|
||||
}
|
||||
|
||||
pub unsafe fn send_finished(
|
||||
&self,
|
||||
this_window: c_ulong,
|
||||
target_window: c_ulong,
|
||||
state: DndState,
|
||||
) -> Result<(), XError> {
|
||||
let (accepted, action) = match state {
|
||||
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,
|
||||
target_window,
|
||||
target_window,
|
||||
self.atoms.finished,
|
||||
None,
|
||||
(this_window as c_long, accepted, action, 0, 0),
|
||||
)
|
||||
}
|
||||
|
||||
pub unsafe fn get_type_list(
|
||||
&self,
|
||||
source_window: c_ulong,
|
||||
) -> Result<Vec<ffi::Atom>, util::GetPropertyError> {
|
||||
util::get_property(
|
||||
&self.xconn,
|
||||
source_window,
|
||||
self.atoms.type_list,
|
||||
ffi::XA_ATOM,
|
||||
)
|
||||
}
|
||||
|
||||
pub unsafe fn convert_selection(&self, window: c_ulong, time: c_ulong) {
|
||||
(self.xconn.xlib.XConvertSelection)(
|
||||
self.xconn.display,
|
||||
self.atoms.selection,
|
||||
self.atoms.uri_list,
|
||||
self.atoms.selection,
|
||||
window,
|
||||
time,
|
||||
);
|
||||
}
|
||||
|
||||
pub unsafe fn read_data(
|
||||
&self,
|
||||
window: c_ulong,
|
||||
) -> Result<Vec<c_uchar>, util::GetPropertyError> {
|
||||
util::get_property(
|
||||
&self.xconn,
|
||||
window,
|
||||
self.atoms.selection,
|
||||
self.atoms.uri_list,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn parse_data(&self, data: &mut Vec<c_uchar>) -> Result<Vec<PathBuf>, DndDataParseError> {
|
||||
if !data.is_empty() {
|
||||
let mut path_list = Vec::new();
|
||||
let decoded = percent_decode(data).decode_utf8()?.into_owned();
|
||||
for uri in decoded.split("\r\n").filter(|u| !u.is_empty()) {
|
||||
// The format is specified as protocol://host/path
|
||||
// However, it's typically simply protocol:///path
|
||||
let path_str = if uri.starts_with("file://") {
|
||||
let path_str = uri.replace("file://", "");
|
||||
if !path_str.starts_with('/') {
|
||||
// A hostname is specified
|
||||
// Supporting this case is beyond the scope of my mental health
|
||||
return Err(DndDataParseError::HostnameSpecified(path_str));
|
||||
}
|
||||
path_str
|
||||
} else {
|
||||
// Only the file protocol is supported
|
||||
return Err(DndDataParseError::UnexpectedProtocol(uri.to_owned()));
|
||||
};
|
||||
|
||||
let path = Path::new(&path_str).canonicalize()?;
|
||||
path_list.push(path);
|
||||
}
|
||||
Ok(path_list)
|
||||
} else {
|
||||
Err(DndDataParseError::EmptyData)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@ use {events, libc};
|
||||
use super::ffi;
|
||||
use VirtualKeyCode;
|
||||
|
||||
pub fn keycode_to_element(scancode: libc::c_uint) -> Option<VirtualKeyCode> {
|
||||
Some(match scancode {
|
||||
pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> {
|
||||
Some(match keysym {
|
||||
ffi::XK_BackSpace => events::VirtualKeyCode::Back,
|
||||
ffi::XK_Tab => events::VirtualKeyCode::Tab,
|
||||
//ffi::XK_Linefeed => events::VirtualKeyCode::Linefeed,
|
||||
@@ -67,19 +67,19 @@ pub fn keycode_to_element(scancode: libc::c_uint) -> Option<VirtualKeyCode> {
|
||||
//ffi::XK_KP_F2 => events::VirtualKeyCode::Kp_f2,
|
||||
//ffi::XK_KP_F3 => events::VirtualKeyCode::Kp_f3,
|
||||
//ffi::XK_KP_F4 => events::VirtualKeyCode::Kp_f4,
|
||||
//ffi::XK_KP_Home => events::VirtualKeyCode::Kp_home,
|
||||
//ffi::XK_KP_Left => events::VirtualKeyCode::NumpadLeft,
|
||||
//ffi::XK_KP_Up => events::VirtualKeyCode::NumpadUp,
|
||||
//ffi::XK_KP_Right => events::VirtualKeyCode::NumpadRight,
|
||||
//ffi::XK_KP_Down => events::VirtualKeyCode::NumpadDown,
|
||||
ffi::XK_KP_Home => events::VirtualKeyCode::Home,
|
||||
ffi::XK_KP_Left => events::VirtualKeyCode::Left,
|
||||
ffi::XK_KP_Up => events::VirtualKeyCode::Up,
|
||||
ffi::XK_KP_Right => events::VirtualKeyCode::Right,
|
||||
ffi::XK_KP_Down => events::VirtualKeyCode::Down,
|
||||
//ffi::XK_KP_Prior => events::VirtualKeyCode::Kp_prior,
|
||||
//ffi::XK_KP_Page_Up => events::VirtualKeyCode::NumpadPageUp,
|
||||
ffi::XK_KP_Page_Up => events::VirtualKeyCode::PageUp,
|
||||
//ffi::XK_KP_Next => events::VirtualKeyCode::Kp_next,
|
||||
//ffi::XK_KP_Page_Down => events::VirtualKeyCode::NumpadPageDown,
|
||||
//ffi::XK_KP_End => events::VirtualKeyCode::NumpadEnd,
|
||||
ffi::XK_KP_Page_Down => events::VirtualKeyCode::PageDown,
|
||||
ffi::XK_KP_End => events::VirtualKeyCode::End,
|
||||
//ffi::XK_KP_Begin => events::VirtualKeyCode::Kp_begin,
|
||||
//ffi::XK_KP_Insert => events::VirtualKeyCode::NumpadInsert,
|
||||
//ffi::XK_KP_Delete => events::VirtualKeyCode::NumpadDelete,
|
||||
ffi::XK_KP_Insert => events::VirtualKeyCode::Insert,
|
||||
ffi::XK_KP_Delete => events::VirtualKeyCode::Delete,
|
||||
ffi::XK_KP_Equal => events::VirtualKeyCode::NumpadEquals,
|
||||
//ffi::XK_KP_Multiply => events::VirtualKeyCode::NumpadMultiply,
|
||||
//ffi::XK_KP_Add => events::VirtualKeyCode::NumpadAdd,
|
||||
@@ -171,6 +171,7 @@ pub fn keycode_to_element(scancode: libc::c_uint) -> Option<VirtualKeyCode> {
|
||||
//ffi::XK_Super_R => events::VirtualKeyCode::Super_r,
|
||||
//ffi::XK_Hyper_L => events::VirtualKeyCode::Hyper_l,
|
||||
//ffi::XK_Hyper_R => events::VirtualKeyCode::Hyper_r,
|
||||
ffi::XK_ISO_Left_Tab => events::VirtualKeyCode::Tab,
|
||||
ffi::XK_space => events::VirtualKeyCode::Space,
|
||||
//ffi::XK_exclam => events::VirtualKeyCode::Exclam,
|
||||
//ffi::XK_quotedbl => events::VirtualKeyCode::Quotedbl,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
pub use x11_dl::keysym::*;
|
||||
pub use x11_dl::xcursor::*;
|
||||
pub use x11_dl::xf86vmode::*;
|
||||
pub use x11_dl::xlib::*;
|
||||
pub use x11_dl::xinput::*;
|
||||
pub use x11_dl::xinput2::*;
|
||||
pub use x11_dl::xlib_xcb::*;
|
||||
pub use x11_dl::error::OpenError;
|
||||
pub use x11_dl::xrandr::*;
|
||||
|
||||
185
src/platform/linux/x11/ime/callbacks.rs
Normal file
185
src/platform/linux/x11/ime/callbacks.rs
Normal file
@@ -0,0 +1,185 @@
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
use super::{ffi, XConnection, XError};
|
||||
|
||||
use super::inner::{close_im, ImeInner};
|
||||
use super::input_method::PotentialInputMethods;
|
||||
use super::context::{ImeContextCreationError, ImeContext};
|
||||
|
||||
pub unsafe fn xim_set_callback(
|
||||
xconn: &Arc<XConnection>,
|
||||
xim: ffi::XIM,
|
||||
field: *const c_char,
|
||||
callback: *mut ffi::XIMCallback,
|
||||
) -> Result<(), XError> {
|
||||
// It's advisable to wrap variadic FFI functions in our own functions, as we want to minimize
|
||||
// access that isn't type-checked.
|
||||
(xconn.xlib.XSetIMValues)(
|
||||
xim,
|
||||
field,
|
||||
callback,
|
||||
ptr::null_mut::<()>(),
|
||||
);
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
// Set a callback for when an input method matching the current locale modifiers becomes
|
||||
// available. Note that this has nothing to do with what input methods are open or able to be
|
||||
// opened, and simply uses the modifiers that are set when the callback is set.
|
||||
// * This is called per locale modifier, not per input method opened with that locale modifier.
|
||||
// * Trying to set this for multiple locale modifiers causes problems, i.e. one of the rebuilt
|
||||
// input contexts would always silently fail to use the input method.
|
||||
pub unsafe fn set_instantiate_callback(
|
||||
xconn: &Arc<XConnection>,
|
||||
client_data: ffi::XPointer,
|
||||
) -> Result<(), XError> {
|
||||
(xconn.xlib.XRegisterIMInstantiateCallback)(
|
||||
xconn.display,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
Some(xim_instantiate_callback),
|
||||
client_data,
|
||||
);
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
pub unsafe fn unset_instantiate_callback(
|
||||
xconn: &Arc<XConnection>,
|
||||
client_data: ffi::XPointer,
|
||||
) -> Result<(), XError> {
|
||||
(xconn.xlib.XUnregisterIMInstantiateCallback)(
|
||||
xconn.display,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
Some(xim_instantiate_callback),
|
||||
client_data,
|
||||
);
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
pub unsafe fn set_destroy_callback(
|
||||
xconn: &Arc<XConnection>,
|
||||
im: ffi::XIM,
|
||||
inner: &ImeInner,
|
||||
) -> Result<(), XError> {
|
||||
xim_set_callback(
|
||||
&xconn,
|
||||
im,
|
||||
ffi::XNDestroyCallback_0.as_ptr() as *const _,
|
||||
&inner.destroy_callback as *const _ as *mut _,
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ReplaceImError {
|
||||
MethodOpenFailed(PotentialInputMethods),
|
||||
ContextCreationFailed(ImeContextCreationError),
|
||||
SetDestroyCallbackFailed(XError),
|
||||
}
|
||||
|
||||
// Attempt to replace current IM (which may or may not be presently valid) with a new one. This
|
||||
// includes replacing all existing input contexts and free'ing resources as necessary. This only
|
||||
// modifies existing state if all operations succeed.
|
||||
unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
|
||||
let xconn = &(*inner).xconn;
|
||||
|
||||
let (new_im, is_fallback) = {
|
||||
let new_im = (*inner).potential_input_methods.open_im(xconn, None);
|
||||
let is_fallback = new_im.is_fallback();
|
||||
(
|
||||
new_im.ok().ok_or_else(|| {
|
||||
ReplaceImError::MethodOpenFailed((*inner).potential_input_methods.clone())
|
||||
})?,
|
||||
is_fallback,
|
||||
)
|
||||
};
|
||||
|
||||
// It's important to always set a destroy callback, since there's otherwise potential for us
|
||||
// to try to use or free a resource that's already been destroyed on the server.
|
||||
{
|
||||
let result = set_destroy_callback(xconn, new_im.im, &*inner);
|
||||
if result.is_err() {
|
||||
let _ = close_im(xconn, new_im.im);
|
||||
}
|
||||
result
|
||||
}.map_err(ReplaceImError::SetDestroyCallbackFailed)?;
|
||||
|
||||
let mut new_contexts = HashMap::new();
|
||||
for (window, old_context) in (*inner).contexts.iter() {
|
||||
let spot = old_context.as_ref().map(|old_context| old_context.ic_spot);
|
||||
let new_context = {
|
||||
let result = ImeContext::new(
|
||||
xconn,
|
||||
new_im.im,
|
||||
*window,
|
||||
spot,
|
||||
);
|
||||
if result.is_err() {
|
||||
let _ = close_im(xconn, new_im.im);
|
||||
}
|
||||
result.map_err(ReplaceImError::ContextCreationFailed)?
|
||||
};
|
||||
new_contexts.insert(*window, Some(new_context));
|
||||
}
|
||||
|
||||
// If we've made it this far, everything succeeded.
|
||||
let _ = (*inner).destroy_all_contexts_if_necessary();
|
||||
let _ = (*inner).close_im_if_necessary();
|
||||
(*inner).im = new_im.im;
|
||||
(*inner).contexts = new_contexts;
|
||||
(*inner).is_destroyed = false;
|
||||
(*inner).is_fallback = is_fallback;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe extern fn xim_instantiate_callback(
|
||||
_display: *mut ffi::Display,
|
||||
client_data: ffi::XPointer,
|
||||
// This field is unsupplied.
|
||||
_call_data: ffi::XPointer,
|
||||
) {
|
||||
let inner: *mut ImeInner = client_data as _;
|
||||
if !inner.is_null() {
|
||||
let xconn = &(*inner).xconn;
|
||||
let result = replace_im(inner);
|
||||
if result.is_ok() {
|
||||
let _ = unset_instantiate_callback(xconn, client_data);
|
||||
(*inner).is_fallback = false;
|
||||
} else if result.is_err() && (*inner).is_destroyed {
|
||||
// We have no usable input methods!
|
||||
result.expect("Failed to reopen input method");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This callback is triggered when the input method is closed on the server end. When this
|
||||
// happens, XCloseIM/XDestroyIC doesn't need to be called, as the resources have already been
|
||||
// free'd (attempting to do so causes our connection to freeze).
|
||||
pub unsafe extern fn xim_destroy_callback(
|
||||
_xim: ffi::XIM,
|
||||
client_data: ffi::XPointer,
|
||||
// This field is unsupplied.
|
||||
_call_data: ffi::XPointer,
|
||||
) {
|
||||
let inner: *mut ImeInner = client_data as _;
|
||||
if !inner.is_null() {
|
||||
(*inner).is_destroyed = true;
|
||||
let xconn = &(*inner).xconn;
|
||||
if !(*inner).is_fallback {
|
||||
let _ = set_instantiate_callback(xconn, client_data);
|
||||
// Attempt to open fallback input method.
|
||||
let result = replace_im(inner);
|
||||
if result.is_ok() {
|
||||
(*inner).is_fallback = true;
|
||||
} else {
|
||||
// We have no usable input methods!
|
||||
result.expect("Failed to open fallback input method");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
134
src/platform/linux/x11/ime/context.rs
Normal file
134
src/platform/linux/x11/ime/context.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::os::raw::{c_short, c_void};
|
||||
|
||||
use super::{ffi, util, XConnection, XError};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ImeContextCreationError {
|
||||
XError(XError),
|
||||
Null,
|
||||
}
|
||||
|
||||
unsafe fn create_pre_edit_attr<'a>(
|
||||
xconn: &'a Arc<XConnection>,
|
||||
ic_spot: &'a ffi::XPoint,
|
||||
) -> util::XSmartPointer<'a, c_void> {
|
||||
util::XSmartPointer::new(
|
||||
xconn,
|
||||
(xconn.xlib.XVaCreateNestedList)(
|
||||
0,
|
||||
ffi::XNSpotLocation_0.as_ptr() as *const _,
|
||||
ic_spot,
|
||||
ptr::null_mut::<()>(),
|
||||
),
|
||||
).expect("XVaCreateNestedList returned NULL")
|
||||
}
|
||||
|
||||
// WARNING: this struct doesn't destroy its XIC resource when dropped.
|
||||
// This is intentional, as it doesn't have enough information to know whether or not the context
|
||||
// still exists on the server. Since `ImeInner` has that awareness, destruction must be handled
|
||||
// through `ImeInner`.
|
||||
#[derive(Debug)]
|
||||
pub struct ImeContext {
|
||||
pub ic: ffi::XIC,
|
||||
pub ic_spot: ffi::XPoint,
|
||||
}
|
||||
|
||||
impl ImeContext {
|
||||
pub unsafe fn new(
|
||||
xconn: &Arc<XConnection>,
|
||||
im: ffi::XIM,
|
||||
window: ffi::Window,
|
||||
ic_spot: Option<ffi::XPoint>,
|
||||
) -> Result<Self, ImeContextCreationError> {
|
||||
let ic = if let Some(ic_spot) = ic_spot {
|
||||
ImeContext::create_ic_with_spot(xconn, im, window, ic_spot)
|
||||
} else {
|
||||
ImeContext::create_ic(xconn, im, window)
|
||||
};
|
||||
|
||||
let ic = ic.ok_or(ImeContextCreationError::Null)?;
|
||||
xconn.check_errors().map_err(ImeContextCreationError::XError)?;
|
||||
|
||||
Ok(ImeContext {
|
||||
ic,
|
||||
ic_spot: ic_spot.unwrap_or_else(|| ffi::XPoint { x: 0, y: 0 }),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn create_ic(
|
||||
xconn: &Arc<XConnection>,
|
||||
im: ffi::XIM,
|
||||
window: ffi::Window,
|
||||
) -> Option<ffi::XIC> {
|
||||
let ic = (xconn.xlib.XCreateIC)(
|
||||
im,
|
||||
ffi::XNInputStyle_0.as_ptr() as *const _,
|
||||
ffi::XIMPreeditNothing | ffi::XIMStatusNothing,
|
||||
ffi::XNClientWindow_0.as_ptr() as *const _,
|
||||
window,
|
||||
ptr::null_mut::<()>(),
|
||||
);
|
||||
if ic.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(ic)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn create_ic_with_spot(
|
||||
xconn: &Arc<XConnection>,
|
||||
im: ffi::XIM,
|
||||
window: ffi::Window,
|
||||
ic_spot: ffi::XPoint,
|
||||
) -> Option<ffi::XIC> {
|
||||
let pre_edit_attr = create_pre_edit_attr(xconn, &ic_spot);
|
||||
let ic = (xconn.xlib.XCreateIC)(
|
||||
im,
|
||||
ffi::XNInputStyle_0.as_ptr() as *const _,
|
||||
ffi::XIMPreeditNothing | ffi::XIMStatusNothing,
|
||||
ffi::XNClientWindow_0.as_ptr() as *const _,
|
||||
window,
|
||||
ffi::XNPreeditAttributes_0.as_ptr() as *const _,
|
||||
pre_edit_attr.ptr,
|
||||
ptr::null_mut::<()>(),
|
||||
);
|
||||
if ic.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(ic)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn focus(&self, xconn: &Arc<XConnection>) -> Result<(), XError> {
|
||||
unsafe {
|
||||
(xconn.xlib.XSetICFocus)(self.ic);
|
||||
}
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
pub fn unfocus(&self, xconn: &Arc<XConnection>) -> Result<(), XError> {
|
||||
unsafe {
|
||||
(xconn.xlib.XUnsetICFocus)(self.ic);
|
||||
}
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
pub fn set_spot(&mut self, xconn: &Arc<XConnection>, x: c_short, y: c_short) {
|
||||
if self.ic_spot.x == x && self.ic_spot.y == y {
|
||||
return;
|
||||
}
|
||||
self.ic_spot = ffi::XPoint { x, y };
|
||||
|
||||
unsafe {
|
||||
let pre_edit_attr = create_pre_edit_attr(xconn, &self.ic_spot);
|
||||
(xconn.xlib.XSetICValues)(
|
||||
self.ic,
|
||||
ffi::XNPreeditAttributes_0.as_ptr() as *const _,
|
||||
pre_edit_attr.ptr,
|
||||
ptr::null_mut::<()>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
75
src/platform/linux/x11/ime/inner.rs
Normal file
75
src/platform/linux/x11/ime/inner.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{ffi, XConnection, XError};
|
||||
|
||||
use super::input_method::PotentialInputMethods;
|
||||
use super::context::ImeContext;
|
||||
|
||||
pub unsafe fn close_im(xconn: &Arc<XConnection>, im: ffi::XIM) -> Result<(), XError> {
|
||||
(xconn.xlib.XCloseIM)(im);
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
pub unsafe fn destroy_ic(xconn: &Arc<XConnection>, ic: ffi::XIC) -> Result<(), XError> {
|
||||
(xconn.xlib.XDestroyIC)(ic);
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
pub struct ImeInner {
|
||||
pub xconn: Arc<XConnection>,
|
||||
// WARNING: this is initially null!
|
||||
pub im: ffi::XIM,
|
||||
pub potential_input_methods: PotentialInputMethods,
|
||||
pub contexts: HashMap<ffi::Window, Option<ImeContext>>,
|
||||
// WARNING: this is initially zeroed!
|
||||
pub destroy_callback: ffi::XIMCallback,
|
||||
// Indicates whether or not the the input method was destroyed on the server end
|
||||
// (i.e. if ibus/fcitx/etc. was terminated/restarted)
|
||||
pub is_destroyed: bool,
|
||||
pub is_fallback: bool,
|
||||
}
|
||||
|
||||
impl ImeInner {
|
||||
pub fn new(
|
||||
xconn: Arc<XConnection>,
|
||||
potential_input_methods: PotentialInputMethods,
|
||||
) -> Self {
|
||||
ImeInner {
|
||||
xconn,
|
||||
im: ptr::null_mut(),
|
||||
potential_input_methods,
|
||||
contexts: HashMap::new(),
|
||||
destroy_callback: unsafe { mem::zeroed() },
|
||||
is_destroyed: false,
|
||||
is_fallback: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn close_im_if_necessary(&self) -> Result<bool, XError> {
|
||||
if !self.is_destroyed {
|
||||
close_im(&self.xconn, self.im).map(|_| true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn destroy_ic_if_necessary(&self, ic: ffi::XIC) -> Result<bool, XError> {
|
||||
if !self.is_destroyed {
|
||||
destroy_ic(&self.xconn, ic).map(|_| true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn destroy_all_contexts_if_necessary(&self) -> Result<bool, XError> {
|
||||
for context in self.contexts.values() {
|
||||
if let &Some(ref context) = context {
|
||||
self.destroy_ic_if_necessary(context.ic)?;
|
||||
}
|
||||
}
|
||||
Ok(!self.is_destroyed)
|
||||
}
|
||||
}
|
||||
277
src/platform/linux/x11/ime/input_method.rs
Normal file
277
src/platform/linux/x11/ime/input_method.rs
Normal file
@@ -0,0 +1,277 @@
|
||||
use std::env;
|
||||
use std::fmt;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::os::raw::c_char;
|
||||
use std::ffi::{CStr, CString, IntoStringError};
|
||||
|
||||
use super::{ffi, util, XConnection, XError};
|
||||
|
||||
unsafe fn open_im(
|
||||
xconn: &Arc<XConnection>,
|
||||
locale_modifiers: &CStr,
|
||||
) -> Option<ffi::XIM> {
|
||||
// XSetLocaleModifiers returns...
|
||||
// * The current locale modifiers if it's given a NULL pointer.
|
||||
// * The new locale modifiers if we succeeded in setting them.
|
||||
// * NULL if the locale modifiers string is malformed.
|
||||
(xconn.xlib.XSetLocaleModifiers)(locale_modifiers.as_ptr());
|
||||
|
||||
let im = (xconn.xlib.XOpenIM)(
|
||||
xconn.display,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
);
|
||||
|
||||
if im.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(im)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InputMethod {
|
||||
pub im: ffi::XIM,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl InputMethod {
|
||||
fn new(im: ffi::XIM, name: String) -> Self {
|
||||
InputMethod { im, name }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InputMethodResult {
|
||||
/// Input method used locale modifier from `XMODIFIERS` environment variable.
|
||||
XModifiers(InputMethod),
|
||||
/// Input method used internal fallback locale modifier.
|
||||
Fallback(InputMethod),
|
||||
/// Input method could not be opened using any locale modifier tried.
|
||||
Failure,
|
||||
}
|
||||
|
||||
impl InputMethodResult {
|
||||
pub fn is_fallback(&self) -> bool {
|
||||
if let &InputMethodResult::Fallback(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ok(self) -> Option<InputMethod> {
|
||||
use self::InputMethodResult::*;
|
||||
match self {
|
||||
XModifiers(im) | Fallback(im) => Some(im),
|
||||
Failure => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum GetXimServersError {
|
||||
XError(XError),
|
||||
GetPropertyError(util::GetPropertyError),
|
||||
InvalidUtf8(IntoStringError),
|
||||
}
|
||||
|
||||
// The root window has a property named XIM_SERVERS, which contains a list of atoms represeting
|
||||
// the availabile XIM servers. For instance, if you're using ibus, it would contain an atom named
|
||||
// "@server=ibus". It's possible for this property to contain multiple atoms, though presumably
|
||||
// rare. Note that we replace "@server=" with "@im=" in order to match the format of locale
|
||||
// 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 root = (xconn.xlib.XDefaultRootWindow)(xconn.display);
|
||||
|
||||
let mut atoms: Vec<ffi::Atom> = util::get_property(
|
||||
&xconn,
|
||||
root,
|
||||
servers_atom,
|
||||
ffi::XA_ATOM,
|
||||
).map_err(GetXimServersError::GetPropertyError)?;
|
||||
|
||||
let mut names: Vec<*const c_char> = Vec::with_capacity(atoms.len());
|
||||
(xconn.xlib.XGetAtomNames)(
|
||||
xconn.display,
|
||||
atoms.as_mut_ptr(),
|
||||
atoms.len() as _,
|
||||
names.as_mut_ptr() as _,
|
||||
);
|
||||
names.set_len(atoms.len());
|
||||
|
||||
let mut formatted_names = Vec::with_capacity(names.len());
|
||||
for name in names {
|
||||
let string = CStr::from_ptr(name)
|
||||
.to_owned()
|
||||
.into_string()
|
||||
.map_err(GetXimServersError::InvalidUtf8)?;
|
||||
(xconn.xlib.XFree)(name as _);
|
||||
formatted_names.push(string.replace("@server=", "@im="));
|
||||
}
|
||||
xconn.check_errors().map_err(GetXimServersError::XError)?;
|
||||
Ok(formatted_names)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct InputMethodName {
|
||||
c_string: CString,
|
||||
string: String,
|
||||
}
|
||||
|
||||
impl InputMethodName {
|
||||
pub fn from_string(string: String) -> Self {
|
||||
let c_string = CString::new(string.clone())
|
||||
.expect("String used to construct CString contained null byte");
|
||||
InputMethodName {
|
||||
c_string,
|
||||
string,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(string: &str) -> Self {
|
||||
let c_string = CString::new(string)
|
||||
.expect("String used to construct CString contained null byte");
|
||||
InputMethodName {
|
||||
c_string,
|
||||
string: string.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for InputMethodName {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.string.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct PotentialInputMethod {
|
||||
name: InputMethodName,
|
||||
successful: Option<bool>,
|
||||
}
|
||||
|
||||
impl PotentialInputMethod {
|
||||
pub fn from_string(string: String) -> Self {
|
||||
PotentialInputMethod {
|
||||
name: InputMethodName::from_string(string),
|
||||
successful: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(string: &str) -> Self {
|
||||
PotentialInputMethod {
|
||||
name: InputMethodName::from_str(string),
|
||||
successful: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.successful = None;
|
||||
}
|
||||
|
||||
pub fn open_im(&mut self, xconn: &Arc<XConnection>) -> Option<InputMethod> {
|
||||
let im = unsafe { open_im(xconn, &self.name.c_string) };
|
||||
self.successful = Some(im.is_some());
|
||||
im.map(|im| InputMethod::new(im, self.name.string.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
// By logging this struct, you get a sequential listing of every locale modifier tried, where it
|
||||
// came from, and if it succceeded.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PotentialInputMethods {
|
||||
// On correctly configured systems, the XMODIFIERS environemnt variable tells us everything we
|
||||
// need to know.
|
||||
xmodifiers: Option<PotentialInputMethod>,
|
||||
// We have some standard options at our disposal that should ostensibly always work. For users
|
||||
// who only need compose sequences, this ensures that the program launches without a hitch
|
||||
// For users who need more sophisticated IME features, this is more or less a silent failure.
|
||||
// Logging features should be added in the future to allow both audiences to be effectively
|
||||
// served.
|
||||
fallbacks: [PotentialInputMethod; 2],
|
||||
// For diagnostic purposes, we include the list of XIM servers that the server reports as
|
||||
// being available.
|
||||
_xim_servers: Result<Vec<String>, GetXimServersError>,
|
||||
}
|
||||
|
||||
impl PotentialInputMethods {
|
||||
pub fn new(xconn: &Arc<XConnection>) -> Self {
|
||||
let xmodifiers = env::var("XMODIFIERS")
|
||||
.ok()
|
||||
.map(PotentialInputMethod::from_string);
|
||||
PotentialInputMethods {
|
||||
// Since passing "" to XSetLocaleModifiers results in it defaulting to the value of
|
||||
// XMODIFIERS, it's worth noting what happens if XMODIFIERS is also "". If simply
|
||||
// running the program with `XMODIFIERS="" cargo run`, then assuming XMODIFIERS is
|
||||
// defined in the profile (or parent environment) then that parent XMODIFIERS is used.
|
||||
// If that XMODIFIERS value is also "" (i.e. if you ran `export XMODIFIERS=""`), then
|
||||
// XSetLocaleModifiers uses the default local input method. Note that defining
|
||||
// XMODIFIERS as "" is different from XMODIFIERS not being defined at all, since in
|
||||
// that case, we get `None` and end up skipping ahead to the next method.
|
||||
xmodifiers,
|
||||
fallbacks: [
|
||||
// This is a standard input method that supports compose equences, which should
|
||||
// always be available. `@im=none` appears to mean the same thing.
|
||||
PotentialInputMethod::from_str("@im=local"),
|
||||
// This explicitly specifies to use the implementation-dependent default, though
|
||||
// that seems to be equivalent to just using the local input method.
|
||||
PotentialInputMethod::from_str("@im="),
|
||||
],
|
||||
// The XIM_SERVERS property can have surprising values. For instance, when I exited
|
||||
// ibus to run fcitx, it retained the value denoting ibus. Even more surprising is
|
||||
// that the fcitx input method could only be successfully opened using "@im=ibus".
|
||||
// Presumably due to this quirk, it's actually possible to alternate between ibus and
|
||||
// fcitx in a running application.
|
||||
_xim_servers: unsafe { get_xim_servers(xconn) },
|
||||
}
|
||||
}
|
||||
|
||||
// This resets the `successful` field of every potential input method, ensuring we have
|
||||
// accurate information when this struct is re-used by the destruction/instantiation callbacks.
|
||||
fn reset(&mut self) {
|
||||
if let Some(ref mut input_method) = self.xmodifiers {
|
||||
input_method.reset();
|
||||
}
|
||||
|
||||
for input_method in &mut self.fallbacks {
|
||||
input_method.reset();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_im(
|
||||
&mut self,
|
||||
xconn: &Arc<XConnection>,
|
||||
callback: Option<&Fn() -> ()>,
|
||||
) -> InputMethodResult {
|
||||
use self::InputMethodResult::*;
|
||||
|
||||
self.reset();
|
||||
|
||||
if let Some(ref mut input_method) = self.xmodifiers {
|
||||
let im = input_method.open_im(xconn);
|
||||
if let Some(im) = im {
|
||||
return XModifiers(im);
|
||||
} else {
|
||||
if let Some(ref callback) = callback {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for input_method in &mut self.fallbacks {
|
||||
let im = input_method.open_im(xconn);
|
||||
if let Some(im) = im {
|
||||
return Fallback(im);
|
||||
}
|
||||
}
|
||||
|
||||
Failure
|
||||
}
|
||||
}
|
||||
165
src/platform/linux/x11/ime/mod.rs
Normal file
165
src/platform/linux/x11/ime/mod.rs
Normal file
@@ -0,0 +1,165 @@
|
||||
// Important: all XIM calls need to happen from the same thread!
|
||||
|
||||
mod inner;
|
||||
mod input_method;
|
||||
mod context;
|
||||
mod callbacks;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
|
||||
use super::{ffi, util, XConnection, XError};
|
||||
|
||||
use self::inner::{close_im, ImeInner};
|
||||
use self::input_method::PotentialInputMethods;
|
||||
use self::context::{ImeContextCreationError, ImeContext};
|
||||
use self::callbacks::*;
|
||||
|
||||
pub type ImeReceiver = Receiver<(ffi::Window, i16, i16)>;
|
||||
pub type ImeSender = Sender<(ffi::Window, i16, i16)>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ImeCreationError {
|
||||
OpenFailure(PotentialInputMethods),
|
||||
SetDestroyCallbackFailed(XError),
|
||||
}
|
||||
|
||||
pub struct Ime {
|
||||
xconn: Arc<XConnection>,
|
||||
// The actual meat of this struct is boxed away, since it needs to have a fixed location in
|
||||
// memory so we can pass a pointer to it around.
|
||||
inner: Box<ImeInner>,
|
||||
}
|
||||
|
||||
impl Ime {
|
||||
pub fn new(xconn: Arc<XConnection>) -> Result<Self, ImeCreationError> {
|
||||
let potential_input_methods = PotentialInputMethods::new(&xconn);
|
||||
|
||||
let (mut inner, client_data) = {
|
||||
let mut inner = Box::new(ImeInner::new(
|
||||
xconn,
|
||||
potential_input_methods,
|
||||
));
|
||||
let inner_ptr = Box::into_raw(inner);
|
||||
let client_data = inner_ptr as _;
|
||||
let destroy_callback = ffi::XIMCallback {
|
||||
client_data,
|
||||
callback: Some(xim_destroy_callback),
|
||||
};
|
||||
inner = unsafe { Box::from_raw(inner_ptr) };
|
||||
inner.destroy_callback = destroy_callback;
|
||||
(inner, client_data)
|
||||
};
|
||||
|
||||
let xconn = Arc::clone(&inner.xconn);
|
||||
|
||||
let input_method = inner.potential_input_methods.open_im(&xconn, Some(&|| {
|
||||
let _ = unsafe { set_instantiate_callback(&xconn, client_data) };
|
||||
}));
|
||||
|
||||
let is_fallback = input_method.is_fallback();
|
||||
if let Some(input_method) = input_method.ok() {
|
||||
inner.im = input_method.im;
|
||||
inner.is_fallback = is_fallback;
|
||||
unsafe {
|
||||
let result = set_destroy_callback(&xconn, input_method.im, &*inner)
|
||||
.map_err(ImeCreationError::SetDestroyCallbackFailed);
|
||||
if result.is_err() {
|
||||
let _ = close_im(&xconn, input_method.im);
|
||||
}
|
||||
result?;
|
||||
}
|
||||
Ok(Ime { xconn, inner })
|
||||
} else {
|
||||
Err(ImeCreationError::OpenFailure(inner.potential_input_methods))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_destroyed(&self) -> bool {
|
||||
self.inner.is_destroyed
|
||||
}
|
||||
|
||||
// This pattern is used for various methods here:
|
||||
// Ok(_) indicates that nothing went wrong internally
|
||||
// Ok(true) indicates that the action was actually performed
|
||||
// Ok(false) indicates that the action is not presently applicable
|
||||
pub fn create_context(&mut self, window: ffi::Window)
|
||||
-> Result<bool, ImeContextCreationError>
|
||||
{
|
||||
let context = if self.is_destroyed() {
|
||||
// Create empty entry in map, so that when IME is rebuilt, this window has a context.
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { ImeContext::new(
|
||||
&self.inner.xconn,
|
||||
self.inner.im,
|
||||
window,
|
||||
None,
|
||||
) }?)
|
||||
};
|
||||
self.inner.contexts.insert(window, context);
|
||||
Ok(!self.is_destroyed())
|
||||
}
|
||||
|
||||
pub fn get_context(&self, window: ffi::Window) -> Option<ffi::XIC> {
|
||||
if self.is_destroyed() {
|
||||
return None;
|
||||
}
|
||||
if let Some(&Some(ref context)) = self.inner.contexts.get(&window) {
|
||||
Some(context.ic)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_context(&mut self, window: ffi::Window) -> Result<bool, XError> {
|
||||
if let Some(Some(context)) = self.inner.contexts.remove(&window) {
|
||||
unsafe {
|
||||
self.inner.destroy_ic_if_necessary(context.ic)?;
|
||||
}
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn focus(&mut self, window: ffi::Window) -> Result<bool, XError> {
|
||||
if self.is_destroyed() {
|
||||
return Ok(false);
|
||||
}
|
||||
if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) {
|
||||
context.focus(&self.xconn).map(|_| true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unfocus(&mut self, window: ffi::Window) -> Result<bool, XError> {
|
||||
if self.is_destroyed() {
|
||||
return Ok(false);
|
||||
}
|
||||
if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) {
|
||||
context.unfocus(&self.xconn).map(|_| true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_xim_spot(&mut self, window: ffi::Window, x: i16, y: i16) {
|
||||
if self.is_destroyed() {
|
||||
return;
|
||||
}
|
||||
if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) {
|
||||
context.set_spot(&self.xconn, x as _, y as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Ime {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let _ = self.inner.destroy_all_contexts_if_necessary();
|
||||
let _ = self.inner.close_im_if_necessary();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,391 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use libc;
|
||||
use std::{mem, ptr};
|
||||
use std::ffi::CString;
|
||||
use std::slice::from_raw_parts;
|
||||
|
||||
use WindowAttributes;
|
||||
|
||||
use events::WindowEvent as Event;
|
||||
use events::ModifiersState;
|
||||
|
||||
use super::{events, ffi};
|
||||
use super::XConnection;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum AxisType {
|
||||
HorizontalScroll,
|
||||
VerticalScroll
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Axis {
|
||||
id: i32,
|
||||
device_id: i32,
|
||||
axis_number: i32,
|
||||
axis_type: AxisType,
|
||||
scroll_increment: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct AxisValue {
|
||||
device_id: i32,
|
||||
axis_number: i32,
|
||||
value: f64
|
||||
}
|
||||
|
||||
struct InputState {
|
||||
/// Last-seen cursor position within a window in (x, y)
|
||||
/// coordinates
|
||||
cursor_pos: (f64, f64),
|
||||
/// Last-seen positions of axes, used to report delta
|
||||
/// movements when a new absolute axis value is received
|
||||
axis_values: Vec<AxisValue>
|
||||
}
|
||||
|
||||
pub struct XInputEventHandler {
|
||||
display: Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
ic: ffi::XIC,
|
||||
axis_list: Vec<Axis>,
|
||||
current_state: InputState,
|
||||
multitouch: bool,
|
||||
}
|
||||
|
||||
impl XInputEventHandler {
|
||||
pub fn new(display: &Arc<XConnection>, window: ffi::Window, ic: ffi::XIC,
|
||||
window_attrs: &WindowAttributes) -> XInputEventHandler {
|
||||
// query XInput support
|
||||
let mut opcode: libc::c_int = 0;
|
||||
let mut event: libc::c_int = 0;
|
||||
let mut error: libc::c_int = 0;
|
||||
let xinput_str = CString::new("XInputExtension").unwrap();
|
||||
|
||||
unsafe {
|
||||
if (display.xlib.XQueryExtension)(display.display, xinput_str.as_ptr(), &mut opcode, &mut event, &mut error) == ffi::False {
|
||||
panic!("XInput not available")
|
||||
}
|
||||
}
|
||||
|
||||
let mut xinput_major_ver = ffi::XI_2_Major;
|
||||
let mut xinput_minor_ver = ffi::XI_2_Minor;
|
||||
|
||||
unsafe {
|
||||
if (display.xinput2.XIQueryVersion)(display.display, &mut xinput_major_ver, &mut xinput_minor_ver) != ffi::Success as libc::c_int {
|
||||
panic!("Unable to determine XInput version");
|
||||
}
|
||||
}
|
||||
|
||||
// specify the XInput events we want to receive.
|
||||
// Button clicks and mouse events are handled via XInput
|
||||
// events. Key presses are still handled via plain core
|
||||
// X11 events.
|
||||
let mut mask: [libc::c_uchar; 3] = [0; 3];
|
||||
let mut input_event_mask = ffi::XIEventMask {
|
||||
deviceid: ffi::XIAllMasterDevices,
|
||||
mask_len: mask.len() as i32,
|
||||
mask: mask.as_mut_ptr()
|
||||
};
|
||||
let events = &[
|
||||
ffi::XI_ButtonPress,
|
||||
ffi::XI_ButtonRelease,
|
||||
ffi::XI_Motion,
|
||||
ffi::XI_Enter,
|
||||
ffi::XI_Leave,
|
||||
ffi::XI_FocusIn,
|
||||
ffi::XI_FocusOut,
|
||||
ffi::XI_TouchBegin,
|
||||
ffi::XI_TouchUpdate,
|
||||
ffi::XI_TouchEnd,
|
||||
];
|
||||
for event in events {
|
||||
ffi::XISetMask(&mut mask, *event);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
match (display.xinput2.XISelectEvents)(display.display, window, &mut input_event_mask, 1) {
|
||||
status if status as u8 == ffi::Success => (),
|
||||
err => panic!("Failed to select events {:?}", err)
|
||||
}
|
||||
}
|
||||
|
||||
XInputEventHandler {
|
||||
display: display.clone(),
|
||||
window: window,
|
||||
ic: ic,
|
||||
axis_list: read_input_axis_info(display),
|
||||
current_state: InputState {
|
||||
cursor_pos: (0.0, 0.0),
|
||||
axis_values: Vec::new()
|
||||
},
|
||||
multitouch: window_attrs.multitouch,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn translate_key_event(&self, event: &mut ffi::XKeyEvent) -> Vec<Event> {
|
||||
use events::WindowEvent::{KeyboardInput, ReceivedCharacter};
|
||||
use events::ElementState::{Pressed, Released};
|
||||
|
||||
let mut translated_events = Vec::new();
|
||||
|
||||
let state;
|
||||
if event.type_ == ffi::KeyPress {
|
||||
let raw_ev: *mut ffi::XKeyEvent = event;
|
||||
unsafe { (self.display.xlib.XFilterEvent)(mem::transmute(raw_ev), self.window) };
|
||||
state = Pressed;
|
||||
} else {
|
||||
state = Released;
|
||||
}
|
||||
|
||||
let mut kp_keysym = 0;
|
||||
|
||||
let mut ev_mods = ModifiersState::default();
|
||||
|
||||
let written = unsafe {
|
||||
use std::str;
|
||||
|
||||
let mut buffer: [u8; 16] = [mem::uninitialized(); 16];
|
||||
let raw_ev: *mut ffi::XKeyEvent = event;
|
||||
let count = (self.display.xlib.Xutf8LookupString)(self.ic, mem::transmute(raw_ev),
|
||||
mem::transmute(buffer.as_mut_ptr()),
|
||||
buffer.len() as libc::c_int, &mut kp_keysym, ptr::null_mut());
|
||||
|
||||
{
|
||||
// Translate x event state to mods
|
||||
let state = event.state;
|
||||
if (state & ffi::Mod1Mask) != 0 {
|
||||
ev_mods.alt = true;
|
||||
}
|
||||
|
||||
if (state & ffi::ShiftMask) != 0 {
|
||||
ev_mods.shift = true;
|
||||
}
|
||||
|
||||
if (state & ffi::ControlMask) != 0 {
|
||||
ev_mods.ctrl = true;
|
||||
}
|
||||
|
||||
if (state & ffi::Mod4Mask) != 0 {
|
||||
ev_mods.logo = true;
|
||||
}
|
||||
}
|
||||
|
||||
str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string()
|
||||
};
|
||||
|
||||
for chr in written.chars() {
|
||||
translated_events.push(ReceivedCharacter(chr));
|
||||
}
|
||||
|
||||
let mut keysym = unsafe {
|
||||
(self.display.xlib.XKeycodeToKeysym)(self.display.display, event.keycode as ffi::KeyCode, 0)
|
||||
};
|
||||
|
||||
if (ffi::XK_KP_Space as libc::c_ulong <= keysym) && (keysym <= ffi::XK_KP_9 as libc::c_ulong) {
|
||||
keysym = kp_keysym
|
||||
};
|
||||
|
||||
let vkey = events::keycode_to_element(keysym as libc::c_uint);
|
||||
|
||||
translated_events.push(KeyboardInput(state, event.keycode as u8, vkey, ev_mods));
|
||||
translated_events
|
||||
}
|
||||
|
||||
pub fn translate_event(&mut self, cookie: &ffi::XGenericEventCookie) -> Option<Event> {
|
||||
use events::WindowEvent::{Focused, MouseEntered, MouseInput, MouseLeft, MouseMoved, MouseWheel};
|
||||
use events::ElementState::{Pressed, Released};
|
||||
use events::MouseButton::{Left, Right, Middle};
|
||||
use events::MouseScrollDelta::LineDelta;
|
||||
use events::{Touch, TouchPhase};
|
||||
|
||||
match cookie.evtype {
|
||||
ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
|
||||
let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
|
||||
if self.multitouch && (event_data.flags & ffi::XIPointerEmulated) != 0 {
|
||||
// Deliver multi-touch events instead of emulated mouse events.
|
||||
return None
|
||||
}
|
||||
let state = if cookie.evtype == ffi::XI_ButtonPress {
|
||||
Pressed
|
||||
} else {
|
||||
Released
|
||||
};
|
||||
match event_data.detail as u32 {
|
||||
ffi::Button1 => Some(MouseInput(state, Left)),
|
||||
ffi::Button2 => Some(MouseInput(state, Middle)),
|
||||
ffi::Button3 => Some(MouseInput(state, Right)),
|
||||
ffi::Button4 | ffi::Button5 => {
|
||||
if event_data.flags & ffi::XIPointerEmulated == 0 {
|
||||
// scroll event from a traditional wheel with
|
||||
// distinct 'clicks'
|
||||
let delta = if event_data.detail as u32 == ffi::Button4 {
|
||||
1.0
|
||||
} else {
|
||||
-1.0
|
||||
};
|
||||
Some(MouseWheel(LineDelta(0.0, delta), TouchPhase::Moved))
|
||||
} else {
|
||||
// emulated button event from a touch/smooth-scroll
|
||||
// event. Ignore these events and handle scrolling
|
||||
// via XI_Motion event handler instead
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
},
|
||||
ffi::XI_Motion => {
|
||||
let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
|
||||
if self.multitouch && (event_data.flags & ffi::XIPointerEmulated) != 0 {
|
||||
// Deliver multi-touch events instead of emulated mouse events.
|
||||
return None
|
||||
}
|
||||
let axis_state = event_data.valuators;
|
||||
let mask = unsafe{ from_raw_parts(axis_state.mask, axis_state.mask_len as usize) };
|
||||
let mut axis_count = 0;
|
||||
|
||||
let mut scroll_delta = (0.0, 0.0);
|
||||
for axis_id in 0..axis_state.mask_len {
|
||||
if ffi::XIMaskIsSet(&mask, axis_id) {
|
||||
let axis_value = unsafe{*axis_state.values.offset(axis_count)};
|
||||
let delta = calc_scroll_deltas(event_data, axis_id, axis_value, &self.axis_list,
|
||||
&mut self.current_state.axis_values);
|
||||
scroll_delta.0 += delta.0;
|
||||
scroll_delta.1 += delta.1;
|
||||
axis_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if scroll_delta.0.abs() > 0.0 || scroll_delta.1.abs() > 0.0 {
|
||||
Some(MouseWheel(LineDelta(scroll_delta.0 as f32, scroll_delta.1 as f32),
|
||||
TouchPhase::Moved))
|
||||
} else {
|
||||
let new_cursor_pos = (event_data.event_x, event_data.event_y);
|
||||
if new_cursor_pos != self.current_state.cursor_pos {
|
||||
self.current_state.cursor_pos = new_cursor_pos;
|
||||
Some(MouseMoved(new_cursor_pos.0 as i32, new_cursor_pos.1 as i32))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
ffi::XI_Enter => {
|
||||
// axis movements whilst the cursor is outside the window
|
||||
// will alter the absolute value of the axes. We only want to
|
||||
// report changes in the axis value whilst the cursor is above
|
||||
// our window however, so clear the previous axis state whenever
|
||||
// the cursor re-enters the window
|
||||
self.current_state.axis_values.clear();
|
||||
Some(MouseEntered)
|
||||
},
|
||||
ffi::XI_Leave => Some(MouseLeft),
|
||||
ffi::XI_FocusIn => Some(Focused(true)),
|
||||
ffi::XI_FocusOut => Some(Focused(false)),
|
||||
ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
|
||||
if !self.multitouch {
|
||||
return None
|
||||
}
|
||||
let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
|
||||
let phase = match cookie.evtype {
|
||||
ffi::XI_TouchBegin => TouchPhase::Started,
|
||||
ffi::XI_TouchUpdate => TouchPhase::Moved,
|
||||
ffi::XI_TouchEnd => TouchPhase::Ended,
|
||||
_ => unreachable!()
|
||||
};
|
||||
Some(Event::Touch(Touch {
|
||||
phase: phase,
|
||||
location: (event_data.event_x, event_data.event_y),
|
||||
id: event_data.detail as u64,
|
||||
}))
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_input_axis_info(display: &Arc<XConnection>) -> Vec<Axis> {
|
||||
let mut axis_list = Vec::new();
|
||||
let mut device_count = 0;
|
||||
|
||||
// Check all input devices for scroll axes.
|
||||
let devices = unsafe{
|
||||
(display.xinput2.XIQueryDevice)(display.display, ffi::XIAllDevices, &mut device_count)
|
||||
};
|
||||
for i in 0..device_count {
|
||||
let device = unsafe { *(devices.offset(i as isize)) };
|
||||
for k in 0..device.num_classes {
|
||||
let class = unsafe { *(device.classes.offset(k as isize)) };
|
||||
match unsafe { (*class)._type } {
|
||||
// Note that scroll axis
|
||||
// are reported both as 'XIScrollClass' and 'XIValuatorClass'
|
||||
// axes. For the moment we only care about scrolling axes.
|
||||
ffi::XIScrollClass => {
|
||||
let scroll_class: &ffi::XIScrollClassInfo = unsafe{mem::transmute(class)};
|
||||
axis_list.push(Axis{
|
||||
id: scroll_class.sourceid,
|
||||
device_id: device.deviceid,
|
||||
axis_number: scroll_class.number,
|
||||
axis_type: match scroll_class.scroll_type {
|
||||
ffi::XIScrollTypeHorizontal => AxisType::HorizontalScroll,
|
||||
ffi::XIScrollTypeVertical => AxisType::VerticalScroll,
|
||||
_ => { unreachable!() }
|
||||
},
|
||||
scroll_increment: scroll_class.increment,
|
||||
})
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
(display.xinput2.XIFreeDeviceInfo)(devices);
|
||||
}
|
||||
|
||||
axis_list
|
||||
}
|
||||
|
||||
/// Given an input motion event for an axis and the previous
|
||||
/// state of the axes, return the horizontal/vertical
|
||||
/// scroll deltas
|
||||
fn calc_scroll_deltas(event: &ffi::XIDeviceEvent,
|
||||
axis_id: i32,
|
||||
axis_value: f64,
|
||||
axis_list: &[Axis],
|
||||
prev_axis_values: &mut Vec<AxisValue>) -> (f64, f64) {
|
||||
let prev_value_pos = prev_axis_values.iter().position(|prev_axis| {
|
||||
prev_axis.device_id == event.sourceid &&
|
||||
prev_axis.axis_number == axis_id
|
||||
});
|
||||
let delta = match prev_value_pos {
|
||||
Some(idx) => prev_axis_values[idx].value - axis_value,
|
||||
None => 0.0
|
||||
};
|
||||
|
||||
let new_axis_value = AxisValue{
|
||||
device_id: event.sourceid,
|
||||
axis_number: axis_id,
|
||||
value: axis_value
|
||||
};
|
||||
|
||||
match prev_value_pos {
|
||||
Some(idx) => prev_axis_values[idx] = new_axis_value,
|
||||
None => prev_axis_values.push(new_axis_value)
|
||||
}
|
||||
|
||||
let mut scroll_delta = (0.0, 0.0);
|
||||
|
||||
for axis in axis_list.iter() {
|
||||
if axis.id == event.sourceid &&
|
||||
axis.axis_number == axis_id {
|
||||
match axis.axis_type {
|
||||
AxisType::HorizontalScroll => scroll_delta.0 = delta / axis.scroll_increment,
|
||||
AxisType::VerticalScroll => scroll_delta.1 = delta / axis.scroll_increment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scroll_delta
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,44 +1,129 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Arc;
|
||||
use std::slice;
|
||||
|
||||
use super::XConnection;
|
||||
use native_monitor::NativeMonitorId;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorId(pub Arc<XConnection>, pub u32);
|
||||
pub struct MonitorId {
|
||||
/// The actual id
|
||||
id: u32,
|
||||
/// The name of the monitor
|
||||
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>) -> VecDeque<MonitorId> {
|
||||
let nb_monitors = unsafe { (x.xlib.XScreenCount)(x.display) };
|
||||
x.check_errors().expect("Failed to call XScreenCount");
|
||||
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);
|
||||
|
||||
let mut monitors = VecDeque::new();
|
||||
monitors.extend((0 .. nb_monitors).map(|i| MonitorId(x.clone(), i as u32)));
|
||||
monitors
|
||||
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 {
|
||||
let primary_monitor = unsafe { (x.xlib.XDefaultScreen)(x.display) };
|
||||
x.check_errors().expect("Failed to call XDefaultScreen");
|
||||
MonitorId(x.clone(), primary_monitor as u32)
|
||||
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")
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
let MonitorId(_, screen_num) = *self;
|
||||
Some(format!("Monitor #{}", screen_num))
|
||||
Some(self.name.clone())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> NativeMonitorId {
|
||||
NativeMonitorId::Numeric(self.1)
|
||||
pub fn get_native_identifier(&self) -> u32 {
|
||||
self.id as u32
|
||||
}
|
||||
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
let screen = unsafe { (self.0.xlib.XScreenOfDisplay)(self.0.display, self.1 as i32) };
|
||||
let width = unsafe { (self.0.xlib.XWidthOfScreen)(screen) };
|
||||
let height = unsafe { (self.0.xlib.XHeightOfScreen)(screen) };
|
||||
self.0.check_errors().expect("Failed to get monitor dimensions");
|
||||
(width as u32, height as u32)
|
||||
self.dimensions
|
||||
}
|
||||
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
self.position
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
self.hidpi_factor
|
||||
}
|
||||
}
|
||||
|
||||
365
src/platform/linux/x11/util.rs
Normal file
365
src/platform/linux/x11/util.rs
Normal file
@@ -0,0 +1,365 @@
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::{c_char, c_double, c_int, c_long, c_short, c_uchar, c_uint, c_ulong};
|
||||
|
||||
use super::{ffi, XConnection, XError};
|
||||
use events::ModifiersState;
|
||||
|
||||
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 _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_atom(xconn: &Arc<XConnection>, name: &[u8]) -> Result<ffi::Atom, XError> {
|
||||
let atom_name: *const c_char = name.as_ptr() as _;
|
||||
let atom = (xconn.xlib.XInternAtom)(xconn.display, atom_name, ffi::False);
|
||||
xconn.check_errors().map(|_| atom)
|
||||
}
|
||||
|
||||
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),
|
||||
) -> Result<(), XError> {
|
||||
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 = 32;
|
||||
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);
|
||||
|
||||
let event_mask = event_mask.unwrap_or(ffi::NoEventMask);
|
||||
|
||||
(xconn.xlib.XSendEvent)(
|
||||
xconn.display,
|
||||
target_window,
|
||||
ffi::False,
|
||||
event_mask,
|
||||
&mut event.into(),
|
||||
);
|
||||
|
||||
xconn.check_errors().map(|_| ())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GetPropertyError {
|
||||
XError(XError),
|
||||
TypeMismatch(ffi::Atom),
|
||||
FormatMismatch(c_int),
|
||||
NothingAllocated,
|
||||
}
|
||||
|
||||
impl GetPropertyError {
|
||||
pub fn is_actual_property_type(&self, t: ffi::Atom) -> bool {
|
||||
if let GetPropertyError::TypeMismatch(actual_type) = *self {
|
||||
actual_type == t
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_property<T>(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: c_ulong,
|
||||
property: ffi::Atom,
|
||||
property_type: ffi::Atom,
|
||||
) -> Result<Vec<T>, GetPropertyError> {
|
||||
let mut data = Vec::new();
|
||||
|
||||
let mut done = false;
|
||||
while !done {
|
||||
let mut actual_type: ffi::Atom = mem::uninitialized();
|
||||
let mut actual_format: c_int = mem::uninitialized();
|
||||
let mut byte_count: 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,
|
||||
(data.len() / 4) as c_long,
|
||||
1024,
|
||||
ffi::False,
|
||||
property_type,
|
||||
&mut actual_type,
|
||||
&mut actual_format,
|
||||
&mut byte_count,
|
||||
&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));
|
||||
}
|
||||
|
||||
// Fun fact: actual_format ISN'T the size of the type; it's more like a really bad enum
|
||||
let format_mismatch = match actual_format as usize {
|
||||
8 => mem::size_of::<T>() != mem::size_of::<c_char>(),
|
||||
16 => mem::size_of::<T>() != mem::size_of::<c_short>(),
|
||||
32 => mem::size_of::<T>() != mem::size_of::<c_long>(),
|
||||
_ => true, // this won't actually be reached; the XError condition above is triggered
|
||||
};
|
||||
|
||||
if format_mismatch {
|
||||
return Err(GetPropertyError::FormatMismatch(actual_format));
|
||||
}
|
||||
|
||||
if !buf.is_null() {
|
||||
let mut buf =
|
||||
Vec::from_raw_parts(buf as *mut T, byte_count as usize, byte_count as usize);
|
||||
data.append(&mut buf);
|
||||
} else {
|
||||
return Err(GetPropertyError::NothingAllocated);
|
||||
}
|
||||
|
||||
done = bytes_after == 0;
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
impl From<ffi::XIModifierState> for ModifiersState {
|
||||
fn from(mods: ffi::XIModifierState) -> Self {
|
||||
let state = mods.effective as c_uint;
|
||||
ModifiersState {
|
||||
alt: state & ffi::Mod1Mask != 0,
|
||||
shift: state & ffi::ShiftMask != 0,
|
||||
ctrl: state & ffi::ControlMask != 0,
|
||||
logo: state & ffi::Mod4Mask != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PointerState {
|
||||
#[allow(dead_code)]
|
||||
root: ffi::Window,
|
||||
#[allow(dead_code)]
|
||||
child: ffi::Window,
|
||||
#[allow(dead_code)]
|
||||
root_x: c_double,
|
||||
#[allow(dead_code)]
|
||||
root_y: c_double,
|
||||
#[allow(dead_code)]
|
||||
win_x: c_double,
|
||||
#[allow(dead_code)]
|
||||
win_y: c_double,
|
||||
#[allow(dead_code)]
|
||||
buttons: ffi::XIButtonState,
|
||||
modifiers: ffi::XIModifierState,
|
||||
#[allow(dead_code)]
|
||||
group: ffi::XIGroupState,
|
||||
#[allow(dead_code)]
|
||||
relative_to_window: bool,
|
||||
}
|
||||
|
||||
impl PointerState {
|
||||
pub fn get_modifier_state(&self) -> ModifiersState {
|
||||
self.modifiers.into()
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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;
|
||||
|
||||
xconn.check_errors()?;
|
||||
|
||||
Ok(PointerState {
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
pub unsafe fn lookup_utf8(
|
||||
xconn: &Arc<XConnection>,
|
||||
ic: ffi::XIC,
|
||||
key_event: &mut ffi::XKeyEvent,
|
||||
) -> String {
|
||||
const INIT_BUFF_SIZE: usize = 16;
|
||||
|
||||
// Buffer allocated on heap instead of stack, due to the possible reallocation
|
||||
let mut buffer: Vec<u8> = vec![mem::uninitialized(); INIT_BUFF_SIZE];
|
||||
let (_, status, mut count) = lookup_utf8_inner(
|
||||
xconn,
|
||||
ic,
|
||||
key_event,
|
||||
&mut buffer,
|
||||
);
|
||||
|
||||
// Buffer overflowed, dynamically reallocate
|
||||
if status == ffi::XBufferOverflow {
|
||||
buffer = vec![mem::uninitialized(); count as usize];
|
||||
let (_, _, new_count) = lookup_utf8_inner(
|
||||
xconn,
|
||||
ic,
|
||||
key_event,
|
||||
&mut buffer,
|
||||
);
|
||||
count = new_count;
|
||||
}
|
||||
|
||||
str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FrameExtents {
|
||||
pub left: c_ulong,
|
||||
pub right: c_ulong,
|
||||
pub top: c_ulong,
|
||||
pub bottom: c_ulong,
|
||||
}
|
||||
|
||||
impl FrameExtents {
|
||||
pub fn new(left: c_ulong, right: c_ulong, top: c_ulong, bottom: c_ulong) -> Self {
|
||||
FrameExtents { left, right, top, bottom }
|
||||
}
|
||||
|
||||
pub fn from_border(border: c_ulong) -> Self {
|
||||
Self::new(border, border, border, border)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WindowGeometry {
|
||||
pub x: c_int,
|
||||
pub y: c_int,
|
||||
pub width: c_uint,
|
||||
pub height: c_uint,
|
||||
pub frame: FrameExtents,
|
||||
}
|
||||
|
||||
impl WindowGeometry {
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
(self.x as _, self.y as _)
|
||||
}
|
||||
|
||||
pub fn get_inner_position(&self) -> (i32, i32) {
|
||||
(
|
||||
self.x.saturating_add(self.frame.left as c_int) as _,
|
||||
self.y.saturating_add(self.frame.top as c_int) as _,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_inner_size(&self) -> (u32, u32) {
|
||||
(self.width as _, self.height as _)
|
||||
}
|
||||
|
||||
pub fn get_outer_size(&self) -> (u32, u32) {
|
||||
(
|
||||
self.width.saturating_add(
|
||||
self.frame.left.saturating_add(self.frame.right) as c_uint
|
||||
) as _,
|
||||
self.height.saturating_add(
|
||||
self.frame.top.saturating_add(self.frame.bottom) as c_uint
|
||||
) as _,
|
||||
)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,10 @@ use super::ffi;
|
||||
/// A connection to an X server.
|
||||
pub struct XConnection {
|
||||
pub xlib: ffi::Xlib,
|
||||
pub xf86vmode: ffi::Xf86vmode,
|
||||
/// Exposes XRandR functions from version < 1.5
|
||||
pub xrandr: ffi::Xrandr_2_2_0,
|
||||
/// Exposes XRandR functions from version = 1.5
|
||||
pub xrandr_1_5: Option<ffi::Xrandr>,
|
||||
pub xcursor: ffi::Xcursor,
|
||||
pub xinput2: ffi::XInput2,
|
||||
pub xlib_xcb: ffi::Xlib_xcb,
|
||||
@@ -28,7 +31,8 @@ impl XConnection {
|
||||
// opening the libraries
|
||||
let xlib = try!(ffi::Xlib::open());
|
||||
let xcursor = try!(ffi::Xcursor::open());
|
||||
let xf86vmode = try!(ffi::Xf86vmode::open());
|
||||
let xrandr = try!(ffi::Xrandr_2_2_0::open());
|
||||
let xrandr_1_5 = ffi::Xrandr::open().ok();
|
||||
let xinput2 = try!(ffi::XInput2::open());
|
||||
let xlib_xcb = try!(ffi::Xlib_xcb::open());
|
||||
|
||||
@@ -46,7 +50,8 @@ impl XConnection {
|
||||
|
||||
Ok(XConnection {
|
||||
xlib: xlib,
|
||||
xf86vmode: xf86vmode,
|
||||
xrandr: xrandr,
|
||||
xrandr_1_5: xrandr_1_5,
|
||||
xcursor: xcursor,
|
||||
xinput2: xinput2,
|
||||
xlib_xcb: xlib_xcb,
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
use {ControlFlow, EventsLoopClosed};
|
||||
use cocoa::{self, appkit, foundation};
|
||||
use cocoa::appkit::{NSApplication, NSEvent, NSView, NSWindow};
|
||||
use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent, ModifiersState};
|
||||
use super::window::Window;
|
||||
use cocoa::appkit::{NSApplication, NSEvent, NSEventMask, NSEventModifierFlags, NSEventPhase, NSView, NSWindow};
|
||||
use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent, DeviceEvent, ModifiersState, KeyboardInput};
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
use super::window::Window2;
|
||||
use std;
|
||||
use super::DeviceId;
|
||||
|
||||
|
||||
pub struct EventsLoop {
|
||||
pub windows: std::sync::Mutex<Vec<std::sync::Weak<Window>>>,
|
||||
pub pending_events: std::sync::Mutex<std::collections::VecDeque<Event>>,
|
||||
modifiers: std::sync::Mutex<Modifiers>,
|
||||
interrupted: std::sync::atomic::AtomicBool,
|
||||
modifiers: Modifiers,
|
||||
pub shared: Arc<Shared>,
|
||||
}
|
||||
|
||||
// State shared between the `EventsLoop` and its registered windows.
|
||||
pub struct Shared {
|
||||
pub windows: Mutex<Vec<Weak<Window2>>>,
|
||||
pub pending_events: Mutex<VecDeque<Event>>,
|
||||
// The user event callback given via either of the `poll_events` or `run_forever` methods.
|
||||
//
|
||||
// We store the user's callback here so that it may be accessed by each of the window delegate
|
||||
@@ -22,6 +29,9 @@ pub struct EventsLoop {
|
||||
user_callback: UserCallback,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Proxy {}
|
||||
|
||||
struct Modifiers {
|
||||
shift_pressed: bool,
|
||||
ctrl_pressed: bool,
|
||||
@@ -33,14 +43,76 @@ struct Modifiers {
|
||||
//
|
||||
// - ensure the callback pointer is never accidentally cloned
|
||||
// - ensure that only the `EventsLoop` can `store` and `drop` the callback pointer
|
||||
// - `unsafe impl Send` and `Sync` so that `Send` and `Sync` can be implemented for `EventsLoop`.
|
||||
// - Share access to the user callback with the NSWindow callbacks.
|
||||
pub struct UserCallback {
|
||||
mutex: std::sync::Mutex<Option<*mut FnMut(Event)>>,
|
||||
mutex: Mutex<Option<*mut FnMut(Event)>>,
|
||||
}
|
||||
|
||||
|
||||
unsafe impl Send for UserCallback {}
|
||||
unsafe impl Sync for UserCallback {}
|
||||
impl Shared {
|
||||
|
||||
pub fn new() -> Self {
|
||||
Shared {
|
||||
windows: Mutex::new(Vec::new()),
|
||||
pending_events: Mutex::new(VecDeque::new()),
|
||||
user_callback: UserCallback { mutex: Mutex::new(None) },
|
||||
}
|
||||
}
|
||||
|
||||
fn call_user_callback_with_pending_events(&self) {
|
||||
loop {
|
||||
let event = match self.pending_events.lock().unwrap().pop_front() {
|
||||
Some(event) => event,
|
||||
None => return,
|
||||
};
|
||||
unsafe {
|
||||
self.user_callback.call_with_event(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calls the user callback if one exists.
|
||||
//
|
||||
// Otherwise, stores the event in the `pending_events` queue.
|
||||
//
|
||||
// This is necessary for the case when `WindowDelegate` callbacks are triggered during a call
|
||||
// to the user's callback.
|
||||
pub fn call_user_callback_with_event_or_store_in_pending(&self, event: Event) {
|
||||
if self.user_callback.mutex.lock().unwrap().is_some() {
|
||||
unsafe {
|
||||
self.user_callback.call_with_event(event);
|
||||
}
|
||||
} else {
|
||||
self.pending_events.lock().unwrap().push_back(event);
|
||||
}
|
||||
}
|
||||
|
||||
// Removes the window with the given `Id` from the `windows` list.
|
||||
//
|
||||
// This is called in response to `windowWillClose`.
|
||||
pub fn find_and_remove_window(&self, id: super::window::Id) {
|
||||
if let Ok(mut windows) = self.windows.lock() {
|
||||
windows.retain(|w| match w.upgrade() {
|
||||
Some(w) => w.id() != id,
|
||||
None => true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
impl Modifiers {
|
||||
pub fn new() -> Self {
|
||||
Modifiers {
|
||||
shift_pressed: false,
|
||||
ctrl_pressed: false,
|
||||
win_pressed: false,
|
||||
alt_pressed: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl UserCallback {
|
||||
|
||||
@@ -91,22 +163,20 @@ impl UserCallback {
|
||||
impl EventsLoop {
|
||||
|
||||
pub fn new() -> Self {
|
||||
let modifiers = Modifiers {
|
||||
shift_pressed: false,
|
||||
ctrl_pressed: false,
|
||||
win_pressed: false,
|
||||
alt_pressed: false,
|
||||
};
|
||||
// Mark this thread as the main thread of the Cocoa event system.
|
||||
//
|
||||
// This must be done before any worker threads get a chance to call it
|
||||
// (e.g., via `EventsLoopProxy::wakeup()`), causing a wrong thread to be
|
||||
// marked as the main thread.
|
||||
unsafe { appkit::NSApp(); }
|
||||
|
||||
EventsLoop {
|
||||
windows: std::sync::Mutex::new(Vec::new()),
|
||||
pending_events: std::sync::Mutex::new(std::collections::VecDeque::new()),
|
||||
modifiers: std::sync::Mutex::new(modifiers),
|
||||
interrupted: std::sync::atomic::AtomicBool::new(false),
|
||||
user_callback: UserCallback { mutex: std::sync::Mutex::new(None) },
|
||||
shared: Arc::new(Shared::new()),
|
||||
modifiers: Modifiers::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_events<F>(&self, mut callback: F)
|
||||
pub fn poll_events<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(Event),
|
||||
{
|
||||
unsafe {
|
||||
@@ -115,19 +185,19 @@ impl EventsLoop {
|
||||
}
|
||||
}
|
||||
|
||||
self.user_callback.store(&mut callback);
|
||||
self.shared.user_callback.store(&mut callback);
|
||||
|
||||
// Loop as long as we have pending events to return.
|
||||
loop {
|
||||
unsafe {
|
||||
// First, yield all pending events.
|
||||
self.call_user_callback_with_pending_events();
|
||||
self.shared.call_user_callback_with_pending_events();
|
||||
|
||||
let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
|
||||
|
||||
// Poll for the next event, returning `nil` if there are none.
|
||||
let ns_event = appkit::NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_(
|
||||
appkit::NSAnyEventMask.bits() | appkit::NSEventMaskPressure.bits(),
|
||||
NSEventMask::NSAnyEventMask.bits() | NSEventMask::NSEventMaskPressure.bits(),
|
||||
foundation::NSDate::distantPast(cocoa::base::nil),
|
||||
foundation::NSDefaultRunLoopMode,
|
||||
cocoa::base::YES);
|
||||
@@ -138,38 +208,48 @@ impl EventsLoop {
|
||||
|
||||
match event {
|
||||
// Call the user's callback.
|
||||
Some(event) => self.user_callback.call_with_event(event),
|
||||
Some(event) => self.shared.user_callback.call_with_event(event),
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.user_callback.drop();
|
||||
self.shared.user_callback.drop();
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&self, mut callback: F)
|
||||
where F: FnMut(Event)
|
||||
pub fn run_forever<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(Event) -> ControlFlow
|
||||
{
|
||||
self.interrupted.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
unsafe {
|
||||
if !msg_send![cocoa::base::class("NSThread"), isMainThread] {
|
||||
panic!("Events can only be polled from the main thread on macOS");
|
||||
}
|
||||
}
|
||||
|
||||
self.user_callback.store(&mut callback);
|
||||
// Track whether or not control flow has changed.
|
||||
let control_flow = std::cell::Cell::new(ControlFlow::Continue);
|
||||
|
||||
let mut callback = |event| {
|
||||
if let ControlFlow::Break = callback(event) {
|
||||
control_flow.set(ControlFlow::Break);
|
||||
}
|
||||
};
|
||||
|
||||
self.shared.user_callback.store(&mut callback);
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
// First, yield all pending events.
|
||||
self.call_user_callback_with_pending_events();
|
||||
self.shared.call_user_callback_with_pending_events();
|
||||
if let ControlFlow::Break = control_flow.get() {
|
||||
break;
|
||||
}
|
||||
|
||||
let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
|
||||
|
||||
// Wait for the next event. Note that this function blocks during resize.
|
||||
let ns_event = appkit::NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_(
|
||||
appkit::NSAnyEventMask.bits() | appkit::NSEventMaskPressure.bits(),
|
||||
NSEventMask::NSAnyEventMask.bits() | NSEventMask::NSEventMaskPressure.bits(),
|
||||
foundation::NSDate::distantFuture(cocoa::base::nil),
|
||||
foundation::NSDefaultRunLoopMode,
|
||||
cocoa::base::YES);
|
||||
@@ -181,84 +261,19 @@ impl EventsLoop {
|
||||
let _: () = msg_send![pool, release];
|
||||
|
||||
if let Some(event) = maybe_event {
|
||||
self.user_callback.call_with_event(event);
|
||||
self.shared.user_callback.call_with_event(event);
|
||||
if let ControlFlow::Break = control_flow.get() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.interrupted.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
self.interrupted.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.user_callback.drop();
|
||||
}
|
||||
|
||||
pub fn interrupt(&self) {
|
||||
self.interrupted.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
// Awaken the event loop by triggering `NSApplicationActivatedEventType`.
|
||||
unsafe {
|
||||
let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
|
||||
let event =
|
||||
NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
|
||||
cocoa::base::nil,
|
||||
appkit::NSApplicationDefined,
|
||||
foundation::NSPoint::new(0.0, 0.0),
|
||||
appkit::NSEventModifierFlags::empty(),
|
||||
0.0,
|
||||
0,
|
||||
cocoa::base::nil,
|
||||
appkit::NSEventSubtype::NSApplicationActivatedEventType,
|
||||
0,
|
||||
0);
|
||||
appkit::NSApp().postEvent_atStart_(event, cocoa::base::NO);
|
||||
foundation::NSAutoreleasePool::drain(pool);
|
||||
}
|
||||
}
|
||||
|
||||
// Removes the window with the given `Id` from the `windows` list.
|
||||
//
|
||||
// This is called when a window is either `Closed` or `Drop`ped.
|
||||
pub fn find_and_remove_window(&self, id: super::window::Id) {
|
||||
if let Ok(mut windows) = self.windows.lock() {
|
||||
windows.retain(|w| match w.upgrade() {
|
||||
Some(w) => w.id() != id,
|
||||
None => true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn call_user_callback_with_pending_events(&self) {
|
||||
loop {
|
||||
let event = match self.pending_events.lock().unwrap().pop_front() {
|
||||
Some(event) => event,
|
||||
None => return,
|
||||
};
|
||||
unsafe {
|
||||
self.user_callback.call_with_event(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calls the user callback if one exists.
|
||||
//
|
||||
// Otherwise, stores the event in the `pending_events` queue.
|
||||
//
|
||||
// This is necessary for the case when `WindowDelegate` callbacks are triggered during a call
|
||||
// to the user's callback.
|
||||
pub fn call_user_callback_with_event_or_store_in_pending(&self, event: Event) {
|
||||
if self.user_callback.mutex.lock().unwrap().is_some() {
|
||||
unsafe {
|
||||
self.user_callback.call_with_event(event);
|
||||
}
|
||||
} else {
|
||||
self.pending_events.lock().unwrap().push_back(event);
|
||||
}
|
||||
self.shared.user_callback.drop();
|
||||
}
|
||||
|
||||
// Convert some given `NSEvent` into a winit `Event`.
|
||||
unsafe fn ns_event_to_event(&self, ns_event: cocoa::base::id) -> Option<Event> {
|
||||
unsafe fn ns_event_to_event(&mut self, ns_event: cocoa::base::id) -> Option<Event> {
|
||||
if ns_event == cocoa::base::nil {
|
||||
return None;
|
||||
}
|
||||
@@ -284,9 +299,9 @@ impl EventsLoop {
|
||||
_ => appkit::NSApp().sendEvent_(ns_event),
|
||||
}
|
||||
|
||||
let windows = self.windows.lock().unwrap();
|
||||
let windows = self.shared.windows.lock().unwrap();
|
||||
let maybe_window = windows.iter()
|
||||
.filter_map(std::sync::Weak::upgrade)
|
||||
.filter_map(Weak::upgrade)
|
||||
.find(|window| window_id == window.id());
|
||||
|
||||
let into_event = |window_event| Event::WindowEvent {
|
||||
@@ -296,7 +311,7 @@ impl EventsLoop {
|
||||
|
||||
// Returns `Some` window if one of our windows is the key window.
|
||||
let maybe_key_window = || windows.iter()
|
||||
.filter_map(std::sync::Weak::upgrade)
|
||||
.filter_map(Weak::upgrade)
|
||||
.find(|window| {
|
||||
let is_key_window: cocoa::base::BOOL = msg_send![*window.window, isKeyWindow];
|
||||
is_key_window == cocoa::base::YES
|
||||
@@ -308,48 +323,76 @@ impl EventsLoop {
|
||||
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));
|
||||
}
|
||||
|
||||
let vkey = to_virtual_key_code(NSEvent::keyCode(ns_event));
|
||||
let state = ElementState::Pressed;
|
||||
let code = NSEvent::keyCode(ns_event) as u8;
|
||||
let window_event = WindowEvent::KeyboardInput(state, code, vkey, event_mods(ns_event));
|
||||
events.push_back(into_event(window_event));
|
||||
let event = events.pop_front();
|
||||
self.pending_events.lock().unwrap().extend(events.into_iter());
|
||||
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 u8;
|
||||
let window_event = WindowEvent::KeyboardInput(state, code, vkey, event_mods(ns_event));
|
||||
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 => {
|
||||
let mut modifiers = self.modifiers.lock().unwrap();
|
||||
|
||||
unsafe fn modifier_event(event: cocoa::base::id,
|
||||
keymask: appkit::NSEventModifierFlags,
|
||||
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 u8;
|
||||
let window_event = WindowEvent::KeyboardInput(state, code, Some(key), event_mods(event));
|
||||
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 u8;
|
||||
let window_event = WindowEvent::KeyboardInput(state, code, Some(key), event_mods(event));
|
||||
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 {
|
||||
@@ -359,55 +402,80 @@ impl EventsLoop {
|
||||
|
||||
let mut events = std::collections::VecDeque::new();
|
||||
if let Some(window_event) = modifier_event(ns_event,
|
||||
appkit::NSShiftKeyMask,
|
||||
NSEventModifierFlags::NSShiftKeyMask,
|
||||
events::VirtualKeyCode::LShift,
|
||||
modifiers.shift_pressed)
|
||||
self.modifiers.shift_pressed)
|
||||
{
|
||||
modifiers.shift_pressed = !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,
|
||||
appkit::NSControlKeyMask,
|
||||
NSEventModifierFlags::NSControlKeyMask,
|
||||
events::VirtualKeyCode::LControl,
|
||||
modifiers.ctrl_pressed)
|
||||
self.modifiers.ctrl_pressed)
|
||||
{
|
||||
modifiers.ctrl_pressed = !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,
|
||||
appkit::NSCommandKeyMask,
|
||||
NSEventModifierFlags::NSCommandKeyMask,
|
||||
events::VirtualKeyCode::LWin,
|
||||
modifiers.win_pressed)
|
||||
self.modifiers.win_pressed)
|
||||
{
|
||||
modifiers.win_pressed = !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,
|
||||
appkit::NSAlternateKeyMask,
|
||||
NSEventModifierFlags::NSAlternateKeyMask,
|
||||
events::VirtualKeyCode::LAlt,
|
||||
modifiers.alt_pressed)
|
||||
self.modifiers.alt_pressed)
|
||||
{
|
||||
modifiers.alt_pressed = !modifiers.alt_pressed;
|
||||
self.modifiers.alt_pressed = !self.modifiers.alt_pressed;
|
||||
events.push_back(into_event(window_event));
|
||||
}
|
||||
|
||||
let event = events.pop_front();
|
||||
self.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(ElementState::Pressed, MouseButton::Left))) },
|
||||
appkit::NSLeftMouseUp => { Some(into_event(WindowEvent::MouseInput(ElementState::Released, MouseButton::Left))) },
|
||||
appkit::NSRightMouseDown => { Some(into_event(WindowEvent::MouseInput(ElementState::Pressed, MouseButton::Right))) },
|
||||
appkit::NSRightMouseUp => { Some(into_event(WindowEvent::MouseInput(ElementState::Released, MouseButton::Right))) },
|
||||
appkit::NSOtherMouseDown => { Some(into_event(WindowEvent::MouseInput(ElementState::Pressed, MouseButton::Middle))) },
|
||||
appkit::NSOtherMouseUp => { Some(into_event(WindowEvent::MouseInput(ElementState::Released, MouseButton::Middle))) },
|
||||
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 => { Some(into_event(WindowEvent::MouseEntered)) },
|
||||
appkit::NSMouseExited => { Some(into_event(WindowEvent::MouseLeft)) },
|
||||
appkit::NSMouseEntered => {
|
||||
let window = match maybe_window.or_else(maybe_key_window) {
|
||||
Some(window) => 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 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 };
|
||||
|
||||
self.shared.pending_events.lock().unwrap().push_back(event);
|
||||
Some(into_event(WindowEvent::CursorEntered { device_id: DEVICE_ID }))
|
||||
},
|
||||
appkit::NSMouseExited => { Some(into_event(WindowEvent::CursorLeft { device_id: DEVICE_ID })) },
|
||||
|
||||
appkit::NSMouseMoved |
|
||||
appkit::NSLeftMouseDragged |
|
||||
@@ -433,11 +501,39 @@ impl EventsLoop {
|
||||
let view_rect = NSView::frame(*window.view);
|
||||
let scale_factor = window.hidpi_factor();
|
||||
|
||||
let x = (scale_factor * view_point.x as f32) as i32;
|
||||
let y = (scale_factor * (view_rect.size.height - view_point.y) as f32) as i32;
|
||||
let window_event = WindowEvent::MouseMoved(x, y);
|
||||
let event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event };
|
||||
Some(event)
|
||||
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;
|
||||
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 };
|
||||
events.push_back(event);
|
||||
}
|
||||
|
||||
let delta_y = (scale_factor * ns_event.deltaY() as f32) 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 };
|
||||
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 };
|
||||
events.push_back(event);
|
||||
}
|
||||
|
||||
let event = events.pop_front();
|
||||
self.shared.pending_events.lock().unwrap().extend(events.into_iter());
|
||||
event
|
||||
},
|
||||
|
||||
appkit::NSScrollWheel => {
|
||||
@@ -457,24 +553,36 @@ impl EventsLoop {
|
||||
scale_factor * ns_event.scrollingDeltaY() as f32)
|
||||
};
|
||||
let phase = match ns_event.phase() {
|
||||
appkit::NSEventPhaseMayBegin | appkit::NSEventPhaseBegan => TouchPhase::Started,
|
||||
appkit::NSEventPhaseEnded => TouchPhase::Ended,
|
||||
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => TouchPhase::Started,
|
||||
NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended,
|
||||
_ => TouchPhase::Moved,
|
||||
};
|
||||
let window_event = WindowEvent::MouseWheel(delta, phase);
|
||||
self.shared.pending_events.lock().unwrap().push_back(Event::DeviceEvent {
|
||||
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)
|
||||
} else {
|
||||
LineDelta(ns_event.scrollingDeltaX() as f32,
|
||||
ns_event.scrollingDeltaY() as f32)
|
||||
},
|
||||
}
|
||||
});
|
||||
let window_event = WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: delta, phase: phase, modifiers: event_mods(ns_event) };
|
||||
Some(into_event(window_event))
|
||||
},
|
||||
|
||||
appkit::NSEventTypePressure => {
|
||||
let pressure = ns_event.pressure();
|
||||
let stage = ns_event.stage();
|
||||
let window_event = WindowEvent::TouchpadPressure(pressure, stage);
|
||||
let window_event = WindowEvent::TouchpadPressure { device_id: DEVICE_ID, pressure: pressure, stage: stage };
|
||||
Some(into_event(window_event))
|
||||
},
|
||||
|
||||
appkit::NSApplicationDefined => match ns_event.subtype() {
|
||||
appkit::NSEventSubtype::NSApplicationActivatedEventType => {
|
||||
Some(into_event(WindowEvent::Awakened))
|
||||
Some(Event::Awakened)
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
@@ -483,6 +591,34 @@ impl EventsLoop {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> Proxy {
|
||||
Proxy {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Proxy {
|
||||
pub fn wakeup(&self) -> Result<(), EventsLoopClosed> {
|
||||
// Awaken the event loop by triggering `NSApplicationActivatedEventType`.
|
||||
unsafe {
|
||||
let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
|
||||
let event =
|
||||
NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
|
||||
cocoa::base::nil,
|
||||
appkit::NSApplicationDefined,
|
||||
foundation::NSPoint::new(0.0, 0.0),
|
||||
appkit::NSEventModifierFlags::empty(),
|
||||
0.0,
|
||||
0,
|
||||
cocoa::base::nil,
|
||||
appkit::NSEventSubtype::NSApplicationActivatedEventType,
|
||||
0,
|
||||
0);
|
||||
appkit::NSApp().postEvent_atStart_(event, cocoa::base::NO);
|
||||
foundation::NSAutoreleasePool::drain(pool);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -617,6 +753,7 @@ fn to_virtual_key_code(code: u16) -> Option<events::VirtualKeyCode> {
|
||||
0x7e => events::VirtualKeyCode::Up,
|
||||
//0x7f => unkown,
|
||||
|
||||
0xa => events::VirtualKeyCode::Caret,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@@ -626,9 +763,12 @@ fn event_mods(event: cocoa::base::id) -> ModifiersState {
|
||||
NSEvent::modifierFlags(event)
|
||||
};
|
||||
ModifiersState {
|
||||
shift: flags.contains(appkit::NSShiftKeyMask),
|
||||
ctrl: flags.contains(appkit::NSControlKeyMask),
|
||||
alt: flags.contains(appkit::NSAlternateKeyMask),
|
||||
logo: flags.contains(appkit::NSCommandKeyMask),
|
||||
shift: flags.contains(NSEventModifierFlags::NSShiftKeyMask),
|
||||
ctrl: flags.contains(NSEventModifierFlags::NSControlKeyMask),
|
||||
alt: flags.contains(NSEventModifierFlags::NSAlternateKeyMask),
|
||||
logo: flags.contains(NSEventModifierFlags::NSCommandKeyMask),
|
||||
}
|
||||
}
|
||||
|
||||
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
||||
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
|
||||
|
||||
@@ -1,34 +1,38 @@
|
||||
#![cfg(target_os = "macos")]
|
||||
|
||||
pub use self::events_loop::EventsLoop;
|
||||
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
|
||||
pub use self::window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, Window};
|
||||
pub use self::events_loop::{EventsLoop, Proxy as EventsLoopProxy};
|
||||
pub use self::monitor::MonitorId;
|
||||
pub use self::window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, Window2};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
|
||||
use {CreationError};
|
||||
|
||||
pub struct Window2 {
|
||||
pub window: ::std::sync::Arc<Window>,
|
||||
pub struct Window {
|
||||
pub window: Arc<Window2>,
|
||||
}
|
||||
|
||||
impl ::std::ops::Deref for Window2 {
|
||||
type Target = Window;
|
||||
impl ::std::ops::Deref for Window {
|
||||
type Target = Window2;
|
||||
#[inline]
|
||||
fn deref(&self) -> &Window {
|
||||
fn deref(&self) -> &Window2 {
|
||||
&*self.window
|
||||
}
|
||||
}
|
||||
|
||||
impl Window2 {
|
||||
impl Window {
|
||||
|
||||
pub fn new(events_loop: ::std::sync::Arc<EventsLoop>,
|
||||
pub fn new(events_loop: &EventsLoop,
|
||||
attributes: &::WindowAttributes,
|
||||
pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result<Self, CreationError>
|
||||
{
|
||||
let weak_events_loop = ::std::sync::Arc::downgrade(&events_loop);
|
||||
let window = ::std::sync::Arc::new(try!(Window::new(weak_events_loop, attributes, pl_attribs)));
|
||||
let weak_window = ::std::sync::Arc::downgrade(&window);
|
||||
events_loop.windows.lock().unwrap().push(weak_window);
|
||||
Ok(Window2 { window: window })
|
||||
let weak_shared = Arc::downgrade(&events_loop.shared);
|
||||
let window = Arc::new(try!(Window2::new(weak_shared, attributes, pl_attribs)));
|
||||
let weak_window = Arc::downgrade(&window);
|
||||
events_loop.shared.windows.lock().unwrap().push(weak_window);
|
||||
Ok(Window { window: window })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,50 +1,94 @@
|
||||
use core_graphics::display;
|
||||
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 native_monitor::NativeMonitorId;
|
||||
use super::EventsLoop;
|
||||
use super::window::IdRef;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorId(u32);
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct MonitorId(CGDirectDisplayID);
|
||||
|
||||
pub fn get_available_monitors() -> VecDeque<MonitorId> {
|
||||
let mut monitors = VecDeque::new();
|
||||
unsafe {
|
||||
let max_displays = 10u32;
|
||||
let mut active_displays = [0u32; 10];
|
||||
let mut display_count = 0;
|
||||
display::CGGetActiveDisplayList(max_displays, &mut active_displays[0], &mut display_count);
|
||||
for i in 0..display_count as usize {
|
||||
monitors.push_back(MonitorId(active_displays[i]));
|
||||
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));
|
||||
}
|
||||
}
|
||||
monitors
|
||||
}
|
||||
monitors
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor() -> MonitorId {
|
||||
let id = unsafe { MonitorId(display::CGMainDisplayID()) };
|
||||
id
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
let id = MonitorId(CGDisplay::main().id);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn make_monitor_from_display(id: CGDirectDisplayID) -> MonitorId {
|
||||
let id = MonitorId(id);
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
let MonitorId(display_id) = *self;
|
||||
let screen_num = unsafe { display::CGDisplayModelNumber(display_id) };
|
||||
let screen_num = CGDisplay::new(display_id).model_number();
|
||||
Some(format!("Monitor #{}", screen_num))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> NativeMonitorId {
|
||||
let MonitorId(display_id) = *self;
|
||||
NativeMonitorId::Numeric(display_id)
|
||||
pub fn get_native_identifier(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
let MonitorId(display_id) = *self;
|
||||
let dimension = unsafe {
|
||||
let height = display::CGDisplayPixelsHigh(display_id);
|
||||
let width = display::CGDisplayPixelsWide(display_id);
|
||||
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
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
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 }
|
||||
}
|
||||
|
||||
pub(crate) fn get_nsscreen(&self) -> Option<id> {
|
||||
unsafe {
|
||||
let native_id = self.get_native_identifier();
|
||||
let screens = NSScreen::screens(nil);
|
||||
let count: NSUInteger = msg_send![screens, count];
|
||||
let key = IdRef::new(NSString::alloc(nil).init_str("NSScreenNumber"));
|
||||
let mut matching_screen: Option<id> = None;
|
||||
for i in 0..count {
|
||||
let screen = msg_send![screens, objectAtIndex: i as NSUInteger];
|
||||
let device_description = NSScreen::deviceDescription(screen);
|
||||
let value: id = msg_send![device_description, objectForKey:*key];
|
||||
if value != nil {
|
||||
let screen_number: NSUInteger = msg_send![value, unsignedIntegerValue];
|
||||
if screen_number as u32 == native_id {
|
||||
matching_screen = Some(screen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
matching_screen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,26 +3,30 @@ use CreationError::OsError;
|
||||
use libc;
|
||||
|
||||
use WindowAttributes;
|
||||
use native_monitor::NativeMonitorId;
|
||||
use os::macos::ActivationPolicy;
|
||||
use os::macos::WindowExt;
|
||||
|
||||
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::{NSPoint, NSRect, NSSize, NSString, NSUInteger};
|
||||
use cocoa::appkit::{self, NSApplication, NSColor, NSView, NSWindow};
|
||||
use cocoa::foundation::{NSDictionary, NSPoint, NSRect, NSSize, NSString};
|
||||
|
||||
use core_graphics::display::{CGAssociateMouseAndMouseCursorPosition, CGMainDisplayID, CGDisplayPixelsHigh, CGWarpMouseCursorPosition};
|
||||
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 os::macos::WindowExt;
|
||||
use super::events_loop::{EventsLoop, Shared};
|
||||
|
||||
use window::MonitorId as RootMonitorId;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Id(pub usize);
|
||||
@@ -30,7 +34,99 @@ pub struct Id(pub usize);
|
||||
struct DelegateState {
|
||||
view: IdRef,
|
||||
window: IdRef,
|
||||
events_loop: std::sync::Weak<super::EventsLoop>,
|
||||
shared: Weak<Shared>,
|
||||
|
||||
win_attribs: RefCell<WindowAttributes>,
|
||||
standard_frame: Cell<Option<NSRect>>,
|
||||
save_style_mask: Cell<Option<NSWindowStyleMask>>,
|
||||
|
||||
// This is set when WindowBuilder::with_fullscreen was set,
|
||||
// see comments of `window_did_fail_to_enter_fullscreen`
|
||||
handle_with_fullscreen: bool,
|
||||
}
|
||||
|
||||
impl DelegateState {
|
||||
fn is_zoomed(&self) -> bool {
|
||||
unsafe {
|
||||
// Because isZoomed do not work in Borderless mode, we set it
|
||||
// resizable temporality
|
||||
let curr_mask = self.window.styleMask();
|
||||
|
||||
if !curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask) {
|
||||
self.window
|
||||
.setStyleMask_(NSWindowStyleMask::NSResizableWindowMask);
|
||||
}
|
||||
|
||||
let is_zoomed: BOOL = msg_send![*self.window, isZoomed];
|
||||
|
||||
// Roll back temp styles
|
||||
if !curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask) {
|
||||
self.window.setStyleMask_(curr_mask);
|
||||
}
|
||||
|
||||
is_zoomed != 0
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
win_attribs.maximized
|
||||
};
|
||||
|
||||
self.perform_maximized(maximized);
|
||||
}
|
||||
|
||||
fn perform_maximized(&self, maximized: bool) {
|
||||
let is_zoomed = self.is_zoomed();
|
||||
|
||||
if is_zoomed == maximized {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the standard frame sized if it is not zoomed
|
||||
if !is_zoomed {
|
||||
unsafe {
|
||||
self.standard_frame.set(Some(NSWindow::frame(*self.window)));
|
||||
}
|
||||
}
|
||||
|
||||
let mut win_attribs = self.win_attribs.borrow_mut();
|
||||
win_attribs.maximized = maximized;
|
||||
|
||||
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
|
||||
unsafe {
|
||||
self.window.zoom_(nil);
|
||||
}
|
||||
} else {
|
||||
// if it is borderless, we set the frame directly
|
||||
unsafe {
|
||||
let new_rect = if maximized {
|
||||
let screen = NSScreen::mainScreen(nil);
|
||||
NSScreen::visibleFrame(screen)
|
||||
} else {
|
||||
self.standard_frame.get().unwrap_or(NSRect::new(
|
||||
NSPoint::new(50.0, 50.0),
|
||||
NSSize::new(800.0, 600.0),
|
||||
))
|
||||
};
|
||||
|
||||
self.window.setFrame_display_(new_rect, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WindowDelegate {
|
||||
@@ -51,8 +147,8 @@ impl WindowDelegate {
|
||||
event: window_event,
|
||||
};
|
||||
|
||||
if let Some(events_loop) = state.events_loop.upgrade() {
|
||||
events_loop.call_user_callback_with_event_or_store_in_pending(event);
|
||||
if let Some(shared) = state.shared.upgrade() {
|
||||
shared.call_user_callback_with_event_or_store_in_pending(event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,15 +165,24 @@ impl WindowDelegate {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
emit_event(state, WindowEvent::Closed);
|
||||
emit_event(state, WindowEvent::CloseRequested);
|
||||
}
|
||||
NO
|
||||
}
|
||||
|
||||
// Remove the window from the events_loop.
|
||||
if let Some(events_loop) = state.events_loop.upgrade() {
|
||||
extern fn window_will_close(this: &Object, _: Sel, _: id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
|
||||
emit_event(state, WindowEvent::Destroyed);
|
||||
|
||||
// Remove the window from the shared state.
|
||||
if let Some(shared) = state.shared.upgrade() {
|
||||
let window_id = get_window_id(*state.window);
|
||||
events_loop.find_and_remove_window(window_id);
|
||||
shared.find_and_remove_window(window_id);
|
||||
}
|
||||
}
|
||||
YES
|
||||
}
|
||||
|
||||
extern fn window_did_resize(this: &Object, _: Sel, _: id) {
|
||||
@@ -93,6 +198,17 @@ impl WindowDelegate {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +230,138 @@ impl WindowDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoked when the dragged image enters destination bounds or frame
|
||||
extern fn dragging_entered(this: &Object, _: Sel, sender: id) -> BOOL {
|
||||
use cocoa::appkit::NSPasteboard;
|
||||
use cocoa::foundation::NSFastEnumeration;
|
||||
use std::path::PathBuf;
|
||||
|
||||
let pb: id = unsafe { msg_send![sender, draggingPasteboard] };
|
||||
let filenames = unsafe { NSPasteboard::propertyListForType(pb, appkit::NSFilenamesPboardType) };
|
||||
|
||||
for file in unsafe { filenames.iter() } {
|
||||
use cocoa::foundation::NSString;
|
||||
use std::ffi::CStr;
|
||||
|
||||
unsafe {
|
||||
let f = NSString::UTF8String(file);
|
||||
let path = CStr::from_ptr(f).to_string_lossy().into_owned();
|
||||
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
emit_event(state, WindowEvent::HoveredFile(PathBuf::from(path)));
|
||||
}
|
||||
};
|
||||
|
||||
YES
|
||||
}
|
||||
|
||||
/// Invoked when the image is released
|
||||
extern fn prepare_for_drag_operation(_: &Object, _: Sel, _: id) {}
|
||||
|
||||
/// Invoked after the released image has been removed from the screen
|
||||
extern fn perform_drag_operation(this: &Object, _: Sel, sender: id) -> BOOL {
|
||||
use cocoa::appkit::NSPasteboard;
|
||||
use cocoa::foundation::NSFastEnumeration;
|
||||
use std::path::PathBuf;
|
||||
|
||||
let pb: id = unsafe { msg_send![sender, draggingPasteboard] };
|
||||
let filenames = unsafe { NSPasteboard::propertyListForType(pb, appkit::NSFilenamesPboardType) };
|
||||
|
||||
for file in unsafe { filenames.iter() } {
|
||||
use cocoa::foundation::NSString;
|
||||
use std::ffi::CStr;
|
||||
|
||||
unsafe {
|
||||
let f = NSString::UTF8String(file);
|
||||
let path = CStr::from_ptr(f).to_string_lossy().into_owned();
|
||||
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
emit_event(state, WindowEvent::DroppedFile(PathBuf::from(path)));
|
||||
}
|
||||
};
|
||||
|
||||
YES
|
||||
}
|
||||
|
||||
/// Invoked when the dragging operation is complete
|
||||
extern fn conclude_drag_operation(_: &Object, _: Sel, _: id) {}
|
||||
|
||||
/// Invoked when the dragging operation is cancelled
|
||||
extern fn dragging_exited(this: &Object, _: Sel, _: id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
emit_event(state, WindowEvent::HoveredFileCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoked when entered fullscreen
|
||||
extern fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id){
|
||||
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.handle_with_fullscreen = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoked when before enter fullscreen
|
||||
extern fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
let is_zoomed = state.is_zoomed();
|
||||
|
||||
state.win_attribs.borrow_mut().maximized = is_zoomed;
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoked when exited fullscreen
|
||||
extern fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id){
|
||||
let state = unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
&mut *(state as *mut DelegateState)
|
||||
};
|
||||
|
||||
state.restore_state_from_fullscreen();
|
||||
}
|
||||
|
||||
/// Invoked when fail to enter fullscreen
|
||||
///
|
||||
/// When this window launch from a fullscreen app (e.g. launch from VS Code
|
||||
/// terminal), it creates a new virtual destkop and a transition animation.
|
||||
/// This animation takes one second and cannot be disable without
|
||||
/// elevated privileges. In this animation time, all toggleFullscreen events
|
||||
/// will be failed. In this implementation, we will try again by using
|
||||
/// performSelector:withObject:afterDelay: until window_did_enter_fullscreen.
|
||||
/// It should be fine as we only do this at initialzation (i.e with_fullscreen
|
||||
/// was set).
|
||||
///
|
||||
/// From Apple doc:
|
||||
/// In some cases, the transition to enter full-screen mode can fail,
|
||||
/// due to being in the midst of handling some other animation or user gesture.
|
||||
/// This method indicates that there was an error, and you should clean up any
|
||||
/// work you may have done to prepare to enter full-screen mode.
|
||||
extern fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
|
||||
if state.handle_with_fullscreen {
|
||||
let _: () = msg_send![*state.window,
|
||||
performSelector:sel!(toggleFullScreen:)
|
||||
withObject:nil
|
||||
afterDelay: 0.5
|
||||
];
|
||||
} else {
|
||||
state.restore_state_from_fullscreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mut DELEGATE_CLASS: *const Class = 0 as *const Class;
|
||||
static INIT: std::sync::Once = std::sync::ONCE_INIT;
|
||||
|
||||
@@ -125,16 +373,41 @@ impl WindowDelegate {
|
||||
// Add callback methods
|
||||
decl.add_method(sel!(windowShouldClose:),
|
||||
window_should_close as extern fn(&Object, Sel, id) -> BOOL);
|
||||
decl.add_method(sel!(windowWillClose:),
|
||||
window_will_close as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(windowDidResize:),
|
||||
window_did_resize as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(windowDidChangeScreen:),
|
||||
window_did_change_screen as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_method(sel!(windowDidChangeBackingProperties:),
|
||||
window_did_change_backing_properties as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(windowDidBecomeKey:),
|
||||
window_did_become_key as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(windowDidResignKey:),
|
||||
window_did_resign_key as extern fn(&Object, Sel, id));
|
||||
|
||||
// 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:),
|
||||
prepare_for_drag_operation as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(performDragOperation:),
|
||||
perform_drag_operation as extern fn(&Object, Sel, id) -> BOOL);
|
||||
decl.add_method(sel!(concludeDragOperation:),
|
||||
conclude_drag_operation as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(draggingExited:),
|
||||
dragging_exited as extern fn(&Object, Sel, id));
|
||||
|
||||
// callbacks for fullscreen events
|
||||
decl.add_method(sel!(windowDidEnterFullScreen:),
|
||||
window_did_enter_fullscreen as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(windowWillEnterFullScreen:),
|
||||
window_will_enter_fullscreen as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(windowDidExitFullScreen:),
|
||||
window_did_exit_fullscreen as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(windowDidFailToEnterFullScreen:),
|
||||
window_did_fail_to_enter_fullscreen as extern fn(&Object, Sel, id));
|
||||
|
||||
// Store internal state as user data
|
||||
decl.add_ivar::<*mut c_void>("winitState");
|
||||
|
||||
@@ -173,36 +446,50 @@ impl Drop for WindowDelegate {
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub activation_policy: ActivationPolicy,
|
||||
pub movable_by_window_background: bool,
|
||||
pub titlebar_transparent: bool,
|
||||
pub title_hidden: bool,
|
||||
pub titlebar_hidden: bool,
|
||||
pub titlebar_buttons_hidden: bool,
|
||||
pub fullsize_content_view: bool,
|
||||
}
|
||||
|
||||
pub struct Window {
|
||||
pub struct Window2 {
|
||||
pub view: IdRef,
|
||||
pub window: IdRef,
|
||||
pub delegate: WindowDelegate,
|
||||
}
|
||||
|
||||
unsafe impl Send for Window {}
|
||||
unsafe impl Sync for Window {}
|
||||
unsafe impl Send for Window2 {}
|
||||
unsafe impl Sync for Window2 {}
|
||||
|
||||
impl Drop for Window {
|
||||
/// Helpper funciton to convert NSScreen::mainScreen to MonitorId
|
||||
unsafe fn get_current_monitor() -> RootMonitorId {
|
||||
let screen = NSScreen::mainScreen(nil);
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window2 {
|
||||
fn drop(&mut self) {
|
||||
// Remove this window from the `EventLoop`s list of windows.
|
||||
let id = self.id();
|
||||
if let Some(ev) = self.delegate.state.events_loop.upgrade() {
|
||||
ev.find_and_remove_window(id);
|
||||
}
|
||||
|
||||
// Close the window if it has not yet been closed.
|
||||
let nswindow = *self.window;
|
||||
if nswindow != nil {
|
||||
unsafe {
|
||||
msg_send![nswindow, close];
|
||||
let () = msg_send![nswindow, close];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowExt for Window {
|
||||
impl WindowExt for Window2 {
|
||||
#[inline]
|
||||
fn get_nswindow(&self) -> *mut c_void {
|
||||
*self.window as *mut c_void
|
||||
@@ -214,29 +501,29 @@ impl WindowExt for Window {
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(events_loop: std::sync::Weak<super::EventsLoop>,
|
||||
win_attribs: &WindowAttributes,
|
||||
pl_attribs: &PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window, CreationError>
|
||||
{
|
||||
impl Window2 {
|
||||
pub fn new(
|
||||
shared: Weak<Shared>,
|
||||
win_attribs: &WindowAttributes,
|
||||
pl_attribs: &PlatformSpecificWindowBuilderAttributes,
|
||||
) -> Result<Window2, CreationError> {
|
||||
unsafe {
|
||||
if !msg_send![cocoa::base::class("NSThread"), isMainThread] {
|
||||
panic!("Windows can only be created on the main thread on macOS");
|
||||
}
|
||||
}
|
||||
|
||||
let app = match Window::create_app(pl_attribs.activation_policy) {
|
||||
let app = match Window2::create_app(pl_attribs.activation_policy) {
|
||||
Some(app) => app,
|
||||
None => { return Err(OsError(format!("Couldn't create NSApplication"))); },
|
||||
};
|
||||
|
||||
let window = match Window::create_window(win_attribs)
|
||||
let window = match Window2::create_window(win_attribs, pl_attribs)
|
||||
{
|
||||
Some(window) => window,
|
||||
None => { return Err(OsError(format!("Couldn't create NSWindow"))); },
|
||||
};
|
||||
let view = match Window::create_view(*window) {
|
||||
let view = match Window2::create_view(*window) {
|
||||
Some(view) => view,
|
||||
None => { return Err(OsError(format!("Couldn't create NSView"))); },
|
||||
};
|
||||
@@ -248,11 +535,6 @@ impl Window {
|
||||
}
|
||||
|
||||
app.activateIgnoringOtherApps_(YES);
|
||||
if win_attribs.visible {
|
||||
window.makeKeyAndOrderFront_(nil);
|
||||
} else {
|
||||
window.makeKeyWindow();
|
||||
}
|
||||
|
||||
if let Some((width, height)) = win_attribs.min_dimensions {
|
||||
nswindow_set_min_dimensions(window.0, width.into(), height.into());
|
||||
@@ -261,20 +543,54 @@ impl Window {
|
||||
if let Some((width, height)) = win_attribs.max_dimensions {
|
||||
nswindow_set_max_dimensions(window.0, width.into(), height.into());
|
||||
}
|
||||
|
||||
use cocoa::foundation::NSArray;
|
||||
// register for drag and drop operations.
|
||||
let () = msg_send![(*window as id),
|
||||
registerForDraggedTypes:NSArray::arrayWithObject(nil, appkit::NSFilenamesPboardType)];
|
||||
}
|
||||
|
||||
let ds = DelegateState {
|
||||
view: view.clone(),
|
||||
window: window.clone(),
|
||||
events_loop: events_loop,
|
||||
win_attribs: RefCell::new(win_attribs.clone()),
|
||||
standard_frame: Cell::new(None),
|
||||
save_style_mask: Cell::new(None),
|
||||
handle_with_fullscreen: win_attribs.fullscreen.is_some(),
|
||||
shared: shared,
|
||||
};
|
||||
ds.win_attribs.borrow_mut().fullscreen = None;
|
||||
|
||||
let window = Window {
|
||||
let window = Window2 {
|
||||
view: view,
|
||||
window: window,
|
||||
delegate: WindowDelegate::new(ds),
|
||||
};
|
||||
|
||||
// Set fullscreen mode after we setup everything
|
||||
if let Some(ref monitor) = win_attribs.fullscreen {
|
||||
unsafe {
|
||||
if monitor.inner != get_current_monitor().inner {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
window.set_fullscreen(Some(monitor.clone()));
|
||||
}
|
||||
|
||||
// Make key have to be after set fullscreen
|
||||
// to prevent normal size window brefly appears
|
||||
unsafe {
|
||||
if win_attribs.visible {
|
||||
window.window.makeKeyAndOrderFront_(nil);
|
||||
} else {
|
||||
window.window.makeKeyWindow();
|
||||
}
|
||||
}
|
||||
|
||||
if win_attribs.maximized {
|
||||
window.delegate.state.perform_maximized(win_attribs.maximized);
|
||||
}
|
||||
|
||||
Ok(window)
|
||||
}
|
||||
|
||||
@@ -295,36 +611,17 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
fn create_window(attrs: &WindowAttributes) -> Option<IdRef> {
|
||||
fn create_window(
|
||||
attrs: &WindowAttributes,
|
||||
pl_attrs: &PlatformSpecificWindowBuilderAttributes)
|
||||
-> Option<IdRef> {
|
||||
unsafe {
|
||||
let screen = match attrs.monitor {
|
||||
let screen = match attrs.fullscreen {
|
||||
Some(ref monitor_id) => {
|
||||
let native_id = match monitor_id.get_native_identifier() {
|
||||
NativeMonitorId::Numeric(num) => num,
|
||||
_ => panic!("OS X monitors should always have a numeric native ID")
|
||||
};
|
||||
let matching_screen = {
|
||||
let screens = appkit::NSScreen::screens(nil);
|
||||
let count: NSUInteger = msg_send![screens, count];
|
||||
let key = IdRef::new(NSString::alloc(nil).init_str("NSScreenNumber"));
|
||||
let mut matching_screen: Option<id> = None;
|
||||
for i in 0..count {
|
||||
let screen = msg_send![screens, objectAtIndex:i as NSUInteger];
|
||||
let device_description = appkit::NSScreen::deviceDescription(screen);
|
||||
let value: id = msg_send![device_description, objectForKey:*key];
|
||||
if value != nil {
|
||||
let screen_number: NSUInteger = msg_send![value, unsignedIntegerValue];
|
||||
if screen_number as u32 == native_id {
|
||||
matching_screen = Some(screen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
matching_screen
|
||||
};
|
||||
Some(matching_screen.unwrap_or(appkit::NSScreen::mainScreen(nil)))
|
||||
let monitor_screen = monitor_id.inner.get_nsscreen();
|
||||
Some(monitor_screen.unwrap_or(appkit::NSScreen::mainScreen(nil)))
|
||||
},
|
||||
None => None
|
||||
_ => None,
|
||||
};
|
||||
let frame = match screen {
|
||||
Some(screen) => appkit::NSScreen::frame(screen),
|
||||
@@ -334,26 +631,47 @@ impl Window {
|
||||
}
|
||||
};
|
||||
|
||||
let masks = if screen.is_some() {
|
||||
// Fullscreen window
|
||||
appkit::NSBorderlessWindowMask as NSUInteger |
|
||||
appkit::NSResizableWindowMask as NSUInteger |
|
||||
appkit::NSTitledWindowMask as NSUInteger
|
||||
} else if attrs.decorations {
|
||||
// Window with a titlebar
|
||||
appkit::NSClosableWindowMask as NSUInteger |
|
||||
appkit::NSMiniaturizableWindowMask as NSUInteger |
|
||||
appkit::NSResizableWindowMask as NSUInteger |
|
||||
appkit::NSTitledWindowMask as NSUInteger
|
||||
let masks = if pl_attrs.titlebar_hidden {
|
||||
NSWindowStyleMask::NSBorderlessWindowMask |
|
||||
NSWindowStyleMask::NSResizableWindowMask
|
||||
} else if pl_attrs.titlebar_transparent {
|
||||
// Window2 with a transparent titlebar and regular content view
|
||||
NSWindowStyleMask::NSClosableWindowMask |
|
||||
NSWindowStyleMask::NSMiniaturizableWindowMask |
|
||||
NSWindowStyleMask::NSResizableWindowMask |
|
||||
NSWindowStyleMask::NSTitledWindowMask
|
||||
} else if pl_attrs.fullsize_content_view {
|
||||
// Window2 with a transparent titlebar and fullsize content view
|
||||
NSWindowStyleMask::NSClosableWindowMask |
|
||||
NSWindowStyleMask::NSMiniaturizableWindowMask |
|
||||
NSWindowStyleMask::NSResizableWindowMask |
|
||||
NSWindowStyleMask::NSTitledWindowMask |
|
||||
NSWindowStyleMask::NSFullSizeContentViewWindowMask
|
||||
} else {
|
||||
// Window without a titlebar
|
||||
appkit::NSClosableWindowMask as NSUInteger |
|
||||
appkit::NSMiniaturizableWindowMask as NSUInteger |
|
||||
appkit::NSResizableWindowMask as NSUInteger |
|
||||
appkit::NSFullSizeContentViewWindowMask as NSUInteger
|
||||
if !attrs.decorations && !screen.is_some() {
|
||||
// Window2 without a titlebar
|
||||
NSWindowStyleMask::NSBorderlessWindowMask
|
||||
} else {
|
||||
// Window2 with a titlebar
|
||||
NSWindowStyleMask::NSClosableWindowMask |
|
||||
NSWindowStyleMask::NSMiniaturizableWindowMask |
|
||||
NSWindowStyleMask::NSResizableWindowMask |
|
||||
NSWindowStyleMask::NSTitledWindowMask
|
||||
}
|
||||
};
|
||||
|
||||
let window = IdRef::new(NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_(
|
||||
let winit_window = Class::get("WinitWindow").unwrap_or_else(|| {
|
||||
let window_superclass = Class::get("NSWindow").unwrap();
|
||||
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);
|
||||
decl.register();
|
||||
Class::get("WinitWindow").unwrap()
|
||||
});
|
||||
|
||||
let window: id = msg_send![winit_window, alloc];
|
||||
|
||||
let window = IdRef::new(window.initWithContentRect_styleMask_backing_defer_(
|
||||
frame,
|
||||
masks,
|
||||
appkit::NSBackingStoreBuffered,
|
||||
@@ -365,17 +683,32 @@ impl Window {
|
||||
window.setTitle_(*title);
|
||||
window.setAcceptsMouseMovedEvents_(YES);
|
||||
|
||||
if pl_attrs.titlebar_transparent {
|
||||
window.setTitlebarAppearsTransparent_(YES);
|
||||
}
|
||||
if pl_attrs.title_hidden {
|
||||
window.setTitleVisibility_(appkit::NSWindowTitleVisibility::NSWindowTitleHidden);
|
||||
}
|
||||
if pl_attrs.titlebar_buttons_hidden {
|
||||
let button = window.standardWindowButton_(NSWindowButton::NSWindowFullScreenButton);
|
||||
let () = msg_send![button, setHidden:YES];
|
||||
let button = window.standardWindowButton_(NSWindowButton::NSWindowMiniaturizeButton);
|
||||
let () = msg_send![button, setHidden:YES];
|
||||
let button = window.standardWindowButton_(NSWindowButton::NSWindowCloseButton);
|
||||
let () = msg_send![button, setHidden:YES];
|
||||
let button = window.standardWindowButton_(NSWindowButton::NSWindowZoomButton);
|
||||
let () = msg_send![button, setHidden:YES];
|
||||
}
|
||||
if pl_attrs.movable_by_window_background {
|
||||
window.setMovableByWindowBackground_(YES);
|
||||
}
|
||||
|
||||
if !attrs.decorations {
|
||||
window.setTitleVisibility_(appkit::NSWindowTitleVisibility::NSWindowTitleHidden);
|
||||
window.setTitlebarAppearsTransparent_(YES);
|
||||
}
|
||||
|
||||
if screen.is_some() {
|
||||
window.setLevel_(appkit::NSMainMenuWindowLevel as i64 + 1);
|
||||
}
|
||||
else {
|
||||
window.center();
|
||||
}
|
||||
window.center();
|
||||
window
|
||||
})
|
||||
}
|
||||
@@ -383,7 +716,7 @@ impl Window {
|
||||
|
||||
fn create_view(window: id) -> Option<IdRef> {
|
||||
unsafe {
|
||||
let view = IdRef::new(NSView::alloc(nil).init());
|
||||
let view = IdRef::new(NSView::init(NSView::alloc(nil)));
|
||||
view.non_nil().map(|view| {
|
||||
view.setWantsBestResolutionOpenGLSurface_(YES);
|
||||
window.setContentView_(*view);
|
||||
@@ -409,56 +742,86 @@ impl Window {
|
||||
unsafe { NSWindow::orderOut_(*self.window, nil); }
|
||||
}
|
||||
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
unsafe {
|
||||
let content_rect = NSWindow::contentRectForFrameRect_(*self.window, NSWindow::frame(*self.window));
|
||||
// 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 _
|
||||
}
|
||||
|
||||
// TODO: consider extrapolating the calculations for the y axis to
|
||||
// a private method
|
||||
Some((content_rect.origin.x as i32, (CGDisplayPixelsHigh(CGMainDisplayID()) as f64 - (content_rect.origin.y + content_rect.size.height)) as i32))
|
||||
}
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
let frame_rect = unsafe { NSWindow::frame(*self.window) };
|
||||
Some((
|
||||
frame_rect.origin.x as i32,
|
||||
Self::bottom_left_to_top_left(frame_rect),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
let content_rect = unsafe {
|
||||
NSWindow::contentRectForFrameRect_(
|
||||
*self.window,
|
||||
NSWindow::frame(*self.window),
|
||||
)
|
||||
};
|
||||
Some((
|
||||
content_rect.origin.x as i32,
|
||||
Self::bottom_left_to_top_left(content_rect),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn set_position(&self, x: i32, y: i32) {
|
||||
let dummy = NSRect::new(
|
||||
NSPoint::new(
|
||||
x as f64,
|
||||
// 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,
|
||||
),
|
||||
NSSize::new(0f64, 0f64),
|
||||
);
|
||||
unsafe {
|
||||
let frame = NSWindow::frame(*self.view);
|
||||
|
||||
// NOTE: `setFrameOrigin` might not give desirable results when
|
||||
// setting window, as it treats bottom left as origin.
|
||||
// `setFrameTopLeftPoint` treats top left as origin (duh), but
|
||||
// does not equal the value returned by `get_window_position`
|
||||
// (there is a difference by 22 for me on yosemite)
|
||||
|
||||
// TODO: consider extrapolating the calculations for the y axis to
|
||||
// a private method
|
||||
let dummy = NSRect::new(NSPoint::new(x as f64, CGDisplayPixelsHigh(CGMainDisplayID()) as f64 - (frame.size.height + y as f64)), NSSize::new(0f64, 0f64));
|
||||
let conv = NSWindow::frameRectForContentRect_(*self.window, dummy);
|
||||
|
||||
// NSWindow::setFrameTopLeftPoint_(*self.window, conv.origin);
|
||||
NSWindow::setFrameOrigin_(*self.window, conv.origin);
|
||||
NSWindow::setFrameTopLeftPoint_(*self.window, dummy.origin);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
let factor = self.hidpi_factor() as f64; // API convention is that size is in physical pixels
|
||||
unsafe {
|
||||
let view_frame = NSView::frame(*self.view);
|
||||
Some((view_frame.size.width as u32, view_frame.size.height as u32))
|
||||
Some(((view_frame.size.width*factor) as u32, (view_frame.size.height*factor) as u32))
|
||||
}
|
||||
}
|
||||
|
||||
#[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 as u32, window_frame.size.height as u32))
|
||||
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, height as f64));
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,7 +854,7 @@ impl Window {
|
||||
MouseCursor::EwResize | MouseCursor::ColResize => "resizeLeftRightCursor",
|
||||
MouseCursor::NsResize | MouseCursor::RowResize => "resizeUpDownCursor",
|
||||
|
||||
/// TODO: Find appropriate OSX cursors
|
||||
// TODO: Find appropriate OSX cursors
|
||||
MouseCursor::NeResize | MouseCursor::NwResize |
|
||||
MouseCursor::SeResize | MouseCursor::SwResize |
|
||||
MouseCursor::NwseResize | MouseCursor::NeswResize |
|
||||
@@ -517,7 +880,7 @@ impl Window {
|
||||
match state {
|
||||
CursorState::Normal => {
|
||||
let _: () = unsafe { msg_send![cls, unhide] };
|
||||
let _: i32 = unsafe { CGAssociateMouseAndMouseCursorPosition(true) };
|
||||
let _ = CGDisplay::associate_mouse_and_mouse_cursor_position(true);
|
||||
Ok(())
|
||||
},
|
||||
CursorState::Hide => {
|
||||
@@ -525,7 +888,8 @@ impl Window {
|
||||
Ok(())
|
||||
},
|
||||
CursorState::Grab => {
|
||||
let _: i32 = unsafe { CGAssociateMouseAndMouseCursorPosition(false) };
|
||||
let _: () = unsafe { msg_send![cls, hide] };
|
||||
let _ = CGDisplay::associate_mouse_and_mouse_cursor_position(false);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -543,17 +907,103 @@ impl Window {
|
||||
let (window_x, window_y) = self.get_position().unwrap_or((0, 0));
|
||||
let (cursor_x, cursor_y) = (window_x + x, window_y + y);
|
||||
|
||||
unsafe {
|
||||
// TODO: Check for errors.
|
||||
let _ = CGWarpMouseCursorPosition(appkit::CGPoint {
|
||||
x: cursor_x as appkit::CGFloat,
|
||||
y: cursor_y as appkit::CGFloat,
|
||||
});
|
||||
let _ = CGAssociateMouseAndMouseCursorPosition(true);
|
||||
}
|
||||
// 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);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
self.delegate.state.perform_maximized(maximized)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// TODO: Right now set_fullscreen do not work on switching monitors
|
||||
/// in fullscreen mode
|
||||
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
|
||||
let state = &self.delegate.state;
|
||||
let current = {
|
||||
let win_attribs = state.win_attribs.borrow_mut();
|
||||
|
||||
let current = win_attribs.fullscreen.clone();
|
||||
match (¤t, monitor) {
|
||||
(&None, None) => {
|
||||
return;
|
||||
}
|
||||
(&Some(ref a), Some(ref b)) if a.inner != b.inner => {
|
||||
unimplemented!();
|
||||
}
|
||||
(&Some(_), Some(_)) => {
|
||||
return;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
current
|
||||
};
|
||||
|
||||
unsafe {
|
||||
// Because toggleFullScreen will not work if the StyleMask is none,
|
||||
// We set a normal style to it temporary.
|
||||
// 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,
|
||||
);
|
||||
state.save_style_mask.set(Some(curr_mask));
|
||||
}
|
||||
}
|
||||
|
||||
self.window.toggleFullScreen_(nil);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, decorations: bool) {
|
||||
let state = &self.delegate.state;
|
||||
let mut win_attribs = state.win_attribs.borrow_mut();
|
||||
|
||||
if win_attribs.decorations == decorations {
|
||||
return;
|
||||
}
|
||||
|
||||
win_attribs.decorations = decorations;
|
||||
|
||||
// Skip modifiy if we are in fullscreen mode,
|
||||
// window_did_exit_fullscreen will handle it
|
||||
if win_attribs.fullscreen.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let new_mask = if decorations {
|
||||
NSWindowStyleMask::NSClosableWindowMask
|
||||
| NSWindowStyleMask::NSMiniaturizableWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask
|
||||
| NSWindowStyleMask::NSTitledWindowMask
|
||||
} else {
|
||||
NSWindowStyleMask::NSBorderlessWindowMask
|
||||
};
|
||||
|
||||
state.window.setStyleMask_(new_mask);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
unsafe {
|
||||
self::get_current_monitor()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the `cocoa::base::id` associated with a window to a usize to use as a unique identifier
|
||||
@@ -614,19 +1064,19 @@ unsafe fn nswindow_set_max_dimensions<V: NSWindow + Copy>(
|
||||
pub struct IdRef(id);
|
||||
|
||||
impl IdRef {
|
||||
fn new(i: id) -> IdRef {
|
||||
pub fn new(i: id) -> IdRef {
|
||||
IdRef(i)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn retain(i: id) -> IdRef {
|
||||
pub fn retain(i: id) -> IdRef {
|
||||
if i != nil {
|
||||
let _: id = unsafe { msg_send![i, retain] };
|
||||
}
|
||||
IdRef(i)
|
||||
}
|
||||
|
||||
fn non_nil(self) -> Option<IdRef> {
|
||||
pub fn non_nil(self) -> Option<IdRef> {
|
||||
if self.0 == nil { None } else { Some(self) }
|
||||
}
|
||||
}
|
||||
@@ -654,3 +1104,7 @@ impl Clone for IdRef {
|
||||
IdRef(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
extern fn yes(_: &Object, _: Sel) -> BOOL {
|
||||
YES
|
||||
}
|
||||
|
||||
@@ -15,8 +15,11 @@ mod platform;
|
||||
#[cfg(target_os = "ios")]
|
||||
#[path="ios/mod.rs"]
|
||||
mod platform;
|
||||
#[cfg(target_os = "emscripten")]
|
||||
#[path="emscripten/mod.rs"]
|
||||
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")))]
|
||||
use this_platform_is_not_supported;
|
||||
not(target_os = "freebsd"), not(target_os = "openbsd"), not(target_os = "emscripten")))]
|
||||
compile_error!("The platform you're compiling for is not supported by winit");
|
||||
|
||||
@@ -1,407 +0,0 @@
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::ffi::OsString;
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
|
||||
use CursorState;
|
||||
use WindowEvent as Event;
|
||||
use events::ModifiersState;
|
||||
use super::event;
|
||||
use super::WindowState;
|
||||
|
||||
use user32;
|
||||
use shell32;
|
||||
use winapi;
|
||||
|
||||
/// There's no parameters passed to the callback function, so it needs to get
|
||||
/// its context (the HWND, the Sender for events, etc.) stashed in
|
||||
/// a thread-local variable.
|
||||
thread_local!(pub static CONTEXT_STASH: RefCell<Option<ThreadLocalData>> = RefCell::new(None));
|
||||
|
||||
pub struct ThreadLocalData {
|
||||
pub win: winapi::HWND,
|
||||
pub sender: Sender<Event>,
|
||||
pub window_state: Arc<Mutex<WindowState>>,
|
||||
pub mouse_in_window: bool
|
||||
}
|
||||
|
||||
/// Equivalent to the windows api [MINMAXINFO](https://msdn.microsoft.com/en-us/library/windows/desktop/ms632605%28v=vs.85%29.aspx)
|
||||
/// struct. Used because winapi-rs doesn't have this declared.
|
||||
#[repr(C)]
|
||||
#[allow(dead_code)]
|
||||
struct MinMaxInfo {
|
||||
reserved: winapi::POINT, // Do not use/change
|
||||
max_size: winapi::POINT,
|
||||
max_position: winapi::POINT,
|
||||
min_track: winapi::POINT,
|
||||
max_track: winapi::POINT
|
||||
}
|
||||
|
||||
/// Checks that the window is the good one, and if so send the event to it.
|
||||
fn send_event(input_window: winapi::HWND, event: Event) {
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let context_stash = context_stash.borrow();
|
||||
let stored = match *context_stash {
|
||||
None => return,
|
||||
Some(ref v) => v
|
||||
};
|
||||
|
||||
let &ThreadLocalData { ref win, ref sender, .. } = stored;
|
||||
|
||||
if win != &input_window {
|
||||
return;
|
||||
}
|
||||
|
||||
sender.send(event).ok(); // ignoring if closed
|
||||
});
|
||||
}
|
||||
|
||||
/// This is the callback that is called by `DispatchMessage` in the events loop.
|
||||
///
|
||||
/// 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: winapi::HWND, msg: winapi::UINT,
|
||||
wparam: winapi::WPARAM, lparam: winapi::LPARAM)
|
||||
-> winapi::LRESULT
|
||||
{
|
||||
match msg {
|
||||
winapi::WM_DESTROY => {
|
||||
use events::WindowEvent::Closed;
|
||||
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let context_stash = context_stash.borrow();
|
||||
let stored = match *context_stash {
|
||||
None => return,
|
||||
Some(ref v) => v
|
||||
};
|
||||
|
||||
let &ThreadLocalData { ref win, .. } = stored;
|
||||
|
||||
if win == &window {
|
||||
user32::PostQuitMessage(0);
|
||||
}
|
||||
});
|
||||
|
||||
send_event(window, Closed);
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_ERASEBKGND => {
|
||||
1
|
||||
},
|
||||
|
||||
winapi::WM_SIZE => {
|
||||
use events::WindowEvent::Resized;
|
||||
let w = winapi::LOWORD(lparam as winapi::DWORD) as u32;
|
||||
let h = winapi::HIWORD(lparam as winapi::DWORD) as u32;
|
||||
send_event(window, Resized(w, h));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_MOVE => {
|
||||
use events::WindowEvent::Moved;
|
||||
let x = winapi::LOWORD(lparam as winapi::DWORD) as i32;
|
||||
let y = winapi::HIWORD(lparam as winapi::DWORD) as i32;
|
||||
send_event(window, Moved(x, y));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_CHAR => {
|
||||
use std::mem;
|
||||
use events::WindowEvent::ReceivedCharacter;
|
||||
let chr: char = mem::transmute(wparam as u32);
|
||||
send_event(window, ReceivedCharacter(chr));
|
||||
0
|
||||
},
|
||||
|
||||
// Prevents default windows menu hotkeys playing unwanted
|
||||
// "ding" sounds. Alternatively could check for WM_SYSCOMMAND
|
||||
// with wparam being SC_KEYMENU, but this may prevent some
|
||||
// other unwanted default hotkeys as well.
|
||||
winapi::WM_SYSCHAR => {
|
||||
0
|
||||
}
|
||||
|
||||
winapi::WM_MOUSEMOVE => {
|
||||
use events::WindowEvent::{MouseEntered, MouseMoved};
|
||||
let mouse_outside_window = CONTEXT_STASH.with(|context_stash| {
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
if let Some(context_stash) = context_stash.as_mut() {
|
||||
if !context_stash.mouse_in_window {
|
||||
context_stash.mouse_in_window = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
});
|
||||
|
||||
if mouse_outside_window {
|
||||
send_event(window, MouseEntered);
|
||||
|
||||
// Calling TrackMouseEvent in order to receive mouse leave events.
|
||||
user32::TrackMouseEvent(&mut winapi::TRACKMOUSEEVENT {
|
||||
cbSize: mem::size_of::<winapi::TRACKMOUSEEVENT>() as winapi::DWORD,
|
||||
dwFlags: winapi::TME_LEAVE,
|
||||
hwndTrack: window,
|
||||
dwHoverTime: winapi::HOVER_DEFAULT,
|
||||
});
|
||||
}
|
||||
|
||||
let x = winapi::GET_X_LPARAM(lparam) as i32;
|
||||
let y = winapi::GET_Y_LPARAM(lparam) as i32;
|
||||
|
||||
send_event(window, MouseMoved(x, y));
|
||||
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_MOUSELEAVE => {
|
||||
use events::WindowEvent::MouseLeft;
|
||||
let mouse_in_window = CONTEXT_STASH.with(|context_stash| {
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
if let Some(context_stash) = context_stash.as_mut() {
|
||||
if context_stash.mouse_in_window {
|
||||
context_stash.mouse_in_window = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
});
|
||||
|
||||
if mouse_in_window {
|
||||
send_event(window, MouseLeft);
|
||||
}
|
||||
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_MOUSEWHEEL => {
|
||||
use events::WindowEvent::MouseWheel;
|
||||
use events::MouseScrollDelta::LineDelta;
|
||||
use events::TouchPhase;
|
||||
|
||||
let value = (wparam >> 16) as i16;
|
||||
let value = value as i32;
|
||||
let value = value as f32 / winapi::WHEEL_DELTA as f32;
|
||||
|
||||
send_event(window, MouseWheel(LineDelta(0.0, value), TouchPhase::Moved));
|
||||
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_KEYDOWN | winapi::WM_SYSKEYDOWN => {
|
||||
use events::WindowEvent::KeyboardInput;
|
||||
use events::ElementState::Pressed;
|
||||
if msg == winapi::WM_SYSKEYDOWN && wparam as i32 == winapi::VK_F4 {
|
||||
user32::DefWindowProcW(window, msg, wparam, lparam)
|
||||
} else {
|
||||
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
|
||||
send_event(window, KeyboardInput(Pressed, scancode, vkey, event::get_key_mods()));
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
winapi::WM_KEYUP | winapi::WM_SYSKEYUP => {
|
||||
use events::WindowEvent::KeyboardInput;
|
||||
use events::ElementState::Released;
|
||||
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
|
||||
send_event(window, KeyboardInput(Released, scancode, vkey, event::get_key_mods()));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_LBUTTONDOWN => {
|
||||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Left;
|
||||
use events::ElementState::Pressed;
|
||||
send_event(window, MouseInput(Pressed, Left));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_LBUTTONUP => {
|
||||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Left;
|
||||
use events::ElementState::Released;
|
||||
send_event(window, MouseInput(Released, Left));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_RBUTTONDOWN => {
|
||||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Right;
|
||||
use events::ElementState::Pressed;
|
||||
send_event(window, MouseInput(Pressed, Right));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_RBUTTONUP => {
|
||||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Right;
|
||||
use events::ElementState::Released;
|
||||
send_event(window, MouseInput(Released, Right));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_MBUTTONDOWN => {
|
||||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Middle;
|
||||
use events::ElementState::Pressed;
|
||||
send_event(window, MouseInput(Pressed, Middle));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_MBUTTONUP => {
|
||||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Middle;
|
||||
use events::ElementState::Released;
|
||||
send_event(window, MouseInput(Released, Middle));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_XBUTTONDOWN => {
|
||||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Other;
|
||||
use events::ElementState::Pressed;
|
||||
let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int; // waiting on PR for winapi to add GET_XBUTTON_WPARAM
|
||||
send_event(window, MouseInput(Pressed, Other(xbutton as u8)));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_XBUTTONUP => {
|
||||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Other;
|
||||
use events::ElementState::Released;
|
||||
let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int;
|
||||
send_event(window, MouseInput(Released, Other(xbutton as u8)));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_INPUT => {
|
||||
let mut data: winapi::RAWINPUT = mem::uninitialized();
|
||||
let mut data_size = mem::size_of::<winapi::RAWINPUT>() as winapi::UINT;
|
||||
user32::GetRawInputData(mem::transmute(lparam), winapi::RID_INPUT,
|
||||
mem::transmute(&mut data), &mut data_size,
|
||||
mem::size_of::<winapi::RAWINPUTHEADER>() as winapi::UINT);
|
||||
|
||||
if data.header.dwType == winapi::RIM_TYPEMOUSE {
|
||||
let _x = data.mouse.lLastX; // FIXME: this is not always the relative movement
|
||||
let _y = data.mouse.lLastY;
|
||||
// TODO:
|
||||
//send_event(window, Event::MouseRawMovement { x: x, y: y });
|
||||
|
||||
0
|
||||
|
||||
} else {
|
||||
user32::DefWindowProcW(window, msg, wparam, lparam)
|
||||
}
|
||||
},
|
||||
|
||||
winapi::WM_SETFOCUS => {
|
||||
use events::WindowEvent::Focused;
|
||||
send_event(window, Focused(true));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_KILLFOCUS => {
|
||||
use events::WindowEvent::Focused;
|
||||
send_event(window, Focused(false));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::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 Ok(window_state) = cstash.window_state.lock() {
|
||||
if cstash.mouse_in_window {
|
||||
match window_state.cursor_state {
|
||||
CursorState::Normal => {
|
||||
user32::SetCursor(user32::LoadCursorW(
|
||||
ptr::null_mut(),
|
||||
window_state.cursor));
|
||||
},
|
||||
CursorState::Grab | CursorState::Hide => {
|
||||
user32::SetCursor(ptr::null_mut());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
call_def_window_proc = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
call_def_window_proc
|
||||
});
|
||||
|
||||
if call_def_window_proc {
|
||||
user32::DefWindowProcW(window, msg, wparam, lparam)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
winapi::WM_DROPFILES => {
|
||||
use events::WindowEvent::DroppedFile;
|
||||
|
||||
let hdrop = wparam as winapi::HDROP;
|
||||
let mut pathbuf: [u16; winapi::MAX_PATH] = mem::uninitialized();
|
||||
let num_drops = shell32::DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0);
|
||||
|
||||
for i in 0..num_drops {
|
||||
let nch = shell32::DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(),
|
||||
winapi::MAX_PATH as u32) as usize;
|
||||
if nch > 0 {
|
||||
send_event(window, DroppedFile(OsString::from_wide(&pathbuf[0..nch]).into()));
|
||||
}
|
||||
}
|
||||
|
||||
shell32::DragFinish(hdrop);
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_GETMINMAXINFO => {
|
||||
let mmi = lparam as *mut MinMaxInfo;
|
||||
//(*mmi).max_position = winapi::POINT { x: -8, y: -8 }; // The upper left corner of the window if it were maximized on the primary monitor.
|
||||
//(*mmi).max_size = winapi::POINT { x: .., y: .. }; // The dimensions of the primary monitor.
|
||||
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
match context_stash.borrow().as_ref() {
|
||||
Some(cstash) => {
|
||||
let window_state = cstash.window_state.lock().unwrap();
|
||||
|
||||
match window_state.attributes.min_dimensions {
|
||||
Some((width, height)) => {
|
||||
(*mmi).min_track = winapi::POINT { x: width as i32, y: height as i32 };
|
||||
},
|
||||
None => { }
|
||||
}
|
||||
|
||||
match window_state.attributes.max_dimensions {
|
||||
Some((width, height)) => {
|
||||
(*mmi).max_track = winapi::POINT { x: width as i32, y: height as i32 };
|
||||
},
|
||||
None => { }
|
||||
}
|
||||
},
|
||||
None => { }
|
||||
}
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
x if x == *super::WAKEUP_MSG_ID => {
|
||||
use events::WindowEvent::Awakened;
|
||||
send_event(window, Awakened);
|
||||
0
|
||||
},
|
||||
|
||||
_ => {
|
||||
user32::DefWindowProcW(window, msg, wparam, lparam)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +1,92 @@
|
||||
use events::VirtualKeyCode;
|
||||
use events::ModifiersState;
|
||||
use winapi;
|
||||
use user32;
|
||||
use ScanCode;
|
||||
|
||||
use winapi::shared::minwindef::{WPARAM, LPARAM};
|
||||
use winapi::um::winuser;
|
||||
|
||||
use ScanCode;
|
||||
use std::char;
|
||||
|
||||
const MAPVK_VK_TO_CHAR: u32 = 2;
|
||||
const MAPVK_VSC_TO_VK_EX: u32 = 3;
|
||||
|
||||
pub fn get_key_mods() -> ModifiersState {
|
||||
let mut mods = ModifiersState::default();
|
||||
unsafe {
|
||||
if user32::GetKeyState(winapi::VK_SHIFT) & (1 << 15) == (1 << 15) {
|
||||
if winuser::GetKeyState(winuser::VK_SHIFT) & (1 << 15) == (1 << 15) {
|
||||
mods.shift = true;
|
||||
}
|
||||
if user32::GetKeyState(winapi::VK_CONTROL) & (1 << 15) == (1 << 15) {
|
||||
if winuser::GetKeyState(winuser::VK_CONTROL) & (1 << 15) == (1 << 15) {
|
||||
mods.ctrl = true;
|
||||
}
|
||||
if user32::GetKeyState(winapi::VK_MENU) & (1 << 15) == (1 << 15) {
|
||||
if winuser::GetKeyState(winuser::VK_MENU) & (1 << 15) == (1 << 15) {
|
||||
mods.alt = true;
|
||||
}
|
||||
if (user32::GetKeyState(winapi::VK_LWIN) | user32::GetKeyState(winapi::VK_RWIN)) & (1 << 15) == (1 << 15) {
|
||||
if (winuser::GetKeyState(winuser::VK_LWIN) | winuser::GetKeyState(winuser::VK_RWIN)) & (1 << 15) == (1 << 15) {
|
||||
mods.logo = true;
|
||||
}
|
||||
}
|
||||
mods
|
||||
}
|
||||
|
||||
pub fn vkeycode_to_element(wparam: winapi::WPARAM, lparam: winapi::LPARAM) -> (ScanCode, Option<VirtualKeyCode>) {
|
||||
let scancode = ((lparam >> 16) & 0xff) as u8;
|
||||
pub fn vkeycode_to_element(wparam: WPARAM, lparam: LPARAM) -> (ScanCode, Option<VirtualKeyCode>) {
|
||||
let scancode = ((lparam >> 16) & 0xff) as u32;
|
||||
let extended = (lparam & 0x01000000) != 0;
|
||||
let vk = match wparam as i32 {
|
||||
winapi::VK_SHIFT => unsafe { user32::MapVirtualKeyA(scancode as u32, MAPVK_VSC_TO_VK_EX) as i32 },
|
||||
winapi::VK_CONTROL => if extended { winapi::VK_RCONTROL } else { winapi::VK_LCONTROL },
|
||||
winapi::VK_MENU => if extended { winapi::VK_RMENU } else { winapi::VK_LMENU },
|
||||
winuser::VK_SHIFT => unsafe { winuser::MapVirtualKeyA(scancode, MAPVK_VSC_TO_VK_EX) as i32 },
|
||||
winuser::VK_CONTROL => if extended { winuser::VK_RCONTROL } else { winuser::VK_LCONTROL },
|
||||
winuser::VK_MENU => if extended { winuser::VK_RMENU } else { winuser::VK_LMENU },
|
||||
other => other
|
||||
};
|
||||
|
||||
// VK_* codes are documented here https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
|
||||
(scancode, match vk {
|
||||
//winapi::VK_LBUTTON => Some(VirtualKeyCode::Lbutton),
|
||||
//winapi::VK_RBUTTON => Some(VirtualKeyCode::Rbutton),
|
||||
//winapi::VK_CANCEL => Some(VirtualKeyCode::Cancel),
|
||||
//winapi::VK_MBUTTON => Some(VirtualKeyCode::Mbutton),
|
||||
//winapi::VK_XBUTTON1 => Some(VirtualKeyCode::Xbutton1),
|
||||
//winapi::VK_XBUTTON2 => Some(VirtualKeyCode::Xbutton2),
|
||||
winapi::VK_BACK => Some(VirtualKeyCode::Back),
|
||||
winapi::VK_TAB => Some(VirtualKeyCode::Tab),
|
||||
//winapi::VK_CLEAR => Some(VirtualKeyCode::Clear),
|
||||
winapi::VK_RETURN => Some(VirtualKeyCode::Return),
|
||||
winapi::VK_LSHIFT => Some(VirtualKeyCode::LShift),
|
||||
winapi::VK_RSHIFT => Some(VirtualKeyCode::RShift),
|
||||
winapi::VK_LCONTROL => Some(VirtualKeyCode::LControl),
|
||||
winapi::VK_RCONTROL => Some(VirtualKeyCode::RControl),
|
||||
winapi::VK_LMENU => Some(VirtualKeyCode::LMenu),
|
||||
winapi::VK_RMENU => Some(VirtualKeyCode::RMenu),
|
||||
winapi::VK_PAUSE => Some(VirtualKeyCode::Pause),
|
||||
winapi::VK_CAPITAL => Some(VirtualKeyCode::Capital),
|
||||
winapi::VK_KANA => Some(VirtualKeyCode::Kana),
|
||||
//winapi::VK_HANGUEL => Some(VirtualKeyCode::Hanguel),
|
||||
//winapi::VK_HANGUL => Some(VirtualKeyCode::Hangul),
|
||||
//winapi::VK_JUNJA => Some(VirtualKeyCode::Junja),
|
||||
//winapi::VK_FINAL => Some(VirtualKeyCode::Final),
|
||||
//winapi::VK_HANJA => Some(VirtualKeyCode::Hanja),
|
||||
winapi::VK_KANJI => Some(VirtualKeyCode::Kanji),
|
||||
winapi::VK_ESCAPE => Some(VirtualKeyCode::Escape),
|
||||
winapi::VK_CONVERT => Some(VirtualKeyCode::Convert),
|
||||
winapi::VK_NONCONVERT => Some(VirtualKeyCode::NoConvert),
|
||||
//winapi::VK_ACCEPT => Some(VirtualKeyCode::Accept),
|
||||
//winapi::VK_MODECHANGE => Some(VirtualKeyCode::Modechange),
|
||||
winapi::VK_SPACE => Some(VirtualKeyCode::Space),
|
||||
winapi::VK_PRIOR => Some(VirtualKeyCode::PageUp),
|
||||
winapi::VK_NEXT => Some(VirtualKeyCode::PageDown),
|
||||
winapi::VK_END => Some(VirtualKeyCode::End),
|
||||
winapi::VK_HOME => Some(VirtualKeyCode::Home),
|
||||
winapi::VK_LEFT => Some(VirtualKeyCode::Left),
|
||||
winapi::VK_UP => Some(VirtualKeyCode::Up),
|
||||
winapi::VK_RIGHT => Some(VirtualKeyCode::Right),
|
||||
winapi::VK_DOWN => Some(VirtualKeyCode::Down),
|
||||
//winapi::VK_SELECT => Some(VirtualKeyCode::Select),
|
||||
//winapi::VK_PRINT => Some(VirtualKeyCode::Print),
|
||||
//winapi::VK_EXECUTE => Some(VirtualKeyCode::Execute),
|
||||
winapi::VK_SNAPSHOT => Some(VirtualKeyCode::Snapshot),
|
||||
winapi::VK_INSERT => Some(VirtualKeyCode::Insert),
|
||||
winapi::VK_DELETE => Some(VirtualKeyCode::Delete),
|
||||
//winapi::VK_HELP => Some(VirtualKeyCode::Help),
|
||||
//winuser::VK_LBUTTON => Some(VirtualKeyCode::Lbutton),
|
||||
//winuser::VK_RBUTTON => Some(VirtualKeyCode::Rbutton),
|
||||
//winuser::VK_CANCEL => Some(VirtualKeyCode::Cancel),
|
||||
//winuser::VK_MBUTTON => Some(VirtualKeyCode::Mbutton),
|
||||
//winuser::VK_XBUTTON1 => Some(VirtualKeyCode::Xbutton1),
|
||||
//winuser::VK_XBUTTON2 => Some(VirtualKeyCode::Xbutton2),
|
||||
winuser::VK_BACK => Some(VirtualKeyCode::Back),
|
||||
winuser::VK_TAB => Some(VirtualKeyCode::Tab),
|
||||
//winuser::VK_CLEAR => Some(VirtualKeyCode::Clear),
|
||||
winuser::VK_RETURN => Some(VirtualKeyCode::Return),
|
||||
winuser::VK_LSHIFT => Some(VirtualKeyCode::LShift),
|
||||
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_PAUSE => Some(VirtualKeyCode::Pause),
|
||||
winuser::VK_CAPITAL => Some(VirtualKeyCode::Capital),
|
||||
winuser::VK_KANA => Some(VirtualKeyCode::Kana),
|
||||
//winuser::VK_HANGUEL => Some(VirtualKeyCode::Hanguel),
|
||||
//winuser::VK_HANGUL => Some(VirtualKeyCode::Hangul),
|
||||
//winuser::VK_JUNJA => Some(VirtualKeyCode::Junja),
|
||||
//winuser::VK_FINAL => Some(VirtualKeyCode::Final),
|
||||
//winuser::VK_HANJA => Some(VirtualKeyCode::Hanja),
|
||||
winuser::VK_KANJI => Some(VirtualKeyCode::Kanji),
|
||||
winuser::VK_ESCAPE => Some(VirtualKeyCode::Escape),
|
||||
winuser::VK_CONVERT => Some(VirtualKeyCode::Convert),
|
||||
winuser::VK_NONCONVERT => Some(VirtualKeyCode::NoConvert),
|
||||
//winuser::VK_ACCEPT => Some(VirtualKeyCode::Accept),
|
||||
//winuser::VK_MODECHANGE => Some(VirtualKeyCode::Modechange),
|
||||
winuser::VK_SPACE => Some(VirtualKeyCode::Space),
|
||||
winuser::VK_PRIOR => Some(VirtualKeyCode::PageUp),
|
||||
winuser::VK_NEXT => Some(VirtualKeyCode::PageDown),
|
||||
winuser::VK_END => Some(VirtualKeyCode::End),
|
||||
winuser::VK_HOME => Some(VirtualKeyCode::Home),
|
||||
winuser::VK_LEFT => Some(VirtualKeyCode::Left),
|
||||
winuser::VK_UP => Some(VirtualKeyCode::Up),
|
||||
winuser::VK_RIGHT => Some(VirtualKeyCode::Right),
|
||||
winuser::VK_DOWN => Some(VirtualKeyCode::Down),
|
||||
//winuser::VK_SELECT => Some(VirtualKeyCode::Select),
|
||||
//winuser::VK_PRINT => Some(VirtualKeyCode::Print),
|
||||
//winuser::VK_EXECUTE => Some(VirtualKeyCode::Execute),
|
||||
winuser::VK_SNAPSHOT => Some(VirtualKeyCode::Snapshot),
|
||||
winuser::VK_INSERT => Some(VirtualKeyCode::Insert),
|
||||
winuser::VK_DELETE => Some(VirtualKeyCode::Delete),
|
||||
//winuser::VK_HELP => Some(VirtualKeyCode::Help),
|
||||
0x30 => Some(VirtualKeyCode::Key0),
|
||||
0x31 => Some(VirtualKeyCode::Key1),
|
||||
0x32 => Some(VirtualKeyCode::Key2),
|
||||
@@ -117,94 +123,110 @@ pub fn vkeycode_to_element(wparam: winapi::WPARAM, lparam: winapi::LPARAM) -> (S
|
||||
0x58 => Some(VirtualKeyCode::X),
|
||||
0x59 => Some(VirtualKeyCode::Y),
|
||||
0x5A => Some(VirtualKeyCode::Z),
|
||||
//winapi::VK_LWIN => Some(VirtualKeyCode::Lwin),
|
||||
//winapi::VK_RWIN => Some(VirtualKeyCode::Rwin),
|
||||
winapi::VK_APPS => Some(VirtualKeyCode::Apps),
|
||||
winapi::VK_SLEEP => Some(VirtualKeyCode::Sleep),
|
||||
winapi::VK_NUMPAD0 => Some(VirtualKeyCode::Numpad0),
|
||||
winapi::VK_NUMPAD1 => Some(VirtualKeyCode::Numpad1),
|
||||
winapi::VK_NUMPAD2 => Some(VirtualKeyCode::Numpad2),
|
||||
winapi::VK_NUMPAD3 => Some(VirtualKeyCode::Numpad3),
|
||||
winapi::VK_NUMPAD4 => Some(VirtualKeyCode::Numpad4),
|
||||
winapi::VK_NUMPAD5 => Some(VirtualKeyCode::Numpad5),
|
||||
winapi::VK_NUMPAD6 => Some(VirtualKeyCode::Numpad6),
|
||||
winapi::VK_NUMPAD7 => Some(VirtualKeyCode::Numpad7),
|
||||
winapi::VK_NUMPAD8 => Some(VirtualKeyCode::Numpad8),
|
||||
winapi::VK_NUMPAD9 => Some(VirtualKeyCode::Numpad9),
|
||||
winapi::VK_MULTIPLY => Some(VirtualKeyCode::Multiply),
|
||||
winapi::VK_ADD => Some(VirtualKeyCode::Add),
|
||||
//winapi::VK_SEPARATOR => Some(VirtualKeyCode::Separator),
|
||||
winapi::VK_SUBTRACT => Some(VirtualKeyCode::Subtract),
|
||||
winapi::VK_DECIMAL => Some(VirtualKeyCode::Decimal),
|
||||
winapi::VK_DIVIDE => Some(VirtualKeyCode::Divide),
|
||||
winapi::VK_F1 => Some(VirtualKeyCode::F1),
|
||||
winapi::VK_F2 => Some(VirtualKeyCode::F2),
|
||||
winapi::VK_F3 => Some(VirtualKeyCode::F3),
|
||||
winapi::VK_F4 => Some(VirtualKeyCode::F4),
|
||||
winapi::VK_F5 => Some(VirtualKeyCode::F5),
|
||||
winapi::VK_F6 => Some(VirtualKeyCode::F6),
|
||||
winapi::VK_F7 => Some(VirtualKeyCode::F7),
|
||||
winapi::VK_F8 => Some(VirtualKeyCode::F8),
|
||||
winapi::VK_F9 => Some(VirtualKeyCode::F9),
|
||||
winapi::VK_F10 => Some(VirtualKeyCode::F10),
|
||||
winapi::VK_F11 => Some(VirtualKeyCode::F11),
|
||||
winapi::VK_F12 => Some(VirtualKeyCode::F12),
|
||||
winapi::VK_F13 => Some(VirtualKeyCode::F13),
|
||||
winapi::VK_F14 => Some(VirtualKeyCode::F14),
|
||||
winapi::VK_F15 => Some(VirtualKeyCode::F15),
|
||||
/*winapi::VK_F16 => Some(VirtualKeyCode::F16),
|
||||
winapi::VK_F17 => Some(VirtualKeyCode::F17),
|
||||
winapi::VK_F18 => Some(VirtualKeyCode::F18),
|
||||
winapi::VK_F19 => Some(VirtualKeyCode::F19),
|
||||
winapi::VK_F20 => Some(VirtualKeyCode::F20),
|
||||
winapi::VK_F21 => Some(VirtualKeyCode::F21),
|
||||
winapi::VK_F22 => Some(VirtualKeyCode::F22),
|
||||
winapi::VK_F23 => Some(VirtualKeyCode::F23),
|
||||
winapi::VK_F24 => Some(VirtualKeyCode::F24),*/
|
||||
winapi::VK_NUMLOCK => Some(VirtualKeyCode::Numlock),
|
||||
winapi::VK_SCROLL => Some(VirtualKeyCode::Scroll),
|
||||
winapi::VK_BROWSER_BACK => Some(VirtualKeyCode::NavigateBackward),
|
||||
winapi::VK_BROWSER_FORWARD => Some(VirtualKeyCode::NavigateForward),
|
||||
winapi::VK_BROWSER_REFRESH => Some(VirtualKeyCode::WebRefresh),
|
||||
winapi::VK_BROWSER_STOP => Some(VirtualKeyCode::WebStop),
|
||||
winapi::VK_BROWSER_SEARCH => Some(VirtualKeyCode::WebSearch),
|
||||
winapi::VK_BROWSER_FAVORITES => Some(VirtualKeyCode::WebFavorites),
|
||||
winapi::VK_BROWSER_HOME => Some(VirtualKeyCode::WebHome),
|
||||
winapi::VK_VOLUME_MUTE => Some(VirtualKeyCode::Mute),
|
||||
winapi::VK_VOLUME_DOWN => Some(VirtualKeyCode::VolumeDown),
|
||||
winapi::VK_VOLUME_UP => Some(VirtualKeyCode::VolumeUp),
|
||||
winapi::VK_MEDIA_NEXT_TRACK => Some(VirtualKeyCode::NextTrack),
|
||||
winapi::VK_MEDIA_PREV_TRACK => Some(VirtualKeyCode::PrevTrack),
|
||||
winapi::VK_MEDIA_STOP => Some(VirtualKeyCode::MediaStop),
|
||||
winapi::VK_MEDIA_PLAY_PAUSE => Some(VirtualKeyCode::PlayPause),
|
||||
winapi::VK_LAUNCH_MAIL => Some(VirtualKeyCode::Mail),
|
||||
winapi::VK_LAUNCH_MEDIA_SELECT => Some(VirtualKeyCode::MediaSelect),
|
||||
/*winapi::VK_LAUNCH_APP1 => Some(VirtualKeyCode::Launch_app1),
|
||||
winapi::VK_LAUNCH_APP2 => Some(VirtualKeyCode::Launch_app2),*/
|
||||
winapi::VK_OEM_PLUS => Some(VirtualKeyCode::Equals),
|
||||
winapi::VK_OEM_COMMA => Some(VirtualKeyCode::Comma),
|
||||
winapi::VK_OEM_MINUS => Some(VirtualKeyCode::Minus),
|
||||
winapi::VK_OEM_PERIOD => Some(VirtualKeyCode::Period),
|
||||
/*winapi::VK_OEM_1 => Some(VirtualKeyCode::Oem_1),
|
||||
winapi::VK_OEM_2 => Some(VirtualKeyCode::Oem_2),
|
||||
winapi::VK_OEM_3 => Some(VirtualKeyCode::Oem_3),
|
||||
winapi::VK_OEM_4 => Some(VirtualKeyCode::Oem_4),
|
||||
winapi::VK_OEM_5 => Some(VirtualKeyCode::Oem_5),
|
||||
winapi::VK_OEM_6 => Some(VirtualKeyCode::Oem_6),
|
||||
winapi::VK_OEM_7 => Some(VirtualKeyCode::Oem_7),
|
||||
winapi::VK_OEM_8 => Some(VirtualKeyCode::Oem_8), */
|
||||
winapi::VK_OEM_102 => Some(VirtualKeyCode::OEM102),
|
||||
/*winapi::VK_PROCESSKEY => Some(VirtualKeyCode::Processkey),
|
||||
winapi::VK_PACKET => Some(VirtualKeyCode::Packet),
|
||||
winapi::VK_ATTN => Some(VirtualKeyCode::Attn),
|
||||
winapi::VK_CRSEL => Some(VirtualKeyCode::Crsel),
|
||||
winapi::VK_EXSEL => Some(VirtualKeyCode::Exsel),
|
||||
winapi::VK_EREOF => Some(VirtualKeyCode::Ereof),
|
||||
winapi::VK_PLAY => Some(VirtualKeyCode::Play),
|
||||
winapi::VK_ZOOM => Some(VirtualKeyCode::Zoom),
|
||||
winapi::VK_NONAME => Some(VirtualKeyCode::Noname),
|
||||
winapi::VK_PA1 => Some(VirtualKeyCode::Pa1),
|
||||
winapi::VK_OEM_CLEAR => Some(VirtualKeyCode::Oem_clear),*/
|
||||
//winuser::VK_LWIN => Some(VirtualKeyCode::Lwin),
|
||||
//winuser::VK_RWIN => Some(VirtualKeyCode::Rwin),
|
||||
winuser::VK_APPS => Some(VirtualKeyCode::Apps),
|
||||
winuser::VK_SLEEP => Some(VirtualKeyCode::Sleep),
|
||||
winuser::VK_NUMPAD0 => Some(VirtualKeyCode::Numpad0),
|
||||
winuser::VK_NUMPAD1 => Some(VirtualKeyCode::Numpad1),
|
||||
winuser::VK_NUMPAD2 => Some(VirtualKeyCode::Numpad2),
|
||||
winuser::VK_NUMPAD3 => Some(VirtualKeyCode::Numpad3),
|
||||
winuser::VK_NUMPAD4 => Some(VirtualKeyCode::Numpad4),
|
||||
winuser::VK_NUMPAD5 => Some(VirtualKeyCode::Numpad5),
|
||||
winuser::VK_NUMPAD6 => Some(VirtualKeyCode::Numpad6),
|
||||
winuser::VK_NUMPAD7 => Some(VirtualKeyCode::Numpad7),
|
||||
winuser::VK_NUMPAD8 => Some(VirtualKeyCode::Numpad8),
|
||||
winuser::VK_NUMPAD9 => Some(VirtualKeyCode::Numpad9),
|
||||
winuser::VK_MULTIPLY => Some(VirtualKeyCode::Multiply),
|
||||
winuser::VK_ADD => Some(VirtualKeyCode::Add),
|
||||
//winuser::VK_SEPARATOR => Some(VirtualKeyCode::Separator),
|
||||
winuser::VK_SUBTRACT => Some(VirtualKeyCode::Subtract),
|
||||
winuser::VK_DECIMAL => Some(VirtualKeyCode::Decimal),
|
||||
winuser::VK_DIVIDE => Some(VirtualKeyCode::Divide),
|
||||
winuser::VK_F1 => Some(VirtualKeyCode::F1),
|
||||
winuser::VK_F2 => Some(VirtualKeyCode::F2),
|
||||
winuser::VK_F3 => Some(VirtualKeyCode::F3),
|
||||
winuser::VK_F4 => Some(VirtualKeyCode::F4),
|
||||
winuser::VK_F5 => Some(VirtualKeyCode::F5),
|
||||
winuser::VK_F6 => Some(VirtualKeyCode::F6),
|
||||
winuser::VK_F7 => Some(VirtualKeyCode::F7),
|
||||
winuser::VK_F8 => Some(VirtualKeyCode::F8),
|
||||
winuser::VK_F9 => Some(VirtualKeyCode::F9),
|
||||
winuser::VK_F10 => Some(VirtualKeyCode::F10),
|
||||
winuser::VK_F11 => Some(VirtualKeyCode::F11),
|
||||
winuser::VK_F12 => Some(VirtualKeyCode::F12),
|
||||
winuser::VK_F13 => Some(VirtualKeyCode::F13),
|
||||
winuser::VK_F14 => Some(VirtualKeyCode::F14),
|
||||
winuser::VK_F15 => Some(VirtualKeyCode::F15),
|
||||
/*winuser::VK_F16 => Some(VirtualKeyCode::F16),
|
||||
winuser::VK_F17 => Some(VirtualKeyCode::F17),
|
||||
winuser::VK_F18 => Some(VirtualKeyCode::F18),
|
||||
winuser::VK_F19 => Some(VirtualKeyCode::F19),
|
||||
winuser::VK_F20 => Some(VirtualKeyCode::F20),
|
||||
winuser::VK_F21 => Some(VirtualKeyCode::F21),
|
||||
winuser::VK_F22 => Some(VirtualKeyCode::F22),
|
||||
winuser::VK_F23 => Some(VirtualKeyCode::F23),
|
||||
winuser::VK_F24 => Some(VirtualKeyCode::F24),*/
|
||||
winuser::VK_NUMLOCK => Some(VirtualKeyCode::Numlock),
|
||||
winuser::VK_SCROLL => Some(VirtualKeyCode::Scroll),
|
||||
winuser::VK_BROWSER_BACK => Some(VirtualKeyCode::NavigateBackward),
|
||||
winuser::VK_BROWSER_FORWARD => Some(VirtualKeyCode::NavigateForward),
|
||||
winuser::VK_BROWSER_REFRESH => Some(VirtualKeyCode::WebRefresh),
|
||||
winuser::VK_BROWSER_STOP => Some(VirtualKeyCode::WebStop),
|
||||
winuser::VK_BROWSER_SEARCH => Some(VirtualKeyCode::WebSearch),
|
||||
winuser::VK_BROWSER_FAVORITES => Some(VirtualKeyCode::WebFavorites),
|
||||
winuser::VK_BROWSER_HOME => Some(VirtualKeyCode::WebHome),
|
||||
winuser::VK_VOLUME_MUTE => Some(VirtualKeyCode::Mute),
|
||||
winuser::VK_VOLUME_DOWN => Some(VirtualKeyCode::VolumeDown),
|
||||
winuser::VK_VOLUME_UP => Some(VirtualKeyCode::VolumeUp),
|
||||
winuser::VK_MEDIA_NEXT_TRACK => Some(VirtualKeyCode::NextTrack),
|
||||
winuser::VK_MEDIA_PREV_TRACK => Some(VirtualKeyCode::PrevTrack),
|
||||
winuser::VK_MEDIA_STOP => Some(VirtualKeyCode::MediaStop),
|
||||
winuser::VK_MEDIA_PLAY_PAUSE => Some(VirtualKeyCode::PlayPause),
|
||||
winuser::VK_LAUNCH_MAIL => Some(VirtualKeyCode::Mail),
|
||||
winuser::VK_LAUNCH_MEDIA_SELECT => Some(VirtualKeyCode::MediaSelect),
|
||||
/*winuser::VK_LAUNCH_APP1 => Some(VirtualKeyCode::Launch_app1),
|
||||
winuser::VK_LAUNCH_APP2 => Some(VirtualKeyCode::Launch_app2),*/
|
||||
winuser::VK_OEM_PLUS => Some(VirtualKeyCode::Equals),
|
||||
winuser::VK_OEM_COMMA => Some(VirtualKeyCode::Comma),
|
||||
winuser::VK_OEM_MINUS => Some(VirtualKeyCode::Minus),
|
||||
winuser::VK_OEM_PERIOD => Some(VirtualKeyCode::Period),
|
||||
winuser::VK_OEM_1 => map_text_keys(vk),
|
||||
winuser::VK_OEM_2 => map_text_keys(vk),
|
||||
winuser::VK_OEM_3 => map_text_keys(vk),
|
||||
winuser::VK_OEM_4 => map_text_keys(vk),
|
||||
winuser::VK_OEM_5 => map_text_keys(vk),
|
||||
winuser::VK_OEM_6 => map_text_keys(vk),
|
||||
winuser::VK_OEM_7 => map_text_keys(vk),
|
||||
/*winuser::VK_OEM_8 => Some(VirtualKeyCode::Oem_8), */
|
||||
winuser::VK_OEM_102 => Some(VirtualKeyCode::OEM102),
|
||||
/*winuser::VK_PROCESSKEY => Some(VirtualKeyCode::Processkey),
|
||||
winuser::VK_PACKET => Some(VirtualKeyCode::Packet),
|
||||
winuser::VK_ATTN => Some(VirtualKeyCode::Attn),
|
||||
winuser::VK_CRSEL => Some(VirtualKeyCode::Crsel),
|
||||
winuser::VK_EXSEL => Some(VirtualKeyCode::Exsel),
|
||||
winuser::VK_EREOF => Some(VirtualKeyCode::Ereof),
|
||||
winuser::VK_PLAY => Some(VirtualKeyCode::Play),
|
||||
winuser::VK_ZOOM => Some(VirtualKeyCode::Zoom),
|
||||
winuser::VK_NONAME => Some(VirtualKeyCode::Noname),
|
||||
winuser::VK_PA1 => Some(VirtualKeyCode::Pa1),
|
||||
winuser::VK_OEM_CLEAR => Some(VirtualKeyCode::Oem_clear),*/
|
||||
_ => None
|
||||
})
|
||||
}
|
||||
|
||||
// This is needed as windows doesn't properly distinguish
|
||||
// some virtual key codes for different keyboard layouts
|
||||
fn map_text_keys(win_virtual_key: i32) -> Option<VirtualKeyCode> {
|
||||
let char_key = unsafe { winuser::MapVirtualKeyA(win_virtual_key as u32, MAPVK_VK_TO_CHAR) } & 0x7FFF;
|
||||
match char::from_u32(char_key) {
|
||||
Some(';') => Some(VirtualKeyCode::Semicolon),
|
||||
Some('/') => Some(VirtualKeyCode::Slash),
|
||||
Some('`') => Some(VirtualKeyCode::Grave),
|
||||
Some('[') => Some(VirtualKeyCode::LBracket),
|
||||
Some(']') => Some(VirtualKeyCode::RBracket),
|
||||
Some('\'') => Some(VirtualKeyCode::Apostrophe),
|
||||
Some('\\') => Some(VirtualKeyCode::Backslash),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
949
src/platform/windows/events_loop.rs
Normal file
949
src/platform/windows/events_loop.rs
Normal file
@@ -0,0 +1,949 @@
|
||||
//! An events loop on Win32 is a background thread.
|
||||
//!
|
||||
//! Creating an events loop spawns a thread and blocks it in a permanent Win32 events loop.
|
||||
//! Destroying the events loop stops the thread.
|
||||
//!
|
||||
//! You can use the `execute_in_thread` method to execute some code in the background thread.
|
||||
//! Since Win32 requires you to create a window in the right thread, you must use this method
|
||||
//! to create a window.
|
||||
//!
|
||||
//! If you create a window whose class is set to `callback`, the window's events will be
|
||||
//! propagated with `run_forever` and `poll_events`.
|
||||
//! 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::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 winapi::shared::minwindef::{LOWORD, HIWORD, DWORD, WPARAM, LPARAM, INT, UINT, LRESULT, MAX_PATH};
|
||||
use winapi::shared::windef::{HWND, POINT, RECT};
|
||||
use winapi::shared::windowsx;
|
||||
use winapi::um::{winuser, shellapi, processthreadsapi};
|
||||
use winapi::um::winnt::LONG;
|
||||
|
||||
use platform::platform::event;
|
||||
use platform::platform::window::adjust_size;
|
||||
use platform::platform::Cursor;
|
||||
use platform::platform::WindowId;
|
||||
use platform::platform::DEVICE_ID;
|
||||
|
||||
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 {
|
||||
/// Window style
|
||||
pub style: LONG,
|
||||
/// Window ex-style
|
||||
pub ex_style: LONG,
|
||||
/// Window position and size
|
||||
pub rect: RECT,
|
||||
}
|
||||
|
||||
/// Contains information about states and the window that the callback is going to use.
|
||||
#[derive(Clone)]
|
||||
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,
|
||||
/// Used by `WM_GETMINMAXINFO`.
|
||||
pub attributes: WindowAttributes,
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
/// Dummy object that allows inserting a window's state.
|
||||
// We store a pointer in order to !impl Send and Sync.
|
||||
pub struct Inserter(*mut u8);
|
||||
|
||||
impl Inserter {
|
||||
/// Inserts a window's state for the callback to use. The state is removed automatically if the
|
||||
/// callback receives a `WM_CLOSE` message for the window.
|
||||
pub fn insert(&self, window: HWND, state: Arc<Mutex<WindowState>>) {
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
let was_in = context_stash.as_mut().unwrap().windows.insert(window, state);
|
||||
assert!(was_in.is_none());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventsLoop {
|
||||
// Id of the background thread from the Win32 API.
|
||||
thread_id: DWORD,
|
||||
// Receiver for the events. The sender is in the background thread.
|
||||
receiver: mpsc::Receiver<Event>,
|
||||
// Variable that contains the block state of the win32 event loop thread during a WM_SIZE event.
|
||||
// The mutex's value is `true` when it's blocked, and should be set to false when it's done
|
||||
// blocking. That's done by the parent thread when it receives a Resized event.
|
||||
win32_block_loop: Arc<(Mutex<bool>, Condvar)>
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new() -> EventsLoop {
|
||||
// The main events transfer channel.
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let win32_block_loop = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
let win32_block_loop_child = win32_block_loop.clone();
|
||||
|
||||
// Local barrier in order to block the `new()` function until the background thread has
|
||||
// an events queue.
|
||||
let barrier = Arc::new(Barrier::new(2));
|
||||
let barrier_clone = barrier.clone();
|
||||
|
||||
let thread = thread::spawn(move || {
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
*context_stash.borrow_mut() = Some(ThreadLocalData {
|
||||
sender: tx,
|
||||
windows: HashMap::with_capacity(4),
|
||||
win32_block_loop: win32_block_loop_child,
|
||||
mouse_buttons_down: 0
|
||||
});
|
||||
});
|
||||
|
||||
unsafe {
|
||||
// Calling `PostThreadMessageA` on a thread that does not have an events queue yet
|
||||
// will fail. In order to avoid this situation, we call `IsGuiThread` to initialize
|
||||
// it.
|
||||
winuser::IsGUIThread(1);
|
||||
// Then only we unblock the `new()` function. We are sure that we don't call
|
||||
// `PostThreadMessageA()` before `new()` returns.
|
||||
barrier_clone.wait();
|
||||
drop(barrier_clone);
|
||||
|
||||
let mut msg = mem::uninitialized();
|
||||
|
||||
loop {
|
||||
if winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 {
|
||||
// Only happens if the message is `WM_QUIT`.
|
||||
debug_assert_eq!(msg.message, winuser::WM_QUIT);
|
||||
break;
|
||||
}
|
||||
|
||||
match msg.message {
|
||||
x if x == *EXEC_MSG_ID => {
|
||||
let mut function: Box<Box<FnMut(Inserter)>> = Box::from_raw(msg.wParam as usize as *mut _);
|
||||
function(Inserter(ptr::null_mut()));
|
||||
},
|
||||
x if x == *WAKEUP_MSG_ID => {
|
||||
send_event(Event::Awakened);
|
||||
},
|
||||
_ => {
|
||||
// Calls `callback` below.
|
||||
winuser::TranslateMessage(&msg);
|
||||
winuser::DispatchMessageW(&msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Blocks this function until the background thread has an events loop. See other comments.
|
||||
barrier.wait();
|
||||
|
||||
let thread_id = unsafe {
|
||||
let handle = mem::transmute(thread.as_raw_handle());
|
||||
processthreadsapi::GetThreadId(handle)
|
||||
};
|
||||
|
||||
EventsLoop {
|
||||
thread_id,
|
||||
receiver: rx,
|
||||
win32_block_loop
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_events<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(Event)
|
||||
{
|
||||
loop {
|
||||
let event = match self.receiver.try_recv() {
|
||||
Ok(e) => e,
|
||||
Err(_) => return
|
||||
};
|
||||
let is_resize = match event {
|
||||
Event::WindowEvent{ event: WindowEvent::Resized(..), .. } => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
callback(event);
|
||||
if is_resize {
|
||||
let (ref mutex, ref cvar) = *self.win32_block_loop;
|
||||
let mut block_thread = mutex.lock().unwrap();
|
||||
*block_thread = false;
|
||||
cvar.notify_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(Event) -> ControlFlow
|
||||
{
|
||||
loop {
|
||||
let event = match self.receiver.recv() {
|
||||
Ok(e) => e,
|
||||
Err(_) => return
|
||||
};
|
||||
let is_resize = match event {
|
||||
Event::WindowEvent{ event: WindowEvent::Resized(..), .. } => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
let flow = callback(event);
|
||||
if is_resize {
|
||||
let (ref mutex, ref cvar) = *self.win32_block_loop;
|
||||
let mut block_thread = mutex.lock().unwrap();
|
||||
*block_thread = false;
|
||||
cvar.notify_all();
|
||||
}
|
||||
match flow {
|
||||
ControlFlow::Continue => continue,
|
||||
ControlFlow::Break => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventsLoopProxy {
|
||||
EventsLoopProxy {
|
||||
thread_id: self.thread_id,
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes a function in the background thread.
|
||||
///
|
||||
/// Note that we use a FnMut instead of a FnOnce because we're too lazy to create an equivalent
|
||||
/// to the unstable FnBox.
|
||||
///
|
||||
/// 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.
|
||||
pub(super) fn execute_in_thread<F>(&self, function: F)
|
||||
where F: FnMut(Inserter) + Send + 'static
|
||||
{
|
||||
self.create_proxy().execute_in_thread(function)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EventsLoop {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// Posting `WM_QUIT` will cause `GetMessage` to stop.
|
||||
winuser::PostThreadMessageA(self.thread_id, winuser::WM_QUIT, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventsLoopProxy {
|
||||
thread_id: DWORD,
|
||||
}
|
||||
|
||||
impl EventsLoopProxy {
|
||||
pub fn wakeup(&self) -> Result<(), EventsLoopClosed> {
|
||||
unsafe {
|
||||
if winuser::PostThreadMessageA(self.thread_id, *WAKEUP_MSG_ID, 0, 0) != 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
// https://msdn.microsoft.com/fr-fr/library/windows/desktop/ms644946(v=vs.85).aspx
|
||||
// > If the function fails, the return value is zero. To get extended error
|
||||
// > information, call GetLastError. GetLastError returns ERROR_INVALID_THREAD_ID
|
||||
// > if idThread is not a valid thread identifier, or if the thread specified by
|
||||
// > idThread does not have a message queue. GetLastError returns
|
||||
// > ERROR_NOT_ENOUGH_QUOTA when the message limit is hit.
|
||||
// TODO: handle ERROR_NOT_ENOUGH_QUOTA
|
||||
Err(EventsLoopClosed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes a function in the background thread.
|
||||
///
|
||||
/// Note that we use a FnMut instead of a FnOnce because we're too lazy to create an equivalent
|
||||
/// to the unstable FnBox.
|
||||
///
|
||||
/// 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.
|
||||
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);
|
||||
|
||||
let res = 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?"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
// Message sent by the `EventsLoopProxy` when we want to wake up the thread.
|
||||
// WPARAM and LPARAM are unused.
|
||||
static ref WAKEUP_MSG_ID: u32 = {
|
||||
unsafe {
|
||||
winuser::RegisterWindowMessageA("Winit::WakeupMsg\0".as_ptr() as *const i8)
|
||||
}
|
||||
};
|
||||
// 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`,
|
||||
// and LPARAM is unused.
|
||||
static ref EXEC_MSG_ID: u32 = {
|
||||
unsafe {
|
||||
winuser::RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr() as *const i8)
|
||||
}
|
||||
};
|
||||
// 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)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// There's no parameters passed to the callback function, so it needs to get its context stashed
|
||||
// in a thread-local variable.
|
||||
thread_local!(static CONTEXT_STASH: RefCell<Option<ThreadLocalData>> = RefCell::new(None));
|
||||
struct ThreadLocalData {
|
||||
sender: mpsc::Sender<Event>,
|
||||
windows: HashMap<HWND, Arc<Mutex<WindowState>>>,
|
||||
win32_block_loop: Arc<(Mutex<bool>, Condvar)>,
|
||||
mouse_buttons_down: u32
|
||||
}
|
||||
|
||||
// Utility function that dispatches an event on the current thread.
|
||||
fn send_event(event: Event) {
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let context_stash = context_stash.borrow();
|
||||
|
||||
let _ = context_stash.as_ref().unwrap().sender.send(event); // Ignoring if closed
|
||||
});
|
||||
}
|
||||
|
||||
/// Capture mouse input, allowing `window` to receive mouse events when the cursor is outside of
|
||||
/// the window.
|
||||
unsafe fn capture_mouse(window: HWND) {
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
if let Some(context_stash) = context_stash.as_mut() {
|
||||
context_stash.mouse_buttons_down += 1;
|
||||
winuser::SetCapture(window);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Release mouse input, stopping windows on this thread from receiving mouse input when the cursor
|
||||
/// is outside the window.
|
||||
unsafe fn release_mouse() {
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
if let Some(context_stash) = context_stash.as_mut() {
|
||||
context_stash.mouse_buttons_down = context_stash.mouse_buttons_down.saturating_sub(1);
|
||||
if context_stash.mouse_buttons_down == 0 {
|
||||
winuser::ReleaseCapture();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Any window whose callback is configured to this function will have its events propagated
|
||||
/// through the events loop of the thread the window was created in.
|
||||
//
|
||||
// This is the callback that is called by `DispatchMessage` in the events loop.
|
||||
//
|
||||
// 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
|
||||
{
|
||||
match msg {
|
||||
winuser::WM_CLOSE => {
|
||||
use events::WindowEvent::CloseRequested;
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: CloseRequested
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_DESTROY => {
|
||||
use events::WindowEvent::Destroyed;
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
context_stash.as_mut().unwrap().windows.remove(&window);
|
||||
});
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: Destroyed
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_PAINT => {
|
||||
use events::WindowEvent::Refresh;
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: Refresh,
|
||||
});
|
||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
},
|
||||
|
||||
winuser::WM_SIZE => {
|
||||
use events::WindowEvent::Resized;
|
||||
let w = LOWORD(lparam as DWORD) as u32;
|
||||
let h = HIWORD(lparam as DWORD) as u32;
|
||||
|
||||
// Wait for the parent thread to process the resize event before returning from the
|
||||
// callback.
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
let cstash = context_stash.as_mut().unwrap();
|
||||
|
||||
let event = Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: Resized(w, h),
|
||||
};
|
||||
|
||||
// If this window has been inserted into the window map, the resize event happened
|
||||
// during the event loop. If it hasn't, the event happened on window creation and
|
||||
// should be ignored.
|
||||
if cstash.windows.get(&window).is_some() {
|
||||
let (ref mutex, ref cvar) = *cstash.win32_block_loop;
|
||||
let mut block_thread = mutex.lock().unwrap();
|
||||
*block_thread = true;
|
||||
|
||||
// The event needs to be sent after the lock to ensure that `notify_all` is
|
||||
// called after `wait`.
|
||||
cstash.sender.send(event).ok();
|
||||
|
||||
while *block_thread {
|
||||
block_thread = cvar.wait(block_thread).unwrap();
|
||||
}
|
||||
} else {
|
||||
cstash.sender.send(event).ok();
|
||||
}
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_MOVE => {
|
||||
use events::WindowEvent::Moved;
|
||||
let x = LOWORD(lparam as DWORD) as i32;
|
||||
let y = HIWORD(lparam as DWORD) as i32;
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: Moved(x, y),
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_CHAR => {
|
||||
use std::mem;
|
||||
use events::WindowEvent::ReceivedCharacter;
|
||||
let chr: char = mem::transmute(wparam as u32);
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: ReceivedCharacter(chr),
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
// Prevents default windows menu hotkeys playing unwanted
|
||||
// "ding" sounds. Alternatively could check for WM_SYSCOMMAND
|
||||
// with wparam being SC_KEYMENU, but this may prevent some
|
||||
// other unwanted default hotkeys as well.
|
||||
winuser::WM_SYSCHAR => {
|
||||
0
|
||||
}
|
||||
|
||||
winuser::WM_MOUSEMOVE => {
|
||||
use events::WindowEvent::{CursorEntered, CursorMoved};
|
||||
let mouse_outside_window = CONTEXT_STASH.with(|context_stash| {
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
if let Some(context_stash) = context_stash.as_mut() {
|
||||
if let Some(w) = context_stash.windows.get_mut(&window) {
|
||||
let mut w = w.lock().unwrap();
|
||||
if !w.mouse_in_window {
|
||||
w.mouse_in_window = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
});
|
||||
|
||||
if mouse_outside_window {
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: CursorEntered { device_id: DEVICE_ID },
|
||||
});
|
||||
|
||||
// Calling TrackMouseEvent in order to receive mouse leave events.
|
||||
winuser::TrackMouseEvent(&mut winuser::TRACKMOUSEEVENT {
|
||||
cbSize: mem::size_of::<winuser::TRACKMOUSEEVENT>() as DWORD,
|
||||
dwFlags: winuser::TME_LEAVE,
|
||||
hwndTrack: window,
|
||||
dwHoverTime: winuser::HOVER_DEFAULT,
|
||||
});
|
||||
}
|
||||
|
||||
let x = windowsx::GET_X_LPARAM(lparam) as f64;
|
||||
let y = windowsx::GET_Y_LPARAM(lparam) as f64;
|
||||
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event::get_key_mods() },
|
||||
});
|
||||
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_MOUSELEAVE => {
|
||||
use events::WindowEvent::CursorLeft;
|
||||
let mouse_in_window = CONTEXT_STASH.with(|context_stash| {
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
if let Some(context_stash) = context_stash.as_mut() {
|
||||
if let Some(w) = context_stash.windows.get_mut(&window) {
|
||||
let mut w = w.lock().unwrap();
|
||||
if w.mouse_in_window {
|
||||
w.mouse_in_window = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
});
|
||||
|
||||
if mouse_in_window {
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: CursorLeft { device_id: DEVICE_ID }
|
||||
});
|
||||
}
|
||||
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_MOUSEWHEEL => {
|
||||
use events::{DeviceEvent, WindowEvent};
|
||||
use events::MouseScrollDelta::LineDelta;
|
||||
use events::TouchPhase;
|
||||
|
||||
let value = (wparam >> 16) as i16;
|
||||
let value = value as i32;
|
||||
let value = value as f32 / winuser::WHEEL_DELTA as f32;
|
||||
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: LineDelta(0.0, value), phase: TouchPhase::Moved, modifiers: event::get_key_mods() },
|
||||
});
|
||||
|
||||
send_event(Event::DeviceEvent {
|
||||
device_id: DEVICE_ID,
|
||||
event: DeviceEvent::MouseWheel { delta: LineDelta(0.0, value) },
|
||||
});
|
||||
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => {
|
||||
use events::ElementState::Pressed;
|
||||
use events::VirtualKeyCode;
|
||||
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) {
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: WindowEvent::ReceivedCharacter('\u{7F}'),
|
||||
});
|
||||
}
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
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(),
|
||||
},
|
||||
}
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_LBUTTONDOWN => {
|
||||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Left;
|
||||
use events::ElementState::Pressed;
|
||||
|
||||
capture_mouse(window);
|
||||
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Left, modifiers: event::get_key_mods() }
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_LBUTTONUP => {
|
||||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Left;
|
||||
use events::ElementState::Released;
|
||||
|
||||
release_mouse();
|
||||
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: MouseInput { device_id: DEVICE_ID, state: Released, button: Left, modifiers: event::get_key_mods() }
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_RBUTTONDOWN => {
|
||||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Right;
|
||||
use events::ElementState::Pressed;
|
||||
|
||||
capture_mouse(window);
|
||||
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Right, modifiers: event::get_key_mods() }
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_RBUTTONUP => {
|
||||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Right;
|
||||
use events::ElementState::Released;
|
||||
|
||||
release_mouse();
|
||||
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: MouseInput { device_id: DEVICE_ID, state: Released, button: Right, modifiers: event::get_key_mods() }
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_MBUTTONDOWN => {
|
||||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Middle;
|
||||
use events::ElementState::Pressed;
|
||||
|
||||
capture_mouse(window);
|
||||
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Middle, modifiers: event::get_key_mods() }
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_MBUTTONUP => {
|
||||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Middle;
|
||||
use events::ElementState::Released;
|
||||
|
||||
release_mouse();
|
||||
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: MouseInput { device_id: DEVICE_ID, state: Released, button: Middle, modifiers: event::get_key_mods() }
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_XBUTTONDOWN => {
|
||||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Other;
|
||||
use events::ElementState::Pressed;
|
||||
let xbutton = winuser::GET_XBUTTON_WPARAM(wparam);
|
||||
|
||||
capture_mouse(window);
|
||||
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Other(xbutton as u8), modifiers: event::get_key_mods() }
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_XBUTTONUP => {
|
||||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Other;
|
||||
use events::ElementState::Released;
|
||||
let xbutton = winuser::GET_XBUTTON_WPARAM(wparam);
|
||||
|
||||
release_mouse();
|
||||
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: MouseInput { device_id: DEVICE_ID, state: Released, button: Other(xbutton as u8), modifiers: event::get_key_mods() }
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_INPUT => {
|
||||
use events::DeviceEvent::{Motion, MouseMotion};
|
||||
let mut data: winuser::RAWINPUT = mem::uninitialized();
|
||||
let mut data_size = mem::size_of::<winuser::RAWINPUT>() as UINT;
|
||||
winuser::GetRawInputData(mem::transmute(lparam), winuser::RID_INPUT,
|
||||
mem::transmute(&mut data), &mut data_size,
|
||||
mem::size_of::<winuser::RAWINPUTHEADER>() as UINT);
|
||||
|
||||
if data.header.dwType == winuser::RIM_TYPEMOUSE {
|
||||
let mouse = data.data.mouse();
|
||||
if mouse.usFlags & winuser::MOUSE_MOVE_RELATIVE == winuser::MOUSE_MOVE_RELATIVE {
|
||||
let x = mouse.lLastX as f64;
|
||||
let y = mouse.lLastY as f64;
|
||||
|
||||
if x != 0.0 {
|
||||
send_event(Event::DeviceEvent {
|
||||
device_id: DEVICE_ID,
|
||||
event: Motion { axis: 0, value: x }
|
||||
});
|
||||
}
|
||||
|
||||
if y != 0.0 {
|
||||
send_event(Event::DeviceEvent {
|
||||
device_id: DEVICE_ID,
|
||||
event: Motion { axis: 1, value: y }
|
||||
});
|
||||
}
|
||||
|
||||
if x != 0.0 || y != 0.0 {
|
||||
send_event(Event::DeviceEvent {
|
||||
device_id: DEVICE_ID,
|
||||
event: MouseMotion { delta: (x, y) }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
} else {
|
||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
}
|
||||
},
|
||||
|
||||
winuser::WM_TOUCH => {
|
||||
let pcount = LOWORD( wparam as DWORD ) as usize;
|
||||
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 {
|
||||
for input in &inputs {
|
||||
send_event( Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: WindowEvent::Touch(Touch {
|
||||
phase:
|
||||
if input.dwFlags & winuser::TOUCHEVENTF_DOWN != 0 {
|
||||
TouchPhase::Started
|
||||
} else if input.dwFlags & winuser::TOUCHEVENTF_UP != 0 {
|
||||
TouchPhase::Ended
|
||||
} else if input.dwFlags & winuser::TOUCHEVENTF_MOVE != 0 {
|
||||
TouchPhase::Moved
|
||||
} else {
|
||||
continue;
|
||||
},
|
||||
location: ((input.x as f64) / 100f64,
|
||||
(input.y as f64) / 100f64),
|
||||
id: input.dwID as u64,
|
||||
device_id: DEVICE_ID,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
winuser::CloseTouchInputHandle( htouch );
|
||||
0
|
||||
}
|
||||
|
||||
winuser::WM_SETFOCUS => {
|
||||
use events::WindowEvent::{Focused, CursorMoved};
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: Focused(true)
|
||||
});
|
||||
|
||||
let x = windowsx::GET_X_LPARAM(lparam) as f64;
|
||||
let y = windowsx::GET_Y_LPARAM(lparam) as f64;
|
||||
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event::get_key_mods() },
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_KILLFOCUS => {
|
||||
use events::WindowEvent::Focused;
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: Focused(false)
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
call_def_window_proc
|
||||
});
|
||||
|
||||
if call_def_window_proc {
|
||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
winuser::WM_DROPFILES => {
|
||||
use events::WindowEvent::DroppedFile;
|
||||
|
||||
let hdrop = wparam as shellapi::HDROP;
|
||||
let mut pathbuf: [u16; MAX_PATH] = mem::uninitialized();
|
||||
let num_drops = shellapi::DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0);
|
||||
|
||||
for i in 0..num_drops {
|
||||
let nch = shellapi::DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(),
|
||||
MAX_PATH as u32) as usize;
|
||||
if nch > 0 {
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: DroppedFile(OsString::from_wide(&pathbuf[0..nch]).into())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
shellapi::DragFinish(hdrop);
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_GETMINMAXINFO => {
|
||||
let mmi = lparam as *mut winuser::MINMAXINFO;
|
||||
//(*mmi).max_position = winapi::shared::windef::POINT { x: -8, y: -8 }; // The upper left corner of the window if it were maximized on the primary monitor.
|
||||
//(*mmi).max_size = winapi::shared::windef::POINT { x: .., y: .. }; // The dimensions of the primary monitor.
|
||||
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
if let Some(cstash) = context_stash.borrow().as_ref() {
|
||||
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() {
|
||||
|
||||
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);
|
||||
(*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);
|
||||
(*mmi).ptMaxTrackSize = POINT { x: width as i32, y: height as i32 };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
0
|
||||
},
|
||||
|
||||
_ => {
|
||||
if msg == *DESTROY_MSG_ID {
|
||||
winuser::DestroyWindow(window);
|
||||
0
|
||||
} else {
|
||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::io;
|
||||
use std::ptr;
|
||||
use std::mem;
|
||||
use std::thread;
|
||||
|
||||
use super::callback;
|
||||
use super::WindowState;
|
||||
use super::Window;
|
||||
use super::MonitorId;
|
||||
use super::WindowWrapper;
|
||||
use super::PlatformSpecificWindowBuilderAttributes;
|
||||
|
||||
use CreationError;
|
||||
use CreationError::OsError;
|
||||
use CursorState;
|
||||
use WindowAttributes;
|
||||
|
||||
use std::ffi::{OsStr};
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
use winapi;
|
||||
use kernel32;
|
||||
use dwmapi;
|
||||
use user32;
|
||||
|
||||
pub fn new_window(window: &WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result<Window, CreationError> {
|
||||
let window = window.clone();
|
||||
let attribs = pl_attribs.clone();
|
||||
// initializing variables to be sent to the task
|
||||
|
||||
let title = OsStr::new(&window.title).encode_wide().chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let (tx, rx) = channel();
|
||||
|
||||
// `GetMessage` must be called in the same thread as CreateWindow, so we create a new thread
|
||||
// dedicated to this window.
|
||||
thread::spawn(move || {
|
||||
unsafe {
|
||||
// creating and sending the `Window`
|
||||
match init(title, &window, attribs) {
|
||||
Ok(w) => tx.send(Ok(w)).ok(),
|
||||
Err(e) => {
|
||||
tx.send(Err(e)).ok();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// now that the `Window` struct is initialized, the main `Window::new()` function will
|
||||
// return and this events loop will run in parallel
|
||||
loop {
|
||||
let mut msg = mem::uninitialized();
|
||||
|
||||
if user32::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
user32::TranslateMessage(&msg);
|
||||
user32::DispatchMessageW(&msg); // calls `callback` (see the callback module)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
rx.recv().unwrap()
|
||||
}
|
||||
|
||||
unsafe fn init(title: Vec<u16>, window: &WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes) -> Result<Window, CreationError> {
|
||||
// registering the window class
|
||||
let class_name = register_window_class();
|
||||
|
||||
// building a RECT object with coordinates
|
||||
let mut rect = winapi::RECT {
|
||||
left: 0, right: window.dimensions.unwrap_or((1024, 768)).0 as winapi::LONG,
|
||||
top: 0, bottom: window.dimensions.unwrap_or((1024, 768)).1 as winapi::LONG,
|
||||
};
|
||||
|
||||
// switching to fullscreen if necessary
|
||||
// this means adjusting the window's position so that it overlaps the right monitor,
|
||||
// and change the monitor's resolution if necessary
|
||||
if window.monitor.is_some() {
|
||||
let monitor = window.monitor.as_ref().unwrap();
|
||||
try!(switch_to_fullscreen(&mut rect, monitor));
|
||||
}
|
||||
|
||||
// computing the style and extended style of the window
|
||||
let (ex_style, style) = if window.monitor.is_some() || !window.decorations {
|
||||
(winapi::WS_EX_APPWINDOW,
|
||||
//winapi::WS_POPUP is incompatible with winapi::WS_CHILD
|
||||
if pl_attribs.parent.is_some() {
|
||||
winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN
|
||||
}
|
||||
else {
|
||||
winapi::WS_POPUP | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN
|
||||
}
|
||||
)
|
||||
} else {
|
||||
(winapi::WS_EX_APPWINDOW | winapi::WS_EX_WINDOWEDGE,
|
||||
winapi::WS_OVERLAPPEDWINDOW | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN)
|
||||
};
|
||||
|
||||
// adjusting the window coordinates using the style
|
||||
user32::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
|
||||
|
||||
// creating the real window this time, by using the functions in `extra_functions`
|
||||
let real_window = {
|
||||
let (width, height) = if window.monitor.is_some() || window.dimensions.is_some() {
|
||||
(Some(rect.right - rect.left), Some(rect.bottom - rect.top))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let (x, y) = if window.monitor.is_some() {
|
||||
(Some(rect.left), Some(rect.top))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let mut style = if !window.visible {
|
||||
style
|
||||
} else {
|
||||
style | winapi::WS_VISIBLE
|
||||
};
|
||||
|
||||
if pl_attribs.parent.is_some() {
|
||||
style |= winapi::WS_CHILD;
|
||||
}
|
||||
|
||||
let handle = user32::CreateWindowExW(ex_style | winapi::WS_EX_ACCEPTFILES,
|
||||
class_name.as_ptr(),
|
||||
title.as_ptr() as winapi::LPCWSTR,
|
||||
style | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN,
|
||||
x.unwrap_or(winapi::CW_USEDEFAULT), y.unwrap_or(winapi::CW_USEDEFAULT),
|
||||
width.unwrap_or(winapi::CW_USEDEFAULT), height.unwrap_or(winapi::CW_USEDEFAULT),
|
||||
pl_attribs.parent.unwrap_or(ptr::null_mut()),
|
||||
ptr::null_mut(), kernel32::GetModuleHandleW(ptr::null()),
|
||||
ptr::null_mut());
|
||||
|
||||
if handle.is_null() {
|
||||
return Err(OsError(format!("CreateWindowEx function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
let hdc = user32::GetDC(handle);
|
||||
if hdc.is_null() {
|
||||
return Err(OsError(format!("GetDC function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
WindowWrapper(handle, hdc)
|
||||
};
|
||||
|
||||
// making the window transparent
|
||||
if window.transparent {
|
||||
let bb = winapi::DWM_BLURBEHIND {
|
||||
dwFlags: 0x1, // FIXME: DWM_BB_ENABLE;
|
||||
fEnable: 1,
|
||||
hRgnBlur: ptr::null_mut(),
|
||||
fTransitionOnMaximized: 0,
|
||||
};
|
||||
|
||||
dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb);
|
||||
}
|
||||
|
||||
// calling SetForegroundWindow if fullscreen
|
||||
if window.monitor.is_some() {
|
||||
user32::SetForegroundWindow(real_window.0);
|
||||
}
|
||||
|
||||
// Creating a mutex to track the current window state
|
||||
let window_state = Arc::new(Mutex::new(WindowState {
|
||||
cursor: winapi::IDC_ARROW, // use arrow by default
|
||||
cursor_state: CursorState::Normal,
|
||||
attributes: window.clone()
|
||||
}));
|
||||
|
||||
// filling the CONTEXT_STASH task-local storage so that we can start receiving events
|
||||
let events_receiver = {
|
||||
let (tx, rx) = channel();
|
||||
let mut tx = Some(tx);
|
||||
callback::CONTEXT_STASH.with(|context_stash| {
|
||||
let data = callback::ThreadLocalData {
|
||||
win: real_window.0,
|
||||
sender: tx.take().unwrap(),
|
||||
window_state: window_state.clone(),
|
||||
mouse_in_window: false
|
||||
};
|
||||
(*context_stash.borrow_mut()) = Some(data);
|
||||
});
|
||||
rx
|
||||
};
|
||||
|
||||
// building the struct
|
||||
Ok(Window {
|
||||
window: real_window,
|
||||
events_receiver: events_receiver,
|
||||
window_state: window_state,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn register_window_class() -> Vec<u16> {
|
||||
let class_name = OsStr::new("Window Class").encode_wide().chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let class = winapi::WNDCLASSEXW {
|
||||
cbSize: mem::size_of::<winapi::WNDCLASSEXW>() as winapi::UINT,
|
||||
style: winapi::CS_HREDRAW | winapi::CS_VREDRAW | winapi::CS_OWNDC,
|
||||
lpfnWndProc: Some(callback::callback),
|
||||
cbClsExtra: 0,
|
||||
cbWndExtra: 0,
|
||||
hInstance: kernel32::GetModuleHandleW(ptr::null()),
|
||||
hIcon: ptr::null_mut(),
|
||||
hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly
|
||||
hbrBackground: ptr::null_mut(),
|
||||
lpszMenuName: ptr::null(),
|
||||
lpszClassName: class_name.as_ptr(),
|
||||
hIconSm: ptr::null_mut(),
|
||||
};
|
||||
|
||||
// We ignore errors because registering the same window class twice would trigger
|
||||
// an error, and because errors here are detected during CreateWindowEx anyway.
|
||||
// Also since there is no weird element in the struct, there is no reason for this
|
||||
// call to fail.
|
||||
user32::RegisterClassExW(&class);
|
||||
|
||||
class_name
|
||||
}
|
||||
|
||||
unsafe fn switch_to_fullscreen(rect: &mut winapi::RECT, monitor: &MonitorId)
|
||||
-> Result<(), CreationError>
|
||||
{
|
||||
// adjusting the rect
|
||||
{
|
||||
let pos = monitor.get_position();
|
||||
rect.left += pos.0 as winapi::LONG;
|
||||
rect.right += pos.0 as winapi::LONG;
|
||||
rect.top += pos.1 as winapi::LONG;
|
||||
rect.bottom += pos.1 as winapi::LONG;
|
||||
}
|
||||
|
||||
// changing device settings
|
||||
let mut screen_settings: winapi::DEVMODEW = mem::zeroed();
|
||||
screen_settings.dmSize = mem::size_of::<winapi::DEVMODEW>() as winapi::WORD;
|
||||
screen_settings.dmPelsWidth = (rect.right - rect.left) as winapi::DWORD;
|
||||
screen_settings.dmPelsHeight = (rect.bottom - rect.top) as winapi::DWORD;
|
||||
screen_settings.dmBitsPerPel = 32; // TODO: ?
|
||||
screen_settings.dmFields = winapi::DM_BITSPERPEL | winapi::DM_PELSWIDTH | winapi::DM_PELSHEIGHT;
|
||||
|
||||
let result = user32::ChangeDisplaySettingsExW(monitor.get_adapter_name().as_ptr(),
|
||||
&mut screen_settings, ptr::null_mut(),
|
||||
winapi::CDS_FULLSCREEN, ptr::null_mut());
|
||||
|
||||
if result != winapi::DISP_CHANGE_SUCCESSFUL {
|
||||
return Err(OsError(format!("ChangeDisplaySettings failed: {}", result)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,385 +1,34 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::ffi::OsStr;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::os::raw::c_int;
|
||||
use std::sync::{
|
||||
Arc,
|
||||
Mutex
|
||||
};
|
||||
use std::sync::mpsc::Receiver;
|
||||
use {CreationError, WindowEvent as Event, MouseCursor};
|
||||
use CursorState;
|
||||
use winapi;
|
||||
use winapi::shared::windef::HWND;
|
||||
|
||||
use WindowAttributes;
|
||||
|
||||
gen_api_transition!();
|
||||
pub use self::events_loop::{EventsLoop, EventsLoopProxy};
|
||||
pub use self::monitor::MonitorId;
|
||||
pub use self::window::Window;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub parent: Option<winapi::HWND>,
|
||||
pub parent: Option<HWND>,
|
||||
}
|
||||
|
||||
unsafe impl Send for PlatformSpecificWindowBuilderAttributes {}
|
||||
unsafe impl Sync for PlatformSpecificWindowBuilderAttributes {}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificHeadlessBuilderAttributes;
|
||||
// TODO: document what this means
|
||||
pub type Cursor = *const winapi::ctypes::wchar_t;
|
||||
|
||||
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
|
||||
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
|
||||
|
||||
use winapi;
|
||||
use user32;
|
||||
use kernel32;
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(HWND);
|
||||
unsafe impl Send for WindowId {}
|
||||
unsafe impl Sync for WindowId {}
|
||||
|
||||
mod callback;
|
||||
mod event;
|
||||
mod init;
|
||||
mod events_loop;
|
||||
mod monitor;
|
||||
|
||||
lazy_static! {
|
||||
static ref WAKEUP_MSG_ID: u32 = unsafe { user32::RegisterWindowMessageA("Glutin::EventID".as_ptr() as *const i8) };
|
||||
}
|
||||
|
||||
/// Cursor
|
||||
pub type Cursor = *const winapi::wchar_t;
|
||||
|
||||
/// Contains information about states and the window for the callback.
|
||||
#[derive(Clone)]
|
||||
pub struct WindowState {
|
||||
pub cursor: Cursor,
|
||||
pub cursor_state: CursorState,
|
||||
pub attributes: WindowAttributes
|
||||
}
|
||||
|
||||
/// The Win32 implementation of the main `Window` object.
|
||||
pub struct Window {
|
||||
/// Main handle for the window.
|
||||
window: WindowWrapper,
|
||||
|
||||
/// Receiver for the events dispatched by the window callback.
|
||||
events_receiver: Receiver<Event>,
|
||||
|
||||
/// The current window state.
|
||||
window_state: Arc<Mutex<WindowState>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Window {}
|
||||
unsafe impl Sync for Window {}
|
||||
|
||||
/// A simple wrapper that destroys the window when it is destroyed.
|
||||
#[doc(hidden)]
|
||||
pub struct WindowWrapper(winapi::HWND, winapi::HDC);
|
||||
|
||||
impl Drop for WindowWrapper {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
user32::DestroyWindow(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WindowProxy {
|
||||
hwnd: winapi::HWND,
|
||||
}
|
||||
|
||||
unsafe impl Send for WindowProxy {}
|
||||
unsafe impl Sync for WindowProxy {}
|
||||
|
||||
impl WindowProxy {
|
||||
#[inline]
|
||||
pub fn wakeup_event_loop(&self) {
|
||||
unsafe {
|
||||
user32::PostMessageA(self.hwnd, *WAKEUP_MSG_ID, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
/// See the docs in the crate root file.
|
||||
pub fn new(window: &WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window, CreationError>
|
||||
{
|
||||
init::new_window(window, pl_attribs)
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
///
|
||||
/// Calls SetWindowText on the HWND.
|
||||
pub fn set_title(&self, text: &str) {
|
||||
let text = OsStr::new(text).encode_wide().chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
unsafe {
|
||||
user32::SetWindowTextW(self.window.0, text.as_ptr() as winapi::LPCWSTR);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show(&self) {
|
||||
unsafe {
|
||||
user32::ShowWindow(self.window.0, winapi::SW_SHOW);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide(&self) {
|
||||
unsafe {
|
||||
user32::ShowWindow(self.window.0, winapi::SW_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
use std::mem;
|
||||
|
||||
let mut placement: winapi::WINDOWPLACEMENT = unsafe { mem::zeroed() };
|
||||
placement.length = mem::size_of::<winapi::WINDOWPLACEMENT>() as winapi::UINT;
|
||||
|
||||
if unsafe { user32::GetWindowPlacement(self.window.0, &mut placement) } == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
let ref rect = placement.rcNormalPosition;
|
||||
Some((rect.left as i32, rect.top as i32))
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn set_position(&self, x: i32, y: i32) {
|
||||
unsafe {
|
||||
user32::SetWindowPos(self.window.0, ptr::null_mut(), x as c_int, y as c_int,
|
||||
0, 0, winapi::SWP_NOZORDER | winapi::SWP_NOSIZE);
|
||||
user32::UpdateWindow(self.window.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
let mut rect: winapi::RECT = unsafe { mem::uninitialized() };
|
||||
|
||||
if unsafe { user32::GetClientRect(self.window.0, &mut rect) } == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
Some((
|
||||
(rect.right - rect.left) as u32,
|
||||
(rect.bottom - rect.top) as u32
|
||||
))
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
let mut rect: winapi::RECT = unsafe { mem::uninitialized() };
|
||||
|
||||
if unsafe { user32::GetWindowRect(self.window.0, &mut rect) } == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
Some((
|
||||
(rect.right - rect.left) as u32,
|
||||
(rect.bottom - rect.top) as u32
|
||||
))
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn set_inner_size(&self, x: u32, y: u32) {
|
||||
unsafe {
|
||||
// Calculate the outer size based upon the specified inner size
|
||||
let mut rect = winapi::RECT { top: 0, left: 0, bottom: y as winapi::LONG, right: x as winapi::LONG };
|
||||
let dw_style = user32::GetWindowLongA(self.window.0, winapi::GWL_STYLE) as winapi::DWORD;
|
||||
let b_menu = !user32::GetMenu(self.window.0).is_null() as winapi::BOOL;
|
||||
let dw_style_ex = user32::GetWindowLongA(self.window.0, winapi::GWL_EXSTYLE) as winapi::DWORD;
|
||||
user32::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;
|
||||
|
||||
user32::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, outer_x, outer_y,
|
||||
winapi::SWP_NOZORDER | winapi::SWP_NOREPOSITION | winapi::SWP_NOMOVE);
|
||||
user32::UpdateWindow(self.window.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn create_window_proxy(&self) -> WindowProxy {
|
||||
WindowProxy { hwnd: self.window.0 }
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
#[inline]
|
||||
pub fn poll_events(&self) -> PollEventsIterator {
|
||||
PollEventsIterator {
|
||||
window: self,
|
||||
}
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
#[inline]
|
||||
pub fn wait_events(&self) -> WaitEventsIterator {
|
||||
WaitEventsIterator {
|
||||
window: self,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut ::libc::c_void {
|
||||
// What should this return on win32?
|
||||
// It could be GetDC(NULL), but that requires a ReleaseDC()
|
||||
// to avoid leaking the DC.
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_window(&self) -> *mut ::libc::c_void {
|
||||
self.window.0 as *mut ::libc::c_void
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _cursor: MouseCursor) {
|
||||
let cursor_id = match _cursor {
|
||||
MouseCursor::Arrow | MouseCursor::Default => winapi::IDC_ARROW,
|
||||
MouseCursor::Hand => winapi::IDC_HAND,
|
||||
MouseCursor::Crosshair => winapi::IDC_CROSS,
|
||||
MouseCursor::Text | MouseCursor::VerticalText => winapi::IDC_IBEAM,
|
||||
MouseCursor::NotAllowed | MouseCursor::NoDrop => winapi::IDC_NO,
|
||||
MouseCursor::EResize => winapi::IDC_SIZEWE,
|
||||
MouseCursor::NResize => winapi::IDC_SIZENS,
|
||||
MouseCursor::WResize => winapi::IDC_SIZEWE,
|
||||
MouseCursor::SResize => winapi::IDC_SIZENS,
|
||||
MouseCursor::EwResize | MouseCursor::ColResize => winapi::IDC_SIZEWE,
|
||||
MouseCursor::NsResize | MouseCursor::RowResize => winapi::IDC_SIZENS,
|
||||
MouseCursor::Wait | MouseCursor::Progress => winapi::IDC_WAIT,
|
||||
MouseCursor::Help => winapi::IDC_HELP,
|
||||
_ => winapi::IDC_ARROW, // use arrow for the missing cases.
|
||||
};
|
||||
|
||||
let mut cur = self.window_state.lock().unwrap();
|
||||
cur.cursor = cursor_id;
|
||||
}
|
||||
|
||||
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
let mut current_state = self.window_state.lock().unwrap();
|
||||
|
||||
let foreground_thread_id = unsafe { user32::GetWindowThreadProcessId(self.window.0, ptr::null_mut()) };
|
||||
let current_thread_id = unsafe { kernel32::GetCurrentThreadId() };
|
||||
|
||||
unsafe { user32::AttachThreadInput(foreground_thread_id, current_thread_id, 1) };
|
||||
|
||||
let res = match (state, current_state.cursor_state) {
|
||||
(CursorState::Normal, CursorState::Normal) => Ok(()),
|
||||
(CursorState::Hide, CursorState::Hide) => Ok(()),
|
||||
(CursorState::Grab, CursorState::Grab) => Ok(()),
|
||||
|
||||
(CursorState::Hide, CursorState::Normal) => {
|
||||
current_state.cursor_state = CursorState::Hide;
|
||||
Ok(())
|
||||
},
|
||||
|
||||
(CursorState::Normal, CursorState::Hide) => {
|
||||
current_state.cursor_state = CursorState::Normal;
|
||||
Ok(())
|
||||
},
|
||||
|
||||
(CursorState::Grab, CursorState::Normal) | (CursorState::Grab, CursorState::Hide) => {
|
||||
unsafe {
|
||||
let mut rect = mem::uninitialized();
|
||||
if user32::GetClientRect(self.window.0, &mut rect) == 0 {
|
||||
return Err(format!("GetWindowRect failed"));
|
||||
}
|
||||
user32::ClientToScreen(self.window.0, mem::transmute(&mut rect.left));
|
||||
user32::ClientToScreen(self.window.0, mem::transmute(&mut rect.right));
|
||||
if user32::ClipCursor(&rect) == 0 {
|
||||
return Err(format!("ClipCursor failed"));
|
||||
}
|
||||
current_state.cursor_state = CursorState::Grab;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
|
||||
(CursorState::Normal, CursorState::Grab) => {
|
||||
unsafe {
|
||||
if user32::ClipCursor(ptr::null()) == 0 {
|
||||
return Err(format!("ClipCursor failed"));
|
||||
}
|
||||
current_state.cursor_state = CursorState::Normal;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
unsafe { user32::AttachThreadInput(foreground_thread_id, current_thread_id, 0) };
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
1.0
|
||||
}
|
||||
|
||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||
let mut point = winapi::POINT {
|
||||
x: x,
|
||||
y: y,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
if user32::ClientToScreen(self.window.0, &mut point) == 0 {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
if user32::SetCursorPos(point.x, point.y) == 0 {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
user32::PostMessageW(self.window.0, winapi::WM_DESTROY, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PollEventsIterator<'a> {
|
||||
window: &'a Window,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for PollEventsIterator<'a> {
|
||||
type Item = Event;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Event> {
|
||||
self.window.events_receiver.try_recv().ok()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WaitEventsIterator<'a> {
|
||||
window: &'a Window,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for WaitEventsIterator<'a> {
|
||||
type Item = Event;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Event> {
|
||||
self.window.events_receiver.recv().ok()
|
||||
}
|
||||
}
|
||||
mod window;
|
||||
|
||||
@@ -1,186 +1,178 @@
|
||||
use winapi;
|
||||
use user32;
|
||||
use winapi::ctypes::wchar_t;
|
||||
use winapi::shared::minwindef::{DWORD, LPARAM, BOOL, TRUE};
|
||||
use winapi::shared::windef::{HMONITOR, HDC, LPRECT, HWND};
|
||||
use winapi::um::winuser;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::mem;
|
||||
use std::{mem, ptr};
|
||||
|
||||
use native_monitor::NativeMonitorId;
|
||||
use super::EventsLoop;
|
||||
|
||||
/// Win32 implementation of the main `MonitorId` object.
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorId {
|
||||
/// The system name of the adapter.
|
||||
adapter_name: [winapi::WCHAR; 32],
|
||||
adapter_name: [wchar_t; 32],
|
||||
|
||||
/// Monitor handle.
|
||||
hmonitor: HMonitor,
|
||||
|
||||
/// The system name of the monitor.
|
||||
monitor_name: String,
|
||||
|
||||
/// Name to give to the user.
|
||||
readable_name: String,
|
||||
|
||||
/// See the `StateFlags` element here:
|
||||
/// http://msdn.microsoft.com/en-us/library/dd183569(v=vs.85).aspx
|
||||
flags: winapi::DWORD,
|
||||
|
||||
/// True if this is the primary monitor.
|
||||
primary: bool,
|
||||
|
||||
/// The position of the monitor in pixels on the desktop.
|
||||
///
|
||||
/// A window that is positionned at these coordinates will overlap the monitor.
|
||||
position: (u32, u32),
|
||||
/// 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,
|
||||
}
|
||||
|
||||
struct DeviceEnumerator {
|
||||
parent_device: *const winapi::WCHAR,
|
||||
current_index: u32,
|
||||
}
|
||||
// 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)]
|
||||
struct HMonitor(HMONITOR);
|
||||
|
||||
impl DeviceEnumerator {
|
||||
fn adapters() -> DeviceEnumerator {
|
||||
use std::ptr;
|
||||
DeviceEnumerator {
|
||||
parent_device: ptr::null(),
|
||||
current_index: 0
|
||||
}
|
||||
}
|
||||
unsafe impl Send for HMonitor {}
|
||||
|
||||
fn monitors(adapter_name: *const winapi::WCHAR) -> DeviceEnumerator {
|
||||
DeviceEnumerator {
|
||||
parent_device: adapter_name,
|
||||
current_index: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for DeviceEnumerator {
|
||||
type Item = winapi::DISPLAY_DEVICEW;
|
||||
fn next(&mut self) -> Option<winapi::DISPLAY_DEVICEW> {
|
||||
use std::mem;
|
||||
loop {
|
||||
let mut output: winapi::DISPLAY_DEVICEW = unsafe { mem::zeroed() };
|
||||
output.cb = mem::size_of::<winapi::DISPLAY_DEVICEW>() as winapi::DWORD;
|
||||
|
||||
if unsafe { user32::EnumDisplayDevicesW(self.parent_device,
|
||||
self.current_index as winapi::DWORD, &mut output, 0) } == 0
|
||||
{
|
||||
// the device doesn't exist, which means we have finished enumerating
|
||||
break;
|
||||
}
|
||||
self.current_index += 1;
|
||||
|
||||
if (output.StateFlags & winapi::DISPLAY_DEVICE_ACTIVE) == 0 ||
|
||||
(output.StateFlags & winapi::DISPLAY_DEVICE_MIRRORING_DRIVER) != 0
|
||||
{
|
||||
// the device is not active
|
||||
// the Win32 api usually returns a lot of inactive devices
|
||||
continue;
|
||||
}
|
||||
|
||||
return Some(output);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn wchar_as_string(wchar: &[winapi::WCHAR]) -> String {
|
||||
fn wchar_as_string(wchar: &[wchar_t]) -> String {
|
||||
String::from_utf16_lossy(wchar)
|
||||
.trim_right_matches(0 as char)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
/// Win32 implementation of the main `get_available_monitors` function.
|
||||
pub fn get_available_monitors() -> VecDeque<MonitorId> {
|
||||
// return value
|
||||
let mut result = VecDeque::new();
|
||||
unsafe extern "system" fn monitor_enum_proc(hmonitor: HMONITOR, _: HDC, place: LPRECT, data: LPARAM) -> BOOL {
|
||||
let monitors = data as *mut VecDeque<MonitorId>;
|
||||
|
||||
for adapter in DeviceEnumerator::adapters() {
|
||||
// getting the position
|
||||
let (position, dimensions) = unsafe {
|
||||
let mut dev: winapi::DEVMODEW = mem::zeroed();
|
||||
dev.dmSize = mem::size_of::<winapi::DEVMODEW>() as winapi::WORD;
|
||||
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);
|
||||
|
||||
if user32::EnumDisplaySettingsExW(adapter.DeviceName.as_ptr(),
|
||||
winapi::ENUM_CURRENT_SETTINGS,
|
||||
&mut dev, 0) == 0
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let point: &winapi::POINTL = mem::transmute(&dev.union1);
|
||||
let position = (point.x as u32, point.y as u32);
|
||||
|
||||
let dimensions = (dev.dmPelsWidth as u32, dev.dmPelsHeight as u32);
|
||||
|
||||
(position, dimensions)
|
||||
};
|
||||
|
||||
for (num, monitor) in DeviceEnumerator::monitors(adapter.DeviceName.as_ptr()).enumerate() {
|
||||
// adding to the resulting list
|
||||
result.push_back(MonitorId {
|
||||
adapter_name: adapter.DeviceName,
|
||||
monitor_name: wchar_as_string(&monitor.DeviceName),
|
||||
readable_name: wchar_as_string(&monitor.DeviceString),
|
||||
flags: monitor.StateFlags,
|
||||
primary: (adapter.StateFlags & winapi::DISPLAY_DEVICE_PRIMARY_DEVICE) != 0 &&
|
||||
num == 0,
|
||||
position: position,
|
||||
dimensions: dimensions,
|
||||
});
|
||||
}
|
||||
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;
|
||||
}
|
||||
result
|
||||
|
||||
(*monitors).push_back(MonitorId {
|
||||
adapter_name: monitor_info.szDevice,
|
||||
hmonitor: HMonitor(hmonitor),
|
||||
monitor_name: wchar_as_string(&monitor_info.szDevice),
|
||||
primary: monitor_info.dwFlags & winuser::MONITORINFOF_PRIMARY != 0,
|
||||
position,
|
||||
dimensions,
|
||||
hidpi_factor: 1.0,
|
||||
});
|
||||
|
||||
// TRUE means continue enumeration.
|
||||
TRUE
|
||||
}
|
||||
|
||||
/// Win32 implementation of the main `get_primary_monitor` function.
|
||||
pub fn get_primary_monitor() -> MonitorId {
|
||||
// we simply get all available monitors and return the one with the `PRIMARY_DEVICE` flag
|
||||
// TODO: it is possible to query the win32 API for the primary monitor, this should be done
|
||||
// instead
|
||||
for monitor in get_available_monitors().into_iter() {
|
||||
if monitor.primary {
|
||||
return monitor;
|
||||
impl EventsLoop {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
panic!("Failed to find the primary monitor")
|
||||
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: wchar_as_string(&monitor_info.szDevice),
|
||||
primary: monitor_info.dwFlags & winuser::MONITORINFOF_PRIMARY != 0,
|
||||
position,
|
||||
dimensions,
|
||||
hidpi_factor: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
panic!("Failed to find the primary monitor")
|
||||
}
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
/// See the docs if the crate root file.
|
||||
#[inline]
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
Some(self.readable_name.clone())
|
||||
Some(self.monitor_name.clone())
|
||||
}
|
||||
|
||||
/// See the docs of the crate root file.
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> NativeMonitorId {
|
||||
NativeMonitorId::Name(self.monitor_name.clone())
|
||||
pub fn get_native_identifier(&self) -> String {
|
||||
self.monitor_name.clone()
|
||||
}
|
||||
|
||||
/// See the docs if the crate root file.
|
||||
/// 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: retreive the dimensions every time this is called
|
||||
// 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) -> &[winapi::WCHAR] {
|
||||
pub fn get_adapter_name(&self) -> &[wchar_t] {
|
||||
&self.adapter_name
|
||||
}
|
||||
|
||||
/// This is a Win32-only function for `MonitorId` that returns the position of the
|
||||
/// monitor on the desktop.
|
||||
/// A window that is positionned at these coordinates will overlap the monitor.
|
||||
/// A window that is positioned at these coordinates will overlap the monitor.
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (u32, u32) {
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
self.position
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
self.hidpi_factor
|
||||
}
|
||||
}
|
||||
|
||||
942
src/platform/windows/window.rs
Normal file
942
src/platform/windows/window.rs
Normal file
@@ -0,0 +1,942 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use std::ffi::OsStr;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::os::raw;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::cell::Cell;
|
||||
|
||||
use platform::platform::events_loop::{self, DESTROY_MSG_ID};
|
||||
use platform::platform::EventsLoop;
|
||||
use platform::platform::PlatformSpecificWindowBuilderAttributes;
|
||||
use platform::platform::WindowId;
|
||||
|
||||
use CreationError;
|
||||
use CursorState;
|
||||
use MouseCursor;
|
||||
use WindowAttributes;
|
||||
use MonitorId as RootMonitorId;
|
||||
|
||||
use winapi::shared::minwindef::{UINT, DWORD, BOOL};
|
||||
use winapi::shared::windef::{HWND, HDC, RECT, POINT};
|
||||
use winapi::shared::hidusage;
|
||||
use winapi::um::{winuser, dwmapi, libloaderapi, processthreadsapi};
|
||||
use winapi::um::winnt::{LPCWSTR, LONG, HRESULT};
|
||||
use winapi::um::combaseapi;
|
||||
use winapi::um::objbase::{COINIT_MULTITHREADED};
|
||||
use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl};
|
||||
|
||||
/// The Win32 implementation of the main `Window` object.
|
||||
pub struct Window {
|
||||
/// Main handle for the window.
|
||||
window: WindowWrapper,
|
||||
|
||||
/// The current window state.
|
||||
window_state: Arc<Mutex<events_loop::WindowState>>,
|
||||
|
||||
// The events loop proxy.
|
||||
events_loop_proxy: events_loop::EventsLoopProxy,
|
||||
}
|
||||
|
||||
unsafe impl Send for Window {}
|
||||
unsafe impl Sync for Window {}
|
||||
|
||||
// https://blogs.msdn.microsoft.com/oldnewthing/20131017-00/?p=2903
|
||||
// The idea here is that we use the AdjustWindowRectEx function to calculate how much additional
|
||||
// non-client area gets added due to the styles we passed. To make the math simple,
|
||||
// we ask for a zero client rectangle, so that the resulting window is all non-client.
|
||||
// And then we pass in the empty rectangle represented by the dot in the middle,
|
||||
// and the AdjustWindowRectEx expands the rectangle in all dimensions.
|
||||
// We see that it added ten pixels to the left, right, and bottom,
|
||||
// and it added fifty pixels to the top.
|
||||
// From this we can perform the reverse calculation: Instead of expanding the rectangle, we shrink it.
|
||||
unsafe fn unjust_window_rect(prc: &mut RECT, style: DWORD, ex_style: DWORD) -> BOOL {
|
||||
let mut rc: RECT = mem::zeroed();
|
||||
|
||||
winuser::SetRectEmpty(&mut rc);
|
||||
|
||||
let frc = winuser::AdjustWindowRectEx(&mut rc, style, 0, ex_style);
|
||||
if frc != 0 {
|
||||
prc.left -= rc.left;
|
||||
prc.top -= rc.top;
|
||||
prc.right -= rc.right;
|
||||
prc.bottom -= rc.bottom;
|
||||
}
|
||||
|
||||
frc
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(events_loop: &EventsLoop, w_attr: &WindowAttributes,
|
||||
pl_attr: &PlatformSpecificWindowBuilderAttributes) -> Result<Window, CreationError>
|
||||
{
|
||||
let mut w_attr = Some(w_attr.clone());
|
||||
let mut pl_attr = Some(pl_attr.clone());
|
||||
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let proxy = events_loop.create_proxy();
|
||||
|
||||
events_loop.execute_in_thread(move |inserter| {
|
||||
// We dispatch an `init` function because of code style.
|
||||
let win = unsafe { init(w_attr.take().unwrap(), pl_attr.take().unwrap(), inserter, proxy.clone()) };
|
||||
let _ = tx.send(win);
|
||||
});
|
||||
|
||||
rx.recv().unwrap()
|
||||
}
|
||||
|
||||
pub fn set_title(&self, text: &str) {
|
||||
unsafe {
|
||||
let text = OsStr::new(text).encode_wide().chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
winuser::SetWindowTextW(self.window.0, text.as_ptr() as LPCWSTR);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show(&self) {
|
||||
unsafe {
|
||||
winuser::ShowWindow(self.window.0, winuser::SW_SHOW);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide(&self) {
|
||||
unsafe {
|
||||
winuser::ShowWindow(self.window.0, winuser::SW_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
use std::mem;
|
||||
|
||||
let mut placement: winuser::WINDOWPLACEMENT = unsafe { mem::zeroed() };
|
||||
placement.length = mem::size_of::<winuser::WINDOWPLACEMENT>() as UINT;
|
||||
|
||||
if unsafe { winuser::GetWindowPlacement(self.window.0, &mut placement) } == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
let ref rect = placement.rcNormalPosition;
|
||||
Some((rect.left as i32, rect.top as i32))
|
||||
}
|
||||
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
use std::mem;
|
||||
|
||||
let mut position: POINT = unsafe{ mem::zeroed() };
|
||||
if unsafe{ winuser::ClientToScreen(self.window.0, &mut position) } == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some((position.x, position.y))
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn set_position(&self, x: i32, y: i32) {
|
||||
unsafe {
|
||||
winuser::SetWindowPos(self.window.0, ptr::null_mut(), x as raw::c_int, y as raw::c_int,
|
||||
0, 0, winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOSIZE);
|
||||
winuser::UpdateWindow(self.window.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
let mut rect: RECT = unsafe { mem::uninitialized() };
|
||||
|
||||
if unsafe { winuser::GetClientRect(self.window.0, &mut rect) } == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
Some((
|
||||
(rect.right - rect.left) as u32,
|
||||
(rect.bottom - rect.top) as u32
|
||||
))
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
let mut rect: RECT = unsafe { mem::uninitialized() };
|
||||
|
||||
if unsafe { winuser::GetWindowRect(self.window.0, &mut rect) } == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
Some((
|
||||
(rect.right - rect.left) as u32,
|
||||
(rect.bottom - rect.top) as u32
|
||||
))
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn set_inner_size(&self, x: u32, y: u32) {
|
||||
unsafe {
|
||||
// Calculate the outer size based upon the specified inner size
|
||||
let mut rect = RECT { top: 0, left: 0, bottom: y as LONG, right: x as LONG };
|
||||
let dw_style = winuser::GetWindowLongA(self.window.0, winuser::GWL_STYLE) as DWORD;
|
||||
let b_menu = !winuser::GetMenu(self.window.0).is_null() as BOOL;
|
||||
let dw_style_ex = winuser::GetWindowLongA(self.window.0, 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 raw::c_int;
|
||||
let outer_y = (rect.top - rect.bottom).abs() as raw::c_int;
|
||||
|
||||
winuser::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, outer_x, outer_y,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOREPOSITION | winuser::SWP_NOMOVE);
|
||||
winuser::UpdateWindow(self.window.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
window_state.attributes.min_dimensions = dimensions;
|
||||
|
||||
// Make windows re-check the window size bounds.
|
||||
if let Some(inner_size) = self.get_inner_size() {
|
||||
unsafe {
|
||||
let mut rect = RECT { top: 0, left: 0, bottom: inner_size.1 as LONG, right: inner_size.0 as LONG };
|
||||
let dw_style = winuser::GetWindowLongA(self.window.0, winuser::GWL_STYLE) as DWORD;
|
||||
let b_menu = !winuser::GetMenu(self.window.0).is_null() as BOOL;
|
||||
let dw_style_ex = winuser::GetWindowLongA(self.window.0, 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 raw::c_int;
|
||||
let outer_y = (rect.top - rect.bottom).abs() as raw::c_int;
|
||||
|
||||
winuser::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, outer_x, outer_y,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOREPOSITION | winuser::SWP_NOMOVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
window_state.attributes.max_dimensions = dimensions;
|
||||
|
||||
// Make windows re-check the window size bounds.
|
||||
if let Some(inner_size) = self.get_inner_size() {
|
||||
unsafe {
|
||||
let mut rect = RECT { top: 0, left: 0, bottom: inner_size.1 as LONG, right: inner_size.0 as LONG };
|
||||
let dw_style = winuser::GetWindowLongA(self.window.0, winuser::GWL_STYLE) as DWORD;
|
||||
let b_menu = !winuser::GetMenu(self.window.0).is_null() as BOOL;
|
||||
let dw_style_ex = winuser::GetWindowLongA(self.window.0, 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 raw::c_int;
|
||||
let outer_y = (rect.top - rect.bottom).abs() as raw::c_int;
|
||||
|
||||
winuser::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, outer_x, outer_y,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOREPOSITION | winuser::SWP_NOMOVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove
|
||||
pub fn platform_display(&self) -> *mut ::libc::c_void {
|
||||
panic!() // Deprecated function ; we don't care anymore
|
||||
}
|
||||
// TODO: remove
|
||||
pub fn platform_window(&self) -> *mut ::libc::c_void {
|
||||
self.window.0 as *mut ::libc::c_void
|
||||
}
|
||||
|
||||
/// Returns the `hwnd` of this window.
|
||||
#[inline]
|
||||
pub fn hwnd(&self) -> HWND {
|
||||
self.window.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||
let cursor_id = match cursor {
|
||||
MouseCursor::Arrow | MouseCursor::Default => winuser::IDC_ARROW,
|
||||
MouseCursor::Hand => winuser::IDC_HAND,
|
||||
MouseCursor::Crosshair => winuser::IDC_CROSS,
|
||||
MouseCursor::Text | MouseCursor::VerticalText => winuser::IDC_IBEAM,
|
||||
MouseCursor::NotAllowed | MouseCursor::NoDrop => winuser::IDC_NO,
|
||||
MouseCursor::Grab | MouseCursor::Grabbing |
|
||||
MouseCursor::Move | MouseCursor::AllScroll => winuser::IDC_SIZEALL,
|
||||
MouseCursor::EResize | MouseCursor::WResize |
|
||||
MouseCursor::EwResize | MouseCursor::ColResize => winuser::IDC_SIZEWE,
|
||||
MouseCursor::NResize | MouseCursor::SResize |
|
||||
MouseCursor::NsResize | MouseCursor::RowResize => winuser::IDC_SIZENS,
|
||||
MouseCursor::NeResize | MouseCursor::SwResize |
|
||||
MouseCursor::NeswResize => winuser::IDC_SIZENESW,
|
||||
MouseCursor::NwResize | MouseCursor::SeResize |
|
||||
MouseCursor::NwseResize => winuser::IDC_SIZENWSE,
|
||||
MouseCursor::Wait => winuser::IDC_WAIT,
|
||||
MouseCursor::Progress => winuser::IDC_APPSTARTING,
|
||||
MouseCursor::Help => winuser::IDC_HELP,
|
||||
_ => winuser::IDC_ARROW, // use arrow for the missing cases.
|
||||
};
|
||||
|
||||
let mut cur = self.window_state.lock().unwrap();
|
||||
cur.cursor = cursor_id;
|
||||
}
|
||||
|
||||
// TODO: it should be possible to rework this function by using the `execute_in_thread` method
|
||||
// of the events loop.
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
let mut current_state = self.window_state.lock().unwrap();
|
||||
|
||||
let foreground_thread_id = unsafe { winuser::GetWindowThreadProcessId(self.window.0, ptr::null_mut()) };
|
||||
let current_thread_id = unsafe { processthreadsapi::GetCurrentThreadId() };
|
||||
|
||||
unsafe { winuser::AttachThreadInput(foreground_thread_id, current_thread_id, 1) };
|
||||
|
||||
let res = match (state, current_state.cursor_state) {
|
||||
(CursorState::Normal, CursorState::Normal) => Ok(()),
|
||||
(CursorState::Hide, CursorState::Hide) => Ok(()),
|
||||
(CursorState::Grab, CursorState::Grab) => Ok(()),
|
||||
|
||||
(CursorState::Hide, CursorState::Normal) => {
|
||||
current_state.cursor_state = CursorState::Hide;
|
||||
Ok(())
|
||||
},
|
||||
|
||||
(CursorState::Normal, CursorState::Hide) => {
|
||||
current_state.cursor_state = CursorState::Normal;
|
||||
Ok(())
|
||||
},
|
||||
|
||||
(CursorState::Grab, CursorState::Normal) | (CursorState::Grab, CursorState::Hide) => {
|
||||
unsafe {
|
||||
let mut rect = mem::uninitialized();
|
||||
if winuser::GetClientRect(self.window.0, &mut rect) == 0 {
|
||||
return Err(format!("GetWindowRect failed"));
|
||||
}
|
||||
winuser::ClientToScreen(self.window.0, mem::transmute(&mut rect.left));
|
||||
winuser::ClientToScreen(self.window.0, mem::transmute(&mut rect.right));
|
||||
if winuser::ClipCursor(&rect) == 0 {
|
||||
return Err(format!("ClipCursor failed"));
|
||||
}
|
||||
current_state.cursor_state = CursorState::Grab;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
|
||||
(CursorState::Normal, CursorState::Grab) => {
|
||||
unsafe {
|
||||
if winuser::ClipCursor(ptr::null()) == 0 {
|
||||
return Err(format!("ClipCursor failed"));
|
||||
}
|
||||
current_state.cursor_state = CursorState::Normal;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
unsafe { winuser::AttachThreadInput(foreground_thread_id, current_thread_id, 0) };
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
1.0
|
||||
}
|
||||
|
||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||
let mut point = POINT {
|
||||
x: x,
|
||||
y: y,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
if winuser::ClientToScreen(self.window.0, &mut point) == 0 {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
if winuser::SetCursorPos(point.x, point.y) == 0 {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId(self.window.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
|
||||
window_state.attributes.maximized = maximized;
|
||||
// we only maximized if we are not in fullscreen
|
||||
if window_state.attributes.fullscreen.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let window = self.window.clone();
|
||||
unsafe {
|
||||
// And because ShowWindow will resize the window
|
||||
// We call it in the main thread
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
winuser::ShowWindow(
|
||||
window.0,
|
||||
if maximized {
|
||||
winuser::SW_MAXIMIZE
|
||||
} else {
|
||||
winuser::SW_RESTORE
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn set_fullscreen_style(&self) -> (LONG, LONG) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
|
||||
if window_state.attributes.fullscreen.is_none() || window_state.saved_window_info.is_none() {
|
||||
let mut rect: RECT = mem::zeroed();
|
||||
|
||||
winuser::GetWindowRect(self.window.0, &mut rect);
|
||||
|
||||
window_state.saved_window_info = Some(events_loop::SavedWindowInfo {
|
||||
style: winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE),
|
||||
ex_style: winuser::GetWindowLongW(self.window.0, winuser::GWL_EXSTYLE),
|
||||
rect,
|
||||
});
|
||||
}
|
||||
|
||||
// We sync the system maximized state here, it will be used when restoring
|
||||
let mut placement: winuser::WINDOWPLACEMENT = mem::zeroed();
|
||||
placement.length = mem::size_of::<winuser::WINDOWPLACEMENT>() as u32;
|
||||
winuser::GetWindowPlacement(self.window.0, &mut placement);
|
||||
window_state.attributes.maximized =
|
||||
placement.showCmd == (winuser::SW_SHOWMAXIMIZED as u32);
|
||||
let saved_window_info = window_state.saved_window_info.as_ref().unwrap();
|
||||
|
||||
(saved_window_info.style, saved_window_info.ex_style)
|
||||
}
|
||||
|
||||
unsafe fn restore_saved_window(&self) {
|
||||
let window_state = self.window_state.lock().unwrap();
|
||||
// Reset original window style and size. The multiple window size/moves
|
||||
// here are ugly, but if SetWindowPos() doesn't redraw, the taskbar won't be
|
||||
// repainted. Better-looking methods welcome.
|
||||
let saved_window_info = window_state.saved_window_info.as_ref().unwrap();
|
||||
|
||||
let rect = saved_window_info.rect.clone();
|
||||
let window = self.window.clone();
|
||||
let (style, ex_style) = (saved_window_info.style, saved_window_info.ex_style);
|
||||
|
||||
let maximized = window_state.attributes.maximized;
|
||||
|
||||
// On restore, resize to the previous saved rect size.
|
||||
// And because SetWindowPos will resize the window
|
||||
// We call it in the main thread
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
winuser::SetWindowLongW(window.0, winuser::GWL_STYLE, style);
|
||||
winuser::SetWindowLongW(window.0, winuser::GWL_EXSTYLE, ex_style);
|
||||
|
||||
winuser::SetWindowPos(
|
||||
window.0,
|
||||
ptr::null_mut(),
|
||||
rect.left,
|
||||
rect.top,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOACTIVATE
|
||||
| winuser::SWP_FRAMECHANGED,
|
||||
);
|
||||
|
||||
// if it was set to maximized when it were fullscreened, we restore it as well
|
||||
winuser::ShowWindow(
|
||||
window.0,
|
||||
if maximized {
|
||||
winuser::SW_MAXIMIZE
|
||||
} else {
|
||||
winuser::SW_RESTORE
|
||||
},
|
||||
);
|
||||
|
||||
mark_fullscreen(window.0, false);
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
|
||||
unsafe {
|
||||
match &monitor {
|
||||
&Some(RootMonitorId { ref inner }) => {
|
||||
let pos = inner.get_position();
|
||||
let dim = inner.get_dimensions();
|
||||
let window = self.window.clone();
|
||||
|
||||
let (style, ex_style) = self.set_fullscreen_style();
|
||||
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
winuser::SetWindowLongW(
|
||||
window.0,
|
||||
winuser::GWL_STYLE,
|
||||
((style as DWORD) & !(winuser::WS_CAPTION | winuser::WS_THICKFRAME))
|
||||
as LONG,
|
||||
);
|
||||
|
||||
winuser::SetWindowLongW(
|
||||
window.0,
|
||||
winuser::GWL_EXSTYLE,
|
||||
((ex_style as DWORD)
|
||||
& !(winuser::WS_EX_DLGMODALFRAME | winuser::WS_EX_WINDOWEDGE
|
||||
| winuser::WS_EX_CLIENTEDGE
|
||||
| winuser::WS_EX_STATICEDGE))
|
||||
as LONG,
|
||||
);
|
||||
|
||||
winuser::SetWindowPos(
|
||||
window.0,
|
||||
ptr::null_mut(),
|
||||
pos.0,
|
||||
pos.1,
|
||||
dim.0 as i32,
|
||||
dim.1 as i32,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER
|
||||
| winuser::SWP_NOACTIVATE
|
||||
| winuser::SWP_FRAMECHANGED,
|
||||
);
|
||||
|
||||
mark_fullscreen(window.0, true);
|
||||
});
|
||||
}
|
||||
&None => {
|
||||
self.restore_saved_window();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
window_state.attributes.fullscreen = monitor;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, decorations: bool) {
|
||||
if let Ok(mut window_state) = self.window_state.lock() {
|
||||
if window_state.attributes.decorations == decorations {
|
||||
return;
|
||||
}
|
||||
|
||||
let style_flags = (winuser::WS_CAPTION | winuser::WS_THICKFRAME) as LONG;
|
||||
let ex_style_flags = (winuser::WS_EX_WINDOWEDGE) as LONG;
|
||||
|
||||
// if we are in fullscreen mode, we only change the saved window info
|
||||
if window_state.attributes.fullscreen.is_some() {
|
||||
{
|
||||
let mut saved = window_state.saved_window_info.as_mut().unwrap();
|
||||
|
||||
unsafe {
|
||||
unjust_window_rect(&mut saved.rect, saved.style as _, saved.ex_style as _);
|
||||
}
|
||||
|
||||
if decorations {
|
||||
saved.style = saved.style | style_flags;
|
||||
saved.ex_style = saved.ex_style | ex_style_flags;
|
||||
} else {
|
||||
saved.style = saved.style & !style_flags;
|
||||
saved.ex_style = saved.ex_style & !ex_style_flags;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
winuser::AdjustWindowRectEx(
|
||||
&mut saved.rect,
|
||||
saved.style as _,
|
||||
0,
|
||||
saved.ex_style as _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
window_state.attributes.decorations = decorations;
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let mut rect: RECT = mem::zeroed();
|
||||
winuser::GetWindowRect(self.window.0, &mut rect);
|
||||
|
||||
let mut style = winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE);
|
||||
let mut ex_style = winuser::GetWindowLongW(self.window.0, winuser::GWL_EXSTYLE);
|
||||
unjust_window_rect(&mut rect, style as _, ex_style as _);
|
||||
|
||||
if decorations {
|
||||
style = style | style_flags;
|
||||
ex_style = ex_style | ex_style_flags;
|
||||
} else {
|
||||
style = style & !style_flags;
|
||||
ex_style = ex_style & !ex_style_flags;
|
||||
}
|
||||
|
||||
let window = self.window.clone();
|
||||
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
winuser::SetWindowLongW(window.0, winuser::GWL_STYLE, style);
|
||||
winuser::SetWindowLongW(window.0, winuser::GWL_EXSTYLE, ex_style);
|
||||
winuser::AdjustWindowRectEx(&mut rect, style as _, 0, ex_style as _);
|
||||
|
||||
winuser::SetWindowPos(
|
||||
window.0,
|
||||
ptr::null_mut(),
|
||||
rect.left,
|
||||
rect.top,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER
|
||||
| winuser::SWP_NOACTIVATE
|
||||
| winuser::SWP_FRAMECHANGED,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
window_state.attributes.decorations = decorations;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
RootMonitorId {
|
||||
inner: EventsLoop::get_current_monitor(self.window.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// The window must be destroyed from the same thread that created it, so we send a
|
||||
// custom message to be handled by our callback to do the actual work.
|
||||
winuser::PostMessageW(self.window.0, *DESTROY_MSG_ID, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple non-owning wrapper around a window.
|
||||
#[doc(hidden)]
|
||||
#[derive(Clone)]
|
||||
pub struct WindowWrapper(HWND, HDC);
|
||||
|
||||
// Send is not implemented for HWND and HDC, 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
|
||||
unsafe impl Send for WindowWrapper {}
|
||||
|
||||
pub unsafe fn adjust_size(
|
||||
(x, y): (u32, u32), style: DWORD, ex_style: DWORD,
|
||||
) -> (LONG, LONG) {
|
||||
let mut rect = RECT { left: 0, right: x as LONG, top: 0, bottom: y as LONG };
|
||||
winuser::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
|
||||
(rect.right - rect.left, rect.bottom - rect.top)
|
||||
}
|
||||
|
||||
unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||
inserter: events_loop::Inserter, events_loop_proxy: events_loop::EventsLoopProxy) -> Result<Window, CreationError> {
|
||||
let title = OsStr::new(&window.title).encode_wide().chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// registering the window class
|
||||
let class_name = register_window_class();
|
||||
|
||||
// building a RECT object with coordinates
|
||||
let mut rect = RECT {
|
||||
left: 0, right: window.dimensions.unwrap_or((1024, 768)).0 as LONG,
|
||||
top: 0, bottom: window.dimensions.unwrap_or((1024, 768)).1 as LONG,
|
||||
};
|
||||
|
||||
// computing the style and extended style of the window
|
||||
let (ex_style, style) = if !window.decorations {
|
||||
(winuser::WS_EX_APPWINDOW,
|
||||
//winapi::WS_POPUP is incompatible with winapi::WS_CHILD
|
||||
if pl_attribs.parent.is_some() {
|
||||
winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN
|
||||
}
|
||||
else {
|
||||
winuser::WS_POPUP | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN
|
||||
}
|
||||
)
|
||||
} else {
|
||||
(winuser::WS_EX_APPWINDOW | winuser::WS_EX_WINDOWEDGE,
|
||||
winuser::WS_OVERLAPPEDWINDOW | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN)
|
||||
};
|
||||
|
||||
// adjusting the window coordinates using the style
|
||||
winuser::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
|
||||
|
||||
// creating the real window this time, by using the functions in `extra_functions`
|
||||
let real_window = {
|
||||
let (width, height) = if window.dimensions.is_some() {
|
||||
let min_dimensions = window.min_dimensions
|
||||
.map(|d| adjust_size(d, style, ex_style))
|
||||
.unwrap_or((0, 0));
|
||||
let max_dimensions = window.max_dimensions
|
||||
.map(|d| adjust_size(d, style, ex_style))
|
||||
.unwrap_or((raw::c_int::max_value(), raw::c_int::max_value()));
|
||||
|
||||
(
|
||||
Some((rect.right - rect.left).min(max_dimensions.0).max(min_dimensions.0)),
|
||||
Some((rect.bottom - rect.top).min(max_dimensions.1).max(min_dimensions.1))
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let mut style = if !window.visible {
|
||||
style
|
||||
} else {
|
||||
style | winuser::WS_VISIBLE
|
||||
};
|
||||
|
||||
if pl_attribs.parent.is_some() {
|
||||
style |= winuser::WS_CHILD;
|
||||
}
|
||||
|
||||
let handle = winuser::CreateWindowExW(ex_style | winuser::WS_EX_ACCEPTFILES,
|
||||
class_name.as_ptr(),
|
||||
title.as_ptr() as LPCWSTR,
|
||||
style | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN,
|
||||
winuser::CW_USEDEFAULT, winuser::CW_USEDEFAULT,
|
||||
width.unwrap_or(winuser::CW_USEDEFAULT), height.unwrap_or(winuser::CW_USEDEFAULT),
|
||||
pl_attribs.parent.unwrap_or(ptr::null_mut()),
|
||||
ptr::null_mut(), libloaderapi::GetModuleHandleW(ptr::null()),
|
||||
ptr::null_mut());
|
||||
|
||||
if handle.is_null() {
|
||||
return Err(CreationError::OsError(format!("CreateWindowEx function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
let hdc = winuser::GetDC(handle);
|
||||
if hdc.is_null() {
|
||||
return Err(CreationError::OsError(format!("GetDC function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
WindowWrapper(handle, hdc)
|
||||
};
|
||||
|
||||
// Set up raw mouse input
|
||||
{
|
||||
let mut rid: winuser::RAWINPUTDEVICE = mem::uninitialized();
|
||||
rid.usUsagePage = hidusage::HID_USAGE_PAGE_GENERIC;
|
||||
rid.usUsage = hidusage::HID_USAGE_GENERIC_MOUSE;
|
||||
rid.dwFlags = 0;
|
||||
rid.hwndTarget = real_window.0;
|
||||
|
||||
winuser::RegisterRawInputDevices(&rid, 1, mem::size_of::<winuser::RAWINPUTDEVICE>() as u32);
|
||||
}
|
||||
|
||||
// Register for touch events if applicable
|
||||
{
|
||||
let digitizer = winuser::GetSystemMetrics( winuser::SM_DIGITIZER ) as u32;
|
||||
if digitizer & winuser::NID_READY != 0 {
|
||||
winuser::RegisterTouchWindow( real_window.0, winuser::TWF_WANTPALM );
|
||||
}
|
||||
}
|
||||
|
||||
// Creating a mutex to track the current window state
|
||||
let window_state = Arc::new(Mutex::new(events_loop::WindowState {
|
||||
cursor: winuser::IDC_ARROW, // use arrow by default
|
||||
cursor_state: CursorState::Normal,
|
||||
attributes: window.clone(),
|
||||
mouse_in_window: false,
|
||||
saved_window_info: None,
|
||||
}));
|
||||
|
||||
// making the window transparent
|
||||
if window.transparent {
|
||||
let bb = dwmapi::DWM_BLURBEHIND {
|
||||
dwFlags: 0x1, // FIXME: DWM_BB_ENABLE;
|
||||
fEnable: 1,
|
||||
hRgnBlur: ptr::null_mut(),
|
||||
fTransitionOnMaximized: 0,
|
||||
};
|
||||
|
||||
dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb);
|
||||
}
|
||||
|
||||
let win = Window {
|
||||
window: real_window,
|
||||
window_state: window_state,
|
||||
events_loop_proxy
|
||||
};
|
||||
|
||||
win.set_maximized(window.maximized);
|
||||
if let Some(_) = window.fullscreen {
|
||||
win.set_fullscreen(window.fullscreen);
|
||||
force_window_active(win.window.0);
|
||||
}
|
||||
|
||||
inserter.insert(win.window.0, win.window_state.clone());
|
||||
|
||||
Ok(win)
|
||||
}
|
||||
|
||||
unsafe fn register_window_class() -> Vec<u16> {
|
||||
let class_name = OsStr::new("Window Class").encode_wide().chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let class = winuser::WNDCLASSEXW {
|
||||
cbSize: mem::size_of::<winuser::WNDCLASSEXW>() as UINT,
|
||||
style: winuser::CS_HREDRAW | winuser::CS_VREDRAW | winuser::CS_OWNDC,
|
||||
lpfnWndProc: Some(events_loop::callback),
|
||||
cbClsExtra: 0,
|
||||
cbWndExtra: 0,
|
||||
hInstance: libloaderapi::GetModuleHandleW(ptr::null()),
|
||||
hIcon: ptr::null_mut(),
|
||||
hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly
|
||||
hbrBackground: ptr::null_mut(),
|
||||
lpszMenuName: ptr::null(),
|
||||
lpszClassName: class_name.as_ptr(),
|
||||
hIconSm: ptr::null_mut(),
|
||||
};
|
||||
|
||||
// We ignore errors because registering the same window class twice would trigger
|
||||
// an error, and because errors here are detected during CreateWindowEx anyway.
|
||||
// Also since there is no weird element in the struct, there is no reason for this
|
||||
// call to fail.
|
||||
winuser::RegisterClassExW(&class);
|
||||
|
||||
class_name
|
||||
}
|
||||
|
||||
|
||||
struct ComInitialized(*mut ());
|
||||
impl Drop for ComInitialized {
|
||||
fn drop(&mut self) {
|
||||
unsafe { combaseapi::CoUninitialize() };
|
||||
}
|
||||
}
|
||||
|
||||
thread_local!{
|
||||
static COM_INITIALIZED: ComInitialized = {
|
||||
unsafe {
|
||||
combaseapi::CoInitializeEx(ptr::null_mut(), COINIT_MULTITHREADED);
|
||||
ComInitialized(ptr::null_mut())
|
||||
}
|
||||
};
|
||||
|
||||
static TASKBAR_LIST: Cell<*mut taskbar::ITaskbarList2> = Cell::new(ptr::null_mut());
|
||||
}
|
||||
|
||||
pub fn com_initialized() {
|
||||
COM_INITIALIZED.with(|_| {});
|
||||
}
|
||||
|
||||
// TODO: remove these when they get added to winapi
|
||||
// https://github.com/retep998/winapi-rs/pull/592
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(dead_code)]
|
||||
mod taskbar {
|
||||
use super::{IUnknown,IUnknownVtbl,HRESULT, HWND,BOOL};
|
||||
DEFINE_GUID!{CLSID_TaskbarList,
|
||||
0x56fdf344, 0xfd6d, 0x11d0, 0x95, 0x8a, 0x00, 0x60, 0x97, 0xc9, 0xa0, 0x90}
|
||||
|
||||
RIDL!(#[uuid(0x56fdf342, 0xfd6d, 0x11d0, 0x95, 0x8a, 0x00, 0x60, 0x97, 0xc9, 0xa0, 0x90)]
|
||||
interface ITaskbarList(ITaskbarListVtbl): IUnknown(IUnknownVtbl) {
|
||||
fn HrInit() -> HRESULT,
|
||||
fn AddTab(
|
||||
hwnd: HWND,
|
||||
) -> HRESULT,
|
||||
fn DeleteTab(
|
||||
hwnd: HWND,
|
||||
) -> HRESULT,
|
||||
fn ActivateTab(
|
||||
hwnd: HWND,
|
||||
) -> HRESULT,
|
||||
fn SetActiveAlt(
|
||||
hwnd: HWND,
|
||||
) -> HRESULT,
|
||||
});
|
||||
|
||||
RIDL!(#[uuid(0x602d4995, 0xb13a, 0x429b, 0xa6, 0x6e, 0x19, 0x35, 0xe4, 0x4f, 0x43, 0x17)]
|
||||
interface ITaskbarList2(ITaskbarList2Vtbl): ITaskbarList(ITaskbarListVtbl) {
|
||||
fn MarkFullscreenWindow(
|
||||
hwnd: HWND,
|
||||
fFullscreen: BOOL,
|
||||
) -> HRESULT,
|
||||
});
|
||||
}
|
||||
|
||||
// Reference Implementation:
|
||||
// https://github.com/chromium/chromium/blob/f18e79d901f56154f80eea1e2218544285e62623/ui/views/win/fullscreen_handler.cc
|
||||
//
|
||||
// As per MSDN marking the window as fullscreen should ensure that the
|
||||
// taskbar is moved to the bottom of the Z-order when the fullscreen window
|
||||
// is activated. If the window is not fullscreen, the Shell falls back to
|
||||
// heuristics to determine how the window should be treated, which means
|
||||
// that it could still consider the window as fullscreen. :(
|
||||
unsafe fn mark_fullscreen(handle: HWND, fullscreen: bool) {
|
||||
com_initialized();
|
||||
|
||||
TASKBAR_LIST.with(|task_bar_list_ptr| {
|
||||
let mut task_bar_list = task_bar_list_ptr.get();
|
||||
|
||||
if task_bar_list == ptr::null_mut() {
|
||||
use winapi::shared::winerror::S_OK;
|
||||
use winapi::Interface;
|
||||
|
||||
let hr = combaseapi::CoCreateInstance(
|
||||
&taskbar::CLSID_TaskbarList,
|
||||
ptr::null_mut(),
|
||||
combaseapi::CLSCTX_ALL,
|
||||
&taskbar::ITaskbarList2::uuidof(),
|
||||
&mut task_bar_list as *mut _ as *mut _,
|
||||
);
|
||||
|
||||
if hr != S_OK || (*task_bar_list).HrInit() != S_OK {
|
||||
// In some old windows, the taskbar object could not be created, we just ignore it
|
||||
return;
|
||||
}
|
||||
task_bar_list_ptr.set(task_bar_list)
|
||||
}
|
||||
|
||||
task_bar_list = task_bar_list_ptr.get();
|
||||
(*task_bar_list).MarkFullscreenWindow(handle, if fullscreen { 1 } else { 0 });
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn force_window_active(handle: HWND) {
|
||||
// In some situation, calling SetForegroundWindow could not bring up the window,
|
||||
// This is a little hack which can "steal" the foreground window permission
|
||||
// We only call this function in the window creation, so it should be fine.
|
||||
// See : https://stackoverflow.com/questions/10740346/setforegroundwindow-only-working-while-visual-studio-is-open
|
||||
let alt_sc = winuser::MapVirtualKeyW(winuser::VK_MENU as _, winuser::MAPVK_VK_TO_VSC);
|
||||
|
||||
let mut inputs: [winuser::INPUT; 2] = mem::zeroed();
|
||||
inputs[0].type_ = winuser::INPUT_KEYBOARD;
|
||||
inputs[0].u.ki_mut().wVk = winuser::VK_LMENU as _;
|
||||
inputs[0].u.ki_mut().wScan = alt_sc as _;
|
||||
inputs[0].u.ki_mut().dwFlags = winuser::KEYEVENTF_EXTENDEDKEY;
|
||||
|
||||
inputs[1].type_ = winuser::INPUT_KEYBOARD;
|
||||
inputs[1].u.ki_mut().wVk = winuser::VK_LMENU as _;
|
||||
inputs[1].u.ki_mut().wScan = alt_sc as _;
|
||||
inputs[1].u.ki_mut().dwFlags = winuser::KEYEVENTF_EXTENDEDKEY | winuser::KEYEVENTF_KEYUP;
|
||||
|
||||
// Simulate a key press and release
|
||||
winuser::SendInput(
|
||||
inputs.len() as _,
|
||||
inputs.as_mut_ptr(),
|
||||
mem::size_of::<winuser::INPUT>() as _,
|
||||
);
|
||||
|
||||
winuser::SetForegroundWindow(handle);
|
||||
}
|
||||
175
src/window.rs
175
src/window.rs
@@ -7,7 +7,6 @@ use MouseCursor;
|
||||
use Window;
|
||||
use WindowBuilder;
|
||||
use WindowId;
|
||||
use native_monitor::NativeMonitorId;
|
||||
|
||||
use libc;
|
||||
use platform;
|
||||
@@ -30,7 +29,7 @@ impl WindowBuilder {
|
||||
self.window.dimensions = Some((width, height));
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
/// Sets a minimum dimension size for the window
|
||||
///
|
||||
/// Width and height are in pixels.
|
||||
@@ -56,13 +55,18 @@ impl WindowBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Requests fullscreen mode.
|
||||
///
|
||||
/// If you don't specify dimensions for the window, it will match the monitor's.
|
||||
/// Sets the window fullscreen state. None means a normal window, Some(MonitorId)
|
||||
/// means a fullscreen window on that specific monitor
|
||||
#[inline]
|
||||
pub fn with_fullscreen(mut self, monitor: MonitorId) -> WindowBuilder {
|
||||
let MonitorId(monitor) = monitor;
|
||||
self.window.monitor = Some(monitor);
|
||||
pub fn with_fullscreen(mut self, monitor: Option<MonitorId>) -> WindowBuilder {
|
||||
self.window.fullscreen = monitor;
|
||||
self
|
||||
}
|
||||
|
||||
/// Requests maximized mode.
|
||||
#[inline]
|
||||
pub fn with_maximized(mut self, maximized: bool) -> WindowBuilder {
|
||||
self.window.maximized = maximized;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -87,7 +91,7 @@ impl WindowBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables multitouch
|
||||
/// Enables multitouch.
|
||||
#[inline]
|
||||
pub fn with_multitouch(mut self) -> WindowBuilder {
|
||||
self.window.multitouch = true;
|
||||
@@ -100,8 +104,10 @@ impl WindowBuilder {
|
||||
/// out of memory, etc.
|
||||
pub fn build(mut self, events_loop: &EventsLoop) -> Result<Window, CreationError> {
|
||||
// resizing the window to the dimensions of the monitor when fullscreen
|
||||
if self.window.dimensions.is_none() && self.window.monitor.is_some() {
|
||||
self.window.dimensions = Some(self.window.monitor.as_ref().unwrap().get_dimensions())
|
||||
if self.window.dimensions.is_none() {
|
||||
if let Some(ref monitor) = self.window.fullscreen {
|
||||
self.window.dimensions = Some(monitor.get_dimensions());
|
||||
}
|
||||
}
|
||||
|
||||
// default dimensions
|
||||
@@ -110,14 +116,14 @@ impl WindowBuilder {
|
||||
}
|
||||
|
||||
// building
|
||||
let w = try!(platform::Window2::new(events_loop.events_loop.clone(), &self.window, &self.platform_specific));
|
||||
let w = try!(platform::Window::new(&events_loop.events_loop, &self.window, &self.platform_specific));
|
||||
|
||||
Ok(Window { window: w })
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
/// Creates a new OpenGL context, and a Window for platforms where this is appropriate.
|
||||
/// Creates a new Window for platforms where this is appropriate.
|
||||
///
|
||||
/// This function is equivalent to `WindowBuilder::new().build(events_loop)`.
|
||||
///
|
||||
@@ -175,9 +181,18 @@ impl Window {
|
||||
self.window.get_position()
|
||||
}
|
||||
|
||||
/// Returns the position of the top-left hand corner of the window's client area relative to the
|
||||
/// top-left hand corner of the desktop.
|
||||
///
|
||||
/// The same conditions that apply to `get_position` apply to this method.
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
self.window.get_inner_position()
|
||||
}
|
||||
|
||||
/// Modifies the position of the window.
|
||||
///
|
||||
/// See `get_position` for more informations about the coordinates.
|
||||
/// See `get_position` for more information about the coordinates.
|
||||
///
|
||||
/// This is a no-op if the window has already been closed.
|
||||
#[inline]
|
||||
@@ -185,6 +200,17 @@ impl Window {
|
||||
self.window.set_position(x, y)
|
||||
}
|
||||
|
||||
/// 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 that need to be supplied to `glViewport`.
|
||||
///
|
||||
/// Returns `None` if the window no longer exists.
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
self.window.get_inner_size()
|
||||
}
|
||||
|
||||
/// Returns the size in points of the client area of the window.
|
||||
///
|
||||
/// The client area is the content of the window, excluding the title bar and borders.
|
||||
@@ -194,22 +220,14 @@ impl Window {
|
||||
///
|
||||
/// DEPRECATED
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
self.window.get_inner_size()
|
||||
}
|
||||
|
||||
/// Returns the size in points of the client area of the 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.
|
||||
#[inline]
|
||||
#[deprecated]
|
||||
pub fn get_inner_size_points(&self) -> Option<(u32, u32)> {
|
||||
self.window.get_inner_size()
|
||||
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.
|
||||
@@ -217,12 +235,12 @@ impl Window {
|
||||
/// 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().map(|(x, y)| {
|
||||
let hidpi = self.hidpi_factor();
|
||||
((x as f32 * hidpi) as u32, (y as f32 * hidpi) as u32)
|
||||
})
|
||||
self.window.get_inner_size()
|
||||
}
|
||||
|
||||
/// Returns the size in pixels of the window.
|
||||
@@ -238,7 +256,7 @@ impl Window {
|
||||
|
||||
/// Modifies the inner size of the window.
|
||||
///
|
||||
/// See `get_inner_size` for more informations about the values.
|
||||
/// See `get_inner_size` for more information about the values.
|
||||
///
|
||||
/// This is a no-op if the window has already been closed.
|
||||
#[inline]
|
||||
@@ -246,6 +264,22 @@ impl Window {
|
||||
self.window.set_inner_size(x, y)
|
||||
}
|
||||
|
||||
/// 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)>) {
|
||||
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)>) {
|
||||
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.
|
||||
@@ -284,7 +318,7 @@ impl Window {
|
||||
self.window.set_cursor_position(x, y)
|
||||
}
|
||||
|
||||
/// Sets how glutin handles the cursor. See the documentation of `CursorState` for details.
|
||||
/// Sets how winit handles the cursor. See the documentation of `CursorState` for details.
|
||||
///
|
||||
/// Has no effect on Android.
|
||||
#[inline]
|
||||
@@ -292,6 +326,29 @@ impl Window {
|
||||
self.window.set_cursor_state(state)
|
||||
}
|
||||
|
||||
/// Sets the window to maximized or back
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
self.window.set_maximized(maximized)
|
||||
}
|
||||
|
||||
/// Sets the window to fullscreen or back
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, monitor: Option<MonitorId>) {
|
||||
self.window.set_fullscreen(monitor)
|
||||
}
|
||||
|
||||
/// Turn window decorations on or off.
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, decorations: bool) {
|
||||
self.window.set_decorations(decorations)
|
||||
}
|
||||
|
||||
/// Returns the monitor on which the window currently resides
|
||||
pub fn get_current_monitor(&self) -> MonitorId {
|
||||
self.window.get_current_monitor()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId(self.window.id())
|
||||
@@ -299,10 +356,10 @@ impl Window {
|
||||
}
|
||||
|
||||
/// An iterator for the list of available monitors.
|
||||
// Implementation note: we retreive the list once, then serve each element by one by one.
|
||||
// Implementation note: we retrieve the list once, then serve each element by one by one.
|
||||
// This may change in the future.
|
||||
pub struct AvailableMonitorsIter {
|
||||
data: VecDequeIter<platform::MonitorId>,
|
||||
pub(crate) data: VecDequeIter<platform::MonitorId>,
|
||||
}
|
||||
|
||||
impl Iterator for AvailableMonitorsIter {
|
||||
@@ -310,7 +367,7 @@ impl Iterator for AvailableMonitorsIter {
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<MonitorId> {
|
||||
self.data.next().map(|id| MonitorId(id))
|
||||
self.data.next().map(|id| MonitorId { inner: id })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -319,41 +376,37 @@ impl Iterator for AvailableMonitorsIter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the list of all available monitors.
|
||||
#[inline]
|
||||
pub fn get_available_monitors() -> AvailableMonitorsIter {
|
||||
let data = platform::get_available_monitors();
|
||||
AvailableMonitorsIter{ data: data.into_iter() }
|
||||
}
|
||||
|
||||
/// Returns the primary monitor of the system.
|
||||
#[inline]
|
||||
pub fn get_primary_monitor() -> MonitorId {
|
||||
MonitorId(platform::get_primary_monitor())
|
||||
}
|
||||
|
||||
/// Identifier for a monitor.
|
||||
pub struct MonitorId(platform::MonitorId);
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorId {
|
||||
pub(crate) inner: platform::MonitorId
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
/// Returns a human-readable name of the monitor.
|
||||
///
|
||||
/// Returns `None` if the monitor doesn't exist anymore.
|
||||
#[inline]
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
let &MonitorId(ref id) = self;
|
||||
id.get_name()
|
||||
}
|
||||
|
||||
/// Returns the native platform identifier for this monitor.
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> NativeMonitorId {
|
||||
let &MonitorId(ref id) = self;
|
||||
id.get_native_identifier()
|
||||
self.inner.get_name()
|
||||
}
|
||||
|
||||
/// Returns the number of pixels currently displayed on the monitor.
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
let &MonitorId(ref id) = self;
|
||||
id.get_dimensions()
|
||||
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) {
|
||||
self.inner.get_position()
|
||||
}
|
||||
|
||||
/// Returns the ratio between the monitor's physical pixels and logical pixels.
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
self.inner.get_hidpi_factor()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
extern crate winit;
|
||||
|
||||
// A part of the API requirement for `EventsLoop` is that it is `Send` + `Sync`.
|
||||
//
|
||||
// This short test will only compile if the `EventsLoop` is `Send` + `Sync`.
|
||||
#[test]
|
||||
fn send_sync() {
|
||||
fn check_send_sync<T: Send + Sync>() {}
|
||||
check_send_sync::<winit::EventsLoop>();
|
||||
}
|
||||
23
tests/send_objects.rs
Normal file
23
tests/send_objects.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
extern crate winit;
|
||||
|
||||
fn needs_send<T:Send>() {}
|
||||
|
||||
#[test]
|
||||
fn events_loop_proxy_send() {
|
||||
// ensures that `winit::EventsLoopProxy` implements `Send`
|
||||
needs_send::<winit::EventsLoopProxy>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_send() {
|
||||
// ensures that `winit::Window` implements `Send`
|
||||
needs_send::<winit::Window>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ids_send() {
|
||||
// ensures that the various `..Id` types implement `Send`
|
||||
needs_send::<winit::WindowId>();
|
||||
needs_send::<winit::DeviceId>();
|
||||
needs_send::<winit::MonitorId>();
|
||||
}
|
||||
9
tests/sync_object.rs
Normal file
9
tests/sync_object.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
extern crate winit;
|
||||
|
||||
fn needs_sync<T:Sync>() {}
|
||||
|
||||
#[test]
|
||||
fn window_sync() {
|
||||
// ensures that `winit::Window` implements `Sync`
|
||||
needs_sync::<winit::Window>();
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
extern crate winit;
|
||||
|
||||
#[cfg(feature = "window")]
|
||||
#[test]
|
||||
fn window_proxy_send() {
|
||||
// ensures that `winit::WindowProxy` implements `Send`
|
||||
fn needs_send<T:Send>() {}
|
||||
needs_send::<winit::WindowProxy>();
|
||||
}
|
||||
Reference in New Issue
Block a user