Compare commits

..

49 Commits

Author SHA1 Message Date
Francesca Frangipane
1703d0417a Release winit 0.16.1 (#587) 2018-07-02 20:14:38 -04:00
Francesca Frangipane
fad72c0441 X11: Fix compilation when c_char==c_uchar (#586) 2018-07-02 11:05:25 -04:00
Francesca Frangipane
2f7321a076 X11+Windows: Guess initial DPI factor (#583)
* X11: Guess initial DPI factor

* Windows: Guess initial DPI factor
2018-07-01 11:01:46 -04:00
icefoxen
85ee422acd Define "DPI" in docs. (#580)
It makes my pedant reflexes tingle.
2018-06-28 14:05:56 -04:00
Francesca Frangipane
089816d9ba Release winit 0.16.0 (#578) 2018-06-25 16:47:10 -04:00
Francesca Frangipane
c873c2db15 Wayland: Fix window creation dimensions (#577)
* Wayland: Fix window creation dimensions

* Wayland: Fix window creation min/max
2018-06-24 08:28:57 -04:00
えちょ
047c67baf3 windows feature WS_EX_NOREDIRECTIONBITMAP (#575)
* set WS_EX_NOREDIRECTIONBITMAP

* add CHANGELOG.md

* more flexibility.

* Skip DwmEnableBlurBehindWindow if no_redirection_bitmap is enabled.
2018-06-21 21:33:29 -04:00
aloucks
8f394f117b Change set_cursor_position to return Result<(), String> (#562)
* Change set_cursor_position to return Result<(), String>

This is now consistent with `grab_cursor`, and
enables `window.set_cursor_position(x, y)?` in functions
that return `Result<_, Box<Error>>`.

* Adjust error handling of unimplemented cusor opertions in wayland

* The final nitpick

* Actually one more
2018-06-19 10:30:15 -04:00
Francesca Frangipane
fb7528c239 grab_cursor and hide_cursor (#571)
* Windows: Use new cursor state API

* X11: Use new cursor state API

* macOS: Use new cursor state API

* Android+iOS: Stubbed new cursor state API

* Emscripten: Use new cursor state API

* Prevent multiple inc/dec of display count on Windows

* Fixed missing imports (no idea where those went)

* Remove NoneCursor

* Improved documentation

* Fix Emscripten build

* Windows: Re-grab before and after fullscreen
2018-06-18 12:32:18 -04:00
Hal Gentz
042f5fe4b3 Shares the XConnection between all event loops instead of just all event (#572)
loops on the same thread.

This is needed for adding shared context support to glutin, as contexts
must be made with the same native display (and therefore the same
connection.)

Signed-off-by: Hal Gentz <zegentzy@protonmail.com>
2018-06-17 20:44:38 -04:00
Francesca Frangipane
289fb47a34 macOS: Fix doubled key repeats (#570) 2018-06-17 15:08:26 -04:00
Nikolai Vazquez
38bc6babb7 Change Travis badge to SVG (#573)
PNG on GitHub does not render well on HiDPI screens.
2018-06-17 15:08:01 -04:00
Francesca Frangipane
e7a8efcfa0 Mirror monitor list methods on Window (#567)
* macOS: Monitor list methods on Window

* X11+Wayland: Monitor list methods on Window

* Windows: Monitor list methods on Window

* iOS: Monitor list methods on Window

* Android: Monitor list methods on Window

* Emscripten: Monitor list methods on Window

* Fixed Wayland implementation
2018-06-16 10:14:12 -04:00
Francesca Frangipane
1b74822cfc DPI for everyone (#548) 2018-06-14 19:42:18 -04:00
Lucas Kent
f083dae328 Windows creates Alt event instead of Menu event. (to match other platforms) (#551)
* Removed VirtualKeyCode::LMenu + VirtualKeyCode::RMenu, Windows now generates VirtualKeyCode::LAlt + VirtualKeyCode::RAlt instead.

* CHANGELOG nits
2018-06-13 13:24:33 -04:00
Francesca Frangipane
23c384bd30 Release winit 0.15.1 (#564) 2018-06-13 12:06:22 -04:00
Victor Berger
ced1616e51 wayland: implement set_resizable (#565) 2018-06-13 11:18:44 -04:00
Peter Atashian
233ac4aed2 Update to winapi 0.3.5 (#563) 2018-06-12 11:58:18 -04:00
Danny Fritz
be5a2b0e87 Windows & X11: Window::set_resizable (#558)
* Windows: Window::set_resizable

* X11: Window::set_resizable

* Code style regarding resizable

* X11: set_resizable remember max/min window size

* Stub out set_resizable on Android, iOS, and emscripten

* remove comment block from docs

* Windows: set_resizable in fullscreen

* Special case Xfwm

* Added fun provisos to docs
2018-06-11 18:47:50 -04:00
Francesca Frangipane
2b4b64f499 macOS: Only detect clicks+motion within client area (#561)
* macOS: Only detect clicks within client area

* macOS: Only track mouse motion within client area

* Add CHANGELOG entry about #463 fix
2018-06-11 11:16:39 -04:00
Francesca Frangipane
262490d074 X11: Fix super fun race conditions (#554)
* X11: Fix super fun race conditions

* Fix build on rustc<1.26
2018-06-07 14:08:19 -04:00
Francesca Frangipane
8891cfd85e macOS: Resizable without decorations (#553)
* macOS: Resizable without decorations

* Fix style mask regressions
2018-06-07 13:29:23 -04:00
Danny Fritz
2cc8fa1eac X11: implement with_resizable (#540) (#556) 2018-06-07 12:46:15 -04:00
Francesca Frangipane
79aebf06dc macOS: Fix alt and win keycodes (#552)
* macOS: Generate LAlt/RAlt VirtualKeyCode

* macOS: Correct RWin/LWin
2018-06-06 11:30:26 -04:00
Francesca Frangipane
19dd961752 X11: Fix flickering when resizing with transparency enabled (#546)
* X11: Fix flickering when resizing with transparency enabled

* X11: Fix with_override_redirect
2018-06-03 13:11:54 -04:00
Christian Duerr
bf413ecb83 Fix DPI with 0 width/hight reported by xorg (#544)
* Fix DPI with 0 width/hight reported by xorg

* Add `WINIT_HIDPI_FACTOR` env variable

It is now possible to override the DPI factor using the
`WINIT_HIDPI_FACTOR` environment variable on X11.

The changelog also has been updated to introduce all current changes
made.

* Add documentation for the environment variable

* Fix nitpicks

* Learning the alphabet

* Panic with error message if DPI env var is <= 0
2018-06-03 12:41:47 -04:00
Francesca Frangipane
fd1a3eda1c Test against rustc 1.24.1 on Travis (#547) 2018-06-02 22:59:59 -04:00
Danny Fritz
0e2488db32 Added a GitHub PULL_REQUEST_TEMPLATE (#542)
* Added a GitHub PULL_REQUEST_TEMPLATE

* Updated to better reflect my dictatorial demands
2018-06-02 11:04:08 -04:00
Danny Fritz
58a00bffbb Windows: implement with_resizable (#540) (#541)
* Windows: implement with_resizable (#540)

* Fixed typo
2018-06-02 10:51:24 -04:00
Johannes Hofmann
bbfe57400d appveyor.yml: Test additional Rust channels (#539)
* In addition to nightly, also test the current stable version and Rust
  1.24.1
* Use rustup-init.exe to install the different versions
2018-05-30 07:57:40 -04:00
Francesca Frangipane
4372f6fdac X11: Flatten window model (#536) 2018-05-29 07:48:47 -04:00
Francesca Frangipane
30f798b246 X11: util design improvements (#534) 2018-05-27 08:49:35 -04:00
Francesca Frangipane
282770f11a Release winit 0.15.0 (#530) 2018-05-22 14:17:41 -04:00
Francesca Frangipane
17373a4e91 X11: Fix primary monitor fallback regression (#532) 2018-05-22 09:07:46 -04:00
Francesca Frangipane
a34147b602 macOS: Fix keyboard regressions (#533)
* Emit `ReceivedCharacter` for key repeats
* Enter emits `\r` instead of `\n`
2018-05-22 09:05:33 -04:00
Francesca Frangipane
cebd15bfd1 X11: Improve hint support (#529)
Fixes #257
2018-05-20 10:47:22 -04:00
Francesca Frangipane
f51f7c0ca8 Add option to make window "always on top" (#528)
* macOS: always_on_top

* Windows: always_on_top

* X11: always_on_top

* Stub set_always_on_top on other platforms
2018-05-20 10:24:05 -04:00
Francesca Frangipane
f6d26df64d Windows: CursorState improvements (#526)
* Windows: CursorState improvements

Fixes #523

Prior to changing the cursor state, we now check the current grab
state, since it can be invalidated by alt-tabbing and other things.

`CursorState::Hide` is also implemented now.

The cursor name is now wrapped in a `Cursor` struct to allow
multithreaded access.

`Window::set_cursor_state` has been reworked to use
`execute_in_thread`. Two unneeded `transmute` calls were also
removed.

The `WM_SETCURSOR` handler is much more readable now.

`MonitorId::get_adapter_name` has been removed, since it's dead
code and appears to be a relic from 4 years ago.

* Windows: CursorState::Grab no longer hides cursor

`MouseCursor::NoneCursor` has been implemented to allow for
equivalent behavior to the older implementation.

Windows and X11 now have consistent cursor grabbing behavior.
macOS still needs to be updated.

* Windows: Grabbing auto-hides again (for now)

This API needs more work, so let's stick to a bug fix and some
refactoring. However, it now hides using a different technique
than it did originally, which applies instantly instead of after
mouse movement.
2018-05-19 12:02:57 -04:00
Francesca Frangipane
fddfb2e2d6 Windows: Fix detection of Pause and Scroll keys (#525)
Fixes #524
2018-05-18 18:48:19 -04:00
Francesca Frangipane
dec728cfa2 macOS: Implement NSTextInputClient (#518)
Fixes #263
2018-05-17 21:28:30 -04:00
Francesca Frangipane
8440091a4e macOS: Implement with_resize_increments (#519)
Fixes #135
2018-05-16 10:16:36 -04:00
Francesca Frangipane
2464a135b3 macOS: Fix Window::get_current_monitor (#521)
* macOS: Implement MonitorId::get_position

* macOS: Fix Window::get_current_monitor
2018-05-16 09:41:45 -04:00
Francesca Frangipane
87fa120ebb macOS: Fix re-enabling decorations after the window is built without them (#520)
Fixes #517
2018-05-16 08:51:56 -04:00
Francesca Frangipane
d86f53a02c X11: Fix get_current_monitor (#515)
* X11: Fix get_current_monitor

Fixes #64

* impl Debug for MonitorId on all platforms
2018-05-14 08:14:57 -04:00
Francesca Frangipane
15a4fec3d9 X11: Fix scroll wheel delta on i3/etc. (#514)
Fixes #447
2018-05-13 08:44:23 -04:00
OJ Kwon
1819be1173 fix(mac_platform): forward keyevent to system (#511)
* fix(mac_platform): forward keyevent to system

* doc(changelog): update changelog
2018-05-12 22:10:57 -04:00
Victor Berger
ffa9b51d27 wayland: improve diagnostic of failed init (#512) 2018-05-12 07:58:11 -04:00
tinaun
b4a8c08f43 compile with icon_loading feature on docs.rs (#509)
* compile with icon_loading feature on docs.rs

these functions should be more visible now.

* Explicitly document which functions require icon_loading
2018-05-11 10:33:06 -04:00
Francesca Frangipane
741bcc4672 Correct privacy for Icon::to_cardinals (#510) 2018-05-10 18:42:41 -04:00
65 changed files with 6768 additions and 3536 deletions

4
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,4 @@
- [ ] Tested on all platforms changed
- [ ] Added an entry to `CHANGELOG.md` if knowledge of this change could be valuable to users
- [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
- [ ] Created an example program if it would help users understand this functionality

2
.gitignore vendored
View File

@@ -1,4 +1,6 @@
Cargo.lock
target/
rls/
.vscode/
*~
#*#

View File

@@ -19,6 +19,12 @@ matrix:
addons:
apt:
packages: *i686_packages
- env: TARGET=i686-unknown-linux-gnu
os: linux
rust: 1.24.1
addons:
apt:
packages: *i686_packages
# Linux 64bit
- env: TARGET=x86_64-unknown-linux-gnu
@@ -27,6 +33,9 @@ matrix:
- env: TARGET=x86_64-unknown-linux-gnu
os: linux
rust: stable
- env: TARGET=x86_64-unknown-linux-gnu
os: linux
rust: 1.24.1
# macOS
- env: TARGET=x86_64-apple-darwin
@@ -35,6 +44,9 @@ matrix:
- env: TARGET=x86_64-apple-darwin
os: osx
rust: stable
- env: TARGET=x86_64-apple-darwin
os: osx
rust: 1.24.1
# iOS
- env: TARGET=x86_64-apple-ios
@@ -43,6 +55,9 @@ matrix:
- env: TARGET=x86_64-apple-ios
os: osx
rust: stable
- env: TARGET=x86_64-apple-ios
os: osx
rust: 1.24.1
install:
- rustup self update

View File

@@ -1,5 +1,76 @@
# Unreleased
# Version 0.16.1 (2018-07-02)
- Added logging through `log`. Logging will become more extensive over time.
- On X11 and Windows, the window's DPI factor is guessed before creating the window. This *greatly* cuts back on unsightly auto-resizing that would occur immediately after window creation.
- Fixed X11 backend compilation for environments where `c_char` is unsigned.
# Version 0.16.0 (2018-06-25)
- Windows additionally has `WindowBuilderExt::with_no_redirection_bitmap`.
- **Breaking:** Removed `VirtualKeyCode::LMenu` and `VirtualKeyCode::RMenu`; Windows now generates `VirtualKeyCode::LAlt` and `VirtualKeyCode::RAlt` instead.
- On X11, exiting fullscreen no longer leaves the window in the monitor's top left corner.
- **Breaking:** `Window::hidpi_factor` has been renamed to `Window::get_hidpi_factor` for better consistency. `WindowEvent::HiDPIFactorChanged` has been renamed to `WindowEvent::HiDpiFactorChanged`. DPI factors are always represented as `f64` instead of `f32` now.
- The Windows backend is now DPI aware. `WindowEvent::HiDpiFactorChanged` is implemented, and `MonitorId::get_hidpi_factor` and `Window::hidpi_factor` return accurate values.
- Implemented `WindowEvent::HiDpiFactorChanged` on X11.
- On macOS, `Window::set_cursor_position` is now relative to the client area.
- On macOS, setting the maximum and minimum dimensions now applies to the client area dimensions rather than to the window dimensions.
- On iOS, `MonitorId::get_dimensions` has been implemented and both `MonitorId::get_hidpi_factor` and `Window::get_hidpi_factor` return accurate values.
- On Emscripten, `MonitorId::get_hidpi_factor` now returns the same value as `Window::get_hidpi_factor` (it previously would always return 1.0).
- **Breaking:** The entire API for sizes, positions, etc. has changed. In the majority of cases, winit produces and consumes positions and sizes as `LogicalPosition` and `LogicalSize`, respectively. The notable exception is `MonitorId` methods, which deal in `PhysicalPosition` and `PhysicalSize`. See the documentation for specifics and explanations of the types. Additionally, winit automatically conserves logical size when the DPI factor changes.
- **Breaking:** All deprecated methods have been removed. For `Window::platform_display` and `Window::platform_window`, switch to the appropriate platform-specific `WindowExt` methods. For `Window::get_inner_size_points` and `Window::get_inner_size_pixels`, use the `LogicalSize` returned by `Window::get_inner_size` and convert as needed.
- HiDPI support for Wayland.
- `EventsLoop::get_available_monitors` and `EventsLoop::get_primary_monitor` now have identical counterparts on `Window`, so this information can be acquired without an `EventsLoop` borrow.
- `AvailableMonitorsIter` now implements `Debug`.
- Fixed quirk on macOS where certain keys would generate characters at twice the normal rate when held down.
- On X11, all event loops now share the same `XConnection`.
- **Breaking:** `Window::set_cursor_state` and `CursorState` enum removed in favor of the more composable `Window::grab_cursor` and `Window::hide_cursor`. As a result, grabbing the cursor no longer automatically hides it; you must call both methods to retain the old behavior on Windows and macOS. `Cursor::NoneCursor` has been removed, as it's no longer useful.
- **Breaking:** `Window::set_cursor_position` now returns `Result<(), String>`, thus allowing for `Box<Error>` conversion via `?`.
# Version 0.15.1 (2018-06-13)
- On X11, the `Moved` event is no longer sent when the window is resized without changing position.
- `MouseCursor` and `CursorState` now implement `Default`.
- `WindowBuilder::with_resizable` implemented for Windows, X11, Wayland, and macOS.
- `Window::set_resizable` implemented for Windows, X11, Wayland, and macOS.
- On X11, if the monitor's width or height in millimeters is reported as 0, the DPI is now 1.0 instead of +inf.
- On X11, the environment variable `WINIT_HIDPI_FACTOR` has been added for overriding DPI factor.
- On X11, enabling transparency no longer causes the window contents to flicker when resizing.
- On X11, `with_override_redirect` now actually enables override redirect.
- macOS now generates `VirtualKeyCode::LAlt` and `VirtualKeyCode::RAlt` instead of `None` for both.
- On macOS, `VirtualKeyCode::RWin` and `VirtualKeyCode::LWin` are no longer switched.
- On macOS, windows without decorations can once again be resized.
- Fixed race conditions when creating an `EventsLoop` on X11, most commonly manifesting as "[xcb] Unknown sequence number while processing queue".
- On macOS, `CursorMoved` and `MouseInput` events are only generated if they occurs within the window's client area.
- On macOS, resizing the window no longer generates a spurious `MouseInput` event.
# Version 0.15.0 (2018-05-22)
- `Icon::to_cardinals` is no longer public, since it was never supposed to be.
- Wayland: improve diagnostics if initialization fails
- Fix some system event key doesn't work when focused, do not block keyevent forward to system on macOS
- On X11, the scroll wheel position is now correctly reset on i3 and other WMs that have the same quirk.
- On X11, `Window::get_current_monitor` now reliably returns the correct monitor.
- On X11, `Window::hidpi_factor` returns values from XRandR rather than the inaccurate values previously queried from the core protocol.
- On X11, the primary monitor is detected correctly even when using versions of XRandR less than 1.5.
- `MonitorId` now implements `Debug`.
- Fixed bug on macOS where using `with_decorations(false)` would cause `set_decorations(true)` to produce a transparent titlebar with no title.
- Implemented `MonitorId::get_position` on macOS.
- On macOS, `Window::get_current_monitor` now returns accurate values.
- Added `WindowBuilderExt::with_resize_increments` to macOS.
- **Breaking:** On X11, `WindowBuilderExt::with_resize_increments` and `WindowBuilderExt::with_base_size` now take `u32` values rather than `i32`.
- macOS keyboard handling has been overhauled, allowing for the use of dead keys, IME, etc. Right modifier keys are also no longer reported as being left.
- Added the `Window::set_ime_spot(x: i32, y: i32)` method, which is implemented on X11 and macOS.
- **Breaking**: `os::unix::WindowExt::send_xim_spot(x: i16, y: i16)` no longer exists. Switch to the new `Window::set_ime_spot(x: i32, y: i32)`, which has equivalent functionality.
- Fixed detection of `Pause` and `Scroll` keys on Windows.
- On Windows, alt-tabbing while the cursor is grabbed no longer makes it impossible to re-grab the cursor.
- On Windows, using `CursorState::Hide` when the cursor is grabbed now ungrabs the cursor first.
- Implemented `MouseCursor::NoneCursor` on Windows.
- Added `WindowBuilder::with_always_on_top` and `Window::set_always_on_top`. Implemented on Windows, macOS, and X11.
- On X11, `WindowBuilderExt` now has `with_class`, `with_override_redirect`, and `with_x11_window_type` to allow for more control over window creation. `WindowExt` additionally has `set_urgent`.
- More hints are set by default on X11, including `_NET_WM_PID` and `WM_CLIENT_MACHINE`. Note that prior to this, the `WM_CLASS` hint was automatically set to whatever value was passed to `with_title`. It's now set to the executable name to better conform to expectations and the specification; if this is undesirable, you must explicitly use `WindowBuilderExt::with_class`.
# Version 0.14.0 (2018-05-09)
- Created the `Copy`, `Paste` and `Cut` `VirtualKeyCode`s and added support for them on X11 and Wayland

View File

@@ -1,6 +1,6 @@
[package]
name = "winit"
version = "0.14.0"
version = "0.16.1"
authors = ["The winit contributors, Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library."
keywords = ["windowing"]
@@ -10,12 +10,16 @@ repository = "https://github.com/tomaka/winit"
documentation = "https://docs.rs/winit"
categories = ["gui"]
[package.metadata.docs.rs]
features = ["icon_loading"]
[features]
icon_loading = ["image"]
[dependencies]
lazy_static = "1"
libc = "0.2"
log = "0.4"
image = { version = "0.19", optional = true }
[target.'cfg(target_os = "android")'.dependencies.android_glue]
@@ -26,30 +30,32 @@ objc = "0.2"
[target.'cfg(target_os = "macos")'.dependencies]
objc = "0.2"
cocoa = "0.14"
core-foundation = "0.5"
core-graphics = "0.13"
cocoa = "0.15"
core-foundation = "0.6"
core-graphics = "0.14"
[target.'cfg(target_os = "windows")'.dependencies.winapi]
version = "0.3"
version = "0.3.5"
features = [
"combaseapi",
"dwmapi",
"hidusage",
"libloaderapi",
"objbase",
"processthreadsapi",
"shellapi",
"shellscalingapi",
"shobjidl_core",
"unknwnbase",
"windowsx",
"wingdi",
"winnt",
"winuser",
"wingdi",
"shellapi",
"dwmapi",
"processthreadsapi",
"libloaderapi",
"windowsx",
"hidusage",
"combaseapi",
"objbase",
"unknwnbase",
]
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies]
wayland-client = { version = "0.20.4", features = [ "dlopen", "egl", "cursor"] }
smithay-client-toolkit = "0.2.1"
wayland-client = { version = "0.20.6", features = [ "dlopen", "egl", "cursor"] }
smithay-client-toolkit = "0.2.2"
x11-dl = "2.17.5"
parking_lot = "0.5"
percent-encoding = "1.0"

View File

@@ -4,12 +4,12 @@
[![Docs.rs](https://docs.rs/winit/badge.svg)](https://docs.rs/winit)
[![Build Status](https://travis-ci.org/tomaka/winit.png?branch=master)](https://travis-ci.org/tomaka/winit)
[![Build Status](https://travis-ci.org/tomaka/winit.svg?branch=master)](https://travis-ci.org/tomaka/winit)
[![Build status](https://ci.appveyor.com/api/projects/status/5h87hj0g4q2xe3j9/branch/master?svg=true)](https://ci.appveyor.com/project/tomaka/winit/branch/master)
```toml
[dependencies]
winit = "0.14"
winit = "0.16"
```
## [Documentation](https://docs.rs/winit)

View File

@@ -1,12 +1,19 @@
environment:
matrix:
- TARGET: x86_64-pc-windows-msvc
CHANNEL: nightly
- TARGET: x86_64-pc-windows-msvc
CHANNEL: stable
- TARGET: x86_64-pc-windows-msvc
CHANNEL: 1.24.1
- TARGET: i686-pc-windows-msvc
CHANNEL: nightly
- TARGET: i686-pc-windows-gnu
CHANNEL: nightly
install:
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe"
- rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
- rustup-init -yv --default-toolchain %CHANNEL% --default-host %TARGET%
- SET PATH=%PATH%;%USERPROFILE%\.cargo\bin
- SET PATH=%PATH%;C:\MinGW\bin
- rustc -V
- cargo -V

View File

@@ -8,7 +8,7 @@ fn main() {
let window = winit::WindowBuilder::new().build(&events_loop).unwrap();
window.set_title("A fantastic window!");
let cursors = [MouseCursor::Default, MouseCursor::Crosshair, MouseCursor::Hand, MouseCursor::Arrow, MouseCursor::Move, MouseCursor::Text, MouseCursor::Wait, MouseCursor::Help, MouseCursor::Progress, MouseCursor::NotAllowed, MouseCursor::ContextMenu, MouseCursor::NoneCursor, MouseCursor::Cell, MouseCursor::VerticalText, MouseCursor::Alias, MouseCursor::Copy, MouseCursor::NoDrop, MouseCursor::Grab, MouseCursor::Grabbing, MouseCursor::AllScroll, MouseCursor::ZoomIn, MouseCursor::ZoomOut, MouseCursor::EResize, MouseCursor::NResize, MouseCursor::NeResize, MouseCursor::NwResize, MouseCursor::SResize, MouseCursor::SeResize, MouseCursor::SwResize, MouseCursor::WResize, MouseCursor::EwResize, MouseCursor::NsResize, MouseCursor::NeswResize, MouseCursor::NwseResize, MouseCursor::ColResize, MouseCursor::RowResize];
let cursors = [MouseCursor::Default, MouseCursor::Crosshair, MouseCursor::Hand, MouseCursor::Arrow, MouseCursor::Move, MouseCursor::Text, MouseCursor::Wait, MouseCursor::Help, MouseCursor::Progress, MouseCursor::NotAllowed, MouseCursor::ContextMenu, MouseCursor::Cell, MouseCursor::VerticalText, MouseCursor::Alias, MouseCursor::Copy, MouseCursor::NoDrop, MouseCursor::Grab, MouseCursor::Grabbing, MouseCursor::AllScroll, MouseCursor::ZoomIn, MouseCursor::ZoomOut, MouseCursor::EResize, MouseCursor::NResize, MouseCursor::NeResize, MouseCursor::NwResize, MouseCursor::SResize, MouseCursor::SeResize, MouseCursor::SwResize, MouseCursor::WResize, MouseCursor::EwResize, MouseCursor::NsResize, MouseCursor::NeswResize, MouseCursor::NwseResize, MouseCursor::ColResize, MouseCursor::RowResize];
let mut cursor_idx = 0;
events_loop.run_forever(|event| {

38
examples/cursor_grab.rs Normal file
View File

@@ -0,0 +1,38 @@
extern crate winit;
fn main() {
let mut events_loop = winit::EventsLoop::new();
let window = winit::WindowBuilder::new()
.with_title("Super Cursor Grab'n'Hide Simulator 9000")
.build(&events_loop)
.unwrap();
events_loop.run_forever(|event| {
if let winit::Event::WindowEvent { event, .. } = event {
use winit::WindowEvent::*;
match event {
CloseRequested => return winit::ControlFlow::Break,
KeyboardInput {
input: winit::KeyboardInput {
state: winit::ElementState::Released,
virtual_keycode: Some(key),
modifiers,
..
},
..
} => {
use winit::VirtualKeyCode::*;
match key {
Escape => return winit::ControlFlow::Break,
G => window.grab_cursor(!modifiers.shift).unwrap(),
H => window.hide_cursor(!modifiers.shift),
_ => (),
}
}
_ => (),
}
}
winit::ControlFlow::Continue
});
}

View File

@@ -1,45 +0,0 @@
extern crate winit;
use winit::{ControlFlow, WindowEvent, ElementState, KeyboardInput};
fn main() {
let mut events_loop = winit::EventsLoop::new();
let window = winit::WindowBuilder::new().build(&events_loop).unwrap();
window.set_title("winit - Cursor grabbing test");
let mut grabbed = false;
events_loop.run_forever(|event| {
println!("{:?}", event);
match event {
winit::Event::WindowEvent { event, .. } => {
match event {
WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. } => {
if grabbed {
grabbed = false;
window.set_cursor_state(winit::CursorState::Normal)
.ok().expect("could not ungrab mouse cursor");
} else {
grabbed = true;
window.set_cursor_state(winit::CursorState::Grab)
.ok().expect("could not grab mouse cursor");
}
},
WindowEvent::CloseRequested => return ControlFlow::Break,
a @ WindowEvent::CursorMoved { .. } => {
println!("{:?}", a);
},
_ => (),
}
}
_ => {}
}
ControlFlow::Continue
});
}

View File

@@ -1,5 +1,7 @@
extern crate winit;
use winit::dpi::LogicalSize;
fn main() {
let mut events_loop = winit::EventsLoop::new();
@@ -7,8 +9,8 @@ fn main() {
.build(&events_loop)
.unwrap();
window.set_min_dimensions(Some((400, 200)));
window.set_max_dimensions(Some((800, 400)));
window.set_min_dimensions(Some(LogicalSize::new(400.0, 200.0)));
window.set_max_dimensions(Some(LogicalSize::new(800.0, 400.0)));
events_loop.run_forever(|event| {
println!("{:?}", event);

7
examples/monitor_list.rs Normal file
View File

@@ -0,0 +1,7 @@
extern crate winit;
fn main() {
let event_loop = winit::EventsLoop::new();
let window = winit::WindowBuilder::new().build(&event_loop).unwrap();
println!("{:#?}\nPrimary: {:#?}", window.get_available_monitors(), window.get_primary_monitor());
}

38
examples/resizable.rs Normal file
View File

@@ -0,0 +1,38 @@
extern crate winit;
fn main() {
let mut events_loop = winit::EventsLoop::new();
let mut resizable = false;
let window = winit::WindowBuilder::new()
.with_title("Hit space to toggle resizability.")
.with_dimensions((400, 200).into())
.with_resizable(resizable)
.build(&events_loop)
.unwrap();
events_loop.run_forever(|event| {
match event {
winit::Event::WindowEvent { event, .. } => match event {
winit::WindowEvent::CloseRequested => return winit::ControlFlow::Break,
winit::WindowEvent::KeyboardInput {
input:
winit::KeyboardInput {
virtual_keycode: Some(winit::VirtualKeyCode::Space),
state: winit::ElementState::Released,
..
},
..
} => {
resizable = !resizable;
println!("Resizable: {}", resizable);
window.set_resizable(resizable);
}
_ => (),
},
_ => (),
};
winit::ControlFlow::Continue
});
}

307
src/dpi.rs Normal file
View File

@@ -0,0 +1,307 @@
//! DPI is important, so read the docs for this module if you don't want to be confused.
//!
//! Originally, `winit` dealt entirely in physical pixels (excluding unintentional inconsistencies), but now all
//! window-related functions both produce and consume logical pixels. Monitor-related functions still use physical
//! pixels, as do any context-related functions in `glutin`.
//!
//! If you've never heard of these terms before, then you're not alone, and this documentation will explain the
//! concepts.
//!
//! Modern screens have a defined physical resolution, most commonly 1920x1080. Indepedent of that is the amount of
//! space the screen occupies, which is to say, the height and width in millimeters. The relationship between these two
//! measurements is the *pixel density*. Mobile screens require a high pixel density, as they're held close to the
//! eyes. Larger displays also require a higher pixel density, hence the growing presence of 1440p and 4K displays.
//!
//! So, this presents a problem. Let's say we want to render a square 100px button. It will occupy 100x100 of the
//! screen's pixels, which in many cases, seems perfectly fine. However, because this size doesn't account for the
//! screen's dimensions or pixel density, the button's size can vary quite a bit. On a 4K display, it would be unusably
//! small.
//!
//! That's a description of what happens when the button is 100x100 *physical* pixels. Instead, let's try using 100x100
//! *logical* pixels. To map logical pixels to physical pixels, we simply multiply by the DPI (dots per inch) factor.
//! On a "typical" desktop display, the DPI factor will be 1.0, so 100x100 logical pixels equates to 100x100 physical
//! pixels. However, a 1440p display may have a DPI factor of 1.25, so the button is rendered as 125x125 physical pixels.
//! Ideally, the button now has approximately the same perceived size across varying displays.
//!
//! Failure to account for the DPI factor can create a badly degraded user experience. Most notably, it can make users
//! feel like they have bad eyesight, which will potentially cause them to think about growing elderly, resulting in
//! them entering an existential panic. Once users enter that state, they will no longer be focused on your application.
//!
//! There are two ways to get the DPI factor: either by calling
//! [`MonitorId::get_hidpi_factor`](../struct.MonitorId.html#method.get_hidpi_factor), or
//! [`Window::get_hidpi_factor`](../struct.Window.html#method.get_hidpi_factor). You'll almost always use the latter,
//! which is basically equivalent to `window.get_current_monitor().get_hidpi_factor()` anyway.
//!
//! Here's an overview of what sort of DPI factors you can expect, and where they come from:
//! - **Windows:** On Windows 8 and 10, per-monitor scaling is readily configured by users from the display settings.
//! While users are free to select any option they want, they're only given a selection of "nice" DPI factors, i.e.
//! 1.0, 1.25, 1.5... on Windows 7, the DPI factor is global and changing it requires logging out.
//! - **macOS:** The buzzword is "retina displays", which have a DPI factor of 2.0. Otherwise, the DPI factor is 1.0.
//! Intermediate DPI factors are never used, thus 1440p displays/etc. aren't properly supported. It's possible for any
//! display to use that 2.0 DPI factor, given the use of the command line.
//! - **X11:** On X11, we calcuate the DPI factor based on the millimeter dimensions provided by XRandR. This can
//! result in a wide range of possible values, including some interesting ones like 1.0833333333333333. This can be
//! overridden using the `WINIT_HIDPI_FACTOR` environment variable, though that's not recommended.
//! - **Wayland:** On Wayland, DPI factors are very much at the discretion of the user.
//! - **iOS:** DPI factors are both constant and device-specific on iOS.
//! - **Android:** This feature isn't yet implemented on Android, so the DPI factor will always be returned as 1.0.
//!
//! The window's logical size is conserved across DPI changes, resulting in the physical size changing instead. This
//! may be surprising on X11, but is quite standard elsewhere. Physical size changes produce a
//! [`Resized`](../enum.WindowEvent.html#variant.Resized) event, even on platforms where no resize actually occurs,
//! such as macOS and Wayland. As a result, it's not necessary to separately handle
//! [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) if you're only listening for size.
//!
//! Your GPU has no awareness of the concept of logical pixels, and unless you like wasting pixel density, your
//! framebuffer's size should be in physical pixels.
/// Checks that the DPI factor is a normal positive `f64`.
///
/// All functions that take a DPI factor assert that this will return `true`. If you're sourcing DPI factors from
/// anywhere other than winit, it's recommended to validate them using this function before passing them to winit;
/// otherwise, you risk panics.
#[inline]
pub fn validate_hidpi_factor(dpi_factor: f64) -> bool {
dpi_factor.is_sign_positive() && dpi_factor.is_normal()
}
/// A position represented in logical pixels.
///
/// The position is stored as floats, so please be careful. Casting floats to integers truncates the fractional part,
/// which can cause noticable issues. To help with that, an `Into<(i32, i32)>` implementation is provided which
/// does the rounding for you.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct LogicalPosition {
pub x: f64,
pub y: f64,
}
impl LogicalPosition {
#[inline]
pub fn new(x: f64, y: f64) -> Self {
LogicalPosition { x, y }
}
#[inline]
pub fn from_physical<T: Into<PhysicalPosition>>(physical: T, dpi_factor: f64) -> Self {
physical.into().to_logical(dpi_factor)
}
#[inline]
pub fn to_physical(&self, dpi_factor: f64) -> PhysicalPosition {
assert!(validate_hidpi_factor(dpi_factor));
let x = self.x * dpi_factor;
let y = self.y * dpi_factor;
PhysicalPosition::new(x, y)
}
}
impl From<(f64, f64)> for LogicalPosition {
#[inline]
fn from((x, y): (f64, f64)) -> Self {
Self::new(x, y)
}
}
impl From<(i32, i32)> for LogicalPosition {
#[inline]
fn from((x, y): (i32, i32)) -> Self {
Self::new(x as f64, y as f64)
}
}
impl Into<(f64, f64)> for LogicalPosition {
#[inline]
fn into(self) -> (f64, f64) {
(self.x, self.y)
}
}
impl Into<(i32, i32)> for LogicalPosition {
/// Note that this rounds instead of truncating.
#[inline]
fn into(self) -> (i32, i32) {
(self.x.round() as _, self.y.round() as _)
}
}
/// A position represented in physical pixels.
///
/// The position is stored as floats, so please be careful. Casting floats to integers truncates the fractional part,
/// which can cause noticable issues. To help with that, an `Into<(i32, i32)>` implementation is provided which
/// does the rounding for you.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct PhysicalPosition {
pub x: f64,
pub y: f64,
}
impl PhysicalPosition {
#[inline]
pub fn new(x: f64, y: f64) -> Self {
PhysicalPosition { x, y }
}
#[inline]
pub fn from_logical<T: Into<LogicalPosition>>(logical: T, dpi_factor: f64) -> Self {
logical.into().to_physical(dpi_factor)
}
#[inline]
pub fn to_logical(&self, dpi_factor: f64) -> LogicalPosition {
assert!(validate_hidpi_factor(dpi_factor));
let x = self.x / dpi_factor;
let y = self.y / dpi_factor;
LogicalPosition::new(x, y)
}
}
impl From<(f64, f64)> for PhysicalPosition {
#[inline]
fn from((x, y): (f64, f64)) -> Self {
Self::new(x, y)
}
}
impl From<(i32, i32)> for PhysicalPosition {
#[inline]
fn from((x, y): (i32, i32)) -> Self {
Self::new(x as f64, y as f64)
}
}
impl Into<(f64, f64)> for PhysicalPosition {
#[inline]
fn into(self) -> (f64, f64) {
(self.x, self.y)
}
}
impl Into<(i32, i32)> for PhysicalPosition {
/// Note that this rounds instead of truncating.
#[inline]
fn into(self) -> (i32, i32) {
(self.x.round() as _, self.y.round() as _)
}
}
/// A size represented in logical pixels.
///
/// The size is stored as floats, so please be careful. Casting floats to integers truncates the fractional part,
/// which can cause noticable issues. To help with that, an `Into<(u32, u32)>` implementation is provided which
/// does the rounding for you.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct LogicalSize {
pub width: f64,
pub height: f64,
}
impl LogicalSize {
#[inline]
pub fn new(width: f64, height: f64) -> Self {
LogicalSize { width, height }
}
#[inline]
pub fn from_physical<T: Into<PhysicalSize>>(physical: T, dpi_factor: f64) -> Self {
physical.into().to_logical(dpi_factor)
}
#[inline]
pub fn to_physical(&self, dpi_factor: f64) -> PhysicalSize {
assert!(validate_hidpi_factor(dpi_factor));
let width = self.width * dpi_factor;
let height = self.height * dpi_factor;
PhysicalSize::new(width, height)
}
}
impl From<(f64, f64)> for LogicalSize {
#[inline]
fn from((width, height): (f64, f64)) -> Self {
Self::new(width, height)
}
}
impl From<(u32, u32)> for LogicalSize {
#[inline]
fn from((width, height): (u32, u32)) -> Self {
Self::new(width as f64, height as f64)
}
}
impl Into<(f64, f64)> for LogicalSize {
#[inline]
fn into(self) -> (f64, f64) {
(self.width, self.height)
}
}
impl Into<(u32, u32)> for LogicalSize {
/// Note that this rounds instead of truncating.
#[inline]
fn into(self) -> (u32, u32) {
(self.width.round() as _, self.height.round() as _)
}
}
/// A size represented in physical pixels.
///
/// The size is stored as floats, so please be careful. Casting floats to integers truncates the fractional part,
/// which can cause noticable issues. To help with that, an `Into<(u32, u32)>` implementation is provided which
/// does the rounding for you.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct PhysicalSize {
pub width: f64,
pub height: f64,
}
impl PhysicalSize {
#[inline]
pub fn new(width: f64, height: f64) -> Self {
PhysicalSize { width, height }
}
#[inline]
pub fn from_logical<T: Into<LogicalSize>>(logical: T, dpi_factor: f64) -> Self {
logical.into().to_physical(dpi_factor)
}
#[inline]
pub fn to_logical(&self, dpi_factor: f64) -> LogicalSize {
assert!(validate_hidpi_factor(dpi_factor));
let width = self.width / dpi_factor;
let height = self.height / dpi_factor;
LogicalSize::new(width, height)
}
}
impl From<(f64, f64)> for PhysicalSize {
#[inline]
fn from((width, height): (f64, f64)) -> Self {
Self::new(width, height)
}
}
impl From<(u32, u32)> for PhysicalSize {
#[inline]
fn from((width, height): (u32, u32)) -> Self {
Self::new(width as f64, height as f64)
}
}
impl Into<(f64, f64)> for PhysicalSize {
#[inline]
fn into(self) -> (f64, f64) {
(self.width, self.height)
}
}
impl Into<(u32, u32)> for PhysicalSize {
/// Note that this rounds instead of truncating.
#[inline]
fn into(self) -> (u32, u32) {
(self.width.round() as _, self.height.round() as _)
}
}

View File

@@ -1,5 +1,6 @@
use std::path::PathBuf;
use {WindowId, DeviceId};
use {DeviceId, LogicalPosition, LogicalSize, WindowId};
/// Describes a generic event.
#[derive(Clone, Debug)]
@@ -23,12 +24,11 @@ pub enum Event {
/// Describes an event from a `Window`.
#[derive(Clone, Debug)]
pub enum WindowEvent {
/// The size of the window has changed. Contains the client area's new dimensions.
Resized(u32, u32),
Resized(LogicalSize),
/// The position of the window has changed. Contains the window's new position.
Moved(i32, i32),
Moved(LogicalPosition),
/// The window has been requested to close.
CloseRequested,
@@ -63,7 +63,7 @@ pub enum WindowEvent {
/// (x,y) coords in pixels relative to the top-left corner of the window. Because the range of this data is
/// limited by the display area and it may have been transformed by the OS to implement effects such as cursor
/// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control.
position: (f64, f64),
position: LogicalPosition,
modifiers: ModifiersState
},
@@ -96,14 +96,16 @@ pub enum WindowEvent {
/// Touch event has been received
Touch(Touch),
/// DPI scaling factor of the window has changed.
/// The DPI factor of the window has changed.
///
/// The following actions cause DPI changes:
/// The following user actions can cause DPI changes:
///
/// * A user changes the resolution.
/// * A user changes the desktop scaling value (e.g. in Control Panel on Windows).
/// * A user moves the application window to a display with a different DPI.
HiDPIFactorChanged(f32),
/// * Changing the display's resolution.
/// * Changing the display's DPI factor (e.g. in Control Panel on Windows).
/// * Moving the window to a display with a different DPI factor.
///
/// For more information about DPI in general, see the [`dpi`](dpi/index.html) module.
HiDpiFactorChanged(f64),
}
/// Represents raw hardware events that are not associated with any particular window.
@@ -197,7 +199,7 @@ pub enum TouchPhase {
pub struct Touch {
pub device_id: DeviceId,
pub phase: TouchPhase,
pub location: (f64,f64),
pub location: LogicalPosition,
/// unique identifier of a finger.
pub id: u64
}
@@ -242,7 +244,7 @@ pub enum MouseScrollDelta {
/// Scroll events are expressed as a PixelDelta if
/// supported by the device (eg. a touchpad) and
/// platform.
PixelDelta(f32, f32)
PixelDelta(LogicalPosition),
}
/// Symbolic name for a keyboard key.
@@ -383,7 +385,6 @@ pub enum VirtualKeyCode {
LAlt,
LBracket,
LControl,
LMenu,
LShift,
LWin,
Mail,
@@ -408,7 +409,6 @@ pub enum VirtualKeyCode {
RAlt,
RBracket,
RControl,
RMenu,
RShift,
RWin,
Semicolon,

View File

@@ -104,12 +104,16 @@ impl Icon {
#[cfg(feature = "icon_loading")]
/// Loads an `Icon` from the path of an image on the filesystem.
///
/// Requires the `icon_loading` feature.
pub fn from_path<P: AsRef<Path>>(path: P) -> image::ImageResult<Self> {
image::open(path).map(Into::into)
}
#[cfg(feature = "icon_loading")]
/// Loads an `Icon` from anything implementing `BufRead` and `Seek`.
///
/// Requires the `icon_loading` feature.
pub fn from_reader<R: BufRead + Seek>(
reader: R,
format: image::ImageFormat,
@@ -120,12 +124,16 @@ impl Icon {
#[cfg(feature = "icon_loading")]
/// Loads an `Icon` from the unprocessed bytes of an image file.
/// Uses heuristics to determine format.
///
/// Requires the `icon_loading` feature.
pub fn from_bytes(bytes: &[u8]) -> image::ImageResult<Self> {
image::load_from_memory(bytes).map(Into::into)
}
#[cfg(feature = "icon_loading")]
/// Loads an `Icon` from the unprocessed bytes of an image.
///
/// Requires the `icon_loading` feature.
pub fn from_bytes_with_format(
bytes: &[u8],
format: image::ImageFormat,
@@ -135,6 +143,7 @@ impl Icon {
}
#[cfg(feature = "icon_loading")]
/// Requires the `icon_loading` feature.
impl From<image::DynamicImage> for Icon {
fn from(image: image::DynamicImage) -> Self {
use image::{GenericImage, Pixel};
@@ -148,6 +157,7 @@ impl From<image::DynamicImage> for Icon {
}
#[cfg(feature = "icon_loading")]
/// Requires the `icon_loading` feature.
impl From<image::RgbaImage> for Icon {
fn from(buf: image::RgbaImage) -> Self {
let (width, height) = buf.dimensions();

View File

@@ -15,7 +15,7 @@
//! - Calling `Window::new(&events_loop)`.
//! - Calling `let builder = WindowBuilder::new()` then `builder.build(&events_loop)`.
//!
//! The first way is the simpliest way and will give you default values for everything.
//! The first way is the simplest way and will give you default values for everything.
//!
//! The second way allows you to customize the way your window will look and behave by modifying
//! the fields of the `WindowBuilder` object before you create the window.
@@ -35,14 +35,18 @@
//!
//! ```no_run
//! use winit::{Event, WindowEvent};
//! use winit::dpi::LogicalSize;
//! # use winit::EventsLoop;
//! # let mut events_loop = EventsLoop::new();
//!
//! loop {
//! events_loop.poll_events(|event| {
//! match event {
//! Event::WindowEvent { event: WindowEvent::Resized(w, h), .. } => {
//! println!("The window was resized to {}x{}", w, h);
//! Event::WindowEvent {
//! event: WindowEvent::Resized(LogicalSize { width, height }),
//! ..
//! } => {
//! println!("The window was resized to {}x{}", width, height);
//! },
//! _ => ()
//! }
@@ -80,15 +84,16 @@
//! to create an OpenGL/Vulkan/DirectX/Metal/etc. context that will draw on the window.
//!
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "windows"))]
#[allow(unused_imports)]
#[macro_use]
extern crate lazy_static;
extern crate libc;
#[macro_use]
extern crate log;
#[cfg(feature = "icon_loading")]
extern crate image;
#[cfg(target_os = "windows")]
#[macro_use]
extern crate winapi;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[macro_use]
@@ -108,14 +113,16 @@ extern crate percent_encoding;
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
extern crate smithay_client_toolkit as sctk;
pub(crate) use dpi::*; // TODO: Actually change the imports throughout the codebase.
pub use events::*;
pub use window::{AvailableMonitorsIter, MonitorId};
pub use icon::*;
mod platform;
pub mod dpi;
mod events;
mod window;
mod icon;
mod platform;
mod window;
pub mod os;
@@ -229,6 +236,11 @@ impl EventsLoop {
/// Calls `callback` every time an event is received. If no event is available, sleeps the
/// current thread and waits for an event. If the callback returns `ControlFlow::Break` then
/// `run_forever` will immediately return.
///
/// # Danger!
///
/// The callback is run after *every* event, so if its execution time is non-trivial the event queue may not empty
/// at a sufficient rate. Rendering in the callback with vsync enabled **will** cause significant lag.
#[inline]
pub fn run_forever<F>(&mut self, callback: F)
where F: FnMut(Event) -> ControlFlow
@@ -345,7 +357,6 @@ pub enum MouseCursor {
/// Cursor showing that something cannot be done.
NotAllowed,
ContextMenu,
NoneCursor,
Cell,
VerticalText,
Alias,
@@ -375,41 +386,35 @@ pub enum MouseCursor {
RowResize,
}
/// Describes how winit handles the cursor.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum CursorState {
/// Normal cursor behavior.
Normal,
/// The cursor will be invisible when over the window.
Hide,
/// Grabs the mouse cursor. The cursor's motion will be confined to this
/// window and the window has exclusive access to further events regarding
/// the cursor.
///
/// This is useful for first-person cameras for example.
Grab,
impl Default for MouseCursor {
fn default() -> Self {
MouseCursor::Default
}
}
/// Attributes to use when creating a window.
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct WindowAttributes {
/// The dimensions of the window. If this is `None`, some platform-specific dimensions will be
/// used.
///
/// The default is `None`.
pub dimensions: Option<(u32, u32)>,
pub dimensions: Option<LogicalSize>,
/// The minimum dimensions a window can be, If this is `None`, the window will have no minimum dimensions (aside from reserved).
///
/// The default is `None`.
pub min_dimensions: Option<(u32, u32)>,
pub min_dimensions: Option<LogicalSize>,
/// The maximum dimensions a window can be, If this is `None`, the maximum will have no maximum or will be set to the primary monitor's dimensions by the platform.
///
/// The default is `None`.
pub max_dimensions: Option<(u32, u32)>,
pub max_dimensions: Option<LogicalSize>,
/// Whether the window is resizable or not.
///
/// The default is `true`.
pub resizable: bool,
/// Whether the window should be set as fullscreen upon creation.
///
@@ -442,6 +447,11 @@ pub struct WindowAttributes {
/// The default is `true`.
pub decorations: bool,
/// Whether the window should always be on top of other windows.
///
/// The default is `false`.
pub always_on_top: bool,
/// The window icon.
///
/// The default is `None`.
@@ -459,12 +469,14 @@ impl Default for WindowAttributes {
dimensions: None,
min_dimensions: None,
max_dimensions: None,
resizable: true,
title: "winit window".to_owned(),
maximized: false,
fullscreen: None,
visible: true,
transparent: false,
decorations: true,
always_on_top: false,
window_icon: None,
multitouch: false,
}

43
src/os/ios.rs Normal file
View File

@@ -0,0 +1,43 @@
#![cfg(target_os = "ios")]
use std::os::raw::c_void;
use {MonitorId, Window};
/// Additional methods on `Window` that are specific to iOS.
pub trait WindowExt {
/// Returns a pointer to the `UIWindow` that is used by this window.
///
/// The pointer will become invalid when the `Window` is destroyed.
fn get_uiwindow(&self) -> *mut c_void;
/// Returns a pointer to the `UIView` that is used by this window.
///
/// The pointer will become invalid when the `Window` is destroyed.
fn get_uiview(&self) -> *mut c_void;
}
impl WindowExt for Window {
#[inline]
fn get_uiwindow(&self) -> *mut c_void {
self.window.get_uiwindow() as _
}
#[inline]
fn get_uiview(&self) -> *mut c_void {
self.window.get_uiview() as _
}
}
/// Additional methods on `MonitorId` that are specific to iOS.
pub trait MonitorIdExt {
/// Returns a pointer to the `UIScreen` that is used by this monitor.
fn get_uiscreen(&self) -> *mut c_void;
}
impl MonitorIdExt for MonitorId {
#[inline]
fn get_uiscreen(&self) -> *mut c_void {
self.inner.get_uiscreen() as _
}
}

View File

@@ -3,7 +3,7 @@
use std::convert::From;
use std::os::raw::c_void;
use cocoa::appkit::NSApplicationActivationPolicy;
use {MonitorId, Window, WindowBuilder};
use {LogicalSize, MonitorId, Window, WindowBuilder};
/// Additional methods on `Window` that are specific to MacOS.
pub trait WindowExt {
@@ -71,64 +71,72 @@ impl From<ActivationPolicy> for NSApplicationActivationPolicy {
/// - `with_titlebar_buttons_hidden`
/// - `with_fullsize_content_view`
pub trait WindowBuilderExt {
/// Sets the activation policy for the window being built.
fn with_activation_policy(self, activation_policy: ActivationPolicy) -> WindowBuilder;
/// Enables click-and-drag behavior for the entire window, not just the titlebar.
fn with_movable_by_window_background(self, movable_by_window_background: bool) -> WindowBuilder;
/// Makes the titlebar transparent and allows the content to appear behind it.
fn with_titlebar_transparent(self, titlebar_transparent: bool) -> WindowBuilder;
/// Hides the window title.
fn with_title_hidden(self, title_hidden: bool) -> WindowBuilder;
/// Hides the window titlebar.
fn with_titlebar_hidden(self, titlebar_hidden: bool) -> WindowBuilder;
/// Hides the window titlebar buttons.
fn with_titlebar_buttons_hidden(self, titlebar_buttons_hidden: bool) -> WindowBuilder;
/// Makes the window content appear behind the titlebar.
fn with_fullsize_content_view(self, fullsize_content_view: bool) -> WindowBuilder;
/// Build window with `resizeIncrements` property. Values must not be 0.
fn with_resize_increments(self, increments: LogicalSize) -> WindowBuilder;
}
impl WindowBuilderExt for WindowBuilder {
/// Sets the activation policy for the window being built
#[inline]
fn with_activation_policy(mut self, activation_policy: ActivationPolicy) -> WindowBuilder {
self.platform_specific.activation_policy = activation_policy;
self
}
/// Enables click-and-drag behavior for the entire window, not just the titlebar
#[inline]
fn with_movable_by_window_background(mut self, movable_by_window_background: bool) -> WindowBuilder {
self.platform_specific.movable_by_window_background = movable_by_window_background;
self
}
/// Makes the titlebar transparent and allows the content to appear behind it
#[inline]
fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> WindowBuilder {
self.platform_specific.titlebar_transparent = titlebar_transparent;
self
}
/// Hides the window titlebar
#[inline]
fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> WindowBuilder {
self.platform_specific.titlebar_hidden = titlebar_hidden;
self
}
/// Hides the window titlebar buttons
#[inline]
fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> WindowBuilder {
self.platform_specific.titlebar_buttons_hidden = titlebar_buttons_hidden;
self
}
/// Hides the window title
#[inline]
fn with_title_hidden(mut self, title_hidden: bool) -> WindowBuilder {
self.platform_specific.title_hidden = title_hidden;
self
}
/// Makes the window content appear behind the titlebar
#[inline]
fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> WindowBuilder {
self.platform_specific.fullsize_content_view = fullsize_content_view;
self
}
#[inline]
fn with_resize_increments(mut self, increments: LogicalSize) -> WindowBuilder {
self.platform_specific.resize_increments = Some(increments.into());
self
}
}
/// Additional methods on `MonitorId` that are specific to MacOS.

View File

@@ -3,6 +3,7 @@
//! Contains the follow modules:
//!
//! - `android`
//! - `ios`
//! - `macos`
//! - `unix`
//! - `windows`
@@ -10,6 +11,7 @@
//! However only the module corresponding to the platform you're compiling to will be available.
//!
pub mod android;
pub mod ios;
pub mod macos;
pub mod unix;
pub mod windows;

View File

@@ -1,14 +1,20 @@
#![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 EventsLoop;
use MonitorId;
use Window;
use platform::EventsLoop as LinuxEventsLoop;
use platform::Window as LinuxWindow;
use WindowBuilder;
use std::sync::Arc;
use {
EventsLoop,
LogicalSize,
MonitorId,
Window,
WindowBuilder,
};
use platform::{
EventsLoop as LinuxEventsLoop,
Window as LinuxWindow,
};
use platform::x11::XConnection;
use platform::x11::ffi::XVisualInfo;
@@ -17,6 +23,7 @@ use platform::x11::ffi::XVisualInfo;
pub use platform::x11;
pub use platform::XNotSupported;
pub use platform::x11::util::WindowType as XWindowType;
/// Additional methods on `EventsLoop` that are specific to Linux.
pub trait EventsLoopExt {
@@ -71,6 +78,7 @@ impl EventsLoopExt for EventsLoop {
}
#[inline]
#[doc(hidden)]
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> {
self.events_loop.x_connection().cloned()
}
@@ -92,9 +100,11 @@ pub trait WindowExt {
fn get_xlib_screen_id(&self) -> Option<raw::c_int>;
#[doc(hidden)]
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>>;
fn send_xim_spot(&self, x: i16, y: i16);
/// Set window urgency hint (`XUrgencyHint`). Only relevant on X.
fn set_urgent(&self, is_urgent: bool);
/// This function returns the underlying `xcb_connection_t` of an xlib `Display`.
///
@@ -144,6 +154,7 @@ impl WindowExt for Window {
}
}
#[inline]
fn get_xlib_screen_id(&self) -> Option<raw::c_int> {
match self.window {
LinuxWindow::X(ref w) => Some(w.get_xlib_screen_id()),
@@ -151,6 +162,8 @@ impl WindowExt for Window {
}
}
#[inline]
#[doc(hidden)]
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> {
match self.window {
LinuxWindow::X(ref w) => Some(w.get_xlib_xconnection()),
@@ -158,6 +171,7 @@ impl WindowExt for Window {
}
}
#[inline]
fn get_xcb_connection(&self) -> Option<*mut raw::c_void> {
match self.window {
LinuxWindow::X(ref w) => Some(w.get_xcb_connection()),
@@ -165,9 +179,10 @@ impl WindowExt for Window {
}
}
fn send_xim_spot(&self, x: i16, y: i16) {
#[inline]
fn set_urgent(&self, is_urgent: bool) {
if let LinuxWindow::X(ref w) = self.window {
w.send_xim_spot(x, y);
w.set_urgent(is_urgent);
}
}
@@ -198,10 +213,16 @@ pub trait WindowBuilderExt {
fn with_x11_visual<T>(self, visual_infos: *const T) -> WindowBuilder;
fn with_x11_screen(self, screen_id: i32) -> WindowBuilder;
/// Build window with `WM_CLASS` hint; defaults to the name of the binary. Only relevant on X11.
fn with_class(self, class: String, instance: String) -> WindowBuilder;
/// Build window with override-redirect flag; defaults to false. Only relevant on X11.
fn with_override_redirect(self, override_redirect: bool) -> WindowBuilder;
/// Build window with `_NET_WM_WINDOW_TYPE` hint; defaults to `Normal`. Only relevant on X11.
fn with_x11_window_type(self, x11_window_type: XWindowType) -> WindowBuilder;
/// Build window with resize increment hint. Only implemented on X11.
fn with_resize_increments(self, width_inc: i32, height_inc: i32) -> WindowBuilder;
fn with_resize_increments(self, increments: LogicalSize) -> WindowBuilder;
/// Build window with base size hint. Only implemented on X11.
fn with_base_size(self, base_width: i32, base_height: i32) -> WindowBuilder;
fn with_base_size(self, base_size: LogicalSize) -> WindowBuilder;
}
impl WindowBuilderExt for WindowBuilder {
@@ -220,14 +241,32 @@ impl WindowBuilderExt for WindowBuilder {
}
#[inline]
fn with_resize_increments(mut self, width_inc: i32, height_inc: i32) -> WindowBuilder {
self.platform_specific.resize_increments = Some((width_inc, height_inc));
fn with_class(mut self, instance: String, class: String) -> WindowBuilder {
self.platform_specific.class = Some((instance, class));
self
}
#[inline]
fn with_base_size(mut self, base_width: i32, base_height: i32) -> WindowBuilder {
self.platform_specific.base_size = Some((base_width, base_height));
fn with_override_redirect(mut self, override_redirect: bool) -> WindowBuilder {
self.platform_specific.override_redirect = override_redirect;
self
}
#[inline]
fn with_x11_window_type(mut self, x11_window_type: XWindowType) -> WindowBuilder {
self.platform_specific.x11_window_type = x11_window_type;
self
}
#[inline]
fn with_resize_increments(mut self, increments: LogicalSize) -> WindowBuilder {
self.platform_specific.resize_increments = Some(increments.into());
self
}
#[inline]
fn with_base_size(mut self, base_size: LogicalSize) -> WindowBuilder {
self.platform_specific.base_size = Some(base_size.into());
self
}
}

View File

@@ -5,7 +5,25 @@ use std::os::raw::c_void;
use libc;
use winapi::shared::windef::HWND;
use {DeviceId, Icon, MonitorId, Window, WindowBuilder};
use {DeviceId, EventsLoop, Icon, MonitorId, Window, WindowBuilder};
use platform::EventsLoop as WindowsEventsLoop;
/// Additional methods on `EventsLoop` that are specific to Windows.
pub trait EventsLoopExt {
/// By default, winit on Windows will attempt to enable process-wide DPI awareness. If that's
/// undesirable, you can create an `EventsLoop` using this function instead.
fn new_dpi_unaware() -> Self where Self: Sized;
}
impl EventsLoopExt for EventsLoop {
#[inline]
fn new_dpi_unaware() -> Self {
EventsLoop {
events_loop: WindowsEventsLoop::with_dpi_awareness(false),
_marker: ::std::marker::PhantomData,
}
}
}
/// Additional methods on `Window` that are specific to Windows.
pub trait WindowExt {
@@ -37,6 +55,9 @@ pub trait WindowBuilderExt {
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
fn with_taskbar_icon(self, taskbar_icon: Option<Icon>) -> WindowBuilder;
/// This sets `WS_EX_NOREDIRECTIONBITMAP`.
fn with_no_redirection_bitmap(self, flag: bool) -> WindowBuilder;
}
impl WindowBuilderExt for WindowBuilder {
@@ -51,6 +72,12 @@ impl WindowBuilderExt for WindowBuilder {
self.platform_specific.taskbar_icon = taskbar_icon;
self
}
#[inline]
fn with_no_redirection_bitmap(mut self, flag: bool) -> WindowBuilder {
self.platform_specific.no_redirection_bitmap = flag;
self
}
}
/// Additional methods on `MonitorId` that are specific to Windows.

View File

@@ -2,24 +2,33 @@
extern crate android_glue;
use libc;
use std::sync::mpsc::{Receiver, channel};
mod ffi;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::fmt;
use std::os::raw::c_void;
use {CreationError, Event, WindowEvent, MouseCursor};
use std::sync::mpsc::{Receiver, channel};
use {
CreationError,
Event,
LogicalPosition,
LogicalSize,
MouseCursor,
PhysicalPosition,
PhysicalSize,
WindowAttributes,
WindowEvent,
WindowId as RootWindowId,
};
use CreationError::OsError;
use WindowId as RootWindowId;
use events::{Touch, TouchPhase};
use window::MonitorId as RootMonitorId;
use std::collections::VecDeque;
use std::cell::RefCell;
use CursorState;
use WindowAttributes;
pub struct EventsLoop {
event_rx: Receiver<android_glue::Event>,
suspend_callback: RefCell<Option<Box<Fn(bool) -> ()>>>
suspend_callback: RefCell<Option<Box<Fn(bool) -> ()>>>,
}
#[derive(Clone)]
@@ -31,13 +40,13 @@ impl EventsLoop {
android_glue::add_sender(tx);
EventsLoop {
event_rx: rx,
suspend_callback: RefCell::new(None),
suspend_callback: Default::default(),
}
}
#[inline]
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
let mut rb = VecDeque::new();
let mut rb = VecDeque::with_capacity(1);
rb.push_back(MonitorId);
rb
}
@@ -47,14 +56,17 @@ impl EventsLoop {
MonitorId
}
pub fn poll_events<F>(&mut self, mut callback: F)
where F: FnMut(::Event)
{
while let Ok(event) = self.event_rx.try_recv() {
let e = match event{
android_glue::Event::EventMotion(motion) => {
let dpi_factor = MonitorId.get_hidpi_factor();
let location = LogicalPosition::from_physical(
(motion.x as f64, motion.y as f64),
dpi_factor,
);
Some(Event::WindowEvent {
window_id: RootWindowId(WindowId),
event: WindowEvent::Touch(Touch {
@@ -64,7 +76,7 @@ impl EventsLoop {
android_glue::MotionAction::Up => TouchPhase::Ended,
android_glue::MotionAction::Cancel => TouchPhase::Cancelled,
},
location: (motion.x as f64, motion.y as f64),
location,
id: motion.pointer_id as u64,
device_id: DEVICE_ID,
}),
@@ -91,11 +103,12 @@ impl EventsLoop {
if native_window.is_null() {
None
} else {
let w = unsafe { ffi::ANativeWindow_getWidth(native_window as *const _) } as u32;
let h = unsafe { ffi::ANativeWindow_getHeight(native_window as *const _) } as u32;
let dpi_factor = MonitorId.get_hidpi_factor();
let physical_size = MonitorId.get_dimensions();
let size = LogicalSize::from_physical(physical_size, dpi_factor);
Some(Event::WindowEvent {
window_id: RootWindowId(WindowId),
event: WindowEvent::Resized(w, h),
event: WindowEvent::Resized(size),
})
}
},
@@ -167,7 +180,26 @@ pub struct Window {
#[derive(Clone)]
pub struct MonitorId;
mod ffi;
impl fmt::Debug for MonitorId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
#[derive(Debug)]
struct MonitorId {
name: Option<String>,
dimensions: PhysicalSize,
position: PhysicalPosition,
hidpi_factor: f64,
}
let monitor_id_proxy = MonitorId {
name: self.get_name(),
dimensions: self.get_dimensions(),
position: self.get_position(),
hidpi_factor: self.get_hidpi_factor(),
};
monitor_id_proxy.fmt(f)
}
}
impl MonitorId {
#[inline]
@@ -176,21 +208,24 @@ impl MonitorId {
}
#[inline]
pub fn get_dimensions(&self) -> (u32, u32) {
pub fn get_dimensions(&self) -> PhysicalSize {
unsafe {
let window = android_glue::get_native_window();
(ffi::ANativeWindow_getWidth(window) as u32, ffi::ANativeWindow_getHeight(window) as u32)
(
ffi::ANativeWindow_getWidth(window) as f64,
ffi::ANativeWindow_getHeight(window) as f64,
).into()
}
}
#[inline]
pub fn get_position(&self) -> (i32, i32) {
pub fn get_position(&self) -> PhysicalPosition {
// Android assumes single screen
(0, 0)
(0, 0).into()
}
#[inline]
pub fn get_hidpi_factor(&self) -> f32 {
pub fn get_hidpi_factor(&self) -> f64 {
1.0
}
}
@@ -205,10 +240,6 @@ impl Window {
_: PlatformSpecificWindowBuilderAttributes)
-> Result<Window, CreationError>
{
// not implemented
assert!(win_attribs.min_dimensions.is_none());
assert!(win_attribs.max_dimensions.is_none());
let native_window = unsafe { android_glue::get_native_window() };
if native_window.is_null() {
return Err(OsError(format!("Android's native window is null")));
@@ -228,93 +259,106 @@ impl Window {
#[inline]
pub fn set_title(&self, _: &str) {
// N/A
}
#[inline]
pub fn show(&self) {
// N/A
}
#[inline]
pub fn hide(&self) {
// N/A
}
#[inline]
pub fn get_position(&self) -> Option<(i32, i32)> {
pub fn get_position(&self) -> Option<LogicalPosition> {
// N/A
None
}
#[inline]
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
// N/A
None
}
#[inline]
pub fn set_position(&self, _x: i32, _y: i32) {
pub fn set_position(&self, _position: LogicalPosition) {
// N/A
}
#[inline]
pub fn set_min_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
pub fn set_min_dimensions(&self, _dimensions: Option<LogicalSize>) {
// N/A
}
#[inline]
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
pub fn set_max_dimensions(&self, _dimensions: Option<LogicalSize>) {
// N/A
}
#[inline]
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
pub fn set_resizable(&self, _resizable: bool) {
// N/A
}
#[inline]
pub fn get_inner_size(&self) -> Option<LogicalSize> {
if self.native_window.is_null() {
None
} else {
Some((
unsafe { ffi::ANativeWindow_getWidth(self.native_window as *const _) } as u32,
unsafe { ffi::ANativeWindow_getHeight(self.native_window as *const _) } as u32
))
let dpi_factor = self.get_hidpi_factor();
let physical_size = self.get_current_monitor().get_dimensions();
Some(LogicalSize::from_physical(physical_size, dpi_factor))
}
}
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
pub fn get_outer_size(&self) -> Option<LogicalSize> {
self.get_inner_size()
}
#[inline]
pub fn set_inner_size(&self, _x: u32, _y: u32) {
pub fn set_inner_size(&self, _size: LogicalSize) {
// N/A
}
#[inline]
pub fn platform_display(&self) -> *mut libc::c_void {
unimplemented!();
}
#[inline]
pub fn platform_window(&self) -> *mut libc::c_void {
unimplemented!()
pub fn get_hidpi_factor(&self) -> f64 {
self.get_current_monitor().get_hidpi_factor()
}
#[inline]
pub fn set_cursor(&self, _: MouseCursor) {
// N/A
}
#[inline]
pub fn set_cursor_state(&self, _state: CursorState) -> Result<(), String> {
Ok(())
pub fn grab_cursor(&self, _grab: bool) -> Result<(), String> {
Err("Cursor grabbing is not possible on Android.".to_owned())
}
#[inline]
pub fn hidpi_factor(&self) -> f32 {
1.0
pub fn hide_cursor(&self, _hide: bool) {
// N/A
}
#[inline]
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
Ok(())
pub fn set_cursor_position(&self, _position: LogicalPosition) -> Result<(), String> {
Err("Setting cursor position is not possible on Android.".to_owned())
}
#[inline]
pub fn set_maximized(&self, _maximized: bool) {
// N/A
// Android has single screen maximized apps so nothing to do
}
#[inline]
pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) {
// N/A
// Android has single screen maximized apps so nothing to do
}
@@ -323,16 +367,39 @@ impl Window {
// N/A
}
#[inline]
pub fn set_always_on_top(&self, _always_on_top: bool) {
// N/A
}
#[inline]
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
// N/A
}
#[inline]
pub fn get_current_monitor(&self) -> RootMonitorId {
RootMonitorId{inner: MonitorId}
pub fn set_ime_spot(&self, _spot: LogicalPosition) {
// N/A
}
#[inline]
pub fn get_current_monitor(&self) -> RootMonitorId {
RootMonitorId { inner: MonitorId }
}
#[inline]
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
let mut rb = VecDeque::with_capacity(1);
rb.push_back(MonitorId);
rb
}
#[inline]
pub fn get_primary_monitor(&self) -> MonitorId {
MonitorId
}
#[inline]
pub fn id(&self) -> WindowId {
WindowId
}

View File

@@ -1,6 +1,4 @@
#![allow(dead_code)]
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(dead_code, non_camel_case_types, non_snake_case)]
use std::os::raw::{c_int, c_char, c_void, c_ulong, c_double, c_long, c_ushort};
#[cfg(test)]

View File

@@ -2,16 +2,22 @@
mod ffi;
use std::mem;
use std::os::raw::{c_char, c_void, c_double, c_ulong, c_int};
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Mutex, Arc};
use std::{mem, ptr, str};
use std::cell::RefCell;
use std::collections::VecDeque;
use std::os::raw::{c_char, c_void, c_double, c_ulong, c_int};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Mutex, Arc};
use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
use window::MonitorId as RootMonitorId;
const DOCUMENT_NAME: &'static str = "#document\0";
fn get_hidpi_factor() -> f64 {
unsafe { ffi::emscripten_get_device_pixel_ratio() as f64 }
}
#[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes;
@@ -24,7 +30,7 @@ pub struct DeviceId;
#[derive(Clone, Default)]
pub struct PlatformSpecificHeadlessBuilderAttributes;
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct MonitorId;
impl MonitorId {
@@ -34,18 +40,18 @@ impl MonitorId {
}
#[inline]
pub fn get_position(&self) -> (i32, i32) {
pub fn get_position(&self) -> PhysicalPosition {
unimplemented!()
}
#[inline]
pub fn get_dimensions(&self) -> (u32, u32) {
(0, 0)
pub fn get_dimensions(&self) -> PhysicalSize {
(0, 0).into()
}
#[inline]
pub fn get_hidpi_factor(&self) -> f32 {
1.0
pub fn get_hidpi_factor(&self) -> f64 {
get_hidpi_factor()
}
}
@@ -90,17 +96,19 @@ impl EventsLoop {
}
}
#[inline]
pub fn interrupt(&self) {
self.interrupted.store(true, Ordering::Relaxed);
}
#[inline]
pub fn create_proxy(&self) -> EventsLoopProxy {
unimplemented!()
}
#[inline]
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
let mut list = VecDeque::new();
let mut list = VecDeque::with_capacity(1);
list.push_back(MonitorId);
list
}
@@ -115,7 +123,7 @@ impl EventsLoop {
{
let ref mut window = *self.window.lock().unwrap();
if let &mut Some(ref mut window) = window {
while let Some(event) = window.events.borrow_mut().pop_front() {
while let Some(event) = window.events.lock().unwrap().pop_front() {
callback(event)
}
}
@@ -142,9 +150,10 @@ impl EventsLoop {
pub struct WindowId(usize);
pub struct Window2 {
cursor_state: Mutex<::CursorState>,
cursor_grabbed: Mutex<bool>,
cursor_hidden: Mutex<bool>,
is_fullscreen: bool,
events: Box<RefCell<VecDeque<::Event>>>,
events: Box<Mutex<VecDeque<::Event>>>,
}
pub struct Window {
@@ -176,7 +185,7 @@ extern "C" fn mouse_callback(
event_queue: *mut c_void) -> ffi::EM_BOOL
{
unsafe {
let queue: &RefCell<VecDeque<::Event>> = mem::transmute(event_queue);
let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue);
let modifiers = ::ModifiersState {
shift: (*event).shiftKey == ffi::EM_TRUE,
@@ -187,15 +196,20 @@ extern "C" fn mouse_callback(
match event_type {
ffi::EMSCRIPTEN_EVENT_MOUSEMOVE => {
queue.borrow_mut().push_back(::Event::WindowEvent {
let dpi_factor = get_hidpi_factor();
let position = LogicalPosition::from_physical(
((*event).canvasX as f64, (*event).canvasY as f64),
dpi_factor,
);
queue.lock().unwrap().push_back(::Event::WindowEvent {
window_id: ::WindowId(WindowId(0)),
event: ::WindowEvent::CursorMoved {
device_id: ::DeviceId(DeviceId),
position: ((*event).canvasX as f64, (*event).canvasY as f64),
position,
modifiers: modifiers,
}
});
queue.borrow_mut().push_back(::Event::DeviceEvent {
queue.lock().unwrap().push_back(::Event::DeviceEvent {
device_id: ::DeviceId(DeviceId),
event: ::DeviceEvent::MouseMotion {
delta: ((*event).movementX as f64, (*event).movementY as f64),
@@ -215,7 +229,7 @@ extern "C" fn mouse_callback(
ffi::EMSCRIPTEN_EVENT_MOUSEUP => ::ElementState::Released,
_ => unreachable!(),
};
queue.borrow_mut().push_back(::Event::WindowEvent {
queue.lock().unwrap().push_back(::Event::WindowEvent {
window_id: ::WindowId(WindowId(0)),
event: ::WindowEvent::MouseInput {
device_id: ::DeviceId(DeviceId),
@@ -238,7 +252,7 @@ extern "C" fn keyboard_callback(
event_queue: *mut c_void) -> ffi::EM_BOOL
{
unsafe {
let queue: &RefCell<VecDeque<::Event>> = mem::transmute(event_queue);
let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue);
let modifiers = ::ModifiersState {
shift: (*event).shiftKey == ffi::EM_TRUE,
@@ -249,7 +263,7 @@ extern "C" fn keyboard_callback(
match event_type {
ffi::EMSCRIPTEN_EVENT_KEYDOWN => {
queue.borrow_mut().push_back(::Event::WindowEvent {
queue.lock().unwrap().push_back(::Event::WindowEvent {
window_id: ::WindowId(WindowId(0)),
event: ::WindowEvent::KeyboardInput {
device_id: ::DeviceId(DeviceId),
@@ -263,7 +277,7 @@ extern "C" fn keyboard_callback(
});
},
ffi::EMSCRIPTEN_EVENT_KEYUP => {
queue.borrow_mut().push_back(::Event::WindowEvent {
queue.lock().unwrap().push_back(::Event::WindowEvent {
window_id: ::WindowId(WindowId(0)),
event: ::WindowEvent::KeyboardInput {
device_id: ::DeviceId(DeviceId),
@@ -289,7 +303,7 @@ extern fn touch_callback(
event_queue: *mut c_void) -> ffi::EM_BOOL
{
unsafe {
let queue: &RefCell<VecDeque<::Event>> = mem::transmute(event_queue);
let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue);
let phase = match event_type {
ffi::EMSCRIPTEN_EVENT_TOUCHSTART => ::TouchPhase::Started,
@@ -302,13 +316,18 @@ extern fn touch_callback(
for touch in 0..(*event).numTouches as usize {
let touch = (*event).touches[touch];
if touch.isChanged == ffi::EM_TRUE {
queue.borrow_mut().push_back(::Event::WindowEvent {
let dpi_factor = get_hidpi_factor();
let location = LogicalPosition::from_physical(
(touch.canvasX as f64, touch.canvasY as f64),
dpi_factor,
);
queue.lock().unwrap().push_back(::Event::WindowEvent {
window_id: ::WindowId(WindowId(0)),
event: ::WindowEvent::Touch(::Touch {
device_id: ::DeviceId(DeviceId),
phase,
id: touch.identifier as u64,
location: (touch.canvasX as f64, touch.canvasY as f64),
location,
}),
});
}
@@ -356,8 +375,9 @@ impl Window {
}
let w = Window2 {
cursor_state: Mutex::new(::CursorState::Normal),
events: Box::new(RefCell::new(VecDeque::new())),
cursor_grabbed: Default::default(),
cursor_hidden: Default::default(),
events: Default::default(),
is_fullscreen: attribs.fullscreen.is_some(),
};
@@ -395,8 +415,8 @@ impl Window {
em_try(ffi::emscripten_set_fullscreenchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, Some(fullscreen_callback)))
.map_err(|e| ::CreationError::OsError(e))?;
}
} else if let Some((w, h)) = attribs.dimensions {
window.set_inner_size(w, h);
} else if let Some(size) = attribs.dimensions {
window.set_inner_size(size);
}
*events_loop.window.lock().unwrap() = Some(window.window.clone());
@@ -413,22 +433,22 @@ impl Window {
}
#[inline]
pub fn get_position(&self) -> Option<(i32, i32)> {
Some((0, 0))
pub fn get_position(&self) -> Option<LogicalPosition> {
Some((0, 0).into())
}
#[inline]
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
Some((0, 0))
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
Some((0, 0).into())
}
#[inline]
pub fn set_position(&self, _: i32, _: i32) {
pub fn set_position(&self, _: LogicalPosition) {
}
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
#[inline]
pub fn get_inner_size(&self) -> Option<LogicalSize> {
unsafe {
use std::{mem, ptr};
let mut width = 0;
let mut height = 0;
let mut fullscreen = 0;
@@ -438,94 +458,109 @@ impl Window {
{
None
} else {
Some((width as u32, height as u32))
let dpi_factor = self.get_hidpi_factor();
let logical = LogicalSize::from_physical((width as u32, height as u32), dpi_factor);
Some(logical)
}
}
}
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
pub fn get_outer_size(&self) -> Option<LogicalSize> {
self.get_inner_size()
}
#[inline]
pub fn set_inner_size(&self, width: u32, height: u32) {
pub fn set_inner_size(&self, size: LogicalSize) {
unsafe {
use std::ptr;
ffi::emscripten_set_element_css_size(ptr::null(), width as c_double, height
as c_double);
let dpi_factor = self.get_hidpi_factor();
let physical = PhysicalSize::from_logical(size, dpi_factor);
let (width, height): (u32, u32) = physical.into();
ffi::emscripten_set_element_css_size(
ptr::null(),
width as c_double,
height as c_double,
);
}
}
#[inline]
pub fn set_min_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
#[inline]
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
#[inline]
pub fn show(&self) {}
#[inline]
pub fn hide(&self) {}
#[inline]
pub fn platform_display(&self) -> *mut ::libc::c_void {
unimplemented!()
pub fn set_min_dimensions(&self, _dimensions: Option<LogicalSize>) {
// N/A
}
#[inline]
pub fn platform_window(&self) -> *mut ::libc::c_void {
unimplemented!()
pub fn set_max_dimensions(&self, _dimensions: Option<LogicalSize>) {
// N/A
}
#[inline]
pub fn set_cursor(&self, _cursor: ::MouseCursor) {}
pub fn set_resizable(&self, _resizable: bool) {
// N/A
}
#[inline]
pub fn set_cursor_state(&self, state: ::CursorState) -> Result<(), String> {
pub fn show(&self) {
// N/A
}
#[inline]
pub fn hide(&self) {
// N/A
}
#[inline]
pub fn set_cursor(&self, _cursor: ::MouseCursor) {
// N/A
}
#[inline]
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
let mut grabbed_lock = self.window.cursor_grabbed.lock().unwrap();
if grab == *grabbed_lock { return Ok(()); }
unsafe {
use ::CursorState::*;
let mut old_state = self.window.cursor_state.lock().unwrap();
if state == *old_state {
return Ok(());
if grab {
em_try(ffi::emscripten_set_pointerlockchange_callback(
ptr::null(),
0 as *mut c_void,
ffi::EM_FALSE,
Some(pointerlockchange_callback),
))?;
em_try(ffi::emscripten_request_pointerlock(ptr::null(), ffi::EM_TRUE))?;
} else {
em_try(ffi::emscripten_set_pointerlockchange_callback(
ptr::null(),
0 as *mut c_void,
ffi::EM_FALSE,
None,
))?;
em_try(ffi::emscripten_exit_pointerlock())?;
}
// Set or unset grab callback
match state {
Hide | Normal => em_try(ffi::emscripten_set_pointerlockchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, None))?,
Grab => em_try(ffi::emscripten_set_pointerlockchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, Some(pointerlockchange_callback)))?,
}
// Go back to normal cursor state
match *old_state {
Hide => show_mouse(),
Grab => em_try(ffi::emscripten_exit_pointerlock())?,
Normal => (),
}
// Set cursor from normal cursor state
match state {
Hide => ffi::emscripten_hide_mouse(),
Grab => em_try(ffi::emscripten_request_pointerlock(ptr::null(), ffi::EM_TRUE))?,
Normal => (),
}
// Update
*old_state = state;
Ok(())
}
*grabbed_lock = grab;
Ok(())
}
#[inline]
pub fn hidpi_factor(&self) -> f32 {
unsafe { ffi::emscripten_get_device_pixel_ratio() as f32 }
pub fn hide_cursor(&self, hide: bool) {
let mut hidden_lock = self.window.cursor_hidden.lock().unwrap();
if hide == *hidden_lock { return; }
if hide {
unsafe { ffi::emscripten_hide_mouse() };
} else {
show_mouse();
}
*hidden_lock = hide;
}
#[inline]
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
Err(())
pub fn get_hidpi_factor(&self) -> f64 {
get_hidpi_factor()
}
#[inline]
pub fn set_cursor_position(&self, _position: LogicalPosition) -> Result<(), String> {
Err("Setting cursor position is not possible on Emscripten.".to_owned())
}
#[inline]
@@ -543,14 +578,36 @@ impl Window {
// N/A
}
#[inline]
pub fn set_always_on_top(&self, _always_on_top: bool) {
// N/A
}
#[inline]
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
// N/A
}
#[inline]
pub fn get_current_monitor(&self) -> ::MonitorId {
::MonitorId{inner: MonitorId}
pub fn set_ime_spot(&self, _logical_spot: LogicalPosition) {
// N/A
}
#[inline]
pub fn get_current_monitor(&self) -> RootMonitorId {
RootMonitorId { inner: MonitorId }
}
#[inline]
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
let mut list = VecDeque::with_capacity(1);
list.push_back(MonitorId);
list
}
#[inline]
pub fn get_primary_monitor(&self) -> MonitorId {
MonitorId
}
}
@@ -564,7 +621,8 @@ impl Drop for Window {
unsafe {
// Return back to normal cursor state
let _ = self.set_cursor_state(::CursorState::Normal);
self.hide_cursor(false);
self.grab_cursor(false);
// Exit fullscreen if on
if self.window.is_fullscreen {
@@ -597,7 +655,6 @@ fn error_to_str(code: ffi::EMSCRIPTEN_RESULT) -> &'static str {
}
fn key_translate(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES]) -> u8 {
use std::str;
let slice = &input[0..input.iter().take_while(|x| **x != 0).count()];
let maybe_key = unsafe { str::from_utf8(mem::transmute::<_, &[u8]>(slice)) };
let key = match maybe_key {
@@ -614,7 +671,6 @@ fn key_translate(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES]) ->
fn key_translate_virt(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES],
location: c_ulong) -> Option<::VirtualKeyCode>
{
use std::str;
let slice = &input[0..input.iter().take_while(|x| **x != 0).count()];
let maybe_key = unsafe { str::from_utf8(mem::transmute::<_, &[u8]>(slice)) };
let key = match maybe_key {

View File

@@ -1,22 +1,23 @@
#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
use std::ffi::CString;
use std::mem;
use std::os::raw::*;
use libc;
use objc::runtime::{ Object, Class };
use objc::runtime::{Class, Object};
#[allow(non_camel_case_types)]
pub type id = *mut Object;
#[allow(non_camel_case_types)]
#[allow(non_upper_case_globals)]
pub const nil: id = 0 as id;
pub type CFStringRef = *const libc::c_void;
pub type CFStringRef = *const c_void;
pub type CFTimeInterval = f64;
pub type Boolean = u32;
#[allow(non_upper_case_globals)]
pub const kCFRunLoopRunHandledSource: i32 = 4;
pub const UIViewAutoresizingFlexibleWidth: NSUInteger = 1 << 1;
pub const UIViewAutoresizingFlexibleHeight: NSUInteger = 1 << 4;
#[cfg(target_pointer_width = "32")]
pub type CGFloat = f32;
#[cfg(target_pointer_width = "64")]
@@ -38,14 +39,14 @@ pub struct CGPoint {
#[derive(Debug, Clone)]
pub struct CGRect {
pub origin: CGPoint,
pub size: CGSize
pub size: CGSize,
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct CGSize {
pub width: CGFloat,
pub height: CGFloat
pub height: CGFloat,
}
#[link(name = "UIKit", kind = "framework")]
@@ -55,15 +56,24 @@ extern {
pub static kCFRunLoopDefaultMode: CFStringRef;
// int UIApplicationMain ( int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName );
pub fn UIApplicationMain(argc: libc::c_int, argv: *const libc::c_char, principalClassName: id, delegateClassName: id) -> libc::c_int;
pub fn UIApplicationMain(
argc: c_int,
argv: *const c_char,
principalClassName: id,
delegateClassName: id,
) -> c_int;
// SInt32 CFRunLoopRunInMode ( CFStringRef mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled );
pub fn CFRunLoopRunInMode(mode: CFStringRef, seconds: CFTimeInterval, returnAfterSourceHandled: Boolean) -> i32;
pub fn CFRunLoopRunInMode(
mode: CFStringRef,
seconds: CFTimeInterval,
returnAfterSourceHandled: Boolean,
) -> i32;
}
extern {
pub fn setjmp(env: *mut libc::c_void) -> libc::c_int;
pub fn longjmp(env: *mut libc::c_void, val: libc::c_int);
pub fn setjmp(env: *mut c_void) -> c_int;
pub fn longjmp(env: *mut c_void, val: c_int);
}
pub trait NSString: Sized {
@@ -71,17 +81,14 @@ pub trait NSString: Sized {
msg_send![class("NSString"), alloc]
}
#[allow(non_snake_case)]
unsafe fn initWithUTF8String_(self, c_string: *const i8) -> id;
#[allow(non_snake_case)]
unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id;
unsafe fn stringByAppendingString_(self, other: id) -> id;
unsafe fn init_str(self, string: &str) -> Self;
#[allow(non_snake_case)]
unsafe fn UTF8String(self) -> *const libc::c_char;
unsafe fn UTF8String(self) -> *const c_char;
}
impl NSString for id {
unsafe fn initWithUTF8String_(self, c_string: *const i8) -> id {
unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id {
msg_send![self, initWithUTF8String:c_string as id]
}
@@ -94,7 +101,7 @@ impl NSString for id {
self.initWithUTF8String_(cstring.as_ptr())
}
unsafe fn UTF8String(self) -> *const libc::c_char {
unsafe fn UTF8String(self) -> *const c_char {
msg_send![self, UTF8String]
}
}
@@ -102,6 +109,6 @@ impl NSString for id {
#[inline]
pub fn class(name: &str) -> *mut Class {
unsafe {
::std::mem::transmute(Class::get(name))
mem::transmute(Class::get(name))
}
}

View File

@@ -19,7 +19,7 @@
//!
//! ```rust, ignore
//! #[no_mangle]
//! pub extern fn start_glutin_app() {
//! pub extern fn start_winit_app() {
//! start_inner()
//! }
//!
@@ -29,13 +29,13 @@
//!
//! ```
//!
//! Compile project and then drag resulting .a into Xcode project. Add glutin.h to xcode.
//! Compile project and then drag resulting .a into Xcode project. Add winit.h to xcode.
//!
//! ```ignore
//! void start_glutin_app();
//! void start_winit_app();
//! ```
//!
//! Use start_glutin_app inside your xcode's main function.
//! Use start_winit_app inside your xcode's main function.
//!
//!
//! # App lifecycle and events
@@ -45,7 +45,7 @@
//! [app lifecycle](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/).
//!
//!
//! This is how those event are represented in glutin:
//! This is how those event are represented in winit:
//!
//! - applicationDidBecomeActive is Focused(true)
//! - applicationWillResignActive is Focused(false)
@@ -60,100 +60,146 @@
#![cfg(target_os = "ios")]
use std::{fmt, mem, ptr};
use std::collections::VecDeque;
use std::ptr;
use std::mem;
use std::os::raw::c_void;
use std::os::raw::*;
use libc;
use libc::c_int;
use objc::runtime::{Class, Object, Sel, BOOL, YES };
use objc::declare::{ ClassDecl };
use objc::declare::ClassDecl;
use objc::runtime::{BOOL, Class, Object, Sel, YES};
use { CreationError, CursorState, MouseCursor, WindowAttributes };
use WindowId as RootEventId;
use WindowEvent;
use Event;
use events::{ Touch, TouchPhase };
use {
CreationError,
Event,
LogicalPosition,
LogicalSize,
MouseCursor,
PhysicalPosition,
PhysicalSize,
WindowAttributes,
WindowEvent,
WindowId as RootEventId,
};
use events::{Touch, TouchPhase};
use window::MonitorId as RootMonitorId;
mod ffi;
use self::ffi::{
setjmp,
UIApplicationMain,
CFTimeInterval,
CFRunLoopRunInMode,
CGFloat,
CGPoint,
CGRect,
id,
kCFRunLoopDefaultMode,
kCFRunLoopRunHandledSource,
id,
longjmp,
nil,
NSString,
CGFloat,
longjmp,
CGRect,
CGPoint
setjmp,
UIApplicationMain,
UIViewAutoresizingFlexibleWidth,
UIViewAutoresizingFlexibleHeight,
};
static mut jmpbuf: [c_int;27] = [0;27];
#[derive(Clone)]
pub struct MonitorId;
static mut JMPBUF: [c_int; 27] = [0; 27];
pub struct Window {
delegate_state: *mut DelegateState
delegate_state: *mut DelegateState,
}
#[derive(Clone)]
pub struct WindowProxy;
unsafe impl Send for Window {}
unsafe impl Sync for Window {}
#[derive(Debug)]
struct DelegateState {
events_queue: VecDeque<Event>,
window: id,
controller: id,
size: (u32,u32),
scale: f32
view: id,
size: LogicalSize,
scale: f64,
}
impl DelegateState {
#[inline]
fn new(window: id, controller:id, size: (u32,u32), scale: f32) -> DelegateState {
fn new(window: id, controller: id, view: id, size: LogicalSize, scale: f64) -> DelegateState {
DelegateState {
events_queue: VecDeque::new(),
window: window,
controller: controller,
size: size,
scale: scale
window,
controller,
view,
size,
scale,
}
}
}
impl Drop for DelegateState {
fn drop(&mut self) {
unsafe {
let _: () = msg_send![self.window, release];
let _: () = msg_send![self.controller, release];
let _: () = msg_send![self.view, release];
}
}
}
#[derive(Clone)]
pub struct MonitorId;
impl fmt::Debug for MonitorId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
#[derive(Debug)]
struct MonitorId {
name: Option<String>,
dimensions: PhysicalSize,
position: PhysicalPosition,
hidpi_factor: f64,
}
let monitor_id_proxy = MonitorId {
name: self.get_name(),
dimensions: self.get_dimensions(),
position: self.get_position(),
hidpi_factor: self.get_hidpi_factor(),
};
monitor_id_proxy.fmt(f)
}
}
impl MonitorId {
#[inline]
pub fn get_uiscreen(&self) -> id {
let class = Class::get("UIScreen").expect("Failed to get class `UIScreen`");
unsafe { msg_send![class, mainScreen] }
}
#[inline]
pub fn get_name(&self) -> Option<String> {
Some("Primary".to_string())
}
#[inline]
pub fn get_dimensions(&self) -> (u32, u32) {
unimplemented!()
pub fn get_dimensions(&self) -> PhysicalSize {
let bounds: CGRect = unsafe { msg_send![self.get_uiscreen(), nativeBounds] };
(bounds.size.width as f64, bounds.size.height as f64).into()
}
#[inline]
pub fn get_position(&self) -> (i32, i32) {
pub fn get_position(&self) -> PhysicalPosition {
// iOS assumes single screen
(0, 0)
(0, 0).into()
}
#[inline]
pub fn get_hidpi_factor(&self) -> f32 {
1.0
pub fn get_hidpi_factor(&self) -> f64 {
let scale: CGFloat = unsafe { msg_send![self.get_uiscreen(), nativeScale] };
scale as f64
}
}
pub struct EventsLoop {
delegate_state: *mut DelegateState
delegate_state: *mut DelegateState,
}
#[derive(Clone)]
@@ -162,30 +208,26 @@ pub struct EventsLoopProxy;
impl EventsLoop {
pub fn new() -> EventsLoop {
unsafe {
if setjmp(mem::transmute(&mut jmpbuf)) != 0 {
let app: id = msg_send![Class::get("UIApplication").unwrap(), sharedApplication];
if setjmp(mem::transmute(&mut JMPBUF)) != 0 {
let app_class = Class::get("UIApplication").expect("Failed to get class `UIApplication`");
let app: id = msg_send![app_class, sharedApplication];
let delegate: id = msg_send![app, delegate];
let state: *mut c_void = *(&*delegate).get_ivar("glutinState");
let state = state as *mut DelegateState;
let events_loop = EventsLoop {
delegate_state: state
};
return events_loop;
let state: *mut c_void = *(&*delegate).get_ivar("winitState");
let delegate_state = state as *mut DelegateState;
return EventsLoop { delegate_state };
}
}
create_delegate_class();
create_view_class();
create_delegate_class();
start_app();
panic!("Couldn't create UIApplication")
panic!("Couldn't create `UIApplication`!")
}
#[inline]
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
let mut rb = VecDeque::new();
let mut rb = VecDeque::with_capacity(1);
rb.push_back(MonitorId);
rb
}
@@ -207,7 +249,7 @@ impl EventsLoop {
}
// jump hack, so we won't quit on willTerminate event before processing it
if setjmp(mem::transmute(&mut jmpbuf)) != 0 {
if setjmp(mem::transmute(&mut JMPBUF)) != 0 {
if let Some(event) = state.events_queue.pop_front() {
callback(event);
return;
@@ -262,106 +304,123 @@ pub struct DeviceId;
#[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes;
// TODO: AFAIK transparency is enabled by default on iOS,
// so to be consistent with other platforms we have to change that.
impl Window {
pub fn new(ev: &EventsLoop, _: WindowAttributes, _: PlatformSpecificWindowBuilderAttributes)
-> Result<Window, CreationError>
{
Ok(Window {
delegate_state: ev.delegate_state,
})
pub fn new(
ev: &EventsLoop,
_attributes: WindowAttributes,
_pl_alltributes: PlatformSpecificWindowBuilderAttributes,
) -> Result<Window, CreationError> {
Ok(Window { delegate_state: ev.delegate_state })
}
#[inline]
pub fn set_title(&self, _: &str) {
pub fn get_uiwindow(&self) -> id {
unsafe { (*self.delegate_state).window }
}
#[inline]
pub fn get_uiview(&self) -> id {
unsafe { (*self.delegate_state).view }
}
#[inline]
pub fn set_title(&self, _title: &str) {
// N/A
}
#[inline]
pub fn show(&self) {
// N/A
}
#[inline]
pub fn hide(&self) {
// N/A
}
#[inline]
pub fn get_position(&self) -> Option<(i32, i32)> {
pub fn get_position(&self) -> Option<LogicalPosition> {
// N/A
None
}
#[inline]
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
// N/A
None
}
#[inline]
pub fn set_position(&self, _x: i32, _y: i32) {
pub fn set_position(&self, _position: LogicalPosition) {
// N/A
}
#[inline]
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
pub fn get_inner_size(&self) -> Option<LogicalSize> {
unsafe { Some((&*self.delegate_state).size) }
}
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
pub fn get_outer_size(&self) -> Option<LogicalSize> {
self.get_inner_size()
}
#[inline]
pub fn set_inner_size(&self, _x: u32, _y: u32) {
pub fn set_inner_size(&self, _size: LogicalSize) {
// N/A
}
#[inline]
pub fn set_min_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
#[inline]
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
#[inline]
pub fn platform_display(&self) -> *mut libc::c_void {
unimplemented!();
pub fn set_min_dimensions(&self, _dimensions: Option<LogicalSize>) {
// N/A
}
#[inline]
pub fn platform_window(&self) -> *mut libc::c_void {
unimplemented!()
pub fn set_max_dimensions(&self, _dimensions: Option<LogicalSize>) {
// N/A
}
#[inline]
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
pub fn set_resizable(&self, _resizable: bool) {
// N/A
}
#[inline]
pub fn set_cursor(&self, _: MouseCursor) {
pub fn set_cursor(&self, _cursor: MouseCursor) {
// N/A
}
#[inline]
pub fn set_cursor_state(&self, _: CursorState) -> Result<(), String> {
Ok(())
pub fn grab_cursor(&self, _grab: bool) -> Result<(), String> {
Err("Cursor grabbing is not possible on iOS.".to_owned())
}
#[inline]
pub fn hidpi_factor(&self) -> f32 {
pub fn hide_cursor(&self, _hide: bool) {
// N/A
}
#[inline]
pub fn get_hidpi_factor(&self) -> f64 {
unsafe { (&*self.delegate_state) }.scale
}
#[inline]
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
unimplemented!();
}
#[inline]
pub fn create_window_proxy(&self) -> WindowProxy {
WindowProxy
pub fn set_cursor_position(&self, _position: LogicalPosition) -> Result<(), String> {
Err("Setting cursor position is not possible on iOS.".to_owned())
}
#[inline]
pub fn set_maximized(&self, _maximized: bool) {
// N/A
// iOS has single screen maximized apps so nothing to do
}
#[inline]
pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) {
// N/A
// iOS has single screen maximized apps so nothing to do
}
@@ -370,14 +429,36 @@ impl Window {
// N/A
}
#[inline]
pub fn set_always_on_top(&self, _always_on_top: bool) {
// N/A
}
#[inline]
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
// N/A
}
#[inline]
pub fn set_ime_spot(&self, _logical_spot: LogicalPosition) {
// N/A
}
#[inline]
pub fn get_current_monitor(&self) -> RootMonitorId {
RootMonitorId{inner: MonitorId}
RootMonitorId { inner: MonitorId }
}
#[inline]
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
let mut rb = VecDeque::with_capacity(1);
rb.push_back(MonitorId);
rb
}
#[inline]
pub fn get_primary_monitor(&self) -> MonitorId {
MonitorId
}
#[inline]
@@ -388,26 +469,32 @@ impl Window {
fn create_delegate_class() {
extern fn did_finish_launching(this: &mut Object, _: Sel, _: id, _: id) -> BOOL {
let screen_class = Class::get("UIScreen").expect("Failed to get class `UIScreen`");
let window_class = Class::get("UIWindow").expect("Failed to get class `UIWindow`");
let controller_class = Class::get("MainViewController").expect("Failed to get class `MainViewController`");
let view_class = Class::get("MainView").expect("Failed to get class `MainView`");
unsafe {
let main_screen: id = msg_send![Class::get("UIScreen").unwrap(), mainScreen];
let main_screen: id = msg_send![screen_class, mainScreen];
let bounds: CGRect = msg_send![main_screen, bounds];
let scale: CGFloat = msg_send![main_screen, nativeScale];
let window: id = msg_send![Class::get("UIWindow").unwrap(), alloc];
let window: id = msg_send![window_class, alloc];
let window: id = msg_send![window, initWithFrame:bounds.clone()];
let size = (bounds.size.width as u32, bounds.size.height as u32);
let size = (bounds.size.width as f64, bounds.size.height as f64).into();
let view_controller: id = msg_send![Class::get("MainViewController").unwrap(), alloc];
let view_controller: id = msg_send![controller_class, alloc];
let view_controller: id = msg_send![view_controller, init];
let view: id = msg_send![view_class, alloc];
let view: id = msg_send![view, initForGl:&bounds];
let _: () = msg_send![window, setRootViewController:view_controller];
let _: () = msg_send![window, makeKeyAndVisible];
let state = Box::new(DelegateState::new(window, view_controller, size, scale as f32));
let state = Box::new(DelegateState::new(window, view_controller, view, size, scale as f64));
let state_ptr: *mut DelegateState = mem::transmute(state);
this.set_ivar("glutinState", state_ptr as *mut c_void);
this.set_ivar("winitState", state_ptr as *mut c_void);
let _: () = msg_send![this, performSelector:sel!(postLaunch:) withObject:nil afterDelay:0.0];
}
@@ -415,12 +502,12 @@ fn create_delegate_class() {
}
extern fn post_launch(_: &Object, _: Sel, _: id) {
unsafe { longjmp(mem::transmute(&mut jmpbuf),1); }
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 c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
state.events_queue.push_back(Event::WindowEvent {
window_id: RootEventId(WindowId),
@@ -431,7 +518,7 @@ fn create_delegate_class() {
extern fn will_resign_active(this: &Object, _: Sel, _: id) {
unsafe {
let state: *mut c_void = *this.get_ivar("glutinState");
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
state.events_queue.push_back(Event::WindowEvent {
window_id: RootEventId(WindowId),
@@ -442,7 +529,7 @@ fn create_delegate_class() {
extern fn will_enter_foreground(this: &Object, _: Sel, _: id) {
unsafe {
let state: *mut c_void = *this.get_ivar("glutinState");
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
state.events_queue.push_back(Event::Suspended(false));
}
@@ -450,7 +537,7 @@ fn create_delegate_class() {
extern fn did_enter_background(this: &Object, _: Sel, _: id) {
unsafe {
let state: *mut c_void = *this.get_ivar("glutinState");
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
state.events_queue.push_back(Event::Suspended(true));
}
@@ -458,7 +545,7 @@ fn create_delegate_class() {
extern fn will_terminate(this: &Object, _: Sel, _: id) {
unsafe {
let state: *mut c_void = *this.get_ivar("glutinState");
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
// push event to the front to garantee that we'll process it
// immidiatly after jump
@@ -466,13 +553,13 @@ fn create_delegate_class() {
window_id: RootEventId(WindowId),
event: WindowEvent::Destroyed,
});
longjmp(mem::transmute(&mut jmpbuf),1);
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 c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
let touches_enum: id = msg_send![touches, objectEnumerator];
@@ -491,7 +578,7 @@ fn create_delegate_class() {
event: WindowEvent::Touch(Touch {
device_id: DEVICE_ID,
id: touch_id,
location: (location.x as f64, location.y as f64),
location: (location.x as f64, location.y as f64).into(),
phase: match phase {
0 => TouchPhase::Started,
1 => TouchPhase::Moved,
@@ -506,8 +593,8 @@ fn create_delegate_class() {
}
}
let ui_responder = Class::get("UIResponder").unwrap();
let mut decl = ClassDecl::new("AppDelegate", ui_responder).unwrap();
let ui_responder = Class::get("UIResponder").expect("Failed to get class `UIResponder`");
let mut decl = ClassDecl::new("AppDelegate", ui_responder).expect("Failed to declare class `AppDelegate`");
unsafe {
decl.add_method(sel!(application:didFinishLaunchingWithOptions:),
@@ -545,17 +632,45 @@ fn create_delegate_class() {
decl.add_method(sel!(postLaunch:),
post_launch as extern fn(&Object, Sel, id));
decl.add_ivar::<*mut c_void>("glutinState");
decl.add_ivar::<*mut c_void>("winitState");
decl.register();
}
}
fn create_view_class() {
let ui_view_controller = Class::get("UIViewController").unwrap();
let decl = ClassDecl::new("MainViewController", ui_view_controller).unwrap();
// TODO: winit shouldn't contain GL-specfiic code
pub fn create_view_class() {
let superclass = Class::get("UIViewController").expect("Failed to get class `UIViewController`");
let decl = ClassDecl::new("MainViewController", superclass).expect("Failed to declare class `MainViewController`");
decl.register();
extern fn init_for_gl(this: &Object, _: Sel, frame: *const c_void) -> id {
unsafe {
let bounds = frame as *const CGRect;
let view: id = msg_send![this, initWithFrame:(*bounds).clone()];
let mask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
let _: () = msg_send![view, setAutoresizingMask:mask];
let _: () = msg_send![view, setAutoresizesSubviews:YES];
let layer: id = msg_send![view, layer];
let _ : () = msg_send![layer, setOpaque:YES];
view
}
}
extern fn layer_class(_: &Class, _: Sel) -> *const Class {
unsafe { mem::transmute(Class::get("CAEAGLLayer").expect("Failed to get class `CAEAGLLayer`")) }
}
let superclass = Class::get("GLKView").expect("Failed to get class `GLKView`");
let mut decl = ClassDecl::new("MainView", superclass).expect("Failed to declare class `MainView`");
unsafe {
decl.add_method(sel!(initForGl:), init_for_gl as extern fn(&Object, Sel, *const c_void) -> id);
decl.add_class_method(sel!(layerClass), layer_class as extern fn(&Class, Sel) -> *const Class);
decl.register();
}
}
#[inline]

View File

@@ -6,18 +6,18 @@ use std::ffi::CStr;
use std::os::raw::*;
use std::sync::Arc;
// `std::os::raw::c_void` and `libc::c_void` are NOT interchangeable!
use libc;
use parking_lot::Mutex;
use sctk::reexports::client::ConnectError;
use {
CreationError,
CursorState,
EventsLoopClosed,
Icon,
MouseCursor,
ControlFlow,
WindowAttributes,
};
use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
use window::MonitorId as RootMonitorId;
use self::x11::{XConnection, XError};
use self::x11::ffi::XVisualInfo;
@@ -40,34 +40,37 @@ const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND";
pub struct PlatformSpecificWindowBuilderAttributes {
pub visual_infos: Option<XVisualInfo>,
pub screen_id: Option<i32>,
pub resize_increments: Option<(i32, i32)>,
pub base_size: Option<(i32, i32)>,
pub resize_increments: Option<(u32, u32)>,
pub base_size: Option<(u32, u32)>,
pub class: Option<(String, String)>,
pub override_redirect: bool,
pub x11_window_type: x11::util::WindowType,
}
lazy_static!(
pub static ref X11_BACKEND: Result<Arc<XConnection>, XNotSupported> = {
XConnection::new(Some(x_error_callback)).map(Arc::new)
pub static ref X11_BACKEND: Mutex<Result<Arc<XConnection>, XNotSupported>> = {
Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new))
};
);
pub enum Window {
X(x11::Window),
Wayland(wayland::Window)
Wayland(wayland::Window),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum WindowId {
X(x11::WindowId),
Wayland(wayland::WindowId)
Wayland(wayland::WindowId),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum DeviceId {
X(x11::DeviceId),
Wayland(wayland::DeviceId)
Wayland(wayland::DeviceId),
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum MonitorId {
X(x11::MonitorId),
Wayland(wayland::MonitorId),
@@ -91,7 +94,7 @@ impl MonitorId {
}
#[inline]
pub fn get_dimensions(&self) -> (u32, u32) {
pub fn get_dimensions(&self) -> PhysicalSize {
match self {
&MonitorId::X(ref m) => m.get_dimensions(),
&MonitorId::Wayland(ref m) => m.get_dimensions(),
@@ -99,7 +102,7 @@ impl MonitorId {
}
#[inline]
pub fn get_position(&self) -> (i32, i32) {
pub fn get_position(&self) -> PhysicalPosition {
match self {
&MonitorId::X(ref m) => m.get_position(),
&MonitorId::Wayland(ref m) => m.get_position(),
@@ -107,10 +110,10 @@ impl MonitorId {
}
#[inline]
pub fn get_hidpi_factor(&self) -> f32 {
pub fn get_hidpi_factor(&self) -> f64 {
match self {
&MonitorId::X(ref m) => m.get_hidpi_factor(),
&MonitorId::Wayland(ref m) => m.get_hidpi_factor(),
&MonitorId::Wayland(ref m) => m.get_hidpi_factor() as f64,
}
}
}
@@ -136,7 +139,7 @@ impl Window {
pub fn id(&self) -> WindowId {
match self {
&Window::X(ref w) => WindowId::X(w.id()),
&Window::Wayland(ref w) => WindowId::Wayland(w.id())
&Window::Wayland(ref w) => WindowId::Wayland(w.id()),
}
}
@@ -144,7 +147,7 @@ impl Window {
pub fn set_title(&self, title: &str) {
match self {
&Window::X(ref w) => w.set_title(title),
&Window::Wayland(ref w) => w.set_title(title)
&Window::Wayland(ref w) => w.set_title(title),
}
}
@@ -152,7 +155,7 @@ impl Window {
pub fn show(&self) {
match self {
&Window::X(ref w) => w.show(),
&Window::Wayland(ref w) => w.show()
&Window::Wayland(ref w) => w.show(),
}
}
@@ -160,20 +163,20 @@ impl Window {
pub fn hide(&self) {
match self {
&Window::X(ref w) => w.hide(),
&Window::Wayland(ref w) => w.hide()
&Window::Wayland(ref w) => w.hide(),
}
}
#[inline]
pub fn get_position(&self) -> Option<(i32, i32)> {
pub fn get_position(&self) -> Option<LogicalPosition> {
match self {
&Window::X(ref w) => w.get_position(),
&Window::Wayland(ref w) => w.get_position()
&Window::Wayland(ref w) => w.get_position(),
}
}
#[inline]
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
match self {
&Window::X(ref m) => m.get_inner_position(),
&Window::Wayland(ref m) => m.get_inner_position(),
@@ -181,50 +184,58 @@ impl Window {
}
#[inline]
pub fn set_position(&self, x: i32, y: i32) {
pub fn set_position(&self, position: LogicalPosition) {
match self {
&Window::X(ref w) => w.set_position(x, y),
&Window::Wayland(ref w) => w.set_position(x, y)
&Window::X(ref w) => w.set_position(position),
&Window::Wayland(ref w) => w.set_position(position),
}
}
#[inline]
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
pub fn get_inner_size(&self) -> Option<LogicalSize> {
match self {
&Window::X(ref w) => w.get_inner_size(),
&Window::Wayland(ref w) => w.get_inner_size()
&Window::Wayland(ref w) => w.get_inner_size(),
}
}
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
pub fn get_outer_size(&self) -> Option<LogicalSize> {
match self {
&Window::X(ref w) => w.get_outer_size(),
&Window::Wayland(ref w) => w.get_outer_size()
&Window::Wayland(ref w) => w.get_outer_size(),
}
}
#[inline]
pub fn set_inner_size(&self, x: u32, y: u32) {
pub fn set_inner_size(&self, size: LogicalSize) {
match self {
&Window::X(ref w) => w.set_inner_size(x, y),
&Window::Wayland(ref w) => w.set_inner_size(x, y)
&Window::X(ref w) => w.set_inner_size(size),
&Window::Wayland(ref w) => w.set_inner_size(size),
}
}
#[inline]
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
pub fn set_min_dimensions(&self, dimensions: Option<LogicalSize>) {
match self {
&Window::X(ref w) => w.set_min_dimensions(dimensions),
&Window::Wayland(ref w) => w.set_min_dimensions(dimensions)
&Window::Wayland(ref w) => w.set_min_dimensions(dimensions),
}
}
#[inline]
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
pub fn set_max_dimensions(&self, dimensions: Option<LogicalSize>) {
match self {
&Window::X(ref w) => w.set_max_dimensions(dimensions),
&Window::Wayland(ref w) => w.set_max_dimensions(dimensions)
&Window::Wayland(ref w) => w.set_max_dimensions(dimensions),
}
}
#[inline]
pub fn set_resizable(&self, resizable: bool) {
match self {
&Window::X(ref w) => w.set_resizable(resizable),
&Window::Wayland(ref w) => w.set_resizable(resizable),
}
}
@@ -237,42 +248,34 @@ impl Window {
}
#[inline]
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
match self {
&Window::X(ref w) => w.set_cursor_state(state),
&Window::Wayland(ref w) => w.set_cursor_state(state)
&Window::X(ref window) => window.grab_cursor(grab),
&Window::Wayland(ref window) => window.grab_cursor(grab),
}
}
#[inline]
pub fn hidpi_factor(&self) -> f32 {
pub fn hide_cursor(&self, hide: bool) {
match self {
&Window::X(ref window) => window.hide_cursor(hide),
&Window::Wayland(ref window) => window.hide_cursor(hide),
}
}
#[inline]
pub fn get_hidpi_factor(&self) -> f64 {
match self {
&Window::X(ref w) => w.hidpi_factor(),
&Window::Wayland(ref w) => w.hidpi_factor()
&Window::X(ref w) => w.get_hidpi_factor(),
&Window::Wayland(ref w) => w.hidpi_factor() as f64,
}
}
#[inline]
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), String> {
match self {
&Window::X(ref w) => w.set_cursor_position(x, y),
&Window::Wayland(ref w) => w.set_cursor_position(x, y)
}
}
#[inline]
pub fn platform_display(&self) -> *mut libc::c_void {
match self {
&Window::X(ref w) => w.platform_display(),
&Window::Wayland(ref w) => w.get_display().c_ptr() as *mut _
}
}
#[inline]
pub fn platform_window(&self) -> *mut libc::c_void {
match self {
&Window::X(ref w) => w.platform_window(),
&Window::Wayland(ref w) => w.get_surface().c_ptr() as *mut _
&Window::X(ref w) => w.set_cursor_position(position),
&Window::Wayland(ref w) => w.set_cursor_position(position),
}
}
@@ -300,6 +303,14 @@ impl Window {
}
}
#[inline]
pub fn set_always_on_top(&self, always_on_top: bool) {
match self {
&Window::X(ref w) => w.set_always_on_top(always_on_top),
&Window::Wayland(_) => (),
}
}
#[inline]
pub fn set_window_icon(&self, window_icon: Option<Icon>) {
match self {
@@ -308,11 +319,41 @@ impl Window {
}
}
#[inline]
pub fn set_ime_spot(&self, position: LogicalPosition) {
match self {
&Window::X(ref w) => w.set_ime_spot(position),
&Window::Wayland(_) => (),
}
}
#[inline]
pub fn get_current_monitor(&self) -> RootMonitorId {
match self {
&Window::X(ref w) => RootMonitorId{inner: MonitorId::X(w.get_current_monitor())},
&Window::Wayland(ref w) => RootMonitorId{inner: MonitorId::Wayland(w.get_current_monitor())},
&Window::X(ref window) => RootMonitorId { inner: MonitorId::X(window.get_current_monitor()) },
&Window::Wayland(ref window) => RootMonitorId { inner: MonitorId::Wayland(window.get_current_monitor()) },
}
}
#[inline]
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
match self {
&Window::X(ref window) => window.get_available_monitors()
.into_iter()
.map(MonitorId::X)
.collect(),
&Window::Wayland(ref window) => window.get_available_monitors()
.into_iter()
.map(MonitorId::Wayland)
.collect(),
}
}
#[inline]
pub fn get_primary_monitor(&self) -> MonitorId {
match self {
&Window::X(ref window) => MonitorId::X(window.get_primary_monitor()),
&Window::Wayland(ref window) => MonitorId::Wayland(window.get_primary_monitor()),
}
}
}
@@ -321,7 +362,8 @@ unsafe extern "C" fn x_error_callback(
display: *mut x11::ffi::Display,
event: *mut x11::ffi::XErrorEvent,
) -> c_int {
if let Ok(ref xconn) = *X11_BACKEND {
let xconn_lock = X11_BACKEND.lock();
if let Ok(ref xconn) = *xconn_lock {
let mut buf: [c_char; 1024] = mem::uninitialized();
(xconn.xlib.XGetErrorText)(
display,
@@ -338,6 +380,8 @@ unsafe extern "C" fn x_error_callback(
minor_code: (*event).minor_code,
};
eprintln!("[winit X11 error] {:#?}", error);
*xconn.latest_error.lock() = Some(error);
}
// Fun fact: this return value is completely ignored.
@@ -395,30 +439,35 @@ r#"Failed to initialize any backend!
panic!(err_string);
}
pub fn new_wayland() -> Result<EventsLoop, ()> {
pub fn new_wayland() -> Result<EventsLoop, ConnectError> {
wayland::EventsLoop::new()
.map(EventsLoop::Wayland)
.ok_or(())
}
pub fn new_x11() -> Result<EventsLoop, XNotSupported> {
match *X11_BACKEND {
Ok(ref x) => Ok(EventsLoop::X(x11::EventsLoop::new(x.clone()))),
Err(ref err) => Err(err.clone()),
}
X11_BACKEND
.lock()
.as_ref()
.map(Arc::clone)
.map(x11::EventsLoop::new)
.map(EventsLoop::X)
.map_err(|err| err.clone())
}
#[inline]
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
match *self {
EventsLoop::Wayland(ref evlp) => evlp.get_available_monitors()
.into_iter()
.map(MonitorId::Wayland)
.collect(),
EventsLoop::X(ref evlp) => x11::get_available_monitors(evlp.x_connection())
.into_iter()
.map(MonitorId::X)
.collect(),
EventsLoop::Wayland(ref evlp) => evlp
.get_available_monitors()
.into_iter()
.map(MonitorId::Wayland)
.collect(),
EventsLoop::X(ref evlp) => evlp
.x_connection()
.get_available_monitors()
.into_iter()
.map(MonitorId::X)
.collect(),
}
}
@@ -426,7 +475,7 @@ r#"Failed to initialize any backend!
pub fn get_primary_monitor(&self) -> MonitorId {
match *self {
EventsLoop::Wayland(ref evlp) => MonitorId::Wayland(evlp.get_primary_monitor()),
EventsLoop::X(ref evlp) => MonitorId::X(x11::get_primary_monitor(evlp.x_connection())),
EventsLoop::X(ref evlp) => MonitorId::X(evlp.x_connection().get_primary_monitor()),
}
}

View File

@@ -1,16 +1,17 @@
use std::cell::RefCell;
use std::collections::VecDeque;
use std::fmt;
use std::sync::{Arc, Mutex, Weak};
use std::sync::atomic::{AtomicBool, Ordering};
use {ControlFlow, EventsLoopClosed};
use {ControlFlow, EventsLoopClosed, PhysicalPosition, PhysicalSize};
use super::WindowId;
use super::window::WindowStore;
use sctk::Environment;
use sctk::output::OutputMgr;
use sctk::reexports::client::{Display, EventQueue, GlobalEvent, Proxy};
use sctk::reexports::client::{Display, EventQueue, GlobalEvent, Proxy, ConnectError};
use sctk::reexports::client::commons::Implementation;
use sctk::reexports::client::protocol::{wl_keyboard, wl_output, wl_pointer, wl_registry, wl_seat,
wl_touch};
@@ -100,11 +101,8 @@ impl EventsLoopProxy {
}
impl EventsLoop {
pub fn new() -> Option<EventsLoop> {
let (display, mut event_queue) = match Display::connect_to_env() {
Ok(ret) => ret,
Err(_) => return None,
};
pub fn new() -> Result<EventsLoop, ConnectError> {
let (display, mut event_queue) = Display::connect_to_env()?;
let sink = Arc::new(Mutex::new(EventsLoopSink::new()));
let store = Arc::new(Mutex::new(WindowStore::new()));
@@ -120,7 +118,7 @@ impl EventsLoop {
},
).unwrap();
Some(EventsLoop {
Ok(EventsLoop {
display: Arc::new(display),
evq: RefCell::new(event_queue),
sink: sink,
@@ -201,27 +199,11 @@ impl EventsLoop {
}
pub fn get_primary_monitor(&self) -> MonitorId {
self.env.outputs.with_all(|list| {
if let Some(&(_, ref proxy, _)) = list.first() {
MonitorId {
proxy: proxy.clone(),
mgr: self.env.outputs.clone(),
}
} else {
panic!("No monitor is available.")
}
})
get_primary_monitor(&self.env.outputs)
}
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
self.env.outputs.with_all(|list| {
list.iter()
.map(|&(_, ref proxy, _)| MonitorId {
proxy: proxy.clone(),
mgr: self.env.outputs.clone(),
})
.collect()
})
get_available_monitors(&self.env.outputs)
}
}
@@ -250,16 +232,21 @@ impl EventsLoop {
}
// process pending resize/refresh
self.store.lock().unwrap().for_each(
|newsize, refresh, frame_refresh, closed, wid, frame| {
|newsize, size, new_dpi, refresh, frame_refresh, closed, wid, frame| {
if let Some(frame) = frame {
if let Some((w, h)) = newsize {
frame.resize(w as u32, h as u32);
frame.resize(w, h);
frame.refresh();
sink.send_event(::WindowEvent::Resized(w as u32, h as u32), wid);
let logical_size = ::LogicalSize::new(w as f64, h as f64);
sink.send_event(::WindowEvent::Resized(logical_size), wid);
*size = (w, h);
} else if frame_refresh {
frame.refresh();
}
}
if let Some(dpi) = new_dpi {
sink.send_event(::WindowEvent::HiDpiFactorChanged(dpi as f64), wid);
}
if refresh {
sink.send_event(::WindowEvent::Refresh, wid);
}
@@ -430,6 +417,29 @@ impl Clone for MonitorId {
}
}
impl fmt::Debug for MonitorId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
#[derive(Debug)]
struct MonitorId {
name: Option<String>,
native_identifier: u32,
dimensions: PhysicalSize,
position: PhysicalPosition,
hidpi_factor: i32,
}
let monitor_id_proxy = MonitorId {
name: self.get_name(),
native_identifier: self.get_native_identifier(),
dimensions: self.get_dimensions(),
position: self.get_position(),
hidpi_factor: self.get_hidpi_factor(),
};
monitor_id_proxy.fmt(f)
}
}
impl MonitorId {
pub fn get_name(&self) -> Option<String> {
self.mgr.with_info(&self.proxy, |_, info| {
@@ -442,7 +452,7 @@ impl MonitorId {
self.mgr.with_info(&self.proxy, |id, _| id).unwrap_or(0)
}
pub fn get_dimensions(&self) -> (u32, u32) {
pub fn get_dimensions(&self) -> PhysicalSize {
match self.mgr.with_info(&self.proxy, |_, info| {
info.modes
.iter()
@@ -451,19 +461,44 @@ impl MonitorId {
}) {
Some(Some((w, h))) => (w as u32, h as u32),
_ => (0, 0),
}
}.into()
}
pub fn get_position(&self) -> (i32, i32) {
pub fn get_position(&self) -> PhysicalPosition {
self.mgr
.with_info(&self.proxy, |_, info| info.location)
.unwrap_or((0, 0))
.into()
}
#[inline]
pub fn get_hidpi_factor(&self) -> f32 {
pub fn get_hidpi_factor(&self) -> i32 {
self.mgr
.with_info(&self.proxy, |_, info| info.scale_factor as f32)
.unwrap_or(1.0)
.with_info(&self.proxy, |_, info| info.scale_factor)
.unwrap_or(1)
}
}
pub fn get_primary_monitor(outputs: &OutputMgr) -> MonitorId {
outputs.with_all(|list| {
if let Some(&(_, ref proxy, _)) = list.first() {
MonitorId {
proxy: proxy.clone(),
mgr: outputs.clone(),
}
} else {
panic!("No monitor is available.")
}
})
}
pub fn get_available_monitors(outputs: &OutputMgr) -> VecDeque<MonitorId> {
outputs.with_all(|list| {
list.iter()
.map(|&(_, ref proxy, _)| MonitorId {
proxy: proxy.clone(),
mgr: outputs.clone(),
})
.collect()
})
}

View File

@@ -247,7 +247,6 @@ fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
keysyms::XKB_KEY_Alt_L => Some(VirtualKeyCode::LAlt),
// => Some(VirtualKeyCode::LBracket),
keysyms::XKB_KEY_Control_L => Some(VirtualKeyCode::LControl),
// => Some(VirtualKeyCode::LMenu),
keysyms::XKB_KEY_Shift_L => Some(VirtualKeyCode::LShift),
// => Some(VirtualKeyCode::LWin),
// => Some(VirtualKeyCode::Mail),
@@ -270,7 +269,6 @@ fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
keysyms::XKB_KEY_Alt_R => Some(VirtualKeyCode::RAlt),
// => Some(VirtualKeyCode::RBracket),
keysyms::XKB_KEY_Control_R => Some(VirtualKeyCode::RControl),
// => Some(VirtualKeyCode::RMenu),
keysyms::XKB_KEY_Shift_R => Some(VirtualKeyCode::RShift),
// => Some(VirtualKeyCode::RWin),
keysyms::XKB_KEY_semicolon => Some(VirtualKeyCode::Semicolon),

View File

@@ -42,7 +42,7 @@ pub fn implement_pointer(
sink.send_event(
WindowEvent::CursorMoved {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
position: (surface_x, surface_y),
position: (surface_x, surface_y).into(),
// TODO: replace dummy value with actual modifier state
modifiers: ModifiersState::default(),
},
@@ -71,7 +71,7 @@ pub fn implement_pointer(
sink.send_event(
WindowEvent::CursorMoved {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
position: (surface_x, surface_y),
position: (surface_x, surface_y).into(),
// TODO: replace dummy value with actual modifier state
modifiers: ModifiersState::default(),
},
@@ -117,7 +117,7 @@ pub fn implement_pointer(
sink.send_event(
WindowEvent::MouseWheel {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
phase: TouchPhase::Moved,
// TODO: replace dummy value with actual modifier state
modifiers: ModifiersState::default(),
@@ -158,7 +158,7 @@ pub fn implement_pointer(
sink.send_event(
WindowEvent::MouseWheel {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
phase: axis_state,
// TODO: replace dummy value with actual modifier state
modifiers: ModifiersState::default(),

View File

@@ -34,7 +34,7 @@ pub(crate) fn implement_touch(
WindowEvent::Touch(::Touch {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Started,
location: (x, y),
location: (x, y).into(),
id: id as u64,
}),
wid,
@@ -54,7 +54,7 @@ pub(crate) fn implement_touch(
WindowEvent::Touch(::Touch {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Ended,
location: pt.location,
location: pt.location.into(),
id: id as u64,
}),
pt.wid,
@@ -69,7 +69,7 @@ pub(crate) fn implement_touch(
WindowEvent::Touch(::Touch {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Moved,
location: (x, y),
location: (x, y).into(),
id: id as u64,
}),
pt.wid,
@@ -82,7 +82,7 @@ pub(crate) fn implement_touch(
WindowEvent::Touch(::Touch {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Cancelled,
location: pt.location,
location: pt.location.into(),
id: pt.id as u64,
}),
pt.wid,

View File

@@ -1,21 +1,26 @@
use std::collections::VecDeque;
use std::sync::{Arc, Mutex, Weak};
use {CreationError, CursorState, MouseCursor, WindowAttributes};
use {CreationError, MouseCursor, WindowAttributes};
use dpi::{LogicalPosition, LogicalSize};
use platform::MonitorId as PlatformMonitorId;
use window::MonitorId as RootMonitorId;
use sctk::window::{BasicFrame, Event as WEvent, Window as SWindow};
use sctk::reexports::client::{Display, Proxy};
use sctk::reexports::client::protocol::{wl_seat, wl_surface};
use sctk::reexports::client::protocol::{wl_seat, wl_surface, wl_output};
use sctk::reexports::client::protocol::wl_compositor::RequestsTrait as CompositorRequests;
use sctk::reexports::client::protocol::wl_surface::RequestsTrait as SurfaceRequests;
use sctk::output::OutputMgr;
use super::{make_wid, EventsLoop, MonitorId, WindowId};
use platform::platform::wayland::event_loop::{get_available_monitors, get_primary_monitor};
pub struct Window {
surface: Proxy<wl_surface::WlSurface>,
frame: Arc<Mutex<SWindow<BasicFrame>>>,
monitors: Arc<Mutex<Vec<MonitorId>>>,
monitors: Arc<Mutex<MonitorList>>, // Monitors this window is currently on
outputs: OutputMgr, // Access to info for all monitors
size: Arc<Mutex<(u32, u32)>>,
kill_switch: (Arc<Mutex<bool>>, Arc<Mutex<bool>>),
display: Arc<Display>,
@@ -24,23 +29,40 @@ pub struct Window {
impl Window {
pub fn new(evlp: &EventsLoop, attributes: WindowAttributes) -> Result<Window, CreationError> {
let (width, height) = attributes.dimensions.unwrap_or((800, 600));
let (width, height) = attributes.dimensions.map(Into::into).unwrap_or((800, 600));
// Create the window
let size = Arc::new(Mutex::new((width, height)));
// monitor tracking
let monitor_list = Arc::new(Mutex::new(Vec::new()));
let monitor_list = Arc::new(Mutex::new(MonitorList::new()));
let surface = evlp.env.compositor.create_surface().unwrap().implement({
let list = monitor_list.clone();
let omgr = evlp.env.outputs.clone();
move |event, _| match event {
wl_surface::Event::Enter { output } => list.lock().unwrap().push(MonitorId {
proxy: output,
mgr: omgr.clone(),
}),
let window_store = evlp.store.clone();
move |event, surface: Proxy<wl_surface::WlSurface>| match event {
wl_surface::Event::Enter { output } => {
let dpi_change = list.lock().unwrap().add_output(MonitorId {
proxy: output,
mgr: omgr.clone(),
});
if let Some(dpi) = dpi_change {
if surface.version() >= 3 {
// without version 3 we can't be dpi aware
window_store.lock().unwrap().dpi_change(&surface, dpi);
surface.set_buffer_scale(dpi);
}
}
},
wl_surface::Event::Leave { output } => {
list.lock().unwrap().retain(|m| !m.proxy.equals(&output));
let dpi_change = list.lock().unwrap().del_output(&output);
if let Some(dpi) = dpi_change {
if surface.version() >= 3 {
// without version 3 we can't be dpi aware
window_store.lock().unwrap().dpi_change(&surface, dpi);
surface.set_buffer_scale(dpi);
}
}
}
}
});
@@ -59,7 +81,7 @@ impl Window {
let mut store = window_store.lock().unwrap();
for window in &mut store.windows {
if window.surface.equals(&my_surface) {
window.newsize = new_size.map(|(w, h)| (w as i32, h as i32));
window.newsize = new_size;
window.need_refresh = true;
*(window.need_frame_refresh.lock().unwrap()) = true;
return;
@@ -101,12 +123,14 @@ impl Window {
frame.set_maximized();
}
frame.set_resizable(attributes.resizable);
// set decorations
frame.set_decorate(attributes.decorations);
// min-max dimensions
frame.set_min_size(attributes.min_dimensions);
frame.set_max_size(attributes.max_dimensions);
frame.set_min_size(attributes.min_dimensions.map(Into::into));
frame.set_max_size(attributes.max_dimensions.map(Into::into));
let kill_switch = Arc::new(Mutex::new(false));
let need_frame_refresh = Arc::new(Mutex::new(true));
@@ -115,11 +139,14 @@ impl Window {
evlp.store.lock().unwrap().windows.push(InternalWindow {
closed: false,
newsize: None,
size: size.clone(),
need_refresh: false,
need_frame_refresh: need_frame_refresh.clone(),
surface: surface.clone(),
kill_switch: kill_switch.clone(),
frame: Arc::downgrade(&frame),
current_dpi: 1,
new_dpi: None,
});
evlp.evq.borrow_mut().sync_roundtrip().unwrap();
@@ -128,6 +155,7 @@ impl Window {
surface: surface,
frame: frame,
monitors: monitor_list,
outputs: evlp.env.outputs.clone(),
size: size,
kill_switch: (kill_switch, evlp.cleanup_needed.clone()),
need_frame_refresh: need_frame_refresh,
@@ -154,75 +182,59 @@ impl Window {
}
#[inline]
pub fn get_position(&self) -> Option<(i32, i32)> {
pub fn get_position(&self) -> Option<LogicalPosition> {
// Not possible with wayland
None
}
#[inline]
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
// Not possible with wayland
None
}
#[inline]
pub fn set_position(&self, _x: i32, _y: i32) {
pub fn set_position(&self, _pos: LogicalPosition) {
// Not possible with wayland
}
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
Some(self.size.lock().unwrap().clone())
pub fn get_inner_size(&self) -> Option<LogicalSize> {
Some(self.size.lock().unwrap().clone().into())
}
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
pub fn get_outer_size(&self) -> Option<LogicalSize> {
let (w, h) = self.size.lock().unwrap().clone();
// let (w, h) = super::wayland_window::add_borders(w as i32, h as i32);
Some((w as u32, h as u32))
Some((w, h).into())
}
#[inline]
// NOTE: This will only resize the borders, the contents must be updated by the user
pub fn set_inner_size(&self, x: u32, y: u32) {
self.frame.lock().unwrap().resize(x, y);
*(self.size.lock().unwrap()) = (x, y);
pub fn set_inner_size(&self, size: LogicalSize) {
let (w, h) = size.into();
self.frame.lock().unwrap().resize(w, h);
*(self.size.lock().unwrap()) = (w, h);
}
#[inline]
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
self.frame.lock().unwrap().set_min_size(dimensions);
pub fn set_min_dimensions(&self, dimensions: Option<LogicalSize>) {
self.frame.lock().unwrap().set_min_size(dimensions.map(Into::into));
}
#[inline]
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
self.frame.lock().unwrap().set_max_size(dimensions);
pub fn set_max_dimensions(&self, dimensions: Option<LogicalSize>) {
self.frame.lock().unwrap().set_max_size(dimensions.map(Into::into));
}
#[inline]
pub fn set_cursor(&self, _cursor: MouseCursor) {
// TODO
pub fn set_resizable(&self, resizable: bool) {
self.frame.lock().unwrap().set_resizable(resizable);
}
#[inline]
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
use CursorState::{Grab, Hide, Normal};
// TODO : not yet possible on wayland to grab cursor
match state {
Grab => Err("Cursor cannot be grabbed on wayland yet.".to_string()),
Hide => Err("Cursor cannot be hidden on wayland yet.".to_string()),
Normal => Ok(()),
}
}
#[inline]
pub fn hidpi_factor(&self) -> f32 {
let mut factor: f32 = 1.0;
let guard = self.monitors.lock().unwrap();
for monitor_id in guard.iter() {
let hidpif = monitor_id.get_hidpi_factor();
factor = factor.max(hidpif);
}
factor
pub fn hidpi_factor(&self) -> i32 {
self.monitors.lock().unwrap().compute_hidpi_factor()
}
pub fn set_decorations(&self, decorate: bool) {
@@ -253,9 +265,23 @@ impl Window {
}
#[inline]
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
// TODO: not yet possible on wayland
Err(())
pub fn set_cursor(&self, _cursor: MouseCursor) {
// TODO
}
#[inline]
pub fn hide_cursor(&self, _hide: bool) {
// TODO: This isn't possible on Wayland yet
}
#[inline]
pub fn grab_cursor(&self, _grab: bool) -> Result<(), String> {
Err("Cursor grabbing is not yet possible on Wayland.".to_owned())
}
#[inline]
pub fn set_cursor_position(&self, _pos: LogicalPosition) -> Result<(), String> {
Err("Setting the cursor position is not yet possible on Wayland.".to_owned())
}
pub fn get_display(&self) -> &Display {
@@ -270,7 +296,15 @@ impl Window {
// we don't know how much each monitor sees us so...
// just return the most recent one ?
let guard = self.monitors.lock().unwrap();
guard.last().unwrap().clone()
guard.monitors.last().unwrap().clone()
}
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
get_available_monitors(&self.outputs)
}
pub fn get_primary_monitor(&self) -> MonitorId {
get_primary_monitor(&self.outputs)
}
}
@@ -287,12 +321,15 @@ impl Drop for Window {
struct InternalWindow {
surface: Proxy<wl_surface::WlSurface>,
newsize: Option<(i32, i32)>,
newsize: Option<(u32, u32)>,
size: Arc<Mutex<(u32, u32)>>,
need_refresh: bool,
need_frame_refresh: Arc<Mutex<bool>>,
closed: bool,
kill_switch: Arc<Mutex<bool>>,
frame: Weak<Mutex<SWindow<BasicFrame>>>,
current_dpi: i32,
new_dpi: Option<i32>
}
pub struct WindowStore {
@@ -338,24 +375,84 @@ impl WindowStore {
}
}
fn dpi_change(&mut self, surface: &Proxy<wl_surface::WlSurface>, new: i32) {
for window in &mut self.windows {
if surface.equals(&window.surface) {
window.new_dpi = Some(new);
}
}
}
pub fn for_each<F>(&mut self, mut f: F)
where
F: FnMut(Option<(i32, i32)>, bool, bool, bool, WindowId, Option<&mut SWindow<BasicFrame>>),
F: FnMut(Option<(u32, u32)>, &mut (u32, u32), Option<i32>, bool, bool, bool, WindowId, Option<&mut SWindow<BasicFrame>>),
{
for window in &mut self.windows {
let opt_arc = window.frame.upgrade();
let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap());
f(
window.newsize.take(),
&mut *(window.size.lock().unwrap()),
window.new_dpi,
window.need_refresh,
::std::mem::replace(&mut *window.need_frame_refresh.lock().unwrap(), false),
window.closed,
make_wid(&window.surface),
opt_mutex_lock.as_mut().map(|m| &mut **m),
);
if let Some(dpi) = window.new_dpi.take() {
window.current_dpi = dpi;
}
window.need_refresh = false;
// avoid re-spamming the event
window.closed = false;
}
}
}
/*
* Monitor list with some covenience method to compute DPI
*/
struct MonitorList {
monitors: Vec<MonitorId>
}
impl MonitorList {
fn new() -> MonitorList {
MonitorList {
monitors: Vec::new()
}
}
fn compute_hidpi_factor(&self) -> i32 {
let mut factor = 1;
for monitor_id in &self.monitors {
let monitor_dpi = monitor_id.get_hidpi_factor();
if monitor_dpi > factor { factor = monitor_dpi; }
}
factor
}
fn add_output(&mut self, monitor: MonitorId) -> Option<i32> {
let old_dpi = self.compute_hidpi_factor();
let monitor_dpi = monitor.get_hidpi_factor();
self.monitors.push(monitor);
if monitor_dpi > old_dpi {
Some(monitor_dpi)
} else {
None
}
}
fn del_output(&mut self, output: &Proxy<wl_output::WlOutput>) -> Option<i32> {
let old_dpi = self.compute_hidpi_factor();
self.monitors.retain(|m| !m.proxy.equals(output));
let new_dpi = self.compute_hidpi_factor();
if new_dpi != old_dpi {
Some(new_dpi)
} else {
None
}
}
}

View File

@@ -40,7 +40,7 @@ impl DndAtoms {
b"text/uri-list\0".as_ptr() as *mut c_char,
b"None\0".as_ptr() as *mut c_char,
];
let atoms = unsafe { util::get_atoms(xconn, &names) }?;
let atoms = unsafe { xconn.get_atoms(&names) }?;
Ok(DndAtoms {
aware: atoms[0],
enter: atoms[1],
@@ -127,13 +127,12 @@ impl Dnd {
DndState::Accepted => (1, self.atoms.action_private as c_long),
DndState::Rejected => (0, self.atoms.none as c_long),
};
util::send_client_msg(
&self.xconn,
self.xconn.send_client_msg(
target_window,
target_window,
self.atoms.status,
None,
(this_window as c_long, accepted, 0, 0, action),
[this_window as c_long, accepted, 0, 0, action],
).flush()
}
@@ -147,13 +146,12 @@ impl Dnd {
DndState::Accepted => (1, self.atoms.action_private as c_long),
DndState::Rejected => (0, self.atoms.none as c_long),
};
util::send_client_msg(
&self.xconn,
self.xconn.send_client_msg(
target_window,
target_window,
self.atoms.finished,
None,
(this_window as c_long, accepted, action, 0, 0),
[this_window as c_long, accepted, action, 0, 0],
).flush()
}
@@ -161,8 +159,7 @@ impl Dnd {
&self,
source_window: c_ulong,
) -> Result<Vec<ffi::Atom>, util::GetPropertyError> {
util::get_property(
&self.xconn,
self.xconn.get_property(
source_window,
self.atoms.type_list,
ffi::XA_ATOM,
@@ -184,8 +181,7 @@ impl Dnd {
&self,
window: c_ulong,
) -> Result<Vec<c_uchar>, util::GetPropertyError> {
util::get_property(
&self.xconn,
self.xconn.get_property(
window,
self.atoms.selection,
self.atoms.uri_list,

View File

@@ -5,12 +5,20 @@ use std::sync::Arc;
use std::os::raw::c_char;
use std::ffi::{CStr, CString, IntoStringError};
use parking_lot::Mutex;
use super::{ffi, util, XConnection, XError};
lazy_static! {
static ref GLOBAL_LOCK: Mutex<()> = Default::default();
}
unsafe fn open_im(
xconn: &Arc<XConnection>,
locale_modifiers: &CStr,
) -> Option<ffi::XIM> {
let _lock = GLOBAL_LOCK.lock();
// XSetLocaleModifiers returns...
// * The current locale modifiers if it's given a NULL pointer.
// * The new locale modifiers if we succeeded in setting them.
@@ -85,13 +93,11 @@ enum GetXimServersError {
// modifiers, since we don't want a user who's looking at logs to ask "am I supposed to set
// XMODIFIERS to `@server=ibus`?!?"
unsafe fn get_xim_servers(xconn: &Arc<XConnection>) -> Result<Vec<String>, GetXimServersError> {
let servers_atom = util::get_atom(&xconn, b"XIM_SERVERS\0")
.map_err(GetXimServersError::XError)?;
let servers_atom = xconn.get_atom_unchecked(b"XIM_SERVERS\0");
let root = (xconn.xlib.XDefaultRootWindow)(xconn.display);
let mut atoms: Vec<ffi::Atom> = util::get_property(
&xconn,
let mut atoms: Vec<ffi::Atom> = xconn.get_property(
root,
servers_atom,
ffi::XA_ATOM,

File diff suppressed because it is too large Load Diff

View File

@@ -1,110 +1,85 @@
use std::sync::Arc;
use std::slice;
use std::os::raw::*;
use super::XConnection;
use parking_lot::Mutex;
#[derive(Clone)]
use {PhysicalPosition, PhysicalSize};
use super::{util, XConnection, XError};
use super::ffi::{
RRCrtcChangeNotifyMask,
RROutputPropertyNotifyMask,
RRScreenChangeNotifyMask,
True,
Window,
XRRScreenResources,
};
// Used to test XRandR < 1.5 code path. This should always be committed as false.
const FORCE_RANDR_COMPAT: bool = false;
// Also used for testing. This should always be committed as false.
const DISABLE_MONITOR_LIST_CACHING: bool = false;
lazy_static! {
static ref XRANDR_VERSION: Mutex<Option<(c_int, c_int)>> = Mutex::default();
static ref MONITORS: Mutex<Option<Vec<MonitorId>>> = Mutex::default();
}
fn version_is_at_least(major: c_int, minor: c_int) -> bool {
if let Some((avail_major, avail_minor)) = *XRANDR_VERSION.lock() {
if avail_major == major {
avail_minor >= minor
} else {
avail_major > major
}
} else {
unreachable!();
}
}
pub fn invalidate_cached_monitor_list() -> Option<Vec<MonitorId>> {
// We update this lazily.
(*MONITORS.lock()).take()
}
#[derive(Debug, Clone)]
pub struct MonitorId {
/// The actual id
id: u32,
/// The name of the monitor
name: String,
pub(crate) name: String,
/// The size of the monitor
dimensions: (u32, u32),
/// The position of the monitor in the X screen
position: (i32, i32),
/// If the monitor is the primary one
primary: bool,
/// The DPI scaling factor
hidpi_factor: f32,
}
pub fn get_available_monitors(x: &Arc<XConnection>) -> Vec<MonitorId> {
let mut available = Vec::new();
unsafe {
let root = (x.xlib.XDefaultRootWindow)(x.display);
let resources = (x.xrandr.XRRGetScreenResources)(x.display, root);
if let Some(ref xrandr_1_5) = x.xrandr_1_5 {
// We're in XRandR >= 1.5, enumerate Monitors to handle things like MST and videowalls
let mut nmonitors = 0;
let monitors = (xrandr_1_5.XRRGetMonitors)(x.display, root, 1, &mut nmonitors);
for i in 0..nmonitors {
let monitor = *(monitors.offset(i as isize));
let output = (xrandr_1_5.XRRGetOutputInfo)(x.display, resources, *(monitor.outputs.offset(0)));
let nameslice = slice::from_raw_parts((*output).name as *mut u8, (*output).nameLen as usize);
let name = String::from_utf8_lossy(nameslice).into_owned();
let hidpi_factor = {
let x_mm = (*output).mm_width as f32;
let y_mm = (*output).mm_height as f32;
let x_px = monitor.width as f32;
let y_px = monitor.height as f32;
let ppmm = ((x_px * y_px) / (x_mm * y_mm)).sqrt();
// Quantize 1/12 step size
((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0)
};
(xrandr_1_5.XRRFreeOutputInfo)(output);
available.push(MonitorId{
id: i as u32,
name,
hidpi_factor,
dimensions: (monitor.width as u32, monitor.height as u32),
position: (monitor.x as i32, monitor.y as i32),
primary: (monitor.primary != 0),
});
}
(xrandr_1_5.XRRFreeMonitors)(monitors);
} else {
// We're in XRandR < 1.5, enumerate CRTCs. Everything will work but MST and
// videowall setups will show more monitors than the logical groups the user
// cares about
for i in 0..(*resources).ncrtc {
let crtcid = *((*resources).crtcs.offset(i as isize));
let crtc = (x.xrandr.XRRGetCrtcInfo)(x.display, resources, crtcid);
if (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0 {
let output = (x.xrandr.XRRGetOutputInfo)(x.display, resources, *((*crtc).outputs.offset(0)));
let nameslice = slice::from_raw_parts((*output).name as *mut u8, (*output).nameLen as usize);
let name = String::from_utf8_lossy(nameslice).into_owned();
let hidpi_factor = {
let x_mm = (*output).mm_width as f32;
let y_mm = (*output).mm_height as f32;
let x_px = (*crtc).width as f32;
let y_px = (*crtc).height as f32;
let ppmm = ((x_px * y_px) / (x_mm * y_mm)).sqrt();
// Quantize 1/12 step size
((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0)
};
(x.xrandr.XRRFreeOutputInfo)(output);
available.push(MonitorId{
id: crtcid as u32,
name,
hidpi_factor,
dimensions: ((*crtc).width as u32, (*crtc).height as u32),
position: ((*crtc).x as i32, (*crtc).y as i32),
primary: true,
});
}
(x.xrandr.XRRFreeCrtcInfo)(crtc);
}
}
(x.xrandr.XRRFreeScreenResources)(resources);
}
available
}
#[inline]
pub fn get_primary_monitor(x: &Arc<XConnection>) -> MonitorId {
get_available_monitors(x).into_iter().find(|m| m.primary)
// 'no primary' case is better handled picking some existing monitor
.or_else(|| get_available_monitors(x).into_iter().next())
.expect("[winit] Failed to find any x11 monitor")
/// The DPI scale factor
pub(crate) hidpi_factor: f64,
/// Used to determine which windows are on this monitor
pub(crate) rect: util::AaRect,
}
impl MonitorId {
fn from_repr(
xconn: &XConnection,
resources: *mut XRRScreenResources,
id: u32,
repr: util::MonitorRepr,
primary: bool,
) -> Self {
let (name, hidpi_factor) = unsafe { xconn.get_output_info(resources, &repr) };
let (dimensions, position) = unsafe { (repr.get_dimensions(), repr.get_position()) };
let rect = util::AaRect::new(position, dimensions);
MonitorId {
id,
name,
hidpi_factor,
dimensions,
position,
primary,
rect,
}
}
pub fn get_name(&self) -> Option<String> {
Some(self.name.clone())
}
@@ -114,16 +89,181 @@ impl MonitorId {
self.id as u32
}
pub fn get_dimensions(&self) -> (u32, u32) {
self.dimensions
pub fn get_dimensions(&self) -> PhysicalSize {
self.dimensions.into()
}
pub fn get_position(&self) -> (i32, i32) {
self.position
pub fn get_position(&self) -> PhysicalPosition {
self.position.into()
}
#[inline]
pub fn get_hidpi_factor(&self) -> f32 {
pub fn get_hidpi_factor(&self) -> f64 {
self.hidpi_factor
}
}
impl XConnection {
pub fn get_monitor_for_window(&self, window_rect: Option<util::AaRect>) -> MonitorId {
let monitors = self.get_available_monitors();
let default = monitors
.get(0)
.expect("[winit] Failed to find any monitors using XRandR.");
let window_rect = match window_rect {
Some(rect) => rect,
None => return default.to_owned(),
};
let mut largest_overlap = 0;
let mut matched_monitor = default;
for monitor in &monitors {
let overlapping_area = window_rect.get_overlapping_area(&monitor.rect);
if overlapping_area > largest_overlap {
largest_overlap = overlapping_area;
matched_monitor = &monitor;
}
}
matched_monitor.to_owned()
}
fn query_monitor_list(&self) -> Vec<MonitorId> {
unsafe {
let root = (self.xlib.XDefaultRootWindow)(self.display);
// WARNING: this function is supposedly very slow, on the order of hundreds of ms.
// Upon failure, `resources` will be null.
let resources = (self.xrandr.XRRGetScreenResources)(self.display, root);
if resources.is_null() {
panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist.");
}
let mut available;
let mut has_primary = false;
if self.xrandr_1_5.is_some() && version_is_at_least(1, 5) && !FORCE_RANDR_COMPAT {
// We're in XRandR >= 1.5, enumerate monitors. This supports things like MST and
// videowalls.
let xrandr_1_5 = self.xrandr_1_5.as_ref().unwrap();
let mut monitor_count = 0;
let monitors = (xrandr_1_5.XRRGetMonitors)(self.display, root, 1, &mut monitor_count);
assert!(monitor_count >= 0);
available = Vec::with_capacity(monitor_count as usize);
for monitor_index in 0..monitor_count {
let monitor = monitors.offset(monitor_index as isize);
let is_primary = (*monitor).primary != 0;
has_primary |= is_primary;
available.push(MonitorId::from_repr(
self,
resources,
monitor_index as u32,
monitor.into(),
is_primary,
));
}
(xrandr_1_5.XRRFreeMonitors)(monitors);
} else {
// We're in XRandR < 1.5, enumerate CRTCs. Everything will work except MST and
// videowall setups will also show monitors that aren't in the logical groups the user
// cares about.
let primary = (self.xrandr.XRRGetOutputPrimary)(self.display, root);
available = Vec::with_capacity((*resources).ncrtc as usize);
for crtc_index in 0..(*resources).ncrtc {
let crtc_id = *((*resources).crtcs.offset(crtc_index as isize));
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
let is_active = (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0;
if is_active {
let crtc = util::MonitorRepr::from(crtc);
let is_primary = crtc.get_output() == primary;
has_primary |= is_primary;
available.push(MonitorId::from_repr(
self,
resources,
crtc_id as u32,
crtc,
is_primary,
));
}
(self.xrandr.XRRFreeCrtcInfo)(crtc);
}
}
// If no monitors were detected as being primary, we just pick one ourselves!
if !has_primary {
if let Some(ref mut fallback) = available.first_mut() {
// Setting this here will come in handy if we ever add an `is_primary` method.
fallback.primary = true;
}
}
(self.xrandr.XRRFreeScreenResources)(resources);
available
}
}
pub fn get_available_monitors(&self) -> Vec<MonitorId> {
let mut monitors_lock = MONITORS.lock();
(*monitors_lock)
.as_ref()
.cloned()
.or_else(|| {
let monitors = Some(self.query_monitor_list());
if !DISABLE_MONITOR_LIST_CACHING {
(*monitors_lock) = monitors.clone();
}
monitors
})
.unwrap()
}
#[inline]
pub fn get_primary_monitor(&self) -> MonitorId {
self.get_available_monitors()
.into_iter()
.find(|monitor| monitor.primary)
.expect("[winit] Failed to find any monitors using XRandR.")
}
pub fn select_xrandr_input(&self, root: Window) -> Result<c_int, XError> {
{
let mut version_lock = XRANDR_VERSION.lock();
if version_lock.is_none() {
let mut major = 0;
let mut minor = 0;
let has_extension = unsafe {
(self.xrandr.XRRQueryVersion)(
self.display,
&mut major,
&mut minor,
)
};
if has_extension != True {
panic!("[winit] XRandR extension not available.");
}
*version_lock = Some((major, minor));
}
}
let mut event_offset = 0;
let mut error_offset = 0;
let status = unsafe {
(self.xrandr.XRRQueryExtension)(
self.display,
&mut event_offset,
&mut error_offset,
)
};
if status != True {
self.check_errors()?;
unreachable!("[winit] `XRRQueryExtension` failed but no error was received.");
}
let mask = RRCrtcChangeNotifyMask
| RROutputPropertyNotifyMask
| RRScreenChangeNotifyMask;
unsafe { (self.xrandr.XRRSelectInput)(self.display, root, mask) };
Ok(event_offset)
}
}

View File

@@ -1,5 +1,6 @@
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::fmt::Debug;
use std::os::raw::*;
use parking_lot::Mutex;
@@ -12,48 +13,60 @@ lazy_static! {
static ref ATOM_CACHE: Mutex<AtomCache> = Mutex::new(HashMap::with_capacity(2048));
}
pub unsafe fn get_atom(xconn: &Arc<XConnection>, name: &[u8]) -> Result<ffi::Atom, XError> {
let name = CStr::from_bytes_with_nul_unchecked(name); // I trust you. Don't let me down.
let mut atom_cache_lock = ATOM_CACHE.lock();
let cached_atom = (*atom_cache_lock).get(name).cloned();
if let Some(atom) = cached_atom {
Ok(atom)
} else {
let atom = (xconn.xlib.XInternAtom)(
xconn.display,
name.as_ptr() as *const c_char,
impl XConnection {
pub fn get_atom<T: AsRef<CStr> + Debug>(&self, name: T) -> ffi::Atom {
let name = name.as_ref();
let mut atom_cache_lock = ATOM_CACHE.lock();
let cached_atom = (*atom_cache_lock).get(name).cloned();
if let Some(atom) = cached_atom {
atom
} else {
let atom = unsafe { (self.xlib.XInternAtom)(
self.display,
name.as_ptr() as *const c_char,
ffi::False,
) };
if atom == 0 {
let msg = format!(
"`XInternAtom` failed, which really shouldn't happen. Atom: {:?}, Error: {:#?}",
name,
self.check_errors(),
);
panic!(msg);
}
/*println!(
"XInternAtom name:{:?} atom:{:?}",
name,
atom,
);*/
(*atom_cache_lock).insert(name.to_owned(), atom);
atom
}
}
pub unsafe fn get_atom_unchecked(&self, name: &[u8]) -> ffi::Atom {
debug_assert!(CStr::from_bytes_with_nul(name).is_ok());
let name = CStr::from_bytes_with_nul_unchecked(name);
self.get_atom(name)
}
// Note: this doesn't use caching, for the sake of simplicity.
// If you're dealing with this many atoms, you'll usually want to cache them locally anyway.
pub unsafe fn get_atoms(&self, names: &[*mut c_char]) -> Result<Vec<ffi::Atom>, XError> {
let mut atoms = Vec::with_capacity(names.len());
(self.xlib.XInternAtoms)(
self.display,
names.as_ptr() as *mut _,
names.len() as c_int,
ffi::False,
atoms.as_mut_ptr(),
);
self.check_errors()?;
atoms.set_len(names.len());
/*println!(
"XInternAtom name:{:?} atom:{:?}",
name,
atom,
"XInternAtoms atoms:{:?}",
atoms,
);*/
xconn.check_errors()?;
(*atom_cache_lock).insert(name.to_owned(), atom);
Ok(atom)
Ok(atoms)
}
}
// Note: this doesn't use caching, for the sake of simplicity.
// If you're dealing with this many atoms, you'll usually want to cache them locally anyway.
pub unsafe fn get_atoms(
xconn: &Arc<XConnection>,
names: &[*mut c_char],
) -> Result<Vec<ffi::Atom>, XError> {
let mut atoms = Vec::with_capacity(names.len());
(xconn.xlib.XInternAtoms)(
xconn.display,
names.as_ptr() as *mut _,
names.len() as c_int,
ffi::False,
atoms.as_mut_ptr(),
);
xconn.check_errors()?;
atoms.set_len(names.len());
/*println!(
"XInternAtoms atoms:{:?}",
atoms,
);*/
Ok(atoms)
}

View File

@@ -0,0 +1,95 @@
use super::*;
pub type ClientMsgPayload = [c_long; 5];
impl XConnection {
pub fn send_event<T: Into<ffi::XEvent>>(
&self,
target_window: c_ulong,
event_mask: Option<c_long>,
event: T,
) -> Flusher {
let event_mask = event_mask.unwrap_or(ffi::NoEventMask);
unsafe {
(self.xlib.XSendEvent)(
self.display,
target_window,
ffi::False,
event_mask,
&mut event.into(),
);
}
Flusher::new(self)
}
pub fn send_client_msg(
&self,
window: c_ulong, // The window this is "about"; not necessarily this window
target_window: c_ulong, // The window we're sending to
message_type: ffi::Atom,
event_mask: Option<c_long>,
data: ClientMsgPayload,
) -> Flusher {
let mut event: ffi::XClientMessageEvent = unsafe { mem::uninitialized() };
event.type_ = ffi::ClientMessage;
event.display = self.display;
event.window = window;
event.message_type = message_type;
event.format = c_long::FORMAT as c_int;
event.data = unsafe { mem::transmute(data) };
self.send_event(target_window, event_mask, event)
}
// Prepare yourself for the ultimate in unsafety!
// You should favor `send_client_msg` whenever possible, but some protocols (i.e. startup notification) require you
// to send more than one message worth of data.
pub fn send_client_msg_multi<T: Formattable>(
&self,
window: c_ulong, // The window this is "about"; not necessarily this window
target_window: c_ulong, // The window we're sending to
message_type: ffi::Atom,
event_mask: Option<c_long>,
data: &[T],
) -> Flusher {
let format = T::FORMAT;
let size_of_t = mem::size_of::<T>();
debug_assert_eq!(size_of_t, format.get_actual_size());
let mut event: ffi::XClientMessageEvent = unsafe { mem::uninitialized() };
event.type_ = ffi::ClientMessage;
event.display = self.display;
event.window = window;
event.message_type = message_type;
event.format = format as c_int;
let t_per_payload = format.get_payload_size() / size_of_t;
assert!(t_per_payload > 0);
let payload_count = data.len() / t_per_payload;
let payload_remainder = data.len() % t_per_payload;
let payload_ptr = data.as_ptr() as *const ClientMsgPayload;
let mut payload_index = 0;
while payload_index < payload_count {
let payload = unsafe { payload_ptr.offset(payload_index as isize) };
payload_index += 1;
event.data = unsafe { mem::transmute(*payload) };
self.send_event(target_window, event_mask, &event).queue();
}
if payload_remainder > 0 {
let mut payload: ClientMsgPayload = [0; 5];
let t_payload = payload.as_mut_ptr() as *mut T;
let invalid_payload = unsafe { payload_ptr.offset(payload_index as isize) };
let invalid_t_payload = invalid_payload as *const T;
let mut t_index = 0;
while t_index < payload_remainder {
let valid_t = unsafe { invalid_t_payload.offset(t_index as isize) };
unsafe { (*t_payload.offset(t_index as isize)) = (*valid_t).clone() };
t_index += 1;
}
event.data = unsafe { mem::transmute(payload) };
self.send_event(target_window, event_mask, &event).queue();
}
Flusher::new(self)
}
}

View File

@@ -0,0 +1,58 @@
use std::fmt::Debug;
use std::mem;
use std::os::raw::*;
// This isn't actually the number of the bits in the format.
// X11 does a match on this value to determine which type to call sizeof on.
// Thus, we use 32 for c_long, since 32 maps to c_long which maps to 64.
// ...if that sounds confusing, then you know why this enum is here.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Format {
Char = 8,
Short = 16,
Long = 32,
}
impl Format {
pub fn from_format(format: usize) -> Option<Self> {
match format {
8 => Some(Format::Char),
16 => Some(Format::Short),
32 => Some(Format::Long),
_ => None,
}
}
pub fn is_same_size_as<T>(&self) -> bool {
mem::size_of::<T>() == self.get_actual_size()
}
pub fn get_actual_size(&self) -> usize {
match self {
&Format::Char => mem::size_of::<c_char>(),
&Format::Short => mem::size_of::<c_short>(),
&Format::Long => mem::size_of::<c_long>(),
}
}
pub fn get_payload_size(&self) -> usize {
match self {
// Due to the wonders of X11, half the space goes unused if you're not using longs (on 64-bit).
&Format::Char => mem::size_of::<c_char>() * 20,
&Format::Short => mem::size_of::<c_short>() * 10,
&Format::Long => mem::size_of::<c_long>() * 5,
}
}
}
pub trait Formattable: Debug + Clone + Copy + PartialEq + PartialOrd {
const FORMAT: Format;
}
// You might be surprised by the absence of c_int, but not as surprised as X11 would be by the presence of it.
impl Formattable for c_schar { const FORMAT: Format = Format::Char; }
impl Formattable for c_uchar { const FORMAT: Format = Format::Char; }
impl Formattable for c_short { const FORMAT: Format = Format::Short; }
impl Formattable for c_ushort { const FORMAT: Format = Format::Short; }
impl Formattable for c_long { const FORMAT: Format = Format::Long; }
impl Formattable for c_ulong { const FORMAT: Format = Format::Long; }

View File

@@ -1,4 +1,40 @@
use std::cmp;
use super::*;
use {LogicalPosition, LogicalSize};
// Friendly neighborhood axis-aligned rectangle
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AaRect {
x: i64,
y: i64,
width: i64,
height: i64,
}
impl AaRect {
pub fn new((x, y): (i32, i32), (width, height): (u32, u32)) -> Self {
let (x, y) = (x as i64, y as i64);
let (width, height) = (width as i64, height as i64);
AaRect { x, y, width, height }
}
pub fn contains_point(&self, x: i64, y: i64) -> bool {
x >= self.x && x <= self.x + self.width && y >= self.y && y <= self.y + self.height
}
pub fn get_overlapping_area(&self, other: &Self) -> i64 {
let x_overlap = cmp::max(
0,
cmp::min(self.x + self.width, other.x + other.width) - cmp::max(self.x, other.x),
);
let y_overlap = cmp::max(
0,
cmp::min(self.y + self.height, other.y + other.height) - cmp::max(self.y, other.y),
);
x_overlap * y_overlap
}
}
#[derive(Debug)]
pub struct TranslatedCoords {
@@ -7,30 +43,6 @@ pub struct TranslatedCoords {
pub child: ffi::Window,
}
// This is adequate for get_inner_position
pub unsafe fn translate_coords(
xconn: &Arc<XConnection>,
window: ffi::Window,
root: ffi::Window,
) -> Result<TranslatedCoords, XError> {
let mut translated_coords: TranslatedCoords = mem::uninitialized();
(xconn.xlib.XTranslateCoordinates)(
xconn.display,
window,
root,
0,
0,
&mut translated_coords.x_rel_root,
&mut translated_coords.y_rel_root,
&mut translated_coords.child,
);
//println!("XTranslateCoordinates coords:{:?}", translated_coords);
xconn.check_errors().map(|_| translated_coords)
}
#[derive(Debug)]
pub struct Geometry {
pub root: ffi::Window,
@@ -51,30 +63,6 @@ pub struct Geometry {
pub depth: c_uint,
}
// This is adequate for get_inner_size
pub unsafe fn get_geometry(
xconn: &Arc<XConnection>,
window: ffi::Window,
) -> Result<Geometry, XError> {
let mut geometry: Geometry = mem::uninitialized();
let _status = (xconn.xlib.XGetGeometry)(
xconn.display,
window,
&mut geometry.root,
&mut geometry.x_rel_parent,
&mut geometry.y_rel_parent,
&mut geometry.width,
&mut geometry.height,
&mut geometry.border,
&mut geometry.depth,
);
//println!("XGetGeometry geo:{:?}", geometry);
xconn.check_errors().map(|_| geometry)
}
#[derive(Debug, Clone)]
pub struct FrameExtents {
pub left: c_ulong,
@@ -91,109 +79,24 @@ impl FrameExtents {
pub fn from_border(border: c_ulong) -> Self {
Self::new(border, border, border, border)
}
}
fn get_frame_extents(
xconn: &Arc<XConnection>,
window: ffi::Window,
) -> Option<self::FrameExtents> {
let extents_atom = unsafe { self::get_atom(xconn, b"_NET_FRAME_EXTENTS\0") }
.expect("Failed to call XInternAtom (_NET_FRAME_EXTENTS)");
if !self::hint_is_supported(extents_atom) {
return None;
}
// Of the WMs tested, xmonad, i3, dwm, IceWM (1.3.x and earlier), and blackbox don't
// support this. As this is part of EWMH (Extended Window Manager Hints), it's likely to
// be unsupported by many smaller WMs.
let extents: Option<Vec<c_ulong>> = unsafe {
self::get_property(
xconn,
window,
extents_atom,
ffi::XA_CARDINAL,
)
}.ok();
extents.and_then(|extents| {
if extents.len() >= 4 {
Some(self::FrameExtents {
left: extents[0],
right: extents[1],
top: extents[2],
bottom: extents[3],
})
} else {
None
pub fn as_logical(&self, factor: f64) -> LogicalFrameExtents {
let logicalize = |value: c_ulong| value as f64 / factor;
LogicalFrameExtents {
left: logicalize(self.left),
right: logicalize(self.right),
top: logicalize(self.top),
bottom: logicalize(self.bottom),
}
})
}
}
pub fn is_top_level(
xconn: &Arc<XConnection>,
window: ffi::Window,
root: ffi::Window,
) -> Option<bool> {
let client_list_atom = unsafe { self::get_atom(xconn, b"_NET_CLIENT_LIST\0") }
.expect("Failed to call XInternAtom (_NET_CLIENT_LIST)");
if !self::hint_is_supported(client_list_atom) {
return None;
}
let client_list: Option<Vec<ffi::Window>> = unsafe {
self::get_property(
xconn,
root,
client_list_atom,
ffi::XA_WINDOW,
)
}.ok();
client_list.map(|client_list| client_list.contains(&window))
}
unsafe fn get_parent_window(
xconn: &Arc<XConnection>,
window: ffi::Window,
) -> Result<ffi::Window, XError> {
let mut root: ffi::Window = mem::uninitialized();
let mut parent: ffi::Window = mem::uninitialized();
let mut children: *mut ffi::Window = ptr::null_mut();
let mut nchildren: c_uint = mem::uninitialized();
let _status = (xconn.xlib.XQueryTree)(
xconn.display,
window,
&mut root,
&mut parent,
&mut children,
&mut nchildren,
);
// The list of children isn't used
if children != ptr::null_mut() {
(xconn.xlib.XFree)(children as *mut _);
}
xconn.check_errors().map(|_| parent)
}
fn climb_hierarchy(
xconn: &Arc<XConnection>,
window: ffi::Window,
root: ffi::Window,
) -> Result<ffi::Window, XError> {
let mut outer_window = window;
loop {
let candidate = unsafe { get_parent_window(xconn, outer_window) }?;
if candidate == root {
break;
}
outer_window = candidate;
}
Ok(outer_window)
#[derive(Debug, Clone)]
pub struct LogicalFrameExtents {
pub left: f64,
pub right: f64,
pub top: f64,
pub bottom: f64,
}
#[derive(Debug, Clone, PartialEq)]
@@ -219,6 +122,16 @@ impl FrameExtentsHeuristic {
}
}
pub fn inner_pos_to_outer_logical(&self, mut logical: LogicalPosition, factor: f64) -> LogicalPosition {
use self::FrameExtentsHeuristicPath::*;
if self.heuristic_path != UnsupportedBordered {
let frame_extents = self.frame_extents.as_logical(factor);
logical.x -= frame_extents.left;
logical.y -= frame_extents.top;
}
logical
}
pub fn inner_size_to_outer(&self, width: u32, height: u32) -> (u32, u32) {
(
width.saturating_add(
@@ -229,122 +142,246 @@ impl FrameExtentsHeuristic {
),
)
}
pub fn inner_size_to_outer_logical(&self, mut logical: LogicalSize, factor: f64) -> LogicalSize {
let frame_extents = self.frame_extents.as_logical(factor);
logical.width += frame_extents.left + frame_extents.right;
logical.height += frame_extents.top + frame_extents.bottom;
logical
}
}
pub fn get_frame_extents_heuristic(
xconn: &Arc<XConnection>,
window: ffi::Window,
root: ffi::Window,
) -> FrameExtentsHeuristic {
use self::FrameExtentsHeuristicPath::*;
impl XConnection {
// This is adequate for get_inner_position
pub fn translate_coords(&self, window: ffi::Window, root: ffi::Window) -> Result<TranslatedCoords, XError> {
let mut translated_coords: TranslatedCoords = unsafe { mem::uninitialized() };
unsafe {
(self.xlib.XTranslateCoordinates)(
self.display,
window,
root,
0,
0,
&mut translated_coords.x_rel_root,
&mut translated_coords.y_rel_root,
&mut translated_coords.child,
);
}
//println!("XTranslateCoordinates coords:{:?}", translated_coords);
self.check_errors().map(|_| translated_coords)
}
// Position relative to root window.
// With rare exceptions, this is the position of a nested window. Cases where the window
// isn't nested are outlined in the comments throghout this function, but in addition to
// that, fullscreen windows often aren't nested.
let (inner_y_rel_root, child) = {
let coords = unsafe { translate_coords(xconn, window, root) }
.expect("Failed to translate window coordinates");
(
coords.y_rel_root,
coords.child,
)
};
// This is adequate for get_inner_size
pub fn get_geometry(&self, window: ffi::Window) -> Result<Geometry, XError> {
let mut geometry: Geometry = unsafe { mem::uninitialized() };
let _status = unsafe {
(self.xlib.XGetGeometry)(
self.display,
window,
&mut geometry.root,
&mut geometry.x_rel_parent,
&mut geometry.y_rel_parent,
&mut geometry.width,
&mut geometry.height,
&mut geometry.border,
&mut geometry.depth,
)
};
//println!("XGetGeometry geo:{:?}", geometry);
self.check_errors().map(|_| geometry)
}
let (width, height, border) = {
let inner_geometry = unsafe { get_geometry(xconn, window) }
.expect("Failed to get inner window geometry");
(
inner_geometry.width,
inner_geometry.height,
inner_geometry.border,
)
};
fn get_frame_extents(&self, window: ffi::Window) -> Option<FrameExtents> {
let extents_atom = unsafe { self.get_atom_unchecked(b"_NET_FRAME_EXTENTS\0") };
// The first condition is only false for un-nested windows, but isn't always false for
// un-nested windows. Mutter/Muffin/Budgie and Marco present a mysterious discrepancy:
// when y is on the range [0, 2] and if the window has been unfocused since being
// undecorated (or was undecorated upon construction), the first condition is true,
// requiring us to rely on the second condition.
let nested = !(window == child || is_top_level(xconn, child, root) == Some(true));
// Hopefully the WM supports EWMH, allowing us to get exact info on the window frames.
if let Some(mut frame_extents) = get_frame_extents(xconn, window) {
// Mutter/Muffin/Budgie and Marco preserve their decorated frame extents when
// decorations are disabled, but since the window becomes un-nested, it's easy to
// catch.
if !nested {
frame_extents = FrameExtents::new(0, 0, 0, 0);
if !hint_is_supported(extents_atom) {
return None;
}
// The difference between the nested window's position and the outermost window's
// position is equivalent to the frame size. In most scenarios, this is equivalent to
// manually climbing the hierarchy as is done in the case below. Here's a list of
// known discrepancies:
// * Mutter/Muffin/Budgie gives decorated windows a margin of 9px (only 7px on top) in
// addition to a 1px semi-transparent border. The margin can be easily observed by
// using a screenshot tool to get a screenshot of a selected window, and is
// presumably used for drawing drop shadows. Getting window geometry information
// via hierarchy-climbing results in this margin being included in both the
// position and outer size, so a window positioned at (0, 0) would be reported as
// having a position (-10, -8).
// * Compiz has a drop shadow margin just like Mutter/Muffin/Budgie, though it's 10px
// on all sides, and there's no additional border.
// * Enlightenment otherwise gets a y position equivalent to inner_y_rel_root.
// Without decorations, there's no difference. This is presumably related to
// Enlightenment's fairly unique concept of window position; it interprets
// positions given to XMoveWindow as a client area position rather than a position
// of the overall window.
// Of the WMs tested, xmonad, i3, dwm, IceWM (1.3.x and earlier), and blackbox don't
// support this. As this is part of EWMH (Extended Window Manager Hints), it's likely to
// be unsupported by many smaller WMs.
let extents: Option<Vec<c_ulong>> = self.get_property(
window,
extents_atom,
ffi::XA_CARDINAL,
).ok();
FrameExtentsHeuristic {
frame_extents,
heuristic_path: Supported,
extents.and_then(|extents| {
if extents.len() >= 4 {
Some(FrameExtents {
left: extents[0],
right: extents[1],
top: extents[2],
bottom: extents[3],
})
} else {
None
}
})
}
pub fn is_top_level(&self, window: ffi::Window, root: ffi::Window) -> Option<bool> {
let client_list_atom = unsafe { self.get_atom_unchecked(b"_NET_CLIENT_LIST\0") };
if !hint_is_supported(client_list_atom) {
return None;
}
} else if nested {
// If the position value we have is for a nested window used as the client area, we'll
// just climb up the hierarchy and get the geometry of the outermost window we're
// nested in.
let outer_window = climb_hierarchy(xconn, window, root)
.expect("Failed to climb window hierarchy");
let (outer_y, outer_width, outer_height) = {
let outer_geometry = unsafe { get_geometry(xconn, outer_window) }
.expect("Failed to get outer window geometry");
let client_list: Option<Vec<ffi::Window>> = self.get_property(
root,
client_list_atom,
ffi::XA_WINDOW,
).ok();
client_list.map(|client_list| client_list.contains(&window))
}
fn get_parent_window(&self, window: ffi::Window) -> Result<ffi::Window, XError> {
let parent = unsafe {
let mut root: ffi::Window = mem::uninitialized();
let mut parent: ffi::Window = mem::uninitialized();
let mut children: *mut ffi::Window = ptr::null_mut();
let mut nchildren: c_uint = mem::uninitialized();
// What's filled into `parent` if `window` is the root window?
let _status = (self.xlib.XQueryTree)(
self.display,
window,
&mut root,
&mut parent,
&mut children,
&mut nchildren,
);
// The list of children isn't used
if children != ptr::null_mut() {
(self.xlib.XFree)(children as *mut _);
}
parent
};
self.check_errors().map(|_| parent)
}
fn climb_hierarchy(&self, window: ffi::Window, root: ffi::Window) -> Result<ffi::Window, XError> {
let mut outer_window = window;
loop {
let candidate = self.get_parent_window(outer_window)?;
if candidate == root {
break;
}
outer_window = candidate;
}
Ok(outer_window)
}
pub fn get_frame_extents_heuristic(&self, window: ffi::Window, root: ffi::Window) -> FrameExtentsHeuristic {
use self::FrameExtentsHeuristicPath::*;
// Position relative to root window.
// With rare exceptions, this is the position of a nested window. Cases where the window
// isn't nested are outlined in the comments throghout this function, but in addition to
// that, fullscreen windows often aren't nested.
let (inner_y_rel_root, child) = {
let coords = self.translate_coords(window, root).expect("Failed to translate window coordinates");
(
outer_geometry.y_rel_parent,
outer_geometry.width,
outer_geometry.height,
coords.y_rel_root,
coords.child,
)
};
// Since we have the geometry of the outermost window and the geometry of the client
// area, we can figure out what's in between.
let diff_x = outer_width.saturating_sub(width);
let diff_y = outer_height.saturating_sub(height);
let offset_y = inner_y_rel_root.saturating_sub(outer_y) as c_uint;
let (width, height, border) = {
let inner_geometry = self.get_geometry(window).expect("Failed to get inner window geometry");
(
inner_geometry.width,
inner_geometry.height,
inner_geometry.border,
)
};
let left = diff_x / 2;
let right = left;
let top = offset_y;
let bottom = diff_y.saturating_sub(offset_y);
// The first condition is only false for un-nested windows, but isn't always false for
// un-nested windows. Mutter/Muffin/Budgie and Marco present a mysterious discrepancy:
// when y is on the range [0, 2] and if the window has been unfocused since being
// undecorated (or was undecorated upon construction), the first condition is true,
// requiring us to rely on the second condition.
let nested = !(window == child || self.is_top_level(child, root) == Some(true));
let frame_extents = FrameExtents::new(
left.into(),
right.into(),
top.into(),
bottom.into(),
);
FrameExtentsHeuristic {
frame_extents,
heuristic_path: UnsupportedNested,
}
} else {
// This is the case for xmonad and dwm, AKA the only WMs tested that supplied a
// border value. This is convenient, since we can use it to get an accurate frame.
let frame_extents = FrameExtents::from_border(border.into());
FrameExtentsHeuristic {
frame_extents,
heuristic_path: UnsupportedBordered,
// Hopefully the WM supports EWMH, allowing us to get exact info on the window frames.
if let Some(mut frame_extents) = self.get_frame_extents(window) {
// Mutter/Muffin/Budgie and Marco preserve their decorated frame extents when
// decorations are disabled, but since the window becomes un-nested, it's easy to
// catch.
if !nested {
frame_extents = FrameExtents::new(0, 0, 0, 0);
}
// The difference between the nested window's position and the outermost window's
// position is equivalent to the frame size. In most scenarios, this is equivalent to
// manually climbing the hierarchy as is done in the case below. Here's a list of
// known discrepancies:
// * Mutter/Muffin/Budgie gives decorated windows a margin of 9px (only 7px on top) in
// addition to a 1px semi-transparent border. The margin can be easily observed by
// using a screenshot tool to get a screenshot of a selected window, and is
// presumably used for drawing drop shadows. Getting window geometry information
// via hierarchy-climbing results in this margin being included in both the
// position and outer size, so a window positioned at (0, 0) would be reported as
// having a position (-10, -8).
// * Compiz has a drop shadow margin just like Mutter/Muffin/Budgie, though it's 10px
// on all sides, and there's no additional border.
// * Enlightenment otherwise gets a y position equivalent to inner_y_rel_root.
// Without decorations, there's no difference. This is presumably related to
// Enlightenment's fairly unique concept of window position; it interprets
// positions given to XMoveWindow as a client area position rather than a position
// of the overall window.
FrameExtentsHeuristic {
frame_extents,
heuristic_path: Supported,
}
} else if nested {
// If the position value we have is for a nested window used as the client area, we'll
// just climb up the hierarchy and get the geometry of the outermost window we're
// nested in.
let outer_window = self.climb_hierarchy(window, root).expect("Failed to climb window hierarchy");
let (outer_y, outer_width, outer_height) = {
let outer_geometry = self.get_geometry(outer_window).expect("Failed to get outer window geometry");
(
outer_geometry.y_rel_parent,
outer_geometry.width,
outer_geometry.height,
)
};
// Since we have the geometry of the outermost window and the geometry of the client
// area, we can figure out what's in between.
let diff_x = outer_width.saturating_sub(width);
let diff_y = outer_height.saturating_sub(height);
let offset_y = inner_y_rel_root.saturating_sub(outer_y) as c_uint;
let left = diff_x / 2;
let right = left;
let top = offset_y;
let bottom = diff_y.saturating_sub(offset_y);
let frame_extents = FrameExtents::new(
left.into(),
right.into(),
top.into(),
bottom.into(),
);
FrameExtentsHeuristic {
frame_extents,
heuristic_path: UnsupportedNested,
}
} else {
// This is the case for xmonad and dwm, AKA the only WMs tested that supplied a
// border value. This is convenient, since we can use it to get an accurate frame.
let frame_extents = FrameExtents::from_border(border.into());
FrameExtentsHeuristic {
frame_extents,
heuristic_path: UnsupportedBordered,
}
}
}
}

View File

@@ -1,3 +1,5 @@
use std::sync::Arc;
use super::*;
pub const MWM_HINTS_DECORATIONS: c_ulong = 2;
@@ -6,15 +8,204 @@ pub const MWM_HINTS_DECORATIONS: c_ulong = 2;
pub enum StateOperation {
Remove = 0, // _NET_WM_STATE_REMOVE
Add = 1, // _NET_WM_STATE_ADD
_Toggle = 2, // _NET_WM_STATE_TOGGLE
Toggle = 2, // _NET_WM_STATE_TOGGLE
}
impl From<bool> for StateOperation {
fn from(b: bool) -> Self {
if b {
fn from(op: bool) -> Self {
if op {
StateOperation::Add
} else {
StateOperation::Remove
}
}
}
/// X window type. Maps directly to
/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/1.3/ar01s05.html).
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum WindowType {
/// A desktop feature. This can include a single window containing desktop icons with the same dimensions as the
/// screen, allowing the desktop environment to have full control of the desktop, without the need for proxying
/// root window clicks.
Desktop,
/// A dock or panel feature. Typically a Window Manager would keep such windows on top of all other windows.
Dock,
/// Toolbar windows. "Torn off" from the main application.
Toolbar,
/// Pinnable menu windows. "Torn off" from the main application.
Menu,
/// A small persistent utility window, such as a palette or toolbox.
Utility,
/// The window is a splash screen displayed as an application is starting up.
Splash,
/// This is a dialog window.
Dialog,
/// This is a normal, top-level window.
Normal,
}
impl Default for WindowType {
fn default() -> Self {
WindowType::Normal
}
}
impl WindowType {
pub(crate) fn as_atom(&self, xconn: &Arc<XConnection>) -> ffi::Atom {
use self::WindowType::*;
let atom_name: &[u8] = match self {
&Desktop => b"_NET_WM_WINDOW_TYPE_DESKTOP\0",
&Dock => b"_NET_WM_WINDOW_TYPE_DOCK\0",
&Toolbar => b"_NET_WM_WINDOW_TYPE_TOOLBAR\0",
&Menu => b"_NET_WM_WINDOW_TYPE_MENU\0",
&Utility => b"_NET_WM_WINDOW_TYPE_UTILITY\0",
&Splash => b"_NET_WM_WINDOW_TYPE_SPLASH\0",
&Dialog => b"_NET_WM_WINDOW_TYPE_DIALOG\0",
&Normal => b"_NET_WM_WINDOW_TYPE_NORMAL\0",
};
unsafe { xconn.get_atom_unchecked(atom_name) }
}
}
pub struct NormalHints<'a> {
size_hints: XSmartPointer<'a, ffi::XSizeHints>,
}
impl<'a> NormalHints<'a> {
pub fn new(xconn: &'a XConnection) -> Self {
NormalHints { size_hints: xconn.alloc_size_hints() }
}
pub fn has_flag(&self, flag: c_long) -> bool {
has_flag(self.size_hints.flags, flag)
}
fn getter(&self, flag: c_long, field1: &c_int, field2: &c_int) -> Option<(u32, u32)> {
if self.has_flag(flag) {
Some((*field1 as _, *field2 as _))
} else {
None
}
}
pub fn get_size(&self) -> Option<(u32, u32)> {
self.getter(ffi::PSize, &self.size_hints.width, &self.size_hints.height)
}
// WARNING: This hint is obsolete
pub fn set_size(&mut self, size: Option<(u32, u32)>) {
if let Some((width, height)) = size {
self.size_hints.flags |= ffi::PSize;
self.size_hints.width = width as c_int;
self.size_hints.height = height as c_int;
} else {
self.size_hints.flags &= !ffi::PSize;
}
}
pub fn get_max_size(&self) -> Option<(u32, u32)> {
self.getter(ffi::PMaxSize, &self.size_hints.max_width, &self.size_hints.max_height)
}
pub fn set_max_size(&mut self, max_size: Option<(u32, u32)>) {
if let Some((max_width, max_height)) = max_size {
self.size_hints.flags |= ffi::PMaxSize;
self.size_hints.max_width = max_width as c_int;
self.size_hints.max_height = max_height as c_int;
} else {
self.size_hints.flags &= !ffi::PMaxSize;
}
}
pub fn get_min_size(&self) -> Option<(u32, u32)> {
self.getter(ffi::PMinSize, &self.size_hints.min_width, &self.size_hints.min_height)
}
pub fn set_min_size(&mut self, min_size: Option<(u32, u32)>) {
if let Some((min_width, min_height)) = min_size {
self.size_hints.flags |= ffi::PMinSize;
self.size_hints.min_width = min_width as c_int;
self.size_hints.min_height = min_height as c_int;
} else {
self.size_hints.flags &= !ffi::PMinSize;
}
}
pub fn get_resize_increments(&self) -> Option<(u32, u32)> {
self.getter(ffi::PResizeInc, &self.size_hints.width_inc, &self.size_hints.height_inc)
}
pub fn set_resize_increments(&mut self, resize_increments: Option<(u32, u32)>) {
if let Some((width_inc, height_inc)) = resize_increments {
self.size_hints.flags |= ffi::PResizeInc;
self.size_hints.width_inc = width_inc as c_int;
self.size_hints.height_inc = height_inc as c_int;
} else {
self.size_hints.flags &= !ffi::PResizeInc;
}
}
pub fn get_base_size(&self) -> Option<(u32, u32)> {
self.getter(ffi::PBaseSize, &self.size_hints.base_width, &self.size_hints.base_height)
}
pub fn set_base_size(&mut self, base_size: Option<(u32, u32)>) {
if let Some((base_width, base_height)) = base_size {
self.size_hints.flags |= ffi::PBaseSize;
self.size_hints.base_width = base_width as c_int;
self.size_hints.base_height = base_height as c_int;
} else {
self.size_hints.flags &= !ffi::PBaseSize;
}
}
}
impl XConnection {
pub fn get_wm_hints(&self, window: ffi::Window) -> Result<XSmartPointer<ffi::XWMHints>, XError> {
let wm_hints = unsafe { (self.xlib.XGetWMHints)(self.display, window) };
self.check_errors()?;
let wm_hints = if wm_hints.is_null() {
self.alloc_wm_hints()
} else {
XSmartPointer::new(self, wm_hints).unwrap()
};
Ok(wm_hints)
}
pub fn set_wm_hints(&self, window: ffi::Window, wm_hints: XSmartPointer<ffi::XWMHints>) -> Flusher {
unsafe {
(self.xlib.XSetWMHints)(
self.display,
window,
wm_hints.ptr,
);
}
Flusher::new(self)
}
pub fn get_normal_hints(&self, window: ffi::Window) -> Result<NormalHints, XError> {
let size_hints = self.alloc_size_hints();
let mut supplied_by_user: c_long = unsafe { mem::uninitialized() };
unsafe {
(self.xlib.XGetWMNormalHints)(
self.display,
window,
size_hints.ptr,
&mut supplied_by_user,
);
}
self.check_errors().map(|_| NormalHints { size_hints })
}
pub fn set_normal_hints(&self, window: ffi::Window, normal_hints: NormalHints) -> Flusher {
unsafe {
(self.xlib.XSetWMNormalHints)(
self.display,
window,
normal_hints.size_hints.ptr,
);
}
Flusher::new(self)
}
}

View File

@@ -17,7 +17,7 @@ impl Pixel {
}
impl Icon {
pub fn to_cardinals(&self) -> Vec<Cardinal> {
pub(crate) fn to_cardinals(&self) -> Vec<Cardinal> {
assert_eq!(self.rgba.len() % PIXEL_SIZE, 0);
let pixel_count = self.rgba.len() / PIXEL_SIZE;
assert_eq!(pixel_count, (self.width * self.height) as usize);

View File

@@ -1,44 +1,15 @@
use std::str;
use super::*;
use events::ModifiersState;
pub unsafe fn select_xinput_events(
xconn: &Arc<XConnection>,
window: c_ulong,
device_id: c_int,
mask: i32,
) -> Flusher {
let mut event_mask = ffi::XIEventMask {
deviceid: device_id,
mask: &mask as *const _ as *mut c_uchar,
mask_len: mem::size_of_val(&mask) as c_int,
};
(xconn.xinput2.XISelectEvents)(
xconn.display,
window,
&mut event_mask as *mut ffi::XIEventMask,
1, // number of masks to read from pointer above
);
Flusher::new(xconn)
}
pub const VIRTUAL_CORE_POINTER: c_int = 2;
pub const VIRTUAL_CORE_KEYBOARD: c_int = 3;
#[allow(dead_code)]
pub unsafe fn select_xkb_events(
xconn: &Arc<XConnection>,
device_id: c_uint,
mask: c_ulong,
) -> Option<Flusher> {
let status = (xconn.xlib.XkbSelectEvents)(
xconn.display,
device_id,
mask,
mask,
);
if status == ffi::True {
Some(Flusher::new(xconn))
} else {
None
}
}
// A base buffer size of 1kB uses a negligible amount of RAM while preventing us from having to
// re-allocate (and make another round-trip) in the *vast* majority of cases.
// To test if `lookup_utf8` works correctly, set this to 1.
const TEXT_BUFFER_SIZE: usize = 1024;
impl From<ffi::XIModifierState> for ModifiersState {
fn from(mods: ffi::XIModifierState) -> Self {
@@ -53,17 +24,17 @@ impl From<ffi::XIModifierState> for ModifiersState {
}
pub struct PointerState<'a> {
xconn: &'a Arc<XConnection>,
_root: ffi::Window,
_child: ffi::Window,
_root_x: c_double,
_root_y: c_double,
_win_x: c_double,
_win_y: c_double,
_buttons: ffi::XIButtonState,
xconn: &'a XConnection,
root: ffi::Window,
child: ffi::Window,
pub root_x: c_double,
pub root_y: c_double,
win_x: c_double,
win_y: c_double,
buttons: ffi::XIButtonState,
modifiers: ffi::XIModifierState,
_group: ffi::XIGroupState,
_relative_to_window: bool,
group: ffi::XIGroupState,
relative_to_window: bool,
}
impl<'a> PointerState<'a> {
@@ -74,114 +45,115 @@ impl<'a> PointerState<'a> {
impl<'a> Drop for PointerState<'a> {
fn drop(&mut self) {
unsafe {
// This is why you need to read the docs carefully...
(self.xconn.xlib.XFree)(self._buttons.mask as _);
if !self.buttons.mask.is_null() {
unsafe {
// This is why you need to read the docs carefully...
(self.xconn.xlib.XFree)(self.buttons.mask as _);
}
}
}
}
pub unsafe fn query_pointer(
xconn: &Arc<XConnection>,
window: ffi::Window,
device_id: c_int,
) -> Result<PointerState, XError> {
let mut root_return = mem::uninitialized();
let mut child_return = mem::uninitialized();
let mut root_x_return = mem::uninitialized();
let mut root_y_return = mem::uninitialized();
let mut win_x_return = mem::uninitialized();
let mut win_y_return = mem::uninitialized();
let mut buttons_return = mem::uninitialized();
let mut modifiers_return = mem::uninitialized();
let mut group_return = mem::uninitialized();
impl XConnection {
pub fn select_xinput_events(&self, window: c_ulong, device_id: c_int, mask: i32) -> Flusher {
let mut event_mask = ffi::XIEventMask {
deviceid: device_id,
mask: &mask as *const _ as *mut c_uchar,
mask_len: mem::size_of_val(&mask) as c_int,
};
unsafe {
(self.xinput2.XISelectEvents)(
self.display,
window,
&mut event_mask as *mut ffi::XIEventMask,
1, // number of masks to read from pointer above
);
}
Flusher::new(self)
}
let relative_to_window = (xconn.xinput2.XIQueryPointer)(
xconn.display,
device_id,
window,
&mut root_return,
&mut child_return,
&mut root_x_return,
&mut root_y_return,
&mut win_x_return,
&mut win_y_return,
&mut buttons_return,
&mut modifiers_return,
&mut group_return,
) == ffi::True;
#[allow(dead_code)]
pub fn select_xkb_events(&self, device_id: c_uint, mask: c_ulong) -> Option<Flusher> {
let status = unsafe {
(self.xlib.XkbSelectEvents)(
self.display,
device_id,
mask,
mask,
)
};
if status == ffi::True {
Some(Flusher::new(self))
} else {
None
}
}
xconn.check_errors()?;
pub fn query_pointer(&self, window: ffi::Window, device_id: c_int) -> Result<PointerState, XError> {
unsafe {
let mut pointer_state: PointerState = mem::uninitialized();
pointer_state.xconn = self;
pointer_state.relative_to_window = (self.xinput2.XIQueryPointer)(
self.display,
device_id,
window,
&mut pointer_state.root,
&mut pointer_state.child,
&mut pointer_state.root_x,
&mut pointer_state.root_y,
&mut pointer_state.win_x,
&mut pointer_state.win_y,
&mut pointer_state.buttons,
&mut pointer_state.modifiers,
&mut pointer_state.group,
) == ffi::True;
if let Err(err) = self.check_errors() {
// Running the destrutor would be bad news for us...
mem::forget(pointer_state);
Err(err)
} else {
Ok(pointer_state)
}
}
}
Ok(PointerState {
xconn,
_root: root_return,
_child: child_return,
_root_x: root_x_return,
_root_y: root_y_return,
_win_x: win_x_return,
_win_y: win_y_return,
_buttons: buttons_return,
modifiers: modifiers_return,
_group: group_return,
_relative_to_window: relative_to_window,
})
}
fn lookup_utf8_inner(
&self,
ic: ffi::XIC,
key_event: &mut ffi::XKeyEvent,
buffer: &mut [u8],
) -> (ffi::KeySym, ffi::Status, c_int) {
let mut keysym: ffi::KeySym = 0;
let mut status: ffi::Status = 0;
let count = unsafe {
(self.xlib.Xutf8LookupString)(
ic,
key_event,
buffer.as_mut_ptr() as *mut c_char,
buffer.len() as c_int,
&mut keysym,
&mut status,
)
};
(keysym, status, count)
}
unsafe fn lookup_utf8_inner(
xconn: &Arc<XConnection>,
ic: ffi::XIC,
key_event: &mut ffi::XKeyEvent,
buffer: &mut [u8],
) -> (ffi::KeySym, ffi::Status, c_int) {
let mut keysym: ffi::KeySym = 0;
let mut status: ffi::Status = 0;
let count = (xconn.xlib.Xutf8LookupString)(
ic,
key_event,
buffer.as_mut_ptr() as *mut c_char,
buffer.len() as c_int,
&mut keysym,
&mut status,
);
(keysym, status, count)
}
// A base buffer size of 1kB uses a negligible amount of RAM while preventing us from having to
// re-allocate (and make another round-trip) in the *vast* majority of cases.
// To test if lookup_utf8 works correctly, set this to 1.
const TEXT_BUFFER_SIZE: usize = 1024;
pub unsafe fn lookup_utf8(
xconn: &Arc<XConnection>,
ic: ffi::XIC,
key_event: &mut ffi::XKeyEvent,
) -> String {
let mut buffer: [u8; TEXT_BUFFER_SIZE] = mem::uninitialized();
let (_, status, count) = lookup_utf8_inner(
xconn,
ic,
key_event,
&mut buffer,
);
// The buffer overflowed, so we'll make a new one on the heap.
if status == ffi::XBufferOverflow {
let mut buffer = Vec::with_capacity(count as usize);
buffer.set_len(count as usize);
let (_, _, new_count) = lookup_utf8_inner(
xconn,
ic,
key_event,
&mut buffer,
);
debug_assert_eq!(count, new_count);
str::from_utf8(&buffer[..count as usize])
.unwrap_or("")
.to_string()
} else {
str::from_utf8(&buffer[..count as usize])
.unwrap_or("")
.to_string()
pub fn lookup_utf8(&self, ic: ffi::XIC, key_event: &mut ffi::XKeyEvent) -> String {
let mut buffer: [u8; TEXT_BUFFER_SIZE] = unsafe { mem::uninitialized() };
let (_, status, count) = self.lookup_utf8_inner(ic, key_event, &mut buffer);
// The buffer overflowed, so we'll make a new one on the heap.
if status == ffi::XBufferOverflow {
let mut buffer = Vec::with_capacity(count as usize);
unsafe { buffer.set_len(count as usize) };
let (_, _, new_count) = self.lookup_utf8_inner(ic, key_event, &mut buffer);
debug_assert_eq!(count, new_count);
str::from_utf8(&buffer[..count as usize])
.unwrap_or("")
.to_string()
} else {
str::from_utf8(&buffer[..count as usize])
.unwrap_or("")
.to_string()
}
}
}

View File

@@ -0,0 +1,62 @@
use std::ops::{Deref, DerefMut};
use super::*;
pub struct XSmartPointer<'a, T> {
xconn: &'a XConnection,
pub ptr: *mut T,
}
impl<'a, T> XSmartPointer<'a, T> {
// You're responsible for only passing things to this that should be XFree'd.
// Returns None if ptr is null.
pub fn new(xconn: &'a XConnection, ptr: *mut T) -> Option<Self> {
if !ptr.is_null() {
Some(XSmartPointer {
xconn,
ptr,
})
} else {
None
}
}
}
impl<'a, T> Deref for XSmartPointer<'a, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.ptr }
}
}
impl<'a, T> DerefMut for XSmartPointer<'a, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.ptr }
}
}
impl<'a, T> Drop for XSmartPointer<'a, T> {
fn drop(&mut self) {
unsafe {
(self.xconn.xlib.XFree)(self.ptr as *mut _);
}
}
}
impl XConnection {
pub fn alloc_class_hint(&self) -> XSmartPointer<ffi::XClassHint> {
XSmartPointer::new(self, unsafe { (self.xlib.XAllocClassHint)() })
.expect("`XAllocClassHint` returned null; out of memory")
}
pub fn alloc_size_hints(&self) -> XSmartPointer<ffi::XSizeHints> {
XSmartPointer::new(self, unsafe { (self.xlib.XAllocSizeHints)() })
.expect("`XAllocSizeHints` returned null; out of memory")
}
pub fn alloc_wm_hints(&self) -> XSmartPointer<ffi::XWMHints> {
XSmartPointer::new(self, unsafe { (self.xlib.XAllocWMHints)() })
.expect("`XAllocWMHints` returned null; out of memory")
}
}

View File

@@ -2,178 +2,103 @@
// *results may vary
mod atom;
mod client_msg;
mod format;
mod geometry;
mod hint;
mod icon;
mod input;
mod memory;
mod randr;
mod window_property;
mod wm;
pub use self::atom::*;
pub use self::client_msg::*;
pub use self::format::*;
pub use self::geometry::*;
pub use self::hint::*;
pub use self::icon::*;
pub use self::input::*;
pub use self::memory::*;
pub use self::randr::*;
pub use self::window_property::*;
pub use self::wm::*;
use std::mem;
use std::ptr;
use std::str;
use std::sync::Arc;
use std::ops::{Deref, DerefMut};
use std::ops::BitAnd;
use std::os::raw::*;
use super::{ffi, XConnection, XError};
// This isn't actually the number of the bits in the format.
// X11 does a match on this value to determine which type to call sizeof on.
// Thus, we use 32 for c_long, since 32 maps to c_long which maps to 64.
// ...if that sounds confusing, then you know why this enum is here.
#[derive(Debug, Copy, Clone)]
pub enum Format {
Char = 8,
Short = 16,
Long = 32,
pub fn reinterpret<'a, A, B>(a: &'a A) -> &'a B {
let b_ptr = a as *const _ as *const B;
unsafe { &*b_ptr }
}
impl Format {
pub fn from_format(format: usize) -> Option<Self> {
match format {
8 => Some(Format::Char),
16 => Some(Format::Short),
32 => Some(Format::Long),
_ => None,
}
}
pub fn is_same_size_as<T>(&self) -> bool {
mem::size_of::<T>() == self.get_actual_size()
}
pub fn get_actual_size(&self) -> usize {
match self {
&Format::Char => mem::size_of::<c_char>(),
&Format::Short => mem::size_of::<c_short>(),
&Format::Long => mem::size_of::<c_long>(),
}
pub fn maybe_change<T: PartialEq>(field: &mut Option<T>, value: T) -> bool {
let wrapped = Some(value);
if *field != wrapped {
*field = wrapped;
true
} else {
false
}
}
pub struct XSmartPointer<'a, T> {
xconn: &'a Arc<XConnection>,
pub ptr: *mut T,
}
impl<'a, T> XSmartPointer<'a, T> {
// You're responsible for only passing things to this that should be XFree'd.
// Returns None if ptr is null.
pub fn new(xconn: &'a Arc<XConnection>, ptr: *mut T) -> Option<Self> {
if !ptr.is_null() {
Some(XSmartPointer {
xconn,
ptr,
})
} else {
None
}
}
}
impl<'a, T> Deref for XSmartPointer<'a, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.ptr }
}
}
impl<'a, T> DerefMut for XSmartPointer<'a, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.ptr }
}
}
impl<'a, T> Drop for XSmartPointer<'a, T> {
fn drop(&mut self) {
unsafe {
(self.xconn.xlib.XFree)(self.ptr as *mut _);
}
}
}
// This is impoartant, so pay attention!
// Xlib has an output buffer, and tries to hide the async nature of X from you.
// This buffer contains the requests you make, and is flushed under various circumstances:
// 1. XPending, XNextEvent, and XWindowEvent flush "as needed"
// 2. XFlush explicitly flushes
// 3. XSync flushes and blocks until all requests are responded to
// 4. Calls that have a return dependent on a response (i.e. XGetWindowProperty) sync internally.
// When in doubt, check the X11 source; if a function calls _XReply, it flushes and waits.
// All util functions that abstract an async function will return a Flusher.
pub unsafe fn flush_requests(xconn: &Arc<XConnection>) -> Result<(), XError> {
(xconn.xlib.XFlush)(xconn.display);
//println!("XFlush");
// This isn't necessarily a useful time to check for errors (since our request hasn't
// necessarily been processed yet)
xconn.check_errors()
}
pub unsafe fn sync_with_server(xconn: &Arc<XConnection>) -> Result<(), XError> {
(xconn.xlib.XSync)(xconn.display, ffi::False);
//println!("XSync");
xconn.check_errors()
pub fn has_flag<T>(bitset: T, flag: T) -> bool
where T:
Copy + PartialEq + BitAnd<T, Output = T>
{
bitset & flag == flag
}
#[must_use = "This request was made asynchronously, and is still in the output buffer. You must explicitly choose to either `.flush()` (empty the output buffer, sending the request now) or `.queue()` (wait to send the request, allowing you to continue to add more requests without additional round-trips). For more information, see the documentation for `util::flush_requests`."]
pub struct Flusher<'a> {
xconn: &'a Arc<XConnection>,
xconn: &'a XConnection,
}
impl<'a> Flusher<'a> {
pub fn new(xconn: &'a Arc<XConnection>) -> Self {
pub fn new(xconn: &'a XConnection) -> Self {
Flusher { xconn }
}
// "I want this request sent now!"
pub fn flush(self) -> Result<(), XError> {
unsafe { flush_requests(self.xconn) }
self.xconn.flush_requests()
}
// "I want the response now too!"
pub fn sync(self) -> Result<(), XError> {
self.xconn.sync_with_server()
}
// "I'm aware that this request hasn't been sent, and I'm okay with waiting."
pub fn queue(self) {}
}
pub unsafe fn send_client_msg(
xconn: &Arc<XConnection>,
window: c_ulong, // The window this is "about"; not necessarily this window
target_window: c_ulong, // The window we're sending to
message_type: ffi::Atom,
event_mask: Option<c_long>,
data: (c_long, c_long, c_long, c_long, c_long),
) -> Flusher {
let mut event: ffi::XClientMessageEvent = mem::uninitialized();
event.type_ = ffi::ClientMessage;
event.display = xconn.display;
event.window = window;
event.message_type = message_type;
event.format = Format::Long as c_int;
event.data = ffi::ClientMessageData::new();
event.data.set_long(0, data.0);
event.data.set_long(1, data.1);
event.data.set_long(2, data.2);
event.data.set_long(3, data.3);
event.data.set_long(4, data.4);
impl XConnection {
// This is impoartant, so pay attention!
// Xlib has an output buffer, and tries to hide the async nature of X from you.
// This buffer contains the requests you make, and is flushed under various circumstances:
// 1. `XPending`, `XNextEvent`, and `XWindowEvent` flush "as needed"
// 2. `XFlush` explicitly flushes
// 3. `XSync` flushes and blocks until all requests are responded to
// 4. Calls that have a return dependent on a response (i.e. `XGetWindowProperty`) sync internally.
// When in doubt, check the X11 source; if a function calls `_XReply`, it flushes and waits.
// All util functions that abstract an async function will return a `Flusher`.
pub fn flush_requests(&self) -> Result<(), XError> {
unsafe { (self.xlib.XFlush)(self.display) };
//println!("XFlush");
// This isn't necessarily a useful time to check for errors (since our request hasn't
// necessarily been processed yet)
self.check_errors()
}
let event_mask = event_mask.unwrap_or(ffi::NoEventMask);
(xconn.xlib.XSendEvent)(
xconn.display,
target_window,
ffi::False,
event_mask,
&mut event.into(),
);
Flusher::new(xconn)
pub fn sync_with_server(&self) -> Result<(), XError> {
unsafe { (self.xlib.XSync)(self.display, ffi::False) };
//println!("XSync");
self.check_errors()
}
}

View File

@@ -0,0 +1,100 @@
use std::{env, slice};
use std::str::FromStr;
use validate_hidpi_factor;
use super::*;
pub fn calc_dpi_factor(
(width_px, height_px): (u32, u32),
(width_mm, height_mm): (u64, u64),
) -> f64 {
// Override DPI if `WINIT_HIDPI_FACTOR` variable is set
let dpi_override = env::var("WINIT_HIDPI_FACTOR")
.ok()
.and_then(|var| f64::from_str(&var).ok());
if let Some(dpi_override) = dpi_override {
if !validate_hidpi_factor(dpi_override) {
panic!(
"`WINIT_HIDPI_FACTOR` invalid; DPI factors must be normal floats greater than 0. Got `{}`",
dpi_override,
);
}
return dpi_override;
}
// See http://xpra.org/trac/ticket/728 for more information.
if width_mm == 0 || width_mm == 0 {
warn!("XRandR reported that the display's 0mm in size, which is certifiably insane");
return 1.0;
}
let ppmm = (
(width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)
).sqrt();
// Quantize 1/12 step size
let dpi_factor = ((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0);
assert!(validate_hidpi_factor(dpi_factor));
dpi_factor
}
pub enum MonitorRepr {
Monitor(*mut ffi::XRRMonitorInfo),
Crtc(*mut ffi::XRRCrtcInfo),
}
impl MonitorRepr {
pub unsafe fn get_output(&self) -> ffi::RROutput {
match *self {
// Same member names, but different locations within the struct...
MonitorRepr::Monitor(monitor) => *((*monitor).outputs.offset(0)),
MonitorRepr::Crtc(crtc) => *((*crtc).outputs.offset(0)),
}
}
pub unsafe fn get_dimensions(&self) -> (u32, u32) {
match *self {
MonitorRepr::Monitor(monitor) => ((*monitor).width as u32, (*monitor).height as u32),
MonitorRepr::Crtc(crtc) => ((*crtc).width as u32, (*crtc).height as u32),
}
}
pub unsafe fn get_position(&self) -> (i32, i32) {
match *self {
MonitorRepr::Monitor(monitor) => ((*monitor).x as i32, (*monitor).y as i32),
MonitorRepr::Crtc(crtc) => ((*crtc).x as i32, (*crtc).y as i32),
}
}
}
impl From<*mut ffi::XRRMonitorInfo> for MonitorRepr {
fn from(monitor: *mut ffi::XRRMonitorInfo) -> Self {
MonitorRepr::Monitor(monitor)
}
}
impl From<*mut ffi::XRRCrtcInfo> for MonitorRepr {
fn from(crtc: *mut ffi::XRRCrtcInfo) -> Self {
MonitorRepr::Crtc(crtc)
}
}
impl XConnection {
pub unsafe fn get_output_info(&self, resources: *mut ffi::XRRScreenResources, repr: &MonitorRepr) -> (String, f64) {
let output_info = (self.xrandr.XRRGetOutputInfo)(
self.display,
resources,
repr.get_output(),
);
let name_slice = slice::from_raw_parts(
(*output_info).name as *mut u8,
(*output_info).nameLen as usize,
);
let name = String::from_utf8_lossy(name_slice).into();
let hidpi_factor = calc_dpi_factor(
repr.get_dimensions(),
((*output_info).mm_width as u64, (*output_info).mm_height as u64),
);
(self.xrandr.XRRFreeOutputInfo)(output_info);
(name, hidpi_factor)
}
}

View File

@@ -1,5 +1,4 @@
use std;
use std::fmt::Debug;
use super::*;
@@ -25,139 +24,121 @@ impl GetPropertyError {
}
// Number of 32-bit chunks to retrieve per iteration of get_property's inner loop.
// To test if get_property works correctly, set this to 1.
// To test if `get_property` works correctly, set this to 1.
const PROPERTY_BUFFER_SIZE: c_long = 1024; // 4k of RAM ought to be enough for anyone!
pub unsafe fn get_property<T: Debug + Clone>(
xconn: &Arc<XConnection>,
window: c_ulong,
property: ffi::Atom,
property_type: ffi::Atom,
) -> Result<Vec<T>, GetPropertyError> {
let mut data = Vec::new();
let mut offset = 0;
let mut done = false;
while !done {
let mut actual_type: ffi::Atom = mem::uninitialized();
let mut actual_format: c_int = mem::uninitialized();
let mut quantity_returned: c_ulong = mem::uninitialized();
let mut bytes_after: c_ulong = mem::uninitialized();
let mut buf: *mut c_uchar = ptr::null_mut();
(xconn.xlib.XGetWindowProperty)(
xconn.display,
window,
property,
// This offset is in terms of 32-bit chunks.
offset,
// This is the quanity of 32-bit chunks to receive at once.
PROPERTY_BUFFER_SIZE,
ffi::False,
property_type,
&mut actual_type,
&mut actual_format,
// This is the quantity of items we retrieved in our format, NOT of 32-bit chunks!
&mut quantity_returned,
// ...and this is a quantity of bytes. So, this function deals in 3 different units.
&mut bytes_after,
&mut buf,
);
if let Err(e) = xconn.check_errors() {
return Err(GetPropertyError::XError(e));
}
if actual_type != property_type {
return Err(GetPropertyError::TypeMismatch(actual_type));
}
let format_mismatch = Format::from_format(actual_format as _)
.map(|actual_format| !actual_format.is_same_size_as::<T>())
// This won't actually be reached; the XError condition above is triggered first.
.unwrap_or(true);
if format_mismatch {
return Err(GetPropertyError::FormatMismatch(actual_format));
}
if !buf.is_null() {
offset += PROPERTY_BUFFER_SIZE;
let new_data = std::slice::from_raw_parts(
buf as *mut T,
quantity_returned as usize,
);
/*println!(
"XGetWindowProperty prop:{:?} fmt:{:02} len:{:02} off:{:02} out:{:02}, buf:{:?}",
property,
mem::size_of::<T>() * 8,
data.len(),
offset,
quantity_returned,
new_data,
);*/
data.extend_from_slice(&new_data);
// Fun fact: XGetWindowProperty allocates one extra byte at the end.
(xconn.xlib.XFree)(buf as _); // Don't try to access new_data after this.
} else {
return Err(GetPropertyError::NothingAllocated);
}
done = bytes_after == 0;
}
Ok(data)
}
#[derive(Debug)]
pub enum PropMode {
Replace = ffi::PropModeReplace as isize,
_Prepend = ffi::PropModePrepend as isize,
_Append = ffi::PropModeAppend as isize,
Prepend = ffi::PropModePrepend as isize,
Append = ffi::PropModeAppend as isize,
}
#[derive(Debug, Clone)]
pub struct InvalidFormat {
format_used: Format,
size_passed: usize,
size_expected: usize,
}
impl XConnection {
pub fn get_property<T: Formattable>(
&self,
window: c_ulong,
property: ffi::Atom,
property_type: ffi::Atom,
) -> Result<Vec<T>, GetPropertyError> {
let mut data = Vec::new();
let mut offset = 0;
pub unsafe fn change_property<'a, T: Debug>(
xconn: &'a Arc<XConnection>,
window: c_ulong,
property: ffi::Atom,
property_type: ffi::Atom,
format: Format,
mode: PropMode,
new_value: &[T],
) -> Flusher<'a> {
if !format.is_same_size_as::<T>() {
panic!(format!(
"[winit developer error] Incorrect usage of `util::change_property`: {:#?}",
InvalidFormat {
format_used: format,
size_passed: mem::size_of::<T>() * 8,
size_expected: format.get_actual_size() * 8,
},
));
let mut done = false;
while !done {
unsafe {
let mut actual_type: ffi::Atom = mem::uninitialized();
let mut actual_format: c_int = mem::uninitialized();
let mut quantity_returned: c_ulong = mem::uninitialized();
let mut bytes_after: c_ulong = mem::uninitialized();
let mut buf: *mut c_uchar = ptr::null_mut();
(self.xlib.XGetWindowProperty)(
self.display,
window,
property,
// This offset is in terms of 32-bit chunks.
offset,
// This is the quanity of 32-bit chunks to receive at once.
PROPERTY_BUFFER_SIZE,
ffi::False,
property_type,
&mut actual_type,
&mut actual_format,
// This is the quantity of items we retrieved in our format, NOT of 32-bit chunks!
&mut quantity_returned,
// ...and this is a quantity of bytes. So, this function deals in 3 different units.
&mut bytes_after,
&mut buf,
);
if let Err(e) = self.check_errors() {
return Err(GetPropertyError::XError(e));
}
if actual_type != property_type {
return Err(GetPropertyError::TypeMismatch(actual_type));
}
let format_mismatch = Format::from_format(actual_format as _) != Some(T::FORMAT);
if format_mismatch {
return Err(GetPropertyError::FormatMismatch(actual_format));
}
if !buf.is_null() {
offset += PROPERTY_BUFFER_SIZE;
let new_data = std::slice::from_raw_parts(
buf as *mut T,
quantity_returned as usize,
);
/*println!(
"XGetWindowProperty prop:{:?} fmt:{:02} len:{:02} off:{:02} out:{:02}, buf:{:?}",
property,
mem::size_of::<T>() * 8,
data.len(),
offset,
quantity_returned,
new_data,
);*/
data.extend_from_slice(&new_data);
// Fun fact: XGetWindowProperty allocates one extra byte at the end.
(self.xlib.XFree)(buf as _); // Don't try to access new_data after this.
} else {
return Err(GetPropertyError::NothingAllocated);
}
done = bytes_after == 0;
}
}
Ok(data)
}
(xconn.xlib.XChangeProperty)(
xconn.display,
window,
property,
property_type,
format as c_int,
mode as c_int,
new_value.as_ptr() as *const c_uchar,
new_value.len() as c_int,
);
/*println!(
"XChangeProperty prop:{:?} val:{:?}",
property,
new_value,
);*/
Flusher::new(xconn)
pub fn change_property<'a, T: Formattable>(
&'a self,
window: c_ulong,
property: ffi::Atom,
property_type: ffi::Atom,
mode: PropMode,
new_value: &[T],
) -> Flusher<'a> {
debug_assert_eq!(mem::size_of::<T>(), T::FORMAT.get_actual_size());
unsafe {
(self.xlib.XChangeProperty)(
self.display,
window,
property,
property_type,
T::FORMAT as c_int,
mode as c_int,
new_value.as_ptr() as *const c_uchar,
new_value.len() as c_int,
);
}
/*println!(
"XChangeProperty prop:{:?} val:{:?}",
property,
new_value,
);*/
Flusher::new(self)
}
}

View File

@@ -20,139 +20,122 @@ pub fn wm_name_is_one_of(names: &[&str]) -> bool {
}
}
pub fn update_cached_wm_info(xconn: &Arc<XConnection>, root: ffi::Window) {
*SUPPORTED_HINTS.lock() = self::get_supported_hints(xconn, root);
*WM_NAME.lock() = self::get_wm_name(xconn, root);
}
impl XConnection {
pub fn update_cached_wm_info(&self, root: ffi::Window) {
*SUPPORTED_HINTS.lock() = self.get_supported_hints(root);
*WM_NAME.lock() = self.get_wm_name(root);
}
fn get_supported_hints(xconn: &Arc<XConnection>, root: ffi::Window) -> Vec<ffi::Atom> {
let supported_atom = unsafe { self::get_atom(xconn, b"_NET_SUPPORTED\0") }
.expect("Failed to call XInternAtom (_NET_SUPPORTED)");
unsafe {
self::get_property(
xconn,
fn get_supported_hints(&self, root: ffi::Window) -> Vec<ffi::Atom> {
let supported_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTED\0") };
self.get_property(
root,
supported_atom,
ffi::XA_ATOM,
)
}.unwrap_or_else(|_| Vec::with_capacity(0))
}
).unwrap_or_else(|_| Vec::with_capacity(0))
}
fn get_wm_name(xconn: &Arc<XConnection>, root: ffi::Window) -> Option<String> {
let check_atom = unsafe { self::get_atom(xconn, b"_NET_SUPPORTING_WM_CHECK\0") }
.expect("Failed to call XInternAtom (_NET_SUPPORTING_WM_CHECK)");
let wm_name_atom = unsafe { self::get_atom(xconn, b"_NET_WM_NAME\0") }
.expect("Failed to call XInternAtom (_NET_WM_NAME)");
fn get_wm_name(&self, root: ffi::Window) -> Option<String> {
let check_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTING_WM_CHECK\0") };
let wm_name_atom = unsafe { self.get_atom_unchecked(b"_NET_WM_NAME\0") };
// Mutter/Muffin/Budgie doesn't have _NET_SUPPORTING_WM_CHECK in its _NET_SUPPORTED, despite
// it working and being supported. This has been reported upstream, but due to the
// inavailability of time machines, we'll just try to get _NET_SUPPORTING_WM_CHECK
// regardless of whether or not the WM claims to support it.
//
// Blackbox 0.70 also incorrectly reports not supporting this, though that appears to be fixed
// in 0.72.
/*if !supported_hints.contains(&check_atom) {
return None;
}*/
// Mutter/Muffin/Budgie doesn't have _NET_SUPPORTING_WM_CHECK in its _NET_SUPPORTED, despite
// it working and being supported. This has been reported upstream, but due to the
// inavailability of time machines, we'll just try to get _NET_SUPPORTING_WM_CHECK
// regardless of whether or not the WM claims to support it.
//
// Blackbox 0.70 also incorrectly reports not supporting this, though that appears to be fixed
// in 0.72.
/*if !supported_hints.contains(&check_atom) {
return None;
}*/
// IceWM (1.3.x and earlier) doesn't report supporting _NET_WM_NAME, but will nonetheless
// provide us with a value for it. Note that the unofficial 1.4 fork of IceWM works fine.
/*if !supported_hints.contains(&wm_name_atom) {
return None;
}*/
// IceWM (1.3.x and earlier) doesn't report supporting _NET_WM_NAME, but will nonetheless
// provide us with a value for it. Note that the unofficial 1.4 fork of IceWM works fine.
/*if !supported_hints.contains(&wm_name_atom) {
return None;
}*/
// Of the WMs tested, only xmonad and dwm fail to provide a WM name.
// Of the WMs tested, only xmonad and dwm fail to provide a WM name.
// Querying this property on the root window will give us the ID of a child window created by
// the WM.
let root_window_wm_check = {
let result = unsafe {
self::get_property(
xconn,
// Querying this property on the root window will give us the ID of a child window created by
// the WM.
let root_window_wm_check = {
let result = self.get_property(
root,
check_atom,
ffi::XA_WINDOW,
)
);
let wm_check = result
.ok()
.and_then(|wm_check| wm_check.get(0).cloned());
if let Some(wm_check) = wm_check {
wm_check
} else {
return None;
}
};
let wm_check = result
.ok()
.and_then(|wm_check| wm_check.get(0).cloned());
if let Some(wm_check) = wm_check {
wm_check
} else {
return None;
}
};
// Querying the same property on the child window we were given, we should get this child
// window's ID again.
let child_window_wm_check = {
let result = unsafe {
self::get_property(
xconn,
// Querying the same property on the child window we were given, we should get this child
// window's ID again.
let child_window_wm_check = {
let result = self.get_property(
root_window_wm_check,
check_atom,
ffi::XA_WINDOW,
)
);
let wm_check = result
.ok()
.and_then(|wm_check| wm_check.get(0).cloned());
if let Some(wm_check) = wm_check {
wm_check
} else {
return None;
}
};
let wm_check = result
.ok()
.and_then(|wm_check| wm_check.get(0).cloned());
if let Some(wm_check) = wm_check {
wm_check
} else {
// These values should be the same.
if root_window_wm_check != child_window_wm_check {
return None;
}
};
// These values should be the same.
if root_window_wm_check != child_window_wm_check {
return None;
}
// All of that work gives us a window ID that we can get the WM name from.
let wm_name = {
let utf8_string_atom = unsafe { self.get_atom_unchecked(b"UTF8_STRING\0") };
// All of that work gives us a window ID that we can get the WM name from.
let wm_name = {
let utf8_string_atom = unsafe { self::get_atom(xconn, b"UTF8_STRING\0") }
.expect("Failed to call XInternAtom (UTF8_STRING)");
let result = unsafe {
self::get_property(
xconn,
let result = self.get_property(
root_window_wm_check,
wm_name_atom,
utf8_string_atom,
)
};
);
// IceWM requires this. IceWM was also the only WM tested that returns a null-terminated
// string. For more fun trivia, IceWM is also unique in including version and uname
// information in this string (this means you'll have to be careful if you want to match
// against it, though).
// The unofficial 1.4 fork of IceWM still includes the extra details, but properly
// returns a UTF8 string that isn't null-terminated.
let no_utf8 = if let Err(ref err) = result {
err.is_actual_property_type(ffi::XA_STRING)
} else {
false
};
// IceWM requires this. IceWM was also the only WM tested that returns a null-terminated
// string. For more fun trivia, IceWM is also unique in including version and uname
// information in this string (this means you'll have to be careful if you want to match
// against it, though).
// The unofficial 1.4 fork of IceWM still includes the extra details, but properly
// returns a UTF8 string that isn't null-terminated.
let no_utf8 = if let Err(ref err) = result {
err.is_actual_property_type(ffi::XA_STRING)
} else {
false
};
if no_utf8 {
unsafe {
self::get_property(
xconn,
if no_utf8 {
self.get_property(
root_window_wm_check,
wm_name_atom,
ffi::XA_STRING,
)
} else {
result
}
} else {
result
}
}.ok();
}.ok();
wm_name.and_then(|wm_name| String::from_utf8(wm_name).ok())
wm_name.and_then(|wm_name| String::from_utf8(wm_name).ok())
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,14 @@
use {ControlFlow, EventsLoopClosed};
use cocoa::{self, appkit, foundation};
use cocoa::appkit::{NSApplication, NSEvent, NSEventMask, NSEventModifierFlags, NSEventPhase, NSView, NSWindow};
use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent, DeviceEvent, ModifiersState, KeyboardInput};
use events::{self, ElementState, Event, TouchPhase, WindowEvent, DeviceEvent, ModifiersState, KeyboardInput};
use std::collections::VecDeque;
use std::sync::{Arc, Mutex, Weak};
use super::window::Window2;
use std;
use std::os::raw::*;
use super::DeviceId;
pub struct EventsLoop {
modifiers: Modifiers,
pub shared: Arc<Shared>,
@@ -294,10 +294,7 @@ impl EventsLoop {
// FIXME: Document this. Why do we do this? Seems like it passes on events to window/app.
// If we don't do this, window does not become main for some reason.
match event_type {
appkit::NSKeyDown => (),
_ => appkit::NSApp().sendEvent_(ns_event),
}
appkit::NSApp().sendEvent_(ns_event);
let windows = self.shared.windows.lock().unwrap();
let maybe_window = windows.iter()
@@ -318,137 +315,53 @@ impl EventsLoop {
});
match event_type {
appkit::NSKeyDown => {
let mut events = std::collections::VecDeque::new();
let received_c_str = foundation::NSString::UTF8String(ns_event.characters());
let received_str = std::ffi::CStr::from_ptr(received_c_str);
let vkey = to_virtual_key_code(NSEvent::keyCode(ns_event));
let state = ElementState::Pressed;
let code = NSEvent::keyCode(ns_event) as u32;
let window_event = WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state: state,
scancode: code,
virtual_keycode: vkey,
modifiers: event_mods(ns_event),
},
};
for received_char in std::str::from_utf8(received_str.to_bytes()).unwrap().chars() {
let window_event = WindowEvent::ReceivedCharacter(received_char);
events.push_back(into_event(window_event));
}
self.shared.pending_events.lock().unwrap().extend(events.into_iter());
Some(into_event(window_event))
},
appkit::NSKeyUp => {
let vkey = to_virtual_key_code(NSEvent::keyCode(ns_event));
let state = ElementState::Released;
let code = NSEvent::keyCode(ns_event) as u32;
let window_event = WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state: state,
scancode: code,
virtual_keycode: vkey,
modifiers: event_mods(ns_event),
},
};
Some(into_event(window_event))
},
appkit::NSFlagsChanged => {
unsafe fn modifier_event(event: cocoa::base::id,
keymask: NSEventModifierFlags,
key: events::VirtualKeyCode,
key_pressed: bool) -> Option<WindowEvent>
{
if !key_pressed && NSEvent::modifierFlags(event).contains(keymask) {
let state = ElementState::Pressed;
let code = NSEvent::keyCode(event) as u32;
let window_event = WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state: state,
scancode: code,
virtual_keycode: Some(key),
modifiers: event_mods(event),
},
};
Some(window_event)
} else if key_pressed && !NSEvent::modifierFlags(event).contains(keymask) {
let state = ElementState::Released;
let code = NSEvent::keyCode(event) as u32;
let window_event = WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state: state,
scancode: code,
virtual_keycode: Some(key),
modifiers: event_mods(event),
},
};
Some(window_event)
} else {
None
}
}
let mut events = std::collections::VecDeque::new();
if let Some(window_event) = modifier_event(ns_event,
NSEventModifierFlags::NSShiftKeyMask,
events::VirtualKeyCode::LShift,
self.modifiers.shift_pressed)
{
if let Some(window_event) = modifier_event(
ns_event,
NSEventModifierFlags::NSShiftKeyMask,
self.modifiers.shift_pressed,
) {
self.modifiers.shift_pressed = !self.modifiers.shift_pressed;
events.push_back(into_event(window_event));
}
if let Some(window_event) = modifier_event(ns_event,
NSEventModifierFlags::NSControlKeyMask,
events::VirtualKeyCode::LControl,
self.modifiers.ctrl_pressed)
{
if let Some(window_event) = modifier_event(
ns_event,
NSEventModifierFlags::NSControlKeyMask,
self.modifiers.ctrl_pressed,
) {
self.modifiers.ctrl_pressed = !self.modifiers.ctrl_pressed;
events.push_back(into_event(window_event));
}
if let Some(window_event) = modifier_event(ns_event,
NSEventModifierFlags::NSCommandKeyMask,
events::VirtualKeyCode::LWin,
self.modifiers.win_pressed)
{
if let Some(window_event) = modifier_event(
ns_event,
NSEventModifierFlags::NSCommandKeyMask,
self.modifiers.win_pressed,
) {
self.modifiers.win_pressed = !self.modifiers.win_pressed;
events.push_back(into_event(window_event));
}
if let Some(window_event) = modifier_event(ns_event,
NSEventModifierFlags::NSAlternateKeyMask,
events::VirtualKeyCode::LAlt,
self.modifiers.alt_pressed)
{
if let Some(window_event) = modifier_event(
ns_event,
NSEventModifierFlags::NSAlternateKeyMask,
self.modifiers.alt_pressed,
) {
self.modifiers.alt_pressed = !self.modifiers.alt_pressed;
events.push_back(into_event(window_event));
}
let event = events.pop_front();
self.shared.pending_events.lock().unwrap().extend(events.into_iter());
self.shared.pending_events
.lock()
.unwrap()
.extend(events.into_iter());
event
},
appkit::NSLeftMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Left, modifiers: event_mods(ns_event) })) },
appkit::NSLeftMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Left, modifiers: event_mods(ns_event) })) },
appkit::NSRightMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Right, modifiers: event_mods(ns_event) })) },
appkit::NSRightMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Right, modifiers: event_mods(ns_event) })) },
appkit::NSOtherMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Middle, modifiers: event_mods(ns_event) })) },
appkit::NSOtherMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Middle, modifiers: event_mods(ns_event) })) },
appkit::NSMouseEntered => {
let window = match maybe_window.or_else(maybe_key_window) {
Some(window) => window,
@@ -464,14 +377,16 @@ impl EventsLoop {
} else {
window.view.convertPoint_fromView_(window_point, cocoa::base::nil)
};
let view_rect = NSView::frame(*window.view);
let scale_factor = window.hidpi_factor();
let x = (scale_factor * view_point.x as f32) as f64;
let y = (scale_factor * (view_rect.size.height - view_point.y) as f32) as f64;
let window_event = WindowEvent::CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event_mods(ns_event) };
let x = view_point.x as f64;
let y = (view_rect.size.height - view_point.y) as f64;
let window_event = WindowEvent::CursorMoved {
device_id: DEVICE_ID,
position: (x, y).into(),
modifiers: event_mods(ns_event),
};
let event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event };
self.shared.pending_events.lock().unwrap().push_back(event);
Some(into_event(WindowEvent::CursorEntered { device_id: DEVICE_ID }))
},
@@ -484,50 +399,30 @@ impl EventsLoop {
// If the mouse movement was on one of our windows, use it.
// Otherwise, if one of our windows is the key window (receiving input), use it.
// Otherwise, return `None`.
let window = match maybe_window.or_else(maybe_key_window) {
Some(window) => window,
match maybe_window.or_else(maybe_key_window) {
Some(_window) => (),
None => return None,
};
let window_point = ns_event.locationInWindow();
let view_point = if ns_window == cocoa::base::nil {
let ns_size = foundation::NSSize::new(0.0, 0.0);
let ns_rect = foundation::NSRect::new(window_point, ns_size);
let window_rect = window.window.convertRectFromScreen_(ns_rect);
window.view.convertPoint_fromView_(window_rect.origin, cocoa::base::nil)
} else {
window.view.convertPoint_fromView_(window_point, cocoa::base::nil)
};
let view_rect = NSView::frame(*window.view);
let scale_factor = window.hidpi_factor();
let mut events = std::collections::VecDeque::new();
{
let x = (scale_factor * view_point.x as f32) as f64;
let y = (scale_factor * (view_rect.size.height - view_point.y) as f32) as f64;
let window_event = WindowEvent::CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event_mods(ns_event) };
let event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event };
events.push_back(event);
}
let delta_x = (scale_factor * ns_event.deltaX() as f32) as f64;
let mut events = std::collections::VecDeque::with_capacity(3);
let delta_x = ns_event.deltaX() as f64;
if delta_x != 0.0 {
let motion_event = DeviceEvent::Motion { axis: 0, value: delta_x };
let event = Event::DeviceEvent{ device_id: DEVICE_ID, event: motion_event };
let event = Event::DeviceEvent { device_id: DEVICE_ID, event: motion_event };
events.push_back(event);
}
let delta_y = (scale_factor * ns_event.deltaY() as f32) as f64;
let delta_y = ns_event.deltaY() as f64;
if delta_y != 0.0 {
let motion_event = DeviceEvent::Motion { axis: 1, value: delta_y };
let event = Event::DeviceEvent{ device_id: DEVICE_ID, event: motion_event };
let event = Event::DeviceEvent { device_id: DEVICE_ID, event: motion_event };
events.push_back(event);
}
if delta_x != 0.0 || delta_y != 0.0 {
let motion_event = DeviceEvent::MouseMotion { delta: (delta_x, delta_y) };
let event = Event::DeviceEvent{ device_id: DEVICE_ID, event: motion_event };
let event = Event::DeviceEvent { device_id: DEVICE_ID, event: motion_event };
events.push_back(event);
}
@@ -538,19 +433,22 @@ impl EventsLoop {
appkit::NSScrollWheel => {
// If none of the windows received the scroll, return `None`.
let window = match maybe_window {
Some(window) => window,
None => return None,
};
if maybe_window.is_none() {
return None;
}
use events::MouseScrollDelta::{LineDelta, PixelDelta};
let scale_factor = window.hidpi_factor();
let delta = if ns_event.hasPreciseScrollingDeltas() == cocoa::base::YES {
PixelDelta(scale_factor * ns_event.scrollingDeltaX() as f32,
scale_factor * ns_event.scrollingDeltaY() as f32)
PixelDelta((
ns_event.scrollingDeltaX() as f64,
ns_event.scrollingDeltaY() as f64,
).into())
} else {
LineDelta(scale_factor * ns_event.scrollingDeltaX() as f32,
scale_factor * ns_event.scrollingDeltaY() as f32)
// TODO: This is probably wrong
LineDelta(
ns_event.scrollingDeltaX() as f32,
ns_event.scrollingDeltaY() as f32,
)
};
let phase = match ns_event.phase() {
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => TouchPhase::Started,
@@ -561,11 +459,15 @@ impl EventsLoop {
device_id: DEVICE_ID,
event: DeviceEvent::MouseWheel {
delta: if ns_event.hasPreciseScrollingDeltas() == cocoa::base::YES {
PixelDelta(ns_event.scrollingDeltaX() as f32,
ns_event.scrollingDeltaY() as f32)
PixelDelta((
ns_event.scrollingDeltaX() as f64,
ns_event.scrollingDeltaY() as f64,
).into())
} else {
LineDelta(ns_event.scrollingDeltaX() as f32,
ns_event.scrollingDeltaY() as f32)
LineDelta(
ns_event.scrollingDeltaX() as f32,
ns_event.scrollingDeltaY() as f32,
)
},
}
});
@@ -621,8 +523,7 @@ impl Proxy {
}
}
fn to_virtual_key_code(code: u16) -> Option<events::VirtualKeyCode> {
pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> {
Some(match code {
0x00 => events::VirtualKeyCode::A,
0x01 => events::VirtualKeyCode::S,
@@ -678,14 +579,14 @@ fn to_virtual_key_code(code: u16) -> Option<events::VirtualKeyCode> {
0x33 => events::VirtualKeyCode::Back,
//0x34 => unkown,
0x35 => events::VirtualKeyCode::Escape,
0x36 => events::VirtualKeyCode::RWin,
0x37 => events::VirtualKeyCode::LWin,
0x36 => events::VirtualKeyCode::LWin,
0x37 => events::VirtualKeyCode::RWin,
0x38 => events::VirtualKeyCode::LShift,
//0x39 => Caps lock,
//0x3a => Left alt,
0x3a => events::VirtualKeyCode::LAlt,
0x3b => events::VirtualKeyCode::LControl,
0x3c => events::VirtualKeyCode::RShift,
//0x3d => Right alt,
0x3d => events::VirtualKeyCode::RAlt,
0x3e => events::VirtualKeyCode::RControl,
//0x3f => Fn key,
//0x40 => F17 Key,
@@ -758,7 +659,7 @@ fn to_virtual_key_code(code: u16) -> Option<events::VirtualKeyCode> {
})
}
fn event_mods(event: cocoa::base::id) -> ModifiersState {
pub fn event_mods(event: cocoa::base::id) -> ModifiersState {
let flags = unsafe {
NSEvent::modifierFlags(event)
};
@@ -770,5 +671,30 @@ fn event_mods(event: cocoa::base::id) -> ModifiersState {
}
}
unsafe fn modifier_event(
ns_event: cocoa::base::id,
keymask: NSEventModifierFlags,
key_pressed: bool,
) -> Option<WindowEvent> {
if !key_pressed && NSEvent::modifierFlags(ns_event).contains(keymask)
|| key_pressed && !NSEvent::modifierFlags(ns_event).contains(keymask) {
let state = ElementState::Released;
let keycode = NSEvent::keyCode(ns_event);
let scancode = keycode as u32;
let virtual_keycode = to_virtual_key_code(keycode);
Some(WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state,
scancode,
virtual_keycode,
modifiers: event_mods(ns_event),
},
})
} else {
None
}
}
// Constant device ID, to be removed when this backend is updated to report real device IDs.
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
pub const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);

107
src/platform/macos/ffi.rs Normal file
View File

@@ -0,0 +1,107 @@
// TODO: Upstream these
#![allow(dead_code, non_snake_case, non_upper_case_globals)]
use cocoa::base::{class, id};
use cocoa::foundation::{NSInteger, NSUInteger};
use objc;
pub const NSNotFound: NSInteger = NSInteger::max_value();
#[repr(C)]
pub struct NSRange {
pub location: NSUInteger,
pub length: NSUInteger,
}
impl NSRange {
#[inline]
pub fn new(location: NSUInteger, length: NSUInteger) -> NSRange {
NSRange { location, length }
}
}
unsafe impl objc::Encode for NSRange {
fn encode() -> objc::Encoding {
let encoding = format!(
// TODO: Verify that this is correct
"{{NSRange={}{}}}",
NSUInteger::encode().as_str(),
NSUInteger::encode().as_str(),
);
unsafe { objc::Encoding::from_str(&encoding) }
}
}
pub trait NSMutableAttributedString: Sized {
unsafe fn alloc(_: Self) -> id {
msg_send![class("NSMutableAttributedString"), alloc]
}
unsafe fn init(self) -> id; // *mut NSMutableAttributedString
unsafe fn initWithString(self, string: id) -> id;
unsafe fn initWithAttributedString(self, string: id) -> id;
unsafe fn string(self) -> id; // *mut NSString
unsafe fn mutableString(self) -> id; // *mut NSMutableString
unsafe fn length(self) -> NSUInteger;
}
impl NSMutableAttributedString for id {
unsafe fn init(self) -> id {
msg_send![self, init]
}
unsafe fn initWithString(self, string: id) -> id {
msg_send![self, initWithString:string]
}
unsafe fn initWithAttributedString(self, string: id) -> id {
msg_send![self, initWithAttributedString:string]
}
unsafe fn string(self) -> id {
msg_send![self, string]
}
unsafe fn mutableString(self) -> id {
msg_send![self, mutableString]
}
unsafe fn length(self) -> NSUInteger {
msg_send![self, length]
}
}
pub const kCGBaseWindowLevelKey: NSInteger = 0;
pub const kCGMinimumWindowLevelKey: NSInteger = 1;
pub const kCGDesktopWindowLevelKey: NSInteger = 2;
pub const kCGBackstopMenuLevelKey: NSInteger = 3;
pub const kCGNormalWindowLevelKey: NSInteger = 4;
pub const kCGFloatingWindowLevelKey: NSInteger = 5;
pub const kCGTornOffMenuWindowLevelKey: NSInteger = 6;
pub const kCGDockWindowLevelKey: NSInteger = 7;
pub const kCGMainMenuWindowLevelKey: NSInteger = 8;
pub const kCGStatusWindowLevelKey: NSInteger = 9;
pub const kCGModalPanelWindowLevelKey: NSInteger = 10;
pub const kCGPopUpMenuWindowLevelKey: NSInteger = 11;
pub const kCGDraggingWindowLevelKey: NSInteger = 12;
pub const kCGScreenSaverWindowLevelKey: NSInteger = 13;
pub const kCGMaximumWindowLevelKey: NSInteger = 14;
pub const kCGOverlayWindowLevelKey: NSInteger = 15;
pub const kCGHelpWindowLevelKey: NSInteger = 16;
pub const kCGUtilityWindowLevelKey: NSInteger = 17;
pub const kCGDesktopIconWindowLevelKey: NSInteger = 18;
pub const kCGCursorWindowLevelKey: NSInteger = 19;
pub const kCGNumberOfWindowLevelKeys: NSInteger = 20;
pub enum NSWindowLevel {
NSNormalWindowLevel = kCGBaseWindowLevelKey as _,
NSFloatingWindowLevel = kCGFloatingWindowLevelKey as _,
NSTornOffMenuWindowLevel = kCGTornOffMenuWindowLevelKey as _,
NSModalPanelWindowLevel = kCGModalPanelWindowLevelKey as _,
NSMainMenuWindowLevel = kCGMainMenuWindowLevelKey as _,
NSStatusWindowLevel = kCGStatusWindowLevelKey as _,
NSPopUpMenuWindowLevel = kCGPopUpMenuWindowLevelKey as _,
NSScreenSaverWindowLevel = kCGScreenSaverWindowLevelKey as _,
}

View File

@@ -38,5 +38,8 @@ impl Window {
}
mod events_loop;
mod ffi;
mod monitor;
mod util;
mod view;
mod window;

View File

@@ -1,29 +1,44 @@
use std::collections::VecDeque;
use std::fmt;
use cocoa::appkit::NSScreen;
use cocoa::base::{id, nil};
use cocoa::foundation::{NSString, NSUInteger};
use core_graphics::display::{CGDirectDisplayID, CGDisplay};
use std::collections::VecDeque;
use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds};
use {PhysicalPosition, PhysicalSize};
use super::EventsLoop;
use super::window::IdRef;
use super::window::{IdRef, Window2};
#[derive(Clone, PartialEq)]
pub struct MonitorId(CGDirectDisplayID);
impl EventsLoop {
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
let mut monitors = VecDeque::new();
if let Ok(displays) = CGDisplay::active_displays() {
for d in displays {
monitors.push_back(MonitorId(d));
}
fn get_available_monitors() -> VecDeque<MonitorId> {
if let Ok(displays) = CGDisplay::active_displays() {
let mut monitors = VecDeque::with_capacity(displays.len());
for d in displays {
monitors.push_back(MonitorId(d));
}
monitors
} else {
VecDeque::with_capacity(0)
}
}
pub fn get_primary_monitor() -> MonitorId {
let id = MonitorId(CGDisplay::main().id);
id
}
impl EventsLoop {
#[inline]
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
get_available_monitors()
}
#[inline]
pub fn get_primary_monitor(&self) -> MonitorId {
let id = MonitorId(CGDisplay::main().id);
id
get_primary_monitor()
}
pub fn make_monitor_from_display(id: CGDirectDisplayID) -> MonitorId {
@@ -32,6 +47,41 @@ impl EventsLoop {
}
}
impl Window2 {
#[inline]
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
get_available_monitors()
}
#[inline]
pub fn get_primary_monitor(&self) -> MonitorId {
get_primary_monitor()
}
}
impl fmt::Debug for MonitorId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
#[derive(Debug)]
struct MonitorId {
name: Option<String>,
native_identifier: u32,
dimensions: PhysicalSize,
position: PhysicalPosition,
hidpi_factor: f64,
}
let monitor_id_proxy = MonitorId {
name: self.get_name(),
native_identifier: self.get_native_identifier(),
dimensions: self.get_dimensions(),
position: self.get_position(),
hidpi_factor: self.get_hidpi_factor(),
};
monitor_id_proxy.fmt(f)
}
}
impl MonitorId {
pub fn get_name(&self) -> Option<String> {
let MonitorId(display_id) = *self;
@@ -44,29 +94,32 @@ impl MonitorId {
self.0
}
pub fn get_dimensions(&self) -> (u32, u32) {
pub fn get_dimensions(&self) -> PhysicalSize {
let MonitorId(display_id) = *self;
let display = CGDisplay::new(display_id);
let dimension = {
let height = display.pixels_high();
let width = display.pixels_wide();
(width as u32, height as u32)
};
dimension
let height = display.pixels_high();
let width = display.pixels_wide();
PhysicalSize::from_logical(
(width as f64, height as f64),
self.get_hidpi_factor(),
)
}
#[inline]
pub fn get_position(&self) -> (i32, i32) {
unimplemented!()
pub fn get_position(&self) -> PhysicalPosition {
let bounds = unsafe { CGDisplayBounds(self.get_native_identifier()) };
PhysicalPosition::from_logical(
(bounds.origin.x as f64, bounds.origin.y as f64),
self.get_hidpi_factor(),
)
}
pub fn get_hidpi_factor(&self) -> f32 {
pub fn get_hidpi_factor(&self) -> f64 {
let screen = match self.get_nsscreen() {
Some(screen) => screen,
None => return 1.0, // default to 1.0 when we can't find the screen
};
unsafe { NSScreen::backingScaleFactor(screen) as f32 }
unsafe { NSScreen::backingScaleFactor(screen) as f64 }
}
pub(crate) fn get_nsscreen(&self) -> Option<id> {

View File

@@ -0,0 +1,38 @@
use cocoa::appkit::NSWindowStyleMask;
use cocoa::base::{class, id, nil};
use cocoa::foundation::{NSRect, NSUInteger};
use core_graphics::display::CGDisplay;
use platform::platform::ffi;
use platform::platform::window::IdRef;
pub const EMPTY_RANGE: ffi::NSRange = ffi::NSRange {
location: ffi::NSNotFound as NSUInteger,
length: 0,
};
// For consistency with other platforms, this will...
// 1. translate the bottom-left window corner into the top-left window corner
// 2. translate the coordinate from a bottom-left origin coordinate system to a top-left one
pub fn bottom_left_to_top_left(rect: NSRect) -> f64 {
CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height)
}
pub unsafe fn set_style_mask(window: id, view: id, mask: NSWindowStyleMask) {
use cocoa::appkit::NSWindow;
window.setStyleMask_(mask);
// If we don't do this, key handling will break. Therefore, never call `setStyleMask` directly!
window.makeFirstResponder_(view);
}
pub unsafe fn create_input_context(view: id) -> IdRef {
let input_context: id = msg_send![class("NSTextInputContext"), alloc];
let input_context: id = msg_send![input_context, initWithClient:view];
IdRef::new(input_context)
}
#[allow(dead_code)]
pub unsafe fn open_emoji_picker() {
let app: id = msg_send![class("NSApplication"), sharedApplication];
let _: () = msg_send![app, orderFrontCharacterPalette:nil];
}

568
src/platform/macos/view.rs Normal file
View File

@@ -0,0 +1,568 @@
// This is a pretty close port of the implementation in GLFW:
// https://github.com/glfw/glfw/blob/7ef34eb06de54dd9186d3d21a401b2ef819b59e7/src/cocoa_window.m
use std::{slice, str};
use std::boxed::Box;
use std::collections::VecDeque;
use std::os::raw::*;
use std::sync::Weak;
use cocoa::base::{class, id, nil};
use cocoa::appkit::{NSEvent, NSView, NSWindow};
use cocoa::foundation::{NSPoint, NSRect, NSSize, NSString, NSUInteger};
use objc::declare::ClassDecl;
use objc::runtime::{Class, Object, Protocol, Sel, BOOL};
use {ElementState, Event, KeyboardInput, MouseButton, WindowEvent, WindowId};
use platform::platform::events_loop::{DEVICE_ID, event_mods, Shared, to_virtual_key_code};
use platform::platform::util;
use platform::platform::ffi::*;
use platform::platform::window::{get_window_id, IdRef};
struct ViewState {
window: id,
shared: Weak<Shared>,
ime_spot: Option<(f64, f64)>,
raw_characters: Option<String>,
last_insert: Option<String>,
}
pub fn new_view(window: id, shared: Weak<Shared>) -> IdRef {
let state = ViewState {
window,
shared,
ime_spot: None,
raw_characters: None,
last_insert: None,
};
unsafe {
// This is free'd in `dealloc`
let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void;
let view: id = msg_send![VIEW_CLASS.0, alloc];
IdRef::new(msg_send![view, initWithWinit:state_ptr])
}
}
pub fn set_ime_spot(view: id, input_context: id, x: f64, y: f64) {
unsafe {
let state_ptr: *mut c_void = *(*view).get_mut_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
let content_rect = NSWindow::contentRectForFrameRect_(
state.window,
NSWindow::frame(state.window),
);
let base_x = content_rect.origin.x as f64;
let base_y = (content_rect.origin.y + content_rect.size.height) as f64;
state.ime_spot = Some((base_x + x, base_y - y));
let _: () = msg_send![input_context, invalidateCharacterCoordinates];
}
}
struct ViewClass(*const Class);
unsafe impl Send for ViewClass {}
unsafe impl Sync for ViewClass {}
lazy_static! {
static ref VIEW_CLASS: ViewClass = unsafe {
let superclass = Class::get("NSView").unwrap();
let mut decl = ClassDecl::new("WinitView", superclass).unwrap();
decl.add_method(sel!(dealloc), dealloc as extern fn(&Object, Sel));
decl.add_method(
sel!(initWithWinit:),
init_with_winit as extern fn(&Object, Sel, *mut c_void) -> id,
);
decl.add_method(sel!(hasMarkedText), has_marked_text as extern fn(&Object, Sel) -> BOOL);
decl.add_method(
sel!(markedRange),
marked_range as extern fn(&Object, Sel) -> NSRange,
);
decl.add_method(sel!(selectedRange), selected_range as extern fn(&Object, Sel) -> NSRange);
decl.add_method(
sel!(setMarkedText:selectedRange:replacementRange:),
set_marked_text as extern fn(&mut Object, Sel, id, NSRange, NSRange),
);
decl.add_method(sel!(unmarkText), unmark_text as extern fn(&Object, Sel));
decl.add_method(
sel!(validAttributesForMarkedText),
valid_attributes_for_marked_text as extern fn(&Object, Sel) -> id,
);
decl.add_method(
sel!(attributedSubstringForProposedRange:actualRange:),
attributed_substring_for_proposed_range
as extern fn(&Object, Sel, NSRange, *mut c_void) -> id,
);
decl.add_method(
sel!(insertText:replacementRange:),
insert_text as extern fn(&Object, Sel, id, NSRange),
);
decl.add_method(
sel!(characterIndexForPoint:),
character_index_for_point as extern fn(&Object, Sel, NSPoint) -> NSUInteger,
);
decl.add_method(
sel!(firstRectForCharacterRange:actualRange:),
first_rect_for_character_range
as extern fn(&Object, Sel, NSRange, *mut c_void) -> NSRect,
);
decl.add_method(
sel!(doCommandBySelector:),
do_command_by_selector as extern fn(&Object, Sel, Sel),
);
decl.add_method(sel!(keyDown:), key_down as extern fn(&Object, Sel, id));
decl.add_method(sel!(keyUp:), key_up as extern fn(&Object, Sel, id));
decl.add_method(sel!(insertTab:), insert_tab as extern fn(&Object, Sel, id));
decl.add_method(sel!(insertBackTab:), insert_back_tab as extern fn(&Object, Sel, id));
decl.add_method(sel!(mouseDown:), mouse_down as extern fn(&Object, Sel, id));
decl.add_method(sel!(mouseUp:), mouse_up as extern fn(&Object, Sel, id));
decl.add_method(sel!(rightMouseDown:), right_mouse_down as extern fn(&Object, Sel, id));
decl.add_method(sel!(rightMouseUp:), right_mouse_up as extern fn(&Object, Sel, id));
decl.add_method(sel!(otherMouseDown:), other_mouse_down as extern fn(&Object, Sel, id));
decl.add_method(sel!(otherMouseUp:), other_mouse_up as extern fn(&Object, Sel, id));
decl.add_method(sel!(mouseMoved:), mouse_moved as extern fn(&Object, Sel, id));
decl.add_method(sel!(mouseDragged:), mouse_dragged as extern fn(&Object, Sel, id));
decl.add_method(sel!(rightMouseDragged:), right_mouse_dragged as extern fn(&Object, Sel, id));
decl.add_method(sel!(otherMouseDragged:), other_mouse_dragged as extern fn(&Object, Sel, id));
decl.add_ivar::<*mut c_void>("winitState");
decl.add_ivar::<id>("markedText");
let protocol = Protocol::get("NSTextInputClient").unwrap();
decl.add_protocol(&protocol);
ViewClass(decl.register())
};
}
extern fn dealloc(this: &Object, _sel: Sel) {
unsafe {
let state: *mut c_void = *this.get_ivar("winitState");
let marked_text: id = *this.get_ivar("markedText");
let _: () = msg_send![marked_text, release];
Box::from_raw(state as *mut ViewState);
}
}
extern fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> id {
unsafe {
let this: id = msg_send![this, init];
if this != nil {
(*this).set_ivar("winitState", state);
let marked_text = <id as NSMutableAttributedString>::init(
NSMutableAttributedString::alloc(nil),
);
(*this).set_ivar("markedText", marked_text);
}
this
}
}
extern fn has_marked_text(this: &Object, _sel: Sel) -> BOOL {
//println!("hasMarkedText");
unsafe {
let marked_text: id = *this.get_ivar("markedText");
(marked_text.length() > 0) as i8
}
}
extern fn marked_range(this: &Object, _sel: Sel) -> NSRange {
//println!("markedRange");
unsafe {
let marked_text: id = *this.get_ivar("markedText");
let length = marked_text.length();
if length > 0 {
NSRange::new(0, length - 1)
} else {
util::EMPTY_RANGE
}
}
}
extern fn selected_range(_this: &Object, _sel: Sel) -> NSRange {
//println!("selectedRange");
util::EMPTY_RANGE
}
extern fn set_marked_text(
this: &mut Object,
_sel: Sel,
string: id,
_selected_range: NSRange,
_replacement_range: NSRange,
) {
//println!("setMarkedText");
unsafe {
let marked_text_ref: &mut id = this.get_mut_ivar("markedText");
let _: () = msg_send![(*marked_text_ref), release];
let marked_text = NSMutableAttributedString::alloc(nil);
let has_attr = msg_send![string, isKindOfClass:class("NSAttributedString")];
if has_attr {
marked_text.initWithAttributedString(string);
} else {
marked_text.initWithString(string);
};
*marked_text_ref = marked_text;
}
}
extern fn unmark_text(this: &Object, _sel: Sel) {
//println!("unmarkText");
unsafe {
let marked_text: id = *this.get_ivar("markedText");
let mutable_string = marked_text.mutableString();
let _: () = msg_send![mutable_string, setString:""];
let input_context: id = msg_send![this, inputContext];
let _: () = msg_send![input_context, discardMarkedText];
}
}
extern fn valid_attributes_for_marked_text(_this: &Object, _sel: Sel) -> id {
//println!("validAttributesForMarkedText");
unsafe { msg_send![class("NSArray"), array] }
}
extern fn attributed_substring_for_proposed_range(
_this: &Object,
_sel: Sel,
_range: NSRange,
_actual_range: *mut c_void, // *mut NSRange
) -> id {
//println!("attributedSubstringForProposedRange");
nil
}
extern fn character_index_for_point(_this: &Object, _sel: Sel, _point: NSPoint) -> NSUInteger {
//println!("characterIndexForPoint");
0
}
extern fn first_rect_for_character_range(
this: &Object,
_sel: Sel,
_range: NSRange,
_actual_range: *mut c_void, // *mut NSRange
) -> NSRect {
//println!("firstRectForCharacterRange");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
let (x, y) = state.ime_spot.unwrap_or_else(|| {
let content_rect = NSWindow::contentRectForFrameRect_(
state.window,
NSWindow::frame(state.window),
);
let x = content_rect.origin.x;
let y = util::bottom_left_to_top_left(content_rect);
(x, y)
});
NSRect::new(
NSPoint::new(x as _, y as _),
NSSize::new(0.0, 0.0),
)
}
}
extern fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange) {
//println!("insertText");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
let has_attr = msg_send![string, isKindOfClass:class("NSAttributedString")];
let characters = if has_attr {
// This is a *mut NSAttributedString
msg_send![string, string]
} else {
// This is already a *mut NSString
string
};
let slice = slice::from_raw_parts(
characters.UTF8String() as *const c_uchar,
characters.len(),
);
let string = str::from_utf8_unchecked(slice);
state.last_insert = Some(string.to_owned());
// We don't need this now, but it's here if that changes.
//let event: id = msg_send![class("NSApp"), currentEvent];
let mut events = VecDeque::with_capacity(characters.len());
for character in string.chars() {
events.push_back(Event::WindowEvent {
window_id: WindowId(get_window_id(state.window)),
event: WindowEvent::ReceivedCharacter(character),
});
}
if let Some(shared) = state.shared.upgrade() {
shared.pending_events
.lock()
.unwrap()
.append(&mut events);
}
}
}
extern fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
//println!("doCommandBySelector");
// Basically, we're sent this message whenever a keyboard event that doesn't generate a "human readable" character
// happens, i.e. newlines, tabs, and Ctrl+C.
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
let shared = if let Some(shared) = state.shared.upgrade() {
shared
} else {
return;
};
let mut events = VecDeque::with_capacity(1);
if command == sel!(insertNewline:) {
// The `else` condition would emit the same character, but I'm keeping this here both...
// 1) as a reminder for how `doCommandBySelector` works
// 2) to make our use of carriage return explicit
events.push_back(Event::WindowEvent {
window_id: WindowId(get_window_id(state.window)),
event: WindowEvent::ReceivedCharacter('\r'),
});
} else {
let raw_characters = state.raw_characters.take();
if let Some(raw_characters) = raw_characters {
for character in raw_characters.chars() {
events.push_back(Event::WindowEvent {
window_id: WindowId(get_window_id(state.window)),
event: WindowEvent::ReceivedCharacter(character),
});
}
}
};
shared.pending_events
.lock()
.unwrap()
.append(&mut events);
}
}
extern fn key_down(this: &Object, _sel: Sel, event: id) {
//println!("keyDown");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
let window_id = WindowId(get_window_id(state.window));
let keycode: c_ushort = msg_send![event, keyCode];
let virtual_keycode = to_virtual_key_code(keycode);
let scancode = keycode as u32;
let is_repeat = msg_send![event, isARepeat];
let window_event = Event::WindowEvent {
window_id,
event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state: ElementState::Pressed,
scancode,
virtual_keycode,
modifiers: event_mods(event),
},
},
};
state.raw_characters = {
let characters: id = msg_send![event, characters];
let slice = slice::from_raw_parts(
characters.UTF8String() as *const c_uchar,
characters.len(),
);
let string = str::from_utf8_unchecked(slice);
Some(string.to_owned())
};
if let Some(shared) = state.shared.upgrade() {
shared.pending_events
.lock()
.unwrap()
.push_back(window_event);
// Emit `ReceivedCharacter` for key repeats
if is_repeat && state.last_insert.is_some() {
let last_insert = state.last_insert.as_ref().unwrap();
for character in last_insert.chars() {
let window_event = Event::WindowEvent {
window_id,
event: WindowEvent::ReceivedCharacter(character),
};
shared.pending_events
.lock()
.unwrap()
.push_back(window_event);
}
} else {
// Some keys (and only *some*, with no known reason) don't trigger `insertText`, while others do...
// So, we don't give repeats the opportunity to trigger that, since otherwise our hack will cause some
// keys to generate twice as many characters.
let array: id = msg_send![class("NSArray"), arrayWithObject:event];
let (): _ = msg_send![this, interpretKeyEvents:array];
}
}
}
}
extern fn key_up(this: &Object, _sel: Sel, event: id) {
//println!("keyUp");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
state.last_insert = None;
let keycode: c_ushort = msg_send![event, keyCode];
let virtual_keycode = to_virtual_key_code(keycode);
let scancode = keycode as u32;
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.window)),
event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state: ElementState::Released,
scancode,
virtual_keycode,
modifiers: event_mods(event),
},
},
};
if let Some(shared) = state.shared.upgrade() {
shared.pending_events
.lock()
.unwrap()
.push_back(window_event);
}
}
}
extern fn insert_tab(this: &Object, _sel: Sel, _sender: id) {
unsafe {
let window: id = msg_send![this, window];
let first_responder: id = msg_send![window, firstResponder];
let this_ptr = this as *const _ as *mut _;
if first_responder == this_ptr {
let (): _ = msg_send![window, selectNextKeyView:this];
}
}
}
extern fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) {
unsafe {
let window: id = msg_send![this, window];
let first_responder: id = msg_send![window, firstResponder];
let this_ptr = this as *const _ as *mut _;
if first_responder == this_ptr {
let (): _ = msg_send![window, selectPreviousKeyView:this];
}
}
}
fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: ElementState) {
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.window)),
event: WindowEvent::MouseInput {
device_id: DEVICE_ID,
state: button_state,
button,
modifiers: event_mods(event),
},
};
if let Some(shared) = state.shared.upgrade() {
shared.pending_events
.lock()
.unwrap()
.push_back(window_event);
}
}
}
extern fn mouse_down(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Left, ElementState::Pressed);
}
extern fn mouse_up(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Left, ElementState::Released);
}
extern fn right_mouse_down(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Right, ElementState::Pressed);
}
extern fn right_mouse_up(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Right, ElementState::Released);
}
extern fn other_mouse_down(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Middle, ElementState::Pressed);
}
extern fn other_mouse_up(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Middle, ElementState::Released);
}
fn mouse_motion(this: &Object, event: id) {
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
// We have to do this to have access to the `NSView` trait...
let view: id = this as *const _ as *mut _;
let window_point = event.locationInWindow();
let view_point = view.convertPoint_fromView_(window_point, nil);
let view_rect = NSView::frame(view);
if view_point.x.is_sign_negative()
|| view_point.y.is_sign_negative()
|| view_point.x > view_rect.size.width
|| view_point.y > view_rect.size.height {
// Point is outside of the client area (view)
return;
}
let x = view_point.x as f64;
let y = view_rect.size.height as f64 - view_point.y as f64;
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.window)),
event: WindowEvent::CursorMoved {
device_id: DEVICE_ID,
position: (x, y).into(),
modifiers: event_mods(event),
},
};
if let Some(shared) = state.shared.upgrade() {
shared.pending_events
.lock()
.unwrap()
.push_back(window_event);
}
}
}
extern fn mouse_moved(this: &Object, _sel: Sel, event: id) {
mouse_motion(this, event);
}
extern fn mouse_dragged(this: &Object, _sel: Sel, event: id) {
mouse_motion(this, event);
}
extern fn right_mouse_dragged(this: &Object, _sel: Sel, event: id) {
mouse_motion(this, event);
}
extern fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) {
mouse_motion(this, event);
}

View File

@@ -1,37 +1,51 @@
use {CreationError, Event, WindowEvent, WindowId, MouseCursor, CursorState};
use CreationError::OsError;
use libc;
use WindowAttributes;
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::{NSDictionary, NSPoint, NSRect, NSSize, NSString, NSAutoreleasePool};
use core_graphics::display::CGDisplay;
use std;
use std::ops::Deref;
use std::os::raw::c_void;
use std::sync::Weak;
use std::cell::{Cell, RefCell};
use super::events_loop::{EventsLoop, Shared};
use cocoa;
use cocoa::appkit::{
self,
CGFloat,
NSApplication,
NSColor,
NSScreen,
NSView,
NSWindow,
NSWindowButton,
NSWindowStyleMask,
};
use cocoa::base::{id, nil};
use cocoa::foundation::{NSAutoreleasePool, NSDictionary, NSPoint, NSRect, NSSize, NSString};
use core_graphics::display::CGDisplay;
use objc;
use objc::runtime::{Class, Object, Sel, BOOL, YES, NO};
use objc::declare::ClassDecl;
use {
CreationError,
Event,
LogicalPosition,
LogicalSize,
MouseCursor,
WindowAttributes,
WindowEvent,
WindowId,
};
use CreationError::OsError;
use os::macos::{ActivationPolicy, WindowExt};
use platform::platform::{ffi, util};
use platform::platform::events_loop::{EventsLoop, Shared};
use platform::platform::view::{new_view, set_ime_spot};
use window::MonitorId as RootMonitorId;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Id(pub usize);
struct DelegateState {
pub struct DelegateState {
view: IdRef,
window: IdRef,
shared: Weak<Shared>,
@@ -44,8 +58,11 @@ struct DelegateState {
// see comments of `window_did_fail_to_enter_fullscreen`
handle_with_fullscreen: bool,
// During windowDidResize, we use this to only send Moved if the position changed.
previous_position: Option<(i32, i32)>,
// During `windowDidResize`, we use this to only send Moved if the position changed.
previous_position: Option<(f64, f64)>,
// Used to prevent redundant events.
previous_dpi_factor: f64,
}
impl DelegateState {
@@ -55,16 +72,17 @@ impl DelegateState {
// resizable temporality
let curr_mask = self.window.styleMask();
if !curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask) {
self.window
.setStyleMask_(NSWindowStyleMask::NSResizableWindowMask);
let required = NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask;
let needs_temp_mask = !curr_mask.contains(required);
if needs_temp_mask {
util::set_style_mask(*self.window, *self.view, required);
}
let is_zoomed: BOOL = msg_send![*self.window, isZoomed];
// Roll back temp styles
if !curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask) {
self.window.setStyleMask_(curr_mask);
if needs_temp_mask {
util::set_style_mask(*self.window, *self.view, curr_mask);
}
is_zoomed != 0
@@ -74,13 +92,20 @@ impl DelegateState {
fn restore_state_from_fullscreen(&mut self) {
let maximized = unsafe {
let mut win_attribs = self.win_attribs.borrow_mut();
win_attribs.fullscreen = None;
let save_style_opt = self.save_style_mask.take();
if let Some(save_style) = save_style_opt {
self.window.setStyleMask_(save_style);
}
let mask = {
let base_mask = self.save_style_mask
.take()
.unwrap_or_else(|| self.window.styleMask());
if win_attribs.resizable {
base_mask | NSWindowStyleMask::NSResizableWindowMask
} else {
base_mask & !NSWindowStyleMask::NSResizableWindowMask
}
};
util::set_style_mask(*self.window, *self.view, mask);
win_attribs.maximized
};
@@ -105,16 +130,17 @@ impl DelegateState {
let mut win_attribs = self.win_attribs.borrow_mut();
win_attribs.maximized = maximized;
let curr_mask = unsafe { self.window.styleMask() };
if win_attribs.fullscreen.is_some() {
// Handle it in window_did_exit_fullscreen
return;
} else if win_attribs.decorations {
// Just use the native zoom if not borderless
} else if curr_mask.contains(NSWindowStyleMask::NSResizableWindowMask) {
// Just use the native zoom if resizable
unsafe {
self.window.zoom_(nil);
}
} else {
// if it is borderless, we set the frame directly
// if it's not resizable, we set the frame directly
unsafe {
let new_rect = if maximized {
let screen = NSScreen::mainScreen(nil);
@@ -138,48 +164,44 @@ pub struct WindowDelegate {
}
impl WindowDelegate {
// Emits an event via the `EventsLoop`'s callback or stores it in the pending queue.
pub fn emit_event(state: &mut DelegateState, window_event: WindowEvent) {
let window_id = get_window_id(*state.window);
let event = Event::WindowEvent {
window_id: WindowId(window_id),
event: window_event,
};
if let Some(shared) = state.shared.upgrade() {
shared.call_user_callback_with_event_or_store_in_pending(event);
}
}
pub fn emit_resize_event(state: &mut DelegateState) {
let rect = unsafe { NSView::frame(*state.view) };
let size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64);
WindowDelegate::emit_event(state, WindowEvent::Resized(size));
}
pub fn emit_move_event(state: &mut DelegateState) {
let rect = unsafe { NSWindow::frame(*state.window) };
let x = rect.origin.x as f64;
let y = util::bottom_left_to_top_left(rect);
let moved = state.previous_position != Some((x, y));
if moved {
state.previous_position = Some((x, y));
WindowDelegate::emit_event(state, WindowEvent::Moved((x, y).into()));
}
}
/// Get the delegate class, initiailizing it neccessary
fn class() -> *const Class {
use std::os::raw::c_void;
// Emits an event via the `EventsLoop`'s callback or stores it in the pending queue.
unsafe fn emit_event(state: &mut DelegateState, window_event: WindowEvent) {
let window_id = get_window_id(*state.window);
let event = Event::WindowEvent {
window_id: WindowId(window_id),
event: window_event,
};
if let Some(shared) = state.shared.upgrade() {
shared.call_user_callback_with_event_or_store_in_pending(event);
}
}
// Called when the window is resized or when the window was moved to a different screen.
unsafe fn emit_resize_event(state: &mut DelegateState) {
let rect = NSView::frame(*state.view);
let scale_factor = NSWindow::backingScaleFactor(*state.window) as f32;
let width = (scale_factor * rect.size.width as f32) as u32;
let height = (scale_factor * rect.size.height as f32) as u32;
emit_event(state, WindowEvent::Resized(width, height));
}
unsafe fn emit_move_event(state: &mut DelegateState) {
let frame_rect = NSWindow::frame(*state.window);
let x = frame_rect.origin.x as _;
let y = Window2::bottom_left_to_top_left(frame_rect);
let moved = state.previous_position != Some((x, y));
if moved {
state.previous_position = Some((x, y));
emit_event(state, WindowEvent::Moved(x, y));
}
}
extern fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
unsafe {
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
emit_event(state, WindowEvent::CloseRequested);
WindowDelegate::emit_event(state, WindowEvent::CloseRequested);
}
NO
}
@@ -189,7 +211,7 @@ impl WindowDelegate {
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
emit_event(state, WindowEvent::Destroyed);
WindowDelegate::emit_event(state, WindowEvent::Destroyed);
// Remove the window from the shared state.
if let Some(shared) = state.shared.upgrade() {
@@ -203,8 +225,8 @@ impl WindowDelegate {
unsafe {
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
emit_resize_event(state);
emit_move_event(state);
WindowDelegate::emit_resize_event(state);
WindowDelegate::emit_move_event(state);
}
}
@@ -213,7 +235,7 @@ impl WindowDelegate {
unsafe {
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
emit_move_event(state);
WindowDelegate::emit_move_event(state);
}
}
@@ -221,18 +243,26 @@ impl WindowDelegate {
unsafe {
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
emit_resize_event(state);
let scale_factor = NSWindow::backingScaleFactor(*state.window) as f32;
emit_event(state, WindowEvent::HiDPIFactorChanged(scale_factor));
let dpi_factor = NSWindow::backingScaleFactor(*state.window) as f64;
if state.previous_dpi_factor != dpi_factor {
state.previous_dpi_factor = dpi_factor;
WindowDelegate::emit_event(state, WindowEvent::HiDpiFactorChanged(dpi_factor));
WindowDelegate::emit_resize_event(state);
}
}
}
// This will always be called before `window_did_change_screen`.
extern fn window_did_change_backing_properties(this: &Object, _:Sel, _:id) {
unsafe {
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
let scale_factor = NSWindow::backingScaleFactor(*state.window) as f32;
emit_event(state, WindowEvent::HiDPIFactorChanged(scale_factor));
let dpi_factor = NSWindow::backingScaleFactor(*state.window) as f64;
if state.previous_dpi_factor != dpi_factor {
state.previous_dpi_factor = dpi_factor;
WindowDelegate::emit_event(state, WindowEvent::HiDpiFactorChanged(dpi_factor));
WindowDelegate::emit_resize_event(state);
}
}
}
@@ -242,7 +272,7 @@ impl WindowDelegate {
// lost focus
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
emit_event(state, WindowEvent::Focused(true));
WindowDelegate::emit_event(state, WindowEvent::Focused(true));
}
}
@@ -250,7 +280,7 @@ impl WindowDelegate {
unsafe {
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
emit_event(state, WindowEvent::Focused(false));
WindowDelegate::emit_event(state, WindowEvent::Focused(false));
}
}
@@ -273,7 +303,7 @@ impl WindowDelegate {
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
emit_event(state, WindowEvent::HoveredFile(PathBuf::from(path)));
WindowDelegate::emit_event(state, WindowEvent::HoveredFile(PathBuf::from(path)));
}
};
@@ -302,7 +332,7 @@ impl WindowDelegate {
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
emit_event(state, WindowEvent::DroppedFile(PathBuf::from(path)));
WindowDelegate::emit_event(state, WindowEvent::DroppedFile(PathBuf::from(path)));
}
};
@@ -317,7 +347,7 @@ impl WindowDelegate {
unsafe {
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
emit_event(state, WindowEvent::HoveredFileCancelled);
WindowDelegate::emit_event(state, WindowEvent::HoveredFileCancelled);
}
}
@@ -326,7 +356,7 @@ impl WindowDelegate {
unsafe {
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
state.win_attribs.borrow_mut().fullscreen = Some(get_current_monitor());
state.win_attribs.borrow_mut().fullscreen = Some(get_current_monitor(*state.window));
state.handle_with_fullscreen = false;
}
@@ -415,13 +445,13 @@ impl WindowDelegate {
// callbacks for drag and drop events
decl.add_method(sel!(draggingEntered:),
dragging_entered as extern fn(&Object, Sel, id) -> BOOL);
decl.add_method(sel!(prepareForDragOperation:),
decl.add_method(sel!(prepareForDragOperation:),
prepare_for_drag_operation as extern fn(&Object, Sel, id));
decl.add_method(sel!(performDragOperation:),
decl.add_method(sel!(performDragOperation:),
perform_drag_operation as extern fn(&Object, Sel, id) -> BOOL);
decl.add_method(sel!(concludeDragOperation:),
decl.add_method(sel!(concludeDragOperation:),
conclude_drag_operation as extern fn(&Object, Sel, id));
decl.add_method(sel!(draggingExited:),
decl.add_method(sel!(draggingExited:),
dragging_exited as extern fn(&Object, Sel, id));
// callbacks for fullscreen events
@@ -488,29 +518,27 @@ pub struct PlatformSpecificWindowBuilderAttributes {
pub titlebar_hidden: bool,
pub titlebar_buttons_hidden: bool,
pub fullsize_content_view: bool,
pub resize_increments: Option<LogicalSize>,
}
pub struct Window2 {
pub view: IdRef,
pub window: IdRef,
pub delegate: WindowDelegate,
pub input_context: IdRef,
cursor_hidden: Cell<bool>,
}
unsafe impl Send for Window2 {}
unsafe impl Sync for Window2 {}
/// Helpper funciton to convert NSScreen::mainScreen to MonitorId
unsafe fn get_current_monitor() -> RootMonitorId {
let screen = NSScreen::mainScreen(nil);
unsafe fn get_current_monitor(window: id) -> RootMonitorId {
let screen: id = msg_send![window, screen];
let desc = NSScreen::deviceDescription(screen);
let key = IdRef::new(NSString::alloc(nil).init_str("NSScreenNumber"));
let value = NSDictionary::valueForKey_(desc, *key);
let display_id = msg_send![value, unsignedIntegerValue];
RootMonitorId {
inner: EventsLoop::make_monitor_from_display(display_id),
}
RootMonitorId { inner: EventsLoop::make_monitor_from_display(display_id) }
}
impl Drop for Window2 {
@@ -559,7 +587,7 @@ impl WindowExt for Window2 {
impl Window2 {
pub fn new(
shared: Weak<Shared>,
win_attribs: WindowAttributes,
mut win_attribs: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<Window2, CreationError> {
unsafe {
@@ -567,34 +595,39 @@ impl Window2 {
panic!("Windows can only be created on the main thread on macOS");
}
}
// Might as well save some RAM...
win_attribs.window_icon.take();
let autoreleasepool = unsafe {
NSAutoreleasePool::new(nil)
};
let app = match Window2::create_app(pl_attribs.activation_policy) {
Some(app) => app,
None => {
None => {
let _: () = unsafe { msg_send![autoreleasepool, drain] };
return Err(OsError(format!("Couldn't create NSApplication")));
},
};
let window = match Window2::create_window(&win_attribs, &pl_attribs)
{
Some(window) => window,
None => {
let window = match Window2::create_window(&win_attribs, &pl_attribs) {
Some(res) => res,
None => {
let _: () = unsafe { msg_send![autoreleasepool, drain] };
return Err(OsError(format!("Couldn't create NSWindow")));
},
};
let view = match Window2::create_view(*window) {
let view = match Window2::create_view(*window, Weak::clone(&shared)) {
Some(view) => view,
None => {
None => {
let _: () = unsafe { msg_send![autoreleasepool, drain] };
return Err(OsError(format!("Couldn't create NSView")));
},
};
let input_context = unsafe { util::create_input_context(*view) };
unsafe {
if win_attribs.transparent {
(*window as id).setOpaque_(NO);
@@ -603,12 +636,11 @@ impl Window2 {
app.activateIgnoringOtherApps_(YES);
if let Some((width, height)) = win_attribs.min_dimensions {
nswindow_set_min_dimensions(window.0, width.into(), height.into());
if let Some(dimensions) = win_attribs.min_dimensions {
nswindow_set_min_dimensions(window.0, dimensions);
}
if let Some((width, height)) = win_attribs.max_dimensions {
nswindow_set_max_dimensions(window.0, width.into(), height.into());
if let Some(dimensions) = win_attribs.max_dimensions {
nswindow_set_max_dimensions(window.0, dimensions);
}
use cocoa::foundation::NSArray;
@@ -617,7 +649,9 @@ impl Window2 {
registerForDraggedTypes:NSArray::arrayWithObject(nil, appkit::NSFilenamesPboardType)];
}
let ds = DelegateState {
let dpi_factor = unsafe { NSWindow::backingScaleFactor(*window) as f64 };
let mut delegate_state = DelegateState {
view: view.clone(),
window: window.clone(),
shared,
@@ -626,19 +660,27 @@ impl Window2 {
save_style_mask: Cell::new(None),
handle_with_fullscreen: win_attribs.fullscreen.is_some(),
previous_position: None,
previous_dpi_factor: dpi_factor,
};
ds.win_attribs.borrow_mut().fullscreen = None;
delegate_state.win_attribs.borrow_mut().fullscreen = None;
if dpi_factor != 1.0 {
WindowDelegate::emit_event(&mut delegate_state, WindowEvent::HiDpiFactorChanged(dpi_factor));
WindowDelegate::emit_resize_event(&mut delegate_state);
}
let window = Window2 {
view: view,
window: window,
delegate: WindowDelegate::new(ds),
delegate: WindowDelegate::new(delegate_state),
input_context,
cursor_hidden: Default::default(),
};
// Set fullscreen mode after we setup everything
if let Some(ref monitor) = win_attribs.fullscreen {
unsafe {
if monitor.inner != get_current_monitor().inner {
if monitor.inner != get_current_monitor(*window.window).inner {
unimplemented!();
}
}
@@ -714,15 +756,18 @@ impl Window2 {
let frame = match screen {
Some(screen) => appkit::NSScreen::frame(screen),
None => {
let (width, height) = attrs.dimensions.unwrap_or((800, 600));
NSRect::new(NSPoint::new(0., 0.), NSSize::new(width as f64, height as f64))
let (width, height) = attrs.dimensions
.map(|logical| (logical.width, logical.height))
.unwrap_or((800.0, 600.0));
NSRect::new(NSPoint::new(0.0, 0.0), NSSize::new(width, height))
}
};
let mut masks = if !attrs.decorations && !screen.is_some() {
// unresizable Window2 without a titlebar or borders
// Resizable Window2 without a titlebar or borders
// if decorations is set to false, ignore pl_attrs
NSWindowStyleMask::NSBorderlessWindowMask
| NSWindowStyleMask::NSResizableWindowMask
} else if pl_attrs.titlebar_hidden {
// if the titlebar is hidden, ignore other pl_attrs
NSWindowStyleMask::NSBorderlessWindowMask |
@@ -735,8 +780,12 @@ impl Window2 {
NSWindowStyleMask::NSTitledWindowMask
};
if !attrs.resizable {
masks &= !NSWindowStyleMask::NSResizableWindowMask;
}
if pl_attrs.fullsize_content_view {
masks = masks | NSWindowStyleMask::NSFullSizeContentViewWindowMask;
masks |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
}
let winit_window = Window2::class();
@@ -775,9 +824,16 @@ impl Window2 {
window.setMovableByWindowBackground_(YES);
}
if !attrs.decorations {
window.setTitleVisibility_(appkit::NSWindowTitleVisibility::NSWindowTitleHidden);
window.setTitlebarAppearsTransparent_(YES);
if attrs.always_on_top {
let _: () = msg_send![*window, setLevel:ffi::NSWindowLevel::NSFloatingWindowLevel];
}
if let Some(increments) = pl_attrs.resize_increments {
let (x, y) = (increments.width, increments.height);
if x >= 1.0 && y >= 1.0 {
let size = NSSize::new(x as CGFloat, y as CGFloat);
window.setResizeIncrements_(size);
}
}
window.center();
@@ -788,12 +844,13 @@ impl Window2 {
}
}
fn create_view(window: id) -> Option<IdRef> {
fn create_view(window: id, shared: Weak<Shared>) -> Option<IdRef> {
unsafe {
let view = IdRef::new(NSView::init(NSView::alloc(nil)));
let view = new_view(window, shared);
view.non_nil().map(|view| {
view.setWantsBestResolutionOpenGLSurface_(YES);
window.setContentView_(*view);
window.makeFirstResponder_(*view);
view
})
}
@@ -816,22 +873,15 @@ impl Window2 {
unsafe { NSWindow::orderOut_(*self.window, nil); }
}
// For consistency with other platforms, this will...
// 1. translate the bottom-left window corner into the top-left window corner
// 2. translate the coordinate from a bottom-left origin coordinate system to a top-left one
fn bottom_left_to_top_left(rect: NSRect) -> i32 {
(CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height)) as _
}
pub fn get_position(&self) -> Option<(i32, i32)> {
pub fn get_position(&self) -> Option<LogicalPosition> {
let frame_rect = unsafe { NSWindow::frame(*self.window) };
Some((
frame_rect.origin.x as i32,
Self::bottom_left_to_top_left(frame_rect),
))
frame_rect.origin.x as f64,
util::bottom_left_to_top_left(frame_rect),
).into())
}
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
let content_rect = unsafe {
NSWindow::contentRectForFrameRect_(
*self.window,
@@ -839,18 +889,18 @@ impl Window2 {
)
};
Some((
content_rect.origin.x as i32,
Self::bottom_left_to_top_left(content_rect),
))
content_rect.origin.x as f64,
util::bottom_left_to_top_left(content_rect),
).into())
}
pub fn set_position(&self, x: i32, y: i32) {
pub fn set_position(&self, position: LogicalPosition) {
let dummy = NSRect::new(
NSPoint::new(
x as f64,
position.x,
// While it's true that we're setting the top-left position, it still needs to be
// in a bottom-left coordinate system.
CGDisplay::main().pixels_high() as f64 - y as f64,
CGDisplay::main().pixels_high() as f64 - position.y,
),
NSSize::new(0f64, 0f64),
);
@@ -860,53 +910,51 @@ impl Window2 {
}
#[inline]
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
let factor = self.hidpi_factor() as f64; // API convention is that size is in physical pixels
pub fn get_inner_size(&self) -> Option<LogicalSize> {
let view_frame = unsafe { NSView::frame(*self.view) };
Some((view_frame.size.width as f64, view_frame.size.height as f64).into())
}
#[inline]
pub fn get_outer_size(&self) -> Option<LogicalSize> {
let view_frame = unsafe { NSWindow::frame(*self.window) };
Some((view_frame.size.width as f64, view_frame.size.height as f64).into())
}
#[inline]
pub fn set_inner_size(&self, size: LogicalSize) {
unsafe {
let view_frame = NSView::frame(*self.view);
Some(((view_frame.size.width*factor) as u32, (view_frame.size.height*factor) as u32))
NSWindow::setContentSize_(*self.window, NSSize::new(size.width as CGFloat, size.height as CGFloat));
}
}
pub fn set_min_dimensions(&self, dimensions: Option<LogicalSize>) {
unsafe {
let dimensions = dimensions.unwrap_or_else(|| (0, 0).into());
nswindow_set_min_dimensions(self.window.0, dimensions);
}
}
pub fn set_max_dimensions(&self, dimensions: Option<LogicalSize>) {
unsafe {
let dimensions = dimensions.unwrap_or_else(|| (!0, !0).into());
nswindow_set_max_dimensions(self.window.0, dimensions);
}
}
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
let factor = self.hidpi_factor() as f64; // API convention is that size is in physical pixels
unsafe {
let window_frame = NSWindow::frame(*self.window);
Some(((window_frame.size.width*factor) as u32, (window_frame.size.height*factor) as u32))
}
}
#[inline]
pub fn set_inner_size(&self, width: u32, height: u32) {
let factor = self.hidpi_factor() as f64; // API convention is that size is in physical pixels
unsafe {
NSWindow::setContentSize_(*self.window, NSSize::new((width as f64)/factor, (height as f64)/factor));
}
}
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
unsafe {
let (width, height) = dimensions.unwrap_or((0, 0));
nswindow_set_min_dimensions(self.window.0, width.into(), height.into());
}
}
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
unsafe {
let (width, height) = dimensions.unwrap_or((!0, !0));
nswindow_set_max_dimensions(self.window.0, width.into(), height.into());
}
}
#[inline]
pub fn platform_display(&self) -> *mut libc::c_void {
unimplemented!()
}
#[inline]
pub fn platform_window(&self) -> *mut libc::c_void {
*self.window as *mut libc::c_void
pub fn set_resizable(&self, resizable: bool) {
let mut win_attribs = self.delegate.state.win_attribs.borrow_mut();
win_attribs.resizable = resizable;
if win_attribs.fullscreen.is_none() {
let mut mask = unsafe { self.window.styleMask() };
if resizable {
mask |= NSWindowStyleMask::NSResizableWindowMask;
} else {
mask &= !NSWindowStyleMask::NSResizableWindowMask;
}
unsafe { util::set_style_mask(*self.window, *self.view, mask) };
} // Otherwise, we don't change the mask until we exit fullscreen.
}
pub fn set_cursor(&self, cursor: MouseCursor) {
@@ -933,7 +981,7 @@ impl Window2 {
MouseCursor::SeResize | MouseCursor::SwResize |
MouseCursor::NwseResize | MouseCursor::NeswResize |
MouseCursor::Cell | MouseCursor::NoneCursor |
MouseCursor::Cell |
MouseCursor::Wait | MouseCursor::Progress | MouseCursor::Help |
MouseCursor::Move | MouseCursor::AllScroll | MouseCursor::ZoomIn |
MouseCursor::ZoomOut => "arrowCursor",
@@ -947,46 +995,47 @@ impl Window2 {
}
}
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
let cls = Class::get("NSCursor").unwrap();
#[inline]
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
// TODO: Do this for real https://stackoverflow.com/a/40922095/5435443
CGDisplay::associate_mouse_and_mouse_cursor_position(!grab)
.map_err(|status| format!("Failed to grab cursor: `CGError` {:?}", status))
}
// TODO: Check for errors.
match state {
CursorState::Normal => {
let _: () = unsafe { msg_send![cls, unhide] };
let _ = CGDisplay::associate_mouse_and_mouse_cursor_position(true);
Ok(())
},
CursorState::Hide => {
let _: () = unsafe { msg_send![cls, hide] };
Ok(())
},
CursorState::Grab => {
let _: () = unsafe { msg_send![cls, hide] };
let _ = CGDisplay::associate_mouse_and_mouse_cursor_position(false);
Ok(())
#[inline]
pub fn hide_cursor(&self, hide: bool) {
let cursor_class = Class::get("NSCursor").unwrap();
// macOS uses a "hide counter" like Windows does, so we avoid incrementing it more than once.
// (otherwise, `hide_cursor(false)` would need to be called n times!)
if hide != self.cursor_hidden.get() {
if hide {
let _: () = unsafe { msg_send![cursor_class, hide] };
} else {
let _: () = unsafe { msg_send![cursor_class, unhide] };
}
self.cursor_hidden.replace(hide);
}
}
#[inline]
pub fn hidpi_factor(&self) -> f32 {
pub fn get_hidpi_factor(&self) -> f64 {
unsafe {
NSWindow::backingScaleFactor(*self.window) as f32
NSWindow::backingScaleFactor(*self.window) as f64
}
}
#[inline]
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
let (window_x, window_y) = self.get_position().unwrap_or((0, 0));
let (cursor_x, cursor_y) = (window_x + x, window_y + y);
// TODO: Check for errors.
let _ = CGDisplay::warp_mouse_cursor_position(appkit::CGPoint {
x: cursor_x as appkit::CGFloat,
y: cursor_y as appkit::CGFloat,
});
let _ = CGDisplay::associate_mouse_and_mouse_cursor_position(true);
pub fn set_cursor_position(&self, cursor_position: LogicalPosition) -> Result<(), String> {
let window_position = self.get_inner_position()
.ok_or("`get_inner_position` failed".to_owned())?;
let point = appkit::CGPoint {
x: (cursor_position.x + window_position.x) as CGFloat,
y: (cursor_position.y + window_position.y) as CGFloat,
};
CGDisplay::warp_mouse_cursor_position(point)
.map_err(|e| format!("`CGWarpMouseCursorPosition` failed: {:?}", e))?;
CGDisplay::associate_mouse_and_mouse_cursor_position(true)
.map_err(|e| format!("`CGAssociateMouseAndMouseCursorPosition` failed: {:?}", e))?;
Ok(())
}
@@ -1027,12 +1076,9 @@ impl Window2 {
// It will clean up at window_did_exit_fullscreen.
if current.is_none() {
let curr_mask = state.window.styleMask();
if !curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask) {
state.window.setStyleMask_(
NSWindowStyleMask::NSTitledWindowMask
| NSWindowStyleMask::NSResizableWindowMask,
);
let required = NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask;
if !curr_mask.contains(required) {
util::set_style_mask(*self.window, *self.view, required);
state.save_style_mask.set(Some(curr_mask));
}
}
@@ -1059,33 +1105,54 @@ impl Window2 {
}
unsafe {
let new_mask = if decorations {
let mut new_mask = if decorations {
NSWindowStyleMask::NSClosableWindowMask
| NSWindowStyleMask::NSMiniaturizableWindowMask
| NSWindowStyleMask::NSResizableWindowMask
| NSWindowStyleMask::NSTitledWindowMask
} else {
NSWindowStyleMask::NSBorderlessWindowMask
| NSWindowStyleMask::NSResizableWindowMask
};
if !win_attribs.resizable {
new_mask &= !NSWindowStyleMask::NSResizableWindowMask;
}
util::set_style_mask(*state.window, *state.view, new_mask);
}
}
state.window.setStyleMask_(new_mask);
#[inline]
pub fn set_always_on_top(&self, always_on_top: bool) {
unsafe {
let level = if always_on_top {
ffi::NSWindowLevel::NSFloatingWindowLevel
} else {
ffi::NSWindowLevel::NSNormalWindowLevel
};
let _: () = msg_send![*self.window, setLevel:level];
}
}
#[inline]
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
// macOS doesn't have window icons. Though, there is `setRepresentedFilename`, but that's
// semantically distinct and should only be used when the window is in some representing a
// specific file/directory. For instance, Terminal.app uses this for the CWD. Anyway, that
// should eventually be implemented as `WindowBuilderExt::with_represented_file` or
// something, and doesn't have anything to do with this.
// semantically distinct and should only be used when the window is in some way
// representing a specific file/directory. For instance, Terminal.app uses this for the
// CWD. Anyway, that should eventually be implemented as
// `WindowBuilderExt::with_represented_file` or something, and doesn't have anything to do
// with `set_window_icon`.
// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/WinPanel/Tasks/SettingWindowTitle.html
}
#[inline]
pub fn set_ime_spot(&self, logical_spot: LogicalPosition) {
set_ime_spot(*self.view, *self.input_context, logical_spot.x, logical_spot.y);
}
#[inline]
pub fn get_current_monitor(&self) -> RootMonitorId {
unsafe {
self::get_current_monitor()
self::get_current_monitor(*self.window)
}
}
}
@@ -1096,51 +1163,50 @@ pub fn get_window_id(window_cocoa_id: id) -> Id {
Id(window_cocoa_id as *const objc::runtime::Object as usize)
}
unsafe fn nswindow_set_min_dimensions<V: NSWindow + Copy>(
window: V, min_width: f64, min_height: f64)
{
unsafe fn nswindow_set_min_dimensions<V: NSWindow + Copy>(window: V, mut min_size: LogicalSize) {
let mut current_rect = NSWindow::frame(window);
let content_rect = NSWindow::contentRectForFrameRect_(window, NSWindow::frame(window));
// Convert from client area size to window size
min_size.width += (current_rect.size.width - content_rect.size.width) as f64; // this tends to be 0
min_size.height += (current_rect.size.height - content_rect.size.height) as f64;
window.setMinSize_(NSSize {
width: min_width,
height: min_height,
width: min_size.width as CGFloat,
height: min_size.height as CGFloat,
});
// If necessary, resize the window to match constraint
let mut current_rect = NSWindow::frame(window);
if current_rect.size.width < min_width {
current_rect.size.width = min_width;
if current_rect.size.width < min_size.width {
current_rect.size.width = min_size.width;
window.setFrame_display_(current_rect, 0)
}
if current_rect.size.height < min_height {
// The origin point of a rectangle is at its bottom left in Cocoa. To
// ensure the window's top-left point remains the same:
current_rect.origin.y +=
current_rect.size.height - min_height;
current_rect.size.height = min_height;
if current_rect.size.height < min_size.height {
// The origin point of a rectangle is at its bottom left in Cocoa.
// To ensure the window's top-left point remains the same:
current_rect.origin.y += current_rect.size.height - min_size.height;
current_rect.size.height = min_size.height;
window.setFrame_display_(current_rect, 0)
}
}
unsafe fn nswindow_set_max_dimensions<V: NSWindow + Copy>(
window: V, max_width: f64, max_height: f64)
{
unsafe fn nswindow_set_max_dimensions<V: NSWindow + Copy>(window: V, mut max_size: LogicalSize) {
let mut current_rect = NSWindow::frame(window);
let content_rect = NSWindow::contentRectForFrameRect_(window, NSWindow::frame(window));
// Convert from client area size to window size
max_size.width += (current_rect.size.width - content_rect.size.width) as f64; // this tends to be 0
max_size.height += (current_rect.size.height - content_rect.size.height) as f64;
window.setMaxSize_(NSSize {
width: max_width,
height: max_height,
width: max_size.width as CGFloat,
height: max_size.height as CGFloat,
});
// If necessary, resize the window to match constraint
let mut current_rect = NSWindow::frame(window);
if current_rect.size.width > max_width {
current_rect.size.width = max_width;
if current_rect.size.width > max_size.width {
current_rect.size.width = max_size.width;
window.setFrame_display_(current_rect, 0)
}
if current_rect.size.height > max_height {
// The origin point of a rectangle is at its bottom left in
// Cocoa. To ensure the window's top-left point remains the
// same:
current_rect.origin.y +=
current_rect.size.height - max_height;
current_rect.size.height = max_height;
if current_rect.size.height > max_size.height {
// The origin point of a rectangle is at its bottom left in Cocoa.
// To ensure the window's top-left point remains the same:
current_rect.origin.y += current_rect.size.height - max_size.height;
current_rect.size.height = max_size.height;
window.setFrame_display_(current_rect, 0)
}
}
@@ -1169,8 +1235,7 @@ impl Drop for IdRef {
fn drop(&mut self) {
if self.0 != nil {
unsafe {
let autoreleasepool =
NSAutoreleasePool::new(nil);
let autoreleasepool = NSAutoreleasePool::new(nil);
let _ : () = msg_send![self.0, release];
let _ : () = msg_send![autoreleasepool, release];
};

196
src/platform/windows/dpi.rs Normal file
View File

@@ -0,0 +1,196 @@
#![allow(non_snake_case, unused_unsafe)]
use std::mem;
use std::os::raw::c_void;
use std::sync::{Once, ONCE_INIT};
use winapi::shared::minwindef::{BOOL, UINT, FALSE};
use winapi::shared::windef::{
DPI_AWARENESS_CONTEXT,
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE,
HDC,
HMONITOR,
HWND,
};
use winapi::shared::winerror::S_OK;
use winapi::um::libloaderapi::{GetProcAddress, LoadLibraryA};
use winapi::um::shellscalingapi::{
MDT_EFFECTIVE_DPI,
MONITOR_DPI_TYPE,
PROCESS_DPI_AWARENESS,
PROCESS_PER_MONITOR_DPI_AWARE,
};
use winapi::um::wingdi::{GetDeviceCaps, LOGPIXELSX};
use winapi::um::winnt::{HRESULT, LPCSTR};
use winapi::um::winuser::{self, MONITOR_DEFAULTTONEAREST};
const DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2: DPI_AWARENESS_CONTEXT = -4isize as _;
type SetProcessDPIAware = unsafe extern "system" fn () -> BOOL;
type SetProcessDpiAwareness = unsafe extern "system" fn (
value: PROCESS_DPI_AWARENESS,
) -> HRESULT;
type SetProcessDpiAwarenessContext = unsafe extern "system" fn (
value: DPI_AWARENESS_CONTEXT,
) -> BOOL;
type GetDpiForWindow = unsafe extern "system" fn (hwnd: HWND) -> UINT;
type GetDpiForMonitor = unsafe extern "system" fn (
hmonitor: HMONITOR,
dpi_type: MONITOR_DPI_TYPE,
dpi_x: *mut UINT,
dpi_y: *mut UINT,
) -> HRESULT;
type EnableNonClientDpiScaling = unsafe extern "system" fn (hwnd: HWND) -> BOOL;
// Helper function to dynamically load function pointer.
// `library` and `function` must be zero-terminated.
fn get_function_impl(library: &str, function: &str) -> Option<*const c_void> {
assert_eq!(library.chars().last(), Some('\0'));
assert_eq!(function.chars().last(), Some('\0'));
// Library names we will use are ASCII so we can use the A version to avoid string conversion.
let module = unsafe { LoadLibraryA(library.as_ptr() as LPCSTR) };
if module.is_null() {
return None;
}
let function_ptr = unsafe { GetProcAddress(module, function.as_ptr() as LPCSTR) };
if function_ptr.is_null() {
return None;
}
Some(function_ptr as _)
}
macro_rules! get_function {
($lib:expr, $func:ident) => {
get_function_impl(concat!($lib, '\0'), concat!(stringify!($func), '\0'))
.map(|f| unsafe { mem::transmute::<*const _, $func>(f) })
}
}
lazy_static! {
static ref GET_DPI_FOR_WINDOW: Option<GetDpiForWindow> = get_function!(
"user32.dll",
GetDpiForWindow
);
static ref GET_DPI_FOR_MONITOR: Option<GetDpiForMonitor> = get_function!(
"shcore.dll",
GetDpiForMonitor
);
static ref ENABLE_NON_CLIENT_DPI_SCALING: Option<EnableNonClientDpiScaling> = get_function!(
"user32.dll",
EnableNonClientDpiScaling
);
}
pub fn become_dpi_aware(enable: bool) {
if !enable { return; }
static ENABLE_DPI_AWARENESS: Once = ONCE_INIT;
ENABLE_DPI_AWARENESS.call_once(|| { unsafe {
if let Some(SetProcessDpiAwarenessContext) = get_function!(
"user32.dll",
SetProcessDpiAwarenessContext
) {
// We are on Windows 10 Anniversary Update (1607) or later.
if SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
== FALSE {
// V2 only works with Windows 10 Creators Update (1703). Try using the older
// V1 if we can't set V2.
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
}
} else if let Some(SetProcessDpiAwareness) = get_function!(
"shcore.dll",
SetProcessDpiAwareness
) {
// We are on Windows 8.1 or later.
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
} else if let Some(SetProcessDPIAware) = get_function!(
"user32.dll",
SetProcessDPIAware
) {
// We are on Vista or later.
SetProcessDPIAware();
}
} });
}
pub fn enable_non_client_dpi_scaling(hwnd: HWND) {
unsafe {
if let Some(EnableNonClientDpiScaling) = *ENABLE_NON_CLIENT_DPI_SCALING {
EnableNonClientDpiScaling(hwnd);
}
}
}
pub fn get_monitor_dpi(hmonitor: HMONITOR) -> Option<u32> {
unsafe {
if let Some(GetDpiForMonitor) = *GET_DPI_FOR_MONITOR {
// We are on Windows 8.1 or later.
let mut dpi_x = 0;
let mut dpi_y = 0;
if GetDpiForMonitor(hmonitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) == S_OK {
// MSDN says that "the values of *dpiX and *dpiY are identical. You only need to
// record one of the values to determine the DPI and respond appropriately".
// https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx
return Some(dpi_x as u32)
}
}
}
None
}
pub const BASE_DPI: u32 = 96;
pub fn dpi_to_scale_factor(dpi: u32) -> f64 {
dpi as f64 / BASE_DPI as f64
}
pub unsafe fn get_window_dpi(hwnd: HWND, hdc: HDC) -> u32 {
if let Some(GetDpiForWindow) = *GET_DPI_FOR_WINDOW {
// We are on Windows 10 Anniversary Update (1607) or later.
match GetDpiForWindow(hwnd) {
0 => BASE_DPI, // 0 is returned if hwnd is invalid
dpi => dpi as u32,
}
} else if let Some(GetDpiForMonitor) = *GET_DPI_FOR_MONITOR {
// We are on Windows 8.1 or later.
let monitor = winuser::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if monitor.is_null() {
return BASE_DPI;
}
let mut dpi_x = 0;
let mut dpi_y = 0;
if GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) == S_OK {
dpi_x as u32
} else {
BASE_DPI
}
} else {
// We are on Vista or later.
if winuser::IsProcessDPIAware() != FALSE {
// If the process is DPI aware, then scaling must be handled by the application using
// this DPI value.
GetDeviceCaps(hdc, LOGPIXELSX) as u32
} else {
// If the process is DPI unaware, then scaling is performed by the OS; we thus return
// 96 (scale factor 1.0) to prevent the window from being re-scaled by both the
// application and the WM.
BASE_DPI
}
}
}
// Use this when you have both the HWND and HDC on hand (i.e. window methods)
pub fn get_window_scale_factor(hwnd: HWND, hdc: HDC) -> f64 {
dpi_to_scale_factor(unsafe { get_window_dpi(hwnd, hdc) })
}
// Use this when you only have the HWND (i.e. event handling)
pub fn get_hwnd_scale_factor(hwnd: HWND) -> f64 {
let hdc = unsafe { winuser::GetDC(hwnd) };
if hdc.is_null() {
panic!("[winit] `GetDC` returned null!");
}
unsafe { get_window_scale_factor(hwnd, hdc) }
}

View File

@@ -45,8 +45,8 @@ pub fn vkey_to_winit_vkey(vkey: c_int) -> Option<VirtualKeyCode> {
winuser::VK_RSHIFT => Some(VirtualKeyCode::RShift),
winuser::VK_LCONTROL => Some(VirtualKeyCode::LControl),
winuser::VK_RCONTROL => Some(VirtualKeyCode::RControl),
winuser::VK_LMENU => Some(VirtualKeyCode::LMenu),
winuser::VK_RMENU => Some(VirtualKeyCode::RMenu),
winuser::VK_LMENU => Some(VirtualKeyCode::LAlt),
winuser::VK_RMENU => Some(VirtualKeyCode::RAlt),
winuser::VK_PAUSE => Some(VirtualKeyCode::Pause),
winuser::VK_CAPITAL => Some(VirtualKeyCode::Capital),
winuser::VK_KANA => Some(VirtualKeyCode::Kana),
@@ -205,31 +205,49 @@ pub fn vkey_to_winit_vkey(vkey: c_int) -> Option<VirtualKeyCode> {
}
}
pub fn vkey_left_right(vkey: c_int, scancode: UINT, extended: bool) -> c_int {
match vkey {
winuser::VK_SHIFT => unsafe { winuser::MapVirtualKeyA(
scancode,
winuser::MAPVK_VSC_TO_VK_EX,
) as _ },
winuser::VK_CONTROL => if extended {
winuser::VK_RCONTROL
} else {
winuser::VK_LCONTROL
},
winuser::VK_MENU => if extended {
winuser::VK_RMENU
} else {
winuser::VK_LMENU
},
_ => vkey,
}
pub fn handle_extended_keys(vkey: c_int, mut scancode: UINT, extended: bool) -> Option<(c_int, UINT)> {
// Welcome to hell https://blog.molecular-matters.com/2011/09/05/properly-handling-keyboard-input/
let vkey = match vkey {
winuser::VK_SHIFT => unsafe { winuser::MapVirtualKeyA(
scancode,
winuser::MAPVK_VSC_TO_VK_EX,
) as _ },
winuser::VK_CONTROL => if extended {
winuser::VK_RCONTROL
} else {
winuser::VK_LCONTROL
},
winuser::VK_MENU => if extended {
winuser::VK_RMENU
} else {
winuser::VK_LMENU
},
_ => match scancode {
// This is only triggered when using raw input. Without this check, we get two events whenever VK_PAUSE is
// pressed, the first one having scancode 0x1D but vkey VK_PAUSE...
0x1D if vkey == winuser::VK_PAUSE => return None,
// ...and the second having scancode 0x45 but an unmatched vkey!
0x45 => winuser::VK_PAUSE,
// VK_PAUSE and VK_SCROLL have the same scancode when using modifiers, alongside incorrect vkey values.
0x46 => {
if extended {
scancode = 0x45;
winuser::VK_PAUSE
} else {
winuser::VK_SCROLL
}
},
_ => vkey,
},
};
Some((vkey, scancode))
}
pub fn vkeycode_to_element(wparam: WPARAM, lparam: LPARAM) -> (ScanCode, Option<VirtualKeyCode>) {
pub fn process_key_params(wparam: WPARAM, lparam: LPARAM) -> Option<(ScanCode, Option<VirtualKeyCode>)> {
let scancode = ((lparam >> 16) & 0xff) as UINT;
let extended = (lparam & 0x01000000) != 0;
let vkey = vkey_left_right(wparam as _, scancode, extended);
(scancode, vkey_to_winit_vkey(vkey))
handle_extended_keys(wparam as _, scancode, extended)
.map(|(vkey, scancode)| (scancode, vkey_to_winit_vkey(vkey)))
}
// This is needed as windows doesn't properly distinguish

View File

@@ -12,42 +12,55 @@
//! The closure passed to the `execute_in_thread` method takes an `Inserter` that you can use to
//! add a `WindowState` entry to a list of window to be used by the callback.
use std::{mem, ptr, thread};
use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::OsString;
use std::mem;
use std::os::windows::ffi::OsStringExt;
use std::os::windows::io::AsRawHandle;
use std::ptr;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Barrier;
use std::sync::Mutex;
use std::sync::Condvar;
use std::thread;
use std::sync::{Arc, Barrier, Condvar, mpsc, Mutex};
use winapi::shared::minwindef::{LOWORD, HIWORD, DWORD, WPARAM, LPARAM, INT, UINT, LRESULT, MAX_PATH};
use winapi::ctypes::c_int;
use winapi::shared::minwindef::{
BOOL,
DWORD,
HIWORD,
INT,
LOWORD,
LPARAM,
LRESULT,
MAX_PATH,
UINT,
WPARAM,
};
use winapi::shared::windef::{HWND, POINT, RECT};
use winapi::shared::windowsx;
use winapi::um::{winuser, shellapi, processthreadsapi};
use winapi::um::winnt::{LONG, SHORT};
use winapi::um::winnt::{LONG, LPCSTR, SHORT};
use events::DeviceEvent;
use {
ControlFlow,
Event,
EventsLoopClosed,
KeyboardInput,
LogicalPosition,
LogicalSize,
PhysicalSize,
WindowEvent,
WindowId as SuperWindowId,
};
use events::{DeviceEvent, Touch, TouchPhase};
use platform::platform::{event, Cursor, WindowId, DEVICE_ID, wrap_device_id, util};
use platform::platform::event::{vkey_to_winit_vkey, vkey_left_right};
use platform::platform::raw_input::*;
use platform::platform::dpi::{
become_dpi_aware,
dpi_to_scale_factor,
enable_non_client_dpi_scaling,
get_hwnd_scale_factor,
};
use platform::platform::event::{handle_extended_keys, process_key_params, vkey_to_winit_vkey};
use platform::platform::raw_input::{get_raw_input_data, get_raw_mouse_button_state};
use platform::platform::window::adjust_size;
use ControlFlow;
use CursorState;
use Event;
use EventsLoopClosed;
use KeyboardInput;
use WindowAttributes;
use WindowEvent;
use WindowId as SuperWindowId;
use events::{Touch, TouchPhase};
/// Contains saved window info for switching between fullscreen
#[derive(Clone)]
pub struct SavedWindowInfo {
@@ -57,6 +70,11 @@ pub struct SavedWindowInfo {
pub ex_style: LONG,
/// Window position and size
pub rect: RECT,
// Since a window can be fullscreened to a different monitor, a DPI change can be triggered. This could result in
// the window being automitcally resized to smaller/larger than it was supposed to be restored to, so we thus must
// check if the post-fullscreen DPI matches the pre-fullscreen DPI.
pub is_fullscreen: bool,
pub dpi_factor: Option<f64>,
}
/// Contains information about states and the window that the callback is going to use.
@@ -64,14 +82,31 @@ pub struct SavedWindowInfo {
pub struct WindowState {
/// Cursor to set at the next `WM_SETCURSOR` event received.
pub cursor: Cursor,
/// Cursor state to set at the next `WM_SETCURSOR` event received.
pub cursor_state: CursorState,
pub cursor_grabbed: bool,
pub cursor_hidden: bool,
/// Used by `WM_GETMINMAXINFO`.
pub attributes: WindowAttributes,
pub max_size: Option<PhysicalSize>,
pub min_size: Option<PhysicalSize>,
/// Will contain `true` if the mouse is hovering the window.
pub mouse_in_window: bool,
/// Saved window info for fullscreen restored
pub saved_window_info: Option<SavedWindowInfo>,
// This is different from the value in `SavedWindowInfo`! That one represents the DPI saved upon entering
// fullscreen. This will always be the most recent DPI for the window.
pub dpi_factor: f64,
}
impl WindowState {
pub fn update_min_max(&mut self, old_dpi_factor: f64, new_dpi_factor: f64) {
let scale_factor = new_dpi_factor / old_dpi_factor;
let dpi_adjuster = |mut physical_size: PhysicalSize| -> PhysicalSize {
physical_size.width *= scale_factor;
physical_size.height *= scale_factor;
physical_size
};
self.max_size = self.max_size.map(&dpi_adjuster);
self.min_size = self.min_size.map(&dpi_adjuster);
}
}
/// Dummy object that allows inserting a window's state.
@@ -98,11 +133,17 @@ pub struct EventsLoop {
// Variable that contains the block state of the win32 event loop thread during a WM_SIZE event.
// The mutex's value is `true` when it's blocked, and should be set to false when it's done
// blocking. That's done by the parent thread when it receives a Resized event.
win32_block_loop: Arc<(Mutex<bool>, Condvar)>
win32_block_loop: Arc<(Mutex<bool>, Condvar)>,
}
impl EventsLoop {
pub fn new() -> EventsLoop {
Self::with_dpi_awareness(true)
}
pub fn with_dpi_awareness(dpi_aware: bool) -> EventsLoop {
become_dpi_aware(dpi_aware);
// The main events transfer channel.
let (tx, rx) = mpsc::channel();
let win32_block_loop = Arc::new((Mutex::new(false), Condvar::new()));
@@ -171,7 +212,7 @@ impl EventsLoop {
EventsLoop {
thread_id,
receiver: rx,
win32_block_loop
win32_block_loop,
}
}
@@ -289,22 +330,21 @@ impl EventsLoopProxy {
where
F: FnMut(Inserter) + Send + 'static,
{
unsafe {
// We are using double-boxing here because it make casting back much easier
let boxed = Box::new(function) as Box<FnMut(_)>;
let boxed2 = Box::new(boxed);
let raw = Box::into_raw(boxed2);
// We are using double-boxing here because it make casting back much easier
let double_box = Box::new(Box::new(function) as Box<FnMut(_)>);
let raw = Box::into_raw(double_box);
let res = winuser::PostThreadMessageA(
let res = unsafe {
winuser::PostThreadMessageA(
self.thread_id,
*EXEC_MSG_ID,
raw as *mut () as usize as WPARAM,
0,
);
// PostThreadMessage can only fail if the thread ID is invalid (which shouldn't happen
// as the events loop is still alive) or if the queue is full.
assert!(res != 0, "PostThreadMessage failed; is the messages queue full?");
}
)
};
// PostThreadMessage can only fail if the thread ID is invalid (which shouldn't happen as
// the events loop is still alive) or if the queue is full.
assert!(res != 0, "PostThreadMessage failed; is the messages queue full?");
}
}
@@ -313,7 +353,7 @@ lazy_static! {
// WPARAM and LPARAM are unused.
static ref WAKEUP_MSG_ID: u32 = {
unsafe {
winuser::RegisterWindowMessageA("Winit::WakeupMsg\0".as_ptr() as *const i8)
winuser::RegisterWindowMessageA("Winit::WakeupMsg\0".as_ptr() as LPCSTR)
}
};
// Message sent when we want to execute a closure in the thread.
@@ -321,14 +361,21 @@ lazy_static! {
// and LPARAM is unused.
static ref EXEC_MSG_ID: u32 = {
unsafe {
winuser::RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr() as *const i8)
winuser::RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr() as LPCSTR)
}
};
// Message sent by a `Window` when it wants to be destroyed by the main thread.
// WPARAM and LPARAM are unused.
pub static ref DESTROY_MSG_ID: u32 = {
unsafe {
winuser::RegisterWindowMessageA("Winit::DestroyMsg\0".as_ptr() as *const i8)
winuser::RegisterWindowMessageA("Winit::DestroyMsg\0".as_ptr() as LPCSTR)
}
};
// Message sent by a `Window` after creation if it has a DPI != 96.
// WPARAM is the the DPI (u32). LOWORD of LPARAM is width, and HIWORD is height.
pub static ref INITIAL_DPI_MSG_ID: u32 = {
unsafe {
winuser::RegisterWindowMessageA("Winit::InitialDpiMsg\0".as_ptr() as LPCSTR)
}
};
}
@@ -385,11 +432,18 @@ unsafe fn release_mouse() {
//
// Returning 0 tells the Win32 API that the message has been processed.
// FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary
pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
wparam: WPARAM, lparam: LPARAM)
-> LRESULT
{
pub unsafe extern "system" fn callback(
window: HWND,
msg: UINT,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
match msg {
winuser::WM_NCCREATE => {
enable_non_client_dpi_scaling(window);
winuser::DefWindowProcW(window, msg, wparam, lparam)
},
winuser::WM_CLOSE => {
use events::WindowEvent::CloseRequested;
send_event(Event::WindowEvent {
@@ -427,9 +481,14 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
let windowpos = lparam as *const winuser::WINDOWPOS;
if (*windowpos).flags & winuser::SWP_NOMOVE != winuser::SWP_NOMOVE {
let dpi_factor = get_hwnd_scale_factor(window);
let logical_position = LogicalPosition::from_physical(
((*windowpos).x, (*windowpos).y),
dpi_factor,
);
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: Moved((*windowpos).x, (*windowpos).y),
event: Moved(logical_position),
});
}
@@ -448,9 +507,11 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
let mut context_stash = context_stash.borrow_mut();
let cstash = context_stash.as_mut().unwrap();
let dpi_factor = get_hwnd_scale_factor(window);
let logical_size = LogicalSize::from_physical((w, h), dpi_factor);
let event = Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: Resized(w, h),
event: Resized(logical_size),
};
// If this window has been inserted into the window map, the resize event happened
@@ -528,10 +589,12 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
let x = windowsx::GET_X_LPARAM(lparam) as f64;
let y = windowsx::GET_Y_LPARAM(lparam) as f64;
let dpi_factor = get_hwnd_scale_factor(window);
let position = LogicalPosition::from_physical((x, y), dpi_factor);
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event::get_key_mods() },
event: CursorMoved { device_id: DEVICE_ID, position, modifiers: event::get_key_mods() },
});
0
@@ -586,26 +649,27 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
if msg == winuser::WM_SYSKEYDOWN && wparam as i32 == winuser::VK_F4 {
winuser::DefWindowProcW(window, msg, wparam, lparam)
} else {
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state: Pressed,
scancode: scancode,
virtual_keycode: vkey,
modifiers: event::get_key_mods(),
}
}
});
// Windows doesn't emit a delete character by default, but in order to make it
// consistent with the other platforms we'll emit a delete character here.
if vkey == Some(VirtualKeyCode::Delete) {
if let Some((scancode, vkey)) = process_key_params(wparam, lparam) {
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: WindowEvent::ReceivedCharacter('\u{7F}'),
event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state: Pressed,
scancode: scancode,
virtual_keycode: vkey,
modifiers: event::get_key_mods(),
}
}
});
// Windows doesn't emit a delete character by default, but in order to make it
// consistent with the other platforms we'll emit a delete character here.
if vkey == Some(VirtualKeyCode::Delete) {
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: WindowEvent::ReceivedCharacter('\u{7F}'),
});
}
}
0
}
@@ -613,19 +677,20 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
winuser::WM_KEYUP | winuser::WM_SYSKEYUP => {
use events::ElementState::Released;
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state: Released,
scancode: scancode,
virtual_keycode: vkey,
modifiers: event::get_key_mods(),
},
}
});
if let Some((scancode, vkey)) = process_key_params(wparam, lparam) {
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state: Released,
scancode: scancode,
virtual_keycode: vkey,
modifiers: event::get_key_mods(),
},
}
});
}
0
},
@@ -836,23 +901,25 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
};
let scancode = keyboard.MakeCode as _;
let extended = util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _);
let vkey = vkey_left_right(
let extended = util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _)
| util::has_flag(keyboard.Flags, winuser::RI_KEY_E1 as _);
if let Some((vkey, scancode)) = handle_extended_keys(
keyboard.VKey as _,
scancode,
extended,
);
let virtual_keycode = vkey_to_winit_vkey(vkey);
) {
let virtual_keycode = vkey_to_winit_vkey(vkey);
send_event(Event::DeviceEvent {
device_id,
event: Key(KeyboardInput {
scancode,
state,
virtual_keycode,
modifiers: event::get_key_mods(),
}),
});
send_event(Event::DeviceEvent {
device_id,
event: Key(KeyboardInput {
scancode,
state,
virtual_keycode,
modifiers: event::get_key_mods(),
}),
});
}
}
}
}
@@ -865,10 +932,17 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
let mut inputs = Vec::with_capacity( pcount );
inputs.set_len( pcount );
let htouch = lparam as winuser::HTOUCHINPUT;
if winuser::GetTouchInputInfo( htouch, pcount as UINT,
inputs.as_mut_ptr(),
mem::size_of::<winuser::TOUCHINPUT>() as INT ) > 0 {
if winuser::GetTouchInputInfo(
htouch,
pcount as UINT,
inputs.as_mut_ptr(),
mem::size_of::<winuser::TOUCHINPUT>() as INT,
) > 0 {
let dpi_factor = get_hwnd_scale_factor(window);
for input in &inputs {
let x = (input.x as f64) / 100f64;
let y = (input.y as f64) / 100f64;
let location = LogicalPosition::from_physical((x, y), dpi_factor);
send_event( Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: WindowEvent::Touch(Touch {
@@ -882,8 +956,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
} else {
continue;
},
location: ((input.x as f64) / 100f64,
(input.y as f64) / 100f64),
location,
id: input.dwID as u64,
device_id: DEVICE_ID,
})
@@ -903,11 +976,14 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
let x = windowsx::GET_X_LPARAM(lparam) as f64;
let y = windowsx::GET_Y_LPARAM(lparam) as f64;
let dpi_factor = get_hwnd_scale_factor(window);
let position = LogicalPosition::from_physical((x, y), dpi_factor);
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event::get_key_mods() },
event: CursorMoved { device_id: DEVICE_ID, position, modifiers: event::get_key_mods() },
});
0
},
@@ -922,30 +998,24 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
winuser::WM_SETCURSOR => {
let call_def_window_proc = CONTEXT_STASH.with(|context_stash| {
let cstash = context_stash.borrow();
let mut call_def_window_proc = false;
if let Some(cstash) = cstash.as_ref() {
if let Some(w_stash) = cstash.windows.get(&window) {
if let Ok(window_state) = w_stash.lock() {
if window_state.mouse_in_window {
match window_state.cursor_state {
CursorState::Normal => {
winuser::SetCursor(winuser::LoadCursorW(
ptr::null_mut(),
window_state.cursor));
},
CursorState::Grab | CursorState::Hide => {
winuser::SetCursor(ptr::null_mut());
}
}
} else {
call_def_window_proc = true;
}
context_stash
.borrow()
.as_ref()
.and_then(|cstash| cstash.windows.get(&window))
.map(|window_state_mutex| {
let window_state = window_state_mutex.lock().unwrap();
if window_state.mouse_in_window {
let cursor = winuser::LoadCursorW(
ptr::null_mut(),
window_state.cursor.0,
);
winuser::SetCursor(cursor);
false
} else {
true
}
}
}
call_def_window_proc
})
.unwrap_or(true)
});
if call_def_window_proc {
@@ -987,18 +1057,15 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
if let Some(wstash) = cstash.windows.get(&window) {
let window_state = wstash.lock().unwrap();
if window_state.attributes.min_dimensions.is_some() ||
window_state.attributes.max_dimensions.is_some() {
if window_state.min_size.is_some() || window_state.max_size.is_some() {
let style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD;
let ex_style = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD;
if let Some(min_dimensions) = window_state.attributes.min_dimensions {
let (width, height) = adjust_size(min_dimensions, style, ex_style);
if let Some(min_size) = window_state.min_size {
let (width, height) = adjust_size(min_size, style, ex_style);
(*mmi).ptMinTrackSize = POINT { x: width as i32, y: height as i32 };
}
if let Some(max_dimensions) = window_state.attributes.max_dimensions {
let (width, height) = adjust_size(max_dimensions, style, ex_style);
if let Some(max_size) = window_state.max_size {
let (width, height) = adjust_size(max_size, style, ex_style);
(*mmi).ptMaxTrackSize = POINT { x: width as i32, y: height as i32 };
}
}
@@ -1009,10 +1076,115 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
0
},
// Only sent on Windows 8.1 or newer. On Windows 7 and older user has to log out to change
// DPI, therefore all applications are closed while DPI is changing.
winuser::WM_DPICHANGED => {
use events::WindowEvent::HiDpiFactorChanged;
// This message actually provides two DPI values - x and y. However MSDN says that
// "you only need to use either the X-axis or the Y-axis value when scaling your
// application since they are the same".
// https://msdn.microsoft.com/en-us/library/windows/desktop/dn312083(v=vs.85).aspx
let new_dpi_x = u32::from(LOWORD(wparam as DWORD));
let new_dpi_factor = dpi_to_scale_factor(new_dpi_x);
let suppress_resize = CONTEXT_STASH.with(|context_stash| {
context_stash
.borrow()
.as_ref()
.and_then(|cstash| cstash.windows.get(&window))
.map(|window_state_mutex| {
let mut window_state = window_state_mutex.lock().unwrap();
let suppress_resize = window_state.saved_window_info
.as_mut()
.map(|saved_window_info| {
let dpi_changed = if !saved_window_info.is_fullscreen {
saved_window_info.dpi_factor.take() != Some(new_dpi_factor)
} else {
false
};
!dpi_changed || saved_window_info.is_fullscreen
})
.unwrap_or(false);
// Now we adjust the min/max dimensions for the new DPI.
if !suppress_resize {
let old_dpi_factor = window_state.dpi_factor;
window_state.update_min_max(old_dpi_factor, new_dpi_factor);
}
window_state.dpi_factor = new_dpi_factor;
suppress_resize
})
.unwrap_or(false)
});
// This prevents us from re-applying DPI adjustment to the restored size after exiting
// fullscreen (the restored size is already DPI adjusted).
if !suppress_resize {
// Resize window to the size suggested by Windows.
let rect = &*(lparam as *const RECT);
winuser::SetWindowPos(
window,
ptr::null_mut(),
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
winuser::SWP_NOZORDER | winuser::SWP_NOACTIVATE,
);
}
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: HiDpiFactorChanged(new_dpi_factor),
});
0
},
_ => {
if msg == *DESTROY_MSG_ID {
winuser::DestroyWindow(window);
0
} else if msg == *INITIAL_DPI_MSG_ID {
use events::WindowEvent::HiDpiFactorChanged;
let scale_factor = dpi_to_scale_factor(wparam as u32);
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: HiDpiFactorChanged(scale_factor),
});
// Automatically resize for actual DPI
let width = LOWORD(lparam as DWORD) as u32;
let height = HIWORD(lparam as DWORD) as u32;
let (adjusted_width, adjusted_height): (u32, u32) = PhysicalSize::from_logical(
(width, height),
scale_factor,
).into();
// We're not done yet! `SetWindowPos` needs the window size, not the client area size.
let mut rect = RECT {
top: 0,
left: 0,
bottom: adjusted_height as LONG,
right: adjusted_width as LONG,
};
let dw_style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD;
let b_menu = !winuser::GetMenu(window).is_null() as BOOL;
let dw_style_ex = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD;
winuser::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
let outer_x = (rect.right - rect.left).abs() as c_int;
let outer_y = (rect.top - rect.bottom).abs() as c_int;
winuser::SetWindowPos(
window,
ptr::null_mut(),
0,
0,
outer_x,
outer_y,
winuser::SWP_NOMOVE
| winuser::SWP_NOREPOSITION
| winuser::SWP_NOZORDER
| winuser::SWP_NOACTIVATE,
);
0
} else {
winuser::DefWindowProcW(window, msg, wparam, lparam)
}

View File

@@ -11,13 +11,17 @@ pub use self::window::Window;
pub struct PlatformSpecificWindowBuilderAttributes {
pub parent: Option<HWND>,
pub taskbar_icon: Option<::Icon>,
pub no_redirection_bitmap: bool,
}
unsafe impl Send for PlatformSpecificWindowBuilderAttributes {}
unsafe impl Sync for PlatformSpecificWindowBuilderAttributes {}
// TODO: document what this means
pub type Cursor = *const winapi::ctypes::wchar_t;
// Cursor name in UTF-16. Used to set cursor in `WM_SETCURSOR`.
#[derive(Debug, Clone)]
pub struct Cursor(pub *const winapi::ctypes::wchar_t);
unsafe impl Send for Cursor {}
unsafe impl Sync for Cursor {}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(u32);
@@ -44,6 +48,7 @@ pub struct WindowId(HWND);
unsafe impl Send for WindowId {}
unsafe impl Sync for WindowId {}
mod dpi;
mod event;
mod events_loop;
mod icon;

View File

@@ -1,172 +1,173 @@
use winapi::ctypes::wchar_t;
use winapi::shared::minwindef::{DWORD, LPARAM, BOOL, TRUE};
use winapi::shared::windef::{HMONITOR, HDC, LPRECT, HWND};
use winapi::shared::minwindef::{BOOL, DWORD, LPARAM, TRUE};
use winapi::shared::windef::{HDC, HMONITOR, HWND, LPRECT, POINT};
use winapi::um::winnt::LONG;
use winapi::um::winuser;
use std::collections::VecDeque;
use std::{mem, ptr};
use std::collections::VecDeque;
use super::{EventsLoop, util};
use dpi::{PhysicalPosition, PhysicalSize};
use platform::platform::dpi::{dpi_to_scale_factor, get_monitor_dpi};
use platform::platform::window::Window;
/// Win32 implementation of the main `MonitorId` object.
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct MonitorId {
/// The system name of the adapter.
adapter_name: [wchar_t; 32],
/// Monitor handle.
hmonitor: HMonitor,
/// The system name of the monitor.
monitor_name: String,
/// True if this is the primary monitor.
primary: bool,
/// The position of the monitor in pixels on the desktop.
///
/// A window that is positioned at these coordinates will overlap the monitor.
position: (i32, i32),
/// The current resolution in pixels on the monitor.
dimensions: (u32, u32),
/// DPI scaling factor.
hidpi_factor: f32,
/// DPI scale factor.
hidpi_factor: f64,
}
// Send is not implemented for HMONITOR, we have to wrap it and implement it manually.
// For more info see:
// https://github.com/retep998/winapi-rs/issues/360
// https://github.com/retep998/winapi-rs/issues/396
#[derive(Clone)]
#[derive(Debug, Clone)]
struct HMonitor(HMONITOR);
unsafe impl Send for HMonitor {}
unsafe extern "system" fn monitor_enum_proc(hmonitor: HMONITOR, _: HDC, place: LPRECT, data: LPARAM) -> BOOL {
unsafe extern "system" fn monitor_enum_proc(
hmonitor: HMONITOR,
_hdc: HDC,
_place: LPRECT,
data: LPARAM,
) -> BOOL {
let monitors = data as *mut VecDeque<MonitorId>;
(*monitors).push_back(MonitorId::from_hmonitor(hmonitor));
TRUE // continue enumeration
}
let place = *place;
let position = (place.left as i32, place.top as i32);
let dimensions = ((place.right - place.left) as u32, (place.bottom - place.top) as u32);
let mut monitor_info: winuser::MONITORINFOEXW = mem::zeroed();
monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
if winuser::GetMonitorInfoW(hmonitor, &mut monitor_info as *mut winuser::MONITORINFOEXW as *mut winuser::MONITORINFO) == 0 {
// Some error occurred, just skip this monitor and go on.
return TRUE;
pub fn get_available_monitors() -> VecDeque<MonitorId> {
let mut monitors: VecDeque<MonitorId> = VecDeque::new();
unsafe {
winuser::EnumDisplayMonitors(
ptr::null_mut(),
ptr::null_mut(),
Some(monitor_enum_proc),
&mut monitors as *mut _ as LPARAM,
);
}
monitors
}
(*monitors).push_back(MonitorId {
adapter_name: monitor_info.szDevice,
hmonitor: HMonitor(hmonitor),
monitor_name: util::wchar_to_string(&monitor_info.szDevice),
primary: monitor_info.dwFlags & winuser::MONITORINFOF_PRIMARY != 0,
position,
dimensions,
hidpi_factor: 1.0,
});
// TRUE means continue enumeration.
TRUE
pub fn get_primary_monitor() -> MonitorId {
const ORIGIN: POINT = POINT { x: 0, y: 0 };
let hmonitor = unsafe {
winuser::MonitorFromPoint(ORIGIN, winuser::MONITOR_DEFAULTTOPRIMARY)
};
MonitorId::from_hmonitor(hmonitor)
}
impl EventsLoop {
// TODO: Investigate opportunities for caching
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
unsafe {
let mut result: VecDeque<MonitorId> = VecDeque::new();
winuser::EnumDisplayMonitors(ptr::null_mut(), ptr::null_mut(), Some(monitor_enum_proc), &mut result as *mut _ as LPARAM);
result
}
get_available_monitors()
}
pub fn get_current_monitor(handle: HWND) -> MonitorId {
unsafe {
let mut monitor_info: winuser::MONITORINFOEXW = mem::zeroed();
monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
let hmonitor = winuser::MonitorFromWindow(handle, winuser::MONITOR_DEFAULTTONEAREST);
winuser::GetMonitorInfoW(
hmonitor,
&mut monitor_info as *mut winuser::MONITORINFOEXW as *mut winuser::MONITORINFO,
);
let place = monitor_info.rcMonitor;
let position = (place.left as i32, place.top as i32);
let dimensions = (
(place.right - place.left) as u32,
(place.bottom - place.top) as u32,
);
MonitorId {
adapter_name: monitor_info.szDevice,
hmonitor: super::monitor::HMonitor(hmonitor),
monitor_name: util::wchar_to_string(&monitor_info.szDevice),
primary: monitor_info.dwFlags & winuser::MONITORINFOF_PRIMARY != 0,
position,
dimensions,
hidpi_factor: 1.0,
}
}
pub fn get_current_monitor(hwnd: HWND) -> MonitorId {
let hmonitor = unsafe {
winuser::MonitorFromWindow(hwnd, winuser::MONITOR_DEFAULTTONEAREST)
};
MonitorId::from_hmonitor(hmonitor)
}
pub fn get_primary_monitor(&self) -> MonitorId {
// we simply get all available monitors and return the one with the `MONITORINFOF_PRIMARY` flag
// TODO: it is possible to query the win32 API for the primary monitor, this should be done
// instead
for monitor in self.get_available_monitors().into_iter() {
if monitor.primary {
return monitor;
}
}
get_primary_monitor()
}
}
panic!("Failed to find the primary monitor")
impl Window {
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
get_available_monitors()
}
pub fn get_primary_monitor(&self) -> MonitorId {
get_primary_monitor()
}
}
fn get_monitor_info(hmonitor: HMONITOR) -> Result<winuser::MONITORINFOEXW, util::WinError> {
let mut monitor_info: winuser::MONITORINFOEXW = unsafe { mem::uninitialized() };
monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
let status = unsafe {
winuser::GetMonitorInfoW(
hmonitor,
&mut monitor_info as *mut winuser::MONITORINFOEXW as *mut winuser::MONITORINFO,
)
};
if status == 0 {
Err(util::WinError::from_last_error())
} else {
Ok(monitor_info)
}
}
impl MonitorId {
/// See the docs if the crate root file.
pub(crate) fn from_hmonitor(hmonitor: HMONITOR) -> Self {
let monitor_info = get_monitor_info(hmonitor).expect("`GetMonitorInfoW` failed");
let place = monitor_info.rcMonitor;
let dimensions = (
(place.right - place.left) as u32,
(place.bottom - place.top) as u32,
);
MonitorId {
hmonitor: HMonitor(hmonitor),
monitor_name: util::wchar_ptr_to_string(monitor_info.szDevice.as_ptr()),
primary: util::has_flag(monitor_info.dwFlags, winuser::MONITORINFOF_PRIMARY),
position: (place.left as i32, place.top as i32),
dimensions,
hidpi_factor: dpi_to_scale_factor(get_monitor_dpi(hmonitor).unwrap_or(96)),
}
}
pub(crate) fn contains_point(&self, point: &POINT) -> bool {
let left = self.position.0 as LONG;
let right = left + self.dimensions.0 as LONG;
let top = self.position.1 as LONG;
let bottom = top + self.dimensions.1 as LONG;
point.x >= left && point.x <= right && point.y >= top && point.y <= bottom
}
#[inline]
pub fn get_name(&self) -> Option<String> {
Some(self.monitor_name.clone())
}
/// See the docs of the crate root file.
#[inline]
pub fn get_native_identifier(&self) -> String {
self.monitor_name.clone()
}
/// See the docs of the crate root file.
#[inline]
pub fn get_hmonitor(&self) -> HMONITOR {
self.hmonitor.0
}
/// See the docs of the crate root file.
#[inline]
pub fn get_dimensions(&self) -> (u32, u32) {
// TODO: retrieve the dimensions every time this is called
self.dimensions
}
/// This is a Win32-only function for `MonitorId` that returns the system name of the adapter
/// device.
#[inline]
pub fn get_adapter_name(&self) -> &[wchar_t] {
&self.adapter_name
}
/// A window that is positioned at these coordinates will overlap the monitor.
#[inline]
pub fn get_position(&self) -> (i32, i32) {
self.position
pub fn get_dimensions(&self) -> PhysicalSize {
self.dimensions.into()
}
#[inline]
pub fn get_hidpi_factor(&self) -> f32 {
pub fn get_position(&self) -> PhysicalPosition {
self.position.into()
}
#[inline]
pub fn get_hidpi_factor(&self) -> f64 {
self.hidpi_factor
}
}

View File

@@ -1,8 +1,9 @@
use std::{self, mem, ptr};
use std::{self, mem, ptr, slice};
use std::ops::BitAnd;
use winapi::ctypes::wchar_t;
use winapi::shared::minwindef::DWORD;
use winapi::shared::minwindef::{BOOL, DWORD};
use winapi::shared::windef::{HWND, POINT, RECT};
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::winbase::{
FormatMessageW,
@@ -18,6 +19,7 @@ use winapi::um::winnt::{
LANG_NEUTRAL,
SUBLANG_DEFAULT,
};
use winapi::um::winuser;
pub fn has_flag<T>(bitset: T, flag: T) -> bool
where T:
@@ -27,9 +29,39 @@ where T:
}
pub fn wchar_to_string(wchar: &[wchar_t]) -> String {
String::from_utf16_lossy(wchar)
.trim_right_matches(0 as char)
.to_string()
String::from_utf16_lossy(wchar).to_string()
}
pub fn wchar_ptr_to_string(wchar: *const wchar_t) -> String {
let len = unsafe { lstrlenW(wchar) } as usize;
let wchar_slice = unsafe { slice::from_raw_parts(wchar, len) };
wchar_to_string(wchar_slice)
}
pub unsafe fn status_map<T, F: FnMut(&mut T) -> BOOL>(mut fun: F) -> Option<T> {
let mut data: T = mem::uninitialized();
if fun(&mut data) != 0 {
Some(data)
} else {
None
}
}
pub fn get_cursor_pos() -> Option<POINT> {
unsafe { status_map(|cursor_pos| winuser::GetCursorPos(cursor_pos)) }
}
pub fn get_window_rect(hwnd: HWND) -> Option<RECT> {
unsafe { status_map(|rect| winuser::GetWindowRect(hwnd, rect)) }
}
// This won't be needed anymore if we just add a derive to winapi.
pub fn rect_eq(a: &RECT, b: &RECT) -> bool {
let left_eq = a.left == b.left;
let right_eq = a.right == b.right;
let top_eq = a.top == b.top;
let bottom_eq = a.bottom == b.bottom;
left_eq && right_eq && top_eq && bottom_eq
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,19 @@
use std::collections::vec_deque::IntoIter as VecDequeIter;
use CreationError;
use CursorState;
use EventsLoop;
use Icon;
use MouseCursor;
use Window;
use WindowBuilder;
use WindowId;
use libc;
use platform;
use {
CreationError,
EventsLoop,
Icon,
LogicalPosition,
LogicalSize,
MouseCursor,
PhysicalPosition,
PhysicalSize,
platform,
Window,
WindowBuilder,
WindowId,
};
impl WindowBuilder {
/// Initializes a new `WindowBuilder` with default values.
@@ -23,29 +26,39 @@ impl WindowBuilder {
}
/// Requests the window to be of specific dimensions.
///
/// Width and height are in pixels.
#[inline]
pub fn with_dimensions(mut self, width: u32, height: u32) -> WindowBuilder {
self.window.dimensions = Some((width, height));
pub fn with_dimensions(mut self, size: LogicalSize) -> WindowBuilder {
self.window.dimensions = Some(size);
self
}
/// Sets a minimum dimension size for the window
///
/// Width and height are in pixels.
#[inline]
pub fn with_min_dimensions(mut self, width: u32, height: u32) -> WindowBuilder {
self.window.min_dimensions = Some((width, height));
pub fn with_min_dimensions(mut self, min_size: LogicalSize) -> WindowBuilder {
self.window.min_dimensions = Some(min_size);
self
}
/// Sets a maximum dimension size for the window
///
/// Width and height are in pixels.
#[inline]
pub fn with_max_dimensions(mut self, width: u32, height: u32) -> WindowBuilder {
self.window.max_dimensions = Some((width, height));
pub fn with_max_dimensions(mut self, max_size: LogicalSize) -> WindowBuilder {
self.window.max_dimensions = Some(max_size);
self
}
/// Sets whether the window is resizable or not
///
/// Note that making the window unresizable doesn't exempt you from handling `Resized`, as that event can still be
/// triggered by DPI scaling, entering fullscreen mode, etc.
///
/// ## Platform-specific
///
/// This only has an effect on desktop platforms.
///
/// Due to a bug in XFCE, this has no effect on Xfwm.
#[inline]
pub fn with_resizable(mut self, resizable: bool) -> WindowBuilder {
self.window.resizable = resizable;
self
}
@@ -92,6 +105,13 @@ impl WindowBuilder {
self
}
/// Sets whether or not the window will always be on top of other windows.
#[inline]
pub fn with_always_on_top(mut self, always_on_top: bool) -> WindowBuilder {
self.window.always_on_top = always_on_top;
self
}
/// Sets the window icon. On Windows and X11, this is typically the small icon in the top-left
/// corner of the titlebar.
///
@@ -121,14 +141,15 @@ impl WindowBuilder {
///
/// Error should be very rare and only occur in case of permission denied, incompatible system,
/// out of memory, etc.
#[inline]
pub fn build(mut self, events_loop: &EventsLoop) -> Result<Window, CreationError> {
self.window.dimensions = Some(self.window.dimensions.unwrap_or_else(|| {
if let Some(ref monitor) = self.window.fullscreen {
// resizing the window to the dimensions of the monitor when fullscreen
monitor.get_dimensions()
LogicalSize::from_physical(monitor.get_dimensions(), 1.0)
} else {
// default dimensions
(1024, 768)
(1024, 768).into()
}
}));
@@ -196,7 +217,7 @@ impl Window {
///
/// Returns `None` if the window no longer exists.
#[inline]
pub fn get_position(&self) -> Option<(i32, i32)> {
pub fn get_position(&self) -> Option<LogicalPosition> {
self.window.get_position()
}
@@ -205,7 +226,7 @@ impl Window {
///
/// The same conditions that apply to `get_position` apply to this method.
#[inline]
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
self.window.get_inner_position()
}
@@ -215,61 +236,30 @@ impl Window {
///
/// This is a no-op if the window has already been closed.
#[inline]
pub fn set_position(&self, x: i32, y: i32) {
self.window.set_position(x, y)
pub fn set_position(&self, position: LogicalPosition) {
self.window.set_position(position)
}
/// Returns the size in pixels of the client area of the window.
/// Returns the logical size of the window's client area.
///
/// The client area is the content of the window, excluding the title bar and borders.
/// These are the dimensions that need to be supplied to `glViewport`.
///
/// Converting the returned `LogicalSize` to `PhysicalSize` produces the size your framebuffer should be.
///
/// Returns `None` if the window no longer exists.
#[inline]
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
pub fn get_inner_size(&self) -> Option<LogicalSize> {
self.window.get_inner_size()
}
/// Returns the size in points of the client area of the window.
/// Returns the logical size of the entire window.
///
/// The client area is the content of the window, excluding the title bar and borders.
/// To get the dimensions of the frame buffer when calling `glViewport`, multiply with hidpi factor.
///
/// Returns `None` if the window no longer exists.
///
/// DEPRECATED
#[inline]
#[deprecated]
pub fn get_inner_size_points(&self) -> Option<(u32, u32)> {
self.window.get_inner_size().map(|(x, y)| {
let hidpi = self.hidpi_factor();
((x as f32 / hidpi) as u32, (y as f32 / hidpi) as u32)
})
}
/// Returns the size in pixels of the client area of the window.
///
/// The client area is the content of the window, excluding the title bar and borders.
/// These are the dimensions of the frame buffer, and the dimensions that you should use
/// when you call `glViewport`.
///
/// Returns `None` if the window no longer exists.
///
/// DEPRECATED
#[inline]
#[deprecated]
pub fn get_inner_size_pixels(&self) -> Option<(u32, u32)> {
self.window.get_inner_size()
}
/// Returns the size in pixels of the window.
///
/// These dimensions include title bar and borders. If you don't want these, you should use
/// use `get_inner_size` instead.
/// These dimensions include the title bar and borders. If you don't want that (and you usually don't),
/// use `get_inner_size` instead.
///
/// Returns `None` if the window no longer exists.
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
pub fn get_outer_size(&self) -> Option<LogicalSize> {
self.window.get_outer_size()
}
@@ -279,70 +269,88 @@ impl Window {
///
/// This is a no-op if the window has already been closed.
#[inline]
pub fn set_inner_size(&self, x: u32, y: u32) {
self.window.set_inner_size(x, y)
pub fn set_inner_size(&self, size: LogicalSize) {
self.window.set_inner_size(size)
}
/// Sets a minimum dimension size for the window.
///
/// Width and height are in pixels.
#[inline]
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
pub fn set_min_dimensions(&self, dimensions: Option<LogicalSize>) {
self.window.set_min_dimensions(dimensions)
}
/// Sets a maximum dimension size for the window.
///
/// Width and height are in pixels.
#[inline]
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
pub fn set_max_dimensions(&self, dimensions: Option<LogicalSize>) {
self.window.set_max_dimensions(dimensions)
}
/// DEPRECATED. Gets the native platform specific display for this window.
/// This is typically only required when integrating with
/// other libraries that need this information.
#[deprecated]
/// Sets whether the window is resizable or not.
///
/// Note that making the window unresizable doesn't exempt you from handling `Resized`, as that event can still be
/// triggered by DPI scaling, entering fullscreen mode, etc.
///
/// ## Platform-specific
///
/// This only has an effect on desktop platforms.
///
/// Due to a bug in XFCE, this has no effect on Xfwm.
#[inline]
pub unsafe fn platform_display(&self) -> *mut libc::c_void {
self.window.platform_display()
pub fn set_resizable(&self, resizable: bool) {
self.window.set_resizable(resizable)
}
/// DEPRECATED. Gets the native platform specific window handle. This is
/// typically only required when integrating with other libraries
/// that need this information.
#[deprecated]
/// Returns the DPI factor that can be used to map logical pixels to physical pixels, and vice versa.
///
/// See the [`dpi`](dpi/index.html) module for more information.
///
/// ## Platform-specific
///
/// - **X11:** Can be overridden using the `WINIT_HIDPI_FACTOR` environment variable.
/// - **Android:** Always returns 1.0.
#[inline]
pub unsafe fn platform_window(&self) -> *mut libc::c_void {
self.window.platform_window()
pub fn get_hidpi_factor(&self) -> f64 {
self.window.get_hidpi_factor()
}
/// Modifies the mouse cursor of the window.
/// Has no effect on Android.
#[inline]
pub fn set_cursor(&self, cursor: MouseCursor) {
self.window.set_cursor(cursor);
}
/// Returns the ratio between the backing framebuffer resolution and the
/// window size in screen pixels. This is typically one for a normal display
/// and two for a retina display.
#[inline]
pub fn hidpi_factor(&self) -> f32 {
self.window.hidpi_factor()
}
/// Changes the position of the cursor in window coordinates.
#[inline]
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
self.window.set_cursor_position(x, y)
pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), String> {
self.window.set_cursor_position(position)
}
/// Sets how winit handles the cursor. See the documentation of `CursorState` for details.
/// Grabs the cursor, preventing it from leaving the window.
///
/// Has no effect on Android.
/// ## Platform-specific
///
/// On macOS, this presently merely locks the cursor in a fixed location, which looks visually awkward.
///
/// This has no effect on Android or iOS.
#[inline]
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
self.window.set_cursor_state(state)
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
self.window.grab_cursor(grab)
}
/// Hides the cursor, making it invisible but still usable.
///
/// ## Platform-specific
///
/// On Windows and X11, the cursor is only hidden within the confines of the window.
///
/// On macOS, the cursor is hidden as long as the window has input focus, even if the cursor is outside of the
/// window.
///
/// This has no effect on Android or iOS.
#[inline]
pub fn hide_cursor(&self, hide: bool) {
self.window.hide_cursor(hide)
}
/// Sets the window to maximized or back
@@ -363,6 +371,12 @@ impl Window {
self.window.set_decorations(decorations)
}
/// Change whether or not the window will always be on top of other windows.
#[inline]
pub fn set_always_on_top(&self, always_on_top: bool) {
self.window.set_always_on_top(always_on_top)
}
/// Sets the window icon. On Windows and X11, this is typically the small icon in the top-left
/// corner of the titlebar.
///
@@ -376,11 +390,35 @@ impl Window {
self.window.set_window_icon(window_icon)
}
/// Sets location of IME candidate box in client area coordinates relative to the top left.
#[inline]
pub fn set_ime_spot(&self, position: LogicalPosition) {
self.window.set_ime_spot(position)
}
/// Returns the monitor on which the window currently resides
#[inline]
pub fn get_current_monitor(&self) -> MonitorId {
self.window.get_current_monitor()
}
/// Returns the list of all the monitors available on the system.
///
/// This is the same as `EventsLoop::get_available_monitors`, and is provided for convenience.
#[inline]
pub fn get_available_monitors(&self) -> AvailableMonitorsIter {
let data = self.window.get_available_monitors();
AvailableMonitorsIter { data: data.into_iter() }
}
/// Returns the primary monitor of the system.
///
/// This is the same as `EventsLoop::get_primary_monitor`, and is provided for convenience.
#[inline]
pub fn get_primary_monitor(&self) -> MonitorId {
MonitorId { inner: self.window.get_primary_monitor() }
}
#[inline]
pub fn id(&self) -> WindowId {
WindowId(self.window.id())
@@ -390,6 +428,7 @@ impl Window {
/// An iterator for the list of available monitors.
// Implementation note: we retrieve the list once, then serve each element by one by one.
// This may change in the future.
#[derive(Debug)]
pub struct AvailableMonitorsIter {
pub(crate) data: VecDequeIter<platform::MonitorId>,
}
@@ -409,7 +448,7 @@ impl Iterator for AvailableMonitorsIter {
}
/// Identifier for a monitor.
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct MonitorId {
pub(crate) inner: platform::MonitorId
}
@@ -423,22 +462,29 @@ impl MonitorId {
self.inner.get_name()
}
/// Returns the number of pixels currently displayed on the monitor.
/// Returns the monitor's resolution.
#[inline]
pub fn get_dimensions(&self) -> (u32, u32) {
pub fn get_dimensions(&self) -> PhysicalSize {
self.inner.get_dimensions()
}
/// Returns the top-left corner position of the monitor relative to the larger full
/// screen area.
#[inline]
pub fn get_position(&self) -> (i32, i32) {
pub fn get_position(&self) -> PhysicalPosition {
self.inner.get_position()
}
/// Returns the ratio between the monitor's physical pixels and logical pixels.
/// Returns the DPI factor that can be used to map logical pixels to physical pixels, and vice versa.
///
/// See the [`dpi`](dpi/index.html) module for more information.
///
/// ## Platform-specific
///
/// - **X11:** Can be overridden using the `WINIT_HIDPI_FACTOR` environment variable.
/// - **Android:** Always returns 1.0.
#[inline]
pub fn get_hidpi_factor(&self) -> f32 {
pub fn get_hidpi_factor(&self) -> f64 {
self.inner.get_hidpi_factor()
}
}