Compare commits

..

2 Commits

Author SHA1 Message Date
Pierre Krieger
444ebc3018 Publish 0.6.4 2017-05-07 20:36:22 +02:00
Jon Gjengset
f8efe87c34 Set WM_CLASS and WM_NAME before mapping window
ICCCM 4.1.2.5 (https://tronche.com/gui/x/icccm/sec-4.html#WM_CLASS)
states that:

> This property must be present when the window leaves the Withdrawn
> state and may be changed only while the window is in the Withdrawn
> state.

Previously, we would first map the window, and then set these
properties, causing sadness for window managers (#167,
tomaka/glutin#879). This patch changes that by setting the class and
name attributes immediately after the window is created, and before it
is mapped.

Fixes #167.
2017-05-07 20:34:50 +02:00
202 changed files with 9639 additions and 39183 deletions

2
.gitattributes vendored
View File

@@ -20,5 +20,3 @@
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
/CHANGELOG.md merge=union

View File

@@ -1,8 +0,0 @@
- [ ] Tested on all platforms changed
- [ ] Compilation warnings were addressed
- [ ] `cargo fmt` has been run on this branch
- [ ] `cargo doc` builds successfully
- [ ] 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 or updated an example program if it would help users understand this functionality
- [ ] Updated [feature matrix](https://github.com/rust-windowing/winit/blob/master/FEATURES.md), if new features were added or implemented

View File

@@ -1,114 +0,0 @@
name: CI
on:
pull_request:
paths:
- '**.rs'
- '**.toml'
- '.github/workflows/ci.yml'
push:
branches: [master]
paths:
- '**.rs'
- '**.toml'
- '.github/workflows/ci.yml'
jobs:
Check_Formatting:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: hecrj/setup-rust-action@v1
with:
rust-version: stable
components: rustfmt
- name: Check Formatting
run: cargo +stable fmt --all -- --check
Tests:
strategy:
fail-fast: false
matrix:
rust_version: [stable, nightly]
platform:
- { target: x86_64-pc-windows-msvc, os: windows-latest, }
- { target: i686-pc-windows-msvc, os: windows-latest, }
- { target: x86_64-pc-windows-gnu, os: windows-latest, host: -x86_64-pc-windows-gnu }
- { target: i686-pc-windows-gnu, os: windows-latest, host: -i686-pc-windows-gnu }
- { target: i686-unknown-linux-gnu, os: ubuntu-latest, }
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, }
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: x11 }
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: "wayland,wayland-dlopen" }
- { target: aarch64-linux-android, os: ubuntu-latest, cmd: 'apk --' }
- { target: x86_64-apple-darwin, os: macos-latest, }
- { target: x86_64-apple-ios, os: macos-latest, }
- { target: aarch64-apple-ios, os: macos-latest, }
# We're using Windows rather than Ubuntu to run the wasm tests because caching cargo-web
# doesn't currently work on Linux.
- { target: wasm32-unknown-unknown, os: windows-latest, }
env:
RUST_BACKTRACE: 1
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-C debuginfo=0"
OPTIONS: ${{ matrix.platform.options }}
FEATURES: ${{ format(',{0}', matrix.platform.features ) }}
CMD: ${{ matrix.platform.cmd }}
runs-on: ${{ matrix.platform.os }}
steps:
- uses: actions/checkout@v2
# Used to cache cargo-web
- name: Cache cargo folder
uses: actions/cache@v1
with:
path: ~/.cargo
key: ${{ matrix.platform.target }}-cargo-${{ matrix.rust_version }}
- uses: hecrj/setup-rust-action@v1
with:
rust-version: ${{ matrix.rust_version }}${{ matrix.platform.host }}
targets: ${{ matrix.platform.target }}
- name: Install GCC Multilib
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
run: sudo apt-get update && sudo apt-get install gcc-multilib
- name: Install cargo-apk
if: contains(matrix.platform.target, 'android')
run: cargo install cargo-apk
- name: Check documentation
shell: bash
if: matrix.platform.target != 'wasm32-unknown-unknown'
run: cargo $CMD doc --no-deps --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
- name: Build
shell: bash
run: cargo $CMD build --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
- name: Build tests
shell: bash
run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
- name: Run tests
shell: bash
if: (
!contains(matrix.platform.target, 'android') &&
!contains(matrix.platform.target, 'ios') &&
!contains(matrix.platform.target, 'wasm32'))
run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
- name: Build with serde enabled
shell: bash
run: cargo $CMD build --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES
- name: Build tests with serde enabled
shell: bash
run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES
- name: Run tests with serde enabled
shell: bash
if: (
!contains(matrix.platform.target, 'android') &&
!contains(matrix.platform.target, 'ios') &&
!contains(matrix.platform.target, 'wasm32'))
run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES

View File

@@ -1,18 +0,0 @@
name: Publish
on:
push:
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
jobs:
Publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: hecrj/setup-rust-action@v1
with:
rust-version: stable
components: rustfmt
- name: Publish to crates.io
run: cargo publish --token ${{ secrets.cratesio_token }}

6
.gitignore vendored
View File

@@ -1,10 +1,4 @@
Cargo.lock
target/
rls/
.vscode/
*~
*.wasm
*.ts
*.js
#*#
.DS_Store

2
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "deps/apk-builder"]
path = deps/apk-builder
url = https://github.com/rust-windowing/android-rs-glue
url = https://github.com/tomaka/android-rs-glue

33
.travis.yml Normal file
View File

@@ -0,0 +1,33 @@
language: rust
rust:
- nightly
- stable
cache: cargo
addons:
apt:
packages:
- libxxf86vm-dev
install:
- |
if [ $TRAVIS_OS_NAME = osx ]; then
rustup target add x86_64-apple-ios
fi
script:
- cargo build --verbose
- if [ $TRAVIS_OS_NAME = osx ]; then cargo build --target x86_64-apple-ios --verbose; fi
- cargo test --verbose
os:
- linux
- osx
after_success:
- |
[ $TRAVIS_BRANCH = master ] &&
[ $TRAVIS_PULL_REQUEST = false ] &&
cargo publish --token ${CRATESIO_TOKEN}

View File

@@ -1,743 +0,0 @@
# 0.26.0 (2021-12-01)
- Update `raw-window-handle` to `v0.4`. This is _not_ a breaking change, we still implement `HasRawWindowHandle` from `v0.3`, see [rust-windowing/raw-window-handle#74](https://github.com/rust-windowing/raw-window-handle/pull/74).
- On X11, bump `mio` to 0.8.
- On Android, fixed `WindowExtAndroid::config` initially returning an empty `Configuration`.
- On Android, fixed `Window::scale_factor` and `MonitorHandle::scale_factor` initially always returning 1.0.
- On X11, select an appropriate visual for transparency if is requested
- On Wayland and X11, fix diagonal window resize cursor orientation.
- On macOS, drop the event callback before exiting.
- On Android, implement `Window::request_redraw`
- **Breaking:** On Web, remove the `stdweb` backend.
- Added `Window::focus_window`to bring the window to the front and set input focus.
- On Wayland and X11, implement `is_maximized` method on `Window`.
- On Windows, prevent ghost window from showing up in the taskbar after either several hours of use or restarting `explorer.exe`.
- On macOS, fix issue where `ReceivedCharacter` was not being emitted during some key repeat events.
- On Wayland, load cursor icons `hand2` and `hand1` for `CursorIcon::Hand`.
- **Breaking:** On Wayland, Theme trait and its support types are dropped.
- On Wayland, bump `smithay-client-toolkit` to 0.15.1.
- On Wayland, implement `request_user_attention` with `xdg_activation_v1`.
- On X11, emit missing `WindowEvent::ScaleFactorChanged` when the only monitor gets reconnected.
- On X11, if RANDR based scale factor is higher than 20 reset it to 1
- On Wayland, add an enabled-by-default feature called `wayland-dlopen` so users can opt out of using `dlopen` to load system libraries.
- **Breaking:** On Android, bump `ndk` and `ndk-glue` to 0.5.
- On Windows, increase wait timer resolution for more accurate timing when using `WaitUntil`.
- On macOS, fix native file dialogs hanging the event loop.
- On Wayland, implement a workaround for wrong configure size when using `xdg_decoration` in `kwin_wayland`
- On macOS, fix an issue that prevented the menu bar from showing in borderless fullscreen mode.
- On X11, EINTR while polling for events no longer causes a panic. Instead it will be treated as a spurious wakeup.
# 0.25.0 (2021-05-15)
- **Breaking:** On macOS, replace `WindowBuilderExtMacOS::with_activation_policy` with `EventLoopExtMacOS::set_activation_policy`
- On macOS, wait with activating the application until the application has initialized.
- On macOS, fix creating new windows when the application has a main menu.
- On Windows, fix fractional deltas for mouse wheel device events.
- On macOS, fix segmentation fault after dropping the main window.
- On Android, `InputEvent::KeyEvent` is partially implemented providing the key scancode.
- Added `is_maximized` method to `Window`.
- On Windows, fix bug where clicking the decoration bar would make the cursor blink.
- On Windows, fix bug causing newly created windows to erroneously display the "wait" (spinning) cursor.
- On macOS, wake up the event loop immediately when a redraw is requested.
- On Windows, change the default window size (1024x768) to match the default on other desktop platforms (800x600).
- On Windows, fix bug causing mouse capture to not be released.
- On Windows, fix fullscreen not preserving minimized/maximized state.
- On Android, unimplemented events are marked as unhandled on the native event loop.
- On Windows, added `WindowBuilderExtWindows::with_menu` to set a custom menu at window creation time.
- On Android, bump `ndk` and `ndk-glue` to 0.3: use predefined constants for event `ident`.
- On macOS, fix objects captured by the event loop closure not being dropped on panic.
- On Windows, fixed `WindowEvent::ThemeChanged` not properly firing and fixed `Window::theme` returning the wrong theme.
- On Web, added support for `DeviceEvent::MouseMotion` to listen for relative mouse movements.
- Added `WindowBuilder::with_position` to allow setting the position of a `Window` on creation. Supported on Windows, macOS and X11.
- Added `Window::drag_window`. Implemented on Windows, macOS, X11 and Wayland.
- On X11, bump `mio` to 0.7.
- On Windows, added `WindowBuilderExtWindows::with_owner_window` to allow creating popup windows.
- On Windows, added `WindowExtWindows::set_enable` to allow creating modal popup windows.
- On macOS, emit `RedrawRequested` events immediately while the window is being resized.
- Implement `Default`, `Hash`, and `Eq` for `LogicalPosition`, `PhysicalPosition`, `LogicalSize`, and `PhysicalSize`.
- On macOS, initialize the Menu Bar with minimal defaults. (Can be prevented using `enable_default_menu_creation`)
- On macOS, change the default behavior for first click when the window was unfocused. Now the window becomes focused and then emits a `MouseInput` event on a "first mouse click".
- Implement mint (math interoperability standard types) conversions (under feature flag `mint`).
# 0.24.0 (2020-12-09)
- On Windows, fix applications not exiting gracefully due to thread_event_target_callback accessing corrupted memory.
- On Windows, implement `Window::set_ime_position`.
- **Breaking:** On Windows, Renamed `WindowBuilderExtWindows`'s `is_dark_mode` to `theme`.
- **Breaking:** On Windows, renamed `WindowBuilderExtWindows::is_dark_mode` to `theme`.
- On Windows, add `WindowBuilderExtWindows::with_theme` to set a preferred theme.
- On Windows, fix bug causing message boxes to appear delayed.
- On Android, calling `WindowEvent::Focused` now works properly instead of always returning false.
- On Windows, fix Alt-Tab behaviour by removing borderless fullscreen "always on top" flag.
- On Windows, fix bug preventing windows with transparency enabled from having fully-opaque regions.
- **Breaking:** On Windows, include prefix byte in scancodes.
- On Wayland, fix window not being resizeable when using `WindowBuilder::with_min_inner_size`.
- On Unix, fix cross-compiling to wasm32 without enabling X11 or Wayland.
- On Windows, fix use-after-free crash during window destruction.
- On Web, fix `WindowEvent::ReceivedCharacter` never being sent on key input.
- On macOS, fix compilation when targeting aarch64.
- On X11, fix `Window::request_redraw` not waking the event loop.
- On Wayland, the keypad arrow keys are now recognized.
- **Breaking** Rename `desktop::EventLoopExtDesktop` to `run_return::EventLoopExtRunReturn`.
- Added `request_user_attention` method to `Window`.
- **Breaking:** On macOS, removed `WindowExt::request_user_attention`, use `Window::request_user_attention`.
- **Breaking:** On X11, removed `WindowExt::set_urgent`, use `Window::request_user_attention`.
- On Wayland, default font size in CSD increased from 11 to 17.
- On Windows, fix bug causing message boxes to appear delayed.
- On Android, support multi-touch.
- On Wayland, extra mouse buttons are not dropped anymore.
- **Breaking**: `MouseButton::Other` now uses `u16`.
# 0.23.0 (2020-10-02)
- On iOS, fixed support for the "Debug View Heirarchy" feature in Xcode.
- On all platforms, `available_monitors` and `primary_monitor` are now on `EventLoopWindowTarget` rather than `EventLoop` to list monitors event in the event loop.
- On Unix, X11 and Wayland are now optional features (enabled by default)
- On X11, fix deadlock when calling `set_fullscreen_inner`.
- On Web, prevent the webpage from scrolling when the user is focused on a winit canvas
- On Web, calling `window.set_cursor_icon` no longer breaks HiDPI scaling
- On Windows, drag and drop is now optional (enabled by default) and can be disabled with `WindowBuilderExtWindows::with_drag_and_drop(false)`.
- On Wayland, fix deadlock when calling to `set_inner_size` from a callback.
- On macOS, add `hide__other_applications` to `EventLoopWindowTarget` via existing `EventLoopWindowTargetExtMacOS` trait. `hide_other_applications` will hide other applications by calling `-[NSApplication hideOtherApplications: nil]`.
- On android added support for `run_return`.
- On MacOS, Fixed fullscreen and dialog support for `run_return`.
- On Windows, fix bug where we'd try to emit `MainEventsCleared` events during nested win32 event loops.
- On Web, use mouse events if pointer events aren't supported. This affects Safari.
- On Windows, `set_ime_position` is now a no-op instead of a runtime crash.
- On Android, `set_fullscreen` is now a no-op instead of a runtime crash.
- On iOS and Android, `set_inner_size` is now a no-op instead of a runtime crash.
- On Android, fix `ControlFlow::Poll` not polling the Android event queue.
- On macOS, add `NSWindow.hasShadow` support.
- On Web, fix vertical mouse wheel scrolling being inverted.
- On Web, implement mouse capturing for click-dragging out of the canvas.
- On Web, fix `ControlFlow::Exit` not properly handled.
- On Web (web-sys only), send `WindowEvent::ScaleFactorChanged` event when `window.devicePixelRatio` is changed.
- **Breaking:** On Web, `set_cursor_position` and `set_cursor_grab` will now always return an error.
- **Breaking:** `PixelDelta` scroll events now return a `PhysicalPosition`.
- On NetBSD, fixed crash due to incorrect detection of the main thread.
- **Breaking:** On X11, `-` key is mapped to the `Minus` virtual key code, instead of `Subtract`.
- On macOS, fix inverted horizontal scroll.
- **Breaking:** `current_monitor` now returns `Option<MonitorHandle>`.
- **Breaking:** `primary_monitor` now returns `Option<MonitorHandle>`.
- On macOS, updated core-* dependencies and cocoa.
- Bump `parking_lot` to 0.11
- On Android, bump `ndk`, `ndk-sys` and `ndk-glue` to 0.2. Checkout the new ndk-glue main proc attribute.
- On iOS, fixed starting the app in landscape where the view still had portrait dimensions.
- Deprecate the stdweb backend, to be removed in a future release
- **Breaking:** Prefixed virtual key codes `Add`, `Multiply`, `Divide`, `Decimal`, and `Subtract` with `Numpad`.
- Added `Asterisk` and `Plus` virtual key codes.
- On Web (web-sys only), the `Event::LoopDestroyed` event is correctly emitted when leaving the page.
- On Web, the `WindowEvent::Destroyed` event now gets emitted when a `Window` is dropped.
- On Web (web-sys only), the event listeners are now removed when a `Window` is dropped or when the event loop is destroyed.
- On Web, the event handler closure passed to `EventLoop::run` now gets dropped after the event loop is destroyed.
- **Breaking:** On Web, the canvas element associated to a `Window` is no longer removed from the DOM when the `Window` is dropped.
- On Web, `WindowEvent::Resized` is now emitted when `Window::set_inner_size` is called.
- **Breaking:** `Fullscreen` enum now uses `Borderless(Option<MonitorHandle>)` instead of `Borderless(MonitorHandle)` to allow picking the current monitor.
- On MacOS, fix `WindowEvent::Moved` ignoring the scale factor.
- On Wayland, add missing virtual keycodes.
- On Wayland, implement proper `set_cursor_grab`.
- On Wayland, the cursor will use similar icons if the requested one isn't available.
- On Wayland, right clicking on client side decorations will request application menu.
- On Wayland, fix tracking of window size after state changes.
- On Wayland, fix client side decorations not being hidden properly in fullscreen.
- On Wayland, fix incorrect size event when entering fullscreen with client side decorations.
- On Wayland, fix `resizable` attribute not being applied properly on startup.
- On Wayland, fix disabled repeat rate not being handled.
- On Wayland, fix decoration buttons not working after tty switch.
- On Wayland, fix scaling not being applied on output re-enable.
- On Wayland, fix crash when `XCURSOR_SIZE` is `0`.
- On Wayland, fix pointer getting created in some cases without pointer capability.
- On Wayland, on kwin, fix space between window and decorations on startup.
- **Breaking:** On Wayland, `Theme` trait was reworked.
- On Wayland, disable maximize button for non-resizable window.
- On Wayland, added support for `set_ime_position`.
- On Wayland, fix crash on startup since GNOME 3.37.90.
- On X11, fix incorrect modifiers state on startup.
# 0.22.2 (2020-05-16)
- Added Clone implementation for 'static events.
- On Windows, fix window intermittently hanging when `ControlFlow` was set to `Poll`.
- On Windows, fix `WindowBuilder::with_maximized` being ignored.
- On Android, minimal platform support.
- On iOS, touch positions are now properly converted to physical pixels.
- On macOS, updated core-* dependencies and cocoa
# 0.22.1 (2020-04-16)
- On X11, fix `ResumeTimeReached` being fired too early.
- On Web, replaced zero timeout for `ControlFlow::Poll` with `requestAnimationFrame`
- On Web, fix a possible panic during event handling
- On macOS, fix `EventLoopProxy` leaking memory for every instance.
# 0.22.0 (2020-03-09)
- On Windows, fix minor timing issue in wait_until_time_or_msg
- On Windows, rework handling of request_redraw() to address panics.
- On macOS, fix `set_simple_screen` to remember frame excluding title bar.
- On Wayland, fix coordinates in touch events when scale factor isn't 1.
- On Wayland, fix color from `close_button_icon_color` not applying.
- Ignore locale if unsupported by X11 backend
- On Wayland, Add HiDPI cursor support
- On Web, add the ability to query "Light" or "Dark" system theme send `ThemeChanged` on change.
- Fix `Event::to_static` returning `None` for user events.
- On Wayland, Hide CSD for fullscreen windows.
- On Windows, ignore spurious mouse move messages.
- **Breaking:** Move `ModifiersChanged` variant from `DeviceEvent` to `WindowEvent`.
- On Windows, add `IconExtWindows` trait which exposes creating an `Icon` from an external file or embedded resource
- Add `BadIcon::OsError` variant for when OS icon functionality fails
- On Windows, fix crash at startup on systems that do not properly support Windows' Dark Mode
- Revert On macOS, fix not sending ReceivedCharacter event for specific keys combinations.
- on macOS, fix incorrect ReceivedCharacter events for some key combinations.
- **Breaking:** Use `i32` instead of `u32` for position type in `WindowEvent::Moved`.
- On macOS, a mouse motion event is now generated before every mouse click.
# 0.21.0 (2020-02-04)
- On Windows, fixed "error: linking with `link.exe` failed: exit code: 1120" error on older versions of windows.
- On macOS, fix set_minimized(true) works only with decorations.
- On macOS, add `hide_application` to `EventLoopWindowTarget` via a new `EventLoopWindowTargetExtMacOS` trait. `hide_application` will hide the entire application by calling `-[NSApplication hide: nil]`.
- On macOS, fix not sending ReceivedCharacter event for specific keys combinations.
- On macOS, fix `CursorMoved` event reporting the cursor position using logical coordinates.
- On macOS, fix issue where unbundled applications would sometimes open without being focused.
- On macOS, fix `run_return` does not return unless it receives a message.
- On Windows, fix bug where `RedrawRequested` would only get emitted every other iteration of the event loop.
- On X11, fix deadlock on window state when handling certain window events.
- `WindowBuilder` now implements `Default`.
- **Breaking:** `WindowEvent::CursorMoved` changed to `f64` units, preserving high-precision data supplied by most backends
- On Wayland, fix coordinates in mouse events when scale factor isn't 1
- On Web, add the ability to provide a custom canvas
- **Breaking:** On Wayland, the `WaylandTheme` struct has been replaced with a `Theme` trait, allowing for extra configuration
# 0.20.0 (2020-01-05)
- On X11, fix `ModifiersChanged` emitting incorrect modifier change events
- **Breaking**: Overhaul how Winit handles DPI:
+ Window functions and events now return `PhysicalSize` instead of `LogicalSize`.
+ Functions that take `Size` or `Position` types can now take either `Logical` or `Physical` types.
+ `hidpi_factor` has been renamed to `scale_factor`.
+ `HiDpiFactorChanged` has been renamed to `ScaleFactorChanged`, and lets you control how the OS
resizes the window in response to the change.
+ On X11, deprecate `WINIT_HIDPI_FACTOR` environment variable in favor of `WINIT_X11_SCALE_FACTOR`.
+ `Size` and `Position` types are now generic over their exact pixel type.
# 0.20.0 Alpha 6 (2020-01-03)
- On macOS, fix `set_cursor_visible` hides cursor outside of window.
- On macOS, fix `CursorEntered` and `CursorLeft` events fired at old window size.
- On macOS, fix error when `set_fullscreen` is called during fullscreen transition.
- On all platforms except mobile and WASM, implement `Window::set_minimized`.
- On X11, fix `CursorEntered` event being generated for non-winit windows.
- On macOS, fix crash when starting maximized without decorations.
- On macOS, fix application not terminating on `run_return`.
- On Wayland, fix cursor icon updates on window borders when using CSD.
- On Wayland, under mutter(GNOME Wayland), fix CSD being behind the status bar, when starting window in maximized mode.
- On Windows, theme the title bar according to whether the system theme is "Light" or "Dark".
- Added `WindowEvent::ThemeChanged` variant to handle changes to the system theme. Currently only implemented on Windows.
- **Breaking**: Changes to the `RedrawRequested` event (#1041):
- `RedrawRequested` has been moved from `WindowEvent` to `Event`.
- `EventsCleared` has been renamed to `MainEventsCleared`.
- `RedrawRequested` is now issued only after `MainEventsCleared`.
- `RedrawEventsCleared` is issued after each set of `RedrawRequested` events.
- Implement synthetic window focus key events on Windows.
- **Breaking**: Change `ModifiersState` to a `bitflags` struct.
- On Windows, implement `VirtualKeyCode` translation for `LWin` and `RWin`.
- On Windows, fix closing the last opened window causing `DeviceEvent`s to stop getting emitted.
- On Windows, fix `Window::set_visible` not setting internal flags correctly. This resulted in some weird behavior.
- Add `DeviceEvent::ModifiersChanged`.
- Deprecate `modifiers` fields in other events in favor of `ModifiersChanged`.
- On X11, `WINIT_HIDPI_FACTOR` now dominates `Xft.dpi` when picking DPI factor for output.
- On X11, add special value `randr` for `WINIT_HIDPI_FACTOR` to make winit use self computed DPI factor instead of the one from `Xft.dpi`.
# 0.20.0 Alpha 5 (2019-12-09)
- On macOS, fix application termination on `ControlFlow::Exit`
- On Windows, fix missing `ReceivedCharacter` events when Alt is held.
- On macOS, stop emitting private corporate characters in `ReceivedCharacter` events.
- On X11, fix misreporting DPI factor at startup.
- On X11, fix events not being reported when using `run_return`.
- On X11, fix key modifiers being incorrectly reported.
- On X11, fix window creation hanging when another window is fullscreen.
- On Windows, fix focusing unfocused windows when switching from fullscreen to windowed.
- On X11, fix reporting incorrect DPI factor when waking from suspend.
- Change `EventLoopClosed` to contain the original event.
- **Breaking**: Add `is_synthetic` field to `WindowEvent` variant `KeyboardInput`,
indicating that the event is generated by winit.
- On X11, generate synthetic key events for keys held when a window gains or loses focus.
- On X11, issue a `CursorMoved` event when a `Touch` event occurs,
as X11 implicitly moves the cursor for such events.
# 0.20.0 Alpha 4 (2019-10-18)
- Add web support via the 'stdweb' or 'web-sys' features
- On Windows, implemented function to get HINSTANCE
- On macOS, implement `run_return`.
- On iOS, fix inverted parameter in `set_prefers_home_indicator_hidden`.
- On X11, performance is improved when rapidly calling `Window::set_cursor_icon`.
- On iOS, fix improper `msg_send` usage that was UB and/or would break if `!` is stabilized.
- On Windows, unset `maximized` when manually changing the window's position or size.
- On Windows, add touch pressure information for touch events.
- On macOS, differentiate between `CursorIcon::Grab` and `CursorIcon::Grabbing`.
- On Wayland, fix event processing sometimes stalling when using OpenGL with vsync.
- Officially remove the Emscripten backend.
- On Windows, fix handling of surrogate pairs when dispatching `ReceivedCharacter`.
- On macOS 10.15, fix freeze upon exiting exclusive fullscreen mode.
- On iOS, fix panic upon closing the app.
- On X11, allow setting mulitple `XWindowType`s.
- On iOS, fix null window on initial `HiDpiFactorChanged` event.
- On Windows, fix fullscreen window shrinking upon getting restored to a normal window.
- On macOS, fix events not being emitted during modal loops, such as when windows are being resized
by the user.
- On Windows, fix hovering the mouse over the active window creating an endless stream of CursorMoved events.
- Always dispatch a `RedrawRequested` event after creating a new window.
- On X11, return dummy monitor data to avoid panicking when no monitors exist.
- On X11, prevent stealing input focus when creating a new window.
Only steal input focus when entering fullscreen mode.
- On Wayland, fixed DeviceEvents for relative mouse movement is not always produced
- On Wayland, add support for set_cursor_visible and set_cursor_grab.
- On Wayland, fixed DeviceEvents for relative mouse movement is not always produced.
- Removed `derivative` crate dependency.
- On Wayland, add support for set_cursor_icon.
- Use `impl Iterator<Item = MonitorHandle>` instead of `AvailableMonitorsIter` consistently.
- On macOS, fix fullscreen state being updated after entering fullscreen instead of before,
resulting in `Window::fullscreen` returning the old state in `Resized` events instead of
reflecting the new fullscreen state
- On X11, fix use-after-free during window creation
- On Windows, disable monitor change keyboard shortcut while in exclusive fullscreen.
- On Windows, ensure that changing a borderless fullscreen window's monitor via keyboard shortcuts keeps the window fullscreen on the new monitor.
- Prevent `EventLoop::new` and `EventLoop::with_user_event` from getting called outside the main thread.
- This is because some platforms cannot run the event loop outside the main thread. Preventing this
reduces the potential for cross-platform compatibility gotchyas.
- On Windows and Linux X11/Wayland, add platform-specific functions for creating an `EventLoop` outside the main thread.
- On Wayland, drop resize events identical to the current window size.
- On Windows, fix window rectangle not getting set correctly on high-DPI systems.
# 0.20.0 Alpha 3 (2019-08-14)
- On macOS, drop the run closure on exit.
- On Windows, location of `WindowEvent::Touch` are window client coordinates instead of screen coordinates.
- On X11, fix delayed events after window redraw.
- On macOS, add `WindowBuilderExt::with_disallow_hidpi` to have the option to turn off best resolution openGL surface.
- On Windows, screen saver won't start if the window is in fullscreen mode.
- Change all occurrences of the `new_user_event` method to `with_user_event`.
- On macOS, the dock and the menu bar are now hidden in fullscreen mode.
- `Window::set_fullscreen` now takes `Option<Fullscreen>` where `Fullscreen`
consists of `Fullscreen::Exclusive(VideoMode)` and
`Fullscreen::Borderless(MonitorHandle)` variants.
- Adds support for exclusive fullscreen mode.
- On iOS, add support for hiding the home indicator.
- On iOS, add support for deferring system gestures.
- On iOS, fix a crash that occurred while acquiring a monitor's name.
- On iOS, fix armv7-apple-ios compile target.
- Removed the `T: Clone` requirement from the `Clone` impl of `EventLoopProxy<T>`.
- On iOS, disable overscan compensation for external displays (removes black
bars surrounding the image).
- On Linux, the functions `is_wayland`, `is_x11`, `xlib_xconnection` and `wayland_display` have been moved to a new `EventLoopWindowTargetExtUnix` trait.
- On iOS, add `set_prefers_status_bar_hidden` extension function instead of
hijacking `set_decorations` for this purpose.
- On macOS and iOS, corrected the auto trait impls of `EventLoopProxy`.
- On iOS, add touch pressure information for touch events.
- Implement `raw_window_handle::HasRawWindowHandle` for `Window` type on all supported platforms.
- On macOS, fix the signature of `-[NSView drawRect:]`.
- On iOS, fix the behavior of `ControlFlow::Poll`. It wasn't polling if that was the only mode ever used by the application.
- On iOS, fix DPI sent out by views on creation was `0.0` - now it gives a reasonable number.
- On iOS, RedrawRequested now works for gl/metal backed views.
- On iOS, RedrawRequested is generally ordered after EventsCleared.
# 0.20.0 Alpha 2 (2019-07-09)
- On X11, non-resizable windows now have maximize explicitly disabled.
- On Windows, support paths longer than MAX_PATH (260 characters) in `WindowEvent::DroppedFile`
and `WindowEvent::HoveredFile`.
- On Mac, implement `DeviceEvent::Button`.
- Change `Event::Suspended(true / false)` to `Event::Suspended` and `Event::Resumed`.
- On X11, fix sanity check which checks that a monitor's reported width and height (in millimeters) are non-zero when calculating the DPI factor.
- Revert the use of invisible surfaces in Wayland, which introduced graphical glitches with OpenGL (#835)
- On X11, implement `_NET_WM_PING` to allow desktop environment to kill unresponsive programs.
- On Windows, when a window is initially invisible, it won't take focus from the existing visible windows.
- On Windows, fix multiple calls to `request_redraw` during `EventsCleared` sending multiple `RedrawRequested events.`
- On Windows, fix edge case where `RedrawRequested` could be dispatched before input events in event loop iteration.
- On Windows, fix timing issue that could cause events to be improperly dispatched after `RedrawRequested` but before `EventsCleared`.
- On macOS, drop unused Metal dependency.
- On Windows, fix the trail effect happening on transparent decorated windows. Borderless (or un-decorated) windows were not affected.
- On Windows, fix `with_maximized` not properly setting window size to entire window.
- On macOS, change `WindowExtMacOS::request_user_attention()` to take an `enum` instead of a `bool`.
# 0.20.0 Alpha 1 (2019-06-21)
- Changes below are considered **breaking**.
- Change all occurrences of `EventsLoop` to `EventLoop`.
- Previously flat API is now exposed through `event`, `event_loop`, `monitor`, and `window` modules.
- `os` module changes:
- Renamed to `platform`.
- All traits now have platform-specific suffixes.
- Exposes new `desktop` module on Windows, Mac, and Linux.
- Changes to event loop types:
- `EventLoopProxy::wakeup` has been removed in favor of `send_event`.
- **Major:** New `run` method drives winit event loop.
- Returns `!` to ensure API behaves identically across all supported platforms.
- This allows `emscripten` implementation to work without lying about the API.
- `ControlFlow`'s variants have been replaced with `Wait`, `WaitUntil(Instant)`, `Poll`, and `Exit`.
- Is read after `EventsCleared` is processed.
- `Wait` waits until new events are available.
- `WaitUntil` waits until either new events are available or the provided time has been reached.
- `Poll` instantly resumes the event loop.
- `Exit` aborts the event loop.
- Takes a closure that implements `'static + FnMut(Event<T>, &EventLoop<T>, &mut ControlFlow)`.
- `&EventLoop<T>` is provided to allow new `Window`s to be created.
- **Major:** `platform::desktop` module exposes `EventLoopExtDesktop` trait with `run_return` method.
- Behaves identically to `run`, but returns control flow to the calling context and can take non-`'static` closures.
- `EventLoop`'s `poll_events` and `run_forever` methods have been removed in favor of `run` and `run_return`.
- Changes to events:
- Remove `Event::Awakened` in favor of `Event::UserEvent(T)`.
- Can be sent with `EventLoopProxy::send_event`.
- Rename `WindowEvent::Refresh` to `WindowEvent::RedrawRequested`.
- `RedrawRequested` can be sent by the user with the `Window::request_redraw` method.
- `EventLoop`, `EventLoopProxy`, and `Event` are now generic over `T`, for use in `UserEvent`.
- **Major:** Add `NewEvents(StartCause)`, `EventsCleared`, and `LoopDestroyed` variants to `Event`.
- `NewEvents` is emitted when new events are ready to be processed by event loop.
- `StartCause` describes why new events are available, with `ResumeTimeReached`, `Poll`, `WaitCancelled`, and `Init` (sent once at start of loop).
- `EventsCleared` is emitted when all available events have been processed.
- Can be used to perform logic that depends on all events being processed (e.g. an iteration of a game loop).
- `LoopDestroyed` is emitted when the `run` or `run_return` method is about to exit.
- Rename `MonitorId` to `MonitorHandle`.
- Removed `serde` implementations from `ControlFlow`.
- Rename several functions to improve both internal consistency and compliance with Rust API guidelines.
- Remove `WindowBuilder::multitouch` field, since it was only implemented on a few platforms. Multitouch is always enabled now.
- **Breaking:** On macOS, change `ns` identifiers to use snake_case for consistency with iOS's `ui` identifiers.
- Add `MonitorHandle::video_modes` method for retrieving supported video modes for the given monitor.
- On Wayland, the window now exists even if nothing has been drawn.
- On Windows, fix initial dimensions of a fullscreen window.
- On Windows, Fix transparent borderless windows rendering wrong.
# Version 0.19.1 (2019-04-08)
- On Wayland, added a `get_wayland_display` function to `EventsLoopExt`.
- On Windows, fix `CursorMoved(0, 0)` getting dispatched on window focus.
- On macOS, fix command key event left and right reverse.
- On FreeBSD, NetBSD, and OpenBSD, fix build of X11 backend.
- On Linux, the numpad's add, subtract and divide keys are now mapped to the `Add`, `Subtract` and `Divide` virtual key codes
- On macOS, the numpad's subtract key has been added to the `Subtract` mapping
- On Wayland, the numpad's home, end, page up and page down keys are now mapped to the `Home`, `End`, `PageUp` and `PageDown` virtual key codes
- On Windows, fix icon not showing up in corner of window.
- On X11, change DPI scaling factor behavior. First, winit tries to read it from "Xft.dpi" XResource, and uses DPI calculation from xrandr dimensions as fallback behavior.
# Version 0.19.0 (2019-03-06)
- On X11, we will use the faster `XRRGetScreenResourcesCurrent` function instead of `XRRGetScreenResources` when available.
- On macOS, fix keycodes being incorrect when using a non-US keyboard layout.
- On Wayland, fix `with_title()` not setting the windows title
- On Wayland, add `set_wayland_theme()` to control client decoration color theme
- Added serde serialization to `os::unix::XWindowType`.
- **Breaking:** Remove the `icon_loading` feature and the associated `image` dependency.
- On X11, make event loop thread safe by replacing XNextEvent with select(2) and XCheckIfEvent
- On Windows, fix malformed function pointer typecast that could invoke undefined behavior.
- Refactored Windows state/flag-setting code.
- On Windows, hiding the cursor no longer hides the cursor for all Winit windows - just the one `hide_cursor` was called on.
- On Windows, cursor grabs used to get perpetually canceled when the grabbing window lost focus. Now, cursor grabs automatically get re-initialized when the window regains focus and the mouse moves over the client area.
- On Windows, only vertical mouse wheel events were handled. Now, horizontal mouse wheel events are also handled.
- On Windows, ignore the AltGr key when populating the `ModifersState` type.
# Version 0.18.1 (2018-12-30)
- On macOS, fix `Yen` (JIS) so applications receive the event.
- On X11 with a tiling WM, fixed high CPU usage when moving windows across monitors.
- On X11, fixed panic caused by dropping the window before running the event loop.
- on macOS, added `WindowExt::set_simple_fullscreen` which does not require a separate space
- Introduce `WindowBuilderExt::with_app_id` to allow setting the application ID on Wayland.
- On Windows, catch panics in event loop child thread and forward them to the parent thread. This prevents an invocation of undefined behavior due to unwinding into foreign code.
- On Windows, fix issue where resizing or moving window combined with grabbing the cursor would freeze program.
- On Windows, fix issue where resizing or moving window would eat `Awakened` events.
- On Windows, exiting fullscreen after entering fullscreen with disabled decorations no longer shrinks window.
- On X11, fixed a segfault when using virtual monitors with XRandR.
- Derive `Ord` and `PartialOrd` for `VirtualKeyCode` enum.
- On Windows, fix issue where hovering or dropping a non file item would create a panic.
- On Wayland, fix resizing and DPI calculation when a `wl_output` is removed without sending a `leave` event to the `wl_surface`, such as disconnecting a monitor from a laptop.
- On Wayland, DPI calculation is handled by smithay-client-toolkit.
- On X11, `WindowBuilder::with_min_dimensions` and `WindowBuilder::with_max_dimensions` now correctly account for DPI.
- Added support for generating dummy `DeviceId`s and `WindowId`s to better support unit testing.
- On macOS, fixed unsoundness in drag-and-drop that could result in drops being rejected.
- On macOS, implemented `WindowEvent::Refresh`.
- On macOS, all `MouseCursor` variants are now implemented and the cursor will no longer reset after unfocusing.
- Removed minimum supported Rust version guarantee.
# Version 0.18.0 (2018-11-07)
- **Breaking:** `image` crate upgraded to 0.20. This is exposed as part of the `icon_loading` API.
- On Wayland, pointer events will now provide the current modifiers state.
- On Wayland, titles will now be displayed in the window header decoration.
- On Wayland, key repetition is now ended when keyboard loses focus.
- On Wayland, windows will now use more stylish and modern client side decorations.
- On Wayland, windows will use server-side decorations when available.
- **Breaking:** Added support for F16-F24 keys (variants were added to the `VirtualKeyCode` enum).
- Fixed graphical glitches when resizing on Wayland.
- On Windows, fix freezes when performing certain actions after a window resize has been triggered. Reintroduces some visual artifacts when resizing.
- Updated window manager hints under X11 to v1.5 of [Extended Window Manager Hints](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html#idm140200472629520).
- Added `WindowBuilderExt::with_gtk_theme_variant` to X11-specific `WindowBuilder` functions.
- Fixed UTF8 handling bug in X11 `set_title` function.
- On Windows, `Window::set_cursor` now applies immediately instead of requiring specific events to occur first.
- On Windows, the `HoveredFile` and `HoveredFileCancelled` events are now implemented.
- On Windows, fix `Window::set_maximized`.
- On Windows 10, fix transparency (#260).
- On macOS, fix modifiers during key repeat.
- Implemented the `Debug` trait for `Window`, `EventsLoop`, `EventsLoopProxy` and `WindowBuilder`.
- On X11, now a `Resized` event will always be generated after a DPI change to ensure the window's logical size is consistent with the new DPI.
- Added further clarifications to the DPI docs.
- On Linux, if neither X11 nor Wayland manage to initialize, the corresponding panic now consists of a single line only.
- Add optional `serde` feature with implementations of `Serialize`/`Deserialize` for DPI types and various event types.
- Add `PartialEq`, `Eq`, and `Hash` implementations on public types that could have them but were missing them.
- On X11, drag-and-drop receiving an unsupported drop type can no longer cause the WM to freeze.
- Fix issue whereby the OpenGL context would not appear at startup on macOS Mojave (#1069).
- **Breaking:** Removed `From<NSApplicationActivationPolicy>` impl from `ActivationPolicy` on macOS.
- On macOS, the application can request the user's attention with `WindowExt::request_user_attention`.
# Version 0.17.2 (2018-08-19)
- On macOS, fix `<C-Tab>` so applications receive the event.
- On macOS, fix `<Cmd-{key}>` so applications receive the event.
- On Wayland, key press events will now be repeated.
# Version 0.17.1 (2018-08-05)
- On X11, prevent a compilation failure in release mode for versions of Rust greater than or equal to 1.30.
- Fixed deadlock that broke fullscreen mode on Windows.
# Version 0.17.0 (2018-08-02)
- Cocoa and core-graphics updates.
- Fixed thread-safety issues in several `Window` functions on Windows.
- On MacOS, the key state for modifiers key events is now properly set.
- On iOS, the view is now set correctly. This makes it possible to render things (instead of being stuck on a black screen), and touch events work again.
- Added NetBSD support.
- **Breaking:** On iOS, `UIView` is now the default root view. `WindowBuilderExt::with_root_view_class` can be used to set the root view objective-c class to `GLKView` (OpenGLES) or `MTKView` (Metal/MoltenVK).
- On iOS, the `UIApplication` is not started until `Window::new` is called.
- Fixed thread unsafety with cursor hiding on macOS.
- On iOS, fixed the size of the `JmpBuf` type used for `setjmp`/`longjmp` calls. Previously this was a buffer overflow on most architectures.
- On Windows, use cached window DPI instead of repeatedly querying the system. This fixes sporadic crashes on Windows 7.
# Version 0.16.2 (2018-07-07)
- On Windows, non-resizable windows now have the maximization button disabled. This is consistent with behavior on macOS and popular X11 WMs.
- Corrected incorrect `unreachable!` usage when guessing the DPI factor with no detected monitors.
# Version 0.16.1 (2018-07-02)
- Added logging through `log`. Logging will become more extensive over time.
- On X11 and Windows, the window's DPI factor is guessed before creating the window. This *greatly* cuts back on unsightly auto-resizing that would occur immediately after window creation.
- Fixed X11 backend compilation for environments where `c_char` is unsigned.
# Version 0.16.0 (2018-06-25)
- Windows additionally has `WindowBuilderExt::with_no_redirection_bitmap`.
- **Breaking:** Removed `VirtualKeyCode::LMenu` and `VirtualKeyCode::RMenu`; Windows now generates `VirtualKeyCode::LAlt` and `VirtualKeyCode::RAlt` instead.
- On X11, exiting fullscreen no longer leaves the window in the monitor's top left corner.
- **Breaking:** `Window::hidpi_factor` has been renamed to `Window::get_hidpi_factor` for better consistency. `WindowEvent::HiDPIFactorChanged` has been renamed to `WindowEvent::HiDpiFactorChanged`. DPI factors are always represented as `f64` instead of `f32` now.
- The Windows backend is now DPI aware. `WindowEvent::HiDpiFactorChanged` is implemented, and `MonitorId::get_hidpi_factor` and `Window::hidpi_factor` return accurate values.
- Implemented `WindowEvent::HiDpiFactorChanged` on X11.
- On macOS, `Window::set_cursor_position` is now relative to the client area.
- On macOS, setting the maximum and minimum dimensions now applies to the client area dimensions rather than to the window dimensions.
- On iOS, `MonitorId::get_dimensions` has been implemented and both `MonitorId::get_hidpi_factor` and `Window::get_hidpi_factor` return accurate values.
- On Emscripten, `MonitorId::get_hidpi_factor` now returns the same value as `Window::get_hidpi_factor` (it previously would always return 1.0).
- **Breaking:** The entire API for sizes, positions, etc. has changed. In the majority of cases, winit produces and consumes positions and sizes as `LogicalPosition` and `LogicalSize`, respectively. The notable exception is `MonitorId` methods, which deal in `PhysicalPosition` and `PhysicalSize`. See the documentation for specifics and explanations of the types. Additionally, winit automatically conserves logical size when the DPI factor changes.
- **Breaking:** All deprecated methods have been removed. For `Window::platform_display` and `Window::platform_window`, switch to the appropriate platform-specific `WindowExt` methods. For `Window::get_inner_size_points` and `Window::get_inner_size_pixels`, use the `LogicalSize` returned by `Window::get_inner_size` and convert as needed.
- HiDPI support for Wayland.
- `EventsLoop::get_available_monitors` and `EventsLoop::get_primary_monitor` now have identical counterparts on `Window`, so this information can be acquired without an `EventsLoop` borrow.
- `AvailableMonitorsIter` now implements `Debug`.
- Fixed quirk on macOS where certain keys would generate characters at twice the normal rate when held down.
- On X11, all event loops now share the same `XConnection`.
- **Breaking:** `Window::set_cursor_state` and `CursorState` enum removed in favor of the more composable `Window::grab_cursor` and `Window::hide_cursor`. As a result, grabbing the cursor no longer automatically hides it; you must call both methods to retain the old behavior on Windows and macOS. `Cursor::NoneCursor` has been removed, as it's no longer useful.
- **Breaking:** `Window::set_cursor_position` now returns `Result<(), String>`, thus allowing for `Box<Error>` conversion via `?`.
# Version 0.15.1 (2018-06-13)
- On X11, the `Moved` event is no longer sent when the window is resized without changing position.
- `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
- Fix `.with_decorations(false)` in macOS
- On Mac, `NSWindow` and supporting objects might be alive long after they were `closed` which resulted in apps consuming more heap then needed. Mainly it was affecting multi window applications. Not expecting any user visible change of behaviour after the fix.
- Fix regression of Window platform extensions for macOS where `NSFullSizeContentViewWindowMask` was not being correctly applied to `.fullsize_content_view`.
- Corrected `get_position` on Windows to be relative to the screen rather than to the taskbar.
- Corrected `Moved` event on Windows to use position values equivalent to those returned by `get_position`. It previously supplied client area positions instead of window positions, and would additionally interpret negative values as being very large (around `u16::MAX`).
- Implemented `Moved` event on macOS.
- On X11, the `Moved` event correctly use window positions rather than client area positions. Additionally, a stray `Moved` that unconditionally accompanied `Resized` with the client area position relative to the parent has been eliminated; `Moved` is still received alongside `Resized`, but now only once and always correctly.
- On Windows, implemented all variants of `DeviceEvent` other than `Text`. Mouse `DeviceEvent`s are now received even if the window isn't in the foreground.
- `DeviceId` on Windows is no longer a unit struct, and now contains a `u32`. For `WindowEvent`s, this will always be 0, but on `DeviceEvent`s it will be the handle to that device. `DeviceIdExt::get_persistent_identifier` can be used to acquire a unique identifier for that device that persists across replugs/reboots/etc.
- Corrected `run_forever` on X11 to stop discarding `Awakened` events.
- Various safety and correctness improvements to the X11 backend internals.
- Fixed memory leak on X11 every time the mouse entered the window.
- On X11, drag and drop now works reliably in release mode.
- Added `WindowBuilderExt::with_resize_increments` and `WindowBuilderExt::with_base_size` to X11, allowing for more optional hints to be set.
- Rework of the wayland backend, migrating it to use [Smithay's Client Toolkit](https://github.com/Smithay/client-toolkit).
- Added `WindowBuilder::with_window_icon` and `Window::set_window_icon`, finally making it possible to set the window icon on Windows and X11. The `icon_loading` feature can be enabled to allow for icons to be easily loaded; see example program `window_icon.rs` for usage.
- Windows additionally has `WindowBuilderExt::with_taskbar_icon` and `WindowExt::set_taskbar_icon`.
- On Windows, fix panic when trying to call `set_fullscreen(None)` on a window that has not been fullscreened prior.
# Version 0.13.1 (2018-04-26)
- Ensure necessary `x11-dl` version is used.
# Version 0.13.0 (2018-04-25)
- Implement `WindowBuilder::with_maximized`, `Window::set_fullscreen`, `Window::set_maximized` and `Window::set_decorations` for MacOS.
- Implement `WindowBuilder::with_maximized`, `Window::set_fullscreen`, `Window::set_maximized` and `Window::set_decorations` for Windows.
- On Windows, `WindowBuilder::with_fullscreen` no longer changing monitor display resolution.
- Overhauled X11 window geometry calculations. `get_position` and `set_position` are more universally accurate across different window managers, and `get_outer_size` actually works now.
- Fixed SIGSEGV/SIGILL crashes on macOS caused by stabilization of the `!` (never) type.
- Implement `WindowEvent::HiDPIFactorChanged` for macOS
- On X11, input methods now work completely out of the box, no longer requiring application developers to manually call `setlocale`. Additionally, when input methods are started, stopped, or restarted on the server end, it's correctly handled.
- Implemented `Refresh` event on Windows.
- Properly calculate the minimum and maximum window size on Windows, including window decorations.
- Map more `MouseCursor` variants to cursor icons on Windows.
- Corrected `get_position` on macOS to return outer frame position, not content area position.
- Corrected `set_position` on macOS to set outer frame position, not content area position.
- Added `get_inner_position` method to `Window`, which gets the position of the window's client area. This is implemented on all applicable platforms (all desktop platforms other than Wayland, where this isn't possible).
- **Breaking:** the `Closed` event has been replaced by `CloseRequested` and `Destroyed`. To migrate, you typically just need to replace all usages of `Closed` with `CloseRequested`; see example programs for more info. The exception is iOS, where `Closed` must be replaced by `Destroyed`.
# Version 0.12.0 (2018-04-06)
- Added subclass to macos windows so they can be made resizable even with no decorations.
- Dead keys now work properly on X11, no longer resulting in a panic.
- On X11, input method creation first tries to use the value from the user's `XMODIFIERS` environment variable, so application developers should no longer need to manually call `XSetLocaleModifiers`. If that fails, fallbacks are tried, which should prevent input method initialization from ever outright failing.
- Fixed thread safety issues with input methods on X11.
- Add support for `Touch` for win32 backend.
- Fixed `Window::get_inner_size` and friends to return the size in pixels instead of points when using HIDPI displays on OSX.
# Version 0.11.3 (2018-03-28)
- Added `set_min_dimensions` and `set_max_dimensions` methods to `Window`, and implemented on Windows, X11, Wayland, and OSX.
- On X11, dropping a `Window` actually closes it now, and clicking the window's × button (or otherwise having the WM signal to close it) will result in the window closing.
- Added `WindowBuilderExt` methods for macos: `with_titlebar_transparent`,
`with_title_hidden`, `with_titlebar_buttons_hidden`,
`with_fullsize_content_view`.
- Mapped X11 numpad keycodes (arrows, Home, End, PageUp, PageDown, Insert and Delete) to corresponding virtual keycodes
# Version 0.11.2 (2018-03-06)
- Impl `Hash`, `PartialEq`, and `Eq` for `events::ModifiersState`.
- Implement `MonitorId::get_hidpi_factor` for MacOS.
- Added method `os::macos::MonitorIdExt::get_nsscreen() -> *mut c_void` that gets a `NSScreen` object matching the monitor ID.
- Send `Awakened` event on Android when event loop is woken up.
# Version 0.11.1 (2018-02-19)
- Fixed windows not receiving mouse events when click-dragging the mouse outside the client area of a window, on Windows platforms.
- Added method `os::android::EventsLoopExt:set_suspend_callback(Option<Box<Fn(bool) -> ()>>)` that allows glutin to register a callback when a suspend event happens
# Version 0.11.0 (2018-02-09)
- Implement `MonitorId::get_dimensions` for Android.
- Added method `os::macos::WindowBuilderExt::with_movable_by_window_background(bool)` that allows to move a window without a titlebar - `with_decorations(false)`
- Implement `Window::set_fullscreen`, `Window::set_maximized` and `Window::set_decorations` for Wayland.
- Added `Caret` as VirtualKeyCode and support OSX ^-Key with german input.
# Version 0.10.1 (2018-02-05)
*Yanked*
# Version 0.10.0 (2017-12-27)
- Add support for `Touch` for emscripten backend.
- Added support for `DroppedFile`, `HoveredFile`, and `HoveredFileCancelled` to X11 backend.
- **Breaking:** `unix::WindowExt` no longer returns pointers for things that aren't actually pointers; `get_xlib_window` now returns `Option<std::os::raw::c_ulong>` and `get_xlib_screen_id` returns `Option<std::os::raw::c_int>`. Additionally, methods that previously returned `libc::c_void` have been changed to return `std::os::raw::c_void`, which are not interchangeable types, so users wanting the former will need to explicitly cast.
- Added `set_decorations` method to `Window` to allow decorations to be toggled after the window is built. Presently only implemented on X11.
- Raised the minimum supported version of Rust to 1.20 on MacOS due to usage of associated constants in new versions of cocoa and core-graphics.
- Added `modifiers` field to `MouseInput`, `MouseWheel`, and `CursorMoved` events to track the modifiers state (`ModifiersState`).
- Fixed the emscripten backend to return the size of the canvas instead of the size of the window.
# Version 0.9.0 (2017-12-01)
- Added event `WindowEvent::HiDPIFactorChanged`.
- Added method `MonitorId::get_hidpi_factor`.
- Deprecated `get_inner_size_pixels` and `get_inner_size_points` methods of `Window` in favor of
`get_inner_size`.
- **Breaking:** `EventsLoop` is `!Send` and `!Sync` because of platform-dependant constraints,
but `Window`, `WindowId`, `DeviceId` and `MonitorId` guaranteed to be `Send`.
- `MonitorId::get_position` now returns `(i32, i32)` instead of `(u32, u32)`.
- Rewrite of the wayland backend to use wayland-client-0.11
- Support for dead keys on wayland for keyboard utf8 input
- Monitor enumeration on Windows is now implemented using `EnumDisplayMonitors` instead of
`EnumDisplayDevices`. This changes the value returned by `MonitorId::get_name()`.
- On Windows added `MonitorIdExt::hmonitor` method
- Impl `Clone` for `EventsLoopProxy`
- `EventsLoop::get_primary_monitor()` on X11 will fallback to any available monitor if no primary is found
- Support for touch event on wayland
- `WindowEvent`s `MouseMoved`, `MouseEntered`, and `MouseLeft` have been renamed to
`CursorMoved`, `CursorEntered`, and `CursorLeft`.
- New `DeviceEvent`s added, `MouseMotion` and `MouseWheel`.
- Send `CursorMoved` event after `CursorEntered` and `Focused` events.
- Add support for `ModifiersState`, `MouseMove`, `MouseInput`, `MouseMotion` for emscripten backend.
# Version 0.8.3 (2017-10-11)
- Fixed issue of calls to `set_inner_size` blocking on Windows.
- Mapped `ISO_Left_Tab` to `VirtualKeyCode::Tab` to make the key work with modifiers
- Fixed the X11 backed on 32bit targets
# Version 0.8.2 (2017-09-28)
- Uniformize keyboard scancode values accross Wayland and X11 (#297).
- Internal rework of the wayland event loop
- Added method `os::linux::WindowExt::is_ready`
# Version 0.8.1 (2017-09-22)
- Added various methods to `os::linux::EventsLoopExt`, plus some hidden items necessary to make
glutin work.
# Version 0.8.0 (2017-09-21)
- Added `Window::set_maximized`, `WindowAttributes::maximized` and `WindowBuilder::with_maximized`.
- Added `Window::set_fullscreen`.
- Changed `with_fullscreen` to take a `Option<MonitorId>` instead of a `MonitorId`.
- Removed `MonitorId::get_native_identifer()` in favor of platform-specific traits in the `os`
module.
- Changed `get_available_monitors()` and `get_primary_monitor()` to be methods of `EventsLoop`
instead of stand-alone methods.
- Changed `EventsLoop` to be tied to a specific X11 or Wayland connection.
- Added a `os::linux::EventsLoopExt` trait that makes it possible to configure the connection.
- Fixed the emscripten code, which now compiles.
- Changed the X11 fullscreen code to use `xrandr` instead of `xxf86vm`.
- Fixed the Wayland backend to produce `Refresh` event after window creation.
- Changed the `Suspended` event to be outside of `WindowEvent`.
- Fixed the X11 backend sometimes reporting the wrong virtual key (#273).

View File

@@ -1,42 +0,0 @@
# Winit Contributing Guidelines
## Scope
[See `FEATURES.md`](./FEATURES.md). When requesting or implementing a new Winit feature, you should
consider whether or not it's directly related to window creation or input handling. If it isn't, it
may be worth creating a separate crate that extends Winit's API to add that functionality.
## Reporting an issue
When reporting an issue, in order to help the maintainers understand what the problem is, please make
your description of the issue as detailed as possible:
- if it is a bug, please provide clear explanation of what happens, what should happen, and how to
reproduce the issue, ideally by providing a minimal program exhibiting the problem
- if it is a feature request, please provide a clear argumentation about why you believe this feature
should be supported by winit
## Making a pull request
When making a code contribution to winit, before opening your pull request, please make sure that:
- you tested your modifications on all the platforms impacted, or if not possible detail which platforms
were not tested, and what should be tested, so that a maintainer or another contributor can test them
- you updated any relevant documentation in winit
- you left comments in your code explaining any part that is not straightforward, so that the
maintainers and future contributors don't have to try to guess what your code is supposed to do
- your PR adds an entry to the changelog file if the introduced change is relevant to winit users
- if your PR affects the platform compatibility of one or more features or adds another feature, the
relevant sections in [`FEATURES.md`](https://github.com/rust-windowing/winit/blob/master/FEATURES.md#features)
should be updated.
Once your PR is open, you can ask for review by a maintainer of your platform. Winit's merging policy
is that a PR must be approved by at least two maintainers of winit before being merged, including
at least a maintainer of the platform (a maintainer making a PR themselves counts as approving it).
## Maintainers & Testers
The current [list of testers and contributors](https://github.com/rust-windowing/winit/wiki/Testers-and-Contributors)
can be found on the Wiki.
If you are interested in contributing or testing on a platform, please add yourself to that table!

View File

@@ -1,128 +1,43 @@
[package]
name = "winit"
version = "0.26.0"
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
version = "0.6.4"
authors = ["The winit contributors, Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library."
edition = "2018"
keywords = ["windowing"]
license = "Apache-2.0"
readme = "README.md"
repository = "https://github.com/rust-windowing/winit"
repository = "https://github.com/tomaka/winit"
documentation = "https://docs.rs/winit"
categories = ["gui"]
[package.metadata.docs.rs]
features = ["serde"]
default-target = "x86_64-unknown-linux-gnu"
targets = ["i686-pc-windows-msvc", "x86_64-pc-windows-msvc", "i686-unknown-linux-gnu", "x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "wasm32-unknown-unknown"]
[features]
default = ["x11", "wayland", "wayland-dlopen"]
x11 = ["x11-dl", "mio", "percent-encoding", "parking_lot"]
wayland = ["wayland-client", "wayland-protocols", "sctk"]
wayland-dlopen = ["sctk/dlopen", "wayland-client/dlopen"]
[dependencies]
instant = { version = "0.1", features = ["wasm-bindgen"] }
lazy_static = "1"
log = "0.4"
serde = { version = "1", optional = true, features = ["serde_derive"] }
raw-window-handle = "0.4.2"
bitflags = "1"
mint = { version = "0.5.6", optional = true }
lazy_static = "0.2.2"
libc = "0.2"
shared_library = "0.1.5"
[dev-dependencies]
image = "0.23.12"
simple_logger = "1.9"
[target.'cfg(target_os = "android")'.dependencies.android_glue]
version = "0.2"
[target.'cfg(target_os = "android")'.dependencies]
ndk = "0.5"
ndk-sys = "0.2.0"
ndk-glue = "0.5"
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
objc = "0.2.7"
[target.'cfg(target_os = "ios")'.dependencies]
objc = "0.2"
[target.'cfg(target_os = "macos")'.dependencies]
cocoa = "0.24"
core-foundation = "0.9"
core-graphics = "0.22"
dispatch = "0.2.0"
block = "0.1"
[target.'cfg(target_os = "macos")'.dependencies.core-video-sys]
version = "0.1.4"
default_features = false
features = ["display_link"]
objc = "0.2"
cgl = "0.2"
cocoa = "=0.5.2"
core-foundation = "0.2"
core-graphics = "0.4"
[target.'cfg(target_os = "windows")'.dependencies]
parking_lot = "0.11"
winapi = "0.2"
shell32-sys = "0.1"
gdi32-sys = "0.1"
user32-sys = "~0.1.2"
kernel32-sys = "0.2"
dwmapi-sys = "0.1"
[target.'cfg(target_os = "windows")'.dependencies.winapi]
version = "0.3.9"
features = [
"combaseapi",
"commctrl",
"dwmapi",
"errhandlingapi",
"imm",
"hidusage",
"libloaderapi",
"objbase",
"ole2",
"processthreadsapi",
"shellapi",
"shellscalingapi",
"shobjidl_core",
"unknwnbase",
"winbase",
"windowsx",
"winerror",
"wingdi",
"winnt",
"winuser",
"mmsystem",
"timeapi"
]
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
wayland-client = { version = "0.29", default_features = false, features = ["use_system_lib"], optional = true }
wayland-protocols = { version = "0.29", features = [ "staging_protocols"], optional = true }
sctk = { package = "smithay-client-toolkit", version = "0.15.1", default_features = false, features = ["calloop"], optional = true }
mio = { version = "0.8", features = ["os-ext"], optional = true }
x11-dl = { version = "2.18.5", optional = true }
percent-encoding = { version = "2.0", optional = true }
parking_lot = { version = "0.11.0", optional = true }
libc = "0.2.64"
[target.'cfg(target_arch = "wasm32")'.dependencies.web_sys]
package = "web-sys"
version = "0.3.22"
features = [
'console',
"AddEventListenerOptions",
'CssStyleDeclaration',
'BeforeUnloadEvent',
'Document',
'DomRect',
'Element',
'Event',
'EventTarget',
'FocusEvent',
'HtmlCanvasElement',
'HtmlElement',
'KeyboardEvent',
'MediaQueryList',
'MediaQueryListEvent',
'MouseEvent',
'Node',
'PointerEvent',
'Window',
'WheelEvent'
]
[target.'cfg(target_arch = "wasm32")'.dependencies.wasm-bindgen]
version = "0.2.45"
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
console_log = "0.2"
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies]
wayland-client = { version = "0.8.6", features = ["dlopen"] }
wayland-kbd = "0.8.0"
wayland-window = "0.5.0"
x11-dl = "2.8"

View File

@@ -1,239 +0,0 @@
# Winit Scope
Winit aims to expose an interface that abstracts over window creation and input handling, and can
be used to create both games and applications. It supports the main graphical platforms:
- Desktop
- Windows
- macOS
- Unix
- via X11
- via Wayland
- Mobile
- iOS
- Android
- Web
- via WASM
Most platforms expose capabilities that cannot be meaningfully transposed onto others. Winit does not
aim to support every single feature of every platform, but rather to abstract over the common features
available everywhere. In this context, APIs exposed in winit can be split into different "support tiers":
- **Core:** Features that are essential to providing a well-formed abstraction over each platform's
windowing and input APIs.
- **Platform:** Platform-specific features that can't be meaningfully exposed through a common API and
cannot be implemented outside of Winit without exposing a significant amount of Winit's internals
or interfering with Winit's abstractions.
- **Usability:** Features that are not strictly essential to Winit's functionality, but provide meaningful
usability improvements and cannot be reasonably implemented in an external crate. These are
generally optional and exposed through Cargo features.
Core features are taken care of by the core Winit maintainers. Platform features are not.
When a platform feature is submitted, the submitter is considered the expert in the
feature and may be asked to support the feature should it break in the future.
Winit ***does not*** directly expose functionality for drawing inside windows or creating native
menus, but ***does*** commit to providing APIs that higher-level crates can use to implement that
functionality.
## `1.0` and stability
When all core features are implemented to the satisfaction of the Winit maintainers, Winit 1.0 will
be released and the library will enter maintenance mode. For the most part, new core features will not
be added past this point. New platform features may be accepted and exposed through point releases.
### Tier upgrades
Some platform features could in theory be exposed across multiple platforms, but have not gone
through the implementation work necessary to function on all platforms. When one of these features
gets implemented across all platforms, a PR can be opened to upgrade the feature to a core feature.
If that gets accepted, the platform-specific functions gets deprecated and become permanently
exposed through the core, cross-platform API.
# Features
## Extending this section
If your PR makes notable changes to Winit's features, please update this section as follows:
- If your PR adds a new feature, add a brief description to the relevant section. If the feature is a core
feature, add a row to the feature matrix and describe what platforms the feature has been implemented on.
- If your PR begins a new API rework, add a row to the `Pending API Reworks` table. If the PR implements the
API rework on all relevant platforms, please move it to the `Completed API Reworks` table.
- If your PR implements an already-existing feature on a new platform, either mark the feature as *completed*,
or mark it as *mostly completed* and link to an issue describing the problems with the implementation.
## Core
### Windowing
- **Window initialization**: Winit allows the creation of a window
- **Providing pointer to init OpenGL**: Winit provides the necessary pointers to initialize a working opengl context
- **Providing pointer to init Vulkan**: Same as OpenGL but for Vulkan
- **Window decorations**: The windows created by winit are properly decorated, and the decorations can
be deactivated
- **Window decorations toggle**: Decorations can be turned on or off after window creation
- **Window resizing**: The windows created by winit can be resized and generate the appropriate events
when they are. The application can precisely control its window size if desired.
- **Window resize increments**: When the window gets resized, the application can choose to snap the window's
size to specific values.
- **Window transparency**: Winit allows the creation of windows with a transparent background.
- **Window maximization**: The windows created by winit can be maximized upon creation.
- **Window maximization toggle**: The windows created by winit can be maximized and unmaximized after
creation.
- **Window minimization**: The windows created by winit can be minimized after creation.
- **Fullscreen**: The windows created by winit can be put into fullscreen mode.
- **Fullscreen toggle**: The windows created by winit can be switched to and from fullscreen after
creation.
- **Exclusive fullscreen**: Winit allows changing the video mode of the monitor
for fullscreen windows, and if applicable, captures the monitor for exclusive
use by this application.
- **HiDPI support**: Winit assists developers in appropriately scaling HiDPI content.
- **Popup / modal windows**: Windows can be created relative to the client area of other windows, and parent
windows can be disabled in favor of popup windows. This feature also guarantees that popup windows
get drawn above their owner.
### System Information
- **Monitor list**: Retrieve the list of monitors and their metadata, including which one is primary.
- **Video mode query**: Monitors can be queried for their supported fullscreen video modes (consisting of resolution, refresh rate, and bit depth).
### Input Handling
- **Mouse events**: Generating mouse events associated with pointer motion, click, and scrolling events.
- **Mouse set location**: Forcibly changing the location of the pointer.
- **Cursor grab**: Locking the cursor so it cannot exit the client area of a window.
- **Cursor icon**: Changing the cursor icon, or hiding the cursor.
- **Touch events**: Single-touch events.
- **Touch pressure**: Touch events contain information about the amount of force being applied.
- **Multitouch**: Multi-touch events, including cancellation of a gesture.
- **Keyboard events**: Properly processing keyboard events using the user-specified keymap and
translating keypresses into UTF-8 characters, handling dead keys and IMEs.
- **Drag & Drop**: Dragging content into winit, detecting when content enters, drops, or if the drop is cancelled.
- **Raw Device Events**: Capturing input from input devices without any OS filtering.
- **Gamepad/Joystick events**: Capturing input from gamepads and joysticks.
- **Device movement events**: Capturing input from the device gyroscope and accelerometer.
## Platform
### Windows
* Setting the taskbar icon
* Setting the parent window
* Setting a menu bar
* `WS_EX_NOREDIRECTIONBITMAP` support
* Theme the title bar according to Windows 10 Dark Mode setting or set a preferred theme
### macOS
* Window activation policy
* Window movable by background
* Transparent titlebar
* Hidden titlebar
* Hidden titlebar buttons
* Full-size content view
### Unix
* Window urgency
* X11 Window Class
* X11 Override Redirect Flag
* GTK Theme Variant
* Base window size
### iOS
* `winit` has a minimum OS requirement of iOS 8
* Get the `UIWindow` object pointer
* Get the `UIViewController` object pointer
* Get the `UIView` object pointer
* Get the `UIScreen` object pointer
* Setting the `UIView` hidpi factor
* Valid orientations
* Home indicator visibility
* Status bar visibility
* Deferrring system gestures
* Support for custom `UIView` derived class
* Getting the device idiom
* Getting the preferred video mode
### Web
* Get if systems preferred color scheme is "dark"
## Usability
* `serde`: Enables serialization/deserialization of certain types with Serde. (Maintainer: @Osspial)
## Compatibility Matrix
Legend:
- ✔️: Works as intended
- ▢: Mostly works but some bugs are known
- ❌: Missing feature or large bugs making it unusable
- **N/A**: Not applicable for this platform
- ❓: Unknown status
### Windowing
|Feature |Windows|MacOS |Linux x11 |Linux Wayland |Android|iOS |WASM |
|-------------------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|Window initialization |✔️ |✔️ |▢[#5] |✔️ |▢[#33]|▢[#33] |✔️ |
|Providing pointer to init OpenGL |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|
|Providing pointer to init Vulkan |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |**N/A**|
|Window decorations |✔️ |✔️ |✔️ |▢[#306] |**N/A**|**N/A**|**N/A**|
|Window decorations toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|
|Window resizing |✔️ |▢[#219]|✔️ |▢[#306] |**N/A**|**N/A**|✔️ |
|Window resize increments |❌ |❌ |❌ |❌ |❌ |❌ |**N/A**|
|Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A |
|Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|
|Window maximization toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|
|Window minimization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|
|Fullscreen |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |
|Fullscreen toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |
|Exclusive fullscreen |✔️ |✔️ |✔️ |**N/A** |❌ |✔️ |**N/A**|
|HiDPI support |✔️ |✔️ |✔️ |✔️ |▢[#721]|✔️ |✔️ |
|Popup windows |❌ |❌ |❌ |❌ |❌ |❌ |**N/A**|
### System information
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |
|---------------- | ----- | ---- | ------- | ----------- | ----- | ------- | -------- |
|Monitor list |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |**N/A**|
|Video mode query |✔️ |✔️ |✔️ |✔️ |❌ |✔️ |**N/A**|
### Input handling
|Feature |Windows |MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |
|----------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|Mouse events |✔️ |▢[#63] |✔️ |✔️ |**N/A**|**N/A**|✔️ |
|Mouse set location |✔️ |✔️ |✔️ |❓ |**N/A**|**N/A**|**N/A**|
|Cursor grab |✔️ |▢[#165] |▢[#242] |✔️ |**N/A**|**N/A**|❓ |
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ |
|Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |❌ |
|Multitouch |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ |
|Keyboard events |✔️ |✔️ |✔️ |✔️ |❓ |❌ |✔️ |
|Drag & Drop |▢[#720] |▢[#720] |▢[#720] |❌[#306] |**N/A**|**N/A**|❓ |
|Raw Device Events |▢[#750] |▢[#750] |▢[#750] |❌ |❌ |❌ |❓ |
|Gamepad/Joystick events |❌[#804] |❌ |❌ |❌ |❌ |❌ |❓ |
|Device movement events |❓ |❓ |❓ |❓ |❌ |❌ |❓ |
|Drag window with cursor |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |
### Pending API Reworks
Changes in the API that have been agreed upon but aren't implemented across all platforms.
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|New API for HiDPI ([#315] [#319]) |✔️ |✔️ |✔️ |✔️ |▢[#721]|✔️ |❓ |
|Event Loop 2.0 ([#459]) |✔️ |✔️ |❌ |✔️ |❌ |✔️ |❓ |
|Keyboard Input ([#812]) |❌ |❌ |❌ |❌ |❌ |❌ |❓ |
### Completed API Reworks
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
[#165]: https://github.com/rust-windowing/winit/issues/165
[#219]: https://github.com/rust-windowing/winit/issues/219
[#242]: https://github.com/rust-windowing/winit/issues/242
[#306]: https://github.com/rust-windowing/winit/issues/306
[#315]: https://github.com/rust-windowing/winit/issues/315
[#319]: https://github.com/rust-windowing/winit/issues/319
[#33]: https://github.com/rust-windowing/winit/issues/33
[#459]: https://github.com/rust-windowing/winit/issues/459
[#5]: https://github.com/rust-windowing/winit/issues/5
[#63]: https://github.com/rust-windowing/winit/issues/63
[#720]: https://github.com/rust-windowing/winit/issues/720
[#721]: https://github.com/rust-windowing/winit/issues/721
[#750]: https://github.com/rust-windowing/winit/issues/750
[#804]: https://github.com/rust-windowing/winit/issues/804
[#812]: https://github.com/rust-windowing/winit/issues/812

View File

@@ -1,23 +0,0 @@
# Hall of Champions
The winit maintainers would like to recognize the following former winit
contributors, without whom winit would not exist in its current form. We thank
them deeply for their time and efforts, and wish them best of luck in their
future endeavors:
* [@tomaka]: For creating the winit project and guiding it through its early
years of existence.
* [@vberger]: For diligently creating the Wayland backend, and being its
extremely helpful and benevolent maintainer for years.
* [@francesca64]: For taking over the responsibility of maintaining almost every
winit backend, and standardizing HiDPI support across all of them.
* [@Osspial]: For heroically landing EventLoop 2.0, and valiantly ushering in a
vastly more sustainable era of winit.
* [@goddessfreya]: For selflessly taking over maintainership of glutin, and her
stellar dedication to improving both winit and glutin.
[@tomaka]: https://github.com/tomaka
[@vberger]: https://github.com/vberger
[@francesca64]: https://github.com/francesca64
[@Osspial]: https://github.com/Osspial
[@goddessfreya]: https://github.com/goddessfreya

117
README.md
View File

@@ -1,32 +1,23 @@
# winit - Cross-platform window creation and management in Rust
[![Crates.io](https://img.shields.io/crates/v/winit.svg)](https://crates.io/crates/winit)
[![](http://meritbadge.herokuapp.com/winit)](https://crates.io/crates/winit)
[![Docs.rs](https://docs.rs/winit/badge.svg)](https://docs.rs/winit)
[![CI Status](https://github.com/rust-windowing/winit/workflows/CI/badge.svg)](https://github.com/rust-windowing/winit/actions)
[![Build Status](https://travis-ci.org/tomaka/winit.png?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.26.0"
winit = "0.5"
```
## [Documentation](https://docs.rs/winit)
For features _within_ the scope of winit, see [FEATURES.md](FEATURES.md).
For features _outside_ the scope of winit, see [Missing features provided by other crates](https://github.com/rust-windowing/winit/wiki/Missing-features-provided-by-other-crates) in the wiki.
## Contact Us
Join us in any of these:
[![Freenode](https://img.shields.io/badge/freenode.net-%23glutin-red.svg)](http://webchat.freenode.net?channels=%23glutin&uio=MTY9dHJ1ZSYyPXRydWUmND10cnVlJjExPTE4NSYxMj10cnVlJjE1PXRydWU7a)
[![Matrix](https://img.shields.io/badge/Matrix-%23Glutin%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#Glutin:matrix.org)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/tomaka/glutin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Usage
Winit is a window creation and management library. It can create windows and lets you handle
events (for example: the window being resized, a key being pressed, a mouse movement, etc.)
events (for example: the window being resized, a key being pressed, a mouse mouvement, etc.)
produced by window.
Winit is designed to be a low-level brick in a hierarchy of libraries. Consequently, in order to
@@ -34,98 +25,16 @@ show something on the window you need to use the platform-specific getters provi
another library.
```rust
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
extern crate winit;
fn main() {
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
let window = winit::Window::new().unwrap();
for event in window.wait_events() {
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
_ => (),
winit::Event::Closed => break,
_ => ()
}
});
}
}
```
Winit is only officially supported on the latest stable version of the Rust compiler.
### Cargo Features
Winit provides the following features, which can be enabled in your `Cargo.toml` file:
* `serde`: Enables serialization/deserialization of certain types with [Serde](https://crates.io/crates/serde).
* `x11` (enabled by default): On Unix platform, compiles with the X11 backend
* `wayland` (enabled by default): On Unix platform, compiles with the Wayland backend
* `mint`: Enables mint (math interoperability standard types) conversions.
### Platform-specific usage
#### WebAssembly
Winit supports compiling to the `wasm32-unknown-unknown` target with `web-sys`.
On the web platform, a Winit window is backed by a `<canvas>` element. You can
either [provide Winit with a `<canvas>` element][web with_canvas], or [let Winit
create a `<canvas>` element which you can then retrieve][web canvas getter] and
insert it into the DOM yourself.
For example code using Winit with WebAssembly, check out the [web example]. For
information on using Rust on WebAssembly, check out the [Rust and WebAssembly
book].
[web with_canvas]: https://docs.rs/winit/latest/wasm32-unknown-unknown/winit/platform/web/trait.WindowBuilderExtWebSys.html#tymethod.with_canvas
[web canvas getter]: https://docs.rs/winit/latest/wasm32-unknown-unknown/winit/platform/web/trait.WindowExtWebSys.html#tymethod.canvas
[web example]: ./examples/web.rs
[Rust and WebAssembly book]: https://rustwasm.github.io/book/
#### Android
This library makes use of the [ndk-rs](https://github.com/rust-windowing/android-ndk-rs) crates, refer to that repo for more documentation.
The `ndk_glue` version needs to match the version used by `winit`. Otherwise, the application will not start correctly as `ndk_glue`'s internal NativeActivity static is not the same due to version mismatch.
`ndk_glue` <-> `winit` version comparison compatibility:
| winit | ndk_glue |
| :---: | :------------------: |
| 0.24 | `ndk_glue = "0.2.0"` |
| 0.25 | `ndk_glue = "0.3.0"` |
| 0.26 | `ndk_glue = "0.5.0"` |
Running on an Android device needs a dynamic system library, add this to Cargo.toml:
```toml
[[example]]
name = "request_redraw_threaded"
crate-type = ["cdylib"]
```
And add this to the example file to add the native activity glue:
```rust
#[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))]
fn main() {
...
}
```
And run the application with `cargo apk run --example request_redraw_threaded`
#### MacOS
To ensure compatibility with older MacOS systems, winit links to
CGDisplayCreateUUIDFromDisplayID through the CoreGraphics framework.
However, under certain setups this function is only available to be linked
through the newer ColorSync framework. So, winit provides the
`WINIT_LINK_COLORSYNC` environment variable which can be set to `1` or `true`
while compiling to enable linking via ColorSync.

17
appveyor.yml Normal file
View File

@@ -0,0 +1,17 @@
environment:
matrix:
- TARGET: x86_64-pc-windows-msvc
- TARGET: i686-pc-windows-msvc
- TARGET: i686-pc-windows-gnu
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
- SET PATH=%PATH%;C:\MinGW\bin
- rustc -V
- cargo -V
build: false
test_script:
- cargo test --verbose

View File

@@ -1,10 +0,0 @@
fn main() {
// If building for macos and WINIT_LINK_COLORSYNC is set to true
// use CGDisplayCreateUUIDFromDisplayID from ColorSync instead of CoreGraphics
if std::env::var("CARGO_CFG_TARGET_OS").map_or(false, |os| os == "macos")
&& std::env::var("WINIT_LINK_COLORSYNC")
.map_or(false, |v| v == "1" || v.eq_ignore_ascii_case("true"))
{
println!("cargo:rustc-cfg=use_colorsync_cgdisplaycreateuuidfromdisplayid");
}
}

View File

@@ -1,114 +0,0 @@
use std::{thread, time};
use simple_logger::SimpleLogger;
use winit::{
event::{Event, KeyboardInput, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Mode {
Wait,
WaitUntil,
Poll,
}
const WAIT_TIME: time::Duration = time::Duration::from_millis(100);
const POLL_SLEEP_TIME: time::Duration = time::Duration::from_millis(100);
fn main() {
SimpleLogger::new().init().unwrap();
println!("Press '1' to switch to Wait mode.");
println!("Press '2' to switch to WaitUntil mode.");
println!("Press '3' to switch to Poll mode.");
println!("Press 'R' to toggle request_redraw() calls.");
println!("Press 'Esc' to close the window.");
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title("Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.")
.build(&event_loop)
.unwrap();
let mut mode = Mode::Wait;
let mut request_redraw = false;
let mut wait_cancelled = false;
let mut close_requested = false;
event_loop.run(move |event, _, control_flow| {
use winit::event::{ElementState, StartCause, VirtualKeyCode};
println!("{:?}", event);
match event {
Event::NewEvents(start_cause) => {
wait_cancelled = match start_cause {
StartCause::WaitCancelled { .. } => mode == Mode::WaitUntil,
_ => false,
}
}
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => {
close_requested = true;
}
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(virtual_code),
state: ElementState::Pressed,
..
},
..
} => match virtual_code {
VirtualKeyCode::Key1 => {
mode = Mode::Wait;
println!("\nmode: {:?}\n", mode);
}
VirtualKeyCode::Key2 => {
mode = Mode::WaitUntil;
println!("\nmode: {:?}\n", mode);
}
VirtualKeyCode::Key3 => {
mode = Mode::Poll;
println!("\nmode: {:?}\n", mode);
}
VirtualKeyCode::R => {
request_redraw = !request_redraw;
println!("\nrequest_redraw: {}\n", request_redraw);
}
VirtualKeyCode::Escape => {
close_requested = true;
}
_ => (),
},
_ => (),
},
Event::MainEventsCleared => {
if request_redraw && !wait_cancelled && !close_requested {
window.request_redraw();
}
if close_requested {
*control_flow = ControlFlow::Exit;
}
}
Event::RedrawRequested(_window_id) => {}
Event::RedrawEventsCleared => {
*control_flow = match mode {
Mode::Wait => ControlFlow::Wait,
Mode::WaitUntil => {
if wait_cancelled {
*control_flow
} else {
ControlFlow::WaitUntil(instant::Instant::now() + WAIT_TIME)
}
}
Mode::Poll => {
thread::sleep(POLL_SLEEP_TIME);
ControlFlow::Poll
}
};
}
_ => (),
}
});
}

View File

@@ -1,89 +1,31 @@
use simple_logger::SimpleLogger;
use winit::{
event::{ElementState, Event, KeyboardInput, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::{CursorIcon, WindowBuilder},
};
extern crate winit;
use winit::{Event, ElementState, MouseCursor, WindowEvent};
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let events_loop = winit::EventsLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
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 mut cursor_idx = 0;
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
events_loop.run_forever(|event| {
match event {
Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
..
},
..
},
..
} => {
println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]);
window.set_cursor_icon(CURSORS[cursor_idx]);
if cursor_idx < CURSORS.len() - 1 {
Event::WindowEvent { event: WindowEvent::KeyboardInput(ElementState::Pressed, _, _, _), .. } => {
println!("Setting cursor to \"{:?}\"", cursors[cursor_idx]);
window.set_cursor(cursors[cursor_idx]);
if cursor_idx < cursors.len() - 1 {
cursor_idx += 1;
} else {
cursor_idx = 0;
}
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
*control_flow = ControlFlow::Exit;
return;
}
_ => (),
},
Event::WindowEvent { event: WindowEvent::Closed, .. } => {
events_loop.interrupt()
},
_ => ()
}
});
}
const CURSORS: &[CursorIcon] = &[
CursorIcon::Default,
CursorIcon::Crosshair,
CursorIcon::Hand,
CursorIcon::Arrow,
CursorIcon::Move,
CursorIcon::Text,
CursorIcon::Wait,
CursorIcon::Help,
CursorIcon::Progress,
CursorIcon::NotAllowed,
CursorIcon::ContextMenu,
CursorIcon::Cell,
CursorIcon::VerticalText,
CursorIcon::Alias,
CursorIcon::Copy,
CursorIcon::NoDrop,
CursorIcon::Grab,
CursorIcon::Grabbing,
CursorIcon::AllScroll,
CursorIcon::ZoomIn,
CursorIcon::ZoomOut,
CursorIcon::EResize,
CursorIcon::NResize,
CursorIcon::NeResize,
CursorIcon::NwResize,
CursorIcon::SResize,
CursorIcon::SeResize,
CursorIcon::SwResize,
CursorIcon::WResize,
CursorIcon::EwResize,
CursorIcon::NsResize,
CursorIcon::NeswResize,
CursorIcon::NwseResize,
CursorIcon::ColResize,
CursorIcon::RowResize,
];

View File

@@ -1,56 +0,0 @@
use simple_logger::SimpleLogger;
use winit::{
event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title("Super Cursor Grab'n'Hide Simulator 9000")
.build(&event_loop)
.unwrap();
let mut modifiers = ModifiersState::default();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Released,
virtual_keycode: Some(key),
..
},
..
} => {
use winit::event::VirtualKeyCode::*;
match key {
Escape => *control_flow = ControlFlow::Exit,
G => window.set_cursor_grab(!modifiers.shift()).unwrap(),
H => window.set_cursor_visible(modifiers.shift()),
_ => (),
}
}
WindowEvent::ModifiersChanged(m) => modifiers = m,
_ => (),
},
Event::DeviceEvent { event, .. } => match event {
DeviceEvent::MouseMotion { delta } => println!("mouse moved: {:?}", delta),
DeviceEvent::Button { button, state } => match state {
ElementState::Pressed => println!("mouse button {} pressed", button),
ElementState::Released => println!("mouse button {} released", button),
},
_ => (),
},
_ => (),
}
});
}

View File

@@ -1,53 +0,0 @@
#[cfg(not(target_arch = "wasm32"))]
fn main() {
use simple_logger::SimpleLogger;
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
#[derive(Debug, Clone, Copy)]
enum CustomEvent {
Timer,
}
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::<CustomEvent>::with_user_event();
let _window = WindowBuilder::new()
.with_title("A fantastic window!")
.build(&event_loop)
.unwrap();
// `EventLoopProxy` allows you to dispatch custom events to the main Winit event
// loop from any thread.
let event_loop_proxy = event_loop.create_proxy();
std::thread::spawn(move || {
// Wake up the `event_loop` once every second and dispatch a custom event
// from a different thread.
loop {
std::thread::sleep(std::time::Duration::from_secs(1));
event_loop_proxy.send_event(CustomEvent::Timer).ok();
}
});
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::UserEvent(event) => println!("user event: {:?}", event),
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => (),
}
});
}
#[cfg(target_arch = "wasm32")]
fn main() {
panic!("This example is not supported on web.");
}

View File

@@ -1,73 +0,0 @@
use simple_logger::SimpleLogger;
use winit::{
event::{
ElementState, Event, KeyboardInput, MouseButton, StartCause, VirtualKeyCode, WindowEvent,
},
event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder, WindowId},
};
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let window_1 = WindowBuilder::new().build(&event_loop).unwrap();
let window_2 = WindowBuilder::new().build(&event_loop).unwrap();
let mut switched = false;
let mut entered_id = window_2.id();
event_loop.run(move |event, _, control_flow| match event {
Event::NewEvents(StartCause::Init) => {
eprintln!("Switch which window is to be dragged by pressing \"x\".")
}
Event::WindowEvent { event, window_id } => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::MouseInput {
state: ElementState::Pressed,
button: MouseButton::Left,
..
} => {
let window = if (window_id == window_1.id() && switched)
|| (window_id == window_2.id() && !switched)
{
&window_2
} else {
&window_1
};
window.drag_window().unwrap()
}
WindowEvent::CursorEntered { .. } => {
entered_id = window_id;
name_windows(entered_id, switched, &window_1, &window_2)
}
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Released,
virtual_keycode: Some(VirtualKeyCode::X),
..
},
..
} => {
switched = !switched;
name_windows(entered_id, switched, &window_1, &window_2);
println!("Switched!")
}
_ => (),
},
_ => (),
});
}
fn name_windows(window_id: WindowId, switched: bool, window_1: &Window, window_2: &Window) {
let (drag_target, other) =
if (window_id == window_1.id() && switched) || (window_id == window_2.id() && !switched) {
(&window_2, &window_1)
} else {
(&window_1, &window_2)
};
drag_target.set_title("drag target");
other.set_title("winit window");
}

View File

@@ -1,118 +1,46 @@
use std::io::{stdin, stdout, Write};
extern crate winit;
use simple_logger::SimpleLogger;
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::monitor::{MonitorHandle, VideoMode};
use winit::window::{Fullscreen, WindowBuilder};
use std::io::{self, Write};
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
// enumerating monitors
let monitor = {
for (num, monitor) in winit::get_available_monitors().enumerate() {
println!("Monitor #{}: {:?}", num, monitor.get_name());
}
print!("Please choose the fullscreen mode: (1) exclusive, (2) borderless: ");
stdout().flush().unwrap();
print!("Please write the number of the monitor to use: ");
io::stdout().flush().unwrap();
let mut num = String::new();
stdin().read_line(&mut num).unwrap();
let num = num.trim().parse().ok().expect("Please enter a number");
let mut num = String::new();
io::stdin().read_line(&mut num).unwrap();
let num = num.trim().parse().ok().expect("Please enter a number");
let monitor = winit::get_available_monitors().nth(num).expect("Please enter a valid ID");
let fullscreen = Some(match num {
1 => Fullscreen::Exclusive(prompt_for_video_mode(&prompt_for_monitor(&event_loop))),
2 => Fullscreen::Borderless(Some(prompt_for_monitor(&event_loop))),
_ => panic!("Please enter a valid number"),
});
println!("Using {:?}", monitor.get_name());
let mut decorations = true;
monitor
};
let window = WindowBuilder::new()
let events_loop = winit::EventsLoop::new();
let _window = winit::WindowBuilder::new()
.with_title("Hello world!")
.with_fullscreen(fullscreen.clone())
.build(&event_loop)
.with_fullscreen(monitor)
.build(&events_loop)
.unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
events_loop.run_forever(|event| {
println!("{:?}", event);
match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(virtual_code),
state,
..
},
..
} => match (virtual_code, state) {
(VirtualKeyCode::Escape, _) => *control_flow = ControlFlow::Exit,
(VirtualKeyCode::F, ElementState::Pressed) => {
if window.fullscreen().is_some() {
window.set_fullscreen(None);
} else {
window.set_fullscreen(fullscreen.clone());
}
}
(VirtualKeyCode::S, ElementState::Pressed) => {
println!("window.fullscreen {:?}", window.fullscreen());
}
(VirtualKeyCode::M, ElementState::Pressed) => {
let is_maximized = window.is_maximized();
window.set_maximized(!is_maximized);
}
(VirtualKeyCode::D, ElementState::Pressed) => {
decorations = !decorations;
window.set_decorations(decorations);
}
_ => (),
},
_ => (),
winit::Event::WindowEvent { event, .. } => {
match event {
winit::WindowEvent::Closed => events_loop.interrupt(),
winit::WindowEvent::KeyboardInput(_, _, Some(winit::VirtualKeyCode::Escape), _) => events_loop.interrupt(),
_ => ()
}
},
_ => {}
}
});
}
// Enumerate monitors and prompt user to choose one
fn prompt_for_monitor(event_loop: &EventLoop<()>) -> MonitorHandle {
for (num, monitor) in event_loop.available_monitors().enumerate() {
println!("Monitor #{}: {:?}", num, monitor.name());
}
print!("Please write the number of the monitor to use: ");
stdout().flush().unwrap();
let mut num = String::new();
stdin().read_line(&mut num).unwrap();
let num = num.trim().parse().ok().expect("Please enter a number");
let monitor = event_loop
.available_monitors()
.nth(num)
.expect("Please enter a valid ID");
println!("Using {:?}", monitor.name());
monitor
}
fn prompt_for_video_mode(monitor: &MonitorHandle) -> VideoMode {
for (i, video_mode) in monitor.video_modes().enumerate() {
println!("Video mode #{}: {}", i, video_mode);
}
print!("Please write the number of the video mode to use: ");
stdout().flush().unwrap();
let mut num = String::new();
stdin().read_line(&mut num).unwrap();
let num = num.trim().parse().ok().expect("Please enter a number");
let video_mode = monitor
.video_modes()
.nth(num)
.expect("Please enter a valid ID");
println!("Using {}", video_mode);
video_mode
}

42
examples/grabbing.rs Normal file
View File

@@ -0,0 +1,42 @@
extern crate winit;
use winit::{WindowEvent, ElementState};
fn main() {
let 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(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::Closed => events_loop.interrupt(),
a @ WindowEvent::MouseMoved(_, _) => {
println!("{:?}", a);
},
_ => (),
}
},
}
});
}

View File

@@ -1,84 +0,0 @@
use simple_logger::SimpleLogger;
use winit::{
event::{Event, KeyboardInput, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let _window = WindowBuilder::new()
.with_title("Your faithful window")
.build(&event_loop)
.unwrap();
let mut close_requested = false;
event_loop.run(move |event, _, control_flow| {
use winit::event::{
ElementState::Released,
VirtualKeyCode::{N, Y},
};
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent { event, .. } => {
match event {
WindowEvent::CloseRequested => {
// `CloseRequested` is sent when the close button on the window is pressed (or
// through whatever other mechanisms the window manager provides for closing a
// window). If you don't handle this event, the close button won't actually do
// anything.
// A common thing to do here is prompt the user if they have unsaved work.
// Creating a proper dialog box for that is far beyond the scope of this
// example, so here we'll just respond to the Y and N keys.
println!("Are you ready to bid your window farewell? [Y/N]");
close_requested = true;
// In applications where you can safely close the window without further
// action from the user, this is generally where you'd handle cleanup before
// closing the window. How to close the window is detailed in the handler for
// the Y key.
}
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(virtual_code),
state: Released,
..
},
..
} => {
match virtual_code {
Y => {
if close_requested {
// This is where you'll want to do any cleanup you need.
println!("Buh-bye!");
// For a single-window application like this, you'd normally just
// break out of the event loop here. If you wanted to keep running the
// event loop (i.e. if it's a multi-window application), you need to
// drop the window. That closes it, and results in `Destroyed` being
// sent.
*control_flow = ControlFlow::Exit;
}
}
N => {
if close_requested {
println!("Your window will continue to stay by your side.");
close_requested = false;
}
}
_ => (),
}
}
_ => (),
}
}
_ => (),
}
});
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -1,30 +1,20 @@
use simple_logger::SimpleLogger;
use winit::{
dpi::LogicalSize,
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
extern crate winit;
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let events_loop = winit::EventsLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
let _window = winit::WindowBuilder::new()
.with_min_dimensions(400, 200)
.with_max_dimensions(800, 400)
.build(&events_loop)
.unwrap();
window.set_min_inner_size(Some(LogicalSize::new(400.0, 200.0)));
window.set_max_inner_size(Some(LogicalSize::new(800.0, 400.0)));
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
events_loop.run_forever(|event| {
println!("{:?}", event);
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => (),
winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => events_loop.interrupt(),
_ => ()
}
});
}

View File

@@ -1,41 +0,0 @@
extern crate winit;
use simple_logger::SimpleLogger;
use winit::event::{Event, VirtualKeyCode, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.build(&event_loop)
.unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
// Keyboard input event to handle minimize via a hotkey
Event::WindowEvent {
event: WindowEvent::KeyboardInput { input, .. },
window_id,
} => {
if window_id == window.id() {
// Pressing the 'M' key will minimize the window
if input.virtual_keycode == Some(VirtualKeyCode::M) {
window.set_minimized(true);
}
}
}
_ => (),
}
});
}

View File

@@ -1,11 +0,0 @@
use simple_logger::SimpleLogger;
use winit::{event_loop::EventLoop, window::WindowBuilder};
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
dbg!(window.available_monitors().collect::<Vec<_>>());
dbg!(window.primary_monitor());
}

View File

@@ -1,48 +0,0 @@
use simple_logger::SimpleLogger;
use winit::{
event::{DeviceEvent, Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title("Mouse Wheel events")
.build(&event_loop)
.unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
_ => (),
},
Event::DeviceEvent { event, .. } => match event {
DeviceEvent::MouseWheel { delta } => match delta {
winit::event::MouseScrollDelta::LineDelta(x, y) => {
println!("mouse wheel Line Delta: ({},{})", x, y);
let pixels_per_line = 120.0;
let mut pos = window.outer_position().unwrap();
pos.x -= (x * pixels_per_line) as i32;
pos.y -= (y * pixels_per_line) as i32;
window.set_outer_position(pos)
}
winit::event::MouseScrollDelta::PixelDelta(p) => {
println!("mouse wheel Pixel Delta: ({},{})", p.x, p.y);
let mut pos = window.outer_position().unwrap();
pos.x -= p.x as i32;
pos.y -= p.y as i32;
window.set_outer_position(pos)
}
},
_ => (),
},
_ => (),
}
});
}

View File

@@ -1,184 +0,0 @@
#[cfg(not(target_arch = "wasm32"))]
fn main() {
use std::{collections::HashMap, sync::mpsc, thread, time::Duration};
use simple_logger::SimpleLogger;
use winit::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::{CursorIcon, Fullscreen, WindowBuilder},
};
const WINDOW_COUNT: usize = 3;
const WINDOW_SIZE: PhysicalSize<u32> = PhysicalSize::new(600, 400);
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let mut window_senders = HashMap::with_capacity(WINDOW_COUNT);
for _ in 0..WINDOW_COUNT {
let window = WindowBuilder::new()
.with_inner_size(WINDOW_SIZE)
.build(&event_loop)
.unwrap();
let mut video_modes: Vec<_> = window.current_monitor().unwrap().video_modes().collect();
let mut video_mode_id = 0usize;
let (tx, rx) = mpsc::channel();
window_senders.insert(window.id(), tx);
thread::spawn(move || {
while let Ok(event) = rx.recv() {
match event {
WindowEvent::Moved { .. } => {
// We need to update our chosen video mode if the window
// was moved to an another monitor, so that the window
// appears on this monitor instead when we go fullscreen
let previous_video_mode = video_modes.iter().cloned().nth(video_mode_id);
video_modes = window.current_monitor().unwrap().video_modes().collect();
video_mode_id = video_mode_id.min(video_modes.len());
let video_mode = video_modes.iter().nth(video_mode_id);
// Different monitors may support different video modes,
// and the index we chose previously may now point to a
// completely different video mode, so notify the user
if video_mode != previous_video_mode.as_ref() {
println!(
"Window moved to another monitor, picked video mode: {}",
video_modes.iter().nth(video_mode_id).unwrap()
);
}
}
#[allow(deprecated)]
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Released,
virtual_keycode: Some(key),
modifiers,
..
},
..
} => {
window.set_title(&format!("{:?}", key));
let state = !modifiers.shift();
use VirtualKeyCode::*;
match key {
A => window.set_always_on_top(state),
C => window.set_cursor_icon(match state {
true => CursorIcon::Progress,
false => CursorIcon::Default,
}),
D => window.set_decorations(!state),
// Cycle through video modes
Right | Left => {
video_mode_id = match key {
Left => video_mode_id.saturating_sub(1),
Right => (video_modes.len() - 1).min(video_mode_id + 1),
_ => unreachable!(),
};
println!(
"Picking video mode: {}",
video_modes.iter().nth(video_mode_id).unwrap()
);
}
F => window.set_fullscreen(match (state, modifiers.alt()) {
(true, false) => Some(Fullscreen::Borderless(None)),
(true, true) => Some(Fullscreen::Exclusive(
video_modes.iter().nth(video_mode_id).unwrap().clone(),
)),
(false, _) => None,
}),
G => window.set_cursor_grab(state).unwrap(),
H => window.set_cursor_visible(!state),
I => {
println!("Info:");
println!("-> outer_position : {:?}", window.outer_position());
println!("-> inner_position : {:?}", window.inner_position());
println!("-> outer_size : {:?}", window.outer_size());
println!("-> inner_size : {:?}", window.inner_size());
println!("-> fullscreen : {:?}", window.fullscreen());
}
L => window.set_min_inner_size(match state {
true => Some(WINDOW_SIZE),
false => None,
}),
M => window.set_maximized(state),
P => window.set_outer_position({
let mut position = window.outer_position().unwrap();
let sign = if state { 1 } else { -1 };
position.x += 10 * sign;
position.y += 10 * sign;
position
}),
Q => window.request_redraw(),
R => window.set_resizable(state),
S => window.set_inner_size(match state {
true => PhysicalSize::new(
WINDOW_SIZE.width + 100,
WINDOW_SIZE.height + 100,
),
false => WINDOW_SIZE,
}),
W => {
if let Size::Physical(size) = WINDOW_SIZE.into() {
window
.set_cursor_position(Position::Physical(
PhysicalPosition::new(
size.width as i32 / 2,
size.height as i32 / 2,
),
))
.unwrap()
}
}
Z => {
window.set_visible(false);
thread::sleep(Duration::from_secs(1));
window.set_visible(true);
}
_ => (),
}
}
_ => (),
}
}
});
}
event_loop.run(move |event, _event_loop, control_flow| {
*control_flow = match !window_senders.is_empty() {
true => ControlFlow::Wait,
false => ControlFlow::Exit,
};
match event {
Event::WindowEvent { event, window_id } => match event {
WindowEvent::CloseRequested
| WindowEvent::Destroyed
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Released,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => {
window_senders.remove(&window_id);
}
_ => {
if let Some(tx) = window_senders.get(&window_id) {
if let Some(event) = event.to_static() {
tx.send(event).unwrap();
}
}
}
},
_ => (),
}
})
}
#[cfg(target_arch = "wasm32")]
fn main() {
panic!("Example not supported on Wasm");
}

View File

@@ -1,52 +1,32 @@
use std::collections::HashMap;
use simple_logger::SimpleLogger;
use winit::{
event::{ElementState, Event, KeyboardInput, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::Window,
};
extern crate winit;
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let events_loop = winit::EventsLoop::new();
let mut windows = HashMap::new();
for _ in 0..3 {
let window = Window::new(&event_loop).unwrap();
windows.insert(window.id(), window);
}
let window1 = winit::Window::new(&events_loop).unwrap();
let window2 = winit::Window::new(&events_loop).unwrap();
let window3 = winit::Window::new(&events_loop).unwrap();
event_loop.run(move |event, event_loop, control_flow| {
*control_flow = ControlFlow::Wait;
let mut num_windows = 3;
events_loop.run_forever(|event| {
match event {
Event::WindowEvent { event, window_id } => {
match event {
WindowEvent::CloseRequested => {
println!("Window {:?} has received the signal to close", window_id);
// This drops the window, causing it to close.
windows.remove(&window_id);
if windows.is_empty() {
*control_flow = ControlFlow::Exit;
}
}
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
..
},
..
} => {
let window = Window::new(&event_loop).unwrap();
windows.insert(window.id(), window);
}
_ => (),
winit::Event::WindowEvent { event: winit::WindowEvent::Closed, window_id } => {
if window_id == window1.id() {
println!("Window 1 has been closed")
} else if window_id == window2.id() {
println!("Window 2 has been closed")
} else if window_id == window3.id() {
println!("Window 3 has been closed");
} else {
unreachable!()
}
}
num_windows -= 1;
if num_windows == 0 {
events_loop.interrupt();
}
},
_ => (),
}
})

View File

@@ -1,39 +0,0 @@
use simple_logger::SimpleLogger;
use winit::{
event::{ElementState, Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.build(&event_loop)
.unwrap();
event_loop.run(move |event, _, control_flow| {
println!("{:?}", event);
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::MouseInput {
state: ElementState::Released,
..
} => {
window.request_redraw();
}
_ => (),
},
Event::RedrawRequested(_) => {
println!("\nredrawing!\n");
}
_ => (),
}
});
}

View File

@@ -1,46 +0,0 @@
use std::{thread, time};
use simple_logger::SimpleLogger;
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
#[cfg(not(target_arch = "wasm32"))]
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.build(&event_loop)
.unwrap();
thread::spawn(move || loop {
thread::sleep(time::Duration::from_secs(1));
window.request_redraw();
});
event_loop.run(move |event, _, control_flow| {
println!("{:?}", event);
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
_ => (),
},
Event::RedrawRequested(_) => {
println!("\nredrawing!\n");
}
_ => (),
}
});
}
#[cfg(target_arch = "wasm32")]
fn main() {
unimplemented!() // `Window` can't be sent between threads
}

View File

@@ -1,46 +0,0 @@
use simple_logger::SimpleLogger;
use winit::{
dpi::LogicalSize,
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let mut resizable = false;
let window = WindowBuilder::new()
.with_title("Hit space to toggle resizability.")
.with_inner_size(LogicalSize::new(400.0, 200.0))
.with_resizable(resizable)
.build(&event_loop)
.unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Space),
state: ElementState::Released,
..
},
..
} => {
resizable = !resizable;
println!("Resizable: {}", resizable);
window.set_resizable(resizable);
}
_ => (),
},
_ => (),
};
});
}

View File

@@ -1,54 +0,0 @@
use simple_logger::SimpleLogger;
use winit::{
dpi::PhysicalPosition,
event::{ElementState, Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
window.set_title("A fantastic window!");
println!("Ime position will system default");
println!("Click to set ime position to cursor's");
let mut cursor_position = PhysicalPosition::new(0.0, 0.0);
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent {
event: WindowEvent::CursorMoved { position, .. },
..
} => {
cursor_position = position;
}
Event::WindowEvent {
event:
WindowEvent::MouseInput {
state: ElementState::Released,
..
},
..
} => {
println!(
"Setting ime position to {}, {}",
cursor_position.x, cursor_position.y
);
window.set_ime_position(cursor_position);
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
*control_flow = ControlFlow::Exit;
return;
}
_ => (),
}
});
}

View File

@@ -1,40 +0,0 @@
use instant::Instant;
use std::time::Duration;
use simple_logger::SimpleLogger;
use winit::{
event::{Event, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let _window = WindowBuilder::new()
.with_title("A fantastic window!")
.build(&event_loop)
.unwrap();
let timer_length = Duration::new(1, 0);
event_loop.run(move |event, _, control_flow| {
println!("{:?}", event);
match event {
Event::NewEvents(StartCause::Init) => {
*control_flow = ControlFlow::WaitUntil(Instant::now() + timer_length)
}
Event::NewEvents(StartCause::ResumeTimeReached { .. }) => {
*control_flow = ControlFlow::WaitUntil(Instant::now() + timer_length);
println!("\nTimer\n");
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => (),
}
});
}

View File

@@ -1,32 +1,20 @@
use simple_logger::SimpleLogger;
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
extern crate winit;
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let events_loop = winit::EventsLoop::new();
let window = WindowBuilder::new()
.with_decorations(false)
.with_transparent(true)
.build(&event_loop)
.unwrap();
let window = winit::WindowBuilder::new().with_decorations(false)
.with_transparency(true)
.build(&events_loop).unwrap();
window.set_title("A fantastic window!");
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
events_loop.run_forever(|event| {
println!("{:?}", event);
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => (),
winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => events_loop.interrupt(),
_ => ()
}
});
}

View File

@@ -1,20 +0,0 @@
use simple_logger::SimpleLogger;
use winit::event_loop::EventLoop;
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let monitor = match event_loop.primary_monitor() {
Some(monitor) => monitor,
None => {
println!("No primary monitor detected.");
return;
}
};
println!("Listing available video modes:");
for mode in monitor.video_modes() {
println!("{}", mode);
}
}

View File

@@ -1,58 +0,0 @@
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
pub fn main() {
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.build(&event_loop)
.unwrap();
#[cfg(target_arch = "wasm32")]
{
use winit::platform::web::WindowExtWebSys;
let canvas = window.canvas();
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let body = document.body().unwrap();
body.append_child(&canvas)
.expect("Append canvas to HTML body");
}
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
#[cfg(target_arch = "wasm32")]
log::debug!("{:?}", event);
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
Event::MainEventsCleared => {
window.request_redraw();
}
_ => (),
}
});
}
#[cfg(target_arch = "wasm32")]
mod wasm {
use wasm_bindgen::prelude::*;
#[wasm_bindgen(start)]
pub fn run() {
console_log::init_with_level(log::Level::Debug);
super::main();
}
}

View File

@@ -1,33 +1,19 @@
use simple_logger::SimpleLogger;
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
extern crate winit;
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let events_loop = winit::EventsLoop::new();
let window = WindowBuilder::new()
let window = winit::WindowBuilder::new()
.with_title("A fantastic window!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.build(&event_loop)
.build(&events_loop)
.unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
events_loop.run_forever(|event| {
println!("{:?}", event);
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
Event::MainEventsCleared => {
window.request_redraw();
}
_ => (),
winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => events_loop.interrupt(),
_ => ()
}
});
}

View File

@@ -1,125 +0,0 @@
// This example is used by developers to test various window functions.
use simple_logger::SimpleLogger;
use winit::{
dpi::{LogicalSize, PhysicalSize},
event::{DeviceEvent, ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::{Fullscreen, WindowBuilder},
};
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.with_inner_size(LogicalSize::new(100.0, 100.0))
.build(&event_loop)
.unwrap();
eprintln!("debugging keys:");
eprintln!(" (E) Enter exclusive fullscreen");
eprintln!(" (F) Toggle borderless fullscreen");
eprintln!(" (P) Toggle borderless fullscreen on system's preffered monitor");
eprintln!(" (M) Toggle minimized");
eprintln!(" (Q) Quit event loop");
eprintln!(" (V) Toggle visibility");
eprintln!(" (X) Toggle maximized");
let mut minimized = false;
let mut visible = true;
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::DeviceEvent {
event:
DeviceEvent::Key(KeyboardInput {
virtual_keycode: Some(key),
state: ElementState::Pressed,
..
}),
..
} => match key {
VirtualKeyCode::M => {
if minimized {
minimized = !minimized;
window.set_minimized(minimized);
}
}
VirtualKeyCode::V => {
if !visible {
visible = !visible;
window.set_visible(visible);
}
}
_ => (),
},
Event::WindowEvent {
event: WindowEvent::KeyboardInput { input, .. },
..
} => match input {
KeyboardInput {
virtual_keycode: Some(key),
state: ElementState::Pressed,
..
} => match key {
VirtualKeyCode::E => {
fn area(size: PhysicalSize<u32>) -> u32 {
size.width * size.height
}
let monitor = window.current_monitor().unwrap();
if let Some(mode) = monitor
.video_modes()
.max_by(|a, b| area(a.size()).cmp(&area(b.size())))
{
window.set_fullscreen(Some(Fullscreen::Exclusive(mode)));
} else {
eprintln!("no video modes available");
}
}
VirtualKeyCode::F => {
if window.fullscreen().is_some() {
window.set_fullscreen(None);
} else {
let monitor = window.current_monitor();
window.set_fullscreen(Some(Fullscreen::Borderless(monitor)));
}
}
VirtualKeyCode::P => {
if window.fullscreen().is_some() {
window.set_fullscreen(None);
} else {
window.set_fullscreen(Some(Fullscreen::Borderless(None)));
}
}
VirtualKeyCode::M => {
minimized = !minimized;
window.set_minimized(minimized);
}
VirtualKeyCode::Q => {
*control_flow = ControlFlow::Exit;
}
VirtualKeyCode::V => {
visible = !visible;
window.set_visible(visible);
}
VirtualKeyCode::X => {
let is_maximized = window.is_maximized();
window.set_maximized(!is_maximized);
}
_ => (),
},
_ => (),
},
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
_ => (),
}
});
}

View File

@@ -1,58 +0,0 @@
extern crate image;
use std::path::Path;
use simple_logger::SimpleLogger;
use winit::{
event::Event,
event_loop::{ControlFlow, EventLoop},
window::{Icon, WindowBuilder},
};
fn main() {
SimpleLogger::new().init().unwrap();
// You'll have to choose an icon size at your own discretion. On X11, the desired size varies
// by WM, and on Windows, you still have to account for screen scaling. Here we use 32px,
// since it seems to work well enough in most cases. Be careful about going too high, or
// you'll be bitten by the low-quality downscaling built into the WM.
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/icon.png");
let icon = load_icon(Path::new(path));
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title("An iconic window!")
// At present, this only does anything on Windows and X11, so if you want to save load
// time, you can put icon loading behind a function that returns `None` on other platforms.
.with_window_icon(Some(icon))
.build(&event_loop)
.unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
if let Event::WindowEvent { event, .. } = event {
use winit::event::WindowEvent::*;
match event {
CloseRequested => *control_flow = ControlFlow::Exit,
DroppedFile(path) => {
window.set_window_icon(Some(load_icon(&path)));
}
_ => (),
}
}
});
}
fn load_icon(path: &Path) -> Icon {
let (icon_rgba, icon_width, icon_height) = {
let image = image::open(path)
.expect("Failed to open icon path")
.into_rgba8();
let (width, height) = image.dimensions();
let rgba = image.into_raw();
(rgba, width, height)
};
Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
}

View File

@@ -1,63 +0,0 @@
// Limit this example to only compatible platforms.
#[cfg(any(
target_os = "windows",
target_os = "macos",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
fn main() {
use std::{thread::sleep, time::Duration};
use simple_logger::SimpleLogger;
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
platform::run_return::EventLoopExtRunReturn,
window::WindowBuilder,
};
let mut event_loop = EventLoop::new();
SimpleLogger::new().init().unwrap();
let _window = WindowBuilder::new()
.with_title("A fantastic window!")
.build(&event_loop)
.unwrap();
let mut quit = false;
while !quit {
event_loop.run_return(|event, _, control_flow| {
*control_flow = ControlFlow::Wait;
if let Event::WindowEvent { event, .. } = &event {
// Print only Window events to reduce noise
println!("{:?}", event);
}
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
quit = true;
}
Event::MainEventsCleared => {
*control_flow = ControlFlow::Exit;
}
_ => (),
}
});
// Sleep for 1/60 second to simulate rendering
println!("rendering");
sleep(Duration::from_millis(16));
}
}
#[cfg(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))]
fn main() {
println!("This platform doesn't support run_return.");
}

View File

@@ -1,3 +0,0 @@
force_explicit_abi=true
use_field_init_shorthand=true
# merge_imports=true

101
src/api_transition.rs Normal file
View File

@@ -0,0 +1,101 @@
//! This temporary module generates types that wrap around the old API (winit v5 and below) and
//! expose the new API (winit v6 and above).
//!
//! This is temporary so that existing backends can smoothly transition. After all implementations
//! have finished transitionning, this module should disappear.
macro_rules! gen_api_transition {
() => {
pub struct EventsLoop {
windows: ::std::sync::Mutex<Vec<::std::sync::Arc<Window>>>,
interrupted: ::std::sync::atomic::AtomicBool,
}
impl EventsLoop {
pub fn new() -> EventsLoop {
EventsLoop {
windows: ::std::sync::Mutex::new(vec![]),
interrupted: ::std::sync::atomic::AtomicBool::new(false),
}
}
pub fn interrupt(&self) {
self.interrupted.store(true, ::std::sync::atomic::Ordering::Relaxed);
}
pub fn poll_events<F>(&self, mut callback: F)
where F: FnMut(::Event)
{
let mut windows = self.windows.lock().unwrap();
for window in windows.iter() {
for event in window.poll_events() {
callback(::Event::WindowEvent {
window_id: ::WindowId(WindowId(&**window as *const Window as usize)),
event: event,
})
}
}
}
pub fn run_forever<F>(&self, mut callback: F)
where F: FnMut(::Event)
{
self.interrupted.store(false, ::std::sync::atomic::Ordering::Relaxed);
// Yeah that's a very bad implementation.
loop {
self.poll_events(|e| callback(e));
::std::thread::sleep_ms(5);
if self.interrupted.load(::std::sync::atomic::Ordering::Relaxed) {
break;
}
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId(usize);
pub struct Window2 {
pub window: ::std::sync::Arc<Window>,
events_loop: ::std::sync::Weak<EventsLoop>,
}
impl ::std::ops::Deref for Window2 {
type Target = Window;
#[inline]
fn deref(&self) -> &Window {
&*self.window
}
}
impl Window2 {
pub fn new(events_loop: ::std::sync::Arc<EventsLoop>, window: &::WindowAttributes,
pl_attribs: &PlatformSpecificWindowBuilderAttributes)
-> Result<Window2, CreationError>
{
let win = ::std::sync::Arc::new(try!(Window::new(window, pl_attribs)));
events_loop.windows.lock().unwrap().push(win.clone());
Ok(Window2 {
window: win,
events_loop: ::std::sync::Arc::downgrade(&events_loop),
})
}
#[inline]
pub fn id(&self) -> WindowId {
WindowId(&*self.window as *const Window as usize)
}
}
impl Drop for Window2 {
fn drop(&mut self) {
if let Some(ev) = self.events_loop.upgrade() {
let mut windows = ev.windows.lock().unwrap();
windows.retain(|w| &**w as *const Window != &*self.window as *const _);
}
}
}
};
}

View File

@@ -1,568 +0,0 @@
//! UI scaling is important, so read the docs for this module if you don't want to be confused.
//!
//! ## Why should I care about UI scaling?
//!
//! Modern computer screens don't have a consistent relationship between resolution and size.
//! 1920x1080 is a common resolution for both desktop and mobile screens, despite mobile screens
//! normally being less than a quarter the size of their desktop counterparts. What's more, neither
//! desktop nor mobile screens are consistent resolutions within their own size classes - common
//! mobile screens range from below 720p to above 1440p, and desktop screens range from 720p to 5K
//! and beyond.
//!
//! Given that, it's a mistake to assume that 2D content will only be displayed on screens with
//! a consistent pixel density. If you were to render a 96-pixel-square image on a 1080p screen,
//! then render the same image on a similarly-sized 4K screen, the 4K rendition would only take up
//! about a quarter of the physical space as it did on the 1080p screen. That issue is especially
//! problematic with text rendering, where quarter-sized text becomes a significant legibility
//! problem.
//!
//! Failure to account for the scale factor can create a significantly 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 having an existential crisis. Once users
//! enter that state, they will no longer be focused on your application.
//!
//! ## How should I handle it?
//!
//! The solution to this problem is to account for the device's *scale factor*. The scale factor is
//! the factor UI elements should be scaled by to be consistent with the rest of the user's system -
//! for example, a button that's normally 50 pixels across would be 100 pixels across on a device
//! with a scale factor of `2.0`, or 75 pixels across with a scale factor of `1.5`.
//!
//! Many UI systems, such as CSS, expose DPI-dependent units like [points] or [picas]. That's
//! usually a mistake, since there's no consistent mapping between the scale factor and the screen's
//! actual DPI. Unless you're printing to a physical medium, you should work in scaled pixels rather
//! than any DPI-dependent units.
//!
//! ### Position and Size types
//!
//! Winit's `Physical(Position|Size)` types correspond with the actual pixels on the device, and the
//! `Logical(Position|Size)` types correspond to the physical pixels divided by the scale factor.
//! All of Winit's functions return physical types, but can take either logical or physical
//! coordinates as input, allowing you to use the most convenient coordinate system for your
//! particular application.
//!
//! Winit's position and size types types are generic over their exact pixel type, `P`, to allow the
//! API to have integer precision where appropriate (e.g. most window manipulation functions) and
//! floating precision when necessary (e.g. logical sizes for fractional scale factors and touch
//! input). If `P` is a floating-point type, please do not cast the values with `as {int}`. Doing so
//! will truncate the fractional part of the float, rather than properly round to the nearest
//! integer. Use the provided `cast` function or `From`/`Into` conversions, which handle the
//! rounding properly. Note that precision loss will still occur when rounding from a float to an
//! int, although rounding lessens the problem.
//!
//! ### Events
//!
//! Winit will dispatch a [`ScaleFactorChanged`](crate::event::WindowEvent::ScaleFactorChanged)
//! event whenever a window's scale factor has changed. This can happen if the user drags their
//! window from a standard-resolution monitor to a high-DPI monitor, or if the user changes their
//! DPI settings. This gives you a chance to rescale your application's UI elements and adjust how
//! the platform changes the window's size to reflect the new scale factor. If a window hasn't
//! received a [`ScaleFactorChanged`](crate::event::WindowEvent::ScaleFactorChanged) event,
//! then its scale factor can be found by calling [window.scale_factor()].
//!
//! ## How is the scale factor calculated?
//!
//! Scale factor is calculated differently on different platforms:
//!
//! - **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" scale factors, i.e. 1.0, 1.25, 1.5... on Windows 7, the scale factor is
//! global and changing it requires logging out. See [this article][windows_1] for technical
//! details.
//! - **macOS:** Recent versions of macOS allow the user to change the scaling factor for certain
//! displays. When this is available, the user may pick a per-monitor scaling factor from a set
//! of pre-defined settings. All "retina displays" have a scaling factor above 1.0 by default but
//! the specific value varies across devices.
//! - **X11:** Many man-hours have been spent trying to figure out how to handle DPI in X11. Winit
//! currently uses a three-pronged approach:
//! + Use the value in the `WINIT_X11_SCALE_FACTOR` environment variable, if present.
//! + If not present, use the value set in `Xft.dpi` in Xresources.
//! + Otherwise, calcuate the scale factor based on the millimeter monitor dimensions provided by XRandR.
//!
//! If `WINIT_X11_SCALE_FACTOR` is set to `randr`, it'll ignore the `Xft.dpi` field and use the
//! XRandR scaling method. Generally speaking, you should try to configure the standard system
//! variables to do what you want before resorting to `WINIT_X11_SCALE_FACTOR`.
//! - **Wayland:** On Wayland, scale factors are set per-screen by the server, and are always
//! integers (most often 1 or 2).
//! - **iOS:** Scale factors are set by Apple to the value that best suits the device, and range
//! from `1.0` to `3.0`. See [this article][apple_1] and [this article][apple_2] for more
//! information.
//! - **Android:** Scale factors are set by the manufacturer to the value that best suits the
//! device, and range from `1.0` to `4.0`. See [this article][android_1] for more information.
//! - **Web:** The scale factor is the ratio between CSS pixels and the physical device pixels.
//! In other words, it is the value of [`window.devicePixelRatio`][web_1]. It is affected by
//! both the screen scaling and the browser zoom level and can go below `1.0`.
//!
//! [points]: https://en.wikipedia.org/wiki/Point_(typography)
//! [picas]: https://en.wikipedia.org/wiki/Pica_(typography)
//! [window.scale_factor()]: crate::window::Window::scale_factor
//! [windows_1]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows
//! [apple_1]: https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html
//! [apple_2]: https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/image-size-and-resolution/
//! [android_1]: https://developer.android.com/training/multiscreen/screendensities
//! [web_1]: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio
pub trait Pixel: Copy + Into<f64> {
fn from_f64(f: f64) -> Self;
fn cast<P: Pixel>(self) -> P {
P::from_f64(self.into())
}
}
impl Pixel for u8 {
fn from_f64(f: f64) -> Self {
f.round() as u8
}
}
impl Pixel for u16 {
fn from_f64(f: f64) -> Self {
f.round() as u16
}
}
impl Pixel for u32 {
fn from_f64(f: f64) -> Self {
f.round() as u32
}
}
impl Pixel for i8 {
fn from_f64(f: f64) -> Self {
f.round() as i8
}
}
impl Pixel for i16 {
fn from_f64(f: f64) -> Self {
f.round() as i16
}
}
impl Pixel for i32 {
fn from_f64(f: f64) -> Self {
f.round() as i32
}
}
impl Pixel for f32 {
fn from_f64(f: f64) -> Self {
f as f32
}
}
impl Pixel for f64 {
fn from_f64(f: f64) -> Self {
f
}
}
/// Checks that the scale factor is a normal positive `f64`.
///
/// All functions that take a scale factor assert that this will return `true`. If you're sourcing scale 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_scale_factor(scale_factor: f64) -> bool {
scale_factor.is_sign_positive() && scale_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, Eq, PartialEq, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LogicalPosition<P> {
pub x: P,
pub y: P,
}
impl<P> LogicalPosition<P> {
#[inline]
pub const fn new(x: P, y: P) -> Self {
LogicalPosition { x, y }
}
}
impl<P: Pixel> LogicalPosition<P> {
#[inline]
pub fn from_physical<T: Into<PhysicalPosition<X>>, X: Pixel>(
physical: T,
scale_factor: f64,
) -> Self {
physical.into().to_logical(scale_factor)
}
#[inline]
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalPosition<X> {
assert!(validate_scale_factor(scale_factor));
let x = self.x.into() * scale_factor;
let y = self.y.into() * scale_factor;
PhysicalPosition::new(x, y).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> LogicalPosition<X> {
LogicalPosition {
x: self.x.cast(),
y: self.y.cast(),
}
}
}
impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalPosition<P> {
fn from((x, y): (X, X)) -> LogicalPosition<P> {
LogicalPosition::new(x.cast(), y.cast())
}
}
impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for (X, X) {
fn from(p: LogicalPosition<P>) -> (X, X) {
(p.x.cast(), p.y.cast())
}
}
impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalPosition<P> {
fn from([x, y]: [X; 2]) -> LogicalPosition<P> {
LogicalPosition::new(x.cast(), y.cast())
}
}
impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for [X; 2] {
fn from(p: LogicalPosition<P>) -> [X; 2] {
[p.x.cast(), p.y.cast()]
}
}
#[cfg(feature = "mint")]
impl<P: Pixel> From<mint::Point2<P>> for LogicalPosition<P> {
fn from(p: mint::Point2<P>) -> Self {
Self::new(p.x, p.y)
}
}
#[cfg(feature = "mint")]
impl<P: Pixel> From<LogicalPosition<P>> for mint::Point2<P> {
fn from(p: LogicalPosition<P>) -> Self {
mint::Point2 { x: p.x, y: p.y }
}
}
/// A position represented in physical pixels.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PhysicalPosition<P> {
pub x: P,
pub y: P,
}
impl<P> PhysicalPosition<P> {
#[inline]
pub const fn new(x: P, y: P) -> Self {
PhysicalPosition { x, y }
}
}
impl<P: Pixel> PhysicalPosition<P> {
#[inline]
pub fn from_logical<T: Into<LogicalPosition<X>>, X: Pixel>(
logical: T,
scale_factor: f64,
) -> Self {
logical.into().to_physical(scale_factor)
}
#[inline]
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalPosition<X> {
assert!(validate_scale_factor(scale_factor));
let x = self.x.into() / scale_factor;
let y = self.y.into() / scale_factor;
LogicalPosition::new(x, y).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalPosition<X> {
PhysicalPosition {
x: self.x.cast(),
y: self.y.cast(),
}
}
}
impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalPosition<P> {
fn from((x, y): (X, X)) -> PhysicalPosition<P> {
PhysicalPosition::new(x.cast(), y.cast())
}
}
impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for (X, X) {
fn from(p: PhysicalPosition<P>) -> (X, X) {
(p.x.cast(), p.y.cast())
}
}
impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalPosition<P> {
fn from([x, y]: [X; 2]) -> PhysicalPosition<P> {
PhysicalPosition::new(x.cast(), y.cast())
}
}
impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for [X; 2] {
fn from(p: PhysicalPosition<P>) -> [X; 2] {
[p.x.cast(), p.y.cast()]
}
}
#[cfg(feature = "mint")]
impl<P: Pixel> From<mint::Point2<P>> for PhysicalPosition<P> {
fn from(p: mint::Point2<P>) -> Self {
Self::new(p.x, p.y)
}
}
#[cfg(feature = "mint")]
impl<P: Pixel> From<PhysicalPosition<P>> for mint::Point2<P> {
fn from(p: PhysicalPosition<P>) -> Self {
mint::Point2 { x: p.x, y: p.y }
}
}
/// A size represented in logical pixels.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LogicalSize<P> {
pub width: P,
pub height: P,
}
impl<P> LogicalSize<P> {
#[inline]
pub const fn new(width: P, height: P) -> Self {
LogicalSize { width, height }
}
}
impl<P: Pixel> LogicalSize<P> {
#[inline]
pub fn from_physical<T: Into<PhysicalSize<X>>, X: Pixel>(
physical: T,
scale_factor: f64,
) -> Self {
physical.into().to_logical(scale_factor)
}
#[inline]
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalSize<X> {
assert!(validate_scale_factor(scale_factor));
let width = self.width.into() * scale_factor;
let height = self.height.into() * scale_factor;
PhysicalSize::new(width, height).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> LogicalSize<X> {
LogicalSize {
width: self.width.cast(),
height: self.height.cast(),
}
}
}
impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalSize<P> {
fn from((x, y): (X, X)) -> LogicalSize<P> {
LogicalSize::new(x.cast(), y.cast())
}
}
impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for (X, X) {
fn from(s: LogicalSize<P>) -> (X, X) {
(s.width.cast(), s.height.cast())
}
}
impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalSize<P> {
fn from([x, y]: [X; 2]) -> LogicalSize<P> {
LogicalSize::new(x.cast(), y.cast())
}
}
impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for [X; 2] {
fn from(s: LogicalSize<P>) -> [X; 2] {
[s.width.cast(), s.height.cast()]
}
}
#[cfg(feature = "mint")]
impl<P: Pixel> From<mint::Vector2<P>> for LogicalSize<P> {
fn from(v: mint::Vector2<P>) -> Self {
Self::new(v.x, v.y)
}
}
#[cfg(feature = "mint")]
impl<P: Pixel> From<LogicalSize<P>> for mint::Vector2<P> {
fn from(s: LogicalSize<P>) -> Self {
mint::Vector2 {
x: s.width,
y: s.height,
}
}
}
/// A size represented in physical pixels.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PhysicalSize<P> {
pub width: P,
pub height: P,
}
impl<P> PhysicalSize<P> {
#[inline]
pub const fn new(width: P, height: P) -> Self {
PhysicalSize { width, height }
}
}
impl<P: Pixel> PhysicalSize<P> {
#[inline]
pub fn from_logical<T: Into<LogicalSize<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
logical.into().to_physical(scale_factor)
}
#[inline]
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalSize<X> {
assert!(validate_scale_factor(scale_factor));
let width = self.width.into() / scale_factor;
let height = self.height.into() / scale_factor;
LogicalSize::new(width, height).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalSize<X> {
PhysicalSize {
width: self.width.cast(),
height: self.height.cast(),
}
}
}
impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalSize<P> {
fn from((x, y): (X, X)) -> PhysicalSize<P> {
PhysicalSize::new(x.cast(), y.cast())
}
}
impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for (X, X) {
fn from(s: PhysicalSize<P>) -> (X, X) {
(s.width.cast(), s.height.cast())
}
}
impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalSize<P> {
fn from([x, y]: [X; 2]) -> PhysicalSize<P> {
PhysicalSize::new(x.cast(), y.cast())
}
}
impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for [X; 2] {
fn from(s: PhysicalSize<P>) -> [X; 2] {
[s.width.cast(), s.height.cast()]
}
}
#[cfg(feature = "mint")]
impl<P: Pixel> From<mint::Vector2<P>> for PhysicalSize<P> {
fn from(v: mint::Vector2<P>) -> Self {
Self::new(v.x, v.y)
}
}
#[cfg(feature = "mint")]
impl<P: Pixel> From<PhysicalSize<P>> for mint::Vector2<P> {
fn from(s: PhysicalSize<P>) -> Self {
mint::Vector2 {
x: s.width,
y: s.height,
}
}
}
/// A size that's either physical or logical.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Size {
Physical(PhysicalSize<u32>),
Logical(LogicalSize<f64>),
}
impl Size {
pub fn new<S: Into<Size>>(size: S) -> Size {
size.into()
}
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalSize<P> {
match *self {
Size::Physical(size) => size.to_logical(scale_factor),
Size::Logical(size) => size.cast(),
}
}
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalSize<P> {
match *self {
Size::Physical(size) => size.cast(),
Size::Logical(size) => size.to_physical(scale_factor),
}
}
}
impl<P: Pixel> From<PhysicalSize<P>> for Size {
#[inline]
fn from(size: PhysicalSize<P>) -> Size {
Size::Physical(size.cast())
}
}
impl<P: Pixel> From<LogicalSize<P>> for Size {
#[inline]
fn from(size: LogicalSize<P>) -> Size {
Size::Logical(size.cast())
}
}
/// A position that's either physical or logical.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Position {
Physical(PhysicalPosition<i32>),
Logical(LogicalPosition<f64>),
}
impl Position {
pub fn new<S: Into<Position>>(position: S) -> Position {
position.into()
}
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalPosition<P> {
match *self {
Position::Physical(position) => position.to_logical(scale_factor),
Position::Logical(position) => position.cast(),
}
}
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalPosition<P> {
match *self {
Position::Physical(position) => position.cast(),
Position::Logical(position) => position.to_physical(scale_factor),
}
}
}
impl<P: Pixel> From<PhysicalPosition<P>> for Position {
#[inline]
fn from(position: PhysicalPosition<P>) -> Position {
Position::Physical(position.cast())
}
}
impl<P: Pixel> From<LogicalPosition<P>> for Position {
#[inline]
fn from(position: LogicalPosition<P>) -> Position {
Position::Logical(position.cast())
}
}

View File

@@ -1,82 +0,0 @@
use std::{error, fmt};
use crate::platform_impl;
/// An error whose cause it outside Winit's control.
#[derive(Debug)]
pub enum ExternalError {
/// The operation is not supported by the backend.
NotSupported(NotSupportedError),
/// The OS cannot perform the operation.
Os(OsError),
}
/// The error type for when the requested operation is not supported by the backend.
#[derive(Clone)]
pub struct NotSupportedError {
_marker: (),
}
/// The error type for when the OS cannot perform the requested operation.
#[derive(Debug)]
pub struct OsError {
line: u32,
file: &'static str,
error: platform_impl::OsError,
}
impl NotSupportedError {
#[inline]
#[allow(dead_code)]
pub(crate) fn new() -> NotSupportedError {
NotSupportedError { _marker: () }
}
}
impl OsError {
#[allow(dead_code)]
pub(crate) fn new(line: u32, file: &'static str, error: platform_impl::OsError) -> OsError {
OsError { line, file, error }
}
}
#[allow(unused_macros)]
macro_rules! os_error {
($error:expr) => {{
crate::error::OsError::new(line!(), file!(), $error)
}};
}
impl fmt::Display for OsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.pad(&format!(
"os error at {}:{}: {}",
self.file, self.line, self.error
))
}
}
impl fmt::Display for ExternalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
ExternalError::NotSupported(e) => e.fmt(f),
ExternalError::Os(e) => e.fmt(f),
}
}
}
impl fmt::Debug for NotSupportedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("NotSupportedError").finish()
}
}
impl fmt::Display for NotSupportedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.pad("the requested operation is not supported by Winit")
}
}
impl error::Error for OsError {}
impl error::Error for ExternalError {}
impl error::Error for NotSupportedError {}

File diff suppressed because it is too large Load Diff

View File

@@ -1,236 +0,0 @@
//! The `EventLoop` struct and assorted supporting types, including `ControlFlow`.
//!
//! If you want to send custom events to the event loop, use [`EventLoop::create_proxy()`][create_proxy]
//! to acquire an [`EventLoopProxy`][event_loop_proxy] and call its [`send_event`][send_event] method.
//!
//! See the root-level documentation for information on how to create and use an event loop to
//! handle events.
//!
//! [create_proxy]: crate::event_loop::EventLoop::create_proxy
//! [event_loop_proxy]: crate::event_loop::EventLoopProxy
//! [send_event]: crate::event_loop::EventLoopProxy::send_event
use instant::Instant;
use std::ops::Deref;
use std::{error, fmt};
use crate::{event::Event, monitor::MonitorHandle, platform_impl};
/// Provides a way to retrieve events from the system and from the windows that were registered to
/// the events loop.
///
/// An `EventLoop` can be seen more or less as a "context". Calling `EventLoop::new()`
/// initializes everything that will be required to create windows. For example on Linux creating
/// an event loop opens a connection to the X or Wayland server.
///
/// To wake up an `EventLoop` from a another thread, see the `EventLoopProxy` docs.
///
/// Note that the `EventLoop` cannot be shared across threads (due to platform-dependant logic
/// forbidding it), as such it is neither `Send` nor `Sync`. If you need cross-thread access, the
/// `Window` created from this `EventLoop` _can_ be sent to an other thread, and the
/// `EventLoopProxy` allows you to wake up an `EventLoop` from another thread.
///
pub struct EventLoop<T: 'static> {
pub(crate) event_loop: platform_impl::EventLoop<T>,
pub(crate) _marker: ::std::marker::PhantomData<*mut ()>, // Not Send nor Sync
}
/// Target that associates windows with an `EventLoop`.
///
/// This type exists to allow you to create new windows while Winit executes
/// your callback. `EventLoop` will coerce into this type (`impl<T> Deref for
/// EventLoop<T>`), so functions that take this as a parameter can also take
/// `&EventLoop`.
pub struct EventLoopWindowTarget<T: 'static> {
pub(crate) p: platform_impl::EventLoopWindowTarget<T>,
pub(crate) _marker: ::std::marker::PhantomData<*mut ()>, // Not Send nor Sync
}
impl<T> fmt::Debug for EventLoop<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("EventLoop { .. }")
}
}
impl<T> fmt::Debug for EventLoopWindowTarget<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("EventLoopWindowTarget { .. }")
}
}
/// Set by the user callback given to the `EventLoop::run` method.
///
/// Indicates the desired behavior of the event loop after [`Event::RedrawEventsCleared`][events_cleared]
/// is emitted. Defaults to `Poll`.
///
/// ## Persistency
/// Almost every change is persistent between multiple calls to the event loop closure within a
/// given run loop. The only exception to this is `Exit` which, once set, cannot be unset. Changes
/// are **not** persistent between multiple calls to `run_return` - issuing a new call will reset
/// the control flow to `Poll`.
///
/// [events_cleared]: crate::event::Event::RedrawEventsCleared
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ControlFlow {
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
/// whether or not new events are available to process.
///
/// ## Platform-specific
/// - **Web:** Events are queued and usually sent when `requestAnimationFrame` fires but sometimes
/// the events in the queue may be sent before the next `requestAnimationFrame` callback, for
/// example when the scaling of the page has changed. This should be treated as an implementation
/// detail which should not be relied on.
Poll,
/// When the current loop iteration finishes, suspend the thread until another event arrives.
Wait,
/// When the current loop iteration finishes, suspend the thread until either another event
/// arrives or the given time is reached.
WaitUntil(Instant),
/// Send a `LoopDestroyed` event and stop the event loop. This variant is *sticky* - once set,
/// `control_flow` cannot be changed from `Exit`, and any future attempts to do so will result
/// in the `control_flow` parameter being reset to `Exit`.
Exit,
}
impl Default for ControlFlow {
#[inline(always)]
fn default() -> ControlFlow {
ControlFlow::Poll
}
}
impl EventLoop<()> {
/// Builds a new event loop with a `()` as the user event type.
///
/// ***For cross-platform compatibility, the `EventLoop` must be created on the main thread.***
/// Attempting to create the event loop on a different thread will panic. This restriction isn't
/// strictly necessary on all platforms, but is imposed to eliminate any nasty surprises when
/// porting to platforms that require it. `EventLoopExt::new_any_thread` functions are exposed
/// in the relevant `platform` module if the target platform supports creating an event loop on
/// any thread.
///
/// Usage will result in display backend initialisation, this can be controlled on linux
/// using an environment variable `WINIT_UNIX_BACKEND`. Legal values are `x11` and `wayland`.
/// If it is not set, winit will try to connect to a wayland connection, and if it fails will
/// fallback on x11. If this variable is set with any other value, winit will panic.
///
/// ## Platform-specific
///
/// - **iOS:** Can only be called on the main thread.
pub fn new() -> EventLoop<()> {
EventLoop::<()>::with_user_event()
}
}
impl<T> EventLoop<T> {
/// Builds a new event loop.
///
/// All caveats documented in [`EventLoop::new`] apply to this function.
///
/// ## Platform-specific
///
/// - **iOS:** Can only be called on the main thread.
pub fn with_user_event() -> EventLoop<T> {
EventLoop {
event_loop: platform_impl::EventLoop::new(),
_marker: ::std::marker::PhantomData,
}
}
/// Hijacks the calling thread and initializes the winit event loop with the provided
/// closure. Since the closure is `'static`, it must be a `move` closure if it needs to
/// access any data from the calling context.
///
/// See the [`ControlFlow`] docs for information on how changes to `&mut ControlFlow` impact the
/// event loop's behavior.
///
/// Any values not passed to this function will *not* be dropped.
///
/// [`ControlFlow`]: crate::event_loop::ControlFlow
#[inline]
pub fn run<F>(self, event_handler: F) -> !
where
F: 'static + FnMut(Event<'_, T>, &EventLoopWindowTarget<T>, &mut ControlFlow),
{
self.event_loop.run(event_handler)
}
/// Creates an `EventLoopProxy` that can be used to dispatch user events to the main event loop.
pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy {
event_loop_proxy: self.event_loop.create_proxy(),
}
}
}
impl<T> Deref for EventLoop<T> {
type Target = EventLoopWindowTarget<T>;
fn deref(&self) -> &EventLoopWindowTarget<T> {
self.event_loop.window_target()
}
}
impl<T> EventLoopWindowTarget<T> {
/// Returns the list of all the monitors available on the system.
#[inline]
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
self.p
.available_monitors()
.into_iter()
.map(|inner| MonitorHandle { inner })
}
/// Returns the primary monitor of the system.
///
/// Returns `None` if it can't identify any monitor as a primary one.
///
/// ## Platform-specific
///
/// **Wayland:** Always returns `None`.
#[inline]
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
self.p.primary_monitor()
}
}
/// Used to send custom events to `EventLoop`.
pub struct EventLoopProxy<T: 'static> {
event_loop_proxy: platform_impl::EventLoopProxy<T>,
}
impl<T: 'static> Clone for EventLoopProxy<T> {
fn clone(&self) -> Self {
Self {
event_loop_proxy: self.event_loop_proxy.clone(),
}
}
}
impl<T: 'static> EventLoopProxy<T> {
/// Send an event to the `EventLoop` from which this proxy was created. This emits a
/// `UserEvent(event)` event in the event loop, where `event` is the value passed to this
/// function.
///
/// Returns an `Err` if the associated `EventLoop` no longer exists.
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.event_loop_proxy.send_event(event)
}
}
impl<T: 'static> fmt::Debug for EventLoopProxy<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("EventLoopProxy { .. }")
}
}
/// The error that is returned when an `EventLoopProxy` attempts to wake up an `EventLoop` that
/// no longer exists. Contains the original event given to `send_event`.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct EventLoopClosed<T>(pub T);
impl<T> fmt::Display for EventLoopClosed<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Tried to wake up a closed `EventLoop`")
}
}
impl<T: fmt::Debug> error::Error for EventLoopClosed<T> {}

339
src/events.rs Normal file
View File

@@ -0,0 +1,339 @@
use std::path::PathBuf;
use WindowId;
#[derive(Clone, Debug)]
pub enum Event {
WindowEvent {
window_id: WindowId,
event: WindowEvent,
}
}
#[derive(Clone, Debug)]
pub enum WindowEvent {
// TODO: remove ; can break the lib internally so be careful
Awakened,
/// The size of the window has changed.
Resized(u32, u32),
/// The position of the window has changed.
Moved(i32, i32),
/// The window has been closed.
Closed,
/// A file has been dropped into the window.
DroppedFile(PathBuf),
/// The window received a unicode character.
ReceivedCharacter(char),
/// The window gained or lost focus.
///
/// The parameter is true if the window has gained focus, and false if it has lost focus.
Focused(bool),
/// An event from the keyboard has been received.
KeyboardInput(ElementState, ScanCode, Option<VirtualKeyCode>, ModifiersState),
/// The cursor has moved on the window.
///
/// The parameter are the (x,y) coords in pixels relative to the top-left corner of the window.
MouseMoved(i32, i32),
/// The cursor has entered the window.
MouseEntered,
/// The cursor has left the window.
MouseLeft,
/// A mouse wheel movement or touchpad scroll occurred.
MouseWheel(MouseScrollDelta, TouchPhase),
/// An event from the mouse has been received.
MouseInput(ElementState, MouseButton),
/// Touchpad pressure event.
///
/// At the moment, only supported on Apple forcetouch-capable macbooks.
/// The parameters are: pressure level (value between 0 and 1 representing how hard the touchpad
/// is being pressed) and stage (integer representing the click level).
TouchpadPressure(f32, i64),
/// The window needs to be redrawn.
Refresh,
/// App has been suspended or resumed.
///
/// The parameter is true if app was suspended, and false if it has been resumed.
Suspended(bool),
/// Touch event has been received
Touch(Touch)
}
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum TouchPhase {
Started,
Moved,
Ended,
Cancelled
}
#[derive(Debug, Clone, Copy)]
/// Represents touch event
///
/// Every time user touches screen new Start event with some finger id is generated.
/// When the finger is removed from the screen End event with same id is generated.
///
/// For every id there will be at least 2 events with phases Start and End (or Cancelled).
/// There may be 0 or more Move events.
///
///
/// Depending on platform implementation id may or may not be reused by system after End event.
///
/// Gesture regonizer using this event should assume that Start event received with same id
/// as previously received End event is a new finger and has nothing to do with an old one.
///
/// Touch may be cancelled if for example window lost focus.
pub struct Touch {
pub phase: TouchPhase,
pub location: (f64,f64),
/// unique identifier of a finger.
pub id: u64
}
pub type ScanCode = u8;
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum ElementState {
Pressed,
Released,
}
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum MouseButton {
Left,
Right,
Middle,
Other(u8),
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MouseScrollDelta {
/// Amount in lines or rows to scroll in the horizontal
/// and vertical directions.
///
/// Positive values indicate movement forward
/// (away from the user) or rightwards.
LineDelta(f32, f32),
/// Amount in pixels to scroll in the horizontal and
/// vertical direction.
///
/// Scroll events are expressed as a PixelDelta if
/// supported by the device (eg. a touchpad) and
/// platform.
PixelDelta(f32, f32)
}
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum VirtualKeyCode {
/// The '1' key over the letters.
Key1,
/// The '2' key over the letters.
Key2,
/// The '3' key over the letters.
Key3,
/// The '4' key over the letters.
Key4,
/// The '5' key over the letters.
Key5,
/// The '6' key over the letters.
Key6,
/// The '7' key over the letters.
Key7,
/// The '8' key over the letters.
Key8,
/// The '9' key over the letters.
Key9,
/// The '0' key over the 'O' and 'P' keys.
Key0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
/// The Escape key, next to F1.
Escape,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
F13,
F14,
F15,
/// Print Screen/SysRq.
Snapshot,
/// Scroll Lock.
Scroll,
/// Pause/Break key, next to Scroll lock.
Pause,
/// `Insert`, next to Backspace.
Insert,
Home,
Delete,
End,
PageDown,
PageUp,
Left,
Up,
Right,
Down,
/// The Backspace key, right over Enter.
// TODO: rename
Back,
/// The Enter key.
Return,
/// The space bar.
Space,
/// The "Compose" key on Linux.
Compose,
Numlock,
Numpad0,
Numpad1,
Numpad2,
Numpad3,
Numpad4,
Numpad5,
Numpad6,
Numpad7,
Numpad8,
Numpad9,
AbntC1,
AbntC2,
Add,
Apostrophe,
Apps,
At,
Ax,
Backslash,
Calculator,
Capital,
Colon,
Comma,
Convert,
Decimal,
Divide,
Equals,
Grave,
Kana,
Kanji,
LAlt,
LBracket,
LControl,
LMenu,
LShift,
LWin,
Mail,
MediaSelect,
MediaStop,
Minus,
Multiply,
Mute,
MyComputer,
NavigateForward, // also called "Prior"
NavigateBackward, // also called "Next"
NextTrack,
NoConvert,
NumpadComma,
NumpadEnter,
NumpadEquals,
OEM102,
Period,
PlayPause,
Power,
PrevTrack,
RAlt,
RBracket,
RControl,
RMenu,
RShift,
RWin,
Semicolon,
Slash,
Sleep,
Stop,
Subtract,
Sysrq,
Tab,
Underline,
Unlabeled,
VolumeDown,
VolumeUp,
Wake,
WebBack,
WebFavorites,
WebForward,
WebHome,
WebRefresh,
WebSearch,
WebStop,
Yen,
}
/// Represents the current state of the keyboard modifiers
///
/// Each field of this struct represents a modifier and is `true` if this modifier is active.
#[derive(Default, Debug, Clone, Copy)]
pub struct ModifiersState {
/// The "shift" key
pub shift: bool,
/// The "control" key
pub ctrl: bool,
/// The "alt" key
pub alt: bool,
/// The "logo" key
///
/// This is the "windows" key on PC and "command" key on Mac.
pub logo: bool
}

View File

@@ -1,135 +0,0 @@
use crate::platform_impl::PlatformIcon;
use std::{error::Error, fmt, io, mem};
#[repr(C)]
#[derive(Debug)]
pub(crate) struct Pixel {
pub(crate) r: u8,
pub(crate) g: u8,
pub(crate) b: u8,
pub(crate) a: u8,
}
pub(crate) const PIXEL_SIZE: usize = mem::size_of::<Pixel>();
#[derive(Debug)]
/// An error produced when using `Icon::from_rgba` with invalid arguments.
pub enum BadIcon {
/// Produced when the length of the `rgba` argument isn't divisible by 4, thus `rgba` can't be
/// safely interpreted as 32bpp RGBA pixels.
ByteCountNotDivisibleBy4 { byte_count: usize },
/// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`.
/// At least one of your arguments is incorrect.
DimensionsVsPixelCount {
width: u32,
height: u32,
width_x_height: usize,
pixel_count: usize,
},
/// Produced when underlying OS functionality failed to create the icon
OsError(io::Error),
}
impl fmt::Display for BadIcon {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BadIcon::ByteCountNotDivisibleBy4 { byte_count } => write!(f,
"The length of the `rgba` argument ({:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels.",
byte_count,
),
BadIcon::DimensionsVsPixelCount {
width,
height,
width_x_height,
pixel_count,
} => write!(f,
"The specified dimensions ({:?}x{:?}) don't match the number of pixels supplied by the `rgba` argument ({:?}). For those dimensions, the expected pixel count is {:?}.",
width, height, pixel_count, width_x_height,
),
BadIcon::OsError(e) => write!(f, "OS error when instantiating the icon: {:?}", e),
}
}
}
impl Error for BadIcon {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(self)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct RgbaIcon {
pub(crate) rgba: Vec<u8>,
pub(crate) width: u32,
pub(crate) height: u32,
}
/// For platforms which don't have window icons (e.g. web)
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct NoIcon;
#[allow(dead_code)] // These are not used on every platform
mod constructors {
use super::*;
impl RgbaIcon {
/// Creates an `Icon` from 32bpp RGBA data.
///
/// The length of `rgba` must be divisible by 4, and `width * height` must equal
/// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error.
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
if rgba.len() % PIXEL_SIZE != 0 {
return Err(BadIcon::ByteCountNotDivisibleBy4 {
byte_count: rgba.len(),
});
}
let pixel_count = rgba.len() / PIXEL_SIZE;
if pixel_count != (width * height) as usize {
Err(BadIcon::DimensionsVsPixelCount {
width,
height,
width_x_height: (width * height) as usize,
pixel_count,
})
} else {
Ok(RgbaIcon {
rgba,
width,
height,
})
}
}
}
impl NoIcon {
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
// Create the rgba icon anyway to validate the input
let _ = RgbaIcon::from_rgba(rgba, width, height)?;
Ok(NoIcon)
}
}
}
/// An icon used for the window titlebar, taskbar, etc.
#[derive(Clone)]
pub struct Icon {
pub(crate) inner: PlatformIcon,
}
impl fmt::Debug for Icon {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Debug::fmt(&self.inner, formatter)
}
}
impl Icon {
/// Creates an `Icon` from 32bpp RGBA data.
///
/// The length of `rgba` must be divisible by 4, and `width * height` must equal
/// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error.
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
Ok(Icon {
inner: PlatformIcon::from_rgba(rgba, width, height)?,
})
}
}

View File

@@ -1,160 +1,404 @@
//! Winit is a cross-platform window creation and event loop management library.
//! Winit allows you to build a window on as many platforms as possible.
//!
//! # Building windows
//! # Building a window
//!
//! Before you can build a [`Window`], you first need to build an [`EventLoop`]. This is done with the
//! [`EventLoop::new()`] function.
//! Before you can build a window, you first need to build an `EventsLoop`. This is done with the
//! `EventsLoop::new()` function. Example:
//!
//! ```no_run
//! use winit::event_loop::EventLoop;
//! let event_loop = EventLoop::new();
//! use winit::EventsLoop;
//! let events_loop = EventsLoop::new();
//! ```
//!
//! Once this is done there are two ways to create a [`Window`]:
//! Once this is done there are two ways to create a window:
//!
//! - Calling [`Window::new(&event_loop)`][window_new].
//! - Calling [`let builder = WindowBuilder::new()`][window_builder_new] then [`builder.build(&event_loop)`][window_builder_build].
//! - Calling `Window::new(&events_loop)`.
//! - Calling `let builder = WindowBuilder::new()` then `builder.build(&events_loop)`.
//!
//! The first method is the simplest, and will give you default values for everything. The second
//! method 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`].
//! The first way is the simpliest way and will give you default values for everything.
//!
//! # Event handling
//! 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.
//!
//! Once a [`Window`] has been created, it will generate different *events*. A [`Window`] object can
//! generate [`WindowEvent`]s when certain input events occur, such as a cursor moving over the
//! window or a key getting pressed while the window is focused. Devices can generate
//! [`DeviceEvent`]s, which contain unfiltered event data that isn't specific to a certain window.
//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a
//! [`DeviceEvent`]. You can also create and handle your own custom [`UserEvent`]s, if desired.
//! # Events handling
//!
//! You can retrieve events by calling [`EventLoop::run`][event_loop_run]. This function will
//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and
//! will run until the `control_flow` argument given to the closure is set to
//! [`ControlFlow`]`::`[`Exit`], at which point [`Event`]`::`[`LoopDestroyed`] is emitted and the
//! entire program terminates.
//! Once a window has been created, it will *generate events*. For example whenever the user moves
//! the window, resizes the window, moves the mouse, etc. an event is generated.
//!
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
//! model, since that can't be implemented properly on some platforms (e.g web, iOS) and works poorly on
//! most other platforms. However, this model can be re-implemented to an extent with
//! [`EventLoopExtRunReturn::run_return`]. See that method's documentation for more reasons about why
//! it's discouraged, beyond compatibility reasons.
//! The events generated by a window can be retreived from the `EventsLoop` the window was created
//! with.
//!
//! There are two ways to do so. The first is to call `events_loop.poll_events(...)`, which will
//! retreive all the events pending on the windows and immediately return after no new event is
//! available. You usually want to use this method in application that render continuously on the
//! screen, such as video games.
//!
//! ```no_run
//! use winit::{
//! event::{Event, WindowEvent},
//! event_loop::{ControlFlow, EventLoop},
//! window::WindowBuilder,
//! };
//! use winit::Event;
//! use winit::WindowEvent;
//! # use winit::EventsLoop;
//! # let events_loop = EventsLoop::new();
//!
//! let event_loop = EventLoop::new();
//! let window = WindowBuilder::new().build(&event_loop).unwrap();
//! 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_loop.run(move |event, _, control_flow| {
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
//! // dispatched any events. This is ideal for games and similar applications.
//! *control_flow = ControlFlow::Poll;
//! The second way is to call `events_loop.run_forever(...)`. As its name tells, it will run
//! forever unless it is stopped by calling `events_loop.interrupt()`.
//!
//! // ControlFlow::Wait pauses the event loop if no events are available to process.
//! // This is ideal for non-game applications that only update in response to user
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
//! *control_flow = ControlFlow::Wait;
//! ```no_run
//! use winit::Event;
//! use winit::WindowEvent;
//! # use winit::EventsLoop;
//! # let events_loop = EventsLoop::new();
//!
//! events_loop.run_forever(|event| {
//! match event {
//! Event::WindowEvent {
//! event: WindowEvent::CloseRequested,
//! ..
//! } => {
//! println!("The close button was pressed; stopping");
//! *control_flow = ControlFlow::Exit
//! },
//! Event::MainEventsCleared => {
//! // Application update code.
//!
//! // Queue a RedrawRequested event.
//! //
//! // You only need to call this if you've determined that you need to redraw, in
//! // applications which do not always need to. Applications that redraw continuously
//! // can just render here instead.
//! window.request_redraw();
//! },
//! Event::RedrawRequested(_) => {
//! // Redraw the application.
//! //
//! // It's preferable for applications that do not render continuously to render in
//! // this event rather than in MainEventsCleared, since rendering in here allows
//! // the program to gracefully handle redraws requested by the OS.
//! Event::WindowEvent { event: WindowEvent::Closed, .. } => {
//! println!("The window was closed ; stopping");
//! events_loop.interrupt();
//! },
//! _ => ()
//! }
//! });
//! ```
//!
//! [`Event`]`::`[`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be
//! compared to the value returned by [`Window::id()`][window_id_fn] to determine which [`Window`]
//! dispatched the event.
//! If you use multiple windows, the `WindowEvent` event has a member named `window_id`. You can
//! compare it with the value returned by the `id()` method of `Window` in order to know which
//! window has received the event.
//!
//! # Drawing on the window
//!
//! Winit doesn't directly provide any methods for drawing on a [`Window`]. However it allows you to
//! retrieve the raw handle of the window (see the [`platform`] module and/or the
//! [`raw_window_handle`] method), which in turn allows you to create an
//! OpenGL/Vulkan/DirectX/Metal/etc. context that can be used to render graphics.
//! Winit doesn't provide any function that allows drawing on a window. However it allows you to
//! retreive the raw handle of the window (see the `os` module for that), which in turn allows you
//! to create an OpenGL/Vulkan/DirectX/Metal/etc. context that will draw on the window.
//!
//! Note that many platforms will display garbage data in the window's client area if the
//! application doesn't render anything to the window by the time the desktop compositor is ready to
//! display the window to the user. If you notice this happening, you should create the window with
//! [`visible` set to `false`](crate::window::WindowBuilder::with_visible) and explicitly make the
//! window visible only once you're ready to render into it.
//!
//! [`EventLoop`]: event_loop::EventLoop
//! [`EventLoopExtRunReturn::run_return`]: ./platform/run_return/trait.EventLoopExtRunReturn.html#tymethod.run_return
//! [`EventLoop::new()`]: event_loop::EventLoop::new
//! [event_loop_run]: event_loop::EventLoop::run
//! [`ControlFlow`]: event_loop::ControlFlow
//! [`Exit`]: event_loop::ControlFlow::Exit
//! [`Window`]: window::Window
//! [`WindowId`]: window::WindowId
//! [`WindowBuilder`]: window::WindowBuilder
//! [window_new]: window::Window::new
//! [window_builder_new]: window::WindowBuilder::new
//! [window_builder_build]: window::WindowBuilder::build
//! [window_id_fn]: window::Window::id
//! [`Event`]: event::Event
//! [`WindowEvent`]: event::WindowEvent
//! [`DeviceEvent`]: event::DeviceEvent
//! [`UserEvent`]: event::Event::UserEvent
//! [`LoopDestroyed`]: event::Event::LoopDestroyed
//! [`platform`]: platform
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
#![deny(rust_2018_idioms)]
#![deny(rustdoc::broken_intra_doc_links)]
#[allow(unused_imports)]
#[macro_use]
extern crate lazy_static;
#[allow(unused_imports)]
#[macro_use]
extern crate log;
#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;
#[macro_use]
extern crate bitflags;
extern crate shared_library;
extern crate libc;
#[cfg(target_os = "windows")]
extern crate winapi;
#[cfg(target_os = "windows")]
extern crate kernel32;
#[cfg(target_os = "windows")]
extern crate shell32;
#[cfg(target_os = "windows")]
extern crate gdi32;
#[cfg(target_os = "windows")]
extern crate user32;
#[cfg(target_os = "windows")]
extern crate dwmapi;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[macro_use]
extern crate objc;
#[cfg(target_os = "macos")]
extern crate cgl;
#[cfg(target_os = "macos")]
extern crate cocoa;
#[cfg(target_os = "macos")]
extern crate core_foundation;
#[cfg(target_os = "macos")]
extern crate core_graphics;
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
extern crate x11_dl;
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
#[macro_use(wayland_env,declare_handler)]
extern crate wayland_client;
use std::sync::Arc;
pub use events::*;
pub use window::{AvailableMonitorsIter, MonitorId, get_available_monitors, get_primary_monitor};
pub use native_monitor::NativeMonitorId;
pub mod dpi;
#[macro_use]
pub mod error;
pub mod event;
pub mod event_loop;
mod icon;
pub mod monitor;
mod platform_impl;
pub mod window;
mod api_transition;
pub mod platform;
mod platform;
mod events;
mod window;
pub mod os;
/// Represents a window.
///
/// # Example
///
/// ```no_run
/// use winit::Event;
/// use winit::EventsLoop;
/// use winit::Window;
/// use winit::WindowEvent;
///
/// let events_loop = EventsLoop::new();
/// let window = Window::new(&events_loop).unwrap();
///
/// events_loop.run_forever(|event| {
/// match event {
/// Event::WindowEvent { event: WindowEvent::Closed, .. } => {
/// events_loop.interrupt();
/// },
/// _ => ()
/// }
/// });
/// ```
pub struct Window {
window: platform::Window2,
}
/// Identifier of a window. Unique for each window.
///
/// Can be obtained with `window.id()`.
///
/// Whenever you receive an event specific to a window, this event contains a `WindowId` which you
/// can then compare to the ids of your windows.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId(platform::WindowId);
/// Provides a way to retreive events from the windows that were registered to it.
// TODO: document usage in multiple threads
pub struct EventsLoop {
events_loop: Arc<platform::EventsLoop>,
}
impl EventsLoop {
/// Builds a new events loop.
pub fn new() -> EventsLoop {
EventsLoop {
events_loop: Arc::new(platform::EventsLoop::new()),
}
}
/// Fetches all the events that are pending, calls the callback function for each of them,
/// and returns.
#[inline]
pub fn poll_events<F>(&self, callback: F)
where F: FnMut(Event)
{
self.events_loop.poll_events(callback)
}
/// Runs forever until `interrupt()` is called. Whenever an event happens, calls the callback.
#[inline]
pub fn run_forever<F>(&self, callback: F)
where F: FnMut(Event)
{
self.events_loop.run_forever(callback)
}
/// If we called `run_forever()`, stops the process of waiting for events.
// TODO: what if we're waiting from multiple threads?
#[inline]
pub fn interrupt(&self) {
self.events_loop.interrupt()
}
}
/// Object that allows you to build windows.
#[derive(Clone)]
pub struct WindowBuilder {
/// The attributes to use to create the window.
pub window: WindowAttributes,
// Platform-specific configuration. Private.
platform_specific: platform::PlatformSpecificWindowBuilderAttributes,
}
/// Error that can happen while creating a window or a headless renderer.
#[derive(Debug)]
pub enum CreationError {
OsError(String),
/// TODO: remove this error
NotSupported,
}
impl CreationError {
fn to_string(&self) -> &str {
match *self {
CreationError::OsError(ref text) => &text,
CreationError::NotSupported => "Some of the requested attributes are not supported",
}
}
}
impl std::fmt::Display for CreationError {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
formatter.write_str(self.to_string())
}
}
impl std::error::Error for CreationError {
fn description(&self) -> &str {
self.to_string()
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum MouseCursor {
/// The platform-dependent default cursor.
Default,
/// A simple crosshair.
Crosshair,
/// A hand (often used to indicate links in web browsers).
Hand,
/// Self explanatory.
Arrow,
/// Indicates something is to be moved.
Move,
/// Indicates text that may be selected or edited.
Text,
/// Program busy indicator.
Wait,
/// Help indicator (often rendered as a "?")
Help,
/// Progress indicator. Shows that processing is being done. But in contrast
/// with "Wait" the user may still interact with the program. Often rendered
/// as a spinning beach ball, or an arrow with a watch or hourglass.
Progress,
/// Cursor showing that something cannot be done.
NotAllowed,
ContextMenu,
NoneCursor,
Cell,
VerticalText,
Alias,
Copy,
NoDrop,
Grab,
Grabbing,
AllScroll,
ZoomIn,
ZoomOut,
/// Indicate that some edge is to be moved. For example, the 'SeResize' cursor
/// is used when the movement starts from the south-east corner of the box.
EResize,
NResize,
NeResize,
NwResize,
SResize,
SeResize,
SwResize,
WResize,
EwResize,
NsResize,
NeswResize,
NwseResize,
ColResize,
RowResize,
}
/// Describes how glutin 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,
}
/// Attributes to use when creating a window.
#[derive(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)>,
/// 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)>,
/// 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)>,
/// If `Some`, the window will be in fullscreen mode with the given monitor.
///
/// The default is `None`.
pub monitor: Option<platform::MonitorId>,
/// The title of the window in the title bar.
///
/// The default is `"glutin window"`.
pub title: String,
/// Whether the window should be immediately visible upon creation.
///
/// The default is `true`.
pub visible: bool,
/// Whether the the window should be transparent. If this is true, writing colors
/// with alpha values different than `1.0` will produce a transparent window.
///
/// The default is `false`.
pub transparent: bool,
/// Whether the window should have borders and bars.
///
/// The default is `true`.
pub decorations: bool,
/// [iOS only] Enable multitouch, see [UIView#multipleTouchEnabled]
/// (https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/occ/instp/UIView/multipleTouchEnabled)
pub multitouch: bool,
}
impl Default for WindowAttributes {
#[inline]
fn default() -> WindowAttributes {
WindowAttributes {
dimensions: None,
min_dimensions: None,
max_dimensions: None,
monitor: None,
title: "glutin window".to_owned(),
visible: true,
transparent: false,
decorations: true,
multitouch: false,
}
}
}
mod native_monitor {
/// Native platform identifier for a monitor. Different platforms use fundamentally different types
/// to represent a monitor ID.
#[derive(Clone, PartialEq, Eq)]
pub enum NativeMonitorId {
/// Cocoa and X11 use a numeric identifier to represent a monitor.
Numeric(u32),
/// Win32 uses a Unicode string to represent a monitor.
Name(String),
/// Other platforms (Android) don't support monitor identification.
Unavailable
}
}

View File

@@ -1,174 +0,0 @@
//! Types useful for interacting with a user's monitors.
//!
//! If you want to get basic information about a monitor, you can use the [`MonitorHandle`][monitor_handle]
//! type. This is retrieved from one of the following methods, which return an iterator of
//! [`MonitorHandle`][monitor_handle]:
//! - [`EventLoopWindowTarget::available_monitors`][loop_get]
//! - [`Window::available_monitors`][window_get].
//!
//! [monitor_handle]: crate::monitor::MonitorHandle
//! [loop_get]: crate::event_loop::EventLoopWindowTarget::available_monitors
//! [window_get]: crate::window::Window::available_monitors
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
platform_impl,
};
/// Describes a fullscreen video mode of a monitor.
///
/// Can be acquired with:
/// - [`MonitorHandle::video_modes`][monitor_get].
///
/// [monitor_get]: crate::monitor::MonitorHandle::video_modes
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct VideoMode {
pub(crate) video_mode: platform_impl::VideoMode,
}
impl std::fmt::Debug for VideoMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.video_mode.fmt(f)
}
}
impl PartialOrd for VideoMode {
fn partial_cmp(&self, other: &VideoMode) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for VideoMode {
fn cmp(&self, other: &VideoMode) -> std::cmp::Ordering {
// TODO: we can impl `Ord` for `PhysicalSize` once we switch from `f32`
// to `u32` there
let size: (u32, u32) = self.size().into();
let other_size: (u32, u32) = other.size().into();
self.monitor().cmp(&other.monitor()).then(
size.cmp(&other_size)
.then(
self.refresh_rate()
.cmp(&other.refresh_rate())
.then(self.bit_depth().cmp(&other.bit_depth())),
)
.reverse(),
)
}
}
impl VideoMode {
/// Returns the resolution of this video mode.
#[inline]
pub fn size(&self) -> PhysicalSize<u32> {
self.video_mode.size()
}
/// Returns the bit depth of this video mode, as in how many bits you have
/// available per color. This is generally 24 bits or 32 bits on modern
/// systems, depending on whether the alpha channel is counted or not.
///
/// ## Platform-specific
///
/// - **Wayland:** Always returns 32.
/// - **iOS:** Always returns 32.
#[inline]
pub fn bit_depth(&self) -> u16 {
self.video_mode.bit_depth()
}
/// Returns the refresh rate of this video mode. **Note**: the returned
/// refresh rate is an integer approximation, and you shouldn't rely on this
/// value to be exact.
#[inline]
pub fn refresh_rate(&self) -> u16 {
self.video_mode.refresh_rate()
}
/// Returns the monitor that this video mode is valid for. Each monitor has
/// a separate set of valid video modes.
#[inline]
pub fn monitor(&self) -> MonitorHandle {
self.video_mode.monitor()
}
}
impl std::fmt::Display for VideoMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}x{} @ {} Hz ({} bpp)",
self.size().width,
self.size().height,
self.refresh_rate(),
self.bit_depth()
)
}
}
/// Handle to a monitor.
///
/// Allows you to retrieve information about a given monitor and can be used in [`Window`] creation.
///
/// [`Window`]: crate::window::Window
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct MonitorHandle {
pub(crate) inner: platform_impl::MonitorHandle,
}
impl MonitorHandle {
/// Returns a human-readable name of the monitor.
///
/// Returns `None` if the monitor doesn't exist anymore.
///
/// ## Platform-specific
///
/// - **Web:** Always returns None
#[inline]
pub fn name(&self) -> Option<String> {
self.inner.name()
}
/// Returns the monitor's resolution.
///
/// ## Platform-specific
///
/// - **Web:** Always returns (0,0)
#[inline]
pub fn size(&self) -> PhysicalSize<u32> {
self.inner.size()
}
/// Returns the top-left corner position of the monitor relative to the larger full
/// screen area.
///
/// ## Platform-specific
///
/// - **Web:** Always returns (0,0)
#[inline]
pub fn position(&self) -> PhysicalPosition<i32> {
self.inner.position()
}
/// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
///
/// See the [`dpi`](crate::dpi) module for more information.
///
/// ## Platform-specific
///
/// - **X11:** Can be overridden using the `WINIT_X11_SCALE_FACTOR` environment variable.
/// - **Android:** Always returns 1.0.
/// - **Web:** Always returns 1.0
#[inline]
pub fn scale_factor(&self) -> f64 {
self.inner.scale_factor()
}
/// Returns all fullscreen video modes supported by this monitor.
///
/// ## Platform-specific
///
/// - **Web:** Always returns an empty iterator
#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.inner.video_modes()
}
}

25
src/os/android.rs Normal file
View File

@@ -0,0 +1,25 @@
#![cfg(any(target_os = "android"))]
use std::os::raw::c_void;
use Window;
use WindowBuilder;
/// Additional methods on `Window` that are specific to Android.
pub trait WindowExt {
fn get_native_window(&self) -> *const c_void;
}
impl WindowExt for Window {
#[inline]
fn get_native_window(&self) -> *const c_void {
self.window.get_native_window()
}
}
/// Additional methods on `WindowBuilder` that are specific to Android.
pub trait WindowBuilderExt {
}
impl WindowBuilderExt for WindowBuilder {
}

75
src/os/macos.rs Executable file
View File

@@ -0,0 +1,75 @@
#![cfg(target_os = "macos")]
use std::convert::From;
use std::os::raw::c_void;
use cocoa::appkit::NSApplicationActivationPolicy;
use {Window, WindowBuilder};
/// Additional methods on `Window` that are specific to MacOS.
pub trait WindowExt {
/// Returns a pointer to the cocoa `NSWindow` that is used by this window.
///
/// The pointer will become invalid when the `Window` is destroyed.
fn get_nswindow(&self) -> *mut c_void;
/// Returns a pointer to the cocoa `NSView` that is used by this window.
///
/// The pointer will become invalid when the `Window` is destroyed.
fn get_nsview(&self) -> *mut c_void;
}
impl WindowExt for Window {
#[inline]
fn get_nswindow(&self) -> *mut c_void {
self.window.get_nswindow()
}
#[inline]
fn get_nsview(&self) -> *mut c_void {
self.window.get_nsview()
}
}
/// Corresponds to `NSApplicationActivationPolicy`.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ActivationPolicy {
/// Corresponds to `NSApplicationActivationPolicyRegular`.
Regular,
/// Corresponds to `NSApplicationActivationPolicyAccessory`.
Accessory,
/// Corresponds to `NSApplicationActivationPolicyProhibited`.
Prohibited,
}
impl Default for ActivationPolicy {
fn default() -> Self {
ActivationPolicy::Regular
}
}
impl From<ActivationPolicy> for NSApplicationActivationPolicy {
fn from(activation_policy: ActivationPolicy) -> Self {
match activation_policy {
ActivationPolicy::Regular =>
NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
ActivationPolicy::Accessory =>
NSApplicationActivationPolicy::NSApplicationActivationPolicyAccessory,
ActivationPolicy::Prohibited =>
NSApplicationActivationPolicy::NSApplicationActivationPolicyProhibited,
}
}
}
/// Additional methods on `WindowBuilder` that are specific to MacOS.
pub trait WindowBuilderExt {
fn with_activation_policy(self, activation_policy: ActivationPolicy) -> 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
}
}

15
src/os/mod.rs Normal file
View File

@@ -0,0 +1,15 @@
//! Contains traits with platform-specific methods in them.
//!
//! Contains the follow modules:
//!
//! - `android`
//! - `macos`
//! - `unix`
//! - `windows`
//!
//! However only the module corresponding to the platform you're compiling to will be available.
//!
pub mod android;
pub mod macos;
pub mod unix;
pub mod windows;

174
src/os/unix.rs Normal file
View File

@@ -0,0 +1,174 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
use std::sync::Arc;
use std::ptr;
use libc;
use Window;
use platform::Window2 as LinuxWindow;
use platform::{UnixBackend, UNIX_BACKEND};
use WindowBuilder;
use platform::x11::XConnection;
use platform::x11::ffi::XVisualInfo;
use wayland_client::protocol::wl_display::WlDisplay;
use wayland_client::protocol::wl_surface::WlSurface;
pub use platform::x11;
// TODO: do not expose XConnection
pub fn get_x11_xconnection() -> Option<Arc<XConnection>> {
match *UNIX_BACKEND {
UnixBackend::X(ref connec) => Some(connec.clone()),
_ => None,
}
}
/// Additional methods on `Window` that are specific to Unix.
pub trait WindowExt {
/// Returns a pointer to the `Window` object of xlib that is used by this window.
///
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
///
/// The pointer will become invalid when the glutin `Window` is destroyed.
fn get_xlib_window(&self) -> Option<*mut libc::c_void>;
/// Returns a pointer to the `Display` object of xlib that is used by this window.
///
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
///
/// The pointer will become invalid when the glutin `Window` is destroyed.
fn get_xlib_display(&self) -> Option<*mut libc::c_void>;
fn get_xlib_screen_id(&self) -> Option<*mut libc::c_void>;
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>>;
/// This function returns the underlying `xcb_connection_t` of an xlib `Display`.
///
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
///
/// The pointer will become invalid when the glutin `Window` is destroyed.
fn get_xcb_connection(&self) -> Option<*mut libc::c_void>;
/// Returns a pointer to the `wl_surface` object of wayland that is used by this window.
///
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
///
/// The pointer will become invalid when the glutin `Window` is destroyed.
fn get_wayland_surface(&self) -> Option<*mut libc::c_void>;
/// Returns a pointer to the `wl_display` object of wayland that is used by this window.
///
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
///
/// The pointer will become invalid when the glutin `Window` is destroyed.
fn get_wayland_display(&self) -> Option<*mut libc::c_void>;
/// Returns a reference to the `WlSurface` object of wayland that is used by this window.
///
/// For use with the `wayland-client` crate.
///
/// **This function is not part of winit's public API.**
///
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
fn get_wayland_client_surface(&self) -> Option<&WlSurface>;
/// Returns a pointer to the `WlDisplay` object of wayland that is used by this window.
///
/// For use with the `wayland-client` crate.
///
/// **This function is not part of winit's public API.**
///
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
fn get_wayland_client_display(&self) -> Option<&WlDisplay>;
}
impl WindowExt for Window {
#[inline]
fn get_xlib_window(&self) -> Option<*mut libc::c_void> {
match self.window {
LinuxWindow::X(ref w) => Some(w.get_xlib_window()),
_ => None
}
}
#[inline]
fn get_xlib_display(&self) -> Option<*mut libc::c_void> {
match self.window {
LinuxWindow::X(ref w) => Some(w.get_xlib_display()),
_ => None
}
}
fn get_xlib_screen_id(&self) -> Option<*mut libc::c_void> {
match self.window {
LinuxWindow::X(ref w) => Some(w.get_xlib_screen_id()),
_ => None
}
}
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> {
match self.window {
LinuxWindow::X(ref w) => Some(w.get_xlib_xconnection()),
_ => None
}
}
fn get_xcb_connection(&self) -> Option<*mut libc::c_void> {
match self.window {
LinuxWindow::X(ref w) => Some(w.get_xcb_connection()),
_ => None
}
}
#[inline]
fn get_wayland_surface(&self) -> Option<*mut libc::c_void> {
use wayland_client::Proxy;
self.get_wayland_client_surface().map(|p| p.ptr() as *mut _)
}
#[inline]
fn get_wayland_display(&self) -> Option<*mut libc::c_void> {
use wayland_client::Proxy;
self.get_wayland_client_display().map(|p| p.ptr() as *mut _)
}
#[inline]
fn get_wayland_client_surface(&self) -> Option<&WlSurface> {
match self.window {
LinuxWindow::Wayland(ref w) => Some(w.get_surface()),
_ => None
}
}
#[inline]
fn get_wayland_client_display(&self) -> Option<&WlDisplay> {
match self.window {
LinuxWindow::Wayland(ref w) => Some(w.get_display()),
_ => None
}
}
}
/// Additional methods on `WindowBuilder` that are specific to Unix.
pub trait WindowBuilderExt {
fn with_x11_visual<T>(self, visual_infos: *const T) -> WindowBuilder;
fn with_x11_screen(self, screen_id: i32) -> WindowBuilder;
}
impl WindowBuilderExt for WindowBuilder {
#[inline]
fn with_x11_visual<T>(mut self, visual_infos: *const T) -> WindowBuilder {
self.platform_specific.visual_infos = Some(
unsafe { ptr::read(visual_infos as *const XVisualInfo) }
);
self
}
#[inline]
fn with_x11_screen(mut self, screen_id: i32) -> WindowBuilder {
self.platform_specific.screen_id = Some(screen_id);
self
}
}

37
src/os/windows.rs Normal file
View File

@@ -0,0 +1,37 @@
#![cfg(target_os = "windows")]
use libc;
use Window;
use WindowBuilder;
use winapi;
/// Additional methods on `Window` that are specific to Windows.
pub trait WindowExt {
/// Returns a pointer to the `Window` object of xlib that is used by this window.
///
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
///
/// The pointer will become invalid when the glutin `Window` is destroyed.
fn get_hwnd(&self) -> *mut libc::c_void;
}
impl WindowExt for Window {
#[inline]
fn get_hwnd(&self) -> *mut libc::c_void {
self.window.platform_window()
}
}
/// Additional methods on `WindowBuilder` that are specific to Windows.
pub trait WindowBuilderExt {
fn with_parent_window(self, parent: winapi::HWND) -> WindowBuilder;
}
impl WindowBuilderExt for WindowBuilder {
/// Sets a parent to the window to be created
#[inline]
fn with_parent_window(mut self, parent: winapi::HWND) -> WindowBuilder {
self.platform_specific.parent = Some(parent);
self
}
}

View File

@@ -1,40 +0,0 @@
#![cfg(any(target_os = "android"))]
use crate::{
event_loop::{EventLoop, EventLoopWindowTarget},
window::{Window, WindowBuilder},
};
use ndk::configuration::Configuration;
use ndk_glue::Rect;
/// Additional methods on `EventLoop` that are specific to Android.
pub trait EventLoopExtAndroid {}
impl<T> EventLoopExtAndroid for EventLoop<T> {}
/// Additional methods on `EventLoopWindowTarget` that are specific to Android.
pub trait EventLoopWindowTargetExtAndroid {}
/// Additional methods on `Window` that are specific to Android.
pub trait WindowExtAndroid {
fn content_rect(&self) -> Rect;
fn config(&self) -> Configuration;
}
impl WindowExtAndroid for Window {
fn content_rect(&self) -> Rect {
self.window.content_rect()
}
fn config(&self) -> Configuration {
self.window.config()
}
}
impl<T> EventLoopWindowTargetExtAndroid for EventLoopWindowTarget<T> {}
/// Additional methods on `WindowBuilder` that are specific to Android.
pub trait WindowBuilderExtAndroid {}
impl WindowBuilderExtAndroid for WindowBuilder {}

108
src/platform/android/ffi.rs Normal file
View File

@@ -0,0 +1,108 @@
#![allow(dead_code)]
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
use libc;
use std::os::raw;
#[link(name = "android")]
#[link(name = "EGL")]
#[link(name = "GLESv2")]
extern {}
/**
* asset_manager.h
*/
pub type AAssetManager = raw::c_void;
/**
* native_window.h
*/
pub type ANativeWindow = raw::c_void;
extern {
pub fn ANativeWindow_getHeight(window: *const ANativeWindow) -> libc::int32_t;
pub fn ANativeWindow_getWidth(window: *const ANativeWindow) -> libc::int32_t;
}
/**
* native_activity.h
*/
pub type JavaVM = ();
pub type JNIEnv = ();
pub type jobject = *const libc::c_void;
pub type AInputQueue = (); // FIXME: wrong
pub type ARect = (); // FIXME: wrong
#[repr(C)]
pub struct ANativeActivity {
pub callbacks: *mut ANativeActivityCallbacks,
pub vm: *mut JavaVM,
pub env: *mut JNIEnv,
pub clazz: jobject,
pub internalDataPath: *const libc::c_char,
pub externalDataPath: *const libc::c_char,
pub sdkVersion: libc::int32_t,
pub instance: *mut libc::c_void,
pub assetManager: *mut AAssetManager,
pub obbPath: *const libc::c_char,
}
#[repr(C)]
pub struct ANativeActivityCallbacks {
pub onStart: extern fn(*mut ANativeActivity),
pub onResume: extern fn(*mut ANativeActivity),
pub onSaveInstanceState: extern fn(*mut ANativeActivity, *mut libc::size_t),
pub onPause: extern fn(*mut ANativeActivity),
pub onStop: extern fn(*mut ANativeActivity),
pub onDestroy: extern fn(*mut ANativeActivity),
pub onWindowFocusChanged: extern fn(*mut ANativeActivity, libc::c_int),
pub onNativeWindowCreated: extern fn(*mut ANativeActivity, *const ANativeWindow),
pub onNativeWindowResized: extern fn(*mut ANativeActivity, *const ANativeWindow),
pub onNativeWindowRedrawNeeded: extern fn(*mut ANativeActivity, *const ANativeWindow),
pub onNativeWindowDestroyed: extern fn(*mut ANativeActivity, *const ANativeWindow),
pub onInputQueueCreated: extern fn(*mut ANativeActivity, *mut AInputQueue),
pub onInputQueueDestroyed: extern fn(*mut ANativeActivity, *mut AInputQueue),
pub onContentRectChanged: extern fn(*mut ANativeActivity, *const ARect),
pub onConfigurationChanged: extern fn(*mut ANativeActivity),
pub onLowMemory: extern fn(*mut ANativeActivity),
}
/**
* looper.h
*/
pub type ALooper = ();
#[link(name = "android")]
extern {
pub fn ALooper_forThread() -> *const ALooper;
pub fn ALooper_acquire(looper: *const ALooper);
pub fn ALooper_release(looper: *const ALooper);
pub fn ALooper_prepare(opts: libc::c_int) -> *const ALooper;
pub fn ALooper_pollOnce(timeoutMillis: libc::c_int, outFd: *mut libc::c_int,
outEvents: *mut libc::c_int, outData: *mut *mut libc::c_void) -> libc::c_int;
pub fn ALooper_pollAll(timeoutMillis: libc::c_int, outFd: *mut libc::c_int,
outEvents: *mut libc::c_int, outData: *mut *mut libc::c_void) -> libc::c_int;
pub fn ALooper_wake(looper: *const ALooper);
pub fn ALooper_addFd(looper: *const ALooper, fd: libc::c_int, ident: libc::c_int,
events: libc::c_int, callback: ALooper_callbackFunc, data: *mut libc::c_void)
-> libc::c_int;
pub fn ALooper_removeFd(looper: *const ALooper, fd: libc::c_int) -> libc::c_int;
}
pub const ALOOPER_PREPARE_ALLOW_NON_CALLBACKS: libc::c_int = 1 << 0;
pub const ALOOPER_POLL_WAKE: libc::c_int = -1;
pub const ALOOPER_POLL_CALLBACK: libc::c_int = -2;
pub const ALOOPER_POLL_TIMEOUT: libc::c_int = -3;
pub const ALOOPER_POLL_ERROR: libc::c_int = -4;
pub const ALOOPER_EVENT_INPUT: libc::c_int = 1 << 0;
pub const ALOOPER_EVENT_OUTPUT: libc::c_int = 1 << 1;
pub const ALOOPER_EVENT_ERROR: libc::c_int = 1 << 2;
pub const ALOOPER_EVENT_HANGUP: libc::c_int = 1 << 3;
pub const ALOOPER_EVENT_INVALID: libc::c_int = 1 << 4;
pub type ALooper_callbackFunc = extern fn(libc::c_int, libc::c_int, *mut libc::c_void) -> libc::c_int;

275
src/platform/android/mod.rs Normal file
View File

@@ -0,0 +1,275 @@
#![cfg(target_os = "android")]
extern crate android_glue;
use libc;
use std::ffi::{CString};
use std::sync::mpsc::{Receiver, channel};
use std::os::raw::c_void;
use {CreationError, WindowEvent as Event, MouseCursor};
use CreationError::OsError;
use events::ElementState::{Pressed, Released};
use events::{Touch, TouchPhase};
use std::collections::VecDeque;
use CursorState;
use WindowAttributes;
use native_monitor::NativeMonitorId;
gen_api_transition!();
pub struct Window {
native_window: *const c_void,
event_rx: Receiver<android_glue::Event>,
}
#[derive(Clone)]
pub struct MonitorId;
mod ffi;
#[inline]
pub fn get_available_monitors() -> VecDeque<MonitorId> {
let mut rb = VecDeque::new();
rb.push_back(MonitorId);
rb
}
#[inline]
pub fn get_primary_monitor() -> MonitorId {
MonitorId
}
impl MonitorId {
#[inline]
pub fn get_name(&self) -> Option<String> {
Some("Primary".to_string())
}
#[inline]
pub fn get_native_identifier(&self) -> NativeMonitorId {
NativeMonitorId::Unavailable
}
#[inline]
pub fn get_dimensions(&self) -> (u32, u32) {
unimplemented!()
}
}
#[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes;
#[derive(Clone, Default)]
pub struct PlatformSpecificHeadlessBuilderAttributes;
pub struct PollEventsIterator<'a> {
window: &'a Window,
}
impl<'a> Iterator for PollEventsIterator<'a> {
type Item = Event;
fn next(&mut self) -> Option<Event> {
match self.window.event_rx.try_recv() {
Ok(android_glue::Event::EventMotion(motion)) => {
Some(Event::Touch(Touch {
phase: match motion.action {
android_glue::MotionAction::Down => TouchPhase::Started,
android_glue::MotionAction::Move => TouchPhase::Moved,
android_glue::MotionAction::Up => TouchPhase::Ended,
android_glue::MotionAction::Cancel => TouchPhase::Cancelled,
},
location: (motion.x as f64, motion.y as f64),
id: motion.pointer_id as u64,
}))
},
Ok(android_glue::Event::InitWindow) => {
// The activity went to foreground.
Some(Event::Suspended(false))
},
Ok(android_glue::Event::TermWindow) => {
// The activity went to background.
Some(Event::Suspended(true))
},
Ok(android_glue::Event::WindowResized) |
Ok(android_glue::Event::ConfigChanged) => {
// Activity Orientation changed or resized.
self.window.get_inner_size().map(|s| Event::Resized(s.0, s.1))
},
Ok(android_glue::Event::WindowRedrawNeeded) => {
// The activity needs to be redrawn.
Some(Event::Refresh)
}
_ => {
None
}
}
}
}
pub struct WaitEventsIterator<'a> {
window: &'a Window,
}
impl<'a> Iterator for WaitEventsIterator<'a> {
type Item = Event;
#[inline]
fn next(&mut self) -> Option<Event> {
loop {
// calling poll_events()
if let Some(ev) = self.window.poll_events().next() {
return Some(ev);
}
// TODO: Implement a proper way of sleeping on the event queue
// timer::sleep(Duration::milliseconds(16));
}
}
}
impl Window {
pub fn new(win_attribs: &WindowAttributes, _: &PlatformSpecificWindowBuilderAttributes)
-> Result<Window, CreationError>
{
use std::{mem, ptr};
// 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")));
}
let (tx, rx) = channel();
android_glue::add_sender(tx);
android_glue::set_multitouch(win_attribs.multitouch);
Ok(Window {
native_window: native_window as *const _,
event_rx: rx,
})
}
#[inline]
pub fn get_native_window(&self) -> *const c_void {
self.native_window
}
#[inline]
pub fn is_closed(&self) -> bool {
false
}
#[inline]
pub fn set_title(&self, _: &str) {
}
#[inline]
pub fn show(&self) {
}
#[inline]
pub fn hide(&self) {
}
#[inline]
pub fn get_position(&self) -> Option<(i32, i32)> {
None
}
#[inline]
pub fn set_position(&self, _x: i32, _y: i32) {
}
#[inline]
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
let native_window = unsafe { android_glue::get_native_window() };
if native_window.is_null() {
None
} else {
Some((
unsafe { ffi::ANativeWindow_getWidth(native_window as *const _) } as u32,
unsafe { ffi::ANativeWindow_getHeight(native_window as *const _) } as u32
))
}
}
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
self.get_inner_size()
}
#[inline]
pub fn set_inner_size(&self, _x: u32, _y: u32) {
}
#[inline]
pub fn create_window_proxy(&self) -> WindowProxy {
WindowProxy
}
#[inline]
pub fn poll_events(&self) -> PollEventsIterator {
PollEventsIterator {
window: self
}
}
#[inline]
pub fn wait_events(&self) -> WaitEventsIterator {
WaitEventsIterator {
window: self
}
}
#[inline]
pub fn platform_display(&self) -> *mut libc::c_void {
unimplemented!();
}
#[inline]
pub fn platform_window(&self) -> *mut libc::c_void {
unimplemented!()
}
#[inline]
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
}
#[inline]
pub fn set_cursor(&self, _: MouseCursor) {
}
#[inline]
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
Ok(())
}
#[inline]
pub fn hidpi_factor(&self) -> f32 {
1.0
}
#[inline]
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
unimplemented!();
}
}
unsafe impl Send for Window {}
unsafe impl Sync for Window {}
#[derive(Clone)]
pub struct WindowProxy;
impl WindowProxy {
#[inline]
pub fn wakeup_event_loop(&self) {
android_glue::wake_event_loop();
}
}

View File

@@ -1,317 +0,0 @@
#![cfg(target_os = "ios")]
use std::os::raw::c_void;
use crate::{
event_loop::EventLoop,
monitor::{MonitorHandle, VideoMode},
window::{Window, WindowBuilder},
};
/// Additional methods on [`EventLoop`] that are specific to iOS.
pub trait EventLoopExtIOS {
/// Returns the [`Idiom`] (phone/tablet/tv/etc) for the current device.
fn idiom(&self) -> Idiom;
}
impl<T: 'static> EventLoopExtIOS for EventLoop<T> {
fn idiom(&self) -> Idiom {
self.event_loop.idiom()
}
}
/// Additional methods on [`Window`] that are specific to iOS.
pub trait WindowExtIOS {
/// Returns a pointer to the [`UIWindow`] that is used by this window.
///
/// The pointer will become invalid when the [`Window`] is destroyed.
///
/// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc
fn ui_window(&self) -> *mut c_void;
/// Returns a pointer to the [`UIViewController`] that is used by this window.
///
/// The pointer will become invalid when the [`Window`] is destroyed.
///
/// [`UIViewController`]: https://developer.apple.com/documentation/uikit/uiviewcontroller?language=objc
fn ui_view_controller(&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.
///
/// [`UIView`]: https://developer.apple.com/documentation/uikit/uiview?language=objc
fn ui_view(&self) -> *mut c_void;
/// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`.
///
/// The default value is device dependent, and it's recommended GLES or Metal applications set
/// this to [`MonitorHandle::scale_factor()`].
///
/// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc
/// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc
fn set_scale_factor(&self, scale_factor: f64);
/// Sets the valid orientations for the [`Window`].
///
/// The default value is [`ValidOrientations::LandscapeAndPortrait`].
///
/// This changes the value returned by
/// [`-[UIViewController supportedInterfaceOrientations]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621435-supportedinterfaceorientations?language=objc),
/// and then calls
/// [`-[UIViewController attemptRotationToDeviceOrientation]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621400-attemptrotationtodeviceorientati?language=objc).
fn set_valid_orientations(&self, valid_orientations: ValidOrientations);
/// Sets whether the [`Window`] prefers the home indicator hidden.
///
/// The default is to prefer showing the home indicator.
///
/// This changes the value returned by
/// [`-[UIViewController prefersHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887510-prefershomeindicatorautohidden?language=objc),
/// and then calls
/// [`-[UIViewController setNeedsUpdateOfHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887509-setneedsupdateofhomeindicatoraut?language=objc).
///
/// This only has an effect on iOS 11.0+.
fn set_prefers_home_indicator_hidden(&self, hidden: bool);
/// Sets the screen edges for which the system gestures will take a lower priority than the
/// application's touch handling.
///
/// This changes the value returned by
/// [`-[UIViewController preferredScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys?language=objc),
/// and then calls
/// [`-[UIViewController setNeedsUpdateOfScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887507-setneedsupdateofscreenedgesdefer?language=objc).
///
/// This only has an effect on iOS 11.0+.
fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge);
/// Sets whether the [`Window`] prefers the status bar hidden.
///
/// The default is to prefer showing the status bar.
///
/// This changes the value returned by
/// [`-[UIViewController prefersStatusBarHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621440-prefersstatusbarhidden?language=objc),
/// and then calls
/// [`-[UIViewController setNeedsStatusBarAppearanceUpdate]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc).
fn set_prefers_status_bar_hidden(&self, hidden: bool);
}
impl WindowExtIOS for Window {
#[inline]
fn ui_window(&self) -> *mut c_void {
self.window.ui_window() as _
}
#[inline]
fn ui_view_controller(&self) -> *mut c_void {
self.window.ui_view_controller() as _
}
#[inline]
fn ui_view(&self) -> *mut c_void {
self.window.ui_view() as _
}
#[inline]
fn set_scale_factor(&self, scale_factor: f64) {
self.window.set_scale_factor(scale_factor)
}
#[inline]
fn set_valid_orientations(&self, valid_orientations: ValidOrientations) {
self.window.set_valid_orientations(valid_orientations)
}
#[inline]
fn set_prefers_home_indicator_hidden(&self, hidden: bool) {
self.window.set_prefers_home_indicator_hidden(hidden)
}
#[inline]
fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) {
self.window
.set_preferred_screen_edges_deferring_system_gestures(edges)
}
#[inline]
fn set_prefers_status_bar_hidden(&self, hidden: bool) {
self.window.set_prefers_status_bar_hidden(hidden)
}
}
/// Additional methods on [`WindowBuilder`] that are specific to iOS.
pub trait WindowBuilderExtIOS {
/// Sets the root view class used by the [`Window`], otherwise a barebones [`UIView`] is provided.
///
/// An instance of the class will be initialized by calling [`-[UIView initWithFrame:]`](https://developer.apple.com/documentation/uikit/uiview/1622488-initwithframe?language=objc).
///
/// [`UIView`]: https://developer.apple.com/documentation/uikit/uiview?language=objc
fn with_root_view_class(self, root_view_class: *const c_void) -> WindowBuilder;
/// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`.
///
/// The default value is device dependent, and it's recommended GLES or Metal applications set
/// this to [`MonitorHandle::scale_factor()`].
///
/// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc
/// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc
fn with_scale_factor(self, scale_factor: f64) -> WindowBuilder;
/// Sets the valid orientations for the [`Window`].
///
/// The default value is [`ValidOrientations::LandscapeAndPortrait`].
///
/// This sets the initial value returned by
/// [`-[UIViewController supportedInterfaceOrientations]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621435-supportedinterfaceorientations?language=objc).
fn with_valid_orientations(self, valid_orientations: ValidOrientations) -> WindowBuilder;
/// Sets whether the [`Window`] prefers the home indicator hidden.
///
/// The default is to prefer showing the home indicator.
///
/// This sets the initial value returned by
/// [`-[UIViewController prefersHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887510-prefershomeindicatorautohidden?language=objc).
///
/// This only has an effect on iOS 11.0+.
fn with_prefers_home_indicator_hidden(self, hidden: bool) -> WindowBuilder;
/// Sets the screen edges for which the system gestures will take a lower priority than the
/// application's touch handling.
///
/// This sets the initial value returned by
/// [`-[UIViewController preferredScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys?language=objc).
///
/// This only has an effect on iOS 11.0+.
fn with_preferred_screen_edges_deferring_system_gestures(
self,
edges: ScreenEdge,
) -> WindowBuilder;
/// Sets whether the [`Window`] prefers the status bar hidden.
///
/// The default is to prefer showing the status bar.
///
/// This sets the initial value returned by
/// [`-[UIViewController prefersStatusBarHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621440-prefersstatusbarhidden?language=objc).
fn with_prefers_status_bar_hidden(self, hidden: bool) -> WindowBuilder;
}
impl WindowBuilderExtIOS for WindowBuilder {
#[inline]
fn with_root_view_class(mut self, root_view_class: *const c_void) -> WindowBuilder {
self.platform_specific.root_view_class = unsafe { &*(root_view_class as *const _) };
self
}
#[inline]
fn with_scale_factor(mut self, scale_factor: f64) -> WindowBuilder {
self.platform_specific.scale_factor = Some(scale_factor);
self
}
#[inline]
fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> WindowBuilder {
self.platform_specific.valid_orientations = valid_orientations;
self
}
#[inline]
fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> WindowBuilder {
self.platform_specific.prefers_home_indicator_hidden = hidden;
self
}
#[inline]
fn with_preferred_screen_edges_deferring_system_gestures(
mut self,
edges: ScreenEdge,
) -> WindowBuilder {
self.platform_specific
.preferred_screen_edges_deferring_system_gestures = edges;
self
}
#[inline]
fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> WindowBuilder {
self.platform_specific.prefers_status_bar_hidden = hidden;
self
}
}
/// Additional methods on [`MonitorHandle`] that are specific to iOS.
pub trait MonitorHandleExtIOS {
/// Returns a pointer to the [`UIScreen`] that is used by this monitor.
///
/// [`UIScreen`]: https://developer.apple.com/documentation/uikit/uiscreen?language=objc
fn ui_screen(&self) -> *mut c_void;
/// Returns the preferred [`VideoMode`] for this monitor.
///
/// This translates to a call to [`-[UIScreen preferredMode]`](https://developer.apple.com/documentation/uikit/uiscreen/1617823-preferredmode?language=objc).
fn preferred_video_mode(&self) -> VideoMode;
}
impl MonitorHandleExtIOS for MonitorHandle {
#[inline]
fn ui_screen(&self) -> *mut c_void {
self.inner.ui_screen() as _
}
#[inline]
fn preferred_video_mode(&self) -> VideoMode {
self.inner.preferred_video_mode()
}
}
/// Valid orientations for a particular [`Window`].
#[derive(Clone, Copy, Debug)]
pub enum ValidOrientations {
/// Excludes `PortraitUpsideDown` on iphone
LandscapeAndPortrait,
Landscape,
/// Excludes `PortraitUpsideDown` on iphone
Portrait,
}
impl Default for ValidOrientations {
#[inline]
fn default() -> ValidOrientations {
ValidOrientations::LandscapeAndPortrait
}
}
/// The device [idiom].
///
/// [idiom]: https://developer.apple.com/documentation/uikit/uidevice/1620037-userinterfaceidiom?language=objc
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Idiom {
Unspecified,
/// iPhone and iPod touch.
Phone,
/// iPad.
Pad,
/// tvOS and Apple TV.
TV,
CarPlay,
}
bitflags! {
/// The [edges] of a screen.
///
/// [edges]: https://developer.apple.com/documentation/uikit/uirectedge?language=objc
#[derive(Default)]
pub struct ScreenEdge: u8 {
const NONE = 0;
const TOP = 1 << 0;
const LEFT = 1 << 1;
const BOTTOM = 1 << 2;
const RIGHT = 1 << 3;
const ALL = ScreenEdge::TOP.bits | ScreenEdge::LEFT.bits
| ScreenEdge::BOTTOM.bits | ScreenEdge::RIGHT.bits;
}
}

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

@@ -0,0 +1,107 @@
use std::ffi::CString;
use libc;
use objc::runtime::{ Object, Class };
#[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 CFTimeInterval = f64;
pub type Boolean = u32;
#[allow(non_upper_case_globals)]
pub const kCFRunLoopRunHandledSource: i32 = 4;
#[cfg(target_pointer_width = "32")]
pub type CGFloat = f32;
#[cfg(target_pointer_width = "64")]
pub type CGFloat = f64;
#[cfg(target_pointer_width = "32")]
pub type NSUInteger = u32;
#[cfg(target_pointer_width = "64")]
pub type NSUInteger = u64;
#[repr(C)]
#[derive(Debug, Clone)]
pub struct CGPoint {
pub x: CGFloat,
pub y: CGFloat,
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct CGRect {
pub origin: CGPoint,
pub size: CGSize
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct CGSize {
pub width: CGFloat,
pub height: CGFloat
}
#[link(name = "UIKit", kind = "framework")]
#[link(name = "CoreFoundation", kind = "framework")]
#[link(name = "GlKit", kind = "framework")]
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;
// SInt32 CFRunLoopRunInMode ( CFStringRef mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled );
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 trait NSString {
unsafe fn alloc(_: Self) -> id {
msg_send![class("NSString"), alloc]
}
#[allow(non_snake_case)]
unsafe fn initWithUTF8String_(self, c_string: *const i8) -> id;
#[allow(non_snake_case)]
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;
}
impl NSString for id {
unsafe fn initWithUTF8String_(self, c_string: *const i8) -> id {
msg_send![self, initWithUTF8String:c_string as id]
}
unsafe fn stringByAppendingString_(self, other: id) -> id {
msg_send![self, stringByAppendingString:other]
}
unsafe fn init_str(self, string: &str) -> id {
let cstring = CString::new(string).unwrap();
self.initWithUTF8String_(cstring.as_ptr())
}
unsafe fn UTF8String(self) -> *const libc::c_char {
msg_send![self, UTF8String]
}
}
#[inline]
pub fn class(name: &str) -> *mut Class {
unsafe {
::std::mem::transmute(Class::get(name))
}
}

497
src/platform/ios/mod.rs Normal file
View File

@@ -0,0 +1,497 @@
//! iOS support
//!
//! # Building app
//! To build ios app you will need rustc built for this targets:
//!
//! - armv7-apple-ios
//! - armv7s-apple-ios
//! - i386-apple-ios
//! - aarch64-apple-ios
//! - x86_64-apple-ios
//!
//! Then
//!
//! ```
//! cargo build --target=...
//! ```
//! The simplest way to integrate your app into xcode environment is to build it
//! as a static library. Wrap your main function and export it.
//!
//! ```rust, ignore
//! #[no_mangle]
//! pub extern fn start_glutin_app() {
//! start_inner()
//! }
//!
//! fn start_inner() {
//! ...
//! }
//!
//! ```
//!
//! Compile project and then drag resulting .a into Xcode project. Add glutin.h to xcode.
//!
//! ```ignore
//! void start_glutin_app();
//! ```
//!
//! Use start_glutin_app inside your xcode's main function.
//!
//!
//! # App lifecycle and events
//!
//! iOS environment is very different from other platforms and you must be very
//! careful with it's events. Familiarize yourself with
//! [app lifecycle](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/).
//!
//!
//! This is how those event are represented in glutin:
//!
//! - applicationDidBecomeActive is Focused(true)
//! - applicationWillResignActive is Focused(false)
//! - applicationDidEnterBackground is Suspended(true)
//! - applicationWillEnterForeground is Suspended(false)
//! - applicationWillTerminate is Closed
//!
//! Keep in mind that after Closed event is received every attempt to draw with
//! opengl will result in segfault.
//!
//! Also note that app will not receive Closed event if suspended, it will be SIGKILL'ed
#![cfg(target_os = "ios")]
use std::collections::VecDeque;
use std::ptr;
use std::mem;
use std::os::raw::c_void;
use libc;
use libc::c_int;
use objc::runtime::{Class, Object, Sel, BOOL, YES };
use objc::declare::{ ClassDecl };
use native_monitor::NativeMonitorId;
use { CreationError, CursorState, MouseCursor, WindowAttributes };
use WindowEvent as Event;
use events::{ Touch, TouchPhase };
mod ffi;
use self::ffi::{
setjmp,
UIApplicationMain,
CFTimeInterval,
CFRunLoopRunInMode,
kCFRunLoopDefaultMode,
kCFRunLoopRunHandledSource,
id,
nil,
NSString,
CGFloat,
longjmp,
CGRect,
CGPoint
};
static mut jmpbuf: [c_int;27] = [0;27];
#[derive(Clone)]
pub struct MonitorId;
pub struct Window {
delegate_state: *mut DelegateState
}
#[derive(Clone)]
pub struct WindowProxy;
pub struct PollEventsIterator<'a> {
window: &'a Window,
}
pub struct WaitEventsIterator<'a> {
window: &'a Window,
}
#[derive(Debug)]
struct DelegateState {
events_queue: VecDeque<Event>,
window: id,
controller: id,
size: (u32,u32),
scale: f32
}
impl DelegateState {
#[inline]
fn new(window: id, controller:id, size: (u32,u32), scale: f32) -> DelegateState {
DelegateState {
events_queue: VecDeque::new(),
window: window,
controller: controller,
size: size,
scale: scale
}
}
}
#[inline]
pub fn get_available_monitors() -> VecDeque<MonitorId> {
let mut rb = VecDeque::new();
rb.push_back(MonitorId);
rb
}
#[inline]
pub fn get_primary_monitor() -> MonitorId {
MonitorId
}
impl MonitorId {
#[inline]
pub fn get_name(&self) -> Option<String> {
Some("Primary".to_string())
}
#[inline]
pub fn get_native_identifier(&self) -> NativeMonitorId {
NativeMonitorId::Unavailable
}
#[inline]
pub fn get_dimensions(&self) -> (u32, u32) {
unimplemented!()
}
}
gen_api_transition!();
#[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes;
impl Window {
pub fn new(_: &WindowAttributes, _: &PlatformSpecificWindowBuilderAttributes) -> Result<Window, CreationError>
{
unsafe {
if setjmp(mem::transmute(&mut jmpbuf)) != 0 {
let app: id = msg_send![Class::get("UIApplication").unwrap(), sharedApplication];
let delegate: id = msg_send![app, delegate];
let state: *mut c_void = *(&*delegate).get_ivar("glutinState");
let state = state as *mut DelegateState;
let window = Window {
delegate_state: state
};
return Ok(window)
}
}
Window::create_delegate_class();
Window::create_view_class();
Window::start_app();
Err(CreationError::OsError(format!("Couldn't create UIApplication")))
}
fn create_delegate_class() {
extern fn did_finish_launching(this: &mut Object, _: Sel, _: id, _: id) -> BOOL {
unsafe {
let main_screen: id = msg_send![Class::get("UIScreen").unwrap(), mainScreen];
let bounds: CGRect = msg_send![main_screen, bounds];
let scale: CGFloat = msg_send![main_screen, nativeScale];
let window: id = msg_send![Class::get("UIWindow").unwrap(), alloc];
let window: id = msg_send![window, initWithFrame:bounds.clone()];
let size = (bounds.size.width as u32, bounds.size.height as u32);
let view_controller: id = msg_send![Class::get("MainViewController").unwrap(), alloc];
let view_controller: id = msg_send![view_controller, init];
let _: () = msg_send![window, setRootViewController:view_controller];
let _: () = msg_send![window, makeKeyAndVisible];
let state = Box::new(DelegateState::new(window, view_controller, size, scale as f32));
let state_ptr: *mut DelegateState = mem::transmute(state);
this.set_ivar("glutinState", state_ptr as *mut c_void);
let _: () = msg_send![this, performSelector:sel!(postLaunch:) withObject:nil afterDelay:0.0];
}
YES
}
extern fn post_launch(_: &Object, _: Sel, _: id) {
unsafe { longjmp(mem::transmute(&mut jmpbuf),1); }
}
extern fn did_become_active(this: &Object, _: Sel, _: id) {
unsafe {
let state: *mut c_void = *this.get_ivar("glutinState");
let state = &mut *(state as *mut DelegateState);
state.events_queue.push_back(Event::Focused(true));
}
}
extern fn will_resign_active(this: &Object, _: Sel, _: id) {
unsafe {
let state: *mut c_void = *this.get_ivar("glutinState");
let state = &mut *(state as *mut DelegateState);
state.events_queue.push_back(Event::Focused(false));
}
}
extern fn will_enter_foreground(this: &Object, _: Sel, _: id) {
unsafe {
let state: *mut c_void = *this.get_ivar("glutinState");
let state = &mut *(state as *mut DelegateState);
state.events_queue.push_back(Event::Suspended(false));
}
}
extern fn did_enter_background(this: &Object, _: Sel, _: id) {
unsafe {
let state: *mut c_void = *this.get_ivar("glutinState");
let state = &mut *(state as *mut DelegateState);
state.events_queue.push_back(Event::Suspended(true));
}
}
extern fn will_terminate(this: &Object, _: Sel, _: id) {
unsafe {
let state: *mut c_void = *this.get_ivar("glutinState");
let state = &mut *(state as *mut DelegateState);
// push event to the front to garantee that we'll process it
// immidiatly after jump
state.events_queue.push_front(Event::Closed);
longjmp(mem::transmute(&mut jmpbuf),1);
}
}
extern fn handle_touches(this: &Object, _: Sel, touches: id, _:id) {
unsafe {
let state: *mut c_void = *this.get_ivar("glutinState");
let state = &mut *(state as *mut DelegateState);
let touches_enum: id = msg_send![touches, objectEnumerator];
loop {
let touch: id = msg_send![touches_enum, nextObject];
if touch == nil {
break
}
let location: CGPoint = msg_send![touch, locationInView:nil];
let touch_id = touch as u64;
let phase: i32 = msg_send![touch, phase];
state.events_queue.push_back(Event::Touch(Touch {
id: touch_id,
location: (location.x as f64, location.y as f64),
phase: match phase {
0 => TouchPhase::Started,
1 => TouchPhase::Moved,
// 2 is UITouchPhaseStationary and is not expected here
3 => TouchPhase::Ended,
4 => TouchPhase::Cancelled,
_ => panic!("unexpected touch phase: {:?}", phase)
}
}));
}
}
}
let ui_responder = Class::get("UIResponder").unwrap();
let mut decl = ClassDecl::new("AppDelegate", ui_responder).unwrap();
unsafe {
decl.add_method(sel!(application:didFinishLaunchingWithOptions:),
did_finish_launching as extern fn(&mut Object, Sel, id, id) -> BOOL);
decl.add_method(sel!(applicationDidBecomeActive:),
did_become_active as extern fn(&Object, Sel, id));
decl.add_method(sel!(applicationWillResignActive:),
will_resign_active as extern fn(&Object, Sel, id));
decl.add_method(sel!(applicationWillEnterForeground:),
will_enter_foreground as extern fn(&Object, Sel, id));
decl.add_method(sel!(applicationDidEnterBackground:),
did_enter_background as extern fn(&Object, Sel, id));
decl.add_method(sel!(applicationWillTerminate:),
will_terminate as extern fn(&Object, Sel, id));
decl.add_method(sel!(touchesBegan:withEvent:),
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
decl.add_method(sel!(touchesMoved:withEvent:),
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
decl.add_method(sel!(touchesEnded:withEvent:),
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
decl.add_method(sel!(touchesCancelled:withEvent:),
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
decl.add_method(sel!(postLaunch:),
post_launch as extern fn(&Object, Sel, id));
decl.add_ivar::<*mut c_void>("glutinState");
decl.register();
}
}
fn create_view_class() {
let ui_view_controller = Class::get("UIViewController").unwrap();
let decl = ClassDecl::new("MainViewController", ui_view_controller).unwrap();
decl.register();
}
#[inline]
fn start_app() {
unsafe {
UIApplicationMain(0, ptr::null(), nil, NSString::alloc(nil).init_str("AppDelegate"));
}
}
#[inline]
pub fn set_title(&self, _: &str) {
}
#[inline]
pub fn show(&self) {
}
#[inline]
pub fn hide(&self) {
}
#[inline]
pub fn get_position(&self) -> Option<(i32, i32)> {
None
}
#[inline]
pub fn set_position(&self, _x: i32, _y: i32) {
}
#[inline]
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
unsafe { Some((&*self.delegate_state).size) }
}
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
self.get_inner_size()
}
#[inline]
pub fn set_inner_size(&self, _x: u32, _y: u32) {
}
#[inline]
pub fn poll_events(&self) -> PollEventsIterator {
PollEventsIterator {
window: self
}
}
#[inline]
pub fn wait_events(&self) -> WaitEventsIterator {
WaitEventsIterator {
window: self
}
}
#[inline]
pub fn platform_display(&self) -> *mut libc::c_void {
unimplemented!();
}
#[inline]
pub fn platform_window(&self) -> *mut libc::c_void {
unimplemented!()
}
#[inline]
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
}
#[inline]
pub fn set_cursor(&self, _: MouseCursor) {
}
#[inline]
pub fn set_cursor_state(&self, _: CursorState) -> Result<(), String> {
Ok(())
}
#[inline]
pub fn hidpi_factor(&self) -> f32 {
unsafe { (&*self.delegate_state) }.scale
}
#[inline]
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
unimplemented!();
}
#[inline]
pub fn create_window_proxy(&self) -> WindowProxy {
WindowProxy
}
}
impl WindowProxy {
#[inline]
pub fn wakeup_event_loop(&self) {
unimplemented!()
}
}
impl<'a> Iterator for WaitEventsIterator<'a> {
type Item = Event;
#[inline]
fn next(&mut self) -> Option<Event> {
loop {
if let Some(ev) = self.window.poll_events().next() {
return Some(ev);
}
}
}
}
impl<'a> Iterator for PollEventsIterator<'a> {
type Item = Event;
fn next(&mut self) -> Option<Event> {
unsafe {
let state = &mut *self.window.delegate_state;
if let Some(event) = state.events_queue.pop_front() {
return Some(event)
}
// jump hack, so we won't quit on willTerminate event before processing it
if setjmp(mem::transmute(&mut jmpbuf)) != 0 {
return state.events_queue.pop_front()
}
// run runloop
let seconds: CFTimeInterval = 0.000002;
while CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, 1) == kCFRunLoopRunHandledSource {}
state.events_queue.pop_front()
}
}
}

View File

@@ -0,0 +1,15 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
#![allow(dead_code)]
use std::os::raw::{c_void, c_char, c_int};
pub const RTLD_LAZY: c_int = 0x001;
pub const RTLD_NOW: c_int = 0x002;
#[link="dl"]
extern {
pub fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void;
pub fn dlerror() -> *mut c_char;
pub fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
pub fn dlclose(handle: *mut c_void) -> c_int;
}

345
src/platform/linux/mod.rs Normal file
View File

@@ -0,0 +1,345 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
use std::collections::VecDeque;
use std::sync::Arc;
use CreationError;
use CursorState;
use MouseCursor;
use libc;
use self::x11::XConnection;
use self::x11::XError;
use self::x11::XNotSupported;
use self::x11::ffi::XVisualInfo;
mod dlopen;
pub mod wayland;
pub mod x11;
#[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes {
pub visual_infos: Option<XVisualInfo>,
pub screen_id: Option<i32>,
}
pub enum UnixBackend {
X(Arc<XConnection>),
Wayland(Arc<wayland::WaylandContext>),
Error(XNotSupported),
}
lazy_static!(
pub static ref UNIX_BACKEND: UnixBackend = {
if let Some(ctxt) = wayland::WaylandContext::init() {
UnixBackend::Wayland(Arc::new(ctxt))
} else {
match XConnection::new(Some(x_error_callback)) {
Ok(x) => UnixBackend::X(Arc::new(x)),
Err(e) => UnixBackend::Error(e),
}
}
};
);
pub enum Window2 {
#[doc(hidden)]
X(x11::Window2),
#[doc(hidden)]
Wayland(wayland::Window)
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum WindowId {
#[doc(hidden)]
X(x11::WindowId),
#[doc(hidden)]
Wayland(wayland::WindowId)
}
#[derive(Clone)]
pub enum MonitorId {
#[doc(hidden)]
X(x11::MonitorId),
#[doc(hidden)]
Wayland(wayland::MonitorId),
#[doc(hidden)]
None,
}
#[inline]
pub fn get_available_monitors() -> VecDeque<MonitorId> {
match *UNIX_BACKEND {
UnixBackend::Wayland(ref ctxt) => wayland::get_available_monitors(ctxt)
.into_iter()
.map(MonitorId::Wayland)
.collect(),
UnixBackend::X(ref connec) => x11::get_available_monitors(connec)
.into_iter()
.map(MonitorId::X)
.collect(),
UnixBackend::Error(_) => { let mut d = VecDeque::new(); d.push_back(MonitorId::None); d},
}
}
#[inline]
pub fn get_primary_monitor() -> MonitorId {
match *UNIX_BACKEND {
UnixBackend::Wayland(ref ctxt) => MonitorId::Wayland(wayland::get_primary_monitor(ctxt)),
UnixBackend::X(ref connec) => MonitorId::X(x11::get_primary_monitor(connec)),
UnixBackend::Error(_) => MonitorId::None,
}
}
impl MonitorId {
#[inline]
pub fn get_name(&self) -> Option<String> {
match self {
&MonitorId::X(ref m) => m.get_name(),
&MonitorId::Wayland(ref m) => m.get_name(),
&MonitorId::None => None,
}
}
#[inline]
pub fn get_native_identifier(&self) -> ::native_monitor::NativeMonitorId {
match self {
&MonitorId::X(ref m) => m.get_native_identifier(),
&MonitorId::Wayland(ref m) => m.get_native_identifier(),
&MonitorId::None => unimplemented!() // FIXME:
}
}
#[inline]
pub fn get_dimensions(&self) -> (u32, u32) {
match self {
&MonitorId::X(ref m) => m.get_dimensions(),
&MonitorId::Wayland(ref m) => m.get_dimensions(),
&MonitorId::None => (800, 600), // FIXME:
}
}
}
impl Window2 {
#[inline]
pub fn new(events_loop: ::std::sync::Arc<EventsLoop>, window: &::WindowAttributes,
pl_attribs: &PlatformSpecificWindowBuilderAttributes)
-> Result<Window2, CreationError>
{
match *UNIX_BACKEND {
UnixBackend::Wayland(ref ctxt) => {
if let EventsLoop::Wayland(ref evlp) = *events_loop {
wayland::Window::new(evlp, ctxt.clone(), window).map(Window2::Wayland)
} else {
// It is not possible to instanciate an EventsLoop not matching its backend
unreachable!()
}
},
UnixBackend::X(ref connec) => {
x11::Window2::new(events_loop, connec, window, pl_attribs).map(Window2::X)
},
UnixBackend::Error(_) => {
// If the Backend is Error(), it is not possible to instanciate an EventsLoop at all,
// thus this function cannot be called!
unreachable!()
}
}
}
#[inline]
pub fn id(&self) -> WindowId {
match self {
&Window2::X(ref w) => WindowId::X(w.id()),
&Window2::Wayland(ref w) => WindowId::Wayland(w.id())
}
}
#[inline]
pub fn set_title(&self, title: &str) {
match self {
&Window2::X(ref w) => w.set_title(title),
&Window2::Wayland(ref w) => w.set_title(title)
}
}
#[inline]
pub fn show(&self) {
match self {
&Window2::X(ref w) => w.show(),
&Window2::Wayland(ref w) => w.show()
}
}
#[inline]
pub fn hide(&self) {
match self {
&Window2::X(ref w) => w.hide(),
&Window2::Wayland(ref w) => w.hide()
}
}
#[inline]
pub fn get_position(&self) -> Option<(i32, i32)> {
match self {
&Window2::X(ref w) => w.get_position(),
&Window2::Wayland(ref w) => w.get_position()
}
}
#[inline]
pub fn set_position(&self, x: i32, y: i32) {
match self {
&Window2::X(ref w) => w.set_position(x, y),
&Window2::Wayland(ref w) => w.set_position(x, y)
}
}
#[inline]
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
match self {
&Window2::X(ref w) => w.get_inner_size(),
&Window2::Wayland(ref w) => w.get_inner_size()
}
}
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
match self {
&Window2::X(ref w) => w.get_outer_size(),
&Window2::Wayland(ref w) => w.get_outer_size()
}
}
#[inline]
pub fn set_inner_size(&self, x: u32, y: u32) {
match self {
&Window2::X(ref w) => w.set_inner_size(x, y),
&Window2::Wayland(ref w) => w.set_inner_size(x, y)
}
}
#[inline]
pub fn set_cursor(&self, cursor: MouseCursor) {
match self {
&Window2::X(ref w) => w.set_cursor(cursor),
&Window2::Wayland(ref w) => w.set_cursor(cursor)
}
}
#[inline]
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
match self {
&Window2::X(ref w) => w.set_cursor_state(state),
&Window2::Wayland(ref w) => w.set_cursor_state(state)
}
}
#[inline]
pub fn hidpi_factor(&self) -> f32 {
match self {
&Window2::X(ref w) => w.hidpi_factor(),
&Window2::Wayland(ref w) => w.hidpi_factor()
}
}
#[inline]
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
match self {
&Window2::X(ref w) => w.set_cursor_position(x, y),
&Window2::Wayland(ref w) => w.set_cursor_position(x, y)
}
}
#[inline]
pub fn platform_display(&self) -> *mut libc::c_void {
use wayland_client::Proxy;
match self {
&Window2::X(ref w) => w.platform_display(),
&Window2::Wayland(ref w) => w.get_display().ptr() as *mut _
}
}
#[inline]
pub fn platform_window(&self) -> *mut libc::c_void {
use wayland_client::Proxy;
match self {
&Window2::X(ref w) => w.platform_window(),
&Window2::Wayland(ref w) => w.get_surface().ptr() as *mut _
}
}
}
unsafe extern "C" fn x_error_callback(dpy: *mut x11::ffi::Display, event: *mut x11::ffi::XErrorEvent)
-> libc::c_int
{
use std::ffi::CStr;
if let UnixBackend::X(ref x) = *UNIX_BACKEND {
let mut buff: Vec<u8> = Vec::with_capacity(1024);
(x.xlib.XGetErrorText)(dpy, (*event).error_code as i32, buff.as_mut_ptr() as *mut libc::c_char, buff.capacity() as i32);
let description = CStr::from_ptr(buff.as_mut_ptr() as *const libc::c_char).to_string_lossy();
let error = XError {
description: description.into_owned(),
error_code: (*event).error_code,
request_code: (*event).request_code,
minor_code: (*event).minor_code,
};
*x.latest_error.lock().unwrap() = Some(error);
}
0
}
pub enum EventsLoop {
#[doc(hidden)]
Wayland(wayland::EventsLoop),
#[doc(hidden)]
X(x11::EventsLoop)
}
impl EventsLoop {
pub fn new() -> EventsLoop {
match *UNIX_BACKEND {
UnixBackend::Wayland(ref ctxt) => {
EventsLoop::Wayland(wayland::EventsLoop::new(ctxt.clone()))
},
UnixBackend::X(_) => {
EventsLoop::X(x11::EventsLoop::new())
},
UnixBackend::Error(_) => {
panic!("Attempted to create an EventsLoop while no backend was available.")
}
}
}
pub fn interrupt(&self) {
match *self {
EventsLoop::Wayland(ref evlp) => evlp.interrupt(),
EventsLoop::X(ref evlp) => evlp.interrupt()
}
}
pub fn poll_events<F>(&self, callback: F)
where F: FnMut(::Event)
{
match *self {
EventsLoop::Wayland(ref evlp) => evlp.poll_events(callback),
EventsLoop::X(ref evlp) => evlp.poll_events(callback)
}
}
pub fn run_forever<F>(&self, callback: F)
where F: FnMut(::Event)
{
match *self {
EventsLoop::Wayland(ref evlp) => evlp.run_forever(callback),
EventsLoop::X(ref evlp) => evlp.run_forever(callback)
}
}
}

View File

@@ -0,0 +1,297 @@
use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use wayland_client::{EnvHandler, default_connect, EventQueue, EventQueueHandle, Init, Proxy};
use wayland_client::protocol::{wl_compositor, wl_seat, wl_shell, wl_shm, wl_subcompositor,
wl_display, wl_registry, wl_output, wl_surface};
use super::wayland_window;
/*
* Registry and globals handling
*/
wayland_env!(InnerEnv,
compositor: wl_compositor::WlCompositor,
shell: wl_shell::WlShell,
shm: wl_shm::WlShm,
subcompositor: wl_subcompositor::WlSubcompositor
);
struct WaylandEnv {
registry: wl_registry::WlRegistry,
inner: EnvHandler<InnerEnv>,
monitors: Vec<OutputInfo>,
my_id: usize,
}
struct OutputInfo {
output: wl_output::WlOutput,
id: u32,
scale: f32,
pix_size: (u32, u32),
name: String
}
impl OutputInfo {
fn new(output: wl_output::WlOutput, id: u32) -> OutputInfo {
OutputInfo {
output: output,
id: id,
scale: 1.0,
pix_size: (0, 0),
name: "".into()
}
}
}
impl WaylandEnv {
fn new(registry: wl_registry::WlRegistry) -> WaylandEnv {
WaylandEnv {
registry: registry,
inner: EnvHandler::new(),
monitors: Vec::new(),
my_id: 0,
}
}
fn get_seat(&self) -> Option<wl_seat::WlSeat> {
for &(name, ref interface, version) in self.inner.globals() {
if interface == "wl_seat" {
if version < 5 {
panic!("Winit requires at least version 5 of the wl_seat global.");
}
let seat = self.registry.bind::<wl_seat::WlSeat>(5, name);
return Some(seat)
}
}
None
}
}
impl Init for WaylandEnv {
fn init(&mut self, evqh: &mut EventQueueHandle, index: usize) {
evqh.register::<_, WaylandEnv>(&self.registry, index);
self.my_id = index
}
}
impl wl_registry::Handler for WaylandEnv {
fn global(&mut self,
evqh: &mut EventQueueHandle,
registry: &wl_registry::WlRegistry,
name: u32,
interface: String,
version: u32)
{
if interface == "wl_output" {
// intercept outputs
// this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69)
let output = self.registry.bind::<wl_output::WlOutput>(1, name);
evqh.register::<_, WaylandEnv>(&output, self.my_id);
self.monitors.push(OutputInfo::new(output, name));
}
self.inner.global(evqh, registry, name, interface, version);
}
fn global_remove(&mut self,
evqh: &mut EventQueueHandle,
registry: &wl_registry::WlRegistry,
name: u32)
{
// prune old monitors
self.monitors.retain(|m| m.id != name);
self.inner.global_remove(evqh, registry, name);
}
}
declare_handler!(WaylandEnv, wl_registry::Handler, wl_registry::WlRegistry);
impl wl_output::Handler for WaylandEnv {
fn geometry(&mut self,
_: &mut EventQueueHandle,
proxy: &wl_output::WlOutput,
_x: i32, _y: i32,
_physical_width: i32, _physical_height: i32,
_subpixel: wl_output::Subpixel,
make: String, model: String,
_transform: wl_output::Transform)
{
for m in self.monitors.iter_mut().filter(|m| m.output.equals(proxy)) {
m.name = format!("{} ({})", model, make);
break;
}
}
fn mode(&mut self,
_: &mut EventQueueHandle,
proxy: &wl_output::WlOutput,
flags: wl_output::Mode,
width: i32, height: i32,
_refresh: i32)
{
if flags.contains(wl_output::Current) {
for m in self.monitors.iter_mut().filter(|m| m.output.equals(proxy)) {
m.pix_size = (width as u32, height as u32);
break;
}
}
}
fn scale(&mut self,
_: &mut EventQueueHandle,
proxy: &wl_output::WlOutput,
factor: i32)
{
for m in self.monitors.iter_mut().filter(|m| m.output.equals(proxy)) {
m.scale = factor as f32;
break;
}
}
}
declare_handler!(WaylandEnv, wl_output::Handler, wl_output::WlOutput);
/*
* Main context struct
*/
pub struct WaylandContext {
pub display: wl_display::WlDisplay,
evq: Mutex<EventQueue>,
env_id: usize,
}
impl WaylandContext {
pub fn init() -> Option<WaylandContext> {
// attempt to connect to the wayland server
// this handles both "no libwayland" and "no compositor" cases
let (display, mut event_queue) = match default_connect() {
Ok(ret) => ret,
Err(_) => return None
};
// this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69)
let registry = display.get_registry();
let env_id = event_queue.add_handler_with_init(WaylandEnv::new(registry));
// two syncs fully initialize
event_queue.sync_roundtrip().expect("Wayland connection unexpectedly lost");
event_queue.sync_roundtrip().expect("Wayland connection unexpectedly lost");
Some(WaylandContext {
evq: Mutex::new(event_queue),
display: display,
env_id: env_id
})
}
pub fn dispatch_pending(&self) {
let mut guard = self.evq.lock().unwrap();
guard.dispatch_pending().expect("Wayland connection unexpectedly lost");
}
pub fn dispatch(&self) {
let mut guard = self.evq.lock().unwrap();
guard.dispatch().expect("Wayland connection unexpectedly lost");
}
pub fn flush(&self) {
let _ = self.display.flush();
}
pub fn get_seat(&self) -> Option<wl_seat::WlSeat> {
let mut guard = self.evq.lock().unwrap();
let state = guard.state();
state.get_handler::<WaylandEnv>(self.env_id).get_seat()
}
pub fn with_output<F>(&self, id: MonitorId, f: F) where F: FnOnce(&wl_output::WlOutput) {
let mut guard = self.evq.lock().unwrap();
let state = guard.state();
let env = state.get_handler::<WaylandEnv>(self.env_id);
for m in env.monitors.iter().filter(|m| m.id == id.id) {
f(&m.output);
break
}
}
pub fn create_window<H: wayland_window::Handler>(&self)
-> (Arc<wl_surface::WlSurface>, wayland_window::DecoratedSurface<H>)
{
let mut guard = self.evq.lock().unwrap();
let mut state = guard.state();
let env = state.get_mut_handler::<WaylandEnv>(self.env_id);
// this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69)
let surface = Arc::new(env.inner.compositor.create_surface());
let decorated = wayland_window::DecoratedSurface::new(
&*surface, 800, 600,
&env.inner.compositor,
&env.inner.subcompositor,
&env.inner.shm,
&env.inner.shell,
env.get_seat(),
false
).expect("Failed to create a tmpfile buffer.");
(surface, decorated)
}
}
/*
* Monitors API
*/
pub fn get_primary_monitor(ctxt: &Arc<WaylandContext>) -> MonitorId {
let mut guard = ctxt.evq.lock().unwrap();
let state = guard.state();
let env = state.get_handler::<WaylandEnv>(ctxt.env_id);
if let Some(ref monitor) = env.monitors.iter().next() {
MonitorId {
id: monitor.id,
ctxt: ctxt.clone()
}
} else {
panic!("No monitor is available.")
}
}
pub fn get_available_monitors(ctxt: &Arc<WaylandContext>) -> VecDeque<MonitorId> {
let mut guard = ctxt.evq.lock().unwrap();
let state = guard.state();
let env = state.get_handler::<WaylandEnv>(ctxt.env_id);
env.monitors.iter()
.map(|m| MonitorId { id: m.id, ctxt: ctxt.clone() })
.collect()
}
#[derive(Clone)]
pub struct MonitorId {
id: u32,
ctxt: Arc<WaylandContext>
}
impl MonitorId {
pub fn get_name(&self) -> Option<String> {
let mut guard = self.ctxt.evq.lock().unwrap();
let state = guard.state();
let env = state.get_handler::<WaylandEnv>(self.ctxt.env_id);
for m in env.monitors.iter().filter(|m| m.id == self.id) {
return Some(m.name.clone())
}
// if we reach here, this monitor does not exist any more
None
}
#[inline]
pub fn get_native_identifier(&self) -> ::native_monitor::NativeMonitorId {
::native_monitor::NativeMonitorId::Unavailable
}
pub fn get_dimensions(&self) -> (u32, u32) {
let mut guard = self.ctxt.evq.lock().unwrap();
let state = guard.state();
let env = state.get_handler::<WaylandEnv>(self.ctxt.env_id);
for m in env.monitors.iter().filter(|m| m.id == self.id) {
return m.pix_size
}
// if we reach here, this monitor does not exist any more
(0,0)
}
}

View File

@@ -0,0 +1,592 @@
use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase, ModifiersState};
use std::sync::{Arc, Mutex};
use std::sync::atomic::AtomicBool;
use super::{DecoratedHandler, WindowId, WaylandContext};
use wayland_client::{EventQueue, EventQueueHandle, Init, Proxy};
use wayland_client::protocol::{wl_seat, wl_surface, wl_pointer, wl_keyboard};
use super::make_wid;
use super::wayland_window::DecoratedSurface;
use super::wayland_kbd::MappedKeyboard;
use super::keyboard::KbdHandler;
/// This struct is used as a holder for the callback
/// during the dispatching of events.
///
/// The proper ay to use it is:
/// - set a callback in it (and retrieve the noop one it contains)
/// - dispatch the EventQueue
/// - put back the noop callback in it
///
/// Failure to do so is unsafe™
pub struct EventsLoopSink {
callback: Box<FnMut(::Event)>
}
unsafe impl Send for EventsLoopSink { }
impl EventsLoopSink {
pub fn new() -> EventsLoopSink {
EventsLoopSink {
callback: Box::new(|_| {}),
}
}
pub fn send_event(&mut self, evt: ::WindowEvent, wid: WindowId) {
let evt = ::Event::WindowEvent {
event: evt,
window_id: ::WindowId(::platform::WindowId::Wayland(wid))
};
(self.callback)(evt)
}
// This function is only safe of the set callback is unset before exclusive
// access to the wayland EventQueue is finished.
//
// The callback also cannot be used any longer as long as it has not been
// cleared from the Sink.
unsafe fn set_callback(&mut self, cb: Box<FnMut(::Event)>) -> Box<FnMut(::Event)> {
::std::mem::replace(&mut self.callback, cb)
}
fn with_callback<F: FnOnce(&mut FnMut(::Event))>(&mut self, f: F) {
f(&mut *self.callback)
}
}
pub struct EventsLoop {
// the global wayland context
ctxt: Arc<WaylandContext>,
// our EventQueue
evq: Arc<Mutex<EventQueue>>,
// ids of the DecoratedHandlers of the surfaces we know
decorated_ids: Mutex<Vec<(usize, Arc<wl_surface::WlSurface>)>>,
// our sink, receiver of callbacks, shared with some handlers
sink: Arc<Mutex<EventsLoopSink>>,
// trigger interruption of the run
interrupted: AtomicBool,
// trigger cleanup of the dead surfaces
cleanup_needed: Arc<AtomicBool>,
hid: usize
}
impl EventsLoop {
pub fn new(ctxt: Arc<WaylandContext>) -> EventsLoop {
let mut evq = ctxt.display.create_event_queue();
let sink = Arc::new(Mutex::new(EventsLoopSink::new()));
let hid = evq.add_handler_with_init(InputHandler::new(&ctxt, sink.clone()));
EventsLoop {
ctxt: ctxt,
evq: Arc::new(Mutex::new(evq)),
decorated_ids: Mutex::new(Vec::new()),
sink: sink,
interrupted: AtomicBool::new(false),
cleanup_needed: Arc::new(AtomicBool::new(false)),
hid: hid
}
}
// some internals that Window needs access to
pub fn get_window_init(&self) -> (Arc<Mutex<EventQueue>>, Arc<AtomicBool>) {
(self.evq.clone(), self.cleanup_needed.clone())
}
pub fn register_window(&self, decorated_id: usize, surface: Arc<wl_surface::WlSurface>) {
self.decorated_ids.lock().unwrap().push((decorated_id, surface.clone()));
let mut guard = self.evq.lock().unwrap();
let mut state = guard.state();
state.get_mut_handler::<InputHandler>(self.hid).windows.push(surface);
}
fn process_resize(evq: &mut EventQueue, ids: &[(usize, Arc<wl_surface::WlSurface>)], callback: &mut FnMut(::Event))
{
let mut state = evq.state();
for &(decorated_id, ref window) in ids {
let decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(decorated_id);
if let Some((w, h)) = decorated.handler().as_mut().and_then(|h| h.take_newsize()) {
decorated.resize(w as i32, h as i32);
callback(
::Event::WindowEvent {
window_id: ::WindowId(::platform::WindowId::Wayland(make_wid(&window))),
event: ::WindowEvent::Resized(w,h)
}
);
}
}
}
pub fn interrupt(&self) {
self.interrupted.store(true, ::std::sync::atomic::Ordering::Relaxed);
}
fn prune_dead_windows(&self) {
self.decorated_ids.lock().unwrap().retain(|&(_, ref w)| w.is_alive());
let mut evq_guard = self.evq.lock().unwrap();
let mut state = evq_guard.state();
let handler = state.get_mut_handler::<InputHandler>(self.hid);
handler.windows.retain(|w| w.is_alive());
if let Some(w) = handler.mouse_focus.take() {
if w.is_alive() {
handler.mouse_focus = Some(w)
}
}
}
pub fn poll_events<F>(&self, callback: F)
where F: FnMut(::Event)
{
// send pending requests to the server...
self.ctxt.flush();
// first of all, get exclusive access to this event queue
let mut evq_guard = self.evq.lock().unwrap();
// read some events from the socket if some are waiting & queue is empty
if let Some(guard) = evq_guard.prepare_read() {
guard.read_events().expect("Wayland connection unexpectedly lost");
}
// set the callback into the sink
// we extend the lifetime of the closure to 'static to be able to put it in
// the sink, but we'll explicitly drop it at the end of this function, so it's fine
let static_cb = unsafe { ::std::mem::transmute(Box::new(callback) as Box<FnMut(_)>) };
let old_cb = unsafe { self.sink.lock().unwrap().set_callback(static_cb) };
// then do the actual dispatching
self.ctxt.dispatch_pending();
evq_guard.dispatch_pending().expect("Wayland connection unexpectedly lost");
let mut sink_guard = self.sink.lock().unwrap();
// events where probably dispatched, process resize
let ids_guard = self.decorated_ids.lock().unwrap();
sink_guard.with_callback(
|cb| Self::process_resize(&mut evq_guard, &ids_guard, cb)
);
// replace the old noop callback
unsafe { self.sink.lock().unwrap().set_callback(old_cb) };
if self.cleanup_needed.swap(false, ::std::sync::atomic::Ordering::Relaxed) {
self.prune_dead_windows()
}
}
pub fn run_forever<F>(&self, callback: F)
where F: FnMut(::Event)
{
// send pending requests to the server...
self.ctxt.flush();
// first of all, get exclusive access to this event queue
let mut evq_guard = self.evq.lock().unwrap();
// set the callback into the sink
// we extend the lifetime of the closure to 'static to be able to put it in
// the sink, but we'll explicitly drop it at the end of this function, so it's fine
let static_cb = unsafe { ::std::mem::transmute(Box::new(callback) as Box<FnMut(_)>) };
let old_cb = unsafe { self.sink.lock().unwrap().set_callback(static_cb) };
while !self.interrupted.load(::std::sync::atomic::Ordering::Relaxed) {
self.ctxt.dispatch();
evq_guard.dispatch_pending().expect("Wayland connection unexpectedly lost");
let ids_guard = self.decorated_ids.lock().unwrap();
self.sink.lock().unwrap().with_callback(
|cb| Self::process_resize(&mut evq_guard, &ids_guard, cb)
);
self.ctxt.flush();
if self.cleanup_needed.swap(false, ::std::sync::atomic::Ordering::Relaxed) {
self.prune_dead_windows()
}
}
// replace the old noop callback
unsafe { self.sink.lock().unwrap().set_callback(old_cb) };
}
}
enum KbdType {
Mapped(MappedKeyboard<KbdHandler>),
Plain(Option<WindowId>)
}
struct InputHandler {
my_id: usize,
windows: Vec<Arc<wl_surface::WlSurface>>,
seat: Option<wl_seat::WlSeat>,
mouse: Option<wl_pointer::WlPointer>,
mouse_focus: Option<Arc<wl_surface::WlSurface>>,
mouse_location: (i32, i32),
axis_buffer: Option<(f32, f32)>,
axis_discrete_buffer: Option<(i32, i32)>,
axis_state: TouchPhase,
kbd: Option<wl_keyboard::WlKeyboard>,
kbd_handler: KbdType,
callback: Arc<Mutex<EventsLoopSink>>
}
impl InputHandler {
fn new(ctxt: &WaylandContext, sink: Arc<Mutex<EventsLoopSink>>) -> InputHandler {
let kbd_handler = match MappedKeyboard::new(KbdHandler::new(sink.clone())) {
Ok(h) => KbdType::Mapped(h),
Err(_) => KbdType::Plain(None)
};
InputHandler {
my_id: 0,
windows: Vec::new(),
seat: ctxt.get_seat(),
mouse: None,
mouse_focus: None,
mouse_location: (0,0),
axis_buffer: None,
axis_discrete_buffer: None,
axis_state: TouchPhase::Started,
kbd: None,
kbd_handler: kbd_handler,
callback: sink
}
}
}
impl Init for InputHandler {
fn init(&mut self, evqh: &mut EventQueueHandle, index: usize) {
if let Some(ref seat) = self.seat {
evqh.register::<_, InputHandler>(seat, index);
}
self.my_id = index;
}
}
impl wl_seat::Handler for InputHandler {
fn capabilities(&mut self,
evqh: &mut EventQueueHandle,
seat: &wl_seat::WlSeat,
capabilities: wl_seat::Capability)
{
// create pointer if applicable
if capabilities.contains(wl_seat::Pointer) && self.mouse.is_none() {
let pointer = seat.get_pointer().expect("Seat is not dead");
evqh.register::<_, InputHandler>(&pointer, self.my_id);
self.mouse = Some(pointer);
}
// destroy pointer if applicable
if !capabilities.contains(wl_seat::Pointer) {
if let Some(pointer) = self.mouse.take() {
pointer.release();
}
}
// create keyboard if applicable
if capabilities.contains(wl_seat::Keyboard) && self.kbd.is_none() {
let kbd = seat.get_keyboard().expect("Seat is not dead");
evqh.register::<_, InputHandler>(&kbd, self.my_id);
self.kbd = Some(kbd);
}
// destroy keyboard if applicable
if !capabilities.contains(wl_seat::Keyboard) {
if let Some(kbd) = self.kbd.take() {
kbd.release();
}
}
}
}
declare_handler!(InputHandler, wl_seat::Handler, wl_seat::WlSeat);
/*
* Pointer Handling
*/
impl wl_pointer::Handler for InputHandler {
fn enter(&mut self,
_evqh: &mut EventQueueHandle,
_proxy: &wl_pointer::WlPointer,
_serial: u32,
surface: &wl_surface::WlSurface,
surface_x: f64,
surface_y: f64)
{
self.mouse_location = (surface_x as i32, surface_y as i32);
for window in &self.windows {
if window.equals(surface) {
self.mouse_focus = Some(window.clone());
let (w, h) = self.mouse_location;
let mut guard = self.callback.lock().unwrap();
guard.send_event(Event::MouseEntered, make_wid(window));
guard.send_event(Event::MouseMoved(w, h), make_wid(window));
break;
}
}
}
fn leave(&mut self,
_evqh: &mut EventQueueHandle,
_proxy: &wl_pointer::WlPointer,
_serial: u32,
surface: &wl_surface::WlSurface)
{
self.mouse_focus = None;
for window in &self.windows {
if window.equals(surface) {
self.callback.lock().unwrap().send_event(Event::MouseLeft, make_wid(window));
}
}
}
fn motion(&mut self,
_evqh: &mut EventQueueHandle,
_proxy: &wl_pointer::WlPointer,
_time: u32,
surface_x: f64,
surface_y: f64)
{
self.mouse_location = (surface_x as i32, surface_y as i32);
if let Some(ref window) = self.mouse_focus {
let (w,h) = self.mouse_location;
self.callback.lock().unwrap().send_event(Event::MouseMoved(w, h), make_wid(window));
}
}
fn button(&mut self,
_evqh: &mut EventQueueHandle,
_proxy: &wl_pointer::WlPointer,
_serial: u32,
_time: u32,
button: u32,
state: wl_pointer::ButtonState)
{
if let Some(ref window) = self.mouse_focus {
let state = match state {
wl_pointer::ButtonState::Pressed => ElementState::Pressed,
wl_pointer::ButtonState::Released => ElementState::Released
};
let button = match button {
0x110 => MouseButton::Left,
0x111 => MouseButton::Right,
0x112 => MouseButton::Middle,
// TODO figure out the translation ?
_ => return
};
self.callback.lock().unwrap().send_event(Event::MouseInput(state, button), make_wid(window));
}
}
fn axis(&mut self,
_evqh: &mut EventQueueHandle,
_proxy: &wl_pointer::WlPointer,
_time: u32,
axis: wl_pointer::Axis,
value: f64)
{
let (mut x, mut y) = self.axis_buffer.unwrap_or((0.0, 0.0));
match axis {
wl_pointer::Axis::VerticalScroll => y += value as f32,
wl_pointer::Axis::HorizontalScroll => x += value as f32
}
self.axis_buffer = Some((x,y));
self.axis_state = match self.axis_state {
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
_ => TouchPhase::Started
}
}
fn frame(&mut self,
_evqh: &mut EventQueueHandle,
_proxy: &wl_pointer::WlPointer)
{
let axis_buffer = self.axis_buffer.take();
let axis_discrete_buffer = self.axis_discrete_buffer.take();
if let Some(ref window) = self.mouse_focus {
if let Some((x, y)) = axis_discrete_buffer {
self.callback.lock().unwrap().send_event(
Event::MouseWheel(
MouseScrollDelta::LineDelta(x as f32, y as f32),
self.axis_state
),
make_wid(window)
);
} else if let Some((x, y)) = axis_buffer {
self.callback.lock().unwrap().send_event(
Event::MouseWheel(
MouseScrollDelta::PixelDelta(x as f32, y as f32),
self.axis_state
),
make_wid(window)
);
}
}
}
fn axis_source(&mut self,
_evqh: &mut EventQueueHandle,
_proxy: &wl_pointer::WlPointer,
_axis_source: wl_pointer::AxisSource)
{
}
fn axis_stop(&mut self,
_evqh: &mut EventQueueHandle,
_proxy: &wl_pointer::WlPointer,
_time: u32,
_axis: wl_pointer::Axis)
{
self.axis_state = TouchPhase::Ended;
}
fn axis_discrete(&mut self,
_evqh: &mut EventQueueHandle,
_proxy: &wl_pointer::WlPointer,
axis: wl_pointer::Axis,
discrete: i32)
{
let (mut x, mut y) = self.axis_discrete_buffer.unwrap_or((0,0));
match axis {
wl_pointer::Axis::VerticalScroll => y += discrete,
wl_pointer::Axis::HorizontalScroll => x += discrete
}
self.axis_discrete_buffer = Some((x,y));
self.axis_state = match self.axis_state {
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
_ => TouchPhase::Started
}
}
}
declare_handler!(InputHandler, wl_pointer::Handler, wl_pointer::WlPointer);
/*
* Keyboard Handling
*/
impl wl_keyboard::Handler for InputHandler {
// mostly pass-through
fn keymap(&mut self,
evqh: &mut EventQueueHandle,
proxy: &wl_keyboard::WlKeyboard,
format: wl_keyboard::KeymapFormat,
fd: ::std::os::unix::io::RawFd,
size: u32)
{
match self.kbd_handler {
KbdType::Mapped(ref mut h) => h.keymap(evqh, proxy, format, fd, size),
_ => ()
}
}
fn enter(&mut self,
evqh: &mut EventQueueHandle,
proxy: &wl_keyboard::WlKeyboard,
serial: u32,
surface: &wl_surface::WlSurface,
keys: Vec<u8>)
{
for window in &self.windows {
if window.equals(surface) {
self.callback.lock().unwrap().send_event(Event::Focused(true), make_wid(window));
match self.kbd_handler {
KbdType::Mapped(ref mut h) => {
h.handler().target = Some(make_wid(window));
h.enter(evqh, proxy, serial, surface, keys);
},
KbdType::Plain(ref mut target) => {
*target = Some(make_wid(window))
}
}
break;
}
}
}
fn leave(&mut self,
evqh: &mut EventQueueHandle,
proxy: &wl_keyboard::WlKeyboard,
serial: u32,
surface: &wl_surface::WlSurface)
{
for window in &self.windows {
if window.equals(surface) {
self.callback.lock().unwrap().send_event(Event::Focused(false), make_wid(window));
match self.kbd_handler {
KbdType::Mapped(ref mut h) => {
h.handler().target = None;
h.leave(evqh, proxy, serial, surface);
},
KbdType::Plain(ref mut target) => {
*target = None
}
}
break;
}
}
}
fn key(&mut self,
evqh: &mut EventQueueHandle,
proxy: &wl_keyboard::WlKeyboard,
serial: u32,
time: u32,
key: u32,
state: wl_keyboard::KeyState)
{
match self.kbd_handler {
KbdType::Mapped(ref mut h) => h.key(evqh, proxy, serial, time, key, state),
KbdType::Plain(Some(wid)) => {
let state = match state {
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
wl_keyboard::KeyState::Released => ElementState::Released,
};
// This is fallback impl if libxkbcommon was not available
// This case should probably never happen, as most wayland
// compositors _need_ libxkbcommon anyway...
//
// In this case, we don't have the modifiers state information
// anyway, as we need libxkbcommon to interpret it (it is
// supposed to be serialized by the compositor using libxkbcommon)
self.callback.lock().unwrap().send_event(
Event::KeyboardInput(
state,
key as u8,
None,
ModifiersState::default()
),
wid
);
},
KbdType::Plain(None) => ()
}
}
fn modifiers(&mut self,
evqh: &mut EventQueueHandle,
proxy: &wl_keyboard::WlKeyboard,
serial: u32,
mods_depressed: u32,
mods_latched: u32,
mods_locked: u32,
group: u32)
{
match self.kbd_handler {
KbdType::Mapped(ref mut h) => h.modifiers(evqh, proxy, serial, mods_depressed,
mods_latched, mods_locked, group),
_ => ()
}
}
fn repeat_info(&mut self,
evqh: &mut EventQueueHandle,
proxy: &wl_keyboard::WlKeyboard,
rate: i32,
delay: i32)
{
match self.kbd_handler {
KbdType::Mapped(ref mut h) => h.repeat_info(evqh, proxy, rate, delay),
_ => ()
}
}
}
declare_handler!(InputHandler, wl_keyboard::Handler, wl_keyboard::WlKeyboard);

View File

@@ -1,22 +1,89 @@
//! Convert Wayland keys to winit keys.
use std::sync::{Arc, Mutex};
use crate::event::VirtualKeyCode;
use {VirtualKeyCode, ElementState, WindowEvent as Event};
pub fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
use sctk::seat::keyboard::keysyms;
use events::ModifiersState;
use super::{wayland_kbd, EventsLoopSink, WindowId};
use wayland_client::EventQueueHandle;
use wayland_client::protocol::wl_keyboard;
pub struct KbdHandler {
sink: Arc<Mutex<EventsLoopSink>>,
pub target: Option<WindowId>
}
impl KbdHandler {
pub fn new(sink: Arc<Mutex<EventsLoopSink>>) -> KbdHandler {
KbdHandler { sink: sink, target: None }
}
}
impl wayland_kbd::Handler for KbdHandler {
fn key(&mut self,
_evqh: &mut EventQueueHandle,
_proxy: &wl_keyboard::WlKeyboard,
_serial: u32,
_time: u32,
mods: &wayland_kbd::ModifiersState,
rawkey: u32,
keysym: u32,
state: wl_keyboard::KeyState,
utf8: Option<String>)
{
if let Some(wid) = self.target {
let state = match state {
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
wl_keyboard::KeyState::Released => ElementState::Released,
};
let vkcode = key_to_vkey(rawkey, keysym);
let mut guard = self.sink.lock().unwrap();
guard.send_event(
Event::KeyboardInput(
state,
rawkey as u8,
vkcode,
ModifiersState {
shift: mods.shift,
ctrl: mods.ctrl,
alt: mods.alt,
logo: mods.logo
}
),
wid
);
// send char event only on key press, not release
if let ElementState::Released = state { return }
if let Some(txt) = utf8 {
for chr in txt.chars() {
guard.send_event(Event::ReceivedCharacter(chr), wid);
}
}
}
}
}
fn key_to_vkey(rawkey: u32, keysym: u32) -> Option<VirtualKeyCode> {
match rawkey {
1 => Some(VirtualKeyCode::Escape),
2 => Some(VirtualKeyCode::Key1),
3 => Some(VirtualKeyCode::Key2),
4 => Some(VirtualKeyCode::Key3),
5 => Some(VirtualKeyCode::Key4),
6 => Some(VirtualKeyCode::Key5),
7 => Some(VirtualKeyCode::Key6),
8 => Some(VirtualKeyCode::Key7),
9 => Some(VirtualKeyCode::Key8),
10 => Some(VirtualKeyCode::Key9),
11 => Some(VirtualKeyCode::Key0),
_ => keysym_to_vkey(keysym)
}
}
fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
use super::wayland_kbd::keysyms;
match keysym {
// Numbers.
keysyms::XKB_KEY_1 => Some(VirtualKeyCode::Key1),
keysyms::XKB_KEY_2 => Some(VirtualKeyCode::Key2),
keysyms::XKB_KEY_3 => Some(VirtualKeyCode::Key3),
keysyms::XKB_KEY_4 => Some(VirtualKeyCode::Key4),
keysyms::XKB_KEY_5 => Some(VirtualKeyCode::Key5),
keysyms::XKB_KEY_6 => Some(VirtualKeyCode::Key6),
keysyms::XKB_KEY_7 => Some(VirtualKeyCode::Key7),
keysyms::XKB_KEY_8 => Some(VirtualKeyCode::Key8),
keysyms::XKB_KEY_9 => Some(VirtualKeyCode::Key9),
keysyms::XKB_KEY_0 => Some(VirtualKeyCode::Key0),
// Letters.
// letters
keysyms::XKB_KEY_A | keysyms::XKB_KEY_a => Some(VirtualKeyCode::A),
keysyms::XKB_KEY_B | keysyms::XKB_KEY_b => Some(VirtualKeyCode::B),
keysyms::XKB_KEY_C | keysyms::XKB_KEY_c => Some(VirtualKeyCode::C),
@@ -43,34 +110,23 @@ pub fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
keysyms::XKB_KEY_X | keysyms::XKB_KEY_x => Some(VirtualKeyCode::X),
keysyms::XKB_KEY_Y | keysyms::XKB_KEY_y => Some(VirtualKeyCode::Y),
keysyms::XKB_KEY_Z | keysyms::XKB_KEY_z => Some(VirtualKeyCode::Z),
// Escape.
keysyms::XKB_KEY_Escape => Some(VirtualKeyCode::Escape),
// Function keys.
keysyms::XKB_KEY_F1 => Some(VirtualKeyCode::F1),
keysyms::XKB_KEY_F2 => Some(VirtualKeyCode::F2),
keysyms::XKB_KEY_F3 => Some(VirtualKeyCode::F3),
keysyms::XKB_KEY_F4 => Some(VirtualKeyCode::F4),
keysyms::XKB_KEY_F5 => Some(VirtualKeyCode::F5),
keysyms::XKB_KEY_F6 => Some(VirtualKeyCode::F6),
keysyms::XKB_KEY_F7 => Some(VirtualKeyCode::F7),
keysyms::XKB_KEY_F8 => Some(VirtualKeyCode::F8),
keysyms::XKB_KEY_F9 => Some(VirtualKeyCode::F9),
// F--
keysyms::XKB_KEY_F1 => Some(VirtualKeyCode::F1),
keysyms::XKB_KEY_F2 => Some(VirtualKeyCode::F2),
keysyms::XKB_KEY_F3 => Some(VirtualKeyCode::F3),
keysyms::XKB_KEY_F4 => Some(VirtualKeyCode::F4),
keysyms::XKB_KEY_F5 => Some(VirtualKeyCode::F5),
keysyms::XKB_KEY_F6 => Some(VirtualKeyCode::F6),
keysyms::XKB_KEY_F7 => Some(VirtualKeyCode::F7),
keysyms::XKB_KEY_F8 => Some(VirtualKeyCode::F8),
keysyms::XKB_KEY_F9 => Some(VirtualKeyCode::F9),
keysyms::XKB_KEY_F10 => Some(VirtualKeyCode::F10),
keysyms::XKB_KEY_F11 => Some(VirtualKeyCode::F11),
keysyms::XKB_KEY_F12 => Some(VirtualKeyCode::F12),
keysyms::XKB_KEY_F13 => Some(VirtualKeyCode::F13),
keysyms::XKB_KEY_F14 => Some(VirtualKeyCode::F14),
keysyms::XKB_KEY_F15 => Some(VirtualKeyCode::F15),
keysyms::XKB_KEY_F16 => Some(VirtualKeyCode::F16),
keysyms::XKB_KEY_F17 => Some(VirtualKeyCode::F17),
keysyms::XKB_KEY_F18 => Some(VirtualKeyCode::F18),
keysyms::XKB_KEY_F19 => Some(VirtualKeyCode::F19),
keysyms::XKB_KEY_F20 => Some(VirtualKeyCode::F20),
keysyms::XKB_KEY_F21 => Some(VirtualKeyCode::F21),
keysyms::XKB_KEY_F22 => Some(VirtualKeyCode::F22),
keysyms::XKB_KEY_F23 => Some(VirtualKeyCode::F23),
keysyms::XKB_KEY_F24 => Some(VirtualKeyCode::F24),
// Flow control.
// flow control
keysyms::XKB_KEY_Print => Some(VirtualKeyCode::Snapshot),
keysyms::XKB_KEY_Scroll_Lock => Some(VirtualKeyCode::Scroll),
keysyms::XKB_KEY_Pause => Some(VirtualKeyCode::Pause),
@@ -80,20 +136,16 @@ pub fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
keysyms::XKB_KEY_End => Some(VirtualKeyCode::End),
keysyms::XKB_KEY_Page_Down => Some(VirtualKeyCode::PageDown),
keysyms::XKB_KEY_Page_Up => Some(VirtualKeyCode::PageUp),
// Arrows.
// arrows
keysyms::XKB_KEY_Left => Some(VirtualKeyCode::Left),
keysyms::XKB_KEY_Up => Some(VirtualKeyCode::Up),
keysyms::XKB_KEY_Right => Some(VirtualKeyCode::Right),
keysyms::XKB_KEY_Down => Some(VirtualKeyCode::Down),
//
keysyms::XKB_KEY_BackSpace => Some(VirtualKeyCode::Back),
keysyms::XKB_KEY_Return => Some(VirtualKeyCode::Return),
keysyms::XKB_KEY_space => Some(VirtualKeyCode::Space),
keysyms::XKB_KEY_Multi_key => Some(VirtualKeyCode::Compose),
keysyms::XKB_KEY_caret => Some(VirtualKeyCode::Caret),
// Keypad.
// keypad
keysyms::XKB_KEY_Num_Lock => Some(VirtualKeyCode::Numlock),
keysyms::XKB_KEY_KP_0 => Some(VirtualKeyCode::Numpad0),
keysyms::XKB_KEY_KP_1 => Some(VirtualKeyCode::Numpad1),
@@ -105,72 +157,63 @@ pub fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
keysyms::XKB_KEY_KP_7 => Some(VirtualKeyCode::Numpad7),
keysyms::XKB_KEY_KP_8 => Some(VirtualKeyCode::Numpad8),
keysyms::XKB_KEY_KP_9 => Some(VirtualKeyCode::Numpad9),
// Misc.
// misc
// => Some(VirtualKeyCode::AbntC1),
// => Some(VirtualKeyCode::AbntC2),
keysyms::XKB_KEY_plus => Some(VirtualKeyCode::Plus),
keysyms::XKB_KEY_plus => Some(VirtualKeyCode::Add),
keysyms::XKB_KEY_apostrophe => Some(VirtualKeyCode::Apostrophe),
// => Some(VirtualKeyCode::Apps),
keysyms::XKB_KEY_at => Some(VirtualKeyCode::At),
// => Some(VirtualKeyCode::At),
// => Some(VirtualKeyCode::Ax),
keysyms::XKB_KEY_backslash => Some(VirtualKeyCode::Backslash),
keysyms::XKB_KEY_XF86Calculator => Some(VirtualKeyCode::Calculator),
// => Some(VirtualKeyCode::Calculator),
// => Some(VirtualKeyCode::Capital),
keysyms::XKB_KEY_colon => Some(VirtualKeyCode::Colon),
keysyms::XKB_KEY_comma => Some(VirtualKeyCode::Comma),
// => Some(VirtualKeyCode::Convert),
// => Some(VirtualKeyCode::Decimal),
// => Some(VirtualKeyCode::Divide),
keysyms::XKB_KEY_equal => Some(VirtualKeyCode::Equals),
keysyms::XKB_KEY_grave => Some(VirtualKeyCode::Grave),
// => Some(VirtualKeyCode::Grave),
// => Some(VirtualKeyCode::Kana),
keysyms::XKB_KEY_Kanji => Some(VirtualKeyCode::Kanji),
// => Some(VirtualKeyCode::Kanji),
keysyms::XKB_KEY_Alt_L => Some(VirtualKeyCode::LAlt),
keysyms::XKB_KEY_bracketleft => Some(VirtualKeyCode::LBracket),
// => Some(VirtualKeyCode::LBracket),
keysyms::XKB_KEY_Control_L => Some(VirtualKeyCode::LControl),
// => Some(VirtualKeyCode::LMenu),
keysyms::XKB_KEY_Shift_L => Some(VirtualKeyCode::LShift),
keysyms::XKB_KEY_Super_L => Some(VirtualKeyCode::LWin),
keysyms::XKB_KEY_XF86Mail => Some(VirtualKeyCode::Mail),
// => Some(VirtualKeyCode::LWin),
// => Some(VirtualKeyCode::Mail),
// => Some(VirtualKeyCode::MediaSelect),
// => Some(VirtualKeyCode::MediaStop),
keysyms::XKB_KEY_minus => Some(VirtualKeyCode::Minus),
keysyms::XKB_KEY_asterisk => Some(VirtualKeyCode::Asterisk),
keysyms::XKB_KEY_XF86AudioMute => Some(VirtualKeyCode::Mute),
keysyms::XKB_KEY_asterisk => Some(VirtualKeyCode::Multiply),
// => Some(VirtualKeyCode::Mute),
// => Some(VirtualKeyCode::MyComputer),
keysyms::XKB_KEY_XF86AudioNext => Some(VirtualKeyCode::NextTrack),
// => Some(VirtualKeyCode::NextTrack),
// => Some(VirtualKeyCode::NoConvert),
keysyms::XKB_KEY_KP_Separator => Some(VirtualKeyCode::NumpadComma),
keysyms::XKB_KEY_KP_Enter => Some(VirtualKeyCode::NumpadEnter),
keysyms::XKB_KEY_KP_Equal => Some(VirtualKeyCode::NumpadEquals),
keysyms::XKB_KEY_KP_Add => Some(VirtualKeyCode::NumpadAdd),
keysyms::XKB_KEY_KP_Subtract => Some(VirtualKeyCode::NumpadSubtract),
keysyms::XKB_KEY_KP_Multiply => Some(VirtualKeyCode::NumpadMultiply),
keysyms::XKB_KEY_KP_Divide => Some(VirtualKeyCode::NumpadDivide),
keysyms::XKB_KEY_KP_Decimal => Some(VirtualKeyCode::NumpadDecimal),
keysyms::XKB_KEY_KP_Page_Up => Some(VirtualKeyCode::PageUp),
keysyms::XKB_KEY_KP_Page_Down => Some(VirtualKeyCode::PageDown),
keysyms::XKB_KEY_KP_Home => Some(VirtualKeyCode::Home),
keysyms::XKB_KEY_KP_End => Some(VirtualKeyCode::End),
keysyms::XKB_KEY_KP_Left => Some(VirtualKeyCode::Left),
keysyms::XKB_KEY_KP_Up => Some(VirtualKeyCode::Up),
keysyms::XKB_KEY_KP_Right => Some(VirtualKeyCode::Right),
keysyms::XKB_KEY_KP_Down => Some(VirtualKeyCode::Down),
// => Some(VirtualKeyCode::OEM102),
keysyms::XKB_KEY_period => Some(VirtualKeyCode::Period),
// => Some(VirtualKeyCode::Period),
// => Some(VirtualKeyCode::Playpause),
keysyms::XKB_KEY_XF86PowerOff => Some(VirtualKeyCode::Power),
keysyms::XKB_KEY_XF86AudioPrev => Some(VirtualKeyCode::PrevTrack),
// => Some(VirtualKeyCode::Power),
// => Some(VirtualKeyCode::Prevtrack),
keysyms::XKB_KEY_Alt_R => Some(VirtualKeyCode::RAlt),
keysyms::XKB_KEY_bracketright => Some(VirtualKeyCode::RBracket),
// => Some(VirtualKeyCode::RBracket),
keysyms::XKB_KEY_Control_R => Some(VirtualKeyCode::RControl),
// => Some(VirtualKeyCode::RMenu),
keysyms::XKB_KEY_Shift_R => Some(VirtualKeyCode::RShift),
keysyms::XKB_KEY_Super_R => Some(VirtualKeyCode::RWin),
// => Some(VirtualKeyCode::RWin),
keysyms::XKB_KEY_semicolon => Some(VirtualKeyCode::Semicolon),
keysyms::XKB_KEY_slash => Some(VirtualKeyCode::Slash),
keysyms::XKB_KEY_XF86Sleep => Some(VirtualKeyCode::Sleep),
// => Some(VirtualKeyCode::Sleep),
// => Some(VirtualKeyCode::Stop),
// => Some(VirtualKeyCode::Subtract),
// => Some(VirtualKeyCode::Sysrq),
keysyms::XKB_KEY_Tab => Some(VirtualKeyCode::Tab),
keysyms::XKB_KEY_ISO_Left_Tab => Some(VirtualKeyCode::Tab),
keysyms::XKB_KEY_underscore => Some(VirtualKeyCode::Underline),
// => Some(VirtualKeyCode::Underline),
// => Some(VirtualKeyCode::Unlabeled),
keysyms::XKB_KEY_XF86AudioLowerVolume => Some(VirtualKeyCode::VolumeDown),
keysyms::XKB_KEY_XF86AudioRaiseVolume => Some(VirtualKeyCode::VolumeUp),
@@ -182,11 +225,8 @@ pub fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
// => Some(VirtualKeyCode::WebRefresh),
// => Some(VirtualKeyCode::WebSearch),
// => Some(VirtualKeyCode::WebStop),
keysyms::XKB_KEY_yen => Some(VirtualKeyCode::Yen),
keysyms::XKB_KEY_XF86Copy => Some(VirtualKeyCode::Copy),
keysyms::XKB_KEY_XF86Paste => Some(VirtualKeyCode::Paste),
keysyms::XKB_KEY_XF86Cut => Some(VirtualKeyCode::Cut),
// Fallback.
_ => None,
// => Some(VirtualKeyCode::Yen),
// fallback
_ => None
}
}

View File

@@ -0,0 +1,17 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
pub use self::window::{Window, WindowId};
pub use self::event_loop::EventsLoop;
pub use self::context::{WaylandContext, MonitorId, get_available_monitors,
get_primary_monitor};
use self::window::{make_wid, DecoratedHandler};
use self::event_loop::EventsLoopSink;
extern crate wayland_kbd;
extern crate wayland_window;
mod context;
mod event_loop;
mod keyboard;
mod window;

View File

@@ -0,0 +1,205 @@
use std::sync::{Arc, Mutex};
use std::sync::atomic::AtomicBool;
use wayland_client::{EventQueue, EventQueueHandle, Proxy};
use wayland_client::protocol::{wl_display,wl_surface,wl_shell_surface};
use {CreationError, MouseCursor, CursorState, WindowAttributes};
use platform::MonitorId as PlatformMonitorId;
use super::{WaylandContext, EventsLoop};
use super::wayland_window;
use super::wayland_window::DecoratedSurface;
pub struct Window {
// the global wayland context
ctxt: Arc<WaylandContext>,
// the EventQueue of our EventsLoop
evq: Arc<Mutex<EventQueue>>,
// signal to advertize the EventsLoop when we are destroyed
cleanup_signal: Arc<AtomicBool>,
// our wayland surface
surface: Arc<wl_surface::WlSurface>,
// our current inner dimensions
size: Mutex<(u32, u32)>,
// the id of our DecoratedHandler in the EventQueue
decorated_id: usize
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId(usize);
#[inline]
pub fn make_wid(s: &wl_surface::WlSurface) -> WindowId {
WindowId(s.ptr() as usize)
}
impl Window {
pub fn new(evlp: &EventsLoop, ctxt: Arc<WaylandContext>, attributes: &WindowAttributes) -> Result<Window, CreationError>
{
let (width, height) = attributes.dimensions.unwrap_or((800,600));
let (surface, decorated) = ctxt.create_window::<DecoratedHandler>();
// init DecoratedSurface
let (evq, cleanup_signal) = evlp.get_window_init();
let decorated_id = {
let mut evq_guard = evq.lock().unwrap();
let decorated_id = evq_guard.add_handler_with_init(decorated);
{
// initialize the DecoratedHandler
let mut state = evq_guard.state();
let decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(decorated_id);
*(decorated.handler()) = Some(DecoratedHandler::new());
// set fullscreen if necessary
if let Some(PlatformMonitorId::Wayland(ref monitor_id)) = attributes.monitor {
ctxt.with_output(monitor_id.clone(), |output| {
decorated.set_fullscreen(
wl_shell_surface::FullscreenMethod::Default,
0,
Some(output)
)
});
} else if attributes.decorations {
decorated.set_decorate(true);
}
// Finally, set the decorations size
decorated.resize(width as i32, height as i32);
}
decorated_id
};
let me = Window {
ctxt: ctxt,
evq: evq,
cleanup_signal: cleanup_signal,
surface: surface,
size: Mutex::new((width, height)),
decorated_id: decorated_id
};
// register ourselves to the EventsLoop
evlp.register_window(me.decorated_id, me.surface.clone());
Ok(me)
}
#[inline]
pub fn id(&self) -> WindowId {
make_wid(&self.surface)
}
pub fn set_title(&self, title: &str) {
let mut guard = self.evq.lock().unwrap();
let mut state = guard.state();
let decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(self.decorated_id);
decorated.set_title(title.into())
}
#[inline]
pub fn show(&self) {
// TODO
}
#[inline]
pub fn hide(&self) {
// TODO
}
#[inline]
pub fn get_position(&self) -> Option<(i32, i32)> {
// Not possible with wayland
None
}
#[inline]
pub fn set_position(&self, _x: i32, _y: i32) {
// Not possible with wayland
}
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
Some(self.size.lock().unwrap().clone())
}
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
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))
}
#[inline]
// NOTE: This will only resize the borders, the contents must be updated by the user
pub fn set_inner_size(&self, x: u32, y: u32) {
let mut guard = self.evq.lock().unwrap();
let mut state = guard.state();
let mut decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(self.decorated_id);
decorated.resize(x as i32, y as i32);
}
#[inline]
pub fn set_cursor(&self, _cursor: MouseCursor) {
// TODO
}
#[inline]
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
use CursorState::{Grab, Normal, Hide};
// 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 {
// TODO
1.0
}
#[inline]
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
// TODO: not yet possible on wayland
Err(())
}
pub fn get_display(&self) -> &wl_display::WlDisplay {
&self.ctxt.display
}
pub fn get_surface(&self) -> &wl_surface::WlSurface {
&self.surface
}
}
impl Drop for Window {
fn drop(&mut self) {
self.surface.destroy();
self.cleanup_signal.store(true, ::std::sync::atomic::Ordering::Relaxed);
}
}
pub struct DecoratedHandler {
newsize: Option<(u32, u32)>
}
impl DecoratedHandler {
fn new() -> DecoratedHandler { DecoratedHandler { newsize: None }}
pub fn take_newsize(&mut self) -> Option<(u32, u32)> {
self.newsize.take()
}
}
impl wayland_window::Handler for DecoratedHandler {
fn configure(&mut self,
_: &mut EventQueueHandle,
_: wl_shell_surface::Resize,
width: i32, height: i32)
{
use std::cmp::max;
self.newsize = Some((max(width,1) as u32, max(height,1) as u32));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
pub use x11_dl::keysym::*;
pub use x11_dl::xcursor::*;
pub use x11_dl::xf86vmode::*;
pub use x11_dl::xlib::*;
pub use x11_dl::xinput::*;
pub use x11_dl::xinput2::*;
pub use x11_dl::xlib_xcb::*;
pub use x11_dl::error::OpenError;

View File

@@ -0,0 +1,391 @@
use std::sync::Arc;
use libc;
use std::{mem, ptr};
use std::ffi::CString;
use std::slice::from_raw_parts;
use WindowAttributes;
use events::WindowEvent as Event;
use events::ModifiersState;
use super::{events, ffi};
use super::XConnection;
#[derive(Debug)]
enum AxisType {
HorizontalScroll,
VerticalScroll
}
#[derive(Debug)]
struct Axis {
id: i32,
device_id: i32,
axis_number: i32,
axis_type: AxisType,
scroll_increment: f64,
}
#[derive(Debug)]
struct AxisValue {
device_id: i32,
axis_number: i32,
value: f64
}
struct InputState {
/// Last-seen cursor position within a window in (x, y)
/// coordinates
cursor_pos: (f64, f64),
/// Last-seen positions of axes, used to report delta
/// movements when a new absolute axis value is received
axis_values: Vec<AxisValue>
}
pub struct XInputEventHandler {
display: Arc<XConnection>,
window: ffi::Window,
ic: ffi::XIC,
axis_list: Vec<Axis>,
current_state: InputState,
multitouch: bool,
}
impl XInputEventHandler {
pub fn new(display: &Arc<XConnection>, window: ffi::Window, ic: ffi::XIC,
window_attrs: &WindowAttributes) -> XInputEventHandler {
// query XInput support
let mut opcode: libc::c_int = 0;
let mut event: libc::c_int = 0;
let mut error: libc::c_int = 0;
let xinput_str = CString::new("XInputExtension").unwrap();
unsafe {
if (display.xlib.XQueryExtension)(display.display, xinput_str.as_ptr(), &mut opcode, &mut event, &mut error) == ffi::False {
panic!("XInput not available")
}
}
let mut xinput_major_ver = ffi::XI_2_Major;
let mut xinput_minor_ver = ffi::XI_2_Minor;
unsafe {
if (display.xinput2.XIQueryVersion)(display.display, &mut xinput_major_ver, &mut xinput_minor_ver) != ffi::Success as libc::c_int {
panic!("Unable to determine XInput version");
}
}
// specify the XInput events we want to receive.
// Button clicks and mouse events are handled via XInput
// events. Key presses are still handled via plain core
// X11 events.
let mut mask: [libc::c_uchar; 3] = [0; 3];
let mut input_event_mask = ffi::XIEventMask {
deviceid: ffi::XIAllMasterDevices,
mask_len: mask.len() as i32,
mask: mask.as_mut_ptr()
};
let events = &[
ffi::XI_ButtonPress,
ffi::XI_ButtonRelease,
ffi::XI_Motion,
ffi::XI_Enter,
ffi::XI_Leave,
ffi::XI_FocusIn,
ffi::XI_FocusOut,
ffi::XI_TouchBegin,
ffi::XI_TouchUpdate,
ffi::XI_TouchEnd,
];
for event in events {
ffi::XISetMask(&mut mask, *event);
}
unsafe {
match (display.xinput2.XISelectEvents)(display.display, window, &mut input_event_mask, 1) {
status if status as u8 == ffi::Success => (),
err => panic!("Failed to select events {:?}", err)
}
}
XInputEventHandler {
display: display.clone(),
window: window,
ic: ic,
axis_list: read_input_axis_info(display),
current_state: InputState {
cursor_pos: (0.0, 0.0),
axis_values: Vec::new()
},
multitouch: window_attrs.multitouch,
}
}
pub fn translate_key_event(&self, event: &mut ffi::XKeyEvent) -> Vec<Event> {
use events::WindowEvent::{KeyboardInput, ReceivedCharacter};
use events::ElementState::{Pressed, Released};
let mut translated_events = Vec::new();
let state;
if event.type_ == ffi::KeyPress {
let raw_ev: *mut ffi::XKeyEvent = event;
unsafe { (self.display.xlib.XFilterEvent)(mem::transmute(raw_ev), self.window) };
state = Pressed;
} else {
state = Released;
}
let mut kp_keysym = 0;
let mut ev_mods = ModifiersState::default();
let written = unsafe {
use std::str;
let mut buffer: [u8; 16] = [mem::uninitialized(); 16];
let raw_ev: *mut ffi::XKeyEvent = event;
let count = (self.display.xlib.Xutf8LookupString)(self.ic, mem::transmute(raw_ev),
mem::transmute(buffer.as_mut_ptr()),
buffer.len() as libc::c_int, &mut kp_keysym, ptr::null_mut());
{
// Translate x event state to mods
let state = event.state;
if (state & ffi::Mod1Mask) != 0 {
ev_mods.alt = true;
}
if (state & ffi::ShiftMask) != 0 {
ev_mods.shift = true;
}
if (state & ffi::ControlMask) != 0 {
ev_mods.ctrl = true;
}
if (state & ffi::Mod4Mask) != 0 {
ev_mods.logo = true;
}
}
str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string()
};
for chr in written.chars() {
translated_events.push(ReceivedCharacter(chr));
}
let mut keysym = unsafe {
(self.display.xlib.XKeycodeToKeysym)(self.display.display, event.keycode as ffi::KeyCode, 0)
};
if (ffi::XK_KP_Space as libc::c_ulong <= keysym) && (keysym <= ffi::XK_KP_9 as libc::c_ulong) {
keysym = kp_keysym
};
let vkey = events::keycode_to_element(keysym as libc::c_uint);
translated_events.push(KeyboardInput(state, event.keycode as u8, vkey, ev_mods));
translated_events
}
pub fn translate_event(&mut self, cookie: &ffi::XGenericEventCookie) -> Option<Event> {
use events::WindowEvent::{Focused, MouseEntered, MouseInput, MouseLeft, MouseMoved, MouseWheel};
use events::ElementState::{Pressed, Released};
use events::MouseButton::{Left, Right, Middle};
use events::MouseScrollDelta::LineDelta;
use events::{Touch, TouchPhase};
match cookie.evtype {
ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
if self.multitouch && (event_data.flags & ffi::XIPointerEmulated) != 0 {
// Deliver multi-touch events instead of emulated mouse events.
return None
}
let state = if cookie.evtype == ffi::XI_ButtonPress {
Pressed
} else {
Released
};
match event_data.detail as u32 {
ffi::Button1 => Some(MouseInput(state, Left)),
ffi::Button2 => Some(MouseInput(state, Middle)),
ffi::Button3 => Some(MouseInput(state, Right)),
ffi::Button4 | ffi::Button5 => {
if event_data.flags & ffi::XIPointerEmulated == 0 {
// scroll event from a traditional wheel with
// distinct 'clicks'
let delta = if event_data.detail as u32 == ffi::Button4 {
1.0
} else {
-1.0
};
Some(MouseWheel(LineDelta(0.0, delta), TouchPhase::Moved))
} else {
// emulated button event from a touch/smooth-scroll
// event. Ignore these events and handle scrolling
// via XI_Motion event handler instead
None
}
}
_ => None
}
},
ffi::XI_Motion => {
let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
if self.multitouch && (event_data.flags & ffi::XIPointerEmulated) != 0 {
// Deliver multi-touch events instead of emulated mouse events.
return None
}
let axis_state = event_data.valuators;
let mask = unsafe{ from_raw_parts(axis_state.mask, axis_state.mask_len as usize) };
let mut axis_count = 0;
let mut scroll_delta = (0.0, 0.0);
for axis_id in 0..axis_state.mask_len {
if ffi::XIMaskIsSet(&mask, axis_id) {
let axis_value = unsafe{*axis_state.values.offset(axis_count)};
let delta = calc_scroll_deltas(event_data, axis_id, axis_value, &self.axis_list,
&mut self.current_state.axis_values);
scroll_delta.0 += delta.0;
scroll_delta.1 += delta.1;
axis_count += 1;
}
}
if scroll_delta.0.abs() > 0.0 || scroll_delta.1.abs() > 0.0 {
Some(MouseWheel(LineDelta(scroll_delta.0 as f32, scroll_delta.1 as f32),
TouchPhase::Moved))
} else {
let new_cursor_pos = (event_data.event_x, event_data.event_y);
if new_cursor_pos != self.current_state.cursor_pos {
self.current_state.cursor_pos = new_cursor_pos;
Some(MouseMoved(new_cursor_pos.0 as i32, new_cursor_pos.1 as i32))
} else {
None
}
}
},
ffi::XI_Enter => {
// axis movements whilst the cursor is outside the window
// will alter the absolute value of the axes. We only want to
// report changes in the axis value whilst the cursor is above
// our window however, so clear the previous axis state whenever
// the cursor re-enters the window
self.current_state.axis_values.clear();
Some(MouseEntered)
},
ffi::XI_Leave => Some(MouseLeft),
ffi::XI_FocusIn => Some(Focused(true)),
ffi::XI_FocusOut => Some(Focused(false)),
ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
if !self.multitouch {
return None
}
let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
let phase = match cookie.evtype {
ffi::XI_TouchBegin => TouchPhase::Started,
ffi::XI_TouchUpdate => TouchPhase::Moved,
ffi::XI_TouchEnd => TouchPhase::Ended,
_ => unreachable!()
};
Some(Event::Touch(Touch {
phase: phase,
location: (event_data.event_x, event_data.event_y),
id: event_data.detail as u64,
}))
}
_ => None
}
}
}
fn read_input_axis_info(display: &Arc<XConnection>) -> Vec<Axis> {
let mut axis_list = Vec::new();
let mut device_count = 0;
// Check all input devices for scroll axes.
let devices = unsafe{
(display.xinput2.XIQueryDevice)(display.display, ffi::XIAllDevices, &mut device_count)
};
for i in 0..device_count {
let device = unsafe { *(devices.offset(i as isize)) };
for k in 0..device.num_classes {
let class = unsafe { *(device.classes.offset(k as isize)) };
match unsafe { (*class)._type } {
// Note that scroll axis
// are reported both as 'XIScrollClass' and 'XIValuatorClass'
// axes. For the moment we only care about scrolling axes.
ffi::XIScrollClass => {
let scroll_class: &ffi::XIScrollClassInfo = unsafe{mem::transmute(class)};
axis_list.push(Axis{
id: scroll_class.sourceid,
device_id: device.deviceid,
axis_number: scroll_class.number,
axis_type: match scroll_class.scroll_type {
ffi::XIScrollTypeHorizontal => AxisType::HorizontalScroll,
ffi::XIScrollTypeVertical => AxisType::VerticalScroll,
_ => { unreachable!() }
},
scroll_increment: scroll_class.increment,
})
},
_ => {}
}
}
}
unsafe {
(display.xinput2.XIFreeDeviceInfo)(devices);
}
axis_list
}
/// Given an input motion event for an axis and the previous
/// state of the axes, return the horizontal/vertical
/// scroll deltas
fn calc_scroll_deltas(event: &ffi::XIDeviceEvent,
axis_id: i32,
axis_value: f64,
axis_list: &[Axis],
prev_axis_values: &mut Vec<AxisValue>) -> (f64, f64) {
let prev_value_pos = prev_axis_values.iter().position(|prev_axis| {
prev_axis.device_id == event.sourceid &&
prev_axis.axis_number == axis_id
});
let delta = match prev_value_pos {
Some(idx) => prev_axis_values[idx].value - axis_value,
None => 0.0
};
let new_axis_value = AxisValue{
device_id: event.sourceid,
axis_number: axis_id,
value: axis_value
};
match prev_value_pos {
Some(idx) => prev_axis_values[idx] = new_axis_value,
None => prev_axis_values.push(new_axis_value)
}
let mut scroll_delta = (0.0, 0.0);
for axis in axis_list.iter() {
if axis.id == event.sourceid &&
axis.axis_number == axis_id {
match axis.axis_type {
AxisType::HorizontalScroll => scroll_delta.0 = delta / axis.scroll_increment,
AxisType::VerticalScroll => scroll_delta.1 = delta / axis.scroll_increment
}
}
}
scroll_delta
}

View File

@@ -0,0 +1,124 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
pub use self::window::{Window, XWindow, PollEventsIterator, WaitEventsIterator, WindowProxy};
pub use self::xdisplay::{XConnection, XNotSupported, XError};
pub mod ffi;
use platform::PlatformSpecificWindowBuilderAttributes;
use CreationError;
use std::sync::Arc;
mod events;
mod input;
mod monitor;
mod window;
mod xdisplay;
// API TRANSITION
//
// We don't use the gen_api_transistion!() macro but rather do the expansion manually:
//
// As this module is nested into platform/linux, its code is not _exactly_ the same as
// the one generated by the macro.
pub struct EventsLoop {
windows: ::std::sync::Mutex<Vec<::std::sync::Arc<Window>>>,
interrupted: ::std::sync::atomic::AtomicBool,
}
impl EventsLoop {
pub fn new() -> EventsLoop {
EventsLoop {
windows: ::std::sync::Mutex::new(vec![]),
interrupted: ::std::sync::atomic::AtomicBool::new(false),
}
}
pub fn interrupt(&self) {
self.interrupted.store(true, ::std::sync::atomic::Ordering::Relaxed);
}
pub fn poll_events<F>(&self, mut callback: F)
where F: FnMut(::Event)
{
let windows = self.windows.lock().unwrap();
for window in windows.iter() {
for event in window.poll_events() {
callback(::Event::WindowEvent {
window_id: ::WindowId(::platform::WindowId::X(WindowId(&**window as *const Window as usize))),
event: event,
})
}
}
}
pub fn run_forever<F>(&self, mut callback: F)
where F: FnMut(::Event)
{
self.interrupted.store(false, ::std::sync::atomic::Ordering::Relaxed);
// Yeah that's a very bad implementation.
loop {
self.poll_events(|e| callback(e));
::std::thread::sleep(::std::time::Duration::from_millis(5));
if self.interrupted.load(::std::sync::atomic::Ordering::Relaxed) {
break;
}
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId(usize);
pub struct Window2 {
pub window: ::std::sync::Arc<Window>,
events_loop: ::std::sync::Weak<::platform::EventsLoop>,
}
impl ::std::ops::Deref for Window2 {
type Target = Window;
#[inline]
fn deref(&self) -> &Window {
&*self.window
}
}
impl Window2 {
pub fn new(events_loop: ::std::sync::Arc<::platform::EventsLoop>, display: &Arc<XConnection>,
window: &::WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes)
-> Result<Window2, CreationError>
{
let win = ::std::sync::Arc::new(try!(Window::new(display, window, pl_attribs)));
if let ::platform::EventsLoop::X(ref ev) = *events_loop {
ev.windows.lock().unwrap().push(win.clone());
} else {
// It should not be possible to create an eventloop not matching the backend
// in use
unreachable!()
}
Ok(Window2 {
window: win,
events_loop: ::std::sync::Arc::downgrade(&events_loop),
})
}
#[inline]
pub fn id(&self) -> WindowId {
WindowId(&*self.window as *const Window as usize)
}
}
impl Drop for Window2 {
fn drop(&mut self) {
if let Some(ev) = self.events_loop.upgrade() {
if let ::platform::EventsLoop::X(ref ev) = *ev {
let mut windows = ev.windows.lock().unwrap();
windows.retain(|w| &**w as *const Window != &*self.window as *const _);
}
}
}
}

View File

@@ -0,0 +1,44 @@
use std::collections::VecDeque;
use std::sync::Arc;
use super::XConnection;
use native_monitor::NativeMonitorId;
#[derive(Clone)]
pub struct MonitorId(pub Arc<XConnection>, pub u32);
pub fn get_available_monitors(x: &Arc<XConnection>) -> VecDeque<MonitorId> {
let nb_monitors = unsafe { (x.xlib.XScreenCount)(x.display) };
x.check_errors().expect("Failed to call XScreenCount");
let mut monitors = VecDeque::new();
monitors.extend((0 .. nb_monitors).map(|i| MonitorId(x.clone(), i as u32)));
monitors
}
#[inline]
pub fn get_primary_monitor(x: &Arc<XConnection>) -> MonitorId {
let primary_monitor = unsafe { (x.xlib.XDefaultScreen)(x.display) };
x.check_errors().expect("Failed to call XDefaultScreen");
MonitorId(x.clone(), primary_monitor as u32)
}
impl MonitorId {
pub fn get_name(&self) -> Option<String> {
let MonitorId(_, screen_num) = *self;
Some(format!("Monitor #{}", screen_num))
}
#[inline]
pub fn get_native_identifier(&self) -> NativeMonitorId {
NativeMonitorId::Numeric(self.1)
}
pub fn get_dimensions(&self) -> (u32, u32) {
let screen = unsafe { (self.0.xlib.XScreenOfDisplay)(self.0.display, self.1 as i32) };
let width = unsafe { (self.0.xlib.XWidthOfScreen)(screen) };
let height = unsafe { (self.0.xlib.XHeightOfScreen)(screen) };
self.0.check_errors().expect("Failed to get monitor dimensions");
(width as u32, height as u32)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,45 +1,36 @@
use std::{collections::HashMap, error::Error, fmt, os::raw::c_int, ptr};
use std::ptr;
use std::fmt;
use std::error::Error;
use std::sync::Mutex;
use libc;
use parking_lot::Mutex;
use crate::window::CursorIcon;
use super::ffi;
/// A connection to an X server.
pub struct XConnection {
pub xlib: ffi::Xlib,
/// Exposes XRandR functions from version < 1.5
pub xrandr: ffi::Xrandr_2_2_0,
/// Exposes XRandR functions from version = 1.5
pub xrandr_1_5: Option<ffi::Xrandr>,
pub xf86vmode: ffi::Xf86vmode,
pub xcursor: ffi::Xcursor,
pub xinput2: ffi::XInput2,
pub xlib_xcb: ffi::Xlib_xcb,
pub xrender: ffi::Xrender,
pub display: *mut ffi::Display,
pub x11_fd: c_int,
pub latest_error: Mutex<Option<XError>>,
pub cursor_cache: Mutex<HashMap<Option<CursorIcon>, ffi::Cursor>>,
}
unsafe impl Send for XConnection {}
unsafe impl Sync for XConnection {}
pub type XErrorHandler =
Option<unsafe extern "C" fn(*mut ffi::Display, *mut ffi::XErrorEvent) -> libc::c_int>;
pub type XErrorHandler = Option<unsafe extern fn(*mut ffi::Display, *mut ffi::XErrorEvent) -> libc::c_int>;
impl XConnection {
pub fn new(error_handler: XErrorHandler) -> Result<XConnection, XNotSupported> {
// opening the libraries
let xlib = ffi::Xlib::open()?;
let xcursor = ffi::Xcursor::open()?;
let xrandr = ffi::Xrandr_2_2_0::open()?;
let xrandr_1_5 = ffi::Xrandr::open().ok();
let xinput2 = ffi::XInput2::open()?;
let xlib_xcb = ffi::Xlib_xcb::open()?;
let xrender = ffi::Xrender::open()?;
let xlib = try!(ffi::Xlib::open());
let xcursor = try!(ffi::Xcursor::open());
let xf86vmode = try!(ffi::Xf86vmode::open());
let xinput2 = try!(ffi::XInput2::open());
let xlib_xcb = try!(ffi::Xlib_xcb::open());
unsafe { (xlib.XInitThreads)() };
unsafe { (xlib.XSetErrorHandler)(error_handler) };
@@ -53,28 +44,22 @@ impl XConnection {
display
};
// Get X11 socket file descriptor
let fd = unsafe { (xlib.XConnectionNumber)(display) };
Ok(XConnection {
xlib,
xrandr,
xrandr_1_5,
xcursor,
xinput2,
xlib_xcb,
xrender,
display,
x11_fd: fd,
xlib: xlib,
xf86vmode: xf86vmode,
xcursor: xcursor,
xinput2: xinput2,
xlib_xcb: xlib_xcb,
display: display,
latest_error: Mutex::new(None),
cursor_cache: Default::default(),
})
}
/// Checks whether an error has been triggered by the previous function calls.
#[inline]
pub fn check_errors(&self) -> Result<(), XError> {
let error = self.latest_error.lock().take();
let error = self.latest_error.lock().unwrap().take();
if let Some(error) = error {
Err(error)
} else {
@@ -85,13 +70,7 @@ impl XConnection {
/// Ignores any previous error.
#[inline]
pub fn ignore_error(&self) {
*self.latest_error.lock() = None;
}
}
impl fmt::Debug for XConnection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display.fmt(f)
*self.latest_error.lock().unwrap() = None;
}
}
@@ -111,15 +90,17 @@ pub struct XError {
pub minor_code: u8,
}
impl Error for XError {}
impl Error for XError {
#[inline]
fn description(&self) -> &str {
&self.description
}
}
impl fmt::Display for XError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
formatter,
"X error: {} (code: {}, request code: {}, minor code: {})",
self.description, self.error_code, self.request_code, self.minor_code
)
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(formatter, "X error: {} (code: {}, request code: {}, minor code: {})",
self.description, self.error_code, self.request_code, self.minor_code)
}
}
@@ -129,7 +110,7 @@ pub enum XNotSupported {
/// Failed to load one or several shared libraries.
LibraryOpenError(ffi::OpenError),
/// Connecting to the X server with `XOpenDisplay` failed.
XOpenDisplayFailed, // TODO: add better message
XOpenDisplayFailed, // TODO: add better message
}
impl From<ffi::OpenError> for XNotSupported {
@@ -139,27 +120,26 @@ impl From<ffi::OpenError> for XNotSupported {
}
}
impl XNotSupported {
fn description(&self) -> &'static str {
match self {
impl Error for XNotSupported {
#[inline]
fn description(&self) -> &str {
match *self {
XNotSupported::LibraryOpenError(_) => "Failed to load one of xlib's shared libraries",
XNotSupported::XOpenDisplayFailed => "Failed to open connection to X server",
}
}
}
impl Error for XNotSupported {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
fn cause(&self) -> Option<&Error> {
match *self {
XNotSupported::LibraryOpenError(ref err) => Some(err),
_ => None,
_ => None
}
}
}
impl fmt::Display for XNotSupported {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
formatter.write_str(self.description())
}
}

View File

@@ -1,250 +0,0 @@
#![cfg(target_os = "macos")]
use std::os::raw::c_void;
use crate::{
dpi::LogicalSize,
event_loop::{EventLoop, EventLoopWindowTarget},
monitor::MonitorHandle,
platform_impl::get_aux_state_mut,
window::{Window, WindowBuilder},
};
/// Additional methods on `Window` that are specific to MacOS.
pub trait WindowExtMacOS {
/// Returns a pointer to the cocoa `NSWindow` that is used by this window.
///
/// The pointer will become invalid when the `Window` is destroyed.
fn ns_window(&self) -> *mut c_void;
/// Returns a pointer to the cocoa `NSView` that is used by this window.
///
/// The pointer will become invalid when the `Window` is destroyed.
fn ns_view(&self) -> *mut c_void;
/// Returns whether or not the window is in simple fullscreen mode.
fn simple_fullscreen(&self) -> bool;
/// Toggles a fullscreen mode that doesn't require a new macOS space.
/// Returns a boolean indicating whether the transition was successful (this
/// won't work if the window was already in the native fullscreen).
///
/// This is how fullscreen used to work on macOS in versions before Lion.
/// And allows the user to have a fullscreen window without using another
/// space or taking control over the entire monitor.
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool;
/// Returns whether or not the window has shadow.
fn has_shadow(&self) -> bool;
/// Sets whether or not the window has shadow.
fn set_has_shadow(&self, has_shadow: bool);
}
impl WindowExtMacOS for Window {
#[inline]
fn ns_window(&self) -> *mut c_void {
self.window.ns_window()
}
#[inline]
fn ns_view(&self) -> *mut c_void {
self.window.ns_view()
}
#[inline]
fn simple_fullscreen(&self) -> bool {
self.window.simple_fullscreen()
}
#[inline]
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
self.window.set_simple_fullscreen(fullscreen)
}
#[inline]
fn has_shadow(&self) -> bool {
self.window.has_shadow()
}
#[inline]
fn set_has_shadow(&self, has_shadow: bool) {
self.window.set_has_shadow(has_shadow)
}
}
/// Corresponds to `NSApplicationActivationPolicy`.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ActivationPolicy {
/// Corresponds to `NSApplicationActivationPolicyRegular`.
Regular,
/// Corresponds to `NSApplicationActivationPolicyAccessory`.
Accessory,
/// Corresponds to `NSApplicationActivationPolicyProhibited`.
Prohibited,
}
impl Default for ActivationPolicy {
fn default() -> Self {
ActivationPolicy::Regular
}
}
/// Additional methods on `WindowBuilder` that are specific to MacOS.
///
/// **Note:** Properties dealing with the titlebar will be overwritten by the `with_decorations` method
/// on the base `WindowBuilder`:
///
/// - `with_titlebar_transparent`
/// - `with_title_hidden`
/// - `with_titlebar_hidden`
/// - `with_titlebar_buttons_hidden`
/// - `with_fullsize_content_view`
pub trait WindowBuilderExtMacOS {
/// 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<f64>) -> WindowBuilder;
fn with_disallow_hidpi(self, disallow_hidpi: bool) -> WindowBuilder;
fn with_has_shadow(self, has_shadow: bool) -> WindowBuilder;
}
impl WindowBuilderExtMacOS for WindowBuilder {
#[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
}
#[inline]
fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> WindowBuilder {
self.platform_specific.titlebar_transparent = titlebar_transparent;
self
}
#[inline]
fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> WindowBuilder {
self.platform_specific.titlebar_hidden = titlebar_hidden;
self
}
#[inline]
fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> WindowBuilder {
self.platform_specific.titlebar_buttons_hidden = titlebar_buttons_hidden;
self
}
#[inline]
fn with_title_hidden(mut self, title_hidden: bool) -> WindowBuilder {
self.platform_specific.title_hidden = title_hidden;
self
}
#[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<f64>) -> WindowBuilder {
self.platform_specific.resize_increments = Some(increments.into());
self
}
#[inline]
fn with_disallow_hidpi(mut self, disallow_hidpi: bool) -> WindowBuilder {
self.platform_specific.disallow_hidpi = disallow_hidpi;
self
}
#[inline]
fn with_has_shadow(mut self, has_shadow: bool) -> WindowBuilder {
self.platform_specific.has_shadow = has_shadow;
self
}
}
pub trait EventLoopExtMacOS {
/// Sets the activation policy for the application. It is set to
/// `NSApplicationActivationPolicyRegular` by default.
///
/// This function only takes effect if it's called before calling [`run`](crate::event_loop::EventLoop::run) or
/// [`run_return`](crate::platform::run_return::EventLoopExtRunReturn::run_return)
fn set_activation_policy(&mut self, activation_policy: ActivationPolicy);
/// Used to prevent a default menubar menu from getting created
///
/// The default menu creation is enabled by default.
///
/// This function only takes effect if it's called before calling
/// [`run`](crate::event_loop::EventLoop::run) or
/// [`run_return`](crate::platform::run_return::EventLoopExtRunReturn::run_return)
fn enable_default_menu_creation(&mut self, enable: bool);
}
impl<T> EventLoopExtMacOS for EventLoop<T> {
#[inline]
fn set_activation_policy(&mut self, activation_policy: ActivationPolicy) {
unsafe {
get_aux_state_mut(&**self.event_loop.delegate).activation_policy = activation_policy;
}
}
#[inline]
fn enable_default_menu_creation(&mut self, enable: bool) {
unsafe {
get_aux_state_mut(&**self.event_loop.delegate).create_default_menu = enable;
}
}
}
/// Additional methods on `MonitorHandle` that are specific to MacOS.
pub trait MonitorHandleExtMacOS {
/// Returns the identifier of the monitor for Cocoa.
fn native_id(&self) -> u32;
/// Returns a pointer to the NSScreen representing this monitor.
fn ns_screen(&self) -> Option<*mut c_void>;
}
impl MonitorHandleExtMacOS for MonitorHandle {
#[inline]
fn native_id(&self) -> u32 {
self.inner.native_identifier()
}
fn ns_screen(&self) -> Option<*mut c_void> {
self.inner.ns_screen().map(|s| s as *mut c_void)
}
}
/// Additional methods on `EventLoopWindowTarget` that are specific to macOS.
pub trait EventLoopWindowTargetExtMacOS {
/// Hide the entire application. In most applications this is typically triggered with Command-H.
fn hide_application(&self);
/// Hide the other applications. In most applications this is typically triggered with Command+Option-H.
fn hide_other_applications(&self);
}
impl<T> EventLoopWindowTargetExtMacOS for EventLoopWindowTarget<T> {
fn hide_application(&self) {
self.p.hide_application()
}
fn hide_other_applications(&self) {
self.p.hide_other_applications()
}
}

View File

@@ -0,0 +1,634 @@
use cocoa::{self, appkit, foundation};
use cocoa::appkit::{NSApplication, NSEvent, NSView, NSWindow};
use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent, ModifiersState};
use super::window::Window;
use std;
pub struct EventsLoop {
pub windows: std::sync::Mutex<Vec<std::sync::Weak<Window>>>,
pub pending_events: std::sync::Mutex<std::collections::VecDeque<Event>>,
modifiers: std::sync::Mutex<Modifiers>,
interrupted: std::sync::atomic::AtomicBool,
// The user event callback given via either of the `poll_events` or `run_forever` methods.
//
// We store the user's callback here so that it may be accessed by each of the window delegate
// callbacks (e.g. resize, close, etc) for the duration of a call to either of the
// `poll_events` or `run_forever` methods.
//
// This is *only* `Some` for the duration of a call to either of these methods and will be
// `None` otherwise.
user_callback: UserCallback,
}
struct Modifiers {
shift_pressed: bool,
ctrl_pressed: bool,
win_pressed: bool,
alt_pressed: bool,
}
// Wrapping the user callback in a type allows us to:
//
// - ensure the callback pointer is never accidentally cloned
// - ensure that only the `EventsLoop` can `store` and `drop` the callback pointer
// - `unsafe impl Send` and `Sync` so that `Send` and `Sync` can be implemented for `EventsLoop`.
pub struct UserCallback {
mutex: std::sync::Mutex<Option<*mut FnMut(Event)>>,
}
unsafe impl Send for UserCallback {}
unsafe impl Sync for UserCallback {}
impl UserCallback {
// Here we store user's `callback` behind the mutex so that they may be safely shared between
// each of the window delegates.
//
// In order to make sure that the pointer is always valid, we must manually guarantee that it
// is dropped before the callback itself is dropped. Thus, this should *only* be called at the
// beginning of a call to `poll_events` and `run_forever`, both of which *must* drop the
// callback at the end of their scope using the `drop` method.
fn store<F>(&self, callback: &mut F)
where F: FnMut(Event)
{
let trait_object = callback as &mut FnMut(Event);
let trait_object_ptr = trait_object as *const FnMut(Event) as *mut FnMut(Event);
*self.mutex.lock().unwrap() = Some(trait_object_ptr);
}
// Emits the given event via the user-given callback.
//
// This is unsafe as it requires dereferencing the pointer to the user-given callback. We
// guarantee this is safe by ensuring the `UserCallback` never lives longer than the user-given
// callback.
//
// Note that the callback may not always be `Some`. This is because some `NSWindowDelegate`
// callbacks can be triggered by means other than `NSApp().sendEvent`. For example, if a window
// is destroyed or created during a call to the user's callback, the `WindowDelegate` methods
// may be called with `windowShouldClose` or `windowDidResignKey`.
unsafe fn call_with_event(&self, event: Event) {
let callback = match self.mutex.lock().unwrap().take() {
Some(callback) => callback,
None => return,
};
(*callback)(event);
*self.mutex.lock().unwrap() = Some(callback);
}
// Used to drop the user callback pointer at the end of the `poll_events` and `run_forever`
// methods. This is done to enforce our guarantee that the top callback will never live longer
// than the call to either `poll_events` or `run_forever` to which it was given.
fn drop(&self) {
self.mutex.lock().unwrap().take();
}
}
impl EventsLoop {
pub fn new() -> Self {
let modifiers = Modifiers {
shift_pressed: false,
ctrl_pressed: false,
win_pressed: false,
alt_pressed: false,
};
EventsLoop {
windows: std::sync::Mutex::new(Vec::new()),
pending_events: std::sync::Mutex::new(std::collections::VecDeque::new()),
modifiers: std::sync::Mutex::new(modifiers),
interrupted: std::sync::atomic::AtomicBool::new(false),
user_callback: UserCallback { mutex: std::sync::Mutex::new(None) },
}
}
pub fn poll_events<F>(&self, mut callback: F)
where F: FnMut(Event),
{
unsafe {
if !msg_send![cocoa::base::class("NSThread"), isMainThread] {
panic!("Events can only be polled from the main thread on macOS");
}
}
self.user_callback.store(&mut callback);
// Loop as long as we have pending events to return.
loop {
unsafe {
// First, yield all pending events.
self.call_user_callback_with_pending_events();
let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
// Poll for the next event, returning `nil` if there are none.
let ns_event = appkit::NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_(
appkit::NSAnyEventMask.bits() | appkit::NSEventMaskPressure.bits(),
foundation::NSDate::distantPast(cocoa::base::nil),
foundation::NSDefaultRunLoopMode,
cocoa::base::YES);
let event = self.ns_event_to_event(ns_event);
let _: () = msg_send![pool, release];
match event {
// Call the user's callback.
Some(event) => self.user_callback.call_with_event(event),
None => break,
}
}
}
self.user_callback.drop();
}
pub fn run_forever<F>(&self, mut callback: F)
where F: FnMut(Event)
{
self.interrupted.store(false, std::sync::atomic::Ordering::Relaxed);
unsafe {
if !msg_send![cocoa::base::class("NSThread"), isMainThread] {
panic!("Events can only be polled from the main thread on macOS");
}
}
self.user_callback.store(&mut callback);
loop {
unsafe {
// First, yield all pending events.
self.call_user_callback_with_pending_events();
let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
// Wait for the next event. Note that this function blocks during resize.
let ns_event = appkit::NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_(
appkit::NSAnyEventMask.bits() | appkit::NSEventMaskPressure.bits(),
foundation::NSDate::distantFuture(cocoa::base::nil),
foundation::NSDefaultRunLoopMode,
cocoa::base::YES);
let maybe_event = self.ns_event_to_event(ns_event);
// Release the pool before calling the top callback in case the user calls either
// `run_forever` or `poll_events` within the callback.
let _: () = msg_send![pool, release];
if let Some(event) = maybe_event {
self.user_callback.call_with_event(event);
}
}
if self.interrupted.load(std::sync::atomic::Ordering::Relaxed) {
self.interrupted.store(false, std::sync::atomic::Ordering::Relaxed);
break;
}
}
self.user_callback.drop();
}
pub fn interrupt(&self) {
self.interrupted.store(true, std::sync::atomic::Ordering::Relaxed);
// Awaken the event loop by triggering `NSApplicationActivatedEventType`.
unsafe {
let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
let event =
NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
cocoa::base::nil,
appkit::NSApplicationDefined,
foundation::NSPoint::new(0.0, 0.0),
appkit::NSEventModifierFlags::empty(),
0.0,
0,
cocoa::base::nil,
appkit::NSEventSubtype::NSApplicationActivatedEventType,
0,
0);
appkit::NSApp().postEvent_atStart_(event, cocoa::base::NO);
foundation::NSAutoreleasePool::drain(pool);
}
}
// Removes the window with the given `Id` from the `windows` list.
//
// This is called when a window is either `Closed` or `Drop`ped.
pub fn find_and_remove_window(&self, id: super::window::Id) {
if let Ok(mut windows) = self.windows.lock() {
windows.retain(|w| match w.upgrade() {
Some(w) => w.id() != id,
None => true,
});
}
}
fn call_user_callback_with_pending_events(&self) {
loop {
let event = match self.pending_events.lock().unwrap().pop_front() {
Some(event) => event,
None => return,
};
unsafe {
self.user_callback.call_with_event(event);
}
}
}
// Calls the user callback if one exists.
//
// Otherwise, stores the event in the `pending_events` queue.
//
// This is necessary for the case when `WindowDelegate` callbacks are triggered during a call
// to the user's callback.
pub fn call_user_callback_with_event_or_store_in_pending(&self, event: Event) {
if self.user_callback.mutex.lock().unwrap().is_some() {
unsafe {
self.user_callback.call_with_event(event);
}
} else {
self.pending_events.lock().unwrap().push_back(event);
}
}
// Convert some given `NSEvent` into a winit `Event`.
unsafe fn ns_event_to_event(&self, ns_event: cocoa::base::id) -> Option<Event> {
if ns_event == cocoa::base::nil {
return None;
}
// FIXME: Despite not being documented anywhere, an `NSEvent` is produced when a user opens
// Spotlight while the NSApplication is in focus. This `NSEvent` produces a `NSEventType`
// with value `21`. This causes a SEGFAULT as soon as we try to match on the `NSEventType`
// enum as there is no variant associated with the value. Thus, we return early if this
// sneaky event occurs. If someone does find some documentation on this, please fix this by
// adding an appropriate variant to the `NSEventType` enum in the cocoa-rs crate.
if ns_event.eventType() as u64 == 21 {
return None;
}
let event_type = ns_event.eventType();
let ns_window = ns_event.window();
let window_id = super::window::get_window_id(ns_window);
// 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),
}
let windows = self.windows.lock().unwrap();
let maybe_window = windows.iter()
.filter_map(std::sync::Weak::upgrade)
.find(|window| window_id == window.id());
let into_event = |window_event| Event::WindowEvent {
window_id: ::WindowId(window_id),
event: window_event,
};
// Returns `Some` window if one of our windows is the key window.
let maybe_key_window = || windows.iter()
.filter_map(std::sync::Weak::upgrade)
.find(|window| {
let is_key_window: cocoa::base::BOOL = msg_send![*window.window, isKeyWindow];
is_key_window == cocoa::base::YES
});
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);
for received_char in std::str::from_utf8(received_str.to_bytes()).unwrap().chars() {
let window_event = WindowEvent::ReceivedCharacter(received_char);
events.push_back(into_event(window_event));
}
let vkey = to_virtual_key_code(NSEvent::keyCode(ns_event));
let state = ElementState::Pressed;
let code = NSEvent::keyCode(ns_event) as u8;
let window_event = WindowEvent::KeyboardInput(state, code, vkey, event_mods(ns_event));
events.push_back(into_event(window_event));
let event = events.pop_front();
self.pending_events.lock().unwrap().extend(events.into_iter());
event
},
appkit::NSKeyUp => {
let vkey = to_virtual_key_code(NSEvent::keyCode(ns_event));
let state = ElementState::Released;
let code = NSEvent::keyCode(ns_event) as u8;
let window_event = WindowEvent::KeyboardInput(state, code, vkey, event_mods(ns_event));
Some(into_event(window_event))
},
appkit::NSFlagsChanged => {
let mut modifiers = self.modifiers.lock().unwrap();
unsafe fn modifier_event(event: cocoa::base::id,
keymask: appkit::NSEventModifierFlags,
key: events::VirtualKeyCode,
key_pressed: bool) -> Option<WindowEvent>
{
if !key_pressed && NSEvent::modifierFlags(event).contains(keymask) {
let state = ElementState::Pressed;
let code = NSEvent::keyCode(event) as u8;
let window_event = WindowEvent::KeyboardInput(state, code, Some(key), event_mods(event));
Some(window_event)
} else if key_pressed && !NSEvent::modifierFlags(event).contains(keymask) {
let state = ElementState::Released;
let code = NSEvent::keyCode(event) as u8;
let window_event = WindowEvent::KeyboardInput(state, code, Some(key), event_mods(event));
Some(window_event)
} else {
None
}
}
let mut events = std::collections::VecDeque::new();
if let Some(window_event) = modifier_event(ns_event,
appkit::NSShiftKeyMask,
events::VirtualKeyCode::LShift,
modifiers.shift_pressed)
{
modifiers.shift_pressed = !modifiers.shift_pressed;
events.push_back(into_event(window_event));
}
if let Some(window_event) = modifier_event(ns_event,
appkit::NSControlKeyMask,
events::VirtualKeyCode::LControl,
modifiers.ctrl_pressed)
{
modifiers.ctrl_pressed = !modifiers.ctrl_pressed;
events.push_back(into_event(window_event));
}
if let Some(window_event) = modifier_event(ns_event,
appkit::NSCommandKeyMask,
events::VirtualKeyCode::LWin,
modifiers.win_pressed)
{
modifiers.win_pressed = !modifiers.win_pressed;
events.push_back(into_event(window_event));
}
if let Some(window_event) = modifier_event(ns_event,
appkit::NSAlternateKeyMask,
events::VirtualKeyCode::LAlt,
modifiers.alt_pressed)
{
modifiers.alt_pressed = !modifiers.alt_pressed;
events.push_back(into_event(window_event));
}
let event = events.pop_front();
self.pending_events.lock().unwrap().extend(events.into_iter());
event
},
appkit::NSLeftMouseDown => { Some(into_event(WindowEvent::MouseInput(ElementState::Pressed, MouseButton::Left))) },
appkit::NSLeftMouseUp => { Some(into_event(WindowEvent::MouseInput(ElementState::Released, MouseButton::Left))) },
appkit::NSRightMouseDown => { Some(into_event(WindowEvent::MouseInput(ElementState::Pressed, MouseButton::Right))) },
appkit::NSRightMouseUp => { Some(into_event(WindowEvent::MouseInput(ElementState::Released, MouseButton::Right))) },
appkit::NSOtherMouseDown => { Some(into_event(WindowEvent::MouseInput(ElementState::Pressed, MouseButton::Middle))) },
appkit::NSOtherMouseUp => { Some(into_event(WindowEvent::MouseInput(ElementState::Released, MouseButton::Middle))) },
appkit::NSMouseEntered => { Some(into_event(WindowEvent::MouseEntered)) },
appkit::NSMouseExited => { Some(into_event(WindowEvent::MouseLeft)) },
appkit::NSMouseMoved |
appkit::NSLeftMouseDragged |
appkit::NSOtherMouseDragged |
appkit::NSRightMouseDragged => {
// 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,
None => return None,
};
let window_point = ns_event.locationInWindow();
let view_point = if ns_window == cocoa::base::nil {
let ns_size = foundation::NSSize::new(0.0, 0.0);
let ns_rect = foundation::NSRect::new(window_point, ns_size);
let window_rect = window.window.convertRectFromScreen_(ns_rect);
window.view.convertPoint_fromView_(window_rect.origin, cocoa::base::nil)
} else {
window.view.convertPoint_fromView_(window_point, cocoa::base::nil)
};
let view_rect = NSView::frame(*window.view);
let scale_factor = window.hidpi_factor();
let x = (scale_factor * view_point.x as f32) as i32;
let y = (scale_factor * (view_rect.size.height - view_point.y) as f32) as i32;
let window_event = WindowEvent::MouseMoved(x, y);
let event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event };
Some(event)
},
appkit::NSScrollWheel => {
// If none of the windows received the scroll, return `None`.
let window = match maybe_window {
Some(window) => window,
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)
} else {
LineDelta(scale_factor * ns_event.scrollingDeltaX() as f32,
scale_factor * ns_event.scrollingDeltaY() as f32)
};
let phase = match ns_event.phase() {
appkit::NSEventPhaseMayBegin | appkit::NSEventPhaseBegan => TouchPhase::Started,
appkit::NSEventPhaseEnded => TouchPhase::Ended,
_ => TouchPhase::Moved,
};
let window_event = WindowEvent::MouseWheel(delta, phase);
Some(into_event(window_event))
},
appkit::NSEventTypePressure => {
let pressure = ns_event.pressure();
let stage = ns_event.stage();
let window_event = WindowEvent::TouchpadPressure(pressure, stage);
Some(into_event(window_event))
},
appkit::NSApplicationDefined => match ns_event.subtype() {
appkit::NSEventSubtype::NSApplicationActivatedEventType => {
Some(into_event(WindowEvent::Awakened))
},
_ => None,
},
_ => None,
}
}
}
fn to_virtual_key_code(code: u16) -> Option<events::VirtualKeyCode> {
Some(match code {
0x00 => events::VirtualKeyCode::A,
0x01 => events::VirtualKeyCode::S,
0x02 => events::VirtualKeyCode::D,
0x03 => events::VirtualKeyCode::F,
0x04 => events::VirtualKeyCode::H,
0x05 => events::VirtualKeyCode::G,
0x06 => events::VirtualKeyCode::Z,
0x07 => events::VirtualKeyCode::X,
0x08 => events::VirtualKeyCode::C,
0x09 => events::VirtualKeyCode::V,
//0x0a => World 1,
0x0b => events::VirtualKeyCode::B,
0x0c => events::VirtualKeyCode::Q,
0x0d => events::VirtualKeyCode::W,
0x0e => events::VirtualKeyCode::E,
0x0f => events::VirtualKeyCode::R,
0x10 => events::VirtualKeyCode::Y,
0x11 => events::VirtualKeyCode::T,
0x12 => events::VirtualKeyCode::Key1,
0x13 => events::VirtualKeyCode::Key2,
0x14 => events::VirtualKeyCode::Key3,
0x15 => events::VirtualKeyCode::Key4,
0x16 => events::VirtualKeyCode::Key6,
0x17 => events::VirtualKeyCode::Key5,
0x18 => events::VirtualKeyCode::Equals,
0x19 => events::VirtualKeyCode::Key9,
0x1a => events::VirtualKeyCode::Key7,
0x1b => events::VirtualKeyCode::Minus,
0x1c => events::VirtualKeyCode::Key8,
0x1d => events::VirtualKeyCode::Key0,
0x1e => events::VirtualKeyCode::RBracket,
0x1f => events::VirtualKeyCode::O,
0x20 => events::VirtualKeyCode::U,
0x21 => events::VirtualKeyCode::LBracket,
0x22 => events::VirtualKeyCode::I,
0x23 => events::VirtualKeyCode::P,
0x24 => events::VirtualKeyCode::Return,
0x25 => events::VirtualKeyCode::L,
0x26 => events::VirtualKeyCode::J,
0x27 => events::VirtualKeyCode::Apostrophe,
0x28 => events::VirtualKeyCode::K,
0x29 => events::VirtualKeyCode::Semicolon,
0x2a => events::VirtualKeyCode::Backslash,
0x2b => events::VirtualKeyCode::Comma,
0x2c => events::VirtualKeyCode::Slash,
0x2d => events::VirtualKeyCode::N,
0x2e => events::VirtualKeyCode::M,
0x2f => events::VirtualKeyCode::Period,
0x30 => events::VirtualKeyCode::Tab,
0x31 => events::VirtualKeyCode::Space,
0x32 => events::VirtualKeyCode::Grave,
0x33 => events::VirtualKeyCode::Back,
//0x34 => unkown,
0x35 => events::VirtualKeyCode::Escape,
0x36 => events::VirtualKeyCode::RWin,
0x37 => events::VirtualKeyCode::LWin,
0x38 => events::VirtualKeyCode::LShift,
//0x39 => Caps lock,
//0x3a => Left alt,
0x3b => events::VirtualKeyCode::LControl,
0x3c => events::VirtualKeyCode::RShift,
//0x3d => Right alt,
0x3e => events::VirtualKeyCode::RControl,
//0x3f => Fn key,
//0x40 => F17 Key,
0x41 => events::VirtualKeyCode::Decimal,
//0x42 -> unkown,
0x43 => events::VirtualKeyCode::Multiply,
//0x44 => unkown,
0x45 => events::VirtualKeyCode::Add,
//0x46 => unkown,
0x47 => events::VirtualKeyCode::Numlock,
//0x48 => KeypadClear,
0x49 => events::VirtualKeyCode::VolumeUp,
0x4a => events::VirtualKeyCode::VolumeDown,
0x4b => events::VirtualKeyCode::Divide,
0x4c => events::VirtualKeyCode::NumpadEnter,
//0x4d => unkown,
0x4e => events::VirtualKeyCode::Subtract,
//0x4f => F18 key,
//0x50 => F19 Key,
0x51 => events::VirtualKeyCode::NumpadEquals,
0x52 => events::VirtualKeyCode::Numpad0,
0x53 => events::VirtualKeyCode::Numpad1,
0x54 => events::VirtualKeyCode::Numpad2,
0x55 => events::VirtualKeyCode::Numpad3,
0x56 => events::VirtualKeyCode::Numpad4,
0x57 => events::VirtualKeyCode::Numpad5,
0x58 => events::VirtualKeyCode::Numpad6,
0x59 => events::VirtualKeyCode::Numpad7,
//0x5a => F20 Key,
0x5b => events::VirtualKeyCode::Numpad8,
0x5c => events::VirtualKeyCode::Numpad9,
//0x5d => unkown,
//0x5e => unkown,
//0x5f => unkown,
0x60 => events::VirtualKeyCode::F5,
0x61 => events::VirtualKeyCode::F6,
0x62 => events::VirtualKeyCode::F7,
0x63 => events::VirtualKeyCode::F3,
0x64 => events::VirtualKeyCode::F8,
0x65 => events::VirtualKeyCode::F9,
//0x66 => unkown,
0x67 => events::VirtualKeyCode::F11,
//0x68 => unkown,
0x69 => events::VirtualKeyCode::F13,
//0x6a => F16 Key,
0x6b => events::VirtualKeyCode::F14,
//0x6c => unkown,
0x6d => events::VirtualKeyCode::F10,
//0x6e => unkown,
0x6f => events::VirtualKeyCode::F12,
//0x70 => unkown,
0x71 => events::VirtualKeyCode::F15,
0x72 => events::VirtualKeyCode::Insert,
0x73 => events::VirtualKeyCode::Home,
0x74 => events::VirtualKeyCode::PageUp,
0x75 => events::VirtualKeyCode::Delete,
0x76 => events::VirtualKeyCode::F4,
0x77 => events::VirtualKeyCode::End,
0x78 => events::VirtualKeyCode::F2,
0x79 => events::VirtualKeyCode::PageDown,
0x7a => events::VirtualKeyCode::F1,
0x7b => events::VirtualKeyCode::Left,
0x7c => events::VirtualKeyCode::Right,
0x7d => events::VirtualKeyCode::Down,
0x7e => events::VirtualKeyCode::Up,
//0x7f => unkown,
_ => return None,
})
}
fn event_mods(event: cocoa::base::id) -> ModifiersState {
let flags = unsafe {
NSEvent::modifierFlags(event)
};
ModifiersState {
shift: flags.contains(appkit::NSShiftKeyMask),
ctrl: flags.contains(appkit::NSControlKeyMask),
alt: flags.contains(appkit::NSAlternateKeyMask),
logo: flags.contains(appkit::NSCommandKeyMask),
}
}

38
src/platform/macos/mod.rs Normal file
View File

@@ -0,0 +1,38 @@
#![cfg(target_os = "macos")]
pub use self::events_loop::EventsLoop;
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
pub use self::window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, Window};
use {CreationError};
pub struct Window2 {
pub window: ::std::sync::Arc<Window>,
}
impl ::std::ops::Deref for Window2 {
type Target = Window;
#[inline]
fn deref(&self) -> &Window {
&*self.window
}
}
impl Window2 {
pub fn new(events_loop: ::std::sync::Arc<EventsLoop>,
attributes: &::WindowAttributes,
pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result<Self, CreationError>
{
let weak_events_loop = ::std::sync::Arc::downgrade(&events_loop);
let window = ::std::sync::Arc::new(try!(Window::new(weak_events_loop, attributes, pl_attribs)));
let weak_window = ::std::sync::Arc::downgrade(&window);
events_loop.windows.lock().unwrap().push(weak_window);
Ok(Window2 { window: window })
}
}
mod events_loop;
mod monitor;
mod window;

View File

@@ -0,0 +1,50 @@
use core_graphics::display;
use std::collections::VecDeque;
use native_monitor::NativeMonitorId;
#[derive(Clone)]
pub struct MonitorId(u32);
pub fn get_available_monitors() -> VecDeque<MonitorId> {
let mut monitors = VecDeque::new();
unsafe {
let max_displays = 10u32;
let mut active_displays = [0u32; 10];
let mut display_count = 0;
display::CGGetActiveDisplayList(max_displays, &mut active_displays[0], &mut display_count);
for i in 0..display_count as usize {
monitors.push_back(MonitorId(active_displays[i]));
}
}
monitors
}
#[inline]
pub fn get_primary_monitor() -> MonitorId {
let id = unsafe { MonitorId(display::CGMainDisplayID()) };
id
}
impl MonitorId {
pub fn get_name(&self) -> Option<String> {
let MonitorId(display_id) = *self;
let screen_num = unsafe { display::CGDisplayModelNumber(display_id) };
Some(format!("Monitor #{}", screen_num))
}
#[inline]
pub fn get_native_identifier(&self) -> NativeMonitorId {
let MonitorId(display_id) = *self;
NativeMonitorId::Numeric(display_id)
}
pub fn get_dimensions(&self) -> (u32, u32) {
let MonitorId(display_id) = *self;
let dimension = unsafe {
let height = display::CGDisplayPixelsHigh(display_id);
let width = display::CGDisplayPixelsWide(display_id);
(width as u32, height as u32)
};
dimension
}
}

View File

@@ -0,0 +1,656 @@
use {CreationError, Event, WindowEvent, WindowId, MouseCursor, CursorState};
use CreationError::OsError;
use libc;
use WindowAttributes;
use native_monitor::NativeMonitorId;
use os::macos::ActivationPolicy;
use objc;
use objc::runtime::{Class, Object, Sel, BOOL, YES, NO};
use objc::declare::ClassDecl;
use cocoa;
use cocoa::base::{id, nil};
use cocoa::foundation::{NSPoint, NSRect, NSSize, NSString, NSUInteger};
use cocoa::appkit::{self, NSApplication, NSColor, NSView, NSWindow};
use core_graphics::display::{CGAssociateMouseAndMouseCursorPosition, CGMainDisplayID, CGDisplayPixelsHigh, CGWarpMouseCursorPosition};
use std;
use std::ops::Deref;
use std::os::raw::c_void;
use os::macos::WindowExt;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Id(pub usize);
struct DelegateState {
view: IdRef,
window: IdRef,
events_loop: std::sync::Weak<super::EventsLoop>,
}
pub struct WindowDelegate {
state: Box<DelegateState>,
_this: IdRef,
}
impl WindowDelegate {
/// 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(events_loop) = state.events_loop.upgrade() {
events_loop.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));
}
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::Closed);
// Remove the window from the events_loop.
if let Some(events_loop) = state.events_loop.upgrade() {
let window_id = get_window_id(*state.window);
events_loop.find_and_remove_window(window_id);
}
}
YES
}
extern fn window_did_resize(this: &Object, _: Sel, _: id) {
unsafe {
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
emit_resize_event(state);
}
}
extern fn window_did_change_screen(this: &Object, _: Sel, _: id) {
unsafe {
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
emit_resize_event(state);
}
}
extern fn window_did_become_key(this: &Object, _: Sel, _: id) {
unsafe {
// TODO: center the cursor if the window had mouse grab when it
// lost focus
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
emit_event(state, WindowEvent::Focused(true));
}
}
extern fn window_did_resign_key(this: &Object, _: Sel, _: id) {
unsafe {
let state: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state as *mut DelegateState);
emit_event(state, WindowEvent::Focused(false));
}
}
static mut DELEGATE_CLASS: *const Class = 0 as *const Class;
static INIT: std::sync::Once = std::sync::ONCE_INIT;
INIT.call_once(|| unsafe {
// Create new NSWindowDelegate
let superclass = Class::get("NSObject").unwrap();
let mut decl = ClassDecl::new("WinitWindowDelegate", superclass).unwrap();
// Add callback methods
decl.add_method(sel!(windowShouldClose:),
window_should_close as extern fn(&Object, Sel, id) -> BOOL);
decl.add_method(sel!(windowDidResize:),
window_did_resize as extern fn(&Object, Sel, id));
decl.add_method(sel!(windowDidChangeScreen:),
window_did_change_screen as extern fn(&Object, Sel, id));
decl.add_method(sel!(windowDidBecomeKey:),
window_did_become_key as extern fn(&Object, Sel, id));
decl.add_method(sel!(windowDidResignKey:),
window_did_resign_key as extern fn(&Object, Sel, id));
// Store internal state as user data
decl.add_ivar::<*mut c_void>("winitState");
DELEGATE_CLASS = decl.register();
});
unsafe {
DELEGATE_CLASS
}
}
fn new(state: DelegateState) -> WindowDelegate {
// Box the state so we can give a pointer to it
let mut state = Box::new(state);
let state_ptr: *mut DelegateState = &mut *state;
unsafe {
let delegate = IdRef::new(msg_send![WindowDelegate::class(), new]);
(&mut **delegate).set_ivar("winitState", state_ptr as *mut ::std::os::raw::c_void);
let _: () = msg_send![*state.window, setDelegate:*delegate];
WindowDelegate { state: state, _this: delegate }
}
}
}
impl Drop for WindowDelegate {
fn drop(&mut self) {
unsafe {
// Nil the window's delegate so it doesn't still reference us
let _: () = msg_send![*self.state.window, setDelegate:nil];
}
}
}
#[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes {
pub activation_policy: ActivationPolicy,
}
pub struct Window {
pub view: IdRef,
pub window: IdRef,
pub delegate: WindowDelegate,
}
unsafe impl Send for Window {}
unsafe impl Sync for Window {}
impl Drop for Window {
fn drop(&mut self) {
// Remove this window from the `EventLoop`s list of windows.
let id = self.id();
if let Some(ev) = self.delegate.state.events_loop.upgrade() {
ev.find_and_remove_window(id);
}
// Close the window if it has not yet been closed.
let nswindow = *self.window;
if nswindow != nil {
unsafe {
msg_send![nswindow, close];
}
}
}
}
impl WindowExt for Window {
#[inline]
fn get_nswindow(&self) -> *mut c_void {
*self.window as *mut c_void
}
#[inline]
fn get_nsview(&self) -> *mut c_void {
*self.view as *mut c_void
}
}
impl Window {
pub fn new(events_loop: std::sync::Weak<super::EventsLoop>,
win_attribs: &WindowAttributes,
pl_attribs: &PlatformSpecificWindowBuilderAttributes)
-> Result<Window, CreationError>
{
unsafe {
if !msg_send![cocoa::base::class("NSThread"), isMainThread] {
panic!("Windows can only be created on the main thread on macOS");
}
}
let app = match Window::create_app(pl_attribs.activation_policy) {
Some(app) => app,
None => { return Err(OsError(format!("Couldn't create NSApplication"))); },
};
let window = match Window::create_window(win_attribs)
{
Some(window) => window,
None => { return Err(OsError(format!("Couldn't create NSWindow"))); },
};
let view = match Window::create_view(*window) {
Some(view) => view,
None => { return Err(OsError(format!("Couldn't create NSView"))); },
};
unsafe {
if win_attribs.transparent {
(*window as id).setOpaque_(NO);
(*window as id).setBackgroundColor_(NSColor::clearColor(nil));
}
app.activateIgnoringOtherApps_(YES);
if win_attribs.visible {
window.makeKeyAndOrderFront_(nil);
} else {
window.makeKeyWindow();
}
if let Some((width, height)) = win_attribs.min_dimensions {
nswindow_set_min_dimensions(window.0, width.into(), height.into());
}
if let Some((width, height)) = win_attribs.max_dimensions {
nswindow_set_max_dimensions(window.0, width.into(), height.into());
}
}
let ds = DelegateState {
view: view.clone(),
window: window.clone(),
events_loop: events_loop,
};
let window = Window {
view: view,
window: window,
delegate: WindowDelegate::new(ds),
};
Ok(window)
}
pub fn id(&self) -> Id {
get_window_id(*self.window)
}
fn create_app(activation_policy: ActivationPolicy) -> Option<id> {
unsafe {
let app = appkit::NSApp();
if app == nil {
None
} else {
app.setActivationPolicy_(activation_policy.into());
app.finishLaunching();
Some(app)
}
}
}
fn create_window(attrs: &WindowAttributes) -> Option<IdRef> {
unsafe {
let screen = match attrs.monitor {
Some(ref monitor_id) => {
let native_id = match monitor_id.get_native_identifier() {
NativeMonitorId::Numeric(num) => num,
_ => panic!("OS X monitors should always have a numeric native ID")
};
let matching_screen = {
let screens = appkit::NSScreen::screens(nil);
let count: NSUInteger = msg_send![screens, count];
let key = IdRef::new(NSString::alloc(nil).init_str("NSScreenNumber"));
let mut matching_screen: Option<id> = None;
for i in 0..count {
let screen = msg_send![screens, objectAtIndex:i as NSUInteger];
let device_description = appkit::NSScreen::deviceDescription(screen);
let value: id = msg_send![device_description, objectForKey:*key];
if value != nil {
let screen_number: NSUInteger = msg_send![value, unsignedIntegerValue];
if screen_number as u32 == native_id {
matching_screen = Some(screen);
break;
}
}
}
matching_screen
};
Some(matching_screen.unwrap_or(appkit::NSScreen::mainScreen(nil)))
},
None => None
};
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 masks = if screen.is_some() {
// Fullscreen window
appkit::NSBorderlessWindowMask as NSUInteger |
appkit::NSResizableWindowMask as NSUInteger |
appkit::NSTitledWindowMask as NSUInteger
} else if attrs.decorations {
// Window with a titlebar
appkit::NSClosableWindowMask as NSUInteger |
appkit::NSMiniaturizableWindowMask as NSUInteger |
appkit::NSResizableWindowMask as NSUInteger |
appkit::NSTitledWindowMask as NSUInteger
} else {
// Window without a titlebar
appkit::NSClosableWindowMask as NSUInteger |
appkit::NSMiniaturizableWindowMask as NSUInteger |
appkit::NSResizableWindowMask as NSUInteger |
appkit::NSFullSizeContentViewWindowMask as NSUInteger
};
let window = IdRef::new(NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_(
frame,
masks,
appkit::NSBackingStoreBuffered,
NO,
));
window.non_nil().map(|window| {
let title = IdRef::new(NSString::alloc(nil).init_str(&attrs.title));
window.setReleasedWhenClosed_(NO);
window.setTitle_(*title);
window.setAcceptsMouseMovedEvents_(YES);
if !attrs.decorations {
window.setTitleVisibility_(appkit::NSWindowTitleVisibility::NSWindowTitleHidden);
window.setTitlebarAppearsTransparent_(YES);
}
if screen.is_some() {
window.setLevel_(appkit::NSMainMenuWindowLevel as i64 + 1);
}
else {
window.center();
}
window
})
}
}
fn create_view(window: id) -> Option<IdRef> {
unsafe {
let view = IdRef::new(NSView::alloc(nil).init());
view.non_nil().map(|view| {
view.setWantsBestResolutionOpenGLSurface_(YES);
window.setContentView_(*view);
view
})
}
}
pub fn set_title(&self, title: &str) {
unsafe {
let title = IdRef::new(NSString::alloc(nil).init_str(title));
self.window.setTitle_(*title);
}
}
#[inline]
pub fn show(&self) {
unsafe { NSWindow::makeKeyAndOrderFront_(*self.window, nil); }
}
#[inline]
pub fn hide(&self) {
unsafe { NSWindow::orderOut_(*self.window, nil); }
}
pub fn get_position(&self) -> Option<(i32, i32)> {
unsafe {
let content_rect = NSWindow::contentRectForFrameRect_(*self.window, NSWindow::frame(*self.window));
// TODO: consider extrapolating the calculations for the y axis to
// a private method
Some((content_rect.origin.x as i32, (CGDisplayPixelsHigh(CGMainDisplayID()) as f64 - (content_rect.origin.y + content_rect.size.height)) as i32))
}
}
pub fn set_position(&self, x: i32, y: i32) {
unsafe {
let frame = NSWindow::frame(*self.view);
// NOTE: `setFrameOrigin` might not give desirable results when
// setting window, as it treats bottom left as origin.
// `setFrameTopLeftPoint` treats top left as origin (duh), but
// does not equal the value returned by `get_window_position`
// (there is a difference by 22 for me on yosemite)
// TODO: consider extrapolating the calculations for the y axis to
// a private method
let dummy = NSRect::new(NSPoint::new(x as f64, CGDisplayPixelsHigh(CGMainDisplayID()) as f64 - (frame.size.height + y as f64)), NSSize::new(0f64, 0f64));
let conv = NSWindow::frameRectForContentRect_(*self.window, dummy);
// NSWindow::setFrameTopLeftPoint_(*self.window, conv.origin);
NSWindow::setFrameOrigin_(*self.window, conv.origin);
}
}
#[inline]
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
unsafe {
let view_frame = NSView::frame(*self.view);
Some((view_frame.size.width as u32, view_frame.size.height as u32))
}
}
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
unsafe {
let window_frame = NSWindow::frame(*self.window);
Some((window_frame.size.width as u32, window_frame.size.height as u32))
}
}
#[inline]
pub fn set_inner_size(&self, width: u32, height: u32) {
unsafe {
NSWindow::setContentSize_(*self.window, NSSize::new(width as f64, height as f64));
}
}
#[inline]
pub fn platform_display(&self) -> *mut libc::c_void {
unimplemented!()
}
#[inline]
pub fn platform_window(&self) -> *mut libc::c_void {
*self.window as *mut libc::c_void
}
pub fn set_cursor(&self, cursor: MouseCursor) {
let cursor_name = match cursor {
MouseCursor::Arrow | MouseCursor::Default => "arrowCursor",
MouseCursor::Hand => "pointingHandCursor",
MouseCursor::Grabbing | MouseCursor::Grab => "closedHandCursor",
MouseCursor::Text => "IBeamCursor",
MouseCursor::VerticalText => "IBeamCursorForVerticalLayout",
MouseCursor::Copy => "dragCopyCursor",
MouseCursor::Alias => "dragLinkCursor",
MouseCursor::NotAllowed | MouseCursor::NoDrop => "operationNotAllowedCursor",
MouseCursor::ContextMenu => "contextualMenuCursor",
MouseCursor::Crosshair => "crosshairCursor",
MouseCursor::EResize => "resizeRightCursor",
MouseCursor::NResize => "resizeUpCursor",
MouseCursor::WResize => "resizeLeftCursor",
MouseCursor::SResize => "resizeDownCursor",
MouseCursor::EwResize | MouseCursor::ColResize => "resizeLeftRightCursor",
MouseCursor::NsResize | MouseCursor::RowResize => "resizeUpDownCursor",
/// TODO: Find appropriate OSX cursors
MouseCursor::NeResize | MouseCursor::NwResize |
MouseCursor::SeResize | MouseCursor::SwResize |
MouseCursor::NwseResize | MouseCursor::NeswResize |
MouseCursor::Cell | MouseCursor::NoneCursor |
MouseCursor::Wait | MouseCursor::Progress | MouseCursor::Help |
MouseCursor::Move | MouseCursor::AllScroll | MouseCursor::ZoomIn |
MouseCursor::ZoomOut => "arrowCursor",
};
let sel = Sel::register(cursor_name);
let cls = Class::get("NSCursor").unwrap();
unsafe {
use objc::Message;
let cursor: id = cls.send_message(sel, ()).unwrap();
let _: () = msg_send![cursor, set];
}
}
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
let cls = Class::get("NSCursor").unwrap();
// TODO: Check for errors.
match state {
CursorState::Normal => {
let _: () = unsafe { msg_send![cls, unhide] };
let _: i32 = unsafe { CGAssociateMouseAndMouseCursorPosition(true) };
Ok(())
},
CursorState::Hide => {
let _: () = unsafe { msg_send![cls, hide] };
Ok(())
},
CursorState::Grab => {
let _: i32 = unsafe { CGAssociateMouseAndMouseCursorPosition(false) };
Ok(())
}
}
}
#[inline]
pub fn hidpi_factor(&self) -> f32 {
unsafe {
NSWindow::backingScaleFactor(*self.window) as f32
}
}
#[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);
unsafe {
// TODO: Check for errors.
let _ = CGWarpMouseCursorPosition(appkit::CGPoint {
x: cursor_x as appkit::CGFloat,
y: cursor_y as appkit::CGFloat,
});
let _ = CGAssociateMouseAndMouseCursorPosition(true);
}
Ok(())
}
}
// Convert the `cocoa::base::id` associated with a window to a usize to use as a unique identifier
// for the window.
pub fn get_window_id(window_cocoa_id: cocoa::base::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)
{
window.setMinSize_(NSSize {
width: min_width,
height: min_height,
});
// 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;
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;
window.setFrame_display_(current_rect, 0)
}
}
unsafe fn nswindow_set_max_dimensions<V: NSWindow + Copy>(
window: V, max_width: f64, max_height: f64)
{
window.setMaxSize_(NSSize {
width: max_width,
height: max_height,
});
// 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;
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;
window.setFrame_display_(current_rect, 0)
}
}
pub struct IdRef(id);
impl IdRef {
fn new(i: id) -> IdRef {
IdRef(i)
}
#[allow(dead_code)]
fn retain(i: id) -> IdRef {
if i != nil {
let _: id = unsafe { msg_send![i, retain] };
}
IdRef(i)
}
fn non_nil(self) -> Option<IdRef> {
if self.0 == nil { None } else { Some(self) }
}
}
impl Drop for IdRef {
fn drop(&mut self) {
if self.0 != nil {
let _: () = unsafe { msg_send![self.0, release] };
}
}
}
impl Deref for IdRef {
type Target = id;
fn deref<'a>(&'a self) -> &'a id {
&self.0
}
}
impl Clone for IdRef {
fn clone(&self) -> IdRef {
if self.0 != nil {
let _: id = unsafe { msg_send![self.0, retain] };
}
IdRef(self.0)
}
}

View File

@@ -1,25 +1,22 @@
//! Contains traits with platform-specific methods in them.
//!
//! Contains the follow OS-specific modules:
//!
//! - `android`
//! - `ios`
//! - `macos`
//! - `unix`
//! - `windows`
//! - `web`
//!
//! And the following platform-specific module:
//!
//! - `run_return` (available on `windows`, `unix`, `macos`, and `android`)
//!
//! However only the module corresponding to the platform you're compiling to will be available.
pub use self::platform::*;
pub mod android;
pub mod ios;
pub mod macos;
pub mod unix;
pub mod windows;
#[cfg(target_os = "windows")]
#[path="windows/mod.rs"]
mod platform;
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
#[path="linux/mod.rs"]
mod platform;
#[cfg(target_os = "macos")]
#[path="macos/mod.rs"]
mod platform;
#[cfg(target_os = "android")]
#[path="android/mod.rs"]
mod platform;
#[cfg(target_os = "ios")]
#[path="ios/mod.rs"]
mod platform;
pub mod run_return;
pub mod web;
#[cfg(all(not(target_os = "ios"), not(target_os = "windows"), not(target_os = "linux"),
not(target_os = "macos"), not(target_os = "android"), not(target_os = "dragonfly"),
not(target_os = "freebsd"), not(target_os = "openbsd")))]
use this_platform_is_not_supported;

View File

@@ -1,58 +0,0 @@
#![cfg(any(
target_os = "windows",
target_os = "macos",
target_os = "android",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
use crate::{
event::Event,
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
};
/// Additional methods on `EventLoop` to return control flow to the caller.
pub trait EventLoopExtRunReturn {
/// A type provided by the user that can be passed through `Event::UserEvent`.
type UserEvent;
/// Initializes the `winit` event loop.
///
/// Unlike `run`, this function accepts non-`'static` (i.e. non-`move`) closures and returns
/// control flow to the caller when `control_flow` is set to `ControlFlow::Exit`.
///
/// # Caveats
/// Despite its appearance at first glance, this is *not* a perfect replacement for
/// `poll_events`. For example, this function will not return on Windows or macOS while a
/// window is getting resized, resulting in all application logic outside of the
/// `event_handler` closure not running until the resize operation ends. Other OS operations
/// may also result in such freezes. This behavior is caused by fundamental limitations in the
/// underlying OS APIs, which cannot be hidden by `winit` without severe stability repercussions.
///
/// You are strongly encouraged to use `run`, unless the use of this is absolutely necessary.
fn run_return<F>(&mut self, event_handler: F)
where
F: FnMut(
Event<'_, Self::UserEvent>,
&EventLoopWindowTarget<Self::UserEvent>,
&mut ControlFlow,
);
}
impl<T> EventLoopExtRunReturn for EventLoop<T> {
type UserEvent = T;
fn run_return<F>(&mut self, event_handler: F)
where
F: FnMut(
Event<'_, Self::UserEvent>,
&EventLoopWindowTarget<Self::UserEvent>,
&mut ControlFlow,
),
{
self.event_loop.run_return(event_handler)
}
}

View File

@@ -1,442 +0,0 @@
#![cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
use std::os::raw;
#[cfg(feature = "x11")]
use std::{ptr, sync::Arc};
use crate::{
event_loop::{EventLoop, EventLoopWindowTarget},
monitor::MonitorHandle,
window::{Window, WindowBuilder},
};
#[cfg(feature = "x11")]
use crate::dpi::Size;
#[cfg(feature = "x11")]
use crate::platform_impl::x11::{ffi::XVisualInfo, XConnection};
use crate::platform_impl::{
EventLoop as LinuxEventLoop, EventLoopWindowTarget as LinuxEventLoopWindowTarget,
Window as LinuxWindow,
};
// TODO: stupid hack so that glutin can do its work
#[doc(hidden)]
#[cfg(feature = "x11")]
pub use crate::platform_impl::x11;
#[cfg(feature = "x11")]
pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupported};
/// Additional methods on `EventLoopWindowTarget` that are specific to Unix.
pub trait EventLoopWindowTargetExtUnix {
/// True if the `EventLoopWindowTarget` uses Wayland.
#[cfg(feature = "wayland")]
fn is_wayland(&self) -> bool;
/// True if the `EventLoopWindowTarget` uses X11.
#[cfg(feature = "x11")]
fn is_x11(&self) -> bool;
#[doc(hidden)]
#[cfg(feature = "x11")]
fn xlib_xconnection(&self) -> Option<Arc<XConnection>>;
/// Returns a pointer to the `wl_display` object of wayland that is used by this
/// `EventLoopWindowTarget`.
///
/// Returns `None` if the `EventLoop` doesn't use wayland (if it uses xlib for example).
///
/// The pointer will become invalid when the winit `EventLoop` is destroyed.
#[cfg(feature = "wayland")]
fn wayland_display(&self) -> Option<*mut raw::c_void>;
}
impl<T> EventLoopWindowTargetExtUnix for EventLoopWindowTarget<T> {
#[inline]
#[cfg(feature = "wayland")]
fn is_wayland(&self) -> bool {
self.p.is_wayland()
}
#[inline]
#[cfg(feature = "x11")]
fn is_x11(&self) -> bool {
!self.p.is_wayland()
}
#[inline]
#[doc(hidden)]
#[cfg(feature = "x11")]
fn xlib_xconnection(&self) -> Option<Arc<XConnection>> {
match self.p {
LinuxEventLoopWindowTarget::X(ref e) => Some(e.x_connection().clone()),
#[cfg(feature = "wayland")]
_ => None,
}
}
#[inline]
#[cfg(feature = "wayland")]
fn wayland_display(&self) -> Option<*mut raw::c_void> {
match self.p {
LinuxEventLoopWindowTarget::Wayland(ref p) => {
Some(p.display().get_display_ptr() as *mut _)
}
#[cfg(feature = "x11")]
_ => None,
}
}
}
/// Additional methods on `EventLoop` that are specific to Unix.
pub trait EventLoopExtUnix {
/// Builds a new `EventLoop` that is forced to use X11.
///
/// # Panics
///
/// If called outside the main thread. To initialize an X11 event loop outside
/// the main thread, use [`new_x11_any_thread`](#tymethod.new_x11_any_thread).
#[cfg(feature = "x11")]
fn new_x11() -> Result<Self, XNotSupported>
where
Self: Sized;
/// Builds a new `EventLoop` that is forced to use Wayland.
///
/// # Panics
///
/// If called outside the main thread. To initialize a Wayland event loop outside
/// the main thread, use [`new_wayland_any_thread`](#tymethod.new_wayland_any_thread).
#[cfg(feature = "wayland")]
fn new_wayland() -> Self
where
Self: Sized;
/// Builds a new `EventLoop` on any thread.
///
/// This method bypasses the cross-platform compatibility requirement
/// that `EventLoop` be created on the main thread.
fn new_any_thread() -> Self
where
Self: Sized;
/// Builds a new X11 `EventLoop` on any thread.
///
/// This method bypasses the cross-platform compatibility requirement
/// that `EventLoop` be created on the main thread.
#[cfg(feature = "x11")]
fn new_x11_any_thread() -> Result<Self, XNotSupported>
where
Self: Sized;
/// Builds a new Wayland `EventLoop` on any thread.
///
/// This method bypasses the cross-platform compatibility requirement
/// that `EventLoop` be created on the main thread.
#[cfg(feature = "wayland")]
fn new_wayland_any_thread() -> Self
where
Self: Sized;
}
fn wrap_ev<T>(event_loop: LinuxEventLoop<T>) -> EventLoop<T> {
EventLoop {
event_loop,
_marker: std::marker::PhantomData,
}
}
impl<T> EventLoopExtUnix for EventLoop<T> {
#[inline]
fn new_any_thread() -> Self {
wrap_ev(LinuxEventLoop::new_any_thread())
}
#[inline]
#[cfg(feature = "x11")]
fn new_x11_any_thread() -> Result<Self, XNotSupported> {
LinuxEventLoop::new_x11_any_thread().map(wrap_ev)
}
#[inline]
#[cfg(feature = "wayland")]
fn new_wayland_any_thread() -> Self {
wrap_ev(
LinuxEventLoop::new_wayland_any_thread()
// TODO: propagate
.expect("failed to open Wayland connection"),
)
}
#[inline]
#[cfg(feature = "x11")]
fn new_x11() -> Result<Self, XNotSupported> {
LinuxEventLoop::new_x11().map(wrap_ev)
}
#[inline]
#[cfg(feature = "wayland")]
fn new_wayland() -> Self {
wrap_ev(
LinuxEventLoop::new_wayland()
// TODO: propagate
.expect("failed to open Wayland connection"),
)
}
}
/// Additional methods on `Window` that are specific to Unix.
pub trait WindowExtUnix {
/// Returns the ID of the `Window` xlib object that is used by this window.
///
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
#[cfg(feature = "x11")]
fn xlib_window(&self) -> Option<raw::c_ulong>;
/// Returns a pointer to the `Display` object of xlib that is used by this window.
///
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
///
/// The pointer will become invalid when the glutin `Window` is destroyed.
#[cfg(feature = "x11")]
fn xlib_display(&self) -> Option<*mut raw::c_void>;
#[cfg(feature = "x11")]
fn xlib_screen_id(&self) -> Option<raw::c_int>;
#[doc(hidden)]
#[cfg(feature = "x11")]
fn xlib_xconnection(&self) -> Option<Arc<XConnection>>;
/// This function returns the underlying `xcb_connection_t` of an xlib `Display`.
///
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
///
/// The pointer will become invalid when the glutin `Window` is destroyed.
#[cfg(feature = "x11")]
fn xcb_connection(&self) -> Option<*mut raw::c_void>;
/// Returns a pointer to the `wl_surface` object of wayland that is used by this window.
///
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
///
/// The pointer will become invalid when the glutin `Window` is destroyed.
#[cfg(feature = "wayland")]
fn wayland_surface(&self) -> Option<*mut raw::c_void>;
/// Returns a pointer to the `wl_display` object of wayland that is used by this window.
///
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
///
/// The pointer will become invalid when the glutin `Window` is destroyed.
#[cfg(feature = "wayland")]
fn wayland_display(&self) -> Option<*mut raw::c_void>;
/// Check if the window is ready for drawing
///
/// It is a remnant of a previous implementation detail for the
/// wayland backend, and is no longer relevant.
///
/// Always return true.
#[deprecated]
fn is_ready(&self) -> bool;
}
impl WindowExtUnix for Window {
#[inline]
#[cfg(feature = "x11")]
fn xlib_window(&self) -> Option<raw::c_ulong> {
match self.window {
LinuxWindow::X(ref w) => Some(w.xlib_window()),
#[cfg(feature = "wayland")]
_ => None,
}
}
#[inline]
#[cfg(feature = "x11")]
fn xlib_display(&self) -> Option<*mut raw::c_void> {
match self.window {
LinuxWindow::X(ref w) => Some(w.xlib_display()),
#[cfg(feature = "wayland")]
_ => None,
}
}
#[inline]
#[cfg(feature = "x11")]
fn xlib_screen_id(&self) -> Option<raw::c_int> {
match self.window {
LinuxWindow::X(ref w) => Some(w.xlib_screen_id()),
#[cfg(feature = "wayland")]
_ => None,
}
}
#[inline]
#[doc(hidden)]
#[cfg(feature = "x11")]
fn xlib_xconnection(&self) -> Option<Arc<XConnection>> {
match self.window {
LinuxWindow::X(ref w) => Some(w.xlib_xconnection()),
#[cfg(feature = "wayland")]
_ => None,
}
}
#[inline]
#[cfg(feature = "x11")]
fn xcb_connection(&self) -> Option<*mut raw::c_void> {
match self.window {
LinuxWindow::X(ref w) => Some(w.xcb_connection()),
#[cfg(feature = "wayland")]
_ => None,
}
}
#[inline]
#[cfg(feature = "wayland")]
fn wayland_surface(&self) -> Option<*mut raw::c_void> {
match self.window {
LinuxWindow::Wayland(ref w) => Some(w.surface().as_ref().c_ptr() as *mut _),
#[cfg(feature = "x11")]
_ => None,
}
}
#[inline]
#[cfg(feature = "wayland")]
fn wayland_display(&self) -> Option<*mut raw::c_void> {
match self.window {
LinuxWindow::Wayland(ref w) => Some(w.display().get_display_ptr() as *mut _),
#[cfg(feature = "x11")]
_ => None,
}
}
#[inline]
fn is_ready(&self) -> bool {
true
}
}
/// Additional methods on `WindowBuilder` that are specific to Unix.
pub trait WindowBuilderExtUnix {
#[cfg(feature = "x11")]
fn with_x11_visual<T>(self, visual_infos: *const T) -> Self;
#[cfg(feature = "x11")]
fn with_x11_screen(self, screen_id: i32) -> Self;
/// Build window with `WM_CLASS` hint; defaults to the name of the binary. Only relevant on X11.
#[cfg(feature = "x11")]
fn with_class(self, class: String, instance: String) -> Self;
/// Build window with override-redirect flag; defaults to false. Only relevant on X11.
#[cfg(feature = "x11")]
fn with_override_redirect(self, override_redirect: bool) -> Self;
/// Build window with `_NET_WM_WINDOW_TYPE` hints; defaults to `Normal`. Only relevant on X11.
#[cfg(feature = "x11")]
fn with_x11_window_type(self, x11_window_type: Vec<XWindowType>) -> Self;
/// Build window with `_GTK_THEME_VARIANT` hint set to the specified value. Currently only relevant on X11.
#[cfg(feature = "x11")]
fn with_gtk_theme_variant(self, variant: String) -> Self;
/// Build window with resize increment hint. Only implemented on X11.
#[cfg(feature = "x11")]
fn with_resize_increments<S: Into<Size>>(self, increments: S) -> Self;
/// Build window with base size hint. Only implemented on X11.
#[cfg(feature = "x11")]
fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self;
/// Build window with a given application ID. It should match the `.desktop` file distributed with
/// your program. Only relevant on Wayland.
///
/// For details about application ID conventions, see the
/// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
#[cfg(feature = "wayland")]
fn with_app_id(self, app_id: String) -> Self;
}
impl WindowBuilderExtUnix for WindowBuilder {
#[inline]
#[cfg(feature = "x11")]
fn with_x11_visual<T>(mut self, visual_infos: *const T) -> Self {
{
self.platform_specific.visual_infos =
Some(unsafe { ptr::read(visual_infos as *const XVisualInfo) });
}
self
}
#[inline]
#[cfg(feature = "x11")]
fn with_x11_screen(mut self, screen_id: i32) -> Self {
self.platform_specific.screen_id = Some(screen_id);
self
}
#[inline]
#[cfg(feature = "x11")]
fn with_class(mut self, instance: String, class: String) -> Self {
self.platform_specific.class = Some((instance, class));
self
}
#[inline]
#[cfg(feature = "x11")]
fn with_override_redirect(mut self, override_redirect: bool) -> Self {
self.platform_specific.override_redirect = override_redirect;
self
}
#[inline]
#[cfg(feature = "x11")]
fn with_x11_window_type(mut self, x11_window_types: Vec<XWindowType>) -> Self {
self.platform_specific.x11_window_types = x11_window_types;
self
}
#[inline]
#[cfg(feature = "x11")]
fn with_gtk_theme_variant(mut self, variant: String) -> Self {
self.platform_specific.gtk_theme_variant = Some(variant);
self
}
#[inline]
#[cfg(feature = "x11")]
fn with_resize_increments<S: Into<Size>>(mut self, increments: S) -> Self {
self.platform_specific.resize_increments = Some(increments.into());
self
}
#[inline]
#[cfg(feature = "x11")]
fn with_base_size<S: Into<Size>>(mut self, base_size: S) -> Self {
self.platform_specific.base_size = Some(base_size.into());
self
}
#[inline]
#[cfg(feature = "wayland")]
fn with_app_id(mut self, app_id: String) -> Self {
self.platform_specific.app_id = Some(app_id);
self
}
}
/// Additional methods on `MonitorHandle` that are specific to Linux.
pub trait MonitorHandleExtUnix {
/// Returns the inner identifier of the monitor.
fn native_id(&self) -> u32;
}
impl MonitorHandleExtUnix for MonitorHandle {
#[inline]
fn native_id(&self) -> u32 {
self.inner.native_identifier()
}
}

View File

@@ -1,29 +0,0 @@
#![cfg(target_arch = "wasm32")]
//! The web target does not automatically insert the canvas element object into the web page, to
//! allow end users to determine how the page should be laid out. Use the `WindowExtWebSys` trait
//! to retrieve the canvas from the Window. Alternatively, use the `WindowBuilderExtWebSys` trait
//! to provide your own canvas.
use crate::window::WindowBuilder;
use web_sys::HtmlCanvasElement;
pub trait WindowExtWebSys {
fn canvas(&self) -> HtmlCanvasElement;
/// Whether the browser reports the preferred color scheme to be "dark".
fn is_dark_mode(&self) -> bool;
}
pub trait WindowBuilderExtWebSys {
fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self;
}
impl WindowBuilderExtWebSys for WindowBuilder {
fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self {
self.platform_specific.canvas = canvas;
self
}
}

View File

@@ -1,293 +0,0 @@
#![cfg(target_os = "windows")]
use std::os::raw::c_void;
use std::path::Path;
use winapi::shared::minwindef::WORD;
use winapi::shared::windef::{HMENU, HWND};
use crate::{
dpi::PhysicalSize,
event::DeviceId,
event_loop::EventLoop,
monitor::MonitorHandle,
platform_impl::{EventLoop as WindowsEventLoop, Parent, WinIcon},
window::{BadIcon, Icon, Theme, Window, WindowBuilder},
};
/// Additional methods on `EventLoop` that are specific to Windows.
pub trait EventLoopExtWindows {
/// Creates an event loop off of the main thread.
///
/// # `Window` caveats
///
/// Note that any `Window` created on the new thread will be destroyed when the thread
/// terminates. Attempting to use a `Window` after its parent thread terminates has
/// unspecified, although explicitly not undefined, behavior.
fn new_any_thread() -> Self
where
Self: Sized;
/// By default, winit on Windows will attempt to enable process-wide DPI awareness. If that's
/// undesirable, you can create an `EventLoop` using this function instead.
fn new_dpi_unaware() -> Self
where
Self: Sized;
/// Creates a DPI-unaware event loop off of the main thread.
///
/// The `Window` caveats in [`new_any_thread`](EventLoopExtWindows::new_any_thread) also apply here.
fn new_dpi_unaware_any_thread() -> Self
where
Self: Sized;
}
impl<T> EventLoopExtWindows for EventLoop<T> {
#[inline]
fn new_any_thread() -> Self {
EventLoop {
event_loop: WindowsEventLoop::new_any_thread(),
_marker: ::std::marker::PhantomData,
}
}
#[inline]
fn new_dpi_unaware() -> Self {
EventLoop {
event_loop: WindowsEventLoop::new_dpi_unaware(),
_marker: ::std::marker::PhantomData,
}
}
#[inline]
fn new_dpi_unaware_any_thread() -> Self {
EventLoop {
event_loop: WindowsEventLoop::new_dpi_unaware_any_thread(),
_marker: ::std::marker::PhantomData,
}
}
}
/// Additional methods on `Window` that are specific to Windows.
pub trait WindowExtWindows {
/// Returns the HINSTANCE of the window
fn hinstance(&self) -> *mut c_void;
/// Returns the native handle that is used by this window.
///
/// The pointer will become invalid when the native window was destroyed.
fn hwnd(&self) -> *mut c_void;
/// Enables or disables mouse and keyboard input to the specified window.
///
/// A window must be enabled before it can be activated.
/// If an application has create a modal dialog box by disabling its owner window
/// (as described in [`WindowBuilderExtWindows::with_owner_window`]), the application must enable
/// the owner window before destroying the dialog box.
/// Otherwise, another window will receive the keyboard focus and be activated.
///
/// If a child window is disabled, it is ignored when the system tries to determine which
/// window should receive mouse messages.
///
/// For more information, see <https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enablewindow#remarks>
/// and <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#disabled-windows>
fn set_enable(&self, enabled: bool);
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>);
/// Returns the current window theme.
fn theme(&self) -> Theme;
}
impl WindowExtWindows for Window {
#[inline]
fn hinstance(&self) -> *mut c_void {
self.window.hinstance() as *mut _
}
#[inline]
fn hwnd(&self) -> *mut c_void {
self.window.hwnd() as *mut _
}
#[inline]
fn set_enable(&self, enabled: bool) {
self.window.set_enable(enabled)
}
#[inline]
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
self.window.set_taskbar_icon(taskbar_icon)
}
#[inline]
fn theme(&self) -> Theme {
self.window.theme()
}
}
/// Additional methods on `WindowBuilder` that are specific to Windows.
pub trait WindowBuilderExtWindows {
/// Sets a parent to the window to be created.
///
/// A child window has the WS_CHILD style and is confined to the client area of its parent window.
///
/// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#child-windows>
fn with_parent_window(self, parent: HWND) -> WindowBuilder;
/// Set an owner to the window to be created. Can be used to create a dialog box, for example.
/// Can be used in combination with [`WindowExtWindows::set_enable(false)`](WindowExtWindows::set_enable)
/// on the owner window to create a modal dialog box.
///
/// From MSDN:
/// - An owned window is always above its owner in the z-order.
/// - The system automatically destroys an owned window when its owner is destroyed.
/// - An owned window is hidden when its owner is minimized.
///
/// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows>
fn with_owner_window(self, parent: HWND) -> WindowBuilder;
/// Sets a menu on the window to be created.
///
/// Parent and menu are mutually exclusive; a child window cannot have a menu!
///
/// The menu must have been manually created beforehand with [`winapi::um::winuser::CreateMenu`] or similar.
///
/// Note: Dark mode cannot be supported for win32 menus, it's simply not possible to change how the menus look.
/// If you use this, it is recommended that you combine it with `with_theme(Some(Theme::Light))` to avoid a jarring effect.
fn with_menu(self, menu: HMENU) -> WindowBuilder;
/// 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;
/// Enables or disables drag and drop support (enabled by default). Will interfere with other crates
/// that use multi-threaded COM API (`CoInitializeEx` with `COINIT_MULTITHREADED` instead of
/// `COINIT_APARTMENTTHREADED`) on the same thread. Note that winit may still attempt to initialize
/// COM API regardless of this option. Currently only fullscreen mode does that, but there may be more in the future.
/// If you need COM API with `COINIT_MULTITHREADED` you must initialize it before calling any winit functions.
/// See <https://docs.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-coinitialize#remarks> for more information.
fn with_drag_and_drop(self, flag: bool) -> WindowBuilder;
/// Forces a theme or uses the system settings if `None` was provided.
fn with_theme(self, theme: Option<Theme>) -> WindowBuilder;
}
impl WindowBuilderExtWindows for WindowBuilder {
#[inline]
fn with_parent_window(mut self, parent: HWND) -> WindowBuilder {
self.platform_specific.parent = Parent::ChildOf(parent);
self
}
#[inline]
fn with_owner_window(mut self, parent: HWND) -> WindowBuilder {
self.platform_specific.parent = Parent::OwnedBy(parent);
self
}
#[inline]
fn with_menu(mut self, menu: HMENU) -> WindowBuilder {
self.platform_specific.menu = Some(menu);
self
}
#[inline]
fn with_taskbar_icon(mut self, taskbar_icon: Option<Icon>) -> 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
}
#[inline]
fn with_drag_and_drop(mut self, flag: bool) -> WindowBuilder {
self.platform_specific.drag_and_drop = flag;
self
}
#[inline]
fn with_theme(mut self, theme: Option<Theme>) -> WindowBuilder {
self.platform_specific.preferred_theme = theme;
self
}
}
/// Additional methods on `MonitorHandle` that are specific to Windows.
pub trait MonitorHandleExtWindows {
/// Returns the name of the monitor adapter specific to the Win32 API.
fn native_id(&self) -> String;
/// Returns the handle of the monitor - `HMONITOR`.
fn hmonitor(&self) -> *mut c_void;
}
impl MonitorHandleExtWindows for MonitorHandle {
#[inline]
fn native_id(&self) -> String {
self.inner.native_identifier()
}
#[inline]
fn hmonitor(&self) -> *mut c_void {
self.inner.hmonitor() as *mut _
}
}
/// Additional methods on `DeviceId` that are specific to Windows.
pub trait DeviceIdExtWindows {
/// Returns an identifier that persistently refers to this specific device.
///
/// Will return `None` if the device is no longer available.
fn persistent_identifier(&self) -> Option<String>;
}
impl DeviceIdExtWindows for DeviceId {
#[inline]
fn persistent_identifier(&self) -> Option<String> {
self.0.persistent_identifier()
}
}
/// Additional methods on `Icon` that are specific to Windows.
pub trait IconExtWindows: Sized {
/// Create an icon from a file path.
///
/// Specify `size` to load a specific icon size from the file, or `None` to load the default
/// icon size from the file.
///
/// In cases where the specified size does not exist in the file, Windows may perform scaling
/// to get an icon of the desired size.
fn from_path<P: AsRef<Path>>(path: P, size: Option<PhysicalSize<u32>>)
-> Result<Self, BadIcon>;
/// Create an icon from a resource embedded in this executable or library.
///
/// Specify `size` to load a specific icon size from the file, or `None` to load the default
/// icon size from the file.
///
/// In cases where the specified size does not exist in the file, Windows may perform scaling
/// to get an icon of the desired size.
fn from_resource(ordinal: WORD, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon>;
}
impl IconExtWindows for Icon {
fn from_path<P: AsRef<Path>>(
path: P,
size: Option<PhysicalSize<u32>>,
) -> Result<Self, BadIcon> {
let win_icon = WinIcon::from_path(path, size)?;
Ok(Icon { inner: win_icon })
}
fn from_resource(ordinal: WORD, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon> {
let win_icon = WinIcon::from_resource(ordinal, size)?;
Ok(Icon { inner: win_icon })
}
}

View File

@@ -0,0 +1,407 @@
use std::mem;
use std::ptr;
use std::cell::RefCell;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use CursorState;
use WindowEvent as Event;
use events::ModifiersState;
use super::event;
use super::WindowState;
use user32;
use shell32;
use winapi;
/// There's no parameters passed to the callback function, so it needs to get
/// its context (the HWND, the Sender for events, etc.) stashed in
/// a thread-local variable.
thread_local!(pub static CONTEXT_STASH: RefCell<Option<ThreadLocalData>> = RefCell::new(None));
pub struct ThreadLocalData {
pub win: winapi::HWND,
pub sender: Sender<Event>,
pub window_state: Arc<Mutex<WindowState>>,
pub mouse_in_window: bool
}
/// Equivalent to the windows api [MINMAXINFO](https://msdn.microsoft.com/en-us/library/windows/desktop/ms632605%28v=vs.85%29.aspx)
/// struct. Used because winapi-rs doesn't have this declared.
#[repr(C)]
#[allow(dead_code)]
struct MinMaxInfo {
reserved: winapi::POINT, // Do not use/change
max_size: winapi::POINT,
max_position: winapi::POINT,
min_track: winapi::POINT,
max_track: winapi::POINT
}
/// Checks that the window is the good one, and if so send the event to it.
fn send_event(input_window: winapi::HWND, event: Event) {
CONTEXT_STASH.with(|context_stash| {
let context_stash = context_stash.borrow();
let stored = match *context_stash {
None => return,
Some(ref v) => v
};
let &ThreadLocalData { ref win, ref sender, .. } = stored;
if win != &input_window {
return;
}
sender.send(event).ok(); // ignoring if closed
});
}
/// This is the callback that is called by `DispatchMessage` in the events loop.
///
/// Returning 0 tells the Win32 API that the message has been processed.
// FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary
pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
wparam: winapi::WPARAM, lparam: winapi::LPARAM)
-> winapi::LRESULT
{
match msg {
winapi::WM_DESTROY => {
use events::WindowEvent::Closed;
CONTEXT_STASH.with(|context_stash| {
let context_stash = context_stash.borrow();
let stored = match *context_stash {
None => return,
Some(ref v) => v
};
let &ThreadLocalData { ref win, .. } = stored;
if win == &window {
user32::PostQuitMessage(0);
}
});
send_event(window, Closed);
0
},
winapi::WM_ERASEBKGND => {
1
},
winapi::WM_SIZE => {
use events::WindowEvent::Resized;
let w = winapi::LOWORD(lparam as winapi::DWORD) as u32;
let h = winapi::HIWORD(lparam as winapi::DWORD) as u32;
send_event(window, Resized(w, h));
0
},
winapi::WM_MOVE => {
use events::WindowEvent::Moved;
let x = winapi::LOWORD(lparam as winapi::DWORD) as i32;
let y = winapi::HIWORD(lparam as winapi::DWORD) as i32;
send_event(window, Moved(x, y));
0
},
winapi::WM_CHAR => {
use std::mem;
use events::WindowEvent::ReceivedCharacter;
let chr: char = mem::transmute(wparam as u32);
send_event(window, ReceivedCharacter(chr));
0
},
// Prevents default windows menu hotkeys playing unwanted
// "ding" sounds. Alternatively could check for WM_SYSCOMMAND
// with wparam being SC_KEYMENU, but this may prevent some
// other unwanted default hotkeys as well.
winapi::WM_SYSCHAR => {
0
}
winapi::WM_MOUSEMOVE => {
use events::WindowEvent::{MouseEntered, MouseMoved};
let mouse_outside_window = CONTEXT_STASH.with(|context_stash| {
let mut context_stash = context_stash.borrow_mut();
if let Some(context_stash) = context_stash.as_mut() {
if !context_stash.mouse_in_window {
context_stash.mouse_in_window = true;
return true;
}
}
false
});
if mouse_outside_window {
send_event(window, MouseEntered);
// Calling TrackMouseEvent in order to receive mouse leave events.
user32::TrackMouseEvent(&mut winapi::TRACKMOUSEEVENT {
cbSize: mem::size_of::<winapi::TRACKMOUSEEVENT>() as winapi::DWORD,
dwFlags: winapi::TME_LEAVE,
hwndTrack: window,
dwHoverTime: winapi::HOVER_DEFAULT,
});
}
let x = winapi::GET_X_LPARAM(lparam) as i32;
let y = winapi::GET_Y_LPARAM(lparam) as i32;
send_event(window, MouseMoved(x, y));
0
},
winapi::WM_MOUSELEAVE => {
use events::WindowEvent::MouseLeft;
let mouse_in_window = CONTEXT_STASH.with(|context_stash| {
let mut context_stash = context_stash.borrow_mut();
if let Some(context_stash) = context_stash.as_mut() {
if context_stash.mouse_in_window {
context_stash.mouse_in_window = false;
return true;
}
}
false
});
if mouse_in_window {
send_event(window, MouseLeft);
}
0
},
winapi::WM_MOUSEWHEEL => {
use events::WindowEvent::MouseWheel;
use events::MouseScrollDelta::LineDelta;
use events::TouchPhase;
let value = (wparam >> 16) as i16;
let value = value as i32;
let value = value as f32 / winapi::WHEEL_DELTA as f32;
send_event(window, MouseWheel(LineDelta(0.0, value), TouchPhase::Moved));
0
},
winapi::WM_KEYDOWN | winapi::WM_SYSKEYDOWN => {
use events::WindowEvent::KeyboardInput;
use events::ElementState::Pressed;
if msg == winapi::WM_SYSKEYDOWN && wparam as i32 == winapi::VK_F4 {
user32::DefWindowProcW(window, msg, wparam, lparam)
} else {
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
send_event(window, KeyboardInput(Pressed, scancode, vkey, event::get_key_mods()));
0
}
},
winapi::WM_KEYUP | winapi::WM_SYSKEYUP => {
use events::WindowEvent::KeyboardInput;
use events::ElementState::Released;
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
send_event(window, KeyboardInput(Released, scancode, vkey, event::get_key_mods()));
0
},
winapi::WM_LBUTTONDOWN => {
use events::WindowEvent::MouseInput;
use events::MouseButton::Left;
use events::ElementState::Pressed;
send_event(window, MouseInput(Pressed, Left));
0
},
winapi::WM_LBUTTONUP => {
use events::WindowEvent::MouseInput;
use events::MouseButton::Left;
use events::ElementState::Released;
send_event(window, MouseInput(Released, Left));
0
},
winapi::WM_RBUTTONDOWN => {
use events::WindowEvent::MouseInput;
use events::MouseButton::Right;
use events::ElementState::Pressed;
send_event(window, MouseInput(Pressed, Right));
0
},
winapi::WM_RBUTTONUP => {
use events::WindowEvent::MouseInput;
use events::MouseButton::Right;
use events::ElementState::Released;
send_event(window, MouseInput(Released, Right));
0
},
winapi::WM_MBUTTONDOWN => {
use events::WindowEvent::MouseInput;
use events::MouseButton::Middle;
use events::ElementState::Pressed;
send_event(window, MouseInput(Pressed, Middle));
0
},
winapi::WM_MBUTTONUP => {
use events::WindowEvent::MouseInput;
use events::MouseButton::Middle;
use events::ElementState::Released;
send_event(window, MouseInput(Released, Middle));
0
},
winapi::WM_XBUTTONDOWN => {
use events::WindowEvent::MouseInput;
use events::MouseButton::Other;
use events::ElementState::Pressed;
let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int; // waiting on PR for winapi to add GET_XBUTTON_WPARAM
send_event(window, MouseInput(Pressed, Other(xbutton as u8)));
0
},
winapi::WM_XBUTTONUP => {
use events::WindowEvent::MouseInput;
use events::MouseButton::Other;
use events::ElementState::Released;
let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int;
send_event(window, MouseInput(Released, Other(xbutton as u8)));
0
},
winapi::WM_INPUT => {
let mut data: winapi::RAWINPUT = mem::uninitialized();
let mut data_size = mem::size_of::<winapi::RAWINPUT>() as winapi::UINT;
user32::GetRawInputData(mem::transmute(lparam), winapi::RID_INPUT,
mem::transmute(&mut data), &mut data_size,
mem::size_of::<winapi::RAWINPUTHEADER>() as winapi::UINT);
if data.header.dwType == winapi::RIM_TYPEMOUSE {
let _x = data.mouse.lLastX; // FIXME: this is not always the relative movement
let _y = data.mouse.lLastY;
// TODO:
//send_event(window, Event::MouseRawMovement { x: x, y: y });
0
} else {
user32::DefWindowProcW(window, msg, wparam, lparam)
}
},
winapi::WM_SETFOCUS => {
use events::WindowEvent::Focused;
send_event(window, Focused(true));
0
},
winapi::WM_KILLFOCUS => {
use events::WindowEvent::Focused;
send_event(window, Focused(false));
0
},
winapi::WM_SETCURSOR => {
let call_def_window_proc = CONTEXT_STASH.with(|context_stash| {
let cstash = context_stash.borrow();
let mut call_def_window_proc = false;
if let Some(cstash) = cstash.as_ref() {
if let Ok(window_state) = cstash.window_state.lock() {
if cstash.mouse_in_window {
match window_state.cursor_state {
CursorState::Normal => {
user32::SetCursor(user32::LoadCursorW(
ptr::null_mut(),
window_state.cursor));
},
CursorState::Grab | CursorState::Hide => {
user32::SetCursor(ptr::null_mut());
}
}
} else {
call_def_window_proc = true;
}
}
}
call_def_window_proc
});
if call_def_window_proc {
user32::DefWindowProcW(window, msg, wparam, lparam)
} else {
0
}
},
winapi::WM_DROPFILES => {
use events::WindowEvent::DroppedFile;
let hdrop = wparam as winapi::HDROP;
let mut pathbuf: [u16; winapi::MAX_PATH] = mem::uninitialized();
let num_drops = shell32::DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0);
for i in 0..num_drops {
let nch = shell32::DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(),
winapi::MAX_PATH as u32) as usize;
if nch > 0 {
send_event(window, DroppedFile(OsString::from_wide(&pathbuf[0..nch]).into()));
}
}
shell32::DragFinish(hdrop);
0
},
winapi::WM_GETMINMAXINFO => {
let mmi = lparam as *mut MinMaxInfo;
//(*mmi).max_position = winapi::POINT { x: -8, y: -8 }; // The upper left corner of the window if it were maximized on the primary monitor.
//(*mmi).max_size = winapi::POINT { x: .., y: .. }; // The dimensions of the primary monitor.
CONTEXT_STASH.with(|context_stash| {
match context_stash.borrow().as_ref() {
Some(cstash) => {
let window_state = cstash.window_state.lock().unwrap();
match window_state.attributes.min_dimensions {
Some((width, height)) => {
(*mmi).min_track = winapi::POINT { x: width as i32, y: height as i32 };
},
None => { }
}
match window_state.attributes.max_dimensions {
Some((width, height)) => {
(*mmi).max_track = winapi::POINT { x: width as i32, y: height as i32 };
},
None => { }
}
},
None => { }
}
});
0
},
x if x == *super::WAKEUP_MSG_ID => {
use events::WindowEvent::Awakened;
send_event(window, Awakened);
0
},
_ => {
user32::DefWindowProcW(window, msg, wparam, lparam)
}
}
}

View File

@@ -0,0 +1,210 @@
use events::VirtualKeyCode;
use events::ModifiersState;
use winapi;
use user32;
use ScanCode;
const MAPVK_VSC_TO_VK_EX: u32 = 3;
pub fn get_key_mods() -> ModifiersState {
let mut mods = ModifiersState::default();
unsafe {
if user32::GetKeyState(winapi::VK_SHIFT) & (1 << 15) == (1 << 15) {
mods.shift = true;
}
if user32::GetKeyState(winapi::VK_CONTROL) & (1 << 15) == (1 << 15) {
mods.ctrl = true;
}
if user32::GetKeyState(winapi::VK_MENU) & (1 << 15) == (1 << 15) {
mods.alt = true;
}
if (user32::GetKeyState(winapi::VK_LWIN) | user32::GetKeyState(winapi::VK_RWIN)) & (1 << 15) == (1 << 15) {
mods.logo = true;
}
}
mods
}
pub fn vkeycode_to_element(wparam: winapi::WPARAM, lparam: winapi::LPARAM) -> (ScanCode, Option<VirtualKeyCode>) {
let scancode = ((lparam >> 16) & 0xff) as u8;
let extended = (lparam & 0x01000000) != 0;
let vk = match wparam as i32 {
winapi::VK_SHIFT => unsafe { user32::MapVirtualKeyA(scancode as u32, MAPVK_VSC_TO_VK_EX) as i32 },
winapi::VK_CONTROL => if extended { winapi::VK_RCONTROL } else { winapi::VK_LCONTROL },
winapi::VK_MENU => if extended { winapi::VK_RMENU } else { winapi::VK_LMENU },
other => other
};
(scancode, match vk {
//winapi::VK_LBUTTON => Some(VirtualKeyCode::Lbutton),
//winapi::VK_RBUTTON => Some(VirtualKeyCode::Rbutton),
//winapi::VK_CANCEL => Some(VirtualKeyCode::Cancel),
//winapi::VK_MBUTTON => Some(VirtualKeyCode::Mbutton),
//winapi::VK_XBUTTON1 => Some(VirtualKeyCode::Xbutton1),
//winapi::VK_XBUTTON2 => Some(VirtualKeyCode::Xbutton2),
winapi::VK_BACK => Some(VirtualKeyCode::Back),
winapi::VK_TAB => Some(VirtualKeyCode::Tab),
//winapi::VK_CLEAR => Some(VirtualKeyCode::Clear),
winapi::VK_RETURN => Some(VirtualKeyCode::Return),
winapi::VK_LSHIFT => Some(VirtualKeyCode::LShift),
winapi::VK_RSHIFT => Some(VirtualKeyCode::RShift),
winapi::VK_LCONTROL => Some(VirtualKeyCode::LControl),
winapi::VK_RCONTROL => Some(VirtualKeyCode::RControl),
winapi::VK_LMENU => Some(VirtualKeyCode::LMenu),
winapi::VK_RMENU => Some(VirtualKeyCode::RMenu),
winapi::VK_PAUSE => Some(VirtualKeyCode::Pause),
winapi::VK_CAPITAL => Some(VirtualKeyCode::Capital),
winapi::VK_KANA => Some(VirtualKeyCode::Kana),
//winapi::VK_HANGUEL => Some(VirtualKeyCode::Hanguel),
//winapi::VK_HANGUL => Some(VirtualKeyCode::Hangul),
//winapi::VK_JUNJA => Some(VirtualKeyCode::Junja),
//winapi::VK_FINAL => Some(VirtualKeyCode::Final),
//winapi::VK_HANJA => Some(VirtualKeyCode::Hanja),
winapi::VK_KANJI => Some(VirtualKeyCode::Kanji),
winapi::VK_ESCAPE => Some(VirtualKeyCode::Escape),
winapi::VK_CONVERT => Some(VirtualKeyCode::Convert),
winapi::VK_NONCONVERT => Some(VirtualKeyCode::NoConvert),
//winapi::VK_ACCEPT => Some(VirtualKeyCode::Accept),
//winapi::VK_MODECHANGE => Some(VirtualKeyCode::Modechange),
winapi::VK_SPACE => Some(VirtualKeyCode::Space),
winapi::VK_PRIOR => Some(VirtualKeyCode::PageUp),
winapi::VK_NEXT => Some(VirtualKeyCode::PageDown),
winapi::VK_END => Some(VirtualKeyCode::End),
winapi::VK_HOME => Some(VirtualKeyCode::Home),
winapi::VK_LEFT => Some(VirtualKeyCode::Left),
winapi::VK_UP => Some(VirtualKeyCode::Up),
winapi::VK_RIGHT => Some(VirtualKeyCode::Right),
winapi::VK_DOWN => Some(VirtualKeyCode::Down),
//winapi::VK_SELECT => Some(VirtualKeyCode::Select),
//winapi::VK_PRINT => Some(VirtualKeyCode::Print),
//winapi::VK_EXECUTE => Some(VirtualKeyCode::Execute),
winapi::VK_SNAPSHOT => Some(VirtualKeyCode::Snapshot),
winapi::VK_INSERT => Some(VirtualKeyCode::Insert),
winapi::VK_DELETE => Some(VirtualKeyCode::Delete),
//winapi::VK_HELP => Some(VirtualKeyCode::Help),
0x30 => Some(VirtualKeyCode::Key0),
0x31 => Some(VirtualKeyCode::Key1),
0x32 => Some(VirtualKeyCode::Key2),
0x33 => Some(VirtualKeyCode::Key3),
0x34 => Some(VirtualKeyCode::Key4),
0x35 => Some(VirtualKeyCode::Key5),
0x36 => Some(VirtualKeyCode::Key6),
0x37 => Some(VirtualKeyCode::Key7),
0x38 => Some(VirtualKeyCode::Key8),
0x39 => Some(VirtualKeyCode::Key9),
0x41 => Some(VirtualKeyCode::A),
0x42 => Some(VirtualKeyCode::B),
0x43 => Some(VirtualKeyCode::C),
0x44 => Some(VirtualKeyCode::D),
0x45 => Some(VirtualKeyCode::E),
0x46 => Some(VirtualKeyCode::F),
0x47 => Some(VirtualKeyCode::G),
0x48 => Some(VirtualKeyCode::H),
0x49 => Some(VirtualKeyCode::I),
0x4A => Some(VirtualKeyCode::J),
0x4B => Some(VirtualKeyCode::K),
0x4C => Some(VirtualKeyCode::L),
0x4D => Some(VirtualKeyCode::M),
0x4E => Some(VirtualKeyCode::N),
0x4F => Some(VirtualKeyCode::O),
0x50 => Some(VirtualKeyCode::P),
0x51 => Some(VirtualKeyCode::Q),
0x52 => Some(VirtualKeyCode::R),
0x53 => Some(VirtualKeyCode::S),
0x54 => Some(VirtualKeyCode::T),
0x55 => Some(VirtualKeyCode::U),
0x56 => Some(VirtualKeyCode::V),
0x57 => Some(VirtualKeyCode::W),
0x58 => Some(VirtualKeyCode::X),
0x59 => Some(VirtualKeyCode::Y),
0x5A => Some(VirtualKeyCode::Z),
//winapi::VK_LWIN => Some(VirtualKeyCode::Lwin),
//winapi::VK_RWIN => Some(VirtualKeyCode::Rwin),
winapi::VK_APPS => Some(VirtualKeyCode::Apps),
winapi::VK_SLEEP => Some(VirtualKeyCode::Sleep),
winapi::VK_NUMPAD0 => Some(VirtualKeyCode::Numpad0),
winapi::VK_NUMPAD1 => Some(VirtualKeyCode::Numpad1),
winapi::VK_NUMPAD2 => Some(VirtualKeyCode::Numpad2),
winapi::VK_NUMPAD3 => Some(VirtualKeyCode::Numpad3),
winapi::VK_NUMPAD4 => Some(VirtualKeyCode::Numpad4),
winapi::VK_NUMPAD5 => Some(VirtualKeyCode::Numpad5),
winapi::VK_NUMPAD6 => Some(VirtualKeyCode::Numpad6),
winapi::VK_NUMPAD7 => Some(VirtualKeyCode::Numpad7),
winapi::VK_NUMPAD8 => Some(VirtualKeyCode::Numpad8),
winapi::VK_NUMPAD9 => Some(VirtualKeyCode::Numpad9),
winapi::VK_MULTIPLY => Some(VirtualKeyCode::Multiply),
winapi::VK_ADD => Some(VirtualKeyCode::Add),
//winapi::VK_SEPARATOR => Some(VirtualKeyCode::Separator),
winapi::VK_SUBTRACT => Some(VirtualKeyCode::Subtract),
winapi::VK_DECIMAL => Some(VirtualKeyCode::Decimal),
winapi::VK_DIVIDE => Some(VirtualKeyCode::Divide),
winapi::VK_F1 => Some(VirtualKeyCode::F1),
winapi::VK_F2 => Some(VirtualKeyCode::F2),
winapi::VK_F3 => Some(VirtualKeyCode::F3),
winapi::VK_F4 => Some(VirtualKeyCode::F4),
winapi::VK_F5 => Some(VirtualKeyCode::F5),
winapi::VK_F6 => Some(VirtualKeyCode::F6),
winapi::VK_F7 => Some(VirtualKeyCode::F7),
winapi::VK_F8 => Some(VirtualKeyCode::F8),
winapi::VK_F9 => Some(VirtualKeyCode::F9),
winapi::VK_F10 => Some(VirtualKeyCode::F10),
winapi::VK_F11 => Some(VirtualKeyCode::F11),
winapi::VK_F12 => Some(VirtualKeyCode::F12),
winapi::VK_F13 => Some(VirtualKeyCode::F13),
winapi::VK_F14 => Some(VirtualKeyCode::F14),
winapi::VK_F15 => Some(VirtualKeyCode::F15),
/*winapi::VK_F16 => Some(VirtualKeyCode::F16),
winapi::VK_F17 => Some(VirtualKeyCode::F17),
winapi::VK_F18 => Some(VirtualKeyCode::F18),
winapi::VK_F19 => Some(VirtualKeyCode::F19),
winapi::VK_F20 => Some(VirtualKeyCode::F20),
winapi::VK_F21 => Some(VirtualKeyCode::F21),
winapi::VK_F22 => Some(VirtualKeyCode::F22),
winapi::VK_F23 => Some(VirtualKeyCode::F23),
winapi::VK_F24 => Some(VirtualKeyCode::F24),*/
winapi::VK_NUMLOCK => Some(VirtualKeyCode::Numlock),
winapi::VK_SCROLL => Some(VirtualKeyCode::Scroll),
winapi::VK_BROWSER_BACK => Some(VirtualKeyCode::NavigateBackward),
winapi::VK_BROWSER_FORWARD => Some(VirtualKeyCode::NavigateForward),
winapi::VK_BROWSER_REFRESH => Some(VirtualKeyCode::WebRefresh),
winapi::VK_BROWSER_STOP => Some(VirtualKeyCode::WebStop),
winapi::VK_BROWSER_SEARCH => Some(VirtualKeyCode::WebSearch),
winapi::VK_BROWSER_FAVORITES => Some(VirtualKeyCode::WebFavorites),
winapi::VK_BROWSER_HOME => Some(VirtualKeyCode::WebHome),
winapi::VK_VOLUME_MUTE => Some(VirtualKeyCode::Mute),
winapi::VK_VOLUME_DOWN => Some(VirtualKeyCode::VolumeDown),
winapi::VK_VOLUME_UP => Some(VirtualKeyCode::VolumeUp),
winapi::VK_MEDIA_NEXT_TRACK => Some(VirtualKeyCode::NextTrack),
winapi::VK_MEDIA_PREV_TRACK => Some(VirtualKeyCode::PrevTrack),
winapi::VK_MEDIA_STOP => Some(VirtualKeyCode::MediaStop),
winapi::VK_MEDIA_PLAY_PAUSE => Some(VirtualKeyCode::PlayPause),
winapi::VK_LAUNCH_MAIL => Some(VirtualKeyCode::Mail),
winapi::VK_LAUNCH_MEDIA_SELECT => Some(VirtualKeyCode::MediaSelect),
/*winapi::VK_LAUNCH_APP1 => Some(VirtualKeyCode::Launch_app1),
winapi::VK_LAUNCH_APP2 => Some(VirtualKeyCode::Launch_app2),*/
winapi::VK_OEM_PLUS => Some(VirtualKeyCode::Equals),
winapi::VK_OEM_COMMA => Some(VirtualKeyCode::Comma),
winapi::VK_OEM_MINUS => Some(VirtualKeyCode::Minus),
winapi::VK_OEM_PERIOD => Some(VirtualKeyCode::Period),
/*winapi::VK_OEM_1 => Some(VirtualKeyCode::Oem_1),
winapi::VK_OEM_2 => Some(VirtualKeyCode::Oem_2),
winapi::VK_OEM_3 => Some(VirtualKeyCode::Oem_3),
winapi::VK_OEM_4 => Some(VirtualKeyCode::Oem_4),
winapi::VK_OEM_5 => Some(VirtualKeyCode::Oem_5),
winapi::VK_OEM_6 => Some(VirtualKeyCode::Oem_6),
winapi::VK_OEM_7 => Some(VirtualKeyCode::Oem_7),
winapi::VK_OEM_8 => Some(VirtualKeyCode::Oem_8), */
winapi::VK_OEM_102 => Some(VirtualKeyCode::OEM102),
/*winapi::VK_PROCESSKEY => Some(VirtualKeyCode::Processkey),
winapi::VK_PACKET => Some(VirtualKeyCode::Packet),
winapi::VK_ATTN => Some(VirtualKeyCode::Attn),
winapi::VK_CRSEL => Some(VirtualKeyCode::Crsel),
winapi::VK_EXSEL => Some(VirtualKeyCode::Exsel),
winapi::VK_EREOF => Some(VirtualKeyCode::Ereof),
winapi::VK_PLAY => Some(VirtualKeyCode::Play),
winapi::VK_ZOOM => Some(VirtualKeyCode::Zoom),
winapi::VK_NONAME => Some(VirtualKeyCode::Noname),
winapi::VK_PA1 => Some(VirtualKeyCode::Pa1),
winapi::VK_OEM_CLEAR => Some(VirtualKeyCode::Oem_clear),*/
_ => None
})
}

View File

@@ -0,0 +1,259 @@
use std::sync::{Arc, Mutex};
use std::io;
use std::ptr;
use std::mem;
use std::thread;
use super::callback;
use super::WindowState;
use super::Window;
use super::MonitorId;
use super::WindowWrapper;
use super::PlatformSpecificWindowBuilderAttributes;
use CreationError;
use CreationError::OsError;
use CursorState;
use WindowAttributes;
use std::ffi::{OsStr};
use std::os::windows::ffi::OsStrExt;
use std::sync::mpsc::channel;
use winapi;
use kernel32;
use dwmapi;
use user32;
pub fn new_window(window: &WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result<Window, CreationError> {
let window = window.clone();
let attribs = pl_attribs.clone();
// initializing variables to be sent to the task
let title = OsStr::new(&window.title).encode_wide().chain(Some(0).into_iter())
.collect::<Vec<_>>();
let (tx, rx) = channel();
// `GetMessage` must be called in the same thread as CreateWindow, so we create a new thread
// dedicated to this window.
thread::spawn(move || {
unsafe {
// creating and sending the `Window`
match init(title, &window, attribs) {
Ok(w) => tx.send(Ok(w)).ok(),
Err(e) => {
tx.send(Err(e)).ok();
return;
}
};
// now that the `Window` struct is initialized, the main `Window::new()` function will
// return and this events loop will run in parallel
loop {
let mut msg = mem::uninitialized();
if user32::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 {
break;
}
user32::TranslateMessage(&msg);
user32::DispatchMessageW(&msg); // calls `callback` (see the callback module)
}
}
});
rx.recv().unwrap()
}
unsafe fn init(title: Vec<u16>, window: &WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes) -> Result<Window, CreationError> {
// registering the window class
let class_name = register_window_class();
// building a RECT object with coordinates
let mut rect = winapi::RECT {
left: 0, right: window.dimensions.unwrap_or((1024, 768)).0 as winapi::LONG,
top: 0, bottom: window.dimensions.unwrap_or((1024, 768)).1 as winapi::LONG,
};
// switching to fullscreen if necessary
// this means adjusting the window's position so that it overlaps the right monitor,
// and change the monitor's resolution if necessary
if window.monitor.is_some() {
let monitor = window.monitor.as_ref().unwrap();
try!(switch_to_fullscreen(&mut rect, monitor));
}
// computing the style and extended style of the window
let (ex_style, style) = if window.monitor.is_some() || !window.decorations {
(winapi::WS_EX_APPWINDOW,
//winapi::WS_POPUP is incompatible with winapi::WS_CHILD
if pl_attribs.parent.is_some() {
winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN
}
else {
winapi::WS_POPUP | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN
}
)
} else {
(winapi::WS_EX_APPWINDOW | winapi::WS_EX_WINDOWEDGE,
winapi::WS_OVERLAPPEDWINDOW | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN)
};
// adjusting the window coordinates using the style
user32::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
// creating the real window this time, by using the functions in `extra_functions`
let real_window = {
let (width, height) = if window.monitor.is_some() || window.dimensions.is_some() {
(Some(rect.right - rect.left), Some(rect.bottom - rect.top))
} else {
(None, None)
};
let (x, y) = if window.monitor.is_some() {
(Some(rect.left), Some(rect.top))
} else {
(None, None)
};
let mut style = if !window.visible {
style
} else {
style | winapi::WS_VISIBLE
};
if pl_attribs.parent.is_some() {
style |= winapi::WS_CHILD;
}
let handle = user32::CreateWindowExW(ex_style | winapi::WS_EX_ACCEPTFILES,
class_name.as_ptr(),
title.as_ptr() as winapi::LPCWSTR,
style | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN,
x.unwrap_or(winapi::CW_USEDEFAULT), y.unwrap_or(winapi::CW_USEDEFAULT),
width.unwrap_or(winapi::CW_USEDEFAULT), height.unwrap_or(winapi::CW_USEDEFAULT),
pl_attribs.parent.unwrap_or(ptr::null_mut()),
ptr::null_mut(), kernel32::GetModuleHandleW(ptr::null()),
ptr::null_mut());
if handle.is_null() {
return Err(OsError(format!("CreateWindowEx function failed: {}",
format!("{}", io::Error::last_os_error()))));
}
let hdc = user32::GetDC(handle);
if hdc.is_null() {
return Err(OsError(format!("GetDC function failed: {}",
format!("{}", io::Error::last_os_error()))));
}
WindowWrapper(handle, hdc)
};
// making the window transparent
if window.transparent {
let bb = winapi::DWM_BLURBEHIND {
dwFlags: 0x1, // FIXME: DWM_BB_ENABLE;
fEnable: 1,
hRgnBlur: ptr::null_mut(),
fTransitionOnMaximized: 0,
};
dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb);
}
// calling SetForegroundWindow if fullscreen
if window.monitor.is_some() {
user32::SetForegroundWindow(real_window.0);
}
// Creating a mutex to track the current window state
let window_state = Arc::new(Mutex::new(WindowState {
cursor: winapi::IDC_ARROW, // use arrow by default
cursor_state: CursorState::Normal,
attributes: window.clone()
}));
// filling the CONTEXT_STASH task-local storage so that we can start receiving events
let events_receiver = {
let (tx, rx) = channel();
let mut tx = Some(tx);
callback::CONTEXT_STASH.with(|context_stash| {
let data = callback::ThreadLocalData {
win: real_window.0,
sender: tx.take().unwrap(),
window_state: window_state.clone(),
mouse_in_window: false
};
(*context_stash.borrow_mut()) = Some(data);
});
rx
};
// building the struct
Ok(Window {
window: real_window,
events_receiver: events_receiver,
window_state: window_state,
})
}
unsafe fn register_window_class() -> Vec<u16> {
let class_name = OsStr::new("Window Class").encode_wide().chain(Some(0).into_iter())
.collect::<Vec<_>>();
let class = winapi::WNDCLASSEXW {
cbSize: mem::size_of::<winapi::WNDCLASSEXW>() as winapi::UINT,
style: winapi::CS_HREDRAW | winapi::CS_VREDRAW | winapi::CS_OWNDC,
lpfnWndProc: Some(callback::callback),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: kernel32::GetModuleHandleW(ptr::null()),
hIcon: ptr::null_mut(),
hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly
hbrBackground: ptr::null_mut(),
lpszMenuName: ptr::null(),
lpszClassName: class_name.as_ptr(),
hIconSm: ptr::null_mut(),
};
// We ignore errors because registering the same window class twice would trigger
// an error, and because errors here are detected during CreateWindowEx anyway.
// Also since there is no weird element in the struct, there is no reason for this
// call to fail.
user32::RegisterClassExW(&class);
class_name
}
unsafe fn switch_to_fullscreen(rect: &mut winapi::RECT, monitor: &MonitorId)
-> Result<(), CreationError>
{
// adjusting the rect
{
let pos = monitor.get_position();
rect.left += pos.0 as winapi::LONG;
rect.right += pos.0 as winapi::LONG;
rect.top += pos.1 as winapi::LONG;
rect.bottom += pos.1 as winapi::LONG;
}
// changing device settings
let mut screen_settings: winapi::DEVMODEW = mem::zeroed();
screen_settings.dmSize = mem::size_of::<winapi::DEVMODEW>() as winapi::WORD;
screen_settings.dmPelsWidth = (rect.right - rect.left) as winapi::DWORD;
screen_settings.dmPelsHeight = (rect.bottom - rect.top) as winapi::DWORD;
screen_settings.dmBitsPerPel = 32; // TODO: ?
screen_settings.dmFields = winapi::DM_BITSPERPEL | winapi::DM_PELSWIDTH | winapi::DM_PELSHEIGHT;
let result = user32::ChangeDisplaySettingsExW(monitor.get_adapter_name().as_ptr(),
&mut screen_settings, ptr::null_mut(),
winapi::CDS_FULLSCREEN, ptr::null_mut());
if result != winapi::DISP_CHANGE_SUCCESSFUL {
return Err(OsError(format!("ChangeDisplaySettings failed: {}", result)));
}
Ok(())
}

385
src/platform/windows/mod.rs Normal file
View File

@@ -0,0 +1,385 @@
#![cfg(target_os = "windows")]
use std::mem;
use std::ptr;
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;
use std::os::raw::c_int;
use std::sync::{
Arc,
Mutex
};
use std::sync::mpsc::Receiver;
use {CreationError, WindowEvent as Event, MouseCursor};
use CursorState;
use WindowAttributes;
gen_api_transition!();
#[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes {
pub parent: Option<winapi::HWND>,
}
unsafe impl Send for PlatformSpecificWindowBuilderAttributes {}
unsafe impl Sync for PlatformSpecificWindowBuilderAttributes {}
#[derive(Clone, Default)]
pub struct PlatformSpecificHeadlessBuilderAttributes;
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
use winapi;
use user32;
use kernel32;
mod callback;
mod event;
mod init;
mod monitor;
lazy_static! {
static ref WAKEUP_MSG_ID: u32 = unsafe { user32::RegisterWindowMessageA("Glutin::EventID".as_ptr() as *const i8) };
}
/// Cursor
pub type Cursor = *const winapi::wchar_t;
/// Contains information about states and the window for the callback.
#[derive(Clone)]
pub struct WindowState {
pub cursor: Cursor,
pub cursor_state: CursorState,
pub attributes: WindowAttributes
}
/// The Win32 implementation of the main `Window` object.
pub struct Window {
/// Main handle for the window.
window: WindowWrapper,
/// Receiver for the events dispatched by the window callback.
events_receiver: Receiver<Event>,
/// The current window state.
window_state: Arc<Mutex<WindowState>>,
}
unsafe impl Send for Window {}
unsafe impl Sync for Window {}
/// A simple wrapper that destroys the window when it is destroyed.
#[doc(hidden)]
pub struct WindowWrapper(winapi::HWND, winapi::HDC);
impl Drop for WindowWrapper {
#[inline]
fn drop(&mut self) {
unsafe {
user32::DestroyWindow(self.0);
}
}
}
#[derive(Clone)]
pub struct WindowProxy {
hwnd: winapi::HWND,
}
unsafe impl Send for WindowProxy {}
unsafe impl Sync for WindowProxy {}
impl WindowProxy {
#[inline]
pub fn wakeup_event_loop(&self) {
unsafe {
user32::PostMessageA(self.hwnd, *WAKEUP_MSG_ID, 0, 0);
}
}
}
impl Window {
/// See the docs in the crate root file.
pub fn new(window: &WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes)
-> Result<Window, CreationError>
{
init::new_window(window, pl_attribs)
}
/// See the docs in the crate root file.
///
/// Calls SetWindowText on the HWND.
pub fn set_title(&self, text: &str) {
let text = OsStr::new(text).encode_wide().chain(Some(0).into_iter())
.collect::<Vec<_>>();
unsafe {
user32::SetWindowTextW(self.window.0, text.as_ptr() as winapi::LPCWSTR);
}
}
#[inline]
pub fn show(&self) {
unsafe {
user32::ShowWindow(self.window.0, winapi::SW_SHOW);
}
}
#[inline]
pub fn hide(&self) {
unsafe {
user32::ShowWindow(self.window.0, winapi::SW_HIDE);
}
}
/// See the docs in the crate root file.
pub fn get_position(&self) -> Option<(i32, i32)> {
use std::mem;
let mut placement: winapi::WINDOWPLACEMENT = unsafe { mem::zeroed() };
placement.length = mem::size_of::<winapi::WINDOWPLACEMENT>() as winapi::UINT;
if unsafe { user32::GetWindowPlacement(self.window.0, &mut placement) } == 0 {
return None
}
let ref rect = placement.rcNormalPosition;
Some((rect.left as i32, rect.top as i32))
}
/// See the docs in the crate root file.
pub fn set_position(&self, x: i32, y: i32) {
unsafe {
user32::SetWindowPos(self.window.0, ptr::null_mut(), x as c_int, y as c_int,
0, 0, winapi::SWP_NOZORDER | winapi::SWP_NOSIZE);
user32::UpdateWindow(self.window.0);
}
}
/// See the docs in the crate root file.
#[inline]
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
let mut rect: winapi::RECT = unsafe { mem::uninitialized() };
if unsafe { user32::GetClientRect(self.window.0, &mut rect) } == 0 {
return None
}
Some((
(rect.right - rect.left) as u32,
(rect.bottom - rect.top) as u32
))
}
/// See the docs in the crate root file.
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
let mut rect: winapi::RECT = unsafe { mem::uninitialized() };
if unsafe { user32::GetWindowRect(self.window.0, &mut rect) } == 0 {
return None
}
Some((
(rect.right - rect.left) as u32,
(rect.bottom - rect.top) as u32
))
}
/// See the docs in the crate root file.
pub fn set_inner_size(&self, x: u32, y: u32) {
unsafe {
// Calculate the outer size based upon the specified inner size
let mut rect = winapi::RECT { top: 0, left: 0, bottom: y as winapi::LONG, right: x as winapi::LONG };
let dw_style = user32::GetWindowLongA(self.window.0, winapi::GWL_STYLE) as winapi::DWORD;
let b_menu = !user32::GetMenu(self.window.0).is_null() as winapi::BOOL;
let dw_style_ex = user32::GetWindowLongA(self.window.0, winapi::GWL_EXSTYLE) as winapi::DWORD;
user32::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
let outer_x = (rect.right - rect.left).abs() as c_int;
let outer_y = (rect.top - rect.bottom).abs() as c_int;
user32::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, outer_x, outer_y,
winapi::SWP_NOZORDER | winapi::SWP_NOREPOSITION | winapi::SWP_NOMOVE);
user32::UpdateWindow(self.window.0);
}
}
#[inline]
pub fn create_window_proxy(&self) -> WindowProxy {
WindowProxy { hwnd: self.window.0 }
}
/// See the docs in the crate root file.
#[inline]
pub fn poll_events(&self) -> PollEventsIterator {
PollEventsIterator {
window: self,
}
}
/// See the docs in the crate root file.
#[inline]
pub fn wait_events(&self) -> WaitEventsIterator {
WaitEventsIterator {
window: self,
}
}
#[inline]
pub fn platform_display(&self) -> *mut ::libc::c_void {
// What should this return on win32?
// It could be GetDC(NULL), but that requires a ReleaseDC()
// to avoid leaking the DC.
ptr::null_mut()
}
#[inline]
pub fn platform_window(&self) -> *mut ::libc::c_void {
self.window.0 as *mut ::libc::c_void
}
#[inline]
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
}
#[inline]
pub fn set_cursor(&self, _cursor: MouseCursor) {
let cursor_id = match _cursor {
MouseCursor::Arrow | MouseCursor::Default => winapi::IDC_ARROW,
MouseCursor::Hand => winapi::IDC_HAND,
MouseCursor::Crosshair => winapi::IDC_CROSS,
MouseCursor::Text | MouseCursor::VerticalText => winapi::IDC_IBEAM,
MouseCursor::NotAllowed | MouseCursor::NoDrop => winapi::IDC_NO,
MouseCursor::EResize => winapi::IDC_SIZEWE,
MouseCursor::NResize => winapi::IDC_SIZENS,
MouseCursor::WResize => winapi::IDC_SIZEWE,
MouseCursor::SResize => winapi::IDC_SIZENS,
MouseCursor::EwResize | MouseCursor::ColResize => winapi::IDC_SIZEWE,
MouseCursor::NsResize | MouseCursor::RowResize => winapi::IDC_SIZENS,
MouseCursor::Wait | MouseCursor::Progress => winapi::IDC_WAIT,
MouseCursor::Help => winapi::IDC_HELP,
_ => winapi::IDC_ARROW, // use arrow for the missing cases.
};
let mut cur = self.window_state.lock().unwrap();
cur.cursor = cursor_id;
}
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
let mut current_state = self.window_state.lock().unwrap();
let foreground_thread_id = unsafe { user32::GetWindowThreadProcessId(self.window.0, ptr::null_mut()) };
let current_thread_id = unsafe { kernel32::GetCurrentThreadId() };
unsafe { user32::AttachThreadInput(foreground_thread_id, current_thread_id, 1) };
let res = match (state, current_state.cursor_state) {
(CursorState::Normal, CursorState::Normal) => Ok(()),
(CursorState::Hide, CursorState::Hide) => Ok(()),
(CursorState::Grab, CursorState::Grab) => Ok(()),
(CursorState::Hide, CursorState::Normal) => {
current_state.cursor_state = CursorState::Hide;
Ok(())
},
(CursorState::Normal, CursorState::Hide) => {
current_state.cursor_state = CursorState::Normal;
Ok(())
},
(CursorState::Grab, CursorState::Normal) | (CursorState::Grab, CursorState::Hide) => {
unsafe {
let mut rect = mem::uninitialized();
if user32::GetClientRect(self.window.0, &mut rect) == 0 {
return Err(format!("GetWindowRect failed"));
}
user32::ClientToScreen(self.window.0, mem::transmute(&mut rect.left));
user32::ClientToScreen(self.window.0, mem::transmute(&mut rect.right));
if user32::ClipCursor(&rect) == 0 {
return Err(format!("ClipCursor failed"));
}
current_state.cursor_state = CursorState::Grab;
Ok(())
}
},
(CursorState::Normal, CursorState::Grab) => {
unsafe {
if user32::ClipCursor(ptr::null()) == 0 {
return Err(format!("ClipCursor failed"));
}
current_state.cursor_state = CursorState::Normal;
Ok(())
}
},
_ => unimplemented!(),
};
unsafe { user32::AttachThreadInput(foreground_thread_id, current_thread_id, 0) };
res
}
#[inline]
pub fn hidpi_factor(&self) -> f32 {
1.0
}
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
let mut point = winapi::POINT {
x: x,
y: y,
};
unsafe {
if user32::ClientToScreen(self.window.0, &mut point) == 0 {
return Err(());
}
if user32::SetCursorPos(point.x, point.y) == 0 {
return Err(());
}
}
Ok(())
}
}
impl Drop for Window {
#[inline]
fn drop(&mut self) {
unsafe {
user32::PostMessageW(self.window.0, winapi::WM_DESTROY, 0, 0);
}
}
}
pub struct PollEventsIterator<'a> {
window: &'a Window,
}
impl<'a> Iterator for PollEventsIterator<'a> {
type Item = Event;
#[inline]
fn next(&mut self) -> Option<Event> {
self.window.events_receiver.try_recv().ok()
}
}
pub struct WaitEventsIterator<'a> {
window: &'a Window,
}
impl<'a> Iterator for WaitEventsIterator<'a> {
type Item = Event;
#[inline]
fn next(&mut self) -> Option<Event> {
self.window.events_receiver.recv().ok()
}
}

View File

@@ -0,0 +1,186 @@
use winapi;
use user32;
use std::collections::VecDeque;
use std::mem;
use native_monitor::NativeMonitorId;
/// Win32 implementation of the main `MonitorId` object.
#[derive(Clone)]
pub struct MonitorId {
/// The system name of the adapter.
adapter_name: [winapi::WCHAR; 32],
/// The system name of the monitor.
monitor_name: String,
/// Name to give to the user.
readable_name: String,
/// See the `StateFlags` element here:
/// http://msdn.microsoft.com/en-us/library/dd183569(v=vs.85).aspx
flags: winapi::DWORD,
/// True if this is the primary monitor.
primary: bool,
/// The position of the monitor in pixels on the desktop.
///
/// A window that is positionned at these coordinates will overlap the monitor.
position: (u32, u32),
/// The current resolution in pixels on the monitor.
dimensions: (u32, u32),
}
struct DeviceEnumerator {
parent_device: *const winapi::WCHAR,
current_index: u32,
}
impl DeviceEnumerator {
fn adapters() -> DeviceEnumerator {
use std::ptr;
DeviceEnumerator {
parent_device: ptr::null(),
current_index: 0
}
}
fn monitors(adapter_name: *const winapi::WCHAR) -> DeviceEnumerator {
DeviceEnumerator {
parent_device: adapter_name,
current_index: 0
}
}
}
impl Iterator for DeviceEnumerator {
type Item = winapi::DISPLAY_DEVICEW;
fn next(&mut self) -> Option<winapi::DISPLAY_DEVICEW> {
use std::mem;
loop {
let mut output: winapi::DISPLAY_DEVICEW = unsafe { mem::zeroed() };
output.cb = mem::size_of::<winapi::DISPLAY_DEVICEW>() as winapi::DWORD;
if unsafe { user32::EnumDisplayDevicesW(self.parent_device,
self.current_index as winapi::DWORD, &mut output, 0) } == 0
{
// the device doesn't exist, which means we have finished enumerating
break;
}
self.current_index += 1;
if (output.StateFlags & winapi::DISPLAY_DEVICE_ACTIVE) == 0 ||
(output.StateFlags & winapi::DISPLAY_DEVICE_MIRRORING_DRIVER) != 0
{
// the device is not active
// the Win32 api usually returns a lot of inactive devices
continue;
}
return Some(output);
}
None
}
}
fn wchar_as_string(wchar: &[winapi::WCHAR]) -> String {
String::from_utf16_lossy(wchar)
.trim_right_matches(0 as char)
.to_string()
}
/// Win32 implementation of the main `get_available_monitors` function.
pub fn get_available_monitors() -> VecDeque<MonitorId> {
// return value
let mut result = VecDeque::new();
for adapter in DeviceEnumerator::adapters() {
// getting the position
let (position, dimensions) = unsafe {
let mut dev: winapi::DEVMODEW = mem::zeroed();
dev.dmSize = mem::size_of::<winapi::DEVMODEW>() as winapi::WORD;
if user32::EnumDisplaySettingsExW(adapter.DeviceName.as_ptr(),
winapi::ENUM_CURRENT_SETTINGS,
&mut dev, 0) == 0
{
continue;
}
let point: &winapi::POINTL = mem::transmute(&dev.union1);
let position = (point.x as u32, point.y as u32);
let dimensions = (dev.dmPelsWidth as u32, dev.dmPelsHeight as u32);
(position, dimensions)
};
for (num, monitor) in DeviceEnumerator::monitors(adapter.DeviceName.as_ptr()).enumerate() {
// adding to the resulting list
result.push_back(MonitorId {
adapter_name: adapter.DeviceName,
monitor_name: wchar_as_string(&monitor.DeviceName),
readable_name: wchar_as_string(&monitor.DeviceString),
flags: monitor.StateFlags,
primary: (adapter.StateFlags & winapi::DISPLAY_DEVICE_PRIMARY_DEVICE) != 0 &&
num == 0,
position: position,
dimensions: dimensions,
});
}
}
result
}
/// Win32 implementation of the main `get_primary_monitor` function.
pub fn get_primary_monitor() -> MonitorId {
// we simply get all available monitors and return the one with the `PRIMARY_DEVICE` flag
// TODO: it is possible to query the win32 API for the primary monitor, this should be done
// instead
for monitor in get_available_monitors().into_iter() {
if monitor.primary {
return monitor;
}
}
panic!("Failed to find the primary monitor")
}
impl MonitorId {
/// See the docs if the crate root file.
#[inline]
pub fn get_name(&self) -> Option<String> {
Some(self.readable_name.clone())
}
/// See the docs of the crate root file.
#[inline]
pub fn get_native_identifier(&self) -> NativeMonitorId {
NativeMonitorId::Name(self.monitor_name.clone())
}
/// See the docs if the crate root file.
#[inline]
pub fn get_dimensions(&self) -> (u32, u32) {
// TODO: retreive the dimensions every time this is called
self.dimensions
}
/// This is a Win32-only function for `MonitorId` that returns the system name of the adapter
/// device.
#[inline]
pub fn get_adapter_name(&self) -> &[winapi::WCHAR] {
&self.adapter_name
}
/// This is a Win32-only function for `MonitorId` that returns the position of the
/// monitor on the desktop.
/// A window that is positionned at these coordinates will overlap the monitor.
#[inline]
pub fn get_position(&self) -> (u32, u32) {
self.position
}
}

View File

@@ -1,697 +0,0 @@
#![cfg(target_os = "android")]
use crate::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error, event,
event_loop::{self, ControlFlow},
monitor, window,
};
use ndk::{
configuration::Configuration,
event::{InputEvent, KeyAction, MotionAction},
looper::{ForeignLooper, Poll, ThreadLooper},
};
use ndk_glue::{Event, Rect};
use raw_window_handle::{AndroidNdkHandle, RawWindowHandle};
use std::{
collections::VecDeque,
sync::{Arc, Mutex, RwLock},
time::{Duration, Instant},
};
lazy_static! {
static ref CONFIG: RwLock<Configuration> = RwLock::new(Configuration::from_asset_manager(
&ndk_glue::native_activity().asset_manager()
));
// If this is `Some()` a `Poll::Wake` is considered an `EventSource::Internal` with the event
// contained in the `Option`. The event is moved outside of the `Option` replacing it with a
// `None`.
//
// This allows us to inject event into the event loop without going through `ndk-glue` and
// calling unsafe function that should only be called by Android.
static ref INTERNAL_EVENT: RwLock<Option<InternalEvent>> = RwLock::new(None);
}
enum InternalEvent {
RedrawRequested,
}
enum EventSource {
Callback,
InputQueue,
User,
Internal(InternalEvent),
}
fn poll(poll: Poll) -> Option<EventSource> {
match poll {
Poll::Event { ident, .. } => match ident {
ndk_glue::NDK_GLUE_LOOPER_EVENT_PIPE_IDENT => Some(EventSource::Callback),
ndk_glue::NDK_GLUE_LOOPER_INPUT_QUEUE_IDENT => Some(EventSource::InputQueue),
_ => unreachable!(),
},
Poll::Timeout => None,
Poll::Wake => Some(
INTERNAL_EVENT
.write()
.unwrap()
.take()
.map_or(EventSource::User, EventSource::Internal),
),
Poll::Callback => unreachable!(),
}
}
pub struct EventLoop<T: 'static> {
window_target: event_loop::EventLoopWindowTarget<T>,
user_queue: Arc<Mutex<VecDeque<T>>>,
first_event: Option<EventSource>,
start_cause: event::StartCause,
looper: ThreadLooper,
running: bool,
}
macro_rules! call_event_handler {
( $event_handler:expr, $window_target:expr, $cf:expr, $event:expr ) => {{
if $cf != ControlFlow::Exit {
$event_handler($event, $window_target, &mut $cf);
} else {
$event_handler($event, $window_target, &mut ControlFlow::Exit);
}
}};
}
impl<T: 'static> EventLoop<T> {
pub fn new() -> Self {
Self {
window_target: event_loop::EventLoopWindowTarget {
p: EventLoopWindowTarget {
_marker: std::marker::PhantomData,
},
_marker: std::marker::PhantomData,
},
user_queue: Default::default(),
first_event: None,
start_cause: event::StartCause::Init,
looper: ThreadLooper::for_thread().unwrap(),
running: false,
}
}
pub fn run<F>(mut self, event_handler: F) -> !
where
F: 'static
+ FnMut(event::Event<'_, T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
{
self.run_return(event_handler);
::std::process::exit(0);
}
pub fn run_return<F>(&mut self, mut event_handler: F)
where
F: FnMut(event::Event<'_, T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
{
let mut control_flow = ControlFlow::default();
'event_loop: loop {
call_event_handler!(
event_handler,
self.window_target(),
control_flow,
event::Event::NewEvents(self.start_cause)
);
let mut redraw = false;
let mut resized = false;
match self.first_event.take() {
Some(EventSource::Callback) => match ndk_glue::poll_events().unwrap() {
Event::WindowCreated => {
call_event_handler!(
event_handler,
self.window_target(),
control_flow,
event::Event::Resumed
);
}
Event::WindowResized => resized = true,
Event::WindowRedrawNeeded => redraw = true,
Event::WindowDestroyed => {
call_event_handler!(
event_handler,
self.window_target(),
control_flow,
event::Event::Suspended
);
}
Event::Pause => self.running = false,
Event::Resume => self.running = true,
Event::ConfigChanged => {
let am = ndk_glue::native_activity().asset_manager();
let config = Configuration::from_asset_manager(&am);
let old_scale_factor = MonitorHandle.scale_factor();
*CONFIG.write().unwrap() = config;
let scale_factor = MonitorHandle.scale_factor();
if (scale_factor - old_scale_factor).abs() < f64::EPSILON {
let mut size = MonitorHandle.size();
let event = event::Event::WindowEvent {
window_id: window::WindowId(WindowId),
event: event::WindowEvent::ScaleFactorChanged {
new_inner_size: &mut size,
scale_factor,
},
};
call_event_handler!(
event_handler,
self.window_target(),
control_flow,
event
);
}
}
Event::WindowHasFocus => {
call_event_handler!(
event_handler,
self.window_target(),
control_flow,
event::Event::WindowEvent {
window_id: window::WindowId(WindowId),
event: event::WindowEvent::Focused(true),
}
);
}
Event::WindowLostFocus => {
call_event_handler!(
event_handler,
self.window_target(),
control_flow,
event::Event::WindowEvent {
window_id: window::WindowId(WindowId),
event: event::WindowEvent::Focused(false),
}
);
}
_ => {}
},
Some(EventSource::InputQueue) => {
if let Some(input_queue) = ndk_glue::input_queue().as_ref() {
while let Some(event) = input_queue.get_event() {
if let Some(event) = input_queue.pre_dispatch(event) {
let mut handled = true;
let window_id = window::WindowId(WindowId);
let device_id = event::DeviceId(DeviceId);
match &event {
InputEvent::MotionEvent(motion_event) => {
let phase = match motion_event.action() {
MotionAction::Down | MotionAction::PointerDown => {
Some(event::TouchPhase::Started)
}
MotionAction::Up | MotionAction::PointerUp => {
Some(event::TouchPhase::Ended)
}
MotionAction::Move => Some(event::TouchPhase::Moved),
MotionAction::Cancel => {
Some(event::TouchPhase::Cancelled)
}
_ => {
handled = false;
None // TODO mouse events
}
};
if let Some(phase) = phase {
let pointers: Box<
dyn Iterator<Item = ndk::event::Pointer<'_>>,
> = match phase {
event::TouchPhase::Started
| event::TouchPhase::Ended => Box::new(
std::iter::once(motion_event.pointer_at_index(
motion_event.pointer_index(),
)),
),
event::TouchPhase::Moved
| event::TouchPhase::Cancelled => {
Box::new(motion_event.pointers())
}
};
for pointer in pointers {
let location = PhysicalPosition {
x: pointer.x() as _,
y: pointer.y() as _,
};
let event = event::Event::WindowEvent {
window_id,
event: event::WindowEvent::Touch(
event::Touch {
device_id,
phase,
location,
id: pointer.pointer_id() as u64,
force: None,
},
),
};
call_event_handler!(
event_handler,
self.window_target(),
control_flow,
event
);
}
}
}
InputEvent::KeyEvent(key) => {
let state = match key.action() {
KeyAction::Down => event::ElementState::Pressed,
KeyAction::Up => event::ElementState::Released,
_ => event::ElementState::Released,
};
#[allow(deprecated)]
let event = event::Event::WindowEvent {
window_id,
event: event::WindowEvent::KeyboardInput {
device_id,
input: event::KeyboardInput {
scancode: key.scan_code() as u32,
state,
virtual_keycode: None,
modifiers: event::ModifiersState::default(),
},
is_synthetic: false,
},
};
call_event_handler!(
event_handler,
self.window_target(),
control_flow,
event
);
}
};
input_queue.finish_event(event, handled);
}
}
}
}
Some(EventSource::User) => {
let mut user_queue = self.user_queue.lock().unwrap();
while let Some(event) = user_queue.pop_front() {
call_event_handler!(
event_handler,
self.window_target(),
control_flow,
event::Event::UserEvent(event)
);
}
}
Some(EventSource::Internal(internal)) => match internal {
InternalEvent::RedrawRequested => redraw = true,
},
None => {}
}
call_event_handler!(
event_handler,
self.window_target(),
control_flow,
event::Event::MainEventsCleared
);
if resized && self.running {
let size = MonitorHandle.size();
let event = event::Event::WindowEvent {
window_id: window::WindowId(WindowId),
event: event::WindowEvent::Resized(size),
};
call_event_handler!(event_handler, self.window_target(), control_flow, event);
}
if redraw && self.running {
let event = event::Event::RedrawRequested(window::WindowId(WindowId));
call_event_handler!(event_handler, self.window_target(), control_flow, event);
}
call_event_handler!(
event_handler,
self.window_target(),
control_flow,
event::Event::RedrawEventsCleared
);
match control_flow {
ControlFlow::Exit => {
self.first_event = poll(
self.looper
.poll_once_timeout(Duration::from_millis(0))
.unwrap(),
);
self.start_cause = event::StartCause::WaitCancelled {
start: Instant::now(),
requested_resume: None,
};
break 'event_loop;
}
ControlFlow::Poll => {
self.first_event = poll(
self.looper
.poll_all_timeout(Duration::from_millis(0))
.unwrap(),
);
self.start_cause = event::StartCause::Poll;
}
ControlFlow::Wait => {
self.first_event = poll(self.looper.poll_all().unwrap());
self.start_cause = event::StartCause::WaitCancelled {
start: Instant::now(),
requested_resume: None,
}
}
ControlFlow::WaitUntil(instant) => {
let start = Instant::now();
let duration = if instant <= start {
Duration::default()
} else {
instant - start
};
self.first_event = poll(self.looper.poll_all_timeout(duration).unwrap());
self.start_cause = if self.first_event.is_some() {
event::StartCause::WaitCancelled {
start,
requested_resume: Some(instant),
}
} else {
event::StartCause::ResumeTimeReached {
start,
requested_resume: instant,
}
}
}
}
}
}
pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget<T> {
&self.window_target
}
pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy {
queue: self.user_queue.clone(),
looper: ForeignLooper::for_thread().expect("called from event loop thread"),
}
}
}
pub struct EventLoopProxy<T: 'static> {
queue: Arc<Mutex<VecDeque<T>>>,
looper: ForeignLooper,
}
impl<T> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopClosed<T>> {
self.queue.lock().unwrap().push_back(event);
self.looper.wake();
Ok(())
}
}
impl<T> Clone for EventLoopProxy<T> {
fn clone(&self) -> Self {
EventLoopProxy {
queue: self.queue.clone(),
looper: self.looper.clone(),
}
}
}
pub struct EventLoopWindowTarget<T: 'static> {
_marker: std::marker::PhantomData<T>,
}
impl<T: 'static> EventLoopWindowTarget<T> {
pub fn primary_monitor(&self) -> Option<monitor::MonitorHandle> {
Some(monitor::MonitorHandle {
inner: MonitorHandle,
})
}
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
let mut v = VecDeque::with_capacity(1);
v.push_back(MonitorHandle);
v
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct WindowId;
impl WindowId {
pub const fn dummy() -> Self {
WindowId
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DeviceId;
impl DeviceId {
pub const fn dummy() -> Self {
DeviceId
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct PlatformSpecificWindowBuilderAttributes;
pub struct Window;
impl Window {
pub fn new<T: 'static>(
_el: &EventLoopWindowTarget<T>,
_window_attrs: window::WindowAttributes,
_: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, error::OsError> {
// FIXME this ignores requested window attributes
Ok(Self)
}
pub fn id(&self) -> WindowId {
WindowId
}
pub fn primary_monitor(&self) -> Option<monitor::MonitorHandle> {
Some(monitor::MonitorHandle {
inner: MonitorHandle,
})
}
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
let mut v = VecDeque::with_capacity(1);
v.push_back(MonitorHandle);
v
}
pub fn current_monitor(&self) -> Option<monitor::MonitorHandle> {
Some(monitor::MonitorHandle {
inner: MonitorHandle,
})
}
pub fn scale_factor(&self) -> f64 {
MonitorHandle.scale_factor()
}
pub fn request_redraw(&self) {
*INTERNAL_EVENT.write().unwrap() = Some(InternalEvent::RedrawRequested);
ForeignLooper::for_thread().unwrap().wake();
}
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, error::NotSupportedError> {
Err(error::NotSupportedError::new())
}
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, error::NotSupportedError> {
Err(error::NotSupportedError::new())
}
pub fn set_outer_position(&self, _position: Position) {
// no effect
}
pub fn inner_size(&self) -> PhysicalSize<u32> {
self.outer_size()
}
pub fn set_inner_size(&self, _size: Size) {
warn!("Cannot set window size on Android");
}
pub fn outer_size(&self) -> PhysicalSize<u32> {
MonitorHandle.size()
}
pub fn set_min_inner_size(&self, _: Option<Size>) {}
pub fn set_max_inner_size(&self, _: Option<Size>) {}
pub fn set_title(&self, _title: &str) {}
pub fn set_visible(&self, _visibility: bool) {}
pub fn set_resizable(&self, _resizeable: bool) {}
pub fn set_minimized(&self, _minimized: bool) {}
pub fn set_maximized(&self, _maximized: bool) {}
pub fn is_maximized(&self) -> bool {
false
}
pub fn set_fullscreen(&self, _monitor: Option<window::Fullscreen>) {
warn!("Cannot set fullscreen on Android");
}
pub fn fullscreen(&self) -> Option<window::Fullscreen> {
None
}
pub fn set_decorations(&self, _decorations: bool) {}
pub fn set_always_on_top(&self, _always_on_top: bool) {}
pub fn set_window_icon(&self, _window_icon: Option<crate::icon::Icon>) {}
pub fn set_ime_position(&self, _position: Position) {}
pub fn focus_window(&self) {}
pub fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {}
pub fn set_cursor_icon(&self, _: window::CursorIcon) {}
pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
}
pub fn set_cursor_grab(&self, _: bool) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
}
pub fn set_cursor_visible(&self, _: bool) {}
pub fn drag_window(&self) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
}
pub fn raw_window_handle(&self) -> RawWindowHandle {
let mut handle = AndroidNdkHandle::empty();
if let Some(native_window) = ndk_glue::native_window().as_ref() {
handle.a_native_window = unsafe { native_window.ptr().as_mut() as *mut _ as *mut _ }
} else {
panic!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events.");
};
RawWindowHandle::AndroidNdk(handle)
}
pub fn config(&self) -> Configuration {
CONFIG.read().unwrap().clone()
}
pub fn content_rect(&self) -> Rect {
ndk_glue::content_rect()
}
}
#[derive(Default, Clone, Debug)]
pub struct OsError;
use std::fmt::{self, Display, Formatter};
impl Display for OsError {
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), fmt::Error> {
write!(fmt, "Android OS Error")
}
}
pub(crate) use crate::icon::NoIcon as PlatformIcon;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct MonitorHandle;
impl MonitorHandle {
pub fn name(&self) -> Option<String> {
Some("Android Device".to_owned())
}
pub fn size(&self) -> PhysicalSize<u32> {
if let Some(native_window) = ndk_glue::native_window().as_ref() {
let width = native_window.width() as _;
let height = native_window.height() as _;
PhysicalSize::new(width, height)
} else {
PhysicalSize::new(0, 0)
}
}
pub fn position(&self) -> PhysicalPosition<i32> {
(0, 0).into()
}
pub fn scale_factor(&self) -> f64 {
let config = CONFIG.read().unwrap();
config
.density()
.map(|dpi| dpi as f64 / 160.0)
.unwrap_or(1.0)
}
pub fn video_modes(&self) -> impl Iterator<Item = monitor::VideoMode> {
let size = self.size().into();
let mut v = Vec::new();
// FIXME this is not the real refresh rate
// (it is guarunteed to support 32 bit color though)
v.push(monitor::VideoMode {
video_mode: VideoMode {
size,
bit_depth: 32,
refresh_rate: 60,
monitor: self.clone(),
},
});
v.into_iter()
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct VideoMode {
size: (u32, u32),
bit_depth: u16,
refresh_rate: u16,
monitor: MonitorHandle,
}
impl VideoMode {
pub fn size(&self) -> PhysicalSize<u32> {
self.size.into()
}
pub fn bit_depth(&self) -> u16 {
self.bit_depth
}
pub fn refresh_rate(&self) -> u16 {
self.refresh_rate
}
pub fn monitor(&self) -> monitor::MonitorHandle {
monitor::MonitorHandle {
inner: self.monitor.clone(),
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,344 +0,0 @@
use std::{
collections::VecDeque,
ffi::c_void,
fmt::{self, Debug},
marker::PhantomData,
mem, ptr,
sync::mpsc::{self, Receiver, Sender},
};
use crate::{
dpi::LogicalSize,
event::Event,
event_loop::{
ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootEventLoopWindowTarget,
},
monitor::MonitorHandle as RootMonitorHandle,
platform::ios::Idiom,
};
use crate::platform_impl::platform::{
app_state,
ffi::{
id, kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes,
kCFRunLoopDefaultMode, kCFRunLoopEntry, kCFRunLoopExit, nil, CFIndex, CFRelease,
CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddSource, CFRunLoopGetMain,
CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext,
CFRunLoopSourceCreate, CFRunLoopSourceInvalidate, CFRunLoopSourceRef,
CFRunLoopSourceSignal, CFRunLoopWakeUp, NSStringRust, UIApplicationMain,
UIUserInterfaceIdiom,
},
monitor, view, MonitorHandle,
};
#[derive(Debug)]
pub enum EventWrapper {
StaticEvent(Event<'static, Never>),
EventProxy(EventProxy),
}
#[derive(Debug, PartialEq)]
pub enum EventProxy {
DpiChangedProxy {
window_id: id,
suggested_size: LogicalSize<f64>,
scale_factor: f64,
},
}
pub struct EventLoopWindowTarget<T: 'static> {
receiver: Receiver<T>,
sender_to_clone: Sender<T>,
}
impl<T: 'static> EventLoopWindowTarget<T> {
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
// guaranteed to be on main thread
unsafe { monitor::uiscreens() }
}
pub fn primary_monitor(&self) -> Option<RootMonitorHandle> {
// guaranteed to be on main thread
let monitor = unsafe { monitor::main_uiscreen() };
Some(RootMonitorHandle { inner: monitor })
}
}
pub struct EventLoop<T: 'static> {
window_target: RootEventLoopWindowTarget<T>,
}
impl<T: 'static> EventLoop<T> {
pub fn new() -> EventLoop<T> {
static mut SINGLETON_INIT: bool = false;
unsafe {
assert_main_thread!("`EventLoop` can only be created on the main thread on iOS");
assert!(
!SINGLETON_INIT,
"Only one `EventLoop` is supported on iOS. \
`EventLoopProxy` might be helpful"
);
SINGLETON_INIT = true;
view::create_delegate_class();
}
let (sender_to_clone, receiver) = mpsc::channel();
// this line sets up the main run loop before `UIApplicationMain`
setup_control_flow_observers();
EventLoop {
window_target: RootEventLoopWindowTarget {
p: EventLoopWindowTarget {
receiver,
sender_to_clone,
},
_marker: PhantomData,
},
}
}
pub fn run<F>(self, event_handler: F) -> !
where
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
{
unsafe {
let application: *mut c_void = msg_send![class!(UIApplication), sharedApplication];
assert_eq!(
application,
ptr::null_mut(),
"\
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
Note: `EventLoop::run` calls `UIApplicationMain` on iOS"
);
app_state::will_launch(Box::new(EventLoopHandler {
f: event_handler,
event_loop: self.window_target,
}));
UIApplicationMain(
0,
ptr::null(),
nil,
NSStringRust::alloc(nil).init_str("AppDelegate"),
);
unreachable!()
}
}
pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy::new(self.window_target.p.sender_to_clone.clone())
}
pub fn window_target(&self) -> &RootEventLoopWindowTarget<T> {
&self.window_target
}
}
// EventLoopExtIOS
impl<T: 'static> EventLoop<T> {
pub fn idiom(&self) -> Idiom {
// guaranteed to be on main thread
unsafe { self::get_idiom() }
}
}
pub struct EventLoopProxy<T> {
sender: Sender<T>,
source: CFRunLoopSourceRef,
}
unsafe impl<T: Send> Send for EventLoopProxy<T> {}
impl<T> Clone for EventLoopProxy<T> {
fn clone(&self) -> EventLoopProxy<T> {
EventLoopProxy::new(self.sender.clone())
}
}
impl<T> Drop for EventLoopProxy<T> {
fn drop(&mut self) {
unsafe {
CFRunLoopSourceInvalidate(self.source);
CFRelease(self.source as _);
}
}
}
impl<T> EventLoopProxy<T> {
fn new(sender: Sender<T>) -> EventLoopProxy<T> {
unsafe {
// just wake up the eventloop
extern "C" fn event_loop_proxy_handler(_: *mut c_void) {}
// adding a Source to the main CFRunLoop lets us wake it up and
// process user events through the normal OS EventLoop mechanisms.
let rl = CFRunLoopGetMain();
// we want all the members of context to be zero/null, except one
let mut context: CFRunLoopSourceContext = mem::zeroed();
context.perform = Some(event_loop_proxy_handler);
let source =
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl);
EventLoopProxy { sender, source }
}
}
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.sender
.send(event)
.map_err(|::std::sync::mpsc::SendError(x)| EventLoopClosed(x))?;
unsafe {
// let the main thread know there's a new event
CFRunLoopSourceSignal(self.source);
let rl = CFRunLoopGetMain();
CFRunLoopWakeUp(rl);
}
Ok(())
}
}
fn setup_control_flow_observers() {
unsafe {
// begin is queued with the highest priority to ensure it is processed before other observers
extern "C" fn control_flow_begin_handler(
_: CFRunLoopObserverRef,
activity: CFRunLoopActivity,
_: *mut c_void,
) {
unsafe {
#[allow(non_upper_case_globals)]
match activity {
kCFRunLoopAfterWaiting => app_state::handle_wakeup_transition(),
kCFRunLoopEntry => unimplemented!(), // not expected to ever happen
_ => unreachable!(),
}
}
}
// Core Animation registers its `CFRunLoopObserver` that performs drawing operations in
// `CA::Transaction::ensure_implicit` with a priority of `0x1e8480`. We set the main_end
// priority to be 0, in order to send MainEventsCleared before RedrawRequested. This value was
// chosen conservatively to guard against apple using different priorities for their redraw
// observers in different OS's or on different devices. If it so happens that it's too
// conservative, the main symptom would be non-redraw events coming in after `MainEventsCleared`.
//
// The value of `0x1e8480` was determined by inspecting stack traces and the associated
// registers for every `CFRunLoopAddObserver` call on an iPad Air 2 running iOS 11.4.
//
// Also tested to be `0x1e8480` on iPhone 8, iOS 13 beta 4.
extern "C" fn control_flow_main_end_handler(
_: CFRunLoopObserverRef,
activity: CFRunLoopActivity,
_: *mut c_void,
) {
unsafe {
#[allow(non_upper_case_globals)]
match activity {
kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(),
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
_ => unreachable!(),
}
}
}
// end is queued with the lowest priority to ensure it is processed after other observers
extern "C" fn control_flow_end_handler(
_: CFRunLoopObserverRef,
activity: CFRunLoopActivity,
_: *mut c_void,
) {
unsafe {
#[allow(non_upper_case_globals)]
match activity {
kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(),
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
_ => unreachable!(),
}
}
}
let main_loop = CFRunLoopGetMain();
let begin_observer = CFRunLoopObserverCreate(
ptr::null_mut(),
kCFRunLoopEntry | kCFRunLoopAfterWaiting,
1, // repeat = true
CFIndex::min_value(),
control_flow_begin_handler,
ptr::null_mut(),
);
CFRunLoopAddObserver(main_loop, begin_observer, kCFRunLoopDefaultMode);
let main_end_observer = CFRunLoopObserverCreate(
ptr::null_mut(),
kCFRunLoopExit | kCFRunLoopBeforeWaiting,
1, // repeat = true
0, // see comment on `control_flow_main_end_handler`
control_flow_main_end_handler,
ptr::null_mut(),
);
CFRunLoopAddObserver(main_loop, main_end_observer, kCFRunLoopDefaultMode);
let end_observer = CFRunLoopObserverCreate(
ptr::null_mut(),
kCFRunLoopExit | kCFRunLoopBeforeWaiting,
1, // repeat = true
CFIndex::max_value(),
control_flow_end_handler,
ptr::null_mut(),
);
CFRunLoopAddObserver(main_loop, end_observer, kCFRunLoopDefaultMode);
}
}
#[derive(Debug)]
pub enum Never {}
pub trait EventHandler: Debug {
fn handle_nonuser_event(&mut self, event: Event<'_, Never>, control_flow: &mut ControlFlow);
fn handle_user_events(&mut self, control_flow: &mut ControlFlow);
}
struct EventLoopHandler<F, T: 'static> {
f: F,
event_loop: RootEventLoopWindowTarget<T>,
}
impl<F, T: 'static> Debug for EventLoopHandler<F, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventLoopHandler")
.field("event_loop", &self.event_loop)
.finish()
}
}
impl<F, T> EventHandler for EventLoopHandler<F, T>
where
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
T: 'static,
{
fn handle_nonuser_event(&mut self, event: Event<'_, Never>, control_flow: &mut ControlFlow) {
(self.f)(
event.map_nonuser_event().unwrap(),
&self.event_loop,
control_flow,
);
}
fn handle_user_events(&mut self, control_flow: &mut ControlFlow) {
for event in self.event_loop.p.receiver.try_iter() {
(self.f)(Event::UserEvent(event), &self.event_loop, control_flow);
}
}
}
// must be called on main thread
pub unsafe fn get_idiom() -> Idiom {
let device: id = msg_send![class!(UIDevice), currentDevice];
let raw_idiom: UIUserInterfaceIdiom = msg_send![device, userInterfaceIdiom];
raw_idiom.into()
}

View File

@@ -1,393 +0,0 @@
#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
use std::{convert::TryInto, ffi::CString, ops::BitOr, os::raw::*};
use objc::{runtime::Object, Encode, Encoding};
use crate::{
dpi::LogicalSize,
platform::ios::{Idiom, ScreenEdge, ValidOrientations},
};
pub type id = *mut Object;
pub const nil: id = 0 as id;
#[cfg(target_pointer_width = "32")]
pub type CGFloat = f32;
#[cfg(target_pointer_width = "64")]
pub type CGFloat = f64;
pub type NSInteger = isize;
pub type NSUInteger = usize;
#[repr(C)]
#[derive(Clone, Debug)]
pub struct NSOperatingSystemVersion {
pub major: NSInteger,
pub minor: NSInteger,
pub patch: NSInteger,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CGPoint {
pub x: CGFloat,
pub y: CGFloat,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CGSize {
pub width: CGFloat,
pub height: CGFloat,
}
impl CGSize {
pub fn new(size: LogicalSize<f64>) -> CGSize {
CGSize {
width: size.width as _,
height: size.height as _,
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CGRect {
pub origin: CGPoint,
pub size: CGSize,
}
impl CGRect {
pub fn new(origin: CGPoint, size: CGSize) -> CGRect {
CGRect { origin, size }
}
}
unsafe impl Encode for CGRect {
fn encode() -> Encoding {
unsafe {
if cfg!(target_pointer_width = "32") {
Encoding::from_str("{CGRect={CGPoint=ff}{CGSize=ff}}")
} else if cfg!(target_pointer_width = "64") {
Encoding::from_str("{CGRect={CGPoint=dd}{CGSize=dd}}")
} else {
unimplemented!()
}
}
}
}
#[derive(Debug)]
#[allow(dead_code)]
#[repr(isize)]
pub enum UITouchPhase {
Began = 0,
Moved,
Stationary,
Ended,
Cancelled,
}
#[derive(Debug, PartialEq)]
#[allow(dead_code)]
#[repr(isize)]
pub enum UIForceTouchCapability {
Unknown = 0,
Unavailable,
Available,
}
#[derive(Debug, PartialEq)]
#[allow(dead_code)]
#[repr(isize)]
pub enum UITouchType {
Direct = 0,
Indirect,
Pencil,
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct UIEdgeInsets {
pub top: CGFloat,
pub left: CGFloat,
pub bottom: CGFloat,
pub right: CGFloat,
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UIUserInterfaceIdiom(NSInteger);
unsafe impl Encode for UIUserInterfaceIdiom {
fn encode() -> Encoding {
NSInteger::encode()
}
}
impl UIUserInterfaceIdiom {
pub const Unspecified: UIUserInterfaceIdiom = UIUserInterfaceIdiom(-1);
pub const Phone: UIUserInterfaceIdiom = UIUserInterfaceIdiom(0);
pub const Pad: UIUserInterfaceIdiom = UIUserInterfaceIdiom(1);
pub const TV: UIUserInterfaceIdiom = UIUserInterfaceIdiom(2);
pub const CarPlay: UIUserInterfaceIdiom = UIUserInterfaceIdiom(3);
}
impl From<Idiom> for UIUserInterfaceIdiom {
fn from(idiom: Idiom) -> UIUserInterfaceIdiom {
match idiom {
Idiom::Unspecified => UIUserInterfaceIdiom::Unspecified,
Idiom::Phone => UIUserInterfaceIdiom::Phone,
Idiom::Pad => UIUserInterfaceIdiom::Pad,
Idiom::TV => UIUserInterfaceIdiom::TV,
Idiom::CarPlay => UIUserInterfaceIdiom::CarPlay,
}
}
}
impl Into<Idiom> for UIUserInterfaceIdiom {
fn into(self) -> Idiom {
match self {
UIUserInterfaceIdiom::Unspecified => Idiom::Unspecified,
UIUserInterfaceIdiom::Phone => Idiom::Phone,
UIUserInterfaceIdiom::Pad => Idiom::Pad,
UIUserInterfaceIdiom::TV => Idiom::TV,
UIUserInterfaceIdiom::CarPlay => Idiom::CarPlay,
_ => unreachable!(),
}
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug)]
pub struct UIInterfaceOrientationMask(NSUInteger);
unsafe impl Encode for UIInterfaceOrientationMask {
fn encode() -> Encoding {
NSUInteger::encode()
}
}
impl UIInterfaceOrientationMask {
pub const Portrait: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 1);
pub const PortraitUpsideDown: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 2);
pub const LandscapeLeft: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 4);
pub const LandscapeRight: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 3);
pub const Landscape: UIInterfaceOrientationMask =
UIInterfaceOrientationMask(Self::LandscapeLeft.0 | Self::LandscapeRight.0);
pub const AllButUpsideDown: UIInterfaceOrientationMask =
UIInterfaceOrientationMask(Self::Landscape.0 | Self::Portrait.0);
pub const All: UIInterfaceOrientationMask =
UIInterfaceOrientationMask(Self::AllButUpsideDown.0 | Self::PortraitUpsideDown.0);
}
impl BitOr for UIInterfaceOrientationMask {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
UIInterfaceOrientationMask(self.0 | rhs.0)
}
}
impl UIInterfaceOrientationMask {
pub fn from_valid_orientations_idiom(
valid_orientations: ValidOrientations,
idiom: Idiom,
) -> UIInterfaceOrientationMask {
match (valid_orientations, idiom) {
(ValidOrientations::LandscapeAndPortrait, Idiom::Phone) => {
UIInterfaceOrientationMask::AllButUpsideDown
}
(ValidOrientations::LandscapeAndPortrait, _) => UIInterfaceOrientationMask::All,
(ValidOrientations::Landscape, _) => UIInterfaceOrientationMask::Landscape,
(ValidOrientations::Portrait, Idiom::Phone) => UIInterfaceOrientationMask::Portrait,
(ValidOrientations::Portrait, _) => {
UIInterfaceOrientationMask::Portrait
| UIInterfaceOrientationMask::PortraitUpsideDown
}
}
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UIRectEdge(NSUInteger);
unsafe impl Encode for UIRectEdge {
fn encode() -> Encoding {
NSUInteger::encode()
}
}
impl From<ScreenEdge> for UIRectEdge {
fn from(screen_edge: ScreenEdge) -> UIRectEdge {
assert_eq!(
screen_edge.bits() & !ScreenEdge::ALL.bits(),
0,
"invalid `ScreenEdge`"
);
UIRectEdge(screen_edge.bits().into())
}
}
impl Into<ScreenEdge> for UIRectEdge {
fn into(self) -> ScreenEdge {
let bits: u8 = self.0.try_into().expect("invalid `UIRectEdge`");
ScreenEdge::from_bits(bits).expect("invalid `ScreenEdge`")
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UIScreenOverscanCompensation(NSInteger);
unsafe impl Encode for UIScreenOverscanCompensation {
fn encode() -> Encoding {
NSInteger::encode()
}
}
#[allow(dead_code)]
impl UIScreenOverscanCompensation {
pub const Scale: UIScreenOverscanCompensation = UIScreenOverscanCompensation(0);
pub const InsetBounds: UIScreenOverscanCompensation = UIScreenOverscanCompensation(1);
pub const None: UIScreenOverscanCompensation = UIScreenOverscanCompensation(2);
}
#[link(name = "UIKit", kind = "framework")]
#[link(name = "CoreFoundation", kind = "framework")]
extern "C" {
pub static kCFRunLoopDefaultMode: CFRunLoopMode;
pub static kCFRunLoopCommonModes: CFRunLoopMode;
pub fn UIApplicationMain(
argc: c_int,
argv: *const c_char,
principalClassName: id,
delegateClassName: id,
) -> c_int;
pub fn CFRunLoopGetMain() -> CFRunLoopRef;
pub fn CFRunLoopWakeUp(rl: CFRunLoopRef);
pub fn CFRunLoopObserverCreate(
allocator: CFAllocatorRef,
activities: CFOptionFlags,
repeats: Boolean,
order: CFIndex,
callout: CFRunLoopObserverCallBack,
context: *mut CFRunLoopObserverContext,
) -> CFRunLoopObserverRef;
pub fn CFRunLoopAddObserver(
rl: CFRunLoopRef,
observer: CFRunLoopObserverRef,
mode: CFRunLoopMode,
);
pub fn CFRunLoopTimerCreate(
allocator: CFAllocatorRef,
fireDate: CFAbsoluteTime,
interval: CFTimeInterval,
flags: CFOptionFlags,
order: CFIndex,
callout: CFRunLoopTimerCallBack,
context: *mut CFRunLoopTimerContext,
) -> CFRunLoopTimerRef;
pub fn CFRunLoopAddTimer(rl: CFRunLoopRef, timer: CFRunLoopTimerRef, mode: CFRunLoopMode);
pub fn CFRunLoopTimerSetNextFireDate(timer: CFRunLoopTimerRef, fireDate: CFAbsoluteTime);
pub fn CFRunLoopTimerInvalidate(time: CFRunLoopTimerRef);
pub fn CFRunLoopSourceCreate(
allocator: CFAllocatorRef,
order: CFIndex,
context: *mut CFRunLoopSourceContext,
) -> CFRunLoopSourceRef;
pub fn CFRunLoopAddSource(rl: CFRunLoopRef, source: CFRunLoopSourceRef, mode: CFRunLoopMode);
pub fn CFRunLoopSourceInvalidate(source: CFRunLoopSourceRef);
pub fn CFRunLoopSourceSignal(source: CFRunLoopSourceRef);
pub fn CFAbsoluteTimeGetCurrent() -> CFAbsoluteTime;
pub fn CFRelease(cftype: *const c_void);
}
pub type Boolean = u8;
pub enum CFAllocator {}
pub type CFAllocatorRef = *mut CFAllocator;
pub enum CFRunLoop {}
pub type CFRunLoopRef = *mut CFRunLoop;
pub type CFRunLoopMode = CFStringRef;
pub enum CFRunLoopObserver {}
pub type CFRunLoopObserverRef = *mut CFRunLoopObserver;
pub enum CFRunLoopTimer {}
pub type CFRunLoopTimerRef = *mut CFRunLoopTimer;
pub enum CFRunLoopSource {}
pub type CFRunLoopSourceRef = *mut CFRunLoopSource;
pub enum CFString {}
pub type CFStringRef = *const CFString;
pub type CFHashCode = c_ulong;
pub type CFIndex = c_long;
pub type CFOptionFlags = c_ulong;
pub type CFRunLoopActivity = CFOptionFlags;
pub type CFAbsoluteTime = CFTimeInterval;
pub type CFTimeInterval = f64;
pub const kCFRunLoopEntry: CFRunLoopActivity = 0;
pub const kCFRunLoopBeforeWaiting: CFRunLoopActivity = 1 << 5;
pub const kCFRunLoopAfterWaiting: CFRunLoopActivity = 1 << 6;
pub const kCFRunLoopExit: CFRunLoopActivity = 1 << 7;
pub type CFRunLoopObserverCallBack =
extern "C" fn(observer: CFRunLoopObserverRef, activity: CFRunLoopActivity, info: *mut c_void);
pub type CFRunLoopTimerCallBack = extern "C" fn(timer: CFRunLoopTimerRef, info: *mut c_void);
pub enum CFRunLoopObserverContext {}
pub enum CFRunLoopTimerContext {}
#[repr(C)]
pub struct CFRunLoopSourceContext {
pub version: CFIndex,
pub info: *mut c_void,
pub retain: Option<extern "C" fn(*const c_void) -> *const c_void>,
pub release: Option<extern "C" fn(*const c_void)>,
pub copyDescription: Option<extern "C" fn(*const c_void) -> CFStringRef>,
pub equal: Option<extern "C" fn(*const c_void, *const c_void) -> Boolean>,
pub hash: Option<extern "C" fn(*const c_void) -> CFHashCode>,
pub schedule: Option<extern "C" fn(*mut c_void, CFRunLoopRef, CFRunLoopMode)>,
pub cancel: Option<extern "C" fn(*mut c_void, CFRunLoopRef, CFRunLoopMode)>,
pub perform: Option<extern "C" fn(*mut c_void)>,
}
// This is named NSStringRust rather than NSString because the "Debug View Heirarchy" feature of
// Xcode requires a non-ambiguous reference to NSString for unclear reasons. This makes Xcode happy
// so please test if you change the name back to NSString.
pub trait NSStringRust: Sized {
unsafe fn alloc(_: Self) -> id {
msg_send![class!(NSString), alloc]
}
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;
unsafe fn UTF8String(self) -> *const c_char;
}
impl NSStringRust for id {
unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id {
msg_send![self, initWithUTF8String: c_string as id]
}
unsafe fn stringByAppendingString_(self, other: id) -> id {
msg_send![self, stringByAppendingString: other]
}
unsafe fn init_str(self, string: &str) -> id {
let cstring = CString::new(string).unwrap();
self.initWithUTF8String_(cstring.as_ptr())
}
unsafe fn UTF8String(self) -> *const c_char {
msg_send![self, UTF8String]
}
}

View File

@@ -1,113 +0,0 @@
//! iOS support
//!
//! # Building app
//! To build ios app you will need rustc built for this targets:
//!
//! - armv7-apple-ios
//! - armv7s-apple-ios
//! - i386-apple-ios
//! - aarch64-apple-ios
//! - x86_64-apple-ios
//!
//! Then
//!
//! ```
//! cargo build --target=...
//! ```
//! The simplest way to integrate your app into xcode environment is to build it
//! as a static library. Wrap your main function and export it.
//!
//! ```rust, ignore
//! #[no_mangle]
//! pub extern fn start_winit_app() {
//! start_inner()
//! }
//!
//! fn start_inner() {
//! ...
//! }
//! ```
//!
//! Compile project and then drag resulting .a into Xcode project. Add winit.h to xcode.
//!
//! ```ignore
//! void start_winit_app();
//! ```
//!
//! Use start_winit_app inside your xcode's main function.
//!
//!
//! # App lifecycle and events
//!
//! iOS environment is very different from other platforms and you must be very
//! careful with it's events. Familiarize yourself with
//! [app lifecycle](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/).
//!
//!
//! This is how those event are represented in winit:
//!
//! - applicationDidBecomeActive is Resumed
//! - applicationWillResignActive is Suspended
//! - applicationWillTerminate is LoopDestroyed
//!
//! Keep in mind that after LoopDestroyed event is received every attempt to draw with
//! opengl will result in segfault.
//!
//! Also note that app may not receive the LoopDestroyed event if suspended; it might be SIGKILL'ed.
#![cfg(target_os = "ios")]
// TODO: (mtak-) UIKit requires main thread for virtually all function/method calls. This could be
// worked around in the future by using GCD (grand central dispatch) and/or caching of values like
// window size/position.
macro_rules! assert_main_thread {
($($t:tt)*) => {
if !msg_send![class!(NSThread), isMainThread] {
panic!($($t)*);
}
};
}
mod app_state;
mod event_loop;
mod ffi;
mod monitor;
mod view;
mod window;
use std::fmt;
pub use self::{
event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget},
monitor::{MonitorHandle, VideoMode},
window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId},
};
pub(crate) use crate::icon::NoIcon as PlatformIcon;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId {
uiscreen: ffi::id,
}
impl DeviceId {
pub const unsafe fn dummy() -> Self {
DeviceId {
uiscreen: std::ptr::null_mut(),
}
}
}
unsafe impl Send for DeviceId {}
unsafe impl Sync for DeviceId {}
#[derive(Debug)]
pub enum OsError {}
impl fmt::Display for OsError {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
_ => unreachable!(),
}
}
}

View File

@@ -1,299 +0,0 @@
use std::{
collections::{BTreeSet, VecDeque},
fmt,
ops::{Deref, DerefMut},
};
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
platform_impl::platform::{
app_state,
ffi::{id, nil, CGFloat, CGRect, CGSize, NSInteger, NSUInteger},
},
};
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct VideoMode {
pub(crate) size: (u32, u32),
pub(crate) bit_depth: u16,
pub(crate) refresh_rate: u16,
pub(crate) screen_mode: NativeDisplayMode,
pub(crate) monitor: MonitorHandle,
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct NativeDisplayMode(pub id);
unsafe impl Send for NativeDisplayMode {}
impl Drop for NativeDisplayMode {
fn drop(&mut self) {
unsafe {
let () = msg_send![self.0, release];
}
}
}
impl Clone for NativeDisplayMode {
fn clone(&self) -> Self {
unsafe {
let _: id = msg_send![self.0, retain];
}
NativeDisplayMode(self.0)
}
}
impl Clone for VideoMode {
fn clone(&self) -> VideoMode {
VideoMode {
size: self.size,
bit_depth: self.bit_depth,
refresh_rate: self.refresh_rate,
screen_mode: self.screen_mode.clone(),
monitor: self.monitor.clone(),
}
}
}
impl VideoMode {
unsafe fn retained_new(uiscreen: id, screen_mode: id) -> VideoMode {
assert_main_thread!("`VideoMode` can only be created on the main thread on iOS");
let os_capabilities = app_state::os_capabilities();
let refresh_rate: NSInteger = if os_capabilities.maximum_frames_per_second {
msg_send![uiscreen, maximumFramesPerSecond]
} else {
// https://developer.apple.com/library/archive/technotes/tn2460/_index.html
// https://en.wikipedia.org/wiki/IPad_Pro#Model_comparison
//
// All iOS devices support 60 fps, and on devices where `maximumFramesPerSecond` is not
// supported, they are all guaranteed to have 60hz refresh rates. This does not
// correctly handle external displays. ProMotion displays support 120fps, but they were
// introduced at the same time as the `maximumFramesPerSecond` API.
//
// FIXME: earlier OSs could calculate the refresh rate using
// `-[CADisplayLink duration]`.
os_capabilities.maximum_frames_per_second_err_msg("defaulting to 60 fps");
60
};
let size: CGSize = msg_send![screen_mode, size];
let screen_mode: id = msg_send![screen_mode, retain];
let screen_mode = NativeDisplayMode(screen_mode);
VideoMode {
size: (size.width as u32, size.height as u32),
bit_depth: 32,
refresh_rate: refresh_rate as u16,
screen_mode,
monitor: MonitorHandle::retained_new(uiscreen),
}
}
pub fn size(&self) -> PhysicalSize<u32> {
self.size.into()
}
pub fn bit_depth(&self) -> u16 {
self.bit_depth
}
pub fn refresh_rate(&self) -> u16 {
self.refresh_rate
}
pub fn monitor(&self) -> RootMonitorHandle {
RootMonitorHandle {
inner: self.monitor.clone(),
}
}
}
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Inner {
uiscreen: id,
}
impl Drop for Inner {
fn drop(&mut self) {
unsafe {
let () = msg_send![self.uiscreen, release];
}
}
}
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct MonitorHandle {
inner: Inner,
}
impl Deref for MonitorHandle {
type Target = Inner;
fn deref(&self) -> &Inner {
unsafe {
assert_main_thread!(
"`MonitorHandle` methods can only be run on the main thread on iOS"
);
}
&self.inner
}
}
impl DerefMut for MonitorHandle {
fn deref_mut(&mut self) -> &mut Inner {
unsafe {
assert_main_thread!(
"`MonitorHandle` methods can only be run on the main thread on iOS"
);
}
&mut self.inner
}
}
unsafe impl Send for MonitorHandle {}
unsafe impl Sync for MonitorHandle {}
impl Clone for MonitorHandle {
fn clone(&self) -> MonitorHandle {
MonitorHandle::retained_new(self.uiscreen)
}
}
impl Drop for MonitorHandle {
fn drop(&mut self) {
unsafe {
assert_main_thread!("`MonitorHandle` can only be dropped on the main thread on iOS");
}
}
}
impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[derive(Debug)]
struct MonitorHandle {
name: Option<String>,
size: PhysicalSize<u32>,
position: PhysicalPosition<i32>,
scale_factor: f64,
}
let monitor_id_proxy = MonitorHandle {
name: self.name(),
size: self.size(),
position: self.position(),
scale_factor: self.scale_factor(),
};
monitor_id_proxy.fmt(f)
}
}
impl MonitorHandle {
pub fn retained_new(uiscreen: id) -> MonitorHandle {
unsafe {
assert_main_thread!("`MonitorHandle` can only be cloned on the main thread on iOS");
let () = msg_send![uiscreen, retain];
}
MonitorHandle {
inner: Inner { uiscreen },
}
}
}
impl Inner {
pub fn name(&self) -> Option<String> {
unsafe {
let main = main_uiscreen();
if self.uiscreen == main.uiscreen {
Some("Primary".to_string())
} else if self.uiscreen == mirrored_uiscreen(&main).uiscreen {
Some("Mirrored".to_string())
} else {
uiscreens()
.iter()
.position(|rhs| rhs.uiscreen == self.uiscreen)
.map(|idx| idx.to_string())
}
}
}
pub fn size(&self) -> PhysicalSize<u32> {
unsafe {
let bounds: CGRect = msg_send![self.ui_screen(), nativeBounds];
PhysicalSize::new(bounds.size.width as u32, bounds.size.height as u32)
}
}
pub fn position(&self) -> PhysicalPosition<i32> {
unsafe {
let bounds: CGRect = msg_send![self.ui_screen(), nativeBounds];
(bounds.origin.x as f64, bounds.origin.y as f64).into()
}
}
pub fn scale_factor(&self) -> f64 {
unsafe {
let scale: CGFloat = msg_send![self.ui_screen(), nativeScale];
scale as f64
}
}
pub fn video_modes(&self) -> impl Iterator<Item = RootVideoMode> {
let mut modes = BTreeSet::new();
unsafe {
let available_modes: id = msg_send![self.uiscreen, availableModes];
let available_mode_count: NSUInteger = msg_send![available_modes, count];
for i in 0..available_mode_count {
let mode: id = msg_send![available_modes, objectAtIndex: i];
modes.insert(RootVideoMode {
video_mode: VideoMode::retained_new(self.uiscreen, mode),
});
}
}
modes.into_iter()
}
}
// MonitorHandleExtIOS
impl Inner {
pub fn ui_screen(&self) -> id {
self.uiscreen
}
pub fn preferred_video_mode(&self) -> RootVideoMode {
unsafe {
let mode: id = msg_send![self.uiscreen, preferredMode];
RootVideoMode {
video_mode: VideoMode::retained_new(self.uiscreen, mode),
}
}
}
}
// requires being run on main thread
pub unsafe fn main_uiscreen() -> MonitorHandle {
let uiscreen: id = msg_send![class!(UIScreen), mainScreen];
MonitorHandle::retained_new(uiscreen)
}
// requires being run on main thread
unsafe fn mirrored_uiscreen(monitor: &MonitorHandle) -> MonitorHandle {
let uiscreen: id = msg_send![monitor.uiscreen, mirroredScreen];
MonitorHandle::retained_new(uiscreen)
}
// requires being run on main thread
pub unsafe fn uiscreens() -> VecDeque<MonitorHandle> {
let screens: id = msg_send![class!(UIScreen), screens];
let count: NSUInteger = msg_send![screens, count];
let mut result = VecDeque::with_capacity(count as _);
let screens_enum: id = msg_send![screens, objectEnumerator];
loop {
let screen: id = msg_send![screens_enum, nextObject];
if screen == nil {
break result;
}
result.push_back(MonitorHandle::retained_new(screen));
}
}

View File

@@ -1,624 +0,0 @@
use std::collections::HashMap;
use objc::{
declare::ClassDecl,
runtime::{Class, Object, Sel, BOOL, NO, YES},
};
use crate::{
dpi::PhysicalPosition,
event::{DeviceId as RootDeviceId, Event, Force, Touch, TouchPhase, WindowEvent},
platform::ios::MonitorHandleExtIOS,
platform_impl::platform::{
app_state::{self, OSCapabilities},
event_loop::{self, EventProxy, EventWrapper},
ffi::{
id, nil, CGFloat, CGPoint, CGRect, UIForceTouchCapability, UIInterfaceOrientationMask,
UIRectEdge, UITouchPhase, UITouchType,
},
window::PlatformSpecificWindowBuilderAttributes,
DeviceId,
},
window::{Fullscreen, WindowAttributes, WindowId as RootWindowId},
};
macro_rules! add_property {
(
$decl:ident,
$name:ident: $t:ty,
$setter_name:ident: |$object:ident| $after_set:expr,
$getter_name:ident,
) => {
add_property!(
$decl,
$name: $t,
$setter_name: true, |_, _|{}; |$object| $after_set,
$getter_name,
)
};
(
$decl:ident,
$name:ident: $t:ty,
$setter_name:ident: $capability:expr, $err:expr; |$object:ident| $after_set:expr,
$getter_name:ident,
) => {
{
const VAR_NAME: &'static str = concat!("_", stringify!($name));
$decl.add_ivar::<$t>(VAR_NAME);
let setter = if $capability {
#[allow(non_snake_case)]
extern "C" fn $setter_name($object: &mut Object, _: Sel, value: $t) {
unsafe {
$object.set_ivar::<$t>(VAR_NAME, value);
}
$after_set
}
$setter_name
} else {
#[allow(non_snake_case)]
extern "C" fn $setter_name($object: &mut Object, _: Sel, value: $t) {
unsafe {
$object.set_ivar::<$t>(VAR_NAME, value);
}
$err(&app_state::os_capabilities(), "ignoring")
}
$setter_name
};
#[allow(non_snake_case)]
extern "C" fn $getter_name($object: &Object, _: Sel) -> $t {
unsafe { *$object.get_ivar::<$t>(VAR_NAME) }
}
$decl.add_method(
sel!($setter_name:),
setter as extern "C" fn(&mut Object, Sel, $t),
);
$decl.add_method(
sel!($getter_name),
$getter_name as extern "C" fn(&Object, Sel) -> $t,
);
}
};
}
// requires main thread
unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
static mut CLASSES: Option<HashMap<*const Class, &'static Class>> = None;
static mut ID: usize = 0;
if CLASSES.is_none() {
CLASSES = Some(HashMap::default());
}
let classes = CLASSES.as_mut().unwrap();
classes.entry(root_view_class).or_insert_with(move || {
let uiview_class = class!(UIView);
let is_uiview: BOOL = msg_send![root_view_class, isSubclassOfClass: uiview_class];
assert_eq!(
is_uiview, YES,
"`root_view_class` must inherit from `UIView`"
);
extern "C" fn draw_rect(object: &Object, _: Sel, rect: CGRect) {
unsafe {
let window: id = msg_send![object, window];
assert!(!window.is_null());
app_state::handle_nonuser_events(
std::iter::once(EventWrapper::StaticEvent(Event::RedrawRequested(
RootWindowId(window.into()),
)))
.chain(std::iter::once(EventWrapper::StaticEvent(
Event::RedrawEventsCleared,
))),
);
let superclass: &'static Class = msg_send![object, superclass];
let () = msg_send![super(object, superclass), drawRect: rect];
}
}
extern "C" fn layout_subviews(object: &Object, _: Sel) {
unsafe {
let superclass: &'static Class = msg_send![object, superclass];
let () = msg_send![super(object, superclass), layoutSubviews];
let window: id = msg_send![object, window];
assert!(!window.is_null());
let window_bounds: CGRect = msg_send![window, bounds];
let screen: id = msg_send![window, screen];
let screen_space: id = msg_send![screen, coordinateSpace];
let screen_frame: CGRect =
msg_send![object, convertRect:window_bounds toCoordinateSpace:screen_space];
let scale_factor: CGFloat = msg_send![screen, scale];
let size = crate::dpi::LogicalSize {
width: screen_frame.size.width as f64,
height: screen_frame.size.height as f64,
}
.to_physical(scale_factor.into());
// If the app is started in landscape, the view frame and window bounds can be mismatched.
// The view frame will be in portrait and the window bounds in landscape. So apply the
// window bounds to the view frame to make it consistent.
let view_frame: CGRect = msg_send![object, frame];
if view_frame != window_bounds {
let () = msg_send![object, setFrame: window_bounds];
}
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::Resized(size),
}));
}
}
extern "C" fn set_content_scale_factor(
object: &mut Object,
_: Sel,
untrusted_scale_factor: CGFloat,
) {
unsafe {
let superclass: &'static Class = msg_send![object, superclass];
let () = msg_send![
super(object, superclass),
setContentScaleFactor: untrusted_scale_factor
];
let window: id = msg_send![object, window];
// `window` is null when `setContentScaleFactor` is invoked prior to `[UIWindow
// makeKeyAndVisible]` at window creation time (either manually or internally by
// UIKit when the `UIView` is first created), in which case we send no events here
if window.is_null() {
return;
}
// `setContentScaleFactor` may be called with a value of 0, which means "reset the
// content scale factor to a device-specific default value", so we can't use the
// parameter here. We can query the actual factor using the getter
let scale_factor: CGFloat = msg_send![object, contentScaleFactor];
assert!(
!scale_factor.is_nan()
&& scale_factor.is_finite()
&& scale_factor.is_sign_positive()
&& scale_factor > 0.0,
"invalid scale_factor set on UIView",
);
let scale_factor: f64 = scale_factor.into();
let bounds: CGRect = msg_send![object, bounds];
let screen: id = msg_send![window, screen];
let screen_space: id = msg_send![screen, coordinateSpace];
let screen_frame: CGRect =
msg_send![object, convertRect:bounds toCoordinateSpace:screen_space];
let size = crate::dpi::LogicalSize {
width: screen_frame.size.width as _,
height: screen_frame.size.height as _,
};
app_state::handle_nonuser_events(
std::iter::once(EventWrapper::EventProxy(EventProxy::DpiChangedProxy {
window_id: window,
scale_factor,
suggested_size: size,
}))
.chain(std::iter::once(EventWrapper::StaticEvent(
Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::Resized(size.to_physical(scale_factor)),
},
))),
);
}
}
extern "C" fn handle_touches(object: &Object, _: Sel, touches: id, _: id) {
unsafe {
let window: id = msg_send![object, window];
assert!(!window.is_null());
let uiscreen: id = msg_send![window, screen];
let touches_enum: id = msg_send![touches, objectEnumerator];
let mut touch_events = Vec::new();
let os_supports_force = app_state::os_capabilities().force_touch;
loop {
let touch: id = msg_send![touches_enum, nextObject];
if touch == nil {
break;
}
let logical_location: CGPoint = msg_send![touch, locationInView: nil];
let touch_type: UITouchType = msg_send![touch, type];
let force = if os_supports_force {
let trait_collection: id = msg_send![object, traitCollection];
let touch_capability: UIForceTouchCapability =
msg_send![trait_collection, forceTouchCapability];
// Both the OS _and_ the device need to be checked for force touch support.
if touch_capability == UIForceTouchCapability::Available {
let force: CGFloat = msg_send![touch, force];
let max_possible_force: CGFloat =
msg_send![touch, maximumPossibleForce];
let altitude_angle: Option<f64> = if touch_type == UITouchType::Pencil {
let angle: CGFloat = msg_send![touch, altitudeAngle];
Some(angle as _)
} else {
None
};
Some(Force::Calibrated {
force: force as _,
max_possible_force: max_possible_force as _,
altitude_angle,
})
} else {
None
}
} else {
None
};
let touch_id = touch as u64;
let phase: UITouchPhase = msg_send![touch, phase];
let phase = match phase {
UITouchPhase::Began => TouchPhase::Started,
UITouchPhase::Moved => TouchPhase::Moved,
// 2 is UITouchPhase::Stationary and is not expected here
UITouchPhase::Ended => TouchPhase::Ended,
UITouchPhase::Cancelled => TouchPhase::Cancelled,
_ => panic!("unexpected touch phase: {:?}", phase as i32),
};
let physical_location = {
let scale_factor: CGFloat = msg_send![object, contentScaleFactor];
PhysicalPosition::from_logical::<(f64, f64), f64>(
(logical_location.x as _, logical_location.y as _),
scale_factor,
)
};
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::Touch(Touch {
device_id: RootDeviceId(DeviceId { uiscreen }),
id: touch_id,
location: physical_location,
force,
phase,
}),
}));
}
app_state::handle_nonuser_events(touch_events);
}
}
let mut decl = ClassDecl::new(&format!("WinitUIView{}", ID), root_view_class)
.expect("Failed to declare class `WinitUIView`");
ID += 1;
decl.add_method(
sel!(drawRect:),
draw_rect as extern "C" fn(&Object, Sel, CGRect),
);
decl.add_method(
sel!(layoutSubviews),
layout_subviews as extern "C" fn(&Object, Sel),
);
decl.add_method(
sel!(setContentScaleFactor:),
set_content_scale_factor as extern "C" fn(&mut Object, Sel, CGFloat),
);
decl.add_method(
sel!(touchesBegan:withEvent:),
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
);
decl.add_method(
sel!(touchesMoved:withEvent:),
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
);
decl.add_method(
sel!(touchesEnded:withEvent:),
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
);
decl.add_method(
sel!(touchesCancelled:withEvent:),
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
);
decl.register()
})
}
// requires main thread
unsafe fn get_view_controller_class() -> &'static Class {
static mut CLASS: Option<&'static Class> = None;
if CLASS.is_none() {
let os_capabilities = app_state::os_capabilities();
let uiviewcontroller_class = class!(UIViewController);
extern "C" fn should_autorotate(_: &Object, _: Sel) -> BOOL {
YES
}
let mut decl = ClassDecl::new("WinitUIViewController", uiviewcontroller_class)
.expect("Failed to declare class `WinitUIViewController`");
decl.add_method(
sel!(shouldAutorotate),
should_autorotate as extern "C" fn(&Object, Sel) -> BOOL,
);
add_property! {
decl,
prefers_status_bar_hidden: BOOL,
setPrefersStatusBarHidden: |object| {
unsafe {
let () = msg_send![object, setNeedsStatusBarAppearanceUpdate];
}
},
prefersStatusBarHidden,
}
add_property! {
decl,
prefers_home_indicator_auto_hidden: BOOL,
setPrefersHomeIndicatorAutoHidden:
os_capabilities.home_indicator_hidden,
OSCapabilities::home_indicator_hidden_err_msg;
|object| {
unsafe {
let () = msg_send![object, setNeedsUpdateOfHomeIndicatorAutoHidden];
}
},
prefersHomeIndicatorAutoHidden,
}
add_property! {
decl,
supported_orientations: UIInterfaceOrientationMask,
setSupportedInterfaceOrientations: |object| {
unsafe {
let () = msg_send![class!(UIViewController), attemptRotationToDeviceOrientation];
}
},
supportedInterfaceOrientations,
}
add_property! {
decl,
preferred_screen_edges_deferring_system_gestures: UIRectEdge,
setPreferredScreenEdgesDeferringSystemGestures:
os_capabilities.defer_system_gestures,
OSCapabilities::defer_system_gestures_err_msg;
|object| {
unsafe {
let () = msg_send![object, setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
}
},
preferredScreenEdgesDeferringSystemGestures,
}
CLASS = Some(decl.register());
}
CLASS.unwrap()
}
// requires main thread
unsafe fn get_window_class() -> &'static Class {
static mut CLASS: Option<&'static Class> = None;
if CLASS.is_none() {
let uiwindow_class = class!(UIWindow);
extern "C" fn become_key_window(object: &Object, _: Sel) {
unsafe {
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(object.into()),
event: WindowEvent::Focused(true),
}));
let () = msg_send![super(object, class!(UIWindow)), becomeKeyWindow];
}
}
extern "C" fn resign_key_window(object: &Object, _: Sel) {
unsafe {
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(object.into()),
event: WindowEvent::Focused(false),
}));
let () = msg_send![super(object, class!(UIWindow)), resignKeyWindow];
}
}
let mut decl = ClassDecl::new("WinitUIWindow", uiwindow_class)
.expect("Failed to declare class `WinitUIWindow`");
decl.add_method(
sel!(becomeKeyWindow),
become_key_window as extern "C" fn(&Object, Sel),
);
decl.add_method(
sel!(resignKeyWindow),
resign_key_window as extern "C" fn(&Object, Sel),
);
CLASS = Some(decl.register());
}
CLASS.unwrap()
}
// requires main thread
pub unsafe fn create_view(
_window_attributes: &WindowAttributes,
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
frame: CGRect,
) -> id {
let class = get_view_class(platform_attributes.root_view_class);
let view: id = msg_send![class, alloc];
assert!(!view.is_null(), "Failed to create `UIView` instance");
let view: id = msg_send![view, initWithFrame: frame];
assert!(!view.is_null(), "Failed to initialize `UIView` instance");
let () = msg_send![view, setMultipleTouchEnabled: YES];
if let Some(scale_factor) = platform_attributes.scale_factor {
let () = msg_send![view, setContentScaleFactor: scale_factor as CGFloat];
}
view
}
// requires main thread
pub unsafe fn create_view_controller(
_window_attributes: &WindowAttributes,
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
view: id,
) -> id {
let class = get_view_controller_class();
let view_controller: id = msg_send![class, alloc];
assert!(
!view_controller.is_null(),
"Failed to create `UIViewController` instance"
);
let view_controller: id = msg_send![view_controller, init];
assert!(
!view_controller.is_null(),
"Failed to initialize `UIViewController` instance"
);
let status_bar_hidden = if platform_attributes.prefers_status_bar_hidden {
YES
} else {
NO
};
let idiom = event_loop::get_idiom();
let supported_orientations = UIInterfaceOrientationMask::from_valid_orientations_idiom(
platform_attributes.valid_orientations,
idiom,
);
let prefers_home_indicator_hidden = if platform_attributes.prefers_home_indicator_hidden {
YES
} else {
NO
};
let edges: UIRectEdge = platform_attributes
.preferred_screen_edges_deferring_system_gestures
.into();
let () = msg_send![
view_controller,
setPrefersStatusBarHidden: status_bar_hidden
];
let () = msg_send![
view_controller,
setSupportedInterfaceOrientations: supported_orientations
];
let () = msg_send![
view_controller,
setPrefersHomeIndicatorAutoHidden: prefers_home_indicator_hidden
];
let () = msg_send![
view_controller,
setPreferredScreenEdgesDeferringSystemGestures: edges
];
let () = msg_send![view_controller, setView: view];
view_controller
}
// requires main thread
pub unsafe fn create_window(
window_attributes: &WindowAttributes,
_platform_attributes: &PlatformSpecificWindowBuilderAttributes,
frame: CGRect,
view_controller: id,
) -> id {
let class = get_window_class();
let window: id = msg_send![class, alloc];
assert!(!window.is_null(), "Failed to create `UIWindow` instance");
let window: id = msg_send![window, initWithFrame: frame];
assert!(
!window.is_null(),
"Failed to initialize `UIWindow` instance"
);
let () = msg_send![window, setRootViewController: view_controller];
match window_attributes.fullscreen {
Some(Fullscreen::Exclusive(ref video_mode)) => {
let uiscreen = video_mode.monitor().ui_screen() as id;
let () = msg_send![uiscreen, setCurrentMode: video_mode.video_mode.screen_mode.0];
msg_send![window, setScreen:video_mode.monitor().ui_screen()]
}
Some(Fullscreen::Borderless(ref monitor)) => {
let uiscreen: id = match &monitor {
Some(monitor) => monitor.ui_screen() as id,
None => {
let uiscreen: id = msg_send![window, screen];
uiscreen
}
};
msg_send![window, setScreen: uiscreen]
}
None => (),
}
window
}
pub fn create_delegate_class() {
extern "C" fn did_finish_launching(_: &mut Object, _: Sel, _: id, _: id) -> BOOL {
unsafe {
app_state::did_finish_launching();
}
YES
}
extern "C" fn did_become_active(_: &Object, _: Sel, _: id) {
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Resumed)) }
}
extern "C" fn will_resign_active(_: &Object, _: Sel, _: id) {
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Suspended)) }
}
extern "C" fn will_enter_foreground(_: &Object, _: Sel, _: id) {}
extern "C" fn did_enter_background(_: &Object, _: Sel, _: id) {}
extern "C" fn will_terminate(_: &Object, _: Sel, _: id) {
unsafe {
let app: id = msg_send![class!(UIApplication), sharedApplication];
let windows: id = msg_send![app, windows];
let windows_enum: id = msg_send![windows, objectEnumerator];
let mut events = Vec::new();
loop {
let window: id = msg_send![windows_enum, nextObject];
if window == nil {
break;
}
let is_winit_window: BOOL = msg_send![window, isKindOfClass: class!(WinitUIWindow)];
if is_winit_window == YES {
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::Destroyed,
}));
}
}
app_state::handle_nonuser_events(events);
app_state::terminated();
}
}
let ui_responder = class!(UIResponder);
let mut decl =
ClassDecl::new("AppDelegate", ui_responder).expect("Failed to declare class `AppDelegate`");
unsafe {
decl.add_method(
sel!(application:didFinishLaunchingWithOptions:),
did_finish_launching as extern "C" fn(&mut Object, Sel, id, id) -> BOOL,
);
decl.add_method(
sel!(applicationDidBecomeActive:),
did_become_active as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(applicationWillResignActive:),
will_resign_active as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(applicationWillEnterForeground:),
will_enter_foreground as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(applicationDidEnterBackground:),
did_enter_background as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(applicationWillTerminate:),
will_terminate as extern "C" fn(&Object, Sel, id),
);
decl.register();
}
}

View File

@@ -1,665 +0,0 @@
use raw_window_handle::{RawWindowHandle, UiKitHandle};
use std::{
collections::VecDeque,
ops::{Deref, DerefMut},
};
use objc::runtime::{Class, Object, BOOL, NO, YES};
use crate::{
dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
event::{Event, WindowEvent},
icon::Icon,
monitor::MonitorHandle as RootMonitorHandle,
platform::ios::{MonitorHandleExtIOS, ScreenEdge, ValidOrientations},
platform_impl::platform::{
app_state,
event_loop::{self, EventProxy, EventWrapper},
ffi::{
id, CGFloat, CGPoint, CGRect, CGSize, UIEdgeInsets, UIInterfaceOrientationMask,
UIRectEdge, UIScreenOverscanCompensation,
},
monitor, view, EventLoopWindowTarget, MonitorHandle,
},
window::{
CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWindowId,
},
};
pub struct Inner {
pub window: id,
pub view_controller: id,
pub view: id,
gl_or_metal_backed: bool,
}
impl Drop for Inner {
fn drop(&mut self) {
unsafe {
let () = msg_send![self.view, release];
let () = msg_send![self.view_controller, release];
let () = msg_send![self.window, release];
}
}
}
impl Inner {
pub fn set_title(&self, _title: &str) {
debug!("`Window::set_title` is ignored on iOS")
}
pub fn set_visible(&self, visible: bool) {
match visible {
true => unsafe {
let () = msg_send![self.window, setHidden: NO];
},
false => unsafe {
let () = msg_send![self.window, setHidden: YES];
},
}
}
pub fn request_redraw(&self) {
unsafe {
if self.gl_or_metal_backed {
// `setNeedsDisplay` does nothing on UIViews which are directly backed by CAEAGLLayer or CAMetalLayer.
// Ordinarily the OS sets up a bunch of UIKit state before calling drawRect: on a UIView, but when using
// raw or gl/metal for drawing this work is completely avoided.
//
// The docs for `setNeedsDisplay` don't mention `CAMetalLayer`; however, this has been confirmed via
// testing.
//
// https://developer.apple.com/documentation/uikit/uiview/1622437-setneedsdisplay?language=objc
app_state::queue_gl_or_metal_redraw(self.window);
} else {
let () = msg_send![self.view, setNeedsDisplay];
}
}
}
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
unsafe {
let safe_area = self.safe_area_screen_space();
let position = LogicalPosition {
x: safe_area.origin.x as f64,
y: safe_area.origin.y as f64,
};
let scale_factor = self.scale_factor();
Ok(position.to_physical(scale_factor))
}
}
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
unsafe {
let screen_frame = self.screen_frame();
let position = LogicalPosition {
x: screen_frame.origin.x as f64,
y: screen_frame.origin.y as f64,
};
let scale_factor = self.scale_factor();
Ok(position.to_physical(scale_factor))
}
}
pub fn set_outer_position(&self, physical_position: Position) {
unsafe {
let scale_factor = self.scale_factor();
let position = physical_position.to_logical::<f64>(scale_factor);
let screen_frame = self.screen_frame();
let new_screen_frame = CGRect {
origin: CGPoint {
x: position.x as _,
y: position.y as _,
},
size: screen_frame.size,
};
let bounds = self.from_screen_space(new_screen_frame);
let () = msg_send![self.window, setBounds: bounds];
}
}
pub fn inner_size(&self) -> PhysicalSize<u32> {
unsafe {
let scale_factor = self.scale_factor();
let safe_area = self.safe_area_screen_space();
let size = LogicalSize {
width: safe_area.size.width as f64,
height: safe_area.size.height as f64,
};
size.to_physical(scale_factor)
}
}
pub fn outer_size(&self) -> PhysicalSize<u32> {
unsafe {
let scale_factor = self.scale_factor();
let screen_frame = self.screen_frame();
let size = LogicalSize {
width: screen_frame.size.width as f64,
height: screen_frame.size.height as f64,
};
size.to_physical(scale_factor)
}
}
pub fn set_inner_size(&self, _size: Size) {
warn!("not clear what `Window::set_inner_size` means on iOS");
}
pub fn set_min_inner_size(&self, _dimensions: Option<Size>) {
warn!("`Window::set_min_inner_size` is ignored on iOS")
}
pub fn set_max_inner_size(&self, _dimensions: Option<Size>) {
warn!("`Window::set_max_inner_size` is ignored on iOS")
}
pub fn set_resizable(&self, _resizable: bool) {
warn!("`Window::set_resizable` is ignored on iOS")
}
pub fn scale_factor(&self) -> f64 {
unsafe {
let hidpi: CGFloat = msg_send![self.view, contentScaleFactor];
hidpi as _
}
}
pub fn set_cursor_icon(&self, _cursor: CursorIcon) {
debug!("`Window::set_cursor_icon` ignored on iOS")
}
pub fn set_cursor_position(&self, _position: Position) -> Result<(), ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
pub fn set_cursor_grab(&self, _grab: bool) -> Result<(), ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
pub fn set_cursor_visible(&self, _visible: bool) {
debug!("`Window::set_cursor_visible` is ignored on iOS")
}
pub fn drag_window(&self) -> Result<(), ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
pub fn set_minimized(&self, _minimized: bool) {
warn!("`Window::set_minimized` is ignored on iOS")
}
pub fn set_maximized(&self, _maximized: bool) {
warn!("`Window::set_maximized` is ignored on iOS")
}
pub fn is_maximized(&self) -> bool {
warn!("`Window::is_maximized` is ignored on iOS");
false
}
pub fn set_fullscreen(&self, monitor: Option<Fullscreen>) {
unsafe {
let uiscreen = match monitor {
Some(Fullscreen::Exclusive(video_mode)) => {
let uiscreen = video_mode.video_mode.monitor.ui_screen() as id;
let () = msg_send![uiscreen, setCurrentMode: video_mode.video_mode.screen_mode];
uiscreen
}
Some(Fullscreen::Borderless(monitor)) => monitor
.unwrap_or_else(|| self.current_monitor_inner())
.ui_screen() as id,
None => {
warn!("`Window::set_fullscreen(None)` ignored on iOS");
return;
}
};
// this is pretty slow on iOS, so avoid doing it if we can
let current: id = msg_send![self.window, screen];
if uiscreen != current {
let () = msg_send![self.window, setScreen: uiscreen];
}
let bounds: CGRect = msg_send![uiscreen, bounds];
let () = msg_send![self.window, setFrame: bounds];
// For external displays, we must disable overscan compensation or
// the displayed image will have giant black bars surrounding it on
// each side
let () = msg_send![
uiscreen,
setOverscanCompensation: UIScreenOverscanCompensation::None
];
}
}
pub fn fullscreen(&self) -> Option<Fullscreen> {
unsafe {
let monitor = self.current_monitor_inner();
let uiscreen = monitor.inner.ui_screen();
let screen_space_bounds = self.screen_frame();
let screen_bounds: CGRect = msg_send![uiscreen, bounds];
// TODO: track fullscreen instead of relying on brittle float comparisons
if screen_space_bounds.origin.x == screen_bounds.origin.x
&& screen_space_bounds.origin.y == screen_bounds.origin.y
&& screen_space_bounds.size.width == screen_bounds.size.width
&& screen_space_bounds.size.height == screen_bounds.size.height
{
Some(Fullscreen::Borderless(Some(monitor)))
} else {
None
}
}
}
pub fn set_decorations(&self, _decorations: bool) {
warn!("`Window::set_decorations` is ignored on iOS")
}
pub fn set_always_on_top(&self, _always_on_top: bool) {
warn!("`Window::set_always_on_top` is ignored on iOS")
}
pub fn set_window_icon(&self, _icon: Option<Icon>) {
warn!("`Window::set_window_icon` is ignored on iOS")
}
pub fn set_ime_position(&self, _position: Position) {
warn!("`Window::set_ime_position` is ignored on iOS")
}
pub fn focus_window(&self) {
warn!("`Window::set_focus` is ignored on iOS")
}
pub fn request_user_attention(&self, _request_type: Option<UserAttentionType>) {
warn!("`Window::request_user_attention` is ignored on iOS")
}
// Allow directly accessing the current monitor internally without unwrapping.
fn current_monitor_inner(&self) -> RootMonitorHandle {
unsafe {
let uiscreen: id = msg_send![self.window, screen];
RootMonitorHandle {
inner: MonitorHandle::retained_new(uiscreen),
}
}
}
pub fn current_monitor(&self) -> Option<RootMonitorHandle> {
Some(self.current_monitor_inner())
}
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
unsafe { monitor::uiscreens() }
}
pub fn primary_monitor(&self) -> Option<RootMonitorHandle> {
let monitor = unsafe { monitor::main_uiscreen() };
Some(RootMonitorHandle { inner: monitor })
}
pub fn id(&self) -> WindowId {
self.window.into()
}
pub fn raw_window_handle(&self) -> RawWindowHandle {
let mut handle = UiKitHandle::empty();
handle.ui_window = self.window as _;
handle.ui_view = self.view as _;
handle.ui_view_controller = self.view_controller as _;
RawWindowHandle::UiKit(handle)
}
}
pub struct Window {
pub inner: Inner,
}
impl Drop for Window {
fn drop(&mut self) {
unsafe {
assert_main_thread!("`Window::drop` can only be run on the main thread on iOS");
}
}
}
unsafe impl Send for Window {}
unsafe impl Sync for Window {}
impl Deref for Window {
type Target = Inner;
fn deref(&self) -> &Inner {
unsafe {
assert_main_thread!("`Window` methods can only be run on the main thread on iOS");
}
&self.inner
}
}
impl DerefMut for Window {
fn deref_mut(&mut self) -> &mut Inner {
unsafe {
assert_main_thread!("`Window` methods can only be run on the main thread on iOS");
}
&mut self.inner
}
}
impl Window {
pub fn new<T>(
_event_loop: &EventLoopWindowTarget<T>,
window_attributes: WindowAttributes,
platform_attributes: PlatformSpecificWindowBuilderAttributes,
) -> Result<Window, RootOsError> {
if let Some(_) = window_attributes.min_inner_size {
warn!("`WindowAttributes::min_inner_size` is ignored on iOS");
}
if let Some(_) = window_attributes.max_inner_size {
warn!("`WindowAttributes::max_inner_size` is ignored on iOS");
}
if window_attributes.always_on_top {
warn!("`WindowAttributes::always_on_top` is unsupported on iOS");
}
// TODO: transparency, visible
unsafe {
let screen = match window_attributes.fullscreen {
Some(Fullscreen::Exclusive(ref video_mode)) => {
video_mode.video_mode.monitor.ui_screen() as id
}
Some(Fullscreen::Borderless(Some(ref monitor))) => monitor.inner.ui_screen(),
Some(Fullscreen::Borderless(None)) | None => {
monitor::main_uiscreen().ui_screen() as id
}
};
let screen_bounds: CGRect = msg_send![screen, bounds];
let frame = match window_attributes.inner_size {
Some(dim) => {
let scale_factor = msg_send![screen, scale];
let size = dim.to_logical::<f64>(scale_factor);
CGRect {
origin: screen_bounds.origin,
size: CGSize {
width: size.width as _,
height: size.height as _,
},
}
}
None => screen_bounds,
};
let view = view::create_view(&window_attributes, &platform_attributes, frame.clone());
let gl_or_metal_backed = {
let view_class: id = msg_send![view, class];
let layer_class: id = msg_send![view_class, layerClass];
let is_metal: BOOL =
msg_send![layer_class, isSubclassOfClass: class!(CAMetalLayer)];
let is_gl: BOOL = msg_send![layer_class, isSubclassOfClass: class!(CAEAGLLayer)];
is_metal == YES || is_gl == YES
};
let view_controller =
view::create_view_controller(&window_attributes, &platform_attributes, view);
let window = view::create_window(
&window_attributes,
&platform_attributes,
frame,
view_controller,
);
let result = Window {
inner: Inner {
window,
view_controller,
view,
gl_or_metal_backed,
},
};
app_state::set_key_window(window);
// Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `Resized`
// event on window creation if the DPI factor != 1.0
let scale_factor: CGFloat = msg_send![view, contentScaleFactor];
let scale_factor: f64 = scale_factor.into();
if scale_factor != 1.0 {
let bounds: CGRect = msg_send![view, bounds];
let screen: id = msg_send![window, screen];
let screen_space: id = msg_send![screen, coordinateSpace];
let screen_frame: CGRect =
msg_send![view, convertRect:bounds toCoordinateSpace:screen_space];
let size = crate::dpi::LogicalSize {
width: screen_frame.size.width as _,
height: screen_frame.size.height as _,
};
app_state::handle_nonuser_events(
std::iter::once(EventWrapper::EventProxy(EventProxy::DpiChangedProxy {
window_id: window,
scale_factor,
suggested_size: size,
}))
.chain(std::iter::once(EventWrapper::StaticEvent(
Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::Resized(size.to_physical(scale_factor)),
},
))),
);
}
Ok(result)
}
}
}
// WindowExtIOS
impl Inner {
pub fn ui_window(&self) -> id {
self.window
}
pub fn ui_view_controller(&self) -> id {
self.view_controller
}
pub fn ui_view(&self) -> id {
self.view
}
pub fn set_scale_factor(&self, scale_factor: f64) {
unsafe {
assert!(
dpi::validate_scale_factor(scale_factor),
"`WindowExtIOS::set_scale_factor` received an invalid hidpi factor"
);
let scale_factor = scale_factor as CGFloat;
let () = msg_send![self.view, setContentScaleFactor: scale_factor];
}
}
pub fn set_valid_orientations(&self, valid_orientations: ValidOrientations) {
unsafe {
let idiom = event_loop::get_idiom();
let supported_orientations = UIInterfaceOrientationMask::from_valid_orientations_idiom(
valid_orientations,
idiom,
);
msg_send![
self.view_controller,
setSupportedInterfaceOrientations: supported_orientations
]
}
}
pub fn set_prefers_home_indicator_hidden(&self, hidden: bool) {
unsafe {
let prefers_home_indicator_hidden = if hidden { YES } else { NO };
let () = msg_send![
self.view_controller,
setPrefersHomeIndicatorAutoHidden: prefers_home_indicator_hidden
];
}
}
pub fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) {
let edges: UIRectEdge = edges.into();
unsafe {
let () = msg_send![
self.view_controller,
setPreferredScreenEdgesDeferringSystemGestures: edges
];
}
}
pub fn set_prefers_status_bar_hidden(&self, hidden: bool) {
unsafe {
let status_bar_hidden = if hidden { YES } else { NO };
let () = msg_send![
self.view_controller,
setPrefersStatusBarHidden: status_bar_hidden
];
}
}
}
impl Inner {
// requires main thread
unsafe fn screen_frame(&self) -> CGRect {
self.to_screen_space(msg_send![self.window, bounds])
}
// requires main thread
unsafe fn to_screen_space(&self, rect: CGRect) -> CGRect {
let screen: id = msg_send![self.window, screen];
if !screen.is_null() {
let screen_space: id = msg_send![screen, coordinateSpace];
msg_send![self.window, convertRect:rect toCoordinateSpace:screen_space]
} else {
rect
}
}
// requires main thread
unsafe fn from_screen_space(&self, rect: CGRect) -> CGRect {
let screen: id = msg_send![self.window, screen];
if !screen.is_null() {
let screen_space: id = msg_send![screen, coordinateSpace];
msg_send![self.window, convertRect:rect fromCoordinateSpace:screen_space]
} else {
rect
}
}
// requires main thread
unsafe fn safe_area_screen_space(&self) -> CGRect {
let bounds: CGRect = msg_send![self.window, bounds];
if app_state::os_capabilities().safe_area {
let safe_area: UIEdgeInsets = msg_send![self.window, safeAreaInsets];
let safe_bounds = CGRect {
origin: CGPoint {
x: bounds.origin.x + safe_area.left,
y: bounds.origin.y + safe_area.top,
},
size: CGSize {
width: bounds.size.width - safe_area.left - safe_area.right,
height: bounds.size.height - safe_area.top - safe_area.bottom,
},
};
self.to_screen_space(safe_bounds)
} else {
let screen_frame = self.to_screen_space(bounds);
let status_bar_frame: CGRect = {
let app: id = msg_send![class!(UIApplication), sharedApplication];
assert!(
!app.is_null(),
"`Window::get_inner_position` cannot be called before `EventLoop::run` on iOS"
);
msg_send![app, statusBarFrame]
};
let (y, height) = if screen_frame.origin.y > status_bar_frame.size.height {
(screen_frame.origin.y, screen_frame.size.height)
} else {
let y = status_bar_frame.size.height;
let height = screen_frame.size.height
- (status_bar_frame.size.height - screen_frame.origin.y);
(y, height)
};
CGRect {
origin: CGPoint {
x: screen_frame.origin.x,
y,
},
size: CGSize {
width: screen_frame.size.width,
height,
},
}
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId {
window: id,
}
impl WindowId {
pub const unsafe fn dummy() -> Self {
WindowId {
window: std::ptr::null_mut(),
}
}
}
unsafe impl Send for WindowId {}
unsafe impl Sync for WindowId {}
impl From<&Object> for WindowId {
fn from(window: &Object) -> WindowId {
WindowId {
window: window as *const _ as _,
}
}
}
impl From<&mut Object> for WindowId {
fn from(window: &mut Object) -> WindowId {
WindowId {
window: window as _,
}
}
}
impl From<id> for WindowId {
fn from(window: id) -> WindowId {
WindowId { window }
}
}
#[derive(Clone)]
pub struct PlatformSpecificWindowBuilderAttributes {
pub root_view_class: &'static Class,
pub scale_factor: Option<f64>,
pub valid_orientations: ValidOrientations,
pub prefers_home_indicator_hidden: bool,
pub prefers_status_bar_hidden: bool,
pub preferred_screen_edges_deferring_system_gestures: ScreenEdge,
}
impl Default for PlatformSpecificWindowBuilderAttributes {
fn default() -> PlatformSpecificWindowBuilderAttributes {
PlatformSpecificWindowBuilderAttributes {
root_view_class: class!(UIView),
scale_factor: None,
valid_orientations: Default::default(),
prefers_home_indicator_hidden: false,
prefers_status_bar_hidden: false,
preferred_screen_edges_deferring_system_gestures: Default::default(),
}
}
}

Some files were not shown because too many files have changed in this diff Show More