Merge branch 'web' into webmerge2

This commit is contained in:
Hal Gentz 2019-07-09 22:43:15 -06:00
commit 613fafdfdf
No known key found for this signature in database
GPG key ID: FFA703792A952E2E
117 changed files with 5740 additions and 4274 deletions

View file

@ -1,56 +0,0 @@
version: 2
jobs:
android-test:
working_directory: ~/winit
docker:
- image: tomaka/cargo-apk
steps:
- run: apt-get -qq update && apt-get install -y git
- checkout
- restore_cache:
key: android-test-cache-{{ checksum "Cargo.toml" }}
- run: cargo apk build --example window
- save_cache:
key: android-test-cache-{{ checksum "Cargo.toml" }}
paths:
- target
asmjs-test:
working_directory: ~/winit
docker:
- image: tomaka/rustc-emscripten
steps:
- run: apt-get -qq update && apt-get install -y git
- checkout
- restore_cache:
key: asmjs-test-cache-{{ checksum "Cargo.toml" }}
- run: cargo build --example window --target asmjs-unknown-emscripten
- save_cache:
key: asmjs-test-cache-{{ checksum "Cargo.toml" }}
paths:
- target
wasm-test:
working_directory: ~/winit
docker:
- image: tomaka/rustc-emscripten
steps:
- run: apt-get -qq update && apt-get install -y git
- checkout
- restore_cache:
key: wasm-test-cache-{{ checksum "Cargo.toml" }}
- run: cargo build --example window --target wasm32-unknown-emscripten
- save_cache:
key: wasm-test-cache-{{ checksum "Cargo.toml" }}
paths:
- target
workflows:
version: 2
build-test-and-deploy:
jobs:
- android-test
- asmjs-test
- wasm-test

2
.gitattributes vendored
View file

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

View file

@ -1,5 +1,6 @@
- [ ] Tested on all platforms changed - [ ] Tested on all platforms changed
- [ ] `cargo fmt` has been run on this branch
- [ ] Added an entry to `CHANGELOG.md` if knowledge of this change could be valuable to users - [ ] 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 - [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
- [ ] Created an example program if it would help users understand this functionality - [ ] Created or updated an example program if it would help users understand this functionality
- [ ] Updated [feature matrix](https://github.com/tomaka/winit/blob/master/FEATURES.md), if new features were added or implemented - [ ] Updated [feature matrix](https://github.com/rust-windowing/winit/blob/master/FEATURES.md), if new features were added or implemented

2
.gitmodules vendored
View file

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

View file

@ -1,7 +1,5 @@
language: rust language: rust
cache: cargo
matrix: matrix:
include: include:
# Linux 32bit # Linux 32bit
@ -47,8 +45,10 @@ matrix:
install: install:
- rustup self update - rustup self update
- rustup target add $TARGET; true - rustup target add $TARGET; true
- rustup component add rustfmt
script: script:
- cargo fmt --all -- --check
- cargo build --target $TARGET --verbose - cargo build --target $TARGET --verbose
- cargo build --target $TARGET --features serde --verbose - cargo build --target $TARGET --features serde --verbose
# Running iOS apps on OSX requires the simulator so we skip that for now # Running iOS apps on OSX requires the simulator so we skip that for now

View file

@ -1,5 +1,26 @@
# Unreleased # Unreleased
# 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**. - Changes below are considered **breaking**.
- Change all occurrences of `EventsLoop` to `EventLoop`. - Change all occurrences of `EventsLoop` to `EventLoop`.
- Previously flat API is now exposed through `event`, `event_loop`, `monitor`, and `window` modules. - Previously flat API is now exposed through `event`, `event_loop`, `monitor`, and `window` modules.
@ -39,6 +60,11 @@
- Removed `serde` implementations from `ControlFlow`. - Removed `serde` implementations from `ControlFlow`.
- Rename several functions to improve both internal consistency and compliance with Rust API guidelines. - 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. - 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) # Version 0.19.1 (2019-04-08)
@ -46,6 +72,9 @@
- On Windows, fix `CursorMoved(0, 0)` getting dispatched on window focus. - On Windows, fix `CursorMoved(0, 0)` getting dispatched on window focus.
- On macOS, fix command key event left and right reverse. - On macOS, fix command key event left and right reverse.
- On FreeBSD, NetBSD, and OpenBSD, fix build of X11 backend. - 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 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. - 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.
@ -64,9 +93,6 @@
- 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, 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, 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. - On Windows, ignore the AltGr key when populating the `ModifersState` type.
- 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
# Version 0.18.1 (2018-12-30) # Version 0.18.1 (2018-12-30)

View file

@ -36,20 +36,7 @@ at least a maintainer of the platform (a maintainer making a PR themselves count
## Maintainers & Testers ## Maintainers & Testers
Winit is managed by several people, each with their specialities, and each maintaining a subset of the The current [list of testers and contributors](https://github.com/rust-windowing/winit/wiki/Testers-and-Contributors)
backends of winit. As such, depending on your platform of interest, your contacts will be different. can be found on the Wiki.
This table summarizes who can be contacted in which case, with the following legend: If you are interested in contributing or testing on a platform, please add yourself to that table!
- `M` - Maintainer: is a main maintainer for this platform
- `C` - Collaborator: can review code and address issues on this platform
- `T` - Tester: has the ability of testing the platform
- ` `: knows nothing of this platform
| Platform | Windows | macOS | X11 | Wayland | Android | iOS | Emscripten | Stdweb |
| :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| @mitchmindtree | T | | T | T | | | | |
| @Osspial | M | | T | T | T | | T | |
| @vberger | | | T | M | | | | |
| @mtak- | | T | | | T | M | | |
| @ryanisacg | T | T | | | | | | M |

View file

@ -1,12 +1,13 @@
[package] [package]
name = "winit" name = "winit"
version = "0.20.0-alpha1" version = "0.20.0-alpha2"
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"] authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library." description = "Cross-platform window creation library."
edition = "2018"
keywords = ["windowing"] keywords = ["windowing"]
license = "Apache-2.0" license = "Apache-2.0"
readme = "README.md" readme = "README.md"
repository = "https://github.com/tomaka/winit" repository = "https://github.com/rust-windowing/winit"
documentation = "https://docs.rs/winit" documentation = "https://docs.rs/winit"
categories = ["gui"] categories = ["gui"]
@ -23,6 +24,7 @@ lazy_static = "1"
libc = "0.2" libc = "0.2"
log = "0.4" log = "0.4"
serde = { version = "1", optional = true, features = ["serde_derive"] } serde = { version = "1", optional = true, features = ["serde_derive"] }
derivative = "1.0.2"
[dev-dependencies] [dev-dependencies]
image = "0.21" image = "0.21"
@ -41,6 +43,11 @@ core-graphics = "0.17.3"
dispatch = "0.1.4" dispatch = "0.1.4"
objc = "0.2.3" objc = "0.2.3"
[target.'cfg(target_os = "macos")'.dependencies.core-video-sys]
version = "0.1.3"
default_features = false
features = ["display_link"]
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
bitflags = "1" bitflags = "1"

View file

@ -92,6 +92,7 @@ If your PR makes notable changes to Winit's features, please update this section
### System Information ### System Information
- **Monitor list**: Retrieve the list of monitors and their metadata, including which one is primary. - **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 ### Input Handling
- **Mouse events**: Generating mouse events associated with pointer motion, click, and scrolling events. - **Mouse events**: Generating mouse events associated with pointer motion, click, and scrolling events.
@ -160,9 +161,10 @@ Legend:
|Popup windows |❌ |❌ |❌ |❌ |❌ |❌ |❌ | |Popup windows |❌ |❌ |❌ |❌ |❌ |❌ |❌ |
### System information ### System information
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Emscripten| |Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Emscripten|
|------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | |---------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|Monitor list |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** | |Monitor list |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |
|Video mode query |✔️ |✔️ |✔️ |✔️ |❌ |✔️ |❌ |
### Input handling ### Input handling
|Feature |Windows |MacOS |Linux x11|Linux Wayland|Android|iOS |Emscripten| |Feature |Windows |MacOS |Linux x11|Linux Wayland|Android|iOS |Emscripten|
@ -192,18 +194,18 @@ Changes in the API that have been agreed upon but aren't implemented across all
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Emscripten| |Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Emscripten|
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | |------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
[#165]: https://github.com/tomaka/winit/issues/165 [#165]: https://github.com/rust-windowing/winit/issues/165
[#219]: https://github.com/tomaka/winit/issues/219 [#219]: https://github.com/rust-windowing/winit/issues/219
[#242]: https://github.com/tomaka/winit/issues/242 [#242]: https://github.com/rust-windowing/winit/issues/242
[#306]: https://github.com/tomaka/winit/issues/306 [#306]: https://github.com/rust-windowing/winit/issues/306
[#315]: https://github.com/tomaka/winit/issues/315 [#315]: https://github.com/rust-windowing/winit/issues/315
[#319]: https://github.com/tomaka/winit/issues/319 [#319]: https://github.com/rust-windowing/winit/issues/319
[#33]: https://github.com/tomaka/winit/issues/33 [#33]: https://github.com/rust-windowing/winit/issues/33
[#459]: https://github.com/tomaka/winit/issues/459 [#459]: https://github.com/rust-windowing/winit/issues/459
[#5]: https://github.com/tomaka/winit/issues/5 [#5]: https://github.com/rust-windowing/winit/issues/5
[#63]: https://github.com/tomaka/winit/issues/63 [#63]: https://github.com/rust-windowing/winit/issues/63
[#720]: https://github.com/tomaka/winit/issues/720 [#720]: https://github.com/rust-windowing/winit/issues/720
[#721]: https://github.com/tomaka/winit/issues/721 [#721]: https://github.com/rust-windowing/winit/issues/721
[#750]: https://github.com/tomaka/winit/issues/750 [#750]: https://github.com/rust-windowing/winit/issues/750
[#804]: https://github.com/tomaka/winit/issues/804 [#804]: https://github.com/rust-windowing/winit/issues/804
[#812]: https://github.com/tomaka/winit/issues/812 [#812]: https://github.com/rust-windowing/winit/issues/812

View file

@ -5,7 +5,10 @@ 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 them deeply for their time and efforts, and wish them best of luck in their
future endeavors: future endeavors:
* @tomaka: For creating the Winit project and guiding it through its early * [@tomaka]: For creating the Winit project and guiding it through its early
years of existence. years of existence.
* @francesca64: For taking over the responsibility of maintaining almost every * [@francesca64]: For taking over the responsibility of maintaining almost every
Winit backend, and standardizing HiDPI support across all of them Winit backend, and standardizing HiDPI support across all of them
[@tomaka]: https://github.com/tomaka
[@francesca64]: https://github.com/francesca64

View file

@ -1,17 +1,21 @@
# winit - Cross-platform window creation and management in Rust # winit - Cross-platform window creation and management in Rust
[![](http://meritbadge.herokuapp.com/winit)](https://crates.io/crates/winit) [![Crates.io](https://img.shields.io/crates/v/winit.svg)](https://crates.io/crates/winit)
[![Docs.rs](https://docs.rs/winit/badge.svg)](https://docs.rs/winit) [![Docs.rs](https://docs.rs/winit/badge.svg)](https://docs.rs/winit)
[![Build Status](https://travis-ci.org/rust-windowing/winit.svg?branch=master)](https://travis-ci.org/rust-windowing/winit) [![Build Status](https://travis-ci.org/rust-windowing/winit.svg?branch=master)](https://travis-ci.org/rust-windowing/winit)
[![Build status](https://ci.appveyor.com/api/projects/status/hr89but4x1n3dphq/branch/master?svg=true)](https://ci.appveyor.com/project/Osspial/winit/branch/master) [![Build status](https://ci.appveyor.com/api/projects/status/hr89but4x1n3dphq/branch/master?svg=true)](https://ci.appveyor.com/project/Osspial/winit/branch/master)
```toml ```toml
[dependencies] [dependencies]
winit = "0.19.1" winit = "0.20.0-alpha2"
``` ```
## [Documentation](https://docs.rs/winit) ## [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 ## Contact Us
Join us in any of these: Join us in any of these:
@ -31,19 +35,23 @@ show something on the window you need to use the platform-specific getters provi
another library. another library.
```rust ```rust
extern crate winit; use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
fn main() { fn main() {
let mut event_loop = winit::EventLoop::new(); let event_loop = EventLoop::new();
let window = winit::Window::new(&event_loop).unwrap(); let window = WindowBuilder::new().build(&event_loop).unwrap();
event_loop.run(|event| { event_loop.run(move |event, _, control_flow| {
match event { match event {
winit::Event::WindowEvent { Event::WindowEvent {
event: winit::WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
.. window_id,
} => winit::ControlFlow::Break, } if window_id == window.id() => *control_flow = ControlFlow::Exit,
_ => winit::ControlFlow::Continue, _ => *control_flow = ControlFlow::Wait,
} }
}); });
} }

View file

@ -1,8 +1,8 @@
extern crate winit; use winit::{
event::{ElementState, Event, KeyboardInput, WindowEvent},
use winit::window::{WindowBuilder, CursorIcon}; event_loop::{ControlFlow, EventLoop},
use winit::event::{Event, WindowEvent, ElementState, KeyboardInput}; window::{CursorIcon, WindowBuilder},
use winit::event_loop::{EventLoop, ControlFlow}; };
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
@ -12,37 +12,72 @@ fn main() {
let mut cursor_idx = 0; let mut cursor_idx = 0;
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| match event {
match event { Event::WindowEvent {
Event::WindowEvent { event: WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. }, .. } => { event:
println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]); WindowEvent::KeyboardInput {
window.set_cursor_icon(CURSORS[cursor_idx]); input:
if cursor_idx < CURSORS.len() - 1 { KeyboardInput {
cursor_idx += 1; state: ElementState::Pressed,
} else { ..
cursor_idx = 0; },
} ..
}, },
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { ..
*control_flow = ControlFlow::Exit; } => {
return; println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]);
}, window.set_cursor_icon(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;
}
_ => (),
}); });
} }
const CURSORS: &[CursorIcon] = &[ const CURSORS: &[CursorIcon] = &[
CursorIcon::Default, CursorIcon::Crosshair, CursorIcon::Hand, CursorIcon::Default,
CursorIcon::Arrow, CursorIcon::Move, CursorIcon::Text, CursorIcon::Crosshair,
CursorIcon::Wait, CursorIcon::Help, CursorIcon::Progress, CursorIcon::Hand,
CursorIcon::NotAllowed, CursorIcon::ContextMenu, CursorIcon::Cell, CursorIcon::Arrow,
CursorIcon::VerticalText, CursorIcon::Alias, CursorIcon::Copy, CursorIcon::Move,
CursorIcon::NoDrop, CursorIcon::Grab, CursorIcon::Grabbing, CursorIcon::Text,
CursorIcon::AllScroll, CursorIcon::ZoomIn, CursorIcon::ZoomOut, CursorIcon::Wait,
CursorIcon::EResize, CursorIcon::NResize, CursorIcon::NeResize, CursorIcon::Help,
CursorIcon::NwResize, CursorIcon::SResize, CursorIcon::SeResize, CursorIcon::Progress,
CursorIcon::SwResize, CursorIcon::WResize, CursorIcon::EwResize, CursorIcon::NotAllowed,
CursorIcon::NsResize, CursorIcon::NeswResize, CursorIcon::NwseResize, CursorIcon::ContextMenu,
CursorIcon::ColResize, CursorIcon::RowResize 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,8 +1,8 @@
extern crate winit; use winit::{
event::{DeviceEvent, ElementState, Event, KeyboardInput, WindowEvent},
use winit::window::WindowBuilder; event_loop::{ControlFlow, EventLoop},
use winit::event::{Event, WindowEvent, ElementState, KeyboardInput}; window::WindowBuilder,
use winit::event_loop::{EventLoop, ControlFlow}; };
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
@ -14,16 +14,17 @@ fn main() {
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait; *control_flow = ControlFlow::Wait;
if let Event::WindowEvent { event, .. } = event { match event {
match event { Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
input: KeyboardInput { input:
state: ElementState::Released, KeyboardInput {
virtual_keycode: Some(key), state: ElementState::Released,
modifiers, virtual_keycode: Some(key),
.. modifiers,
}, ..
},
.. ..
} => { } => {
use winit::event::VirtualKeyCode::*; use winit::event::VirtualKeyCode::*;
@ -35,7 +36,16 @@ fn main() {
} }
} }
_ => (), _ => (),
} },
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,10 +1,10 @@
extern crate winit;
use std::io::{self, Write}; use std::io::{self, Write};
use winit::monitor::MonitorHandle; use winit::{
use winit::window::WindowBuilder; event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
use winit::event::{Event, WindowEvent, VirtualKeyCode, ElementState, KeyboardInput}; event_loop::{ControlFlow, EventLoop},
use winit::event_loop::{EventLoop, ControlFlow}; monitor::MonitorHandle,
window::WindowBuilder,
};
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
@ -91,7 +91,10 @@ fn main() {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
{ {
use winit::platform::macos::WindowExtMacOS; use winit::platform::macos::WindowExtMacOS;
println!("window.simple_fullscreen {:?}", WindowExtMacOS::simple_fullscreen(&window)); println!(
"window.simple_fullscreen {:?}",
WindowExtMacOS::simple_fullscreen(&window)
);
} }
} }
(VirtualKeyCode::M, ElementState::Pressed) => { (VirtualKeyCode::M, ElementState::Pressed) => {
@ -123,7 +126,10 @@ fn prompt_for_monitor(event_loop: &EventLoop<()>) -> MonitorHandle {
let mut num = String::new(); let mut num = String::new();
io::stdin().read_line(&mut num).unwrap(); io::stdin().read_line(&mut num).unwrap();
let num = num.trim().parse().ok().expect("Please enter a number"); 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"); let monitor = event_loop
.available_monitors()
.nth(num)
.expect("Please enter a valid ID");
println!("Using {:?}", monitor.name()); println!("Using {:?}", monitor.name());

View file

@ -1,8 +1,8 @@
extern crate winit; use winit::{
event::{Event, KeyboardInput, WindowEvent},
use winit::window::WindowBuilder; event_loop::{ControlFlow, EventLoop},
use winit::event::{Event, WindowEvent, KeyboardInput}; window::WindowBuilder,
use winit::event_loop::{EventLoop, ControlFlow}; };
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
@ -15,61 +15,67 @@ fn main() {
let mut close_requested = false; let mut close_requested = false;
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
use winit::event::ElementState::Released; use winit::event::{
use winit::event::VirtualKeyCode::{N, Y}; ElementState::Released,
VirtualKeyCode::{N, Y},
};
*control_flow = ControlFlow::Wait; *control_flow = ControlFlow::Wait;
match event { match event {
Event::WindowEvent { event, .. } => match event { Event::WindowEvent { event, .. } => {
WindowEvent::CloseRequested => { match event {
// `CloseRequested` is sent when the close button on the window is pressed (or WindowEvent::CloseRequested => {
// through whatever other mechanisms the window manager provides for closing a // `CloseRequested` is sent when the close button on the window is pressed (or
// window). If you don't handle this event, the close button won't actually do // through whatever other mechanisms the window manager provides for closing a
// anything. // 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. // 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 // 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. // example, so here we'll just respond to the Y and N keys.
println!("Are you ready to bid your window farewell? [Y/N]"); println!("Are you ready to bid your window farewell? [Y/N]");
close_requested = true; close_requested = true;
// In applications where you can safely close the window without further // In applications where you can safely close the window without further
// action from the user, this is generally where you'd handle cleanup before // 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 // closing the window. How to close the window is detailed in the handler for
// the Y key. // 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 => { WindowEvent::KeyboardInput {
if close_requested { input:
println!("Your window will continue to stay by your side."); KeyboardInput {
close_requested = false; 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;
}
}
_ => (),
} }
} }
_ => (), _ => (),
}, }
_ => (), }
},
_ => (), _ => (),
} }
}); });

View file

@ -1,16 +1,14 @@
extern crate winit; use winit::{
dpi::LogicalSize,
use winit::dpi::LogicalSize; event::{Event, WindowEvent},
use winit::window::WindowBuilder; event_loop::{ControlFlow, EventLoop},
use winit::event::{Event, WindowEvent}; window::WindowBuilder,
use winit::event_loop::{EventLoop, ControlFlow}; };
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new().build(&event_loop).unwrap();
.build(&event_loop)
.unwrap();
window.set_min_inner_size(Some(LogicalSize::new(400.0, 200.0))); window.set_min_inner_size(Some(LogicalSize::new(400.0, 200.0)));
window.set_max_inner_size(Some(LogicalSize::new(800.0, 400.0))); window.set_max_inner_size(Some(LogicalSize::new(800.0, 400.0)));
@ -19,8 +17,10 @@ fn main() {
println!("{:?}", event); println!("{:?}", event);
match event { match event {
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => Event::WindowEvent {
*control_flow = ControlFlow::Exit, event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => *control_flow = ControlFlow::Wait, _ => *control_flow = ControlFlow::Wait,
} }
}); });

View file

@ -1,9 +1,9 @@
extern crate winit; use winit::{event_loop::EventLoop, window::WindowBuilder};
use winit::event_loop::EventLoop;
use winit::window::WindowBuilder;
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap(); let window = WindowBuilder::new().build(&event_loop).unwrap();
println!("{:#?}\nPrimary: {:#?}", window.available_monitors(), window.primary_monitor());
dbg!(window.available_monitors());
dbg!(window.primary_monitor());
} }

View file

@ -1,11 +1,10 @@
extern crate env_logger; extern crate env_logger;
extern crate winit;
use std::{collections::HashMap, sync::mpsc, thread, time::Duration}; use std::{collections::HashMap, sync::mpsc, thread, time::Duration};
use winit::{ use winit::{
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::{ControlFlow, EventLoop}, window::{CursorIcon, WindowBuilder}, event_loop::{ControlFlow, EventLoop},
window::{CursorIcon, WindowBuilder},
}; };
const WINDOW_COUNT: usize = 3; const WINDOW_COUNT: usize = 3;
@ -25,12 +24,16 @@ fn main() {
thread::spawn(move || { thread::spawn(move || {
while let Ok(event) = rx.recv() { while let Ok(event) = rx.recv() {
match event { match event {
WindowEvent::KeyboardInput { input: KeyboardInput { WindowEvent::KeyboardInput {
state: ElementState::Released, input:
virtual_keycode: Some(key), KeyboardInput {
modifiers, state: ElementState::Released,
virtual_keycode: Some(key),
modifiers,
..
},
.. ..
}, .. } => { } => {
window.set_title(&format!("{:?}", key)); window.set_title(&format!("{:?}", key));
let state = !modifiers.shift; let state = !modifiers.shift;
use self::VirtualKeyCode::*; use self::VirtualKeyCode::*;
@ -53,7 +56,7 @@ fn main() {
println!("-> inner_position : {:?}", window.inner_position()); println!("-> inner_position : {:?}", window.inner_position());
println!("-> outer_size : {:?}", window.outer_size()); println!("-> outer_size : {:?}", window.outer_size());
println!("-> inner_size : {:?}", window.inner_size()); println!("-> inner_size : {:?}", window.inner_size());
}, }
L => window.set_min_inner_size(match state { L => window.set_min_inner_size(match state {
true => Some(WINDOW_SIZE.into()), true => Some(WINDOW_SIZE.into()),
false => None, false => None,
@ -68,22 +71,26 @@ fn main() {
}), }),
Q => window.request_redraw(), Q => window.request_redraw(),
R => window.set_resizable(state), R => window.set_resizable(state),
S => window.set_inner_size(match state { S => window.set_inner_size(
true => (WINDOW_SIZE.0 + 100, WINDOW_SIZE.1 + 100), match state {
false => WINDOW_SIZE, true => (WINDOW_SIZE.0 + 100, WINDOW_SIZE.1 + 100),
}.into()), false => WINDOW_SIZE,
W => window.set_cursor_position(( }
WINDOW_SIZE.0 as i32 / 2, .into(),
WINDOW_SIZE.1 as i32 / 2, ),
).into()).unwrap(), W => window
.set_cursor_position(
(WINDOW_SIZE.0 as i32 / 2, WINDOW_SIZE.1 as i32 / 2).into(),
)
.unwrap(),
Z => { Z => {
window.set_visible(false); window.set_visible(false);
thread::sleep(Duration::from_secs(1)); thread::sleep(Duration::from_secs(1));
window.set_visible(true); window.set_visible(true);
}, }
_ => (), _ => (),
} }
}, }
_ => (), _ => (),
} }
} }
@ -95,20 +102,25 @@ fn main() {
false => ControlFlow::Exit, false => ControlFlow::Exit,
}; };
match event { match event {
Event::WindowEvent { event, window_id } => { Event::WindowEvent { event, window_id } => match event {
match event { WindowEvent::CloseRequested
WindowEvent::CloseRequested | WindowEvent::Destroyed
| WindowEvent::Destroyed | WindowEvent::KeyboardInput {
| WindowEvent::KeyboardInput { input: KeyboardInput { input:
virtual_keycode: Some(VirtualKeyCode::Escape), KeyboardInput {
.. }, .. } => { virtual_keycode: Some(VirtualKeyCode::Escape),
window_senders.remove(&window_id); ..
}, },
_ => if let Some(tx) = window_senders.get(&window_id) { ..
tx.send(event).unwrap(); } => {
}, window_senders.remove(&window_id);
} }
} _ => {
if let Some(tx) = window_senders.get(&window_id) {
tx.send(event).unwrap();
}
}
},
_ => (), _ => (),
} }
}) })

View file

@ -1,9 +1,9 @@
extern crate winit;
use std::collections::HashMap; use std::collections::HashMap;
use winit::window::Window; use winit::{
use winit::event::{Event, WindowEvent, ElementState, KeyboardInput}; event::{ElementState, Event, KeyboardInput, WindowEvent},
use winit::event_loop::{EventLoop, ControlFlow}; event_loop::{ControlFlow, EventLoop},
window::Window,
};
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
@ -28,12 +28,19 @@ fn main() {
if windows.is_empty() { if windows.is_empty() {
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;
} }
}, }
WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. } => { WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
..
},
..
} => {
let window = Window::new(&event_loop).unwrap(); let window = Window::new(&event_loop).unwrap();
windows.insert(window.id(), window); windows.insert(window.id(), window);
}, }
_ => () _ => (),
} }
} }
_ => (), _ => (),

View file

@ -1,7 +1,8 @@
extern crate winit; use winit::{
use winit::window::WindowBuilder; event::{Event, WindowEvent},
use winit::event::{Event, WindowEvent}; event_loop::{ControlFlow, EventLoop},
use winit::event_loop::{EventLoop, ControlFlow}; window::WindowBuilder,
};
fn main() { fn main() {
let event_loop: EventLoop<i32> = EventLoop::new_user_event(); let event_loop: EventLoop<i32> = EventLoop::new_user_event();
@ -26,8 +27,10 @@ fn main() {
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
println!("{:?}", event); println!("{:?}", event);
match event { match event {
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => Event::WindowEvent {
*control_flow = ControlFlow::Exit, event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => *control_flow = ControlFlow::Wait, _ => *control_flow = ControlFlow::Wait,
} }
}); });

View file

@ -1,9 +1,10 @@
extern crate winit; use std::time::{Duration, Instant};
use std::time::{Instant, Duration};
use winit::window::WindowBuilder; use winit::{
use winit::event::{Event, WindowEvent}; event::{Event, WindowEvent},
use winit::event_loop::{EventLoop, ControlFlow}; event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
@ -13,19 +14,21 @@ fn main() {
.build(&event_loop) .build(&event_loop)
.unwrap(); .unwrap();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| match event {
println!("{:?}", event); Event::WindowEvent {
event: WindowEvent::CloseRequested,
match event { ..
Event::WindowEvent { } => *control_flow = ControlFlow::Exit,
event: WindowEvent::CloseRequested, Event::EventsCleared => {
.. window.request_redraw();
} => *control_flow = ControlFlow::Exit, *control_flow = ControlFlow::WaitUntil(Instant::now() + Duration::new(1, 0))
Event::EventsCleared => {
window.request_redraw();
*control_flow = ControlFlow::WaitUntil(Instant::now() + Duration::new(1, 0))
},
_ => ()
} }
Event::WindowEvent {
event: WindowEvent::RedrawRequested,
..
} => {
println!("{:?}", event);
}
_ => (),
}); });
} }

View file

@ -1,7 +1,8 @@
extern crate winit; use winit::{
use winit::window::WindowBuilder; event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
use winit::event::{Event, WindowEvent, VirtualKeyCode, ElementState, KeyboardInput}; event_loop::{ControlFlow, EventLoop},
use winit::event_loop::{EventLoop, ControlFlow}; window::WindowBuilder,
};
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();

View file

@ -1,8 +1,9 @@
extern crate winit;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use winit::window::WindowBuilder; use winit::{
use winit::event::{Event, WindowEvent, StartCause}; event::{Event, StartCause, WindowEvent},
use winit::event_loop::{EventLoop, ControlFlow}; event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
@ -12,22 +13,24 @@ fn main() {
.build(&event_loop) .build(&event_loop)
.unwrap(); .unwrap();
let timer_length = Duration::new(1, 0);
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
println!("{:?}", event); println!("{:?}", event);
match event { match event {
Event::NewEvents(StartCause::Init) => Event::NewEvents(StartCause::Init) => {
*control_flow = ControlFlow::WaitUntil(Instant::now() + Duration::new(1, 0)), *control_flow = ControlFlow::WaitUntil(Instant::now() + timer_length)
Event::NewEvents(StartCause::ResumeTimeReached{..}) => { }
*control_flow = ControlFlow::WaitUntil(Instant::now() + Duration::new(1, 0)); Event::NewEvents(StartCause::ResumeTimeReached { .. }) => {
*control_flow = ControlFlow::WaitUntil(Instant::now() + timer_length);
println!("\nTimer\n"); println!("\nTimer\n");
_window.set_inner_size(winit::dpi::LogicalSize::new(300.0, 300.0)); }
},
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
_ => () _ => (),
} }
}); });
} }

View file

@ -1,14 +1,17 @@
extern crate winit; use winit::{
use winit::window::WindowBuilder; event::{Event, WindowEvent},
use winit::event::{Event, WindowEvent}; event_loop::{ControlFlow, EventLoop},
use winit::event_loop::{EventLoop, ControlFlow}; window::WindowBuilder,
};
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new().with_decorations(false) let window = WindowBuilder::new()
.with_transparency(true) .with_decorations(false)
.build(&event_loop).unwrap(); .with_transparent(true)
.build(&event_loop)
.unwrap();
window.set_title("A fantastic window!"); window.set_title("A fantastic window!");
@ -16,8 +19,10 @@ fn main() {
println!("{:?}", event); println!("{:?}", event);
match event { match event {
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => Event::WindowEvent {
*control_flow = ControlFlow::Exit, event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => *control_flow = ControlFlow::Wait, _ => *control_flow = ControlFlow::Wait,
} }
}); });

12
examples/video_modes.rs Normal file
View file

@ -0,0 +1,12 @@
use winit::event_loop::EventLoop;
fn main() {
let event_loop = EventLoop::new();
let monitor = event_loop.primary_monitor();
println!("Listing available video modes:");
for mode in monitor.video_modes() {
println!("{:?}", mode);
}
}

View file

@ -1,38 +1,26 @@
extern crate winit; use winit::{
#[cfg(feature = "stdweb")] event::{Event, WindowEvent},
#[macro_use] event_loop::{ControlFlow, EventLoop},
extern crate stdweb; window::WindowBuilder,
#[cfg(feature = "wasm-bindgen")] };
extern crate wasm_bindgen;
#[cfg(feature = "wasm-bindgen")]
extern crate web_sys;
use winit::window::WindowBuilder; fn main() {
use winit::event::{Event, WindowEvent};
use winit::event_loop::{EventLoop, ControlFlow};
use wasm_bindgen::{prelude::*, JsValue};
use web_sys::console;
#[wasm_bindgen(start)]
pub fn main() {
console::log_1(&JsValue::from_str("main"));
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let _window = WindowBuilder::new() let window = WindowBuilder::new()
.with_title("A fantastic window!") .with_title("A fantastic window!")
.build(&event_loop) .build(&event_loop)
.unwrap(); .unwrap();
console::log_1(&JsValue::from_str("Created window"));
event_loop.run(|event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
console::log_1(&JsValue::from_str(&format!("{:?}", event))); println!("{:?}", event);
match event { match event {
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
.. window_id,
} => *control_flow = ControlFlow::Exit, } if window_id == window.id() => *control_flow = ControlFlow::Exit,
_ => () _ => *control_flow = ControlFlow::Wait,
} }
}); });
} }

View file

@ -1,30 +1,19 @@
extern crate winit;
extern crate image; extern crate image;
use std::path::Path; use std::path::Path;
use winit::window::{WindowBuilder, Icon}; use winit::{
use winit::event::Event; event::Event,
use winit::event_loop::{EventLoop, ControlFlow}; event_loop::{ControlFlow, EventLoop},
window::{Icon, WindowBuilder},
};
fn main() { fn main() {
// You'll have to choose an icon size at your own discretion. On X11, the desired size varies // 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, // 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 // 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. // you'll be bitten by the low-quality downscaling built into the WM.
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/icon.png"); let path = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/icon.png");
let (icon_rgba, icon_width, icon_height) = { let icon = load_icon(Path::new(path));
let image = image::open(path).expect("Failed to open icon path");
use image::{GenericImageView, Pixel};
let (width, height) = image.dimensions();
let mut rgba = Vec::with_capacity((width * height) as usize * 4);
for (_, _, pixel) in image.pixels() {
rgba.extend_from_slice(&pixel.to_rgba().data);
}
(rgba, width, height)
};
let icon = Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon");
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
@ -43,10 +32,8 @@ fn main() {
match event { match event {
CloseRequested => *control_flow = ControlFlow::Exit, CloseRequested => *control_flow = ControlFlow::Exit,
DroppedFile(path) => { DroppedFile(path) => {
use image::GenericImageView;
window.set_window_icon(Some(load_icon(&path))); window.set_window_icon(Some(load_icon(&path)));
}, }
_ => (), _ => (),
} }
} }

View file

@ -1,9 +1,9 @@
extern crate winit; use winit::{
event::{Event, WindowEvent},
use winit::window::WindowBuilder; event_loop::{ControlFlow, EventLoop},
use winit::event::{Event, WindowEvent}; platform::desktop::EventLoopExtDesktop,
use winit::event_loop::{EventLoop, ControlFlow}; window::WindowBuilder,
use winit::platform::desktop::EventLoopExtDesktop; };
fn main() { fn main() {
let mut event_loop = EventLoop::new(); let mut event_loop = EventLoop::new();
@ -14,14 +14,12 @@ fn main() {
.unwrap(); .unwrap();
println!("Close the window to continue."); println!("Close the window to continue.");
event_loop.run_return(|event, _, control_flow| { event_loop.run_return(|event, _, control_flow| match event {
match event { Event::WindowEvent {
Event::WindowEvent { event: WindowEvent::CloseRequested,
event: WindowEvent::CloseRequested, ..
.. } => *control_flow = ControlFlow::Exit,
} => *control_flow = ControlFlow::Exit, _ => *control_flow = ControlFlow::Wait,
_ => *control_flow = ControlFlow::Wait,
}
}); });
drop(window); drop(window);
@ -31,14 +29,12 @@ fn main() {
.unwrap(); .unwrap();
println!("Wa ha ha! You thought that closing the window would finish this?!"); println!("Wa ha ha! You thought that closing the window would finish this?!");
event_loop.run_return(|event, _, control_flow| { event_loop.run_return(|event, _, control_flow| match event {
match event { Event::WindowEvent {
Event::WindowEvent { event: WindowEvent::CloseRequested,
event: WindowEvent::CloseRequested, ..
.. } => *control_flow = ControlFlow::Exit,
} => *control_flow = ControlFlow::Exit, _ => *control_flow = ControlFlow::Wait,
_ => *control_flow = ControlFlow::Wait,
}
}); });
println!("Okay we're done now for real."); println!("Okay we're done now for real.");

3
rustfmt.toml Normal file
View file

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

View file

@ -1,4 +1,3 @@
//! DPI is important, so read the docs for this module if you don't want to be confused. //! DPI is important, so read the docs for this module if you don't want to be confused.
//! //!
//! Originally, `winit` dealt entirely in physical pixels (excluding unintentional inconsistencies), but now all //! Originally, `winit` dealt entirely in physical pixels (excluding unintentional inconsistencies), but now all

View file

@ -1,7 +1,6 @@
use std::fmt; use std::{error, fmt};
use std::error;
use platform_impl; use crate::platform_impl;
/// An error whose cause it outside Winit's control. /// An error whose cause it outside Winit's control.
#[derive(Debug)] #[derive(Debug)]
@ -30,20 +29,14 @@ impl NotSupportedError {
#[inline] #[inline]
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn new() -> NotSupportedError { pub(crate) fn new() -> NotSupportedError {
NotSupportedError { NotSupportedError { _marker: () }
_marker: ()
}
} }
} }
impl OsError { impl OsError {
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn new(line: u32, file: &'static str, error: platform_impl::OsError) -> OsError { pub(crate) fn new(line: u32, file: &'static str, error: platform_impl::OsError) -> OsError {
OsError { OsError { line, file, error }
line,
file,
error,
}
} }
} }
@ -51,33 +44,36 @@ impl OsError {
macro_rules! os_error { macro_rules! os_error {
($error:expr) => {{ ($error:expr) => {{
crate::error::OsError::new(line!(), file!(), $error) crate::error::OsError::new(line!(), file!(), $error)
}} }};
} }
impl fmt::Display for OsError { impl fmt::Display for OsError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
formatter.pad(&format!("os error at {}:{}: {}", self.file, self.line, self.error)) f.pad(&format!(
"os error at {}:{}: {}",
self.file, self.line, self.error
))
} }
} }
impl fmt::Display for ExternalError { impl fmt::Display for ExternalError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self { match self {
ExternalError::NotSupported(e) => e.fmt(formatter), ExternalError::NotSupported(e) => e.fmt(f),
ExternalError::Os(e) => e.fmt(formatter), ExternalError::Os(e) => e.fmt(f),
} }
} }
} }
impl fmt::Debug for NotSupportedError { impl fmt::Debug for NotSupportedError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
formatter.debug_struct("NotSupportedError").finish() f.debug_struct("NotSupportedError").finish()
} }
} }
impl fmt::Display for NotSupportedError { impl fmt::Display for NotSupportedError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
formatter.pad("the requested operation is not supported by Winit") f.pad("the requested operation is not supported by Winit")
} }
} }

View file

@ -7,9 +7,11 @@
use instant::Instant; use instant::Instant;
use std::path::PathBuf; use std::path::PathBuf;
use dpi::{LogicalPosition, LogicalSize}; use crate::{
use window::WindowId; dpi::{LogicalPosition, LogicalSize},
use platform_impl; platform_impl,
window::WindowId,
};
/// Describes a generic event. /// Describes a generic event.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -36,10 +38,11 @@ pub enum Event<T> {
/// emitted, it is guaranteed to be the last event emitted. /// emitted, it is guaranteed to be the last event emitted.
LoopDestroyed, LoopDestroyed,
/// Emitted when the application has been suspended or resumed. /// Emitted when the application has been suspended.
/// Suspended,
/// The parameter is true if app was suspended, and false if it has been resumed.
Suspended(bool), /// Emitted when the application has been resumed.
Resumed,
} }
impl<T> Event<T> { impl<T> Event<T> {
@ -47,12 +50,13 @@ impl<T> Event<T> {
use self::Event::*; use self::Event::*;
match self { match self {
UserEvent(_) => Err(self), UserEvent(_) => Err(self),
WindowEvent{window_id, event} => Ok(WindowEvent{window_id, event}), WindowEvent { window_id, event } => Ok(WindowEvent { window_id, event }),
DeviceEvent{device_id, event} => Ok(DeviceEvent{device_id, event}), DeviceEvent { device_id, event } => Ok(DeviceEvent { device_id, event }),
NewEvents(cause) => Ok(NewEvents(cause)), NewEvents(cause) => Ok(NewEvents(cause)),
EventsCleared => Ok(EventsCleared), EventsCleared => Ok(EventsCleared),
LoopDestroyed => Ok(LoopDestroyed), LoopDestroyed => Ok(LoopDestroyed),
Suspended(suspended) => Ok(Suspended(suspended)), Suspended => Ok(Suspended),
Resumed => Ok(Resumed),
} }
} }
} }
@ -65,14 +69,14 @@ pub enum StartCause {
/// guaranteed to be equal to or after the requested resume time. /// guaranteed to be equal to or after the requested resume time.
ResumeTimeReached { ResumeTimeReached {
start: Instant, start: Instant,
requested_resume: Instant requested_resume: Instant,
}, },
/// Sent if the OS has new events to send to the window, after a wait was requested. Contains /// Sent if the OS has new events to send to the window, after a wait was requested. Contains
/// the moment the wait was requested and the resume time, if requested. /// the moment the wait was requested and the resume time, if requested.
WaitCancelled { WaitCancelled {
start: Instant, start: Instant,
requested_resume: Option<Instant> requested_resume: Option<Instant>,
}, },
/// Sent if the event loop is being resumed after the loop's control flow was set to /// Sent if the event loop is being resumed after the loop's control flow was set to
@ -80,7 +84,7 @@ pub enum StartCause {
Poll, Poll,
/// Sent once, immediately after `run` is called. Indicates that the loop was just initialized. /// Sent once, immediately after `run` is called. Indicates that the loop was just initialized.
Init Init,
} }
/// Describes an event from a `Window`. /// Describes an event from a `Window`.
@ -125,7 +129,10 @@ pub enum WindowEvent {
Focused(bool), Focused(bool),
/// An event from the keyboard has been received. /// An event from the keyboard has been received.
KeyboardInput { device_id: DeviceId, input: KeyboardInput }, KeyboardInput {
device_id: DeviceId,
input: KeyboardInput,
},
/// The cursor has moved on the window. /// The cursor has moved on the window.
CursorMoved { CursorMoved {
@ -135,7 +142,7 @@ pub enum WindowEvent {
/// limited by the display area and it may have been transformed by the OS to implement effects such as cursor /// limited by the display area and it may have been transformed by the OS to implement effects such as cursor
/// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control. /// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control.
position: LogicalPosition, position: LogicalPosition,
modifiers: ModifiersState modifiers: ModifiersState,
}, },
/// The cursor has entered the window. /// The cursor has entered the window.
@ -145,21 +152,38 @@ pub enum WindowEvent {
CursorLeft { device_id: DeviceId }, CursorLeft { device_id: DeviceId },
/// A mouse wheel movement or touchpad scroll occurred. /// A mouse wheel movement or touchpad scroll occurred.
MouseWheel { device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase, modifiers: ModifiersState }, MouseWheel {
device_id: DeviceId,
delta: MouseScrollDelta,
phase: TouchPhase,
modifiers: ModifiersState,
},
/// An mouse button press has been received. /// An mouse button press has been received.
MouseInput { device_id: DeviceId, state: ElementState, button: MouseButton, modifiers: ModifiersState }, MouseInput {
device_id: DeviceId,
state: ElementState,
button: MouseButton,
modifiers: ModifiersState,
},
/// Touchpad pressure event. /// Touchpad pressure event.
/// ///
/// At the moment, only supported on Apple forcetouch-capable macbooks. /// 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 /// 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). /// is being pressed) and stage (integer representing the click level).
TouchpadPressure { device_id: DeviceId, pressure: f32, stage: i64 }, TouchpadPressure {
device_id: DeviceId,
pressure: f32,
stage: i64,
},
/// Motion on some analog axis. May report data redundant to other, more specific events. /// Motion on some analog axis. May report data redundant to other, more specific events.
AxisMotion { device_id: DeviceId, axis: AxisId, value: f64 }, AxisMotion {
device_id: DeviceId,
axis: AxisId,
value: f64,
},
/// The OS or application has requested that the window be redrawn. /// The OS or application has requested that the window be redrawn.
RedrawRequested, RedrawRequested,
@ -175,7 +199,7 @@ pub enum WindowEvent {
/// * Changing the display's DPI factor (e.g. in Control Panel on Windows). /// * Changing the display's DPI factor (e.g. in Control Panel on Windows).
/// * Moving the window to a display with a different DPI factor. /// * Moving the window to a display with a different DPI factor.
/// ///
/// For more information about DPI in general, see the [`dpi`](dpi/index.html) module. /// For more information about DPI in general, see the [`dpi`](../dpi/index.html) module.
HiDpiFactorChanged(f64), HiDpiFactorChanged(f64),
} }
@ -229,11 +253,19 @@ pub enum DeviceEvent {
/// Motion on some analog axis. This event will be reported for all arbitrary input devices /// Motion on some analog axis. This event will be reported for all arbitrary input devices
/// that winit supports on this platform, including mouse devices. If the device is a mouse /// that winit supports on this platform, including mouse devices. If the device is a mouse
/// device then this will be reported alongside the MouseMotion event. /// device then this will be reported alongside the MouseMotion event.
Motion { axis: AxisId, value: f64 }, Motion {
axis: AxisId,
value: f64,
},
Button { button: ButtonId, state: ElementState }, Button {
button: ButtonId,
state: ElementState,
},
Key(KeyboardInput), Key(KeyboardInput),
Text { codepoint: char }, Text {
codepoint: char,
},
} }
/// Describes a keyboard input event. /// Describes a keyboard input event.
@ -259,7 +291,7 @@ pub struct KeyboardInput {
/// ///
/// This is tracked internally to avoid tracking errors arising from modifier key state changes when events from /// This is tracked internally to avoid tracking errors arising from modifier key state changes when events from
/// this device are not being delivered to the application, e.g. due to keyboard focus being elsewhere. /// this device are not being delivered to the application, e.g. due to keyboard focus being elsewhere.
pub modifiers: ModifiersState pub modifiers: ModifiersState,
} }
/// Describes touch-screen input state. /// Describes touch-screen input state.
@ -269,7 +301,7 @@ pub enum TouchPhase {
Started, Started,
Moved, Moved,
Ended, Ended,
Cancelled Cancelled,
} }
/// Represents touch event /// Represents touch event
@ -293,7 +325,7 @@ pub struct Touch {
pub phase: TouchPhase, pub phase: TouchPhase,
pub location: LogicalPosition, pub location: LogicalPosition,
/// unique identifier of a finger. /// unique identifier of a finger.
pub id: u64 pub id: u64,
} }
/// Hardware-dependent keyboard scan code. /// Hardware-dependent keyboard scan code.
@ -327,19 +359,19 @@ pub enum MouseButton {
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum MouseScrollDelta { pub enum MouseScrollDelta {
/// Amount in lines or rows to scroll in the horizontal /// Amount in lines or rows to scroll in the horizontal
/// and vertical directions. /// and vertical directions.
/// ///
/// Positive values indicate movement forward /// Positive values indicate movement forward
/// (away from the user) or rightwards. /// (away from the user) or rightwards.
LineDelta(f32, f32), LineDelta(f32, f32),
/// Amount in pixels to scroll in the horizontal and /// Amount in pixels to scroll in the horizontal and
/// vertical direction. /// vertical direction.
/// ///
/// Scroll events are expressed as a PixelDelta if /// Scroll events are expressed as a PixelDelta if
/// supported by the device (eg. a touchpad) and /// supported by the device (eg. a touchpad) and
/// platform. /// platform.
PixelDelta(LogicalPosition), PixelDelta(LogicalPosition),
} }
/// Symbolic name for a keyboard key. /// Symbolic name for a keyboard key.
@ -499,7 +531,7 @@ pub enum VirtualKeyCode {
Multiply, Multiply,
Mute, Mute,
MyComputer, MyComputer,
NavigateForward, // also called "Prior" NavigateForward, // also called "Prior"
NavigateBackward, // also called "Next" NavigateBackward, // also called "Next"
NextTrack, NextTrack,
NoConvert, NoConvert,
@ -557,5 +589,5 @@ pub struct ModifiersState {
/// The "logo" key /// The "logo" key
/// ///
/// This is the "windows" key on PC and "command" key on Mac. /// This is the "windows" key on PC and "command" key on Mac.
pub logo: bool pub logo: bool,
} }

View file

@ -22,14 +22,14 @@ use platform_impl;
/// ///
/// An `EventLoop` can be seen more or less as a "context". Calling `EventLoop::new()` /// 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 /// initializes everything that will be required to create windows. For example on Linux creating
/// an events loop opens a connection to the X or Wayland server. /// 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. /// 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 /// 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 /// 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 /// `Window` created from this `EventLoop` _can_ be sent to an other thread, and the
/// `EventLoopProxy` allows you to wake up an `EventLoop` from an other thread. /// `EventLoopProxy` allows you to wake up an `EventLoop` from another thread.
pub struct EventLoop<T: 'static> { pub struct EventLoop<T: 'static> {
pub(crate) event_loop: platform_impl::EventLoop<T>, pub(crate) event_loop: platform_impl::EventLoop<T>,
pub(crate) _marker: ::std::marker::PhantomData<*mut ()>, // Not Send nor Sync pub(crate) _marker: ::std::marker::PhantomData<*mut ()>, // Not Send nor Sync
@ -46,14 +46,14 @@ pub struct EventLoopWindowTarget<T: 'static> {
} }
impl<T> fmt::Debug for EventLoop<T> { impl<T> fmt::Debug for EventLoop<T> {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmtr.pad("EventLoop { .. }") f.pad("EventLoop { .. }")
} }
} }
impl<T> fmt::Debug for EventLoopWindowTarget<T> { impl<T> fmt::Debug for EventLoopWindowTarget<T> {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmtr.pad("EventLoopWindowTarget { .. }") f.pad("EventLoopWindowTarget { .. }")
} }
} }
@ -121,7 +121,7 @@ impl<T> EventLoop<T> {
} }
} }
/// Hijacks the calling thread and initializes the `winit` event loop with the provided /// 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 /// closure. Since the closure is `'static`, it must be a `move` closure if it needs to
/// access any data from the calling context. /// access any data from the calling context.
/// ///
@ -147,10 +147,8 @@ impl<T> EventLoop<T> {
} }
/// Returns the list of all the monitors available on the system. /// Returns the list of all the monitors available on the system.
///
// Note: should be replaced with `-> impl Iterator` once stable.
#[inline] #[inline]
pub fn available_monitors(&self) -> AvailableMonitorsIter { pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
let data = self.event_loop.available_monitors(); let data = self.event_loop.available_monitors();
AvailableMonitorsIter { AvailableMonitorsIter {
data: data.into_iter(), data: data.into_iter(),
@ -191,8 +189,8 @@ impl<T: 'static> EventLoopProxy<T> {
} }
impl<T: 'static> fmt::Debug for EventLoopProxy<T> { impl<T: 'static> fmt::Debug for EventLoopProxy<T> {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmtr.pad("EventLoopProxy { .. }") f.pad("EventLoopProxy { .. }")
} }
} }
@ -202,7 +200,7 @@ impl<T: 'static> fmt::Debug for EventLoopProxy<T> {
pub struct EventLoopClosed; pub struct EventLoopClosed;
impl fmt::Display for EventLoopClosed { impl fmt::Display for EventLoopClosed {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", error::Error::description(self)) write!(f, "{}", error::Error::description(self))
} }
} }

View file

@ -1,5 +1,4 @@
use std::{fmt, mem}; use std::{error::Error, fmt, mem};
use std::error::Error;
#[repr(C)] #[repr(C)]
#[derive(Debug)] #[derive(Debug)]
@ -17,9 +16,7 @@ pub(crate) const PIXEL_SIZE: usize = mem::size_of::<Pixel>();
pub enum BadIcon { pub enum BadIcon {
/// Produced when the length of the `rgba` argument isn't divisible by 4, thus `rgba` can't be /// Produced when the length of the `rgba` argument isn't divisible by 4, thus `rgba` can't be
/// safely interpreted as 32bpp RGBA pixels. /// safely interpreted as 32bpp RGBA pixels.
ByteCountNotDivisibleBy4 { ByteCountNotDivisibleBy4 { byte_count: usize },
byte_count: usize,
},
/// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`. /// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`.
/// At least one of your arguments is incorrect. /// At least one of your arguments is incorrect.
DimensionsVsPixelCount { DimensionsVsPixelCount {
@ -31,7 +28,7 @@ pub enum BadIcon {
} }
impl fmt::Display for BadIcon { impl fmt::Display for BadIcon {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let msg = match self { let msg = match self {
&BadIcon::ByteCountNotDivisibleBy4 { byte_count } => format!( &BadIcon::ByteCountNotDivisibleBy4 { byte_count } => format!(
"The length of the `rgba` argument ({:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels.", "The length of the `rgba` argument ({:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels.",
@ -47,7 +44,7 @@ impl fmt::Display for BadIcon {
width, height, pixel_count, width_x_height, width, height, pixel_count, width_x_height,
), ),
}; };
write!(formatter, "{}", msg) write!(f, "{}", msg)
} }
} }
@ -56,7 +53,7 @@ impl Error for BadIcon {
"A valid icon cannot be created from these arguments" "A valid icon cannot be created from these arguments"
} }
fn cause(&self) -> Option<&Error> { fn cause(&self) -> Option<&dyn Error> {
Some(self) Some(self)
} }
} }
@ -76,7 +73,9 @@ impl Icon {
/// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error. /// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error.
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> { pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
if rgba.len() % PIXEL_SIZE != 0 { if rgba.len() % PIXEL_SIZE != 0 {
return Err(BadIcon::ByteCountNotDivisibleBy4 { byte_count: rgba.len() }); return Err(BadIcon::ByteCountNotDivisibleBy4 {
byte_count: rgba.len(),
});
} }
let pixel_count = rgba.len() / PIXEL_SIZE; let pixel_count = rgba.len() / PIXEL_SIZE;
if pixel_count != (width * height) as usize { if pixel_count != (width * height) as usize {
@ -87,7 +86,11 @@ impl Icon {
pixel_count, pixel_count,
}) })
} else { } else {
Ok(Icon { rgba, width, height }) Ok(Icon {
rgba,
width,
height,
})
} }
} }
} }

View file

@ -22,29 +22,62 @@
//! //!
//! # Event handling //! # Event handling
//! //!
//! Once a [`Window`] has been created, it will *generate events*. For example whenever the user moves //! Once a [`Window`] has been created, it will generate different *events*. A [`Window`] object can
//! the [`Window`], resizes the [`Window`], moves the mouse, etc. an event is generated. //! generate a [`WindowEvent`] when certain things happen, like whenever the user moves their mouse
//! or presses a key inside the [`Window`]. Devices can generate a [`DeviceEvent`] directly as well,
//! which contains 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.
//! //!
//! The events generated by a [`Window`] can be retreived from the [`EventLoop`] the [`Window`] was created //! Events can be retreived by using an [`EventLoop`]. A [`Window`] will send its events to the
//! with. //! [`EventLoop`] object it was created with.
//! //!
//! You do this by calling [`event_loop.run(...)`][event_loop_run]. This function will run forever //! You do this by calling [`event_loop.run(...)`][event_loop_run]. This function will run forever
//! unless `control_flow` is set to [`ControlFlow`]`::`[`Exit`], at which point [`Event`]`::`[`LoopDestroyed`] //! unless `control_flow` is set to [`ControlFlow`]`::`[`Exit`], at which point [`Event`]`::`[`LoopDestroyed`]
//! is emitted and the entire program terminates. //! is emitted and the entire program terminates.
//! //!
//! ```no_run //! ```no_run
//! use winit::event_loop::ControlFlow; //! use winit::{
//! use winit::event::{Event, WindowEvent}; //! event::{Event, WindowEvent},
//! # use winit::event_loop::EventLoop; //! event_loop::{ControlFlow, EventLoop},
//! # let event_loop = EventLoop::new(); //! window::WindowBuilder,
//! };
//!
//! let event_loop = EventLoop::new();
//! let window = WindowBuilder::new().build(&event_loop).unwrap();
//! //!
//! event_loop.run(move |event, _, control_flow| { //! event_loop.run(move |event, _, control_flow| {
//! match event { //! match event {
//! Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { //! Event::EventsCleared => {
//! // Application update code.
//!
//! // Queue a RedrawRequested event.
//! window.request_redraw();
//! },
//! Event::WindowEvent {
//! event: WindowEvent::RedrawRequested,
//! ..
//! } => {
//! // Redraw the application.
//! //
//! // It's preferrable to render in this event rather than in EventsCleared, since
//! // rendering in here allows the program to gracefully handle redraws requested
//! // by the OS.
//! },
//! Event::WindowEvent {
//! event: WindowEvent::CloseRequested,
//! ..
//! } => {
//! println!("The close button was pressed; stopping"); //! println!("The close button was pressed; stopping");
//! *control_flow = ControlFlow::Exit //! *control_flow = ControlFlow::Exit
//! }, //! },
//! _ => *control_flow = ControlFlow::Wait, //! // 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,
//! // 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,
//! } //! }
//! }); //! });
//! ``` //! ```
@ -71,85 +104,34 @@
//! [window_builder_build]: ./window/struct.WindowBuilder.html#method.build //! [window_builder_build]: ./window/struct.WindowBuilder.html#method.build
//! [window_id_fn]: ./window/struct.Window.html#method.id //! [window_id_fn]: ./window/struct.Window.html#method.id
//! [`Event`]: ./event/enum.Event.html //! [`Event`]: ./event/enum.Event.html
//! [`WindowEvent`]: ./event/enum.Event.html#variant.WindowEvent //! [`WindowEvent`]: ./event/enum.WindowEvent.html
//! [`DeviceEvent`]: ./event/enum.DeviceEvent.html
//! [`UserEvent`]: ./event/enum.Event.html#variant.UserEvent
//! [`LoopDestroyed`]: ./event/enum.Event.html#variant.LoopDestroyed //! [`LoopDestroyed`]: ./event/enum.Event.html#variant.LoopDestroyed
//! [`platform`]: ./platform/index.html //! [`platform`]: ./platform/index.html
extern crate instant; #![deny(rust_2018_idioms)]
#[allow(unused_imports)] #[allow(unused_imports)]
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
extern crate libc;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
#[macro_use] #[macro_use]
extern crate serde; extern crate serde;
#[macro_use]
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
extern crate winapi; extern crate derivative;
#[macro_use] #[macro_use]
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
extern crate bitflags; extern crate bitflags;
#[cfg(any(target_os = "macos", target_os = "ios"))] #[cfg(any(target_os = "macos", target_os = "ios"))]
#[macro_use] #[macro_use]
extern crate objc; extern crate objc;
#[cfg(target_os = "macos")]
extern crate cocoa;
#[cfg(target_os = "macos")]
extern crate core_foundation;
#[cfg(target_os = "macos")]
extern crate core_graphics;
#[cfg(target_os = "macos")]
extern crate dispatch;
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "windows"
))]
extern crate parking_lot;
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
extern crate percent_encoding;
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
extern crate smithay_client_toolkit as sctk;
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
extern crate x11_dl;
#[cfg(feature = "std_web")] #[cfg(feature = "std_web")]
#[macro_use] #[macro_use]
extern crate std_web as stdweb; extern crate std_web as stdweb;
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
extern crate calloop;
#[cfg(feature = "wasm-bindgen")]
extern crate wasm_bindgen;
#[cfg(feature = "web-sys")]
extern crate web_sys;
pub mod dpi; pub mod dpi;
#[macro_use] #[macro_use]

View file

@ -12,8 +12,10 @@
//! [window_get]: ../window/struct.Window.html#method.available_monitors //! [window_get]: ../window/struct.Window.html#method.available_monitors
use std::collections::vec_deque::IntoIter as VecDequeIter; use std::collections::vec_deque::IntoIter as VecDequeIter;
use platform_impl; use crate::{
use dpi::{PhysicalPosition, PhysicalSize}; dpi::{PhysicalPosition, PhysicalSize},
platform_impl,
};
/// An iterator over all available monitors. /// An iterator over all available monitors.
/// ///
@ -44,6 +46,45 @@ impl Iterator for AvailableMonitorsIter {
} }
} }
/// Describes a fullscreen video mode of a monitor.
///
/// Can be acquired with:
/// - [`MonitorHandle::video_modes`][monitor_get].
///
/// [monitor_get]: ../monitor/struct.MonitorHandle.html#method.video_modes
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct VideoMode {
pub(crate) size: (u32, u32),
pub(crate) bit_depth: u16,
pub(crate) refresh_rate: u16,
}
impl VideoMode {
/// Returns the resolution of this video mode.
pub fn size(&self) -> PhysicalSize {
self.size.into()
}
/// 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.
pub fn bit_depth(&self) -> u16 {
self.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.
pub fn refresh_rate(&self) -> u16 {
self.refresh_rate
}
}
/// Handle to a monitor. /// Handle to a monitor.
/// ///
/// Allows you to retrieve information about a given monitor and can be used in [`Window`] creation. /// Allows you to retrieve information about a given monitor and can be used in [`Window`] creation.
@ -51,7 +92,7 @@ impl Iterator for AvailableMonitorsIter {
/// [`Window`]: ../window/struct.Window.html /// [`Window`]: ../window/struct.Window.html
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct MonitorHandle { pub struct MonitorHandle {
pub(crate) inner: platform_impl::MonitorHandle pub(crate) inner: platform_impl::MonitorHandle,
} }
impl MonitorHandle { impl MonitorHandle {
@ -65,8 +106,8 @@ impl MonitorHandle {
/// Returns the monitor's resolution. /// Returns the monitor's resolution.
#[inline] #[inline]
pub fn dimensions(&self) -> PhysicalSize { pub fn size(&self) -> PhysicalSize {
self.inner.dimensions() self.inner.size()
} }
/// Returns the top-left corner position of the monitor relative to the larger full /// Returns the top-left corner position of the monitor relative to the larger full
@ -78,7 +119,7 @@ impl MonitorHandle {
/// Returns the DPI factor that can be used to map logical pixels to physical pixels, and vice versa. /// Returns the DPI factor that can be used to map logical pixels to physical pixels, and vice versa.
/// ///
/// See the [`dpi`](dpi/index.html) module for more information. /// See the [`dpi`](../dpi/index.html) module for more information.
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
@ -88,4 +129,10 @@ impl MonitorHandle {
pub fn hidpi_factor(&self) -> f64 { pub fn hidpi_factor(&self) -> f64 {
self.inner.hidpi_factor() self.inner.hidpi_factor()
} }
/// Returns all fullscreen video modes supported by this monitor.
#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.inner.video_modes()
}
} }

View file

@ -1,18 +1,16 @@
#![cfg(any(target_os = "android"))] #![cfg(any(target_os = "android"))]
use crate::{EventLoop, Window, WindowBuilder};
use std::os::raw::c_void; use std::os::raw::c_void;
use EventLoop;
use Window;
use WindowBuilder;
/// Additional methods on `EventLoop` that are specific to Android. /// Additional methods on `EventLoop` that are specific to Android.
pub trait EventLoopExtAndroid { pub trait EventLoopExtAndroid {
/// Makes it possible for glutin to register a callback when a suspend event happens on Android /// Makes it possible for glutin to register a callback when a suspend event happens on Android
fn set_suspend_callback(&self, cb: Option<Box<Fn(bool) -> ()>>); fn set_suspend_callback(&self, cb: Option<Box<dyn Fn(bool) -> ()>>);
} }
impl EventLoopExtAndroid for EventLoop { impl EventLoopExtAndroid for EventLoop {
fn set_suspend_callback(&self, cb: Option<Box<Fn(bool) -> ()>>) { fn set_suspend_callback(&self, cb: Option<Box<dyn Fn(bool) -> ()>>) {
self.event_loop.set_suspend_callback(cb); self.event_loop.set_suspend_callback(cb);
} }
} }
@ -30,9 +28,6 @@ impl WindowExtAndroid for Window {
} }
/// Additional methods on `WindowBuilder` that are specific to Android. /// Additional methods on `WindowBuilder` that are specific to Android.
pub trait WindowBuilderExtAndroid { pub trait WindowBuilderExtAndroid {}
} impl WindowBuilderExtAndroid for WindowBuilder {}
impl WindowBuilderExtAndroid for WindowBuilder {
}

View file

@ -4,8 +4,10 @@
target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd" target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"
))] ))]
use event::Event; use crate::{
use event_loop::{EventLoop, EventLoopWindowTarget, ControlFlow}; event::Event,
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
};
/// Additional methods on `EventLoop` that are specific to desktop platforms. /// Additional methods on `EventLoop` that are specific to desktop platforms.
pub trait EventLoopExtDesktop { pub trait EventLoopExtDesktop {
@ -16,15 +18,27 @@ pub trait EventLoopExtDesktop {
/// ///
/// Unlike `run`, this function accepts non-`'static` (i.e. non-`move`) closures and returns /// 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`. /// control flow to the caller when `control_flow` is set to `ControlFlow::Exit`.
///
/// # Caveats
/// Despite its apperance 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
/// underyling OS APIs, which cannot be hidden by Winit without severe stability reprecussions.
///
/// You are strongly encouraged to use `run`, unless the use of this is absolutely necessary.
fn run_return<F>(&mut self, event_handler: F) fn run_return<F>(&mut self, event_handler: F)
where F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow); where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow);
} }
impl<T> EventLoopExtDesktop for EventLoop<T> { impl<T> EventLoopExtDesktop for EventLoop<T> {
type UserEvent = T; type UserEvent = T;
fn run_return<F>(&mut self, event_handler: F) fn run_return<F>(&mut self, event_handler: F)
where F: FnMut(Event<T>, &EventLoopWindowTarget<T>, &mut ControlFlow) where
F: FnMut(Event<T>, &EventLoopWindowTarget<T>, &mut ControlFlow),
{ {
self.event_loop.run_return(event_handler) self.event_loop.run_return(event_handler)
} }

View file

@ -2,9 +2,11 @@
use std::os::raw::c_void; use std::os::raw::c_void;
use event_loop::EventLoop; use crate::{
use monitor::MonitorHandle; event_loop::EventLoop,
use window::{Window, WindowBuilder}; monitor::MonitorHandle,
window::{Window, WindowBuilder},
};
/// Additional methods on `EventLoop` that are specific to iOS. /// Additional methods on `EventLoop` that are specific to iOS.
pub trait EventLoopExtIOS { pub trait EventLoopExtIOS {

View file

@ -2,29 +2,47 @@
use std::os::raw::c_void; use std::os::raw::c_void;
use crate::dpi::LogicalSize; use crate::{
use crate::monitor::MonitorHandle; dpi::LogicalSize,
use crate::window::{Window, WindowBuilder}; monitor::MonitorHandle,
window::{Window, WindowBuilder},
};
/// Corresponds to `NSRequestUserAttentionType`.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum RequestUserAttentionType {
/// Corresponds to `NSCriticalRequest`.
///
/// Dock icon will bounce until the application is focused.
Critical,
/// Corresponds to `NSInformationalRequest`.
///
/// Dock icon will bounce once.
Informational,
}
impl Default for RequestUserAttentionType {
fn default() -> Self {
RequestUserAttentionType::Critical
}
}
/// Additional methods on `Window` that are specific to MacOS. /// Additional methods on `Window` that are specific to MacOS.
pub trait WindowExtMacOS { pub trait WindowExtMacOS {
/// Returns a pointer to the cocoa `NSWindow` that is used by this window. /// Returns a pointer to the cocoa `NSWindow` that is used by this window.
/// ///
/// The pointer will become invalid when the `Window` is destroyed. /// The pointer will become invalid when the `Window` is destroyed.
fn nswindow(&self) -> *mut c_void; fn ns_window(&self) -> *mut c_void;
/// Returns a pointer to the cocoa `NSView` that is used by this window. /// Returns a pointer to the cocoa `NSView` that is used by this window.
/// ///
/// The pointer will become invalid when the `Window` is destroyed. /// The pointer will become invalid when the `Window` is destroyed.
fn nsview(&self) -> *mut c_void; fn ns_view(&self) -> *mut c_void;
/// Request user attention, causing the application's dock icon to bounce. /// Request user attention, causing the application's dock icon to bounce.
/// Note that this has no effect if the application is already focused. /// Note that this has no effect if the application is already focused.
/// fn request_user_attention(&self, request_type: RequestUserAttentionType);
/// The `is_critical` flag has the following effects:
/// - `false`: the dock icon will only bounce once.
/// - `true`: the dock icon will bounce until the application is focused.
fn request_user_attention(&self, is_critical: bool);
/// Returns whether or not the window is in simple fullscreen mode. /// Returns whether or not the window is in simple fullscreen mode.
fn simple_fullscreen(&self) -> bool; fn simple_fullscreen(&self) -> bool;
@ -41,18 +59,18 @@ pub trait WindowExtMacOS {
impl WindowExtMacOS for Window { impl WindowExtMacOS for Window {
#[inline] #[inline]
fn nswindow(&self) -> *mut c_void { fn ns_window(&self) -> *mut c_void {
self.window.nswindow() self.window.ns_window()
} }
#[inline] #[inline]
fn nsview(&self) -> *mut c_void { fn ns_view(&self) -> *mut c_void {
self.window.nsview() self.window.ns_view()
} }
#[inline] #[inline]
fn request_user_attention(&self, is_critical: bool) { fn request_user_attention(&self, request_type: RequestUserAttentionType) {
self.window.request_user_attention(is_critical) self.window.request_user_attention(request_type)
} }
#[inline] #[inline]
@ -97,7 +115,8 @@ pub trait WindowBuilderExtMacOS {
/// Sets the activation policy for the window being built. /// Sets the activation policy for the window being built.
fn with_activation_policy(self, activation_policy: ActivationPolicy) -> WindowBuilder; fn with_activation_policy(self, activation_policy: ActivationPolicy) -> WindowBuilder;
/// Enables click-and-drag behavior for the entire window, not just the titlebar. /// 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; 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. /// Makes the titlebar transparent and allows the content to appear behind it.
fn with_titlebar_transparent(self, titlebar_transparent: bool) -> WindowBuilder; fn with_titlebar_transparent(self, titlebar_transparent: bool) -> WindowBuilder;
/// Hides the window title. /// Hides the window title.
@ -120,7 +139,10 @@ impl WindowBuilderExtMacOS for WindowBuilder {
} }
#[inline] #[inline]
fn with_movable_by_window_background(mut self, movable_by_window_background: bool) -> WindowBuilder { 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.platform_specific.movable_by_window_background = movable_by_window_background;
self self
} }
@ -167,7 +189,7 @@ pub trait MonitorHandleExtMacOS {
/// Returns the identifier of the monitor for Cocoa. /// Returns the identifier of the monitor for Cocoa.
fn native_id(&self) -> u32; fn native_id(&self) -> u32;
/// Returns a pointer to the NSScreen representing this monitor. /// Returns a pointer to the NSScreen representing this monitor.
fn nsscreen(&self) -> Option<*mut c_void>; fn ns_screen(&self) -> Option<*mut c_void>;
} }
impl MonitorHandleExtMacOS for MonitorHandle { impl MonitorHandleExtMacOS for MonitorHandle {
@ -176,7 +198,7 @@ impl MonitorHandleExtMacOS for MonitorHandle {
self.inner.native_identifier() self.inner.native_identifier()
} }
fn nsscreen(&self) -> Option<*mut c_void> { fn ns_screen(&self) -> Option<*mut c_void> {
self.inner.nsscreen().map(|s| s as *mut c_void) self.inner.ns_screen().map(|s| s as *mut c_void)
} }
} }

View file

@ -1,29 +1,26 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] #![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
use std::os::raw; use std::{os::raw, ptr, sync::Arc};
use std::ptr;
use std::sync::Arc;
use sctk::window::{ButtonState, Theme}; use smithay_client_toolkit::window::{ButtonState, Theme};
use dpi::LogicalSize; use crate::{
use event_loop::EventLoop; dpi::LogicalSize,
use monitor::MonitorHandle; event_loop::EventLoop,
use window::{Window, WindowBuilder}; monitor::MonitorHandle,
window::{Window, WindowBuilder},
use platform_impl::{ };
EventLoop as LinuxEventLoop,
Window as LinuxWindow, use crate::platform_impl::{
x11::{ffi::XVisualInfo, XConnection},
EventLoop as LinuxEventLoop, Window as LinuxWindow,
}; };
use platform_impl::x11::XConnection;
use platform_impl::x11::ffi::XVisualInfo;
// TODO: stupid hack so that glutin can do its work // TODO: stupid hack so that glutin can do its work
#[doc(hidden)] #[doc(hidden)]
pub use platform_impl::x11; pub use crate::platform_impl::x11;
pub use platform_impl::XNotSupported; pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupported};
pub use platform_impl::x11::util::WindowType as XWindowType;
/// Theme for wayland client side decorations /// Theme for wayland client side decorations
/// ///
@ -97,11 +94,13 @@ impl Theme for WaylandThemeObject {
pub trait EventLoopExtUnix { pub trait EventLoopExtUnix {
/// Builds a new `EventLoops` that is forced to use X11. /// Builds a new `EventLoops` that is forced to use X11.
fn new_x11() -> Result<Self, XNotSupported> fn new_x11() -> Result<Self, XNotSupported>
where Self: Sized; where
Self: Sized;
/// Builds a new `EventLoop` that is forced to use Wayland. /// Builds a new `EventLoop` that is forced to use Wayland.
fn new_wayland() -> Self fn new_wayland() -> Self
where Self: Sized; where
Self: Sized;
/// True if the `EventLoop` uses Wayland. /// True if the `EventLoop` uses Wayland.
fn is_wayland(&self) -> bool; fn is_wayland(&self) -> bool;
@ -123,12 +122,10 @@ pub trait EventLoopExtUnix {
impl<T> EventLoopExtUnix for EventLoop<T> { impl<T> EventLoopExtUnix for EventLoop<T> {
#[inline] #[inline]
fn new_x11() -> Result<Self, XNotSupported> { fn new_x11() -> Result<Self, XNotSupported> {
LinuxEventLoop::new_x11().map(|ev| LinuxEventLoop::new_x11().map(|ev| EventLoop {
EventLoop { event_loop: ev,
event_loop: ev, _marker: ::std::marker::PhantomData,
_marker: ::std::marker::PhantomData, })
}
)
} }
#[inline] #[inline]
@ -136,7 +133,7 @@ impl<T> EventLoopExtUnix for EventLoop<T> {
EventLoop { EventLoop {
event_loop: match LinuxEventLoop::new_wayland() { event_loop: match LinuxEventLoop::new_wayland() {
Ok(e) => e, Ok(e) => e,
Err(_) => panic!() // TODO: propagate Err(_) => panic!(), // TODO: propagate
}, },
_marker: ::std::marker::PhantomData, _marker: ::std::marker::PhantomData,
} }
@ -157,7 +154,7 @@ impl<T> EventLoopExtUnix for EventLoop<T> {
fn xlib_xconnection(&self) -> Option<Arc<XConnection>> { fn xlib_xconnection(&self) -> Option<Arc<XConnection>> {
match self.event_loop { match self.event_loop {
LinuxEventLoop::X(ref e) => Some(e.x_connection().clone()), LinuxEventLoop::X(ref e) => Some(e.x_connection().clone()),
_ => None _ => None,
} }
} }
@ -165,7 +162,7 @@ impl<T> EventLoopExtUnix for EventLoop<T> {
fn wayland_display(&self) -> Option<*mut raw::c_void> { fn wayland_display(&self) -> Option<*mut raw::c_void> {
match self.event_loop { match self.event_loop {
LinuxEventLoop::Wayland(ref e) => Some(e.display().get_display_ptr() as *mut _), LinuxEventLoop::Wayland(ref e) => Some(e.display().get_display_ptr() as *mut _),
_ => None _ => None,
} }
} }
} }
@ -231,7 +228,7 @@ impl WindowExtUnix for Window {
fn xlib_window(&self) -> Option<raw::c_ulong> { fn xlib_window(&self) -> Option<raw::c_ulong> {
match self.window { match self.window {
LinuxWindow::X(ref w) => Some(w.xlib_window()), LinuxWindow::X(ref w) => Some(w.xlib_window()),
_ => None _ => None,
} }
} }
@ -239,7 +236,7 @@ impl WindowExtUnix for Window {
fn xlib_display(&self) -> Option<*mut raw::c_void> { fn xlib_display(&self) -> Option<*mut raw::c_void> {
match self.window { match self.window {
LinuxWindow::X(ref w) => Some(w.xlib_display()), LinuxWindow::X(ref w) => Some(w.xlib_display()),
_ => None _ => None,
} }
} }
@ -247,7 +244,7 @@ impl WindowExtUnix for Window {
fn xlib_screen_id(&self) -> Option<raw::c_int> { fn xlib_screen_id(&self) -> Option<raw::c_int> {
match self.window { match self.window {
LinuxWindow::X(ref w) => Some(w.xlib_screen_id()), LinuxWindow::X(ref w) => Some(w.xlib_screen_id()),
_ => None _ => None,
} }
} }
@ -256,7 +253,7 @@ impl WindowExtUnix for Window {
fn xlib_xconnection(&self) -> Option<Arc<XConnection>> { fn xlib_xconnection(&self) -> Option<Arc<XConnection>> {
match self.window { match self.window {
LinuxWindow::X(ref w) => Some(w.xlib_xconnection()), LinuxWindow::X(ref w) => Some(w.xlib_xconnection()),
_ => None _ => None,
} }
} }
@ -264,7 +261,7 @@ impl WindowExtUnix for Window {
fn xcb_connection(&self) -> Option<*mut raw::c_void> { fn xcb_connection(&self) -> Option<*mut raw::c_void> {
match self.window { match self.window {
LinuxWindow::X(ref w) => Some(w.xcb_connection()), LinuxWindow::X(ref w) => Some(w.xcb_connection()),
_ => None _ => None,
} }
} }
@ -279,7 +276,7 @@ impl WindowExtUnix for Window {
fn wayland_surface(&self) -> Option<*mut raw::c_void> { fn wayland_surface(&self) -> Option<*mut raw::c_void> {
match self.window { match self.window {
LinuxWindow::Wayland(ref w) => Some(w.surface().as_ref().c_ptr() as *mut _), LinuxWindow::Wayland(ref w) => Some(w.surface().as_ref().c_ptr() as *mut _),
_ => None _ => None,
} }
} }
@ -287,7 +284,7 @@ impl WindowExtUnix for Window {
fn wayland_display(&self) -> Option<*mut raw::c_void> { fn wayland_display(&self) -> Option<*mut raw::c_void> {
match self.window { match self.window {
LinuxWindow::Wayland(ref w) => Some(w.display().as_ref().c_ptr() as *mut _), LinuxWindow::Wayland(ref w) => Some(w.display().as_ref().c_ptr() as *mut _),
_ => None _ => None,
} }
} }
@ -334,9 +331,8 @@ pub trait WindowBuilderExtUnix {
impl WindowBuilderExtUnix for WindowBuilder { impl WindowBuilderExtUnix for WindowBuilder {
#[inline] #[inline]
fn with_x11_visual<T>(mut self, visual_infos: *const T) -> WindowBuilder { fn with_x11_visual<T>(mut self, visual_infos: *const T) -> WindowBuilder {
self.platform_specific.visual_infos = Some( self.platform_specific.visual_infos =
unsafe { ptr::read(visual_infos as *const XVisualInfo) } Some(unsafe { ptr::read(visual_infos as *const XVisualInfo) });
);
self self
} }

View file

@ -5,17 +5,21 @@ use std::os::raw::c_void;
use libc; use libc;
use winapi::shared::windef::HWND; use winapi::shared::windef::HWND;
use event::DeviceId; use crate::{
use monitor::MonitorHandle; event::DeviceId,
use event_loop::EventLoop; event_loop::EventLoop,
use window::{Icon, Window, WindowBuilder}; monitor::MonitorHandle,
use platform_impl::EventLoop as WindowsEventLoop; platform_impl::EventLoop as WindowsEventLoop,
window::{Icon, Window, WindowBuilder},
};
/// Additional methods on `EventLoop` that are specific to Windows. /// Additional methods on `EventLoop` that are specific to Windows.
pub trait EventLoopExtWindows { pub trait EventLoopExtWindows {
/// By default, winit on Windows will attempt to enable process-wide DPI awareness. If that's /// 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. /// undesirable, you can create an `EventLoop` using this function instead.
fn new_dpi_unaware() -> Self where Self: Sized; fn new_dpi_unaware() -> Self
where
Self: Sized;
} }
impl<T> EventLoopExtWindows for EventLoop<T> { impl<T> EventLoopExtWindows for EventLoop<T> {

View file

@ -9,32 +9,32 @@ use std::os::raw;
#[link(name = "android")] #[link(name = "android")]
#[link(name = "EGL")] #[link(name = "EGL")]
#[link(name = "GLESv2")] #[link(name = "GLESv2")]
extern {} extern "C" {}
/** /**
* asset_manager.h ** asset_manager.h
*/ **/
pub type AAssetManager = raw::c_void; pub type AAssetManager = raw::c_void;
/** /**
* native_window.h ** native_window.h
*/ **/
pub type ANativeWindow = raw::c_void; pub type ANativeWindow = raw::c_void;
extern { extern "C" {
pub fn ANativeWindow_getHeight(window: *const ANativeWindow) -> libc::int32_t; pub fn ANativeWindow_getHeight(window: *const ANativeWindow) -> libc::int32_t;
pub fn ANativeWindow_getWidth(window: *const ANativeWindow) -> libc::int32_t; pub fn ANativeWindow_getWidth(window: *const ANativeWindow) -> libc::int32_t;
} }
/** /**
* native_activity.h ** native_activity.h
*/ **/
pub type JavaVM = (); pub type JavaVM = ();
pub type JNIEnv = (); pub type JNIEnv = ();
pub type jobject = *const libc::c_void; pub type jobject = *const libc::c_void;
pub type AInputQueue = (); // FIXME: wrong pub type AInputQueue = (); // FIXME: wrong
pub type ARect = (); // FIXME: wrong pub type ARect = (); // FIXME: wrong
#[repr(C)] #[repr(C)]
pub struct ANativeActivity { pub struct ANativeActivity {
@ -52,43 +52,56 @@ pub struct ANativeActivity {
#[repr(C)] #[repr(C)]
pub struct ANativeActivityCallbacks { pub struct ANativeActivityCallbacks {
pub onStart: extern fn(*mut ANativeActivity), pub onStart: extern "C" fn(*mut ANativeActivity),
pub onResume: extern fn(*mut ANativeActivity), pub onResume: extern "C" fn(*mut ANativeActivity),
pub onSaveInstanceState: extern fn(*mut ANativeActivity, *mut libc::size_t), pub onSaveInstanceState: extern "C" fn(*mut ANativeActivity, *mut libc::size_t),
pub onPause: extern fn(*mut ANativeActivity), pub onPause: extern "C" fn(*mut ANativeActivity),
pub onStop: extern fn(*mut ANativeActivity), pub onStop: extern "C" fn(*mut ANativeActivity),
pub onDestroy: extern fn(*mut ANativeActivity), pub onDestroy: extern "C" fn(*mut ANativeActivity),
pub onWindowFocusChanged: extern fn(*mut ANativeActivity, libc::c_int), pub onWindowFocusChanged: extern "C" fn(*mut ANativeActivity, libc::c_int),
pub onNativeWindowCreated: extern fn(*mut ANativeActivity, *const ANativeWindow), pub onNativeWindowCreated: extern "C" fn(*mut ANativeActivity, *const ANativeWindow),
pub onNativeWindowResized: extern fn(*mut ANativeActivity, *const ANativeWindow), pub onNativeWindowResized: extern "C" fn(*mut ANativeActivity, *const ANativeWindow),
pub onNativeWindowRedrawNeeded: extern fn(*mut ANativeActivity, *const ANativeWindow), pub onNativeWindowRedrawNeeded: extern "C" fn(*mut ANativeActivity, *const ANativeWindow),
pub onNativeWindowDestroyed: extern fn(*mut ANativeActivity, *const ANativeWindow), pub onNativeWindowDestroyed: extern "C" fn(*mut ANativeActivity, *const ANativeWindow),
pub onInputQueueCreated: extern fn(*mut ANativeActivity, *mut AInputQueue), pub onInputQueueCreated: extern "C" fn(*mut ANativeActivity, *mut AInputQueue),
pub onInputQueueDestroyed: extern fn(*mut ANativeActivity, *mut AInputQueue), pub onInputQueueDestroyed: extern "C" fn(*mut ANativeActivity, *mut AInputQueue),
pub onContentRectChanged: extern fn(*mut ANativeActivity, *const ARect), pub onContentRectChanged: extern "C" fn(*mut ANativeActivity, *const ARect),
pub onConfigurationChanged: extern fn(*mut ANativeActivity), pub onConfigurationChanged: extern "C" fn(*mut ANativeActivity),
pub onLowMemory: extern fn(*mut ANativeActivity), pub onLowMemory: extern "C" fn(*mut ANativeActivity),
} }
/** /**
* looper.h ** looper.h
*/ **/
pub type ALooper = (); pub type ALooper = ();
#[link(name = "android")] #[link(name = "android")]
extern { extern "C" {
pub fn ALooper_forThread() -> *const ALooper; pub fn ALooper_forThread() -> *const ALooper;
pub fn ALooper_acquire(looper: *const ALooper); pub fn ALooper_acquire(looper: *const ALooper);
pub fn ALooper_release(looper: *const ALooper); pub fn ALooper_release(looper: *const ALooper);
pub fn ALooper_prepare(opts: libc::c_int) -> *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, pub fn ALooper_pollOnce(
outEvents: *mut libc::c_int, outData: *mut *mut libc::c_void) -> libc::c_int; timeoutMillis: libc::c_int,
pub fn ALooper_pollAll(timeoutMillis: libc::c_int, outFd: *mut libc::c_int, outFd: *mut libc::c_int,
outEvents: *mut libc::c_int, outData: *mut *mut libc::c_void) -> 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_wake(looper: *const ALooper);
pub fn ALooper_addFd(looper: *const ALooper, fd: libc::c_int, ident: libc::c_int, pub fn ALooper_addFd(
events: libc::c_int, callback: ALooper_callbackFunc, data: *mut libc::c_void) looper: *const ALooper,
-> libc::c_int; 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 fn ALooper_removeFd(looper: *const ALooper, fd: libc::c_int) -> libc::c_int;
} }
@ -105,4 +118,5 @@ pub const ALOOPER_EVENT_ERROR: libc::c_int = 1 << 2;
pub const ALOOPER_EVENT_HANGUP: libc::c_int = 1 << 3; pub const ALOOPER_EVENT_HANGUP: libc::c_int = 1 << 3;
pub const ALOOPER_EVENT_INVALID: libc::c_int = 1 << 4; 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; pub type ALooper_callbackFunc =
extern "C" fn(libc::c_int, libc::c_int, *mut libc::c_void) -> libc::c_int;

View file

@ -4,34 +4,28 @@ extern crate android_glue;
mod ffi; mod ffi;
use std::cell::RefCell; use std::{
use std::collections::VecDeque; cell::RefCell,
use std::fmt; collections::VecDeque,
use std::os::raw::c_void; fmt,
use std::sync::mpsc::{Receiver, channel}; os::raw::c_void,
sync::mpsc::{channel, Receiver},
};
use { use crate::{
CreationError, error::{ExternalError, NotSupportedError},
Event, events::{Touch, TouchPhase},
LogicalPosition, window::MonitorHandle as RootMonitorHandle,
LogicalSize, CreationError, CursorIcon, Event, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize,
CursorIcon, WindowAttributes, WindowEvent, WindowId as RootWindowId,
PhysicalPosition,
PhysicalSize,
WindowAttributes,
WindowEvent,
WindowId as RootWindowId,
}; };
use CreationError::OsError; use CreationError::OsError;
use error::{ExternalError, NotSupportedError};
use events::{Touch, TouchPhase};
use window::MonitorHandle as RootMonitorHandle;
pub type OsError = std::io::Error; pub type OsError = std::io::Error;
pub struct EventLoop { pub struct EventLoop {
event_rx: Receiver<android_glue::Event>, event_rx: Receiver<android_glue::Event>,
suspend_callback: RefCell<Option<Box<Fn(bool) -> ()>>>, suspend_callback: RefCell<Option<Box<dyn Fn(bool) -> ()>>>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -60,10 +54,11 @@ impl EventLoop {
} }
pub fn poll_events<F>(&mut self, mut callback: F) pub fn poll_events<F>(&mut self, mut callback: F)
where F: FnMut(::Event) where
F: FnMut(::Event),
{ {
while let Ok(event) = self.event_rx.try_recv() { while let Ok(event) = self.event_rx.try_recv() {
let e = match event{ let e = match event {
android_glue::Event::EventMotion(motion) => { android_glue::Event::EventMotion(motion) => {
let dpi_factor = MonitorHandle.hidpi_factor(); let dpi_factor = MonitorHandle.hidpi_factor();
let location = LogicalPosition::from_physical( let location = LogicalPosition::from_physical(
@ -84,37 +79,36 @@ impl EventLoop {
device_id: DEVICE_ID, device_id: DEVICE_ID,
}), }),
}) })
}, }
android_glue::Event::InitWindow => { android_glue::Event::InitWindow => {
// The activity went to foreground. // The activity went to foreground.
if let Some(cb) = self.suspend_callback.borrow().as_ref() { if let Some(cb) = self.suspend_callback.borrow().as_ref() {
(*cb)(false); (*cb)(false);
} }
Some(Event::Suspended(false)) Some(Event::Resumed)
}, }
android_glue::Event::TermWindow => { android_glue::Event::TermWindow => {
// The activity went to background. // The activity went to background.
if let Some(cb) = self.suspend_callback.borrow().as_ref() { if let Some(cb) = self.suspend_callback.borrow().as_ref() {
(*cb)(true); (*cb)(true);
} }
Some(Event::Suspended(true)) Some(Event::Suspended)
}, }
android_glue::Event::WindowResized | android_glue::Event::WindowResized | android_glue::Event::ConfigChanged => {
android_glue::Event::ConfigChanged => {
// Activity Orientation changed or resized. // Activity Orientation changed or resized.
let native_window = unsafe { android_glue::native_window() }; let native_window = unsafe { android_glue::native_window() };
if native_window.is_null() { if native_window.is_null() {
None None
} else { } else {
let dpi_factor = MonitorHandle.hidpi_factor(); let dpi_factor = MonitorHandle.hidpi_factor();
let physical_size = MonitorHandle.dimensions(); let physical_size = MonitorHandle.size();
let size = LogicalSize::from_physical(physical_size, dpi_factor); let size = LogicalSize::from_physical(physical_size, dpi_factor);
Some(Event::WindowEvent { Some(Event::WindowEvent {
window_id: RootWindowId(WindowId), window_id: RootWindowId(WindowId),
event: WindowEvent::Resized(size), event: WindowEvent::Resized(size),
}) })
} }
}, }
android_glue::Event::WindowRedrawNeeded => { android_glue::Event::WindowRedrawNeeded => {
// The activity needs to be redrawn. // The activity needs to be redrawn.
Some(Event::WindowEvent { Some(Event::WindowEvent {
@ -122,26 +116,23 @@ impl EventLoop {
event: WindowEvent::Redraw, event: WindowEvent::Redraw,
}) })
} }
android_glue::Event::Wake => { android_glue::Event::Wake => Some(Event::Awakened),
Some(Event::Awakened) _ => None,
}
_ => {
None
}
}; };
if let Some(event) = e { if let Some(event) = e {
callback(event); callback(event);
} }
}; }
} }
pub fn set_suspend_callback(&self, cb: Option<Box<Fn(bool) -> ()>>) { pub fn set_suspend_callback(&self, cb: Option<Box<dyn Fn(bool) -> ()>>) {
*self.suspend_callback.borrow_mut() = cb; *self.suspend_callback.borrow_mut() = cb;
} }
pub fn run_forever<F>(&mut self, mut callback: F) pub fn run_forever<F>(&mut self, mut callback: F)
where F: FnMut(::Event) -> ::ControlFlow, where
F: FnMut(::Event) -> ::ControlFlow,
{ {
// Yeah that's a very bad implementation. // Yeah that's a very bad implementation.
loop { loop {
@ -196,7 +187,7 @@ pub struct Window {
pub struct MonitorHandle; pub struct MonitorHandle;
impl fmt::Debug for MonitorHandle { impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[derive(Debug)] #[derive(Debug)]
struct MonitorHandle { struct MonitorHandle {
name: Option<String>, name: Option<String>,
@ -207,7 +198,7 @@ impl fmt::Debug for MonitorHandle {
let monitor_id_proxy = MonitorHandle { let monitor_id_proxy = MonitorHandle {
name: self.name(), name: self.name(),
dimensions: self.dimensions(), dimensions: self.size(),
position: self.outer_position(), position: self.outer_position(),
hidpi_factor: self.hidpi_factor(), hidpi_factor: self.hidpi_factor(),
}; };
@ -223,13 +214,14 @@ impl MonitorHandle {
} }
#[inline] #[inline]
pub fn dimensions(&self) -> PhysicalSize { pub fn size(&self) -> PhysicalSize {
unsafe { unsafe {
let window = android_glue::native_window(); let window = android_glue::native_window();
( (
ffi::ANativeWindow_getWidth(window) as f64, ffi::ANativeWindow_getWidth(window) as f64,
ffi::ANativeWindow_getHeight(window) as f64, ffi::ANativeWindow_getHeight(window) as f64,
).into() )
.into()
} }
} }
@ -251,10 +243,11 @@ pub struct PlatformSpecificWindowBuilderAttributes;
pub struct PlatformSpecificHeadlessBuilderAttributes; pub struct PlatformSpecificHeadlessBuilderAttributes;
impl Window { impl Window {
pub fn new(_: &EventLoop, win_attribs: WindowAttributes, pub fn new(
_: PlatformSpecificWindowBuilderAttributes) _: &EventLoop,
-> Result<Window, CreationError> win_attribs: WindowAttributes,
{ _: PlatformSpecificWindowBuilderAttributes,
) -> Result<Window, CreationError> {
let native_window = unsafe { android_glue::native_window() }; let native_window = unsafe { android_glue::native_window() };
if native_window.is_null() { if native_window.is_null() {
return Err(OsError(format!("Android's native window is null"))); return Err(OsError(format!("Android's native window is null")));
@ -325,7 +318,7 @@ impl Window {
None None
} else { } else {
let dpi_factor = self.hidpi_factor(); let dpi_factor = self.hidpi_factor();
let physical_size = self.current_monitor().dimensions(); let physical_size = self.current_monitor().size();
Some(LogicalSize::from_physical(physical_size, dpi_factor)) Some(LogicalSize::from_physical(physical_size, dpi_factor))
} }
} }
@ -406,7 +399,9 @@ impl Window {
#[inline] #[inline]
pub fn current_monitor(&self) -> RootMonitorHandle { pub fn current_monitor(&self) -> RootMonitorHandle {
RootMonitorHandle { inner: MonitorHandle } RootMonitorHandle {
inner: MonitorHandle,
}
} }
#[inline] #[inline]

View file

@ -1,8 +1,8 @@
#![allow(dead_code, non_camel_case_types, non_snake_case)] #![allow(dead_code, non_camel_case_types, non_snake_case)]
use std::os::raw::{c_int, c_char, c_void, c_ulong, c_double, c_long, c_ushort};
#[cfg(test)] #[cfg(test)]
use std::mem; use std::mem;
use std::os::raw::{c_char, c_double, c_int, c_long, c_ulong, c_ushort, c_void};
pub type EM_BOOL = c_int; pub type EM_BOOL = c_int;
pub type EM_UTF8 = c_char; pub type EM_UTF8 = c_char;
@ -71,30 +71,45 @@ pub const DOM_KEY_LOCATION_NUMPAD: c_ulong = 0x03;
pub type em_callback_func = Option<unsafe extern "C" fn()>; pub type em_callback_func = Option<unsafe extern "C" fn()>;
pub type em_key_callback_func = Option<unsafe extern "C" fn( pub type em_key_callback_func = Option<
eventType: c_int, unsafe extern "C" fn(
keyEvent: *const EmscriptenKeyboardEvent, eventType: c_int,
userData: *mut c_void) -> EM_BOOL>; keyEvent: *const EmscriptenKeyboardEvent,
userData: *mut c_void,
) -> EM_BOOL,
>;
pub type em_mouse_callback_func = Option<unsafe extern "C" fn( pub type em_mouse_callback_func = Option<
eventType: c_int, unsafe extern "C" fn(
mouseEvent: *const EmscriptenMouseEvent, eventType: c_int,
userData: *mut c_void) -> EM_BOOL>; mouseEvent: *const EmscriptenMouseEvent,
userData: *mut c_void,
) -> EM_BOOL,
>;
pub type em_pointerlockchange_callback_func = Option<unsafe extern "C" fn( pub type em_pointerlockchange_callback_func = Option<
eventType: c_int, unsafe extern "C" fn(
pointerlockChangeEvent: *const EmscriptenPointerlockChangeEvent, eventType: c_int,
userData: *mut c_void) -> EM_BOOL>; pointerlockChangeEvent: *const EmscriptenPointerlockChangeEvent,
userData: *mut c_void,
) -> EM_BOOL,
>;
pub type em_fullscreenchange_callback_func = Option<unsafe extern "C" fn( pub type em_fullscreenchange_callback_func = Option<
eventType: c_int, unsafe extern "C" fn(
fullscreenChangeEvent: *const EmscriptenFullscreenChangeEvent, eventType: c_int,
userData: *mut c_void) -> EM_BOOL>; fullscreenChangeEvent: *const EmscriptenFullscreenChangeEvent,
userData: *mut c_void,
) -> EM_BOOL,
>;
pub type em_touch_callback_func = Option<unsafe extern "C" fn( pub type em_touch_callback_func = Option<
eventType: c_int, unsafe extern "C" fn(
touchEvent: *const EmscriptenTouchEvent, eventType: c_int,
userData: *mut c_void) -> EM_BOOL>; touchEvent: *const EmscriptenTouchEvent,
userData: *mut c_void,
) -> EM_BOOL,
>;
#[repr(C)] #[repr(C)]
pub struct EmscriptenFullscreenChangeEvent { pub struct EmscriptenFullscreenChangeEvent {
@ -136,7 +151,9 @@ fn bindgen_test_layout_EmscriptenKeyboardEvent() {
assert_eq!(mem::align_of::<EmscriptenKeyboardEvent>(), 8usize); assert_eq!(mem::align_of::<EmscriptenKeyboardEvent>(), 8usize);
} }
impl Clone for EmscriptenKeyboardEvent { impl Clone for EmscriptenKeyboardEvent {
fn clone(&self) -> Self { *self } fn clone(&self) -> Self {
*self
}
} }
#[repr(C)] #[repr(C)]
@ -219,96 +236,128 @@ fn bindgen_test_layout_EmscriptenPointerlockChangeEvent() {
} }
extern "C" { extern "C" {
pub fn emscripten_set_canvas_size( pub fn emscripten_set_canvas_size(width: c_int, height: c_int) -> EMSCRIPTEN_RESULT;
width: c_int, height: c_int)
-> EMSCRIPTEN_RESULT;
pub fn emscripten_get_canvas_size( pub fn emscripten_get_canvas_size(
width: *mut c_int, height: *mut c_int, width: *mut c_int,
is_fullscreen: *mut c_int) height: *mut c_int,
-> EMSCRIPTEN_RESULT; is_fullscreen: *mut c_int,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_element_css_size( pub fn emscripten_set_element_css_size(
target: *const c_char, width: c_double, target: *const c_char,
height: c_double) -> EMSCRIPTEN_RESULT; width: c_double,
height: c_double,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_get_element_css_size( pub fn emscripten_get_element_css_size(
target: *const c_char, width: *mut c_double, target: *const c_char,
height: *mut c_double) -> EMSCRIPTEN_RESULT; width: *mut c_double,
height: *mut c_double,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_request_pointerlock( pub fn emscripten_request_pointerlock(
target: *const c_char, deferUntilInEventHandler: EM_BOOL) target: *const c_char,
-> EMSCRIPTEN_RESULT; deferUntilInEventHandler: EM_BOOL,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_exit_pointerlock() -> EMSCRIPTEN_RESULT; pub fn emscripten_exit_pointerlock() -> EMSCRIPTEN_RESULT;
pub fn emscripten_request_fullscreen( pub fn emscripten_request_fullscreen(
target: *const c_char, deferUntilInEventHandler: EM_BOOL) target: *const c_char,
-> EMSCRIPTEN_RESULT; deferUntilInEventHandler: EM_BOOL,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_exit_fullscreen() -> EMSCRIPTEN_RESULT; pub fn emscripten_exit_fullscreen() -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_keydown_callback( pub fn emscripten_set_keydown_callback(
target: *const c_char, userData: *mut c_void, target: *const c_char,
useCapture: EM_BOOL, callback: em_key_callback_func) userData: *mut c_void,
-> EMSCRIPTEN_RESULT; useCapture: EM_BOOL,
callback: em_key_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_keyup_callback( pub fn emscripten_set_keyup_callback(
target: *const c_char, userData: *mut c_void, target: *const c_char,
useCapture: EM_BOOL, callback: em_key_callback_func) userData: *mut c_void,
-> EMSCRIPTEN_RESULT; useCapture: EM_BOOL,
callback: em_key_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_mousemove_callback( pub fn emscripten_set_mousemove_callback(
target: *const c_char, user_data: *mut c_void, target: *const c_char,
use_capture: EM_BOOL, callback: em_mouse_callback_func) user_data: *mut c_void,
-> EMSCRIPTEN_RESULT; use_capture: EM_BOOL,
callback: em_mouse_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_mousedown_callback( pub fn emscripten_set_mousedown_callback(
target: *const c_char, user_data: *mut c_void, target: *const c_char,
use_capture: EM_BOOL, callback: em_mouse_callback_func) user_data: *mut c_void,
-> EMSCRIPTEN_RESULT; use_capture: EM_BOOL,
callback: em_mouse_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_mouseup_callback( pub fn emscripten_set_mouseup_callback(
target: *const c_char, user_data: *mut c_void, target: *const c_char,
use_capture: EM_BOOL, callback: em_mouse_callback_func) user_data: *mut c_void,
-> EMSCRIPTEN_RESULT; use_capture: EM_BOOL,
callback: em_mouse_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_hide_mouse(); pub fn emscripten_hide_mouse();
pub fn emscripten_get_device_pixel_ratio() -> f64; pub fn emscripten_get_device_pixel_ratio() -> f64;
pub fn emscripten_set_pointerlockchange_callback( pub fn emscripten_set_pointerlockchange_callback(
target: *const c_char, userData: *mut c_void, useCapture: EM_BOOL, target: *const c_char,
callback: em_pointerlockchange_callback_func) -> EMSCRIPTEN_RESULT; userData: *mut c_void,
useCapture: EM_BOOL,
callback: em_pointerlockchange_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_fullscreenchange_callback( pub fn emscripten_set_fullscreenchange_callback(
target: *const c_char, userData: *mut c_void, useCapture: EM_BOOL, target: *const c_char,
callback: em_fullscreenchange_callback_func) -> EMSCRIPTEN_RESULT; userData: *mut c_void,
useCapture: EM_BOOL,
callback: em_fullscreenchange_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_asm_const(code: *const c_char); pub fn emscripten_asm_const(code: *const c_char);
pub fn emscripten_set_main_loop( pub fn emscripten_set_main_loop(
func: em_callback_func, fps: c_int, simulate_infinite_loop: EM_BOOL); func: em_callback_func,
fps: c_int,
simulate_infinite_loop: EM_BOOL,
);
pub fn emscripten_cancel_main_loop(); pub fn emscripten_cancel_main_loop();
pub fn emscripten_set_touchstart_callback( pub fn emscripten_set_touchstart_callback(
target: *const c_char, userData: *mut c_void, target: *const c_char,
useCapture: c_int, callback: em_touch_callback_func) userData: *mut c_void,
-> EMSCRIPTEN_RESULT; useCapture: c_int,
callback: em_touch_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_touchend_callback( pub fn emscripten_set_touchend_callback(
target: *const c_char, userData: *mut c_void, target: *const c_char,
useCapture: c_int, callback: em_touch_callback_func) userData: *mut c_void,
-> EMSCRIPTEN_RESULT; useCapture: c_int,
callback: em_touch_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_touchmove_callback( pub fn emscripten_set_touchmove_callback(
target: *const c_char, userData: *mut c_void, target: *const c_char,
useCapture: c_int, callback: em_touch_callback_func) userData: *mut c_void,
-> EMSCRIPTEN_RESULT; useCapture: c_int,
callback: em_touch_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_touchcancel_callback( pub fn emscripten_set_touchcancel_callback(
target: *const c_char, userData: *mut c_void, target: *const c_char,
useCapture: c_int, callback: em_touch_callback_func) userData: *mut c_void,
-> EMSCRIPTEN_RESULT; useCapture: c_int,
callback: em_touch_callback_func,
) -> EMSCRIPTEN_RESULT;
} }

View file

@ -2,16 +2,23 @@
mod ffi; mod ffi;
use std::{mem, ptr, str}; use std::{
use std::cell::RefCell; cell::RefCell,
use std::collections::VecDeque; collections::VecDeque,
use std::os::raw::{c_char, c_void, c_double, c_ulong, c_int}; mem,
use std::sync::atomic::{AtomicBool, Ordering}; os::raw::{c_char, c_double, c_int, c_ulong, c_void},
use std::sync::{Mutex, Arc}; ptr, str,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
};
use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; use crate::{
use error::{ExternalError, NotSupportedError}; dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize},
use window::MonitorHandle as RootMonitorHandle; error::{ExternalError, NotSupportedError},
window::MonitorHandle as RootMonitorHandle,
};
const DOCUMENT_NAME: &'static str = "#document\0"; const DOCUMENT_NAME: &'static str = "#document\0";
@ -54,7 +61,7 @@ impl MonitorHandle {
} }
#[inline] #[inline]
pub fn dimensions(&self) -> PhysicalSize { pub fn size(&self) -> PhysicalSize {
(0, 0).into() (0, 0).into()
} }
@ -68,14 +75,22 @@ impl MonitorHandle {
thread_local!(static MAIN_LOOP_CALLBACK: RefCell<*mut c_void> = RefCell::new(ptr::null_mut())); thread_local!(static MAIN_LOOP_CALLBACK: RefCell<*mut c_void> = RefCell::new(ptr::null_mut()));
// Used to assign a callback to emscripten main loop // Used to assign a callback to emscripten main loop
pub fn set_main_loop_callback<F>(callback : F) where F : FnMut() { pub fn set_main_loop_callback<F>(callback: F)
where
F: FnMut(),
{
MAIN_LOOP_CALLBACK.with(|log| { MAIN_LOOP_CALLBACK.with(|log| {
*log.borrow_mut() = &callback as *const _ as *mut c_void; *log.borrow_mut() = &callback as *const _ as *mut c_void;
}); });
unsafe { ffi::emscripten_set_main_loop(Some(wrapper::<F>), 0, 1); } unsafe {
ffi::emscripten_set_main_loop(Some(wrapper::<F>), 0, 1);
}
unsafe extern "C" fn wrapper<F>() where F : FnMut() { unsafe extern "C" fn wrapper<F>()
where
F: FnMut(),
{
MAIN_LOOP_CALLBACK.with(|z| { MAIN_LOOP_CALLBACK.with(|z| {
let closure = *z.borrow_mut() as *mut F; let closure = *z.borrow_mut() as *mut F;
(*closure)(); (*closure)();
@ -128,7 +143,8 @@ impl EventLoop {
} }
pub fn poll_events<F>(&self, mut callback: F) pub fn poll_events<F>(&self, mut callback: F)
where F: FnMut(::Event) where
F: FnMut(::Event),
{ {
let ref mut window = *self.window.lock().unwrap(); let ref mut window = *self.window.lock().unwrap();
if let &mut Some(ref mut window) = window { if let &mut Some(ref mut window) = window {
@ -139,17 +155,22 @@ impl EventLoop {
} }
pub fn run_forever<F>(&self, mut callback: F) pub fn run_forever<F>(&self, mut callback: F)
where F: FnMut(::Event) -> ::ControlFlow where
F: FnMut(::Event) -> ::ControlFlow,
{ {
self.interrupted.store(false, Ordering::Relaxed); self.interrupted.store(false, Ordering::Relaxed);
// TODO: handle control flow // TODO: handle control flow
set_main_loop_callback(|| { set_main_loop_callback(|| {
self.poll_events(|e| { callback(e); }); self.poll_events(|e| {
callback(e);
});
::std::thread::sleep(::std::time::Duration::from_millis(5)); ::std::thread::sleep(::std::time::Duration::from_millis(5));
if self.interrupted.load(Ordering::Relaxed) { if self.interrupted.load(Ordering::Relaxed) {
unsafe { ffi::emscripten_cancel_main_loop(); } unsafe {
ffi::emscripten_cancel_main_loop();
}
} }
}); });
} }
@ -190,15 +211,15 @@ fn show_mouse() {
// } // }
// styleSheet.insertRule('canvas.emscripten { border: none; cursor: auto; }', 0); // styleSheet.insertRule('canvas.emscripten { border: none; cursor: auto; }', 0);
unsafe { unsafe {
ffi::emscripten_asm_const(b"var styleSheet = document.styleSheets[0]; var rules = styleSheet.cssRules; for (var i = 0; i < rules.length; i++) { if (rules[i].cssText.substr(0, 6) == 'canvas') { styleSheet.deleteRule(i); i--; } } styleSheet.insertRule('canvas.emscripten { border: none; cursor: auto; }', 0);\0".as_ptr() as *const c_char); ffi::emscripten_asm_const(b"var styleSheet = document.styleSheets[0]; var rules = styleSheet.cssRules; for (var i = 0; i < rules.length; i++) { if (rules[i].cssText.substr(0, 6) == 'canvas') { styleSheet.deleteRule(i); i--; } } styleSheet.insertRule('canvas.emscripten { border: none; cursor: auto; }', 0);\0".as_ptr() as *const c_char);
} }
} }
extern "C" fn mouse_callback( extern "C" fn mouse_callback(
event_type: c_int, event_type: c_int,
event: *const ffi::EmscriptenMouseEvent, event: *const ffi::EmscriptenMouseEvent,
event_queue: *mut c_void) -> ffi::EM_BOOL event_queue: *mut c_void,
{ ) -> ffi::EM_BOOL {
unsafe { unsafe {
let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue); let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue);
@ -221,18 +242,18 @@ extern "C" fn mouse_callback(
event: ::WindowEvent::CursorMoved { event: ::WindowEvent::CursorMoved {
device_id: ::DeviceId(DeviceId), device_id: ::DeviceId(DeviceId),
position, position,
modifiers: modifiers, modifiers,
} },
}); });
queue.lock().unwrap().push_back(::Event::DeviceEvent { queue.lock().unwrap().push_back(::Event::DeviceEvent {
device_id: ::DeviceId(DeviceId), device_id: ::DeviceId(DeviceId),
event: ::DeviceEvent::MouseMotion { event: ::DeviceEvent::MouseMotion {
delta: ((*event).movementX as f64, (*event).movementY as f64), delta: ((*event).movementX as f64, (*event).movementY as f64),
} },
}); });
}, }
mouse_input @ ffi::EMSCRIPTEN_EVENT_MOUSEDOWN | mouse_input @ ffi::EMSCRIPTEN_EVENT_MOUSEDOWN
mouse_input @ ffi::EMSCRIPTEN_EVENT_MOUSEUP => { | mouse_input @ ffi::EMSCRIPTEN_EVENT_MOUSEUP => {
let button = match (*event).button { let button = match (*event).button {
0 => ::MouseButton::Left, 0 => ::MouseButton::Left,
1 => ::MouseButton::Middle, 1 => ::MouseButton::Middle,
@ -248,14 +269,13 @@ extern "C" fn mouse_callback(
window_id: ::WindowId(WindowId(0)), window_id: ::WindowId(WindowId(0)),
event: ::WindowEvent::MouseInput { event: ::WindowEvent::MouseInput {
device_id: ::DeviceId(DeviceId), device_id: ::DeviceId(DeviceId),
state: state, state,
button: button, button,
modifiers: modifiers, modifiers,
} },
}) })
},
_ => {
} }
_ => {}
} }
} }
ffi::EM_FALSE ffi::EM_FALSE
@ -264,8 +284,8 @@ extern "C" fn mouse_callback(
extern "C" fn keyboard_callback( extern "C" fn keyboard_callback(
event_type: c_int, event_type: c_int,
event: *const ffi::EmscriptenKeyboardEvent, event: *const ffi::EmscriptenKeyboardEvent,
event_queue: *mut c_void) -> ffi::EM_BOOL event_queue: *mut c_void,
{ ) -> ffi::EM_BOOL {
unsafe { unsafe {
let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue); let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue);
@ -290,7 +310,7 @@ extern "C" fn keyboard_callback(
}, },
}, },
}); });
}, }
ffi::EMSCRIPTEN_EVENT_KEYUP => { ffi::EMSCRIPTEN_EVENT_KEYUP => {
queue.lock().unwrap().push_back(::Event::WindowEvent { queue.lock().unwrap().push_back(::Event::WindowEvent {
window_id: ::WindowId(WindowId(0)), window_id: ::WindowId(WindowId(0)),
@ -304,19 +324,18 @@ extern "C" fn keyboard_callback(
}, },
}, },
}); });
},
_ => {
} }
_ => {}
} }
} }
ffi::EM_FALSE ffi::EM_FALSE
} }
extern fn touch_callback( extern "C" fn touch_callback(
event_type: c_int, event_type: c_int,
event: *const ffi::EmscriptenTouchEvent, event: *const ffi::EmscriptenTouchEvent,
event_queue: *mut c_void) -> ffi::EM_BOOL event_queue: *mut c_void,
{ ) -> ffi::EM_BOOL {
unsafe { unsafe {
let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue); let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue);
@ -356,8 +375,8 @@ extern fn touch_callback(
unsafe extern "C" fn fullscreen_callback( unsafe extern "C" fn fullscreen_callback(
_eventType: c_int, _eventType: c_int,
_fullscreenChangeEvent: *const ffi::EmscriptenFullscreenChangeEvent, _fullscreenChangeEvent: *const ffi::EmscriptenFullscreenChangeEvent,
_userData: *mut c_void) -> ffi::EM_BOOL _userData: *mut c_void,
{ ) -> ffi::EM_BOOL {
ffi::emscripten_request_fullscreen(ptr::null(), ffi::EM_TRUE); ffi::emscripten_request_fullscreen(ptr::null(), ffi::EM_TRUE);
ffi::EM_FALSE ffi::EM_FALSE
} }
@ -367,8 +386,8 @@ unsafe extern "C" fn fullscreen_callback(
unsafe extern "C" fn pointerlockchange_callback( unsafe extern "C" fn pointerlockchange_callback(
_eventType: c_int, _eventType: c_int,
_pointerlockChangeEvent: *const ffi::EmscriptenPointerlockChangeEvent, _pointerlockChangeEvent: *const ffi::EmscriptenPointerlockChangeEvent,
_userData: *mut c_void) -> ffi::EM_BOOL _userData: *mut c_void,
{ ) -> ffi::EM_BOOL {
ffi::emscripten_request_pointerlock(ptr::null(), ffi::EM_TRUE); ffi::emscripten_request_pointerlock(ptr::null(), ffi::EM_TRUE);
ffi::EM_FALSE ffi::EM_FALSE
} }
@ -381,12 +400,15 @@ fn em_try(res: ffi::EMSCRIPTEN_RESULT) -> Result<(), String> {
} }
impl Window { impl Window {
pub fn new(event_loop: &EventLoop, attribs: ::WindowAttributes, pub fn new(
_pl_attribs: PlatformSpecificWindowBuilderAttributes) event_loop: &EventLoop,
-> Result<Window, ::CreationError> attribs: ::WindowAttributes,
{ _pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<Window, ::CreationError> {
if event_loop.window.lock().unwrap().is_some() { if event_loop.window.lock().unwrap().is_some() {
return Err(::CreationError::OsError("Cannot create another window".to_owned())); return Err(::CreationError::OsError(
"Cannot create another window".to_owned(),
));
} }
let w = Window2 { let w = Window2 {
@ -400,35 +422,87 @@ impl Window {
window: Arc::new(w), window: Arc::new(w),
}; };
// TODO: set up more event callbacks // TODO: set up more event callbacks
unsafe { unsafe {
em_try(ffi::emscripten_set_mousemove_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(mouse_callback))) em_try(ffi::emscripten_set_mousemove_callback(
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?; DOCUMENT_NAME.as_ptr() as *const c_char,
em_try(ffi::emscripten_set_mousedown_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(mouse_callback))) mem::transmute(&*window.window.events),
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?; ffi::EM_FALSE,
em_try(ffi::emscripten_set_mouseup_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(mouse_callback))) Some(mouse_callback),
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?; ))
em_try(ffi::emscripten_set_keydown_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(keyboard_callback))) .map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?; em_try(ffi::emscripten_set_mousedown_callback(
em_try(ffi::emscripten_set_keyup_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(keyboard_callback))) DOCUMENT_NAME.as_ptr() as *const c_char,
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?; mem::transmute(&*window.window.events),
em_try(ffi::emscripten_set_touchstart_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(touch_callback))) ffi::EM_FALSE,
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?; Some(mouse_callback),
em_try(ffi::emscripten_set_touchend_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(touch_callback))) ))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?; .map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_touchmove_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(touch_callback))) em_try(ffi::emscripten_set_mouseup_callback(
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?; DOCUMENT_NAME.as_ptr() as *const c_char,
em_try(ffi::emscripten_set_touchcancel_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(touch_callback))) mem::transmute(&*window.window.events),
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?; ffi::EM_FALSE,
Some(mouse_callback),
))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_keydown_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
mem::transmute(&*window.window.events),
ffi::EM_FALSE,
Some(keyboard_callback),
))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_keyup_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
mem::transmute(&*window.window.events),
ffi::EM_FALSE,
Some(keyboard_callback),
))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_touchstart_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
mem::transmute(&*window.window.events),
ffi::EM_FALSE,
Some(touch_callback),
))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_touchend_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
mem::transmute(&*window.window.events),
ffi::EM_FALSE,
Some(touch_callback),
))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_touchmove_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
mem::transmute(&*window.window.events),
ffi::EM_FALSE,
Some(touch_callback),
))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_touchcancel_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
mem::transmute(&*window.window.events),
ffi::EM_FALSE,
Some(touch_callback),
))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
} }
if attribs.fullscreen.is_some() { if attribs.fullscreen.is_some() {
unsafe { unsafe {
em_try(ffi::emscripten_request_fullscreen(ptr::null(), ffi::EM_TRUE)) em_try(ffi::emscripten_request_fullscreen(
.map_err(|e| ::CreationError::OsError(e))?; ptr::null(),
em_try(ffi::emscripten_set_fullscreenchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, Some(fullscreen_callback))) ffi::EM_TRUE,
.map_err(|e| ::CreationError::OsError(e))?; ))
.map_err(|e| ::CreationError::OsError(e))?;
em_try(ffi::emscripten_set_fullscreenchange_callback(
ptr::null(),
0 as *mut c_void,
ffi::EM_FALSE,
Some(fullscreen_callback),
))
.map_err(|e| ::CreationError::OsError(e))?;
} }
} else if let Some(size) = attribs.inner_size { } else if let Some(size) = attribs.inner_size {
window.set_inner_size(size); window.set_inner_size(size);
@ -444,8 +518,7 @@ impl Window {
} }
#[inline] #[inline]
pub fn set_title(&self, _title: &str) { pub fn set_title(&self, _title: &str) {}
}
#[inline] #[inline]
pub fn outer_position(&self) -> Option<LogicalPosition> { pub fn outer_position(&self) -> Option<LogicalPosition> {
@ -458,8 +531,7 @@ impl Window {
} }
#[inline] #[inline]
pub fn set_outer_position(&self, _: LogicalPosition) { pub fn set_outer_position(&self, _: LogicalPosition) {}
}
#[inline] #[inline]
pub fn inner_size(&self) -> Option<LogicalSize> { pub fn inner_size(&self) -> Option<LogicalSize> {
@ -532,7 +604,9 @@ impl Window {
#[inline] #[inline]
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
let mut grabbed_lock = self.window.cursor_grabbed.lock().unwrap(); let mut grabbed_lock = self.window.cursor_grabbed.lock().unwrap();
if grab == *grabbed_lock { return Ok(()); } if grab == *grabbed_lock {
return Ok(());
}
unsafe { unsafe {
if grab { if grab {
em_try(ffi::emscripten_set_pointerlockchange_callback( em_try(ffi::emscripten_set_pointerlockchange_callback(
@ -541,7 +615,10 @@ impl Window {
ffi::EM_FALSE, ffi::EM_FALSE,
Some(pointerlockchange_callback), Some(pointerlockchange_callback),
))?; ))?;
em_try(ffi::emscripten_request_pointerlock(ptr::null(), ffi::EM_TRUE))?; em_try(ffi::emscripten_request_pointerlock(
ptr::null(),
ffi::EM_TRUE,
))?;
} else { } else {
em_try(ffi::emscripten_set_pointerlockchange_callback( em_try(ffi::emscripten_set_pointerlockchange_callback(
ptr::null(), ptr::null(),
@ -559,7 +636,9 @@ impl Window {
#[inline] #[inline]
pub fn set_cursor_visible(&self, visible: bool) { pub fn set_cursor_visible(&self, visible: bool) {
let mut visible_lock = self.window.cursor_visible.lock().unwrap(); let mut visible_lock = self.window.cursor_visible.lock().unwrap();
if visible == *visible_lock { return; } if visible == *visible_lock {
return;
}
if visible { if visible {
show_mouse(); show_mouse();
} else { } else {
@ -615,7 +694,9 @@ impl Window {
#[inline] #[inline]
pub fn current_monitor(&self) -> RootMonitorHandle { pub fn current_monitor(&self) -> RootMonitorHandle {
RootMonitorHandle { inner: MonitorHandle } RootMonitorHandle {
inner: MonitorHandle,
}
} }
#[inline] #[inline]
@ -646,21 +727,37 @@ impl Drop for Window {
// Exit fullscreen if on // Exit fullscreen if on
if self.window.is_fullscreen { if self.window.is_fullscreen {
ffi::emscripten_set_fullscreenchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, None); ffi::emscripten_set_fullscreenchange_callback(
ptr::null(),
0 as *mut c_void,
ffi::EM_FALSE,
None,
);
ffi::emscripten_exit_fullscreen(); ffi::emscripten_exit_fullscreen();
} }
// Delete callbacks // Delete callbacks
ffi::emscripten_set_keydown_callback(DOCUMENT_NAME.as_ptr() as *const c_char, 0 as *mut c_void, ffi::EM_FALSE,None); ffi::emscripten_set_keydown_callback(
ffi::emscripten_set_keyup_callback(DOCUMENT_NAME.as_ptr() as *const c_char, 0 as *mut c_void, ffi::EM_FALSE,None); DOCUMENT_NAME.as_ptr() as *const c_char,
0 as *mut c_void,
ffi::EM_FALSE,
None,
);
ffi::emscripten_set_keyup_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
0 as *mut c_void,
ffi::EM_FALSE,
None,
);
} }
} }
} }
fn error_to_str(code: ffi::EMSCRIPTEN_RESULT) -> &'static str { fn error_to_str(code: ffi::EMSCRIPTEN_RESULT) -> &'static str {
match code { match code {
ffi::EMSCRIPTEN_RESULT_SUCCESS | ffi::EMSCRIPTEN_RESULT_DEFERRED ffi::EMSCRIPTEN_RESULT_SUCCESS | ffi::EMSCRIPTEN_RESULT_DEFERRED => {
=> "Internal error in the library (success detected as failure)", "Internal error in the library (success detected as failure)"
}
ffi::EMSCRIPTEN_RESULT_NOT_SUPPORTED => "Not supported", ffi::EMSCRIPTEN_RESULT_NOT_SUPPORTED => "Not supported",
ffi::EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED => "Failed not deferred", ffi::EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED => "Failed not deferred",
@ -670,7 +767,7 @@ fn error_to_str(code: ffi::EMSCRIPTEN_RESULT) -> &'static str {
ffi::EMSCRIPTEN_RESULT_FAILED => "Failed", ffi::EMSCRIPTEN_RESULT_FAILED => "Failed",
ffi::EMSCRIPTEN_RESULT_NO_DATA => "No data", ffi::EMSCRIPTEN_RESULT_NO_DATA => "No data",
_ => "Undocumented error" _ => "Undocumented error",
} }
} }
@ -679,7 +776,9 @@ fn key_translate(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES]) ->
let maybe_key = unsafe { str::from_utf8(mem::transmute::<_, &[u8]>(slice)) }; let maybe_key = unsafe { str::from_utf8(mem::transmute::<_, &[u8]>(slice)) };
let key = match maybe_key { let key = match maybe_key {
Ok(key) => key, Ok(key) => key,
Err(_) => { return 0; }, Err(_) => {
return 0;
}
}; };
if key.chars().count() == 1 { if key.chars().count() == 1 {
key.as_bytes()[0] key.as_bytes()[0]
@ -688,14 +787,17 @@ fn key_translate(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES]) ->
} }
} }
fn key_translate_virt(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES], fn key_translate_virt(
location: c_ulong) -> Option<::VirtualKeyCode> input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES],
{ location: c_ulong,
) -> Option<::VirtualKeyCode> {
let slice = &input[0..input.iter().take_while(|x| **x != 0).count()]; let slice = &input[0..input.iter().take_while(|x| **x != 0).count()];
let maybe_key = unsafe { str::from_utf8(mem::transmute::<_, &[u8]>(slice)) }; let maybe_key = unsafe { str::from_utf8(mem::transmute::<_, &[u8]>(slice)) };
let key = match maybe_key { let key = match maybe_key {
Ok(key) => key, Ok(key) => key,
Err(_) => { return None; }, Err(_) => {
return None;
}
}; };
use VirtualKeyCode::*; use VirtualKeyCode::*;
match key { match key {

View file

@ -1,26 +1,23 @@
use std::{mem, ptr}; use std::{
use std::cell::{RefCell, RefMut}; cell::{RefCell, RefMut},
use std::mem::ManuallyDrop; mem::{self, ManuallyDrop},
use std::os::raw::c_void; os::raw::c_void,
use std::time::Instant; ptr,
time::Instant,
};
use event::{Event, StartCause}; use crate::{
use event_loop::ControlFlow; event::{Event, StartCause},
event_loop::ControlFlow,
};
use platform_impl::platform::event_loop::{EventHandler, Never}; use crate::platform_impl::platform::{
use platform_impl::platform::ffi::{ event_loop::{EventHandler, Never},
id, ffi::{
CFAbsoluteTimeGetCurrent, id, kCFRunLoopCommonModes, CFAbsoluteTimeGetCurrent, CFRelease, CFRunLoopAddTimer,
CFRelease, CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate, CFRunLoopTimerInvalidate,
CFRunLoopAddTimer, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate, NSUInteger,
CFRunLoopGetMain, },
CFRunLoopRef,
CFRunLoopTimerCreate,
CFRunLoopTimerInvalidate,
CFRunLoopTimerRef,
CFRunLoopTimerSetNextFireDate,
kCFRunLoopCommonModes,
NSUInteger,
}; };
macro_rules! bug { macro_rules! bug {
@ -39,10 +36,10 @@ enum AppStateImpl {
Launching { Launching {
queued_windows: Vec<id>, queued_windows: Vec<id>,
queued_events: Vec<Event<Never>>, queued_events: Vec<Event<Never>>,
queued_event_handler: Box<EventHandler>, queued_event_handler: Box<dyn EventHandler>,
}, },
ProcessingEvents { ProcessingEvents {
event_handler: Box<EventHandler>, event_handler: Box<dyn EventHandler>,
active_control_flow: ControlFlow, active_control_flow: ControlFlow,
}, },
// special state to deal with reentrancy and prevent mutable aliasing. // special state to deal with reentrancy and prevent mutable aliasing.
@ -50,11 +47,11 @@ enum AppStateImpl {
queued_events: Vec<Event<Never>>, queued_events: Vec<Event<Never>>,
}, },
Waiting { Waiting {
waiting_event_handler: Box<EventHandler>, waiting_event_handler: Box<dyn EventHandler>,
start: Instant, start: Instant,
}, },
PollFinished { PollFinished {
waiting_event_handler: Box<EventHandler>, waiting_event_handler: Box<dyn EventHandler>,
}, },
Terminated, Terminated,
} }
@ -62,12 +59,18 @@ enum AppStateImpl {
impl Drop for AppStateImpl { impl Drop for AppStateImpl {
fn drop(&mut self) { fn drop(&mut self) {
match self { match self {
&mut AppStateImpl::NotLaunched { ref mut queued_windows, .. } | &mut AppStateImpl::NotLaunched {
&mut AppStateImpl::Launching { ref mut queued_windows, .. } => unsafe { ref mut queued_windows,
..
}
| &mut AppStateImpl::Launching {
ref mut queued_windows,
..
} => unsafe {
for &mut window in queued_windows { for &mut window in queued_windows {
let () = msg_send![window, release]; let () = msg_send![window, release];
} }
} },
_ => {} _ => {}
} }
} }
@ -88,7 +91,9 @@ impl AppState {
static mut APP_STATE: RefCell<Option<AppState>> = RefCell::new(None); static mut APP_STATE: RefCell<Option<AppState>> = RefCell::new(None);
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
assert_main_thread!("bug in winit: `AppState::get_mut()` can only be called on the main thread"); assert_main_thread!(
"bug in winit: `AppState::get_mut()` can only be called on the main thread"
);
} }
let mut guard = APP_STATE.borrow_mut(); let mut guard = APP_STATE.borrow_mut();
@ -108,33 +113,36 @@ impl AppState {
} }
init_guard(&mut guard) init_guard(&mut guard)
} }
RefMut::map(guard, |state| { RefMut::map(guard, |state| state.as_mut().unwrap())
state.as_mut().unwrap()
})
} }
// requires main thread and window is a UIWindow // requires main thread and window is a UIWindow
// retains window // retains window
pub unsafe fn set_key_window(window: id) { pub unsafe fn set_key_window(window: id) {
let mut this = AppState::get_mut(); let mut this = AppState::get_mut();
match &mut this.app_state { match &mut this.app_state {
&mut AppStateImpl::NotLaunched { ref mut queued_windows, .. } => { &mut AppStateImpl::NotLaunched {
ref mut queued_windows,
..
} => {
queued_windows.push(window); queued_windows.push(window);
msg_send![window, retain]; msg_send![window, retain];
return; return;
} }
&mut AppStateImpl::ProcessingEvents { .. } => {}, &mut AppStateImpl::ProcessingEvents { .. } => {}
&mut AppStateImpl::InUserCallback { .. } => {}, &mut AppStateImpl::InUserCallback { .. } => {}
&mut AppStateImpl::Terminated => panic!("Attempt to create a `Window` \ &mut AppStateImpl::Terminated => panic!(
after the app has terminated"), "Attempt to create a `Window` \
app_state => unreachable!("unexpected state: {:#?}", app_state), // all other cases should be impossible after the app has terminated"
),
app_state => unreachable!("unexpected state: {:#?}", app_state), /* all other cases should be impossible */
} }
drop(this); drop(this);
msg_send![window, makeKeyAndVisible] msg_send![window, makeKeyAndVisible]
} }
// requires main thread // requires main thread
pub unsafe fn will_launch(queued_event_handler: Box<EventHandler>) { pub unsafe fn will_launch(queued_event_handler: Box<dyn EventHandler>) {
let mut this = AppState::get_mut(); let mut this = AppState::get_mut();
let (queued_windows, queued_events) = match &mut this.app_state { let (queued_windows, queued_events) = match &mut this.app_state {
&mut AppStateImpl::NotLaunched { &mut AppStateImpl::NotLaunched {
@ -145,14 +153,19 @@ impl AppState {
let events = ptr::read(queued_events); let events = ptr::read(queued_events);
(windows, events) (windows, events)
} }
_ => panic!("winit iOS expected the app to be in a `NotLaunched` \ _ => panic!(
state, but was not - please file an issue"), "winit iOS expected the app to be in a `NotLaunched` \
state, but was not - please file an issue"
),
}; };
ptr::write(&mut this.app_state, AppStateImpl::Launching { ptr::write(
queued_windows, &mut this.app_state,
queued_events, AppStateImpl::Launching {
queued_event_handler, queued_windows,
}); queued_events,
queued_event_handler,
},
);
} }
// requires main thread // requires main thread
@ -188,11 +201,11 @@ impl AppState {
let screen: id = msg_send![window, screen]; let screen: id = msg_send![window, screen];
let () = msg_send![screen, retain]; let () = msg_send![screen, retain];
let () = msg_send![window, setScreen:0 as id]; let () = msg_send![window, setScreen:0 as id];
let () = msg_send![window, setScreen:screen]; let () = msg_send![window, setScreen: screen];
let () = msg_send![screen, release]; let () = msg_send![screen, release];
let controller: id = msg_send![window, rootViewController]; let controller: id = msg_send![window, rootViewController];
let () = msg_send![window, setRootViewController:ptr::null::<()>()]; let () = msg_send![window, setRootViewController:ptr::null::<()>()];
let () = msg_send![window, setRootViewController:controller]; let () = msg_send![window, setRootViewController: controller];
let () = msg_send![window, makeKeyAndVisible]; let () = msg_send![window, makeKeyAndVisible];
} }
let () = msg_send![window, release]; let () = msg_send![window, release];
@ -210,15 +223,20 @@ impl AppState {
let event_handler = ptr::read(queued_event_handler); let event_handler = ptr::read(queued_event_handler);
(windows, events, event_handler) (windows, events, event_handler)
} }
_ => panic!("winit iOS expected the app to be in a `Launching` \ _ => panic!(
state, but was not - please file an issue"), "winit iOS expected the app to be in a `Launching` \
state, but was not - please file an issue"
),
}; };
ptr::write(&mut this.app_state, AppStateImpl::ProcessingEvents { ptr::write(
event_handler, &mut this.app_state,
active_control_flow: ControlFlow::Poll, AppStateImpl::ProcessingEvents {
}); event_handler,
active_control_flow: ControlFlow::Poll,
},
);
drop(this); drop(this);
let events = std::iter::once(Event::NewEvents(StartCause::Init)).chain(events); let events = std::iter::once(Event::NewEvents(StartCause::Init)).chain(events);
AppState::handle_nonuser_events(events); AppState::handle_nonuser_events(events);
@ -238,69 +256,79 @@ impl AppState {
// AppState::did_finish_launching handles the special transition `Init` // AppState::did_finish_launching handles the special transition `Init`
pub unsafe fn handle_wakeup_transition() { pub unsafe fn handle_wakeup_transition() {
let mut this = AppState::get_mut(); let mut this = AppState::get_mut();
let event = match this.control_flow { let event =
ControlFlow::Poll => { match this.control_flow {
let event_handler = match &mut this.app_state { ControlFlow::Poll => {
&mut AppStateImpl::NotLaunched { .. } | let event_handler = match &mut this.app_state {
&mut AppStateImpl::Launching { .. } => return, &mut AppStateImpl::NotLaunched { .. }
&mut AppStateImpl::PollFinished { | &mut AppStateImpl::Launching { .. } => return,
ref mut waiting_event_handler, &mut AppStateImpl::PollFinished {
} => ptr::read(waiting_event_handler), ref mut waiting_event_handler,
_ => bug!("`EventHandler` unexpectedly started polling"), } => ptr::read(waiting_event_handler),
}; _ => bug!("`EventHandler` unexpectedly started polling"),
ptr::write(&mut this.app_state, AppStateImpl::ProcessingEvents { };
event_handler, ptr::write(
active_control_flow: ControlFlow::Poll, &mut this.app_state,
}); AppStateImpl::ProcessingEvents {
Event::NewEvents(StartCause::Poll) event_handler,
} active_control_flow: ControlFlow::Poll,
ControlFlow::Wait => { },
let (event_handler, start) = match &mut this.app_state { );
&mut AppStateImpl::NotLaunched { .. } | Event::NewEvents(StartCause::Poll)
&mut AppStateImpl::Launching { .. } => return, }
&mut AppStateImpl::Waiting { ControlFlow::Wait => {
ref mut waiting_event_handler, let (event_handler, start) = match &mut this.app_state {
ref mut start, &mut AppStateImpl::NotLaunched { .. }
} => (ptr::read(waiting_event_handler), *start), | &mut AppStateImpl::Launching { .. } => return,
_ => bug!("`EventHandler` unexpectedly woke up"), &mut AppStateImpl::Waiting {
}; ref mut waiting_event_handler,
ptr::write(&mut this.app_state, AppStateImpl::ProcessingEvents { ref mut start,
event_handler, } => (ptr::read(waiting_event_handler), *start),
active_control_flow: ControlFlow::Wait, _ => bug!("`EventHandler` unexpectedly woke up"),
}); };
Event::NewEvents(StartCause::WaitCancelled { ptr::write(
start, &mut this.app_state,
requested_resume: None, AppStateImpl::ProcessingEvents {
}) event_handler,
} active_control_flow: ControlFlow::Wait,
ControlFlow::WaitUntil(requested_resume) => { },
let (event_handler, start) = match &mut this.app_state { );
&mut AppStateImpl::NotLaunched { .. } |
&mut AppStateImpl::Launching { .. } => return,
&mut AppStateImpl::Waiting {
ref mut waiting_event_handler,
ref mut start,
} => (ptr::read(waiting_event_handler), *start),
_ => bug!("`EventHandler` unexpectedly woke up"),
};
ptr::write(&mut this.app_state, AppStateImpl::ProcessingEvents {
event_handler,
active_control_flow: ControlFlow::WaitUntil(requested_resume),
});
if Instant::now() >= requested_resume {
Event::NewEvents(StartCause::ResumeTimeReached {
start,
requested_resume,
})
} else {
Event::NewEvents(StartCause::WaitCancelled { Event::NewEvents(StartCause::WaitCancelled {
start, start,
requested_resume: Some(requested_resume), requested_resume: None,
}) })
} }
} ControlFlow::WaitUntil(requested_resume) => {
ControlFlow::Exit => bug!("unexpected controlflow `Exit`"), let (event_handler, start) = match &mut this.app_state {
}; &mut AppStateImpl::NotLaunched { .. }
| &mut AppStateImpl::Launching { .. } => return,
&mut AppStateImpl::Waiting {
ref mut waiting_event_handler,
ref mut start,
} => (ptr::read(waiting_event_handler), *start),
_ => bug!("`EventHandler` unexpectedly woke up"),
};
ptr::write(
&mut this.app_state,
AppStateImpl::ProcessingEvents {
event_handler,
active_control_flow: ControlFlow::WaitUntil(requested_resume),
},
);
if Instant::now() >= requested_resume {
Event::NewEvents(StartCause::ResumeTimeReached {
start,
requested_resume,
})
} else {
Event::NewEvents(StartCause::WaitCancelled {
start,
requested_resume: Some(requested_resume),
})
}
}
ControlFlow::Exit => bug!("unexpected controlflow `Exit`"),
};
drop(this); drop(this);
AppState::handle_nonuser_event(event) AppState::handle_nonuser_event(event)
} }
@ -328,7 +356,7 @@ impl AppState {
.. ..
} => { } => {
queued_events.extend(events); queued_events.extend(events);
return return;
} }
&mut AppStateImpl::ProcessingEvents { &mut AppStateImpl::ProcessingEvents {
ref mut event_handler, ref mut event_handler,
@ -338,9 +366,12 @@ impl AppState {
| &mut AppStateImpl::Waiting { .. } | &mut AppStateImpl::Waiting { .. }
| &mut AppStateImpl::Terminated => bug!("unexpected attempted to process an event"), | &mut AppStateImpl::Terminated => bug!("unexpected attempted to process an event"),
}; };
ptr::write(&mut this.app_state, AppStateImpl::InUserCallback { ptr::write(
queued_events: Vec::new(), &mut this.app_state,
}); AppStateImpl::InUserCallback {
queued_events: Vec::new(),
},
);
drop(this); drop(this);
for event in events { for event in events {
@ -360,7 +391,7 @@ impl AppState {
active_control_flow, active_control_flow,
}; };
this.control_flow = control_flow; this.control_flow = control_flow;
break break;
} }
drop(this); drop(this);
for event in queued_events { for event in queued_events {
@ -384,9 +415,12 @@ impl AppState {
| &mut AppStateImpl::Waiting { .. } | &mut AppStateImpl::Waiting { .. }
| &mut AppStateImpl::Terminated => bug!("unexpected attempted to process an event"), | &mut AppStateImpl::Terminated => bug!("unexpected attempted to process an event"),
}; };
ptr::write(&mut this.app_state, AppStateImpl::InUserCallback { ptr::write(
queued_events: Vec::new(), &mut this.app_state,
}); AppStateImpl::InUserCallback {
queued_events: Vec::new(),
},
);
drop(this); drop(this);
event_handler.handle_user_events(&mut control_flow); event_handler.handle_user_events(&mut control_flow);
@ -404,7 +438,7 @@ impl AppState {
active_control_flow, active_control_flow,
}; };
this.control_flow = control_flow; this.control_flow = control_flow;
break break;
} }
drop(this); drop(this);
for event in queued_events { for event in queued_events {
@ -432,20 +466,21 @@ impl AppState {
&mut AppStateImpl::ProcessingEvents { &mut AppStateImpl::ProcessingEvents {
ref mut event_handler, ref mut event_handler,
ref mut active_control_flow, ref mut active_control_flow,
} => (ManuallyDrop::new(ptr::read(event_handler)), *active_control_flow), } => (
ManuallyDrop::new(ptr::read(event_handler)),
*active_control_flow,
),
_ => unreachable!(), _ => unreachable!(),
}; };
let new = this.control_flow; let new = this.control_flow;
match (old, new) { match (old, new) {
(ControlFlow::Poll, ControlFlow::Poll) => { (ControlFlow::Poll, ControlFlow::Poll) => ptr::write(
ptr::write( &mut this.app_state,
&mut this.app_state, AppStateImpl::PollFinished {
AppStateImpl::PollFinished { waiting_event_handler: ManuallyDrop::into_inner(event_handler),
waiting_event_handler: ManuallyDrop::into_inner(event_handler), },
}, ),
)
},
(ControlFlow::Wait, ControlFlow::Wait) => { (ControlFlow::Wait, ControlFlow::Wait) => {
let start = Instant::now(); let start = Instant::now();
ptr::write( ptr::write(
@ -455,9 +490,10 @@ impl AppState {
start, start,
}, },
) )
}, }
(ControlFlow::WaitUntil(old_instant), ControlFlow::WaitUntil(new_instant)) (ControlFlow::WaitUntil(old_instant), ControlFlow::WaitUntil(new_instant))
if old_instant == new_instant => { if old_instant == new_instant =>
{
let start = Instant::now(); let start = Instant::now();
ptr::write( ptr::write(
&mut this.app_state, &mut this.app_state,
@ -477,7 +513,7 @@ impl AppState {
}, },
); );
this.waker.stop() this.waker.stop()
}, }
(_, ControlFlow::WaitUntil(new_instant)) => { (_, ControlFlow::WaitUntil(new_instant)) => {
let start = Instant::now(); let start = Instant::now();
ptr::write( ptr::write(
@ -488,7 +524,7 @@ impl AppState {
}, },
); );
this.waker.start_at(new_instant) this.waker.start_at(new_instant)
}, }
(_, ControlFlow::Poll) => { (_, ControlFlow::Poll) => {
ptr::write( ptr::write(
&mut this.app_state, &mut this.app_state,
@ -497,7 +533,7 @@ impl AppState {
}, },
); );
this.waker.start() this.waker.start()
}, }
(_, ControlFlow::Exit) => { (_, ControlFlow::Exit) => {
// https://developer.apple.com/library/archive/qa/qa1561/_index.html // https://developer.apple.com/library/archive/qa/qa1561/_index.html
// it is not possible to quit an iOS app gracefully and programatically // it is not possible to quit an iOS app gracefully and programatically
@ -511,7 +547,11 @@ impl AppState {
let mut this = unsafe { AppState::get_mut() }; let mut this = unsafe { AppState::get_mut() };
let mut old = mem::replace(&mut this.app_state, AppStateImpl::Terminated); let mut old = mem::replace(&mut this.app_state, AppStateImpl::Terminated);
let mut control_flow = this.control_flow; let mut control_flow = this.control_flow;
if let AppStateImpl::ProcessingEvents { ref mut event_handler, .. } = old { if let AppStateImpl::ProcessingEvents {
ref mut event_handler,
..
} = old
{
drop(this); drop(this);
event_handler.handle_nonuser_event(Event::LoopDestroyed, &mut control_flow) event_handler.handle_nonuser_event(Event::LoopDestroyed, &mut control_flow)
} else { } else {
@ -535,7 +575,7 @@ impl Drop for EventLoopWaker {
impl EventLoopWaker { impl EventLoopWaker {
fn new(rl: CFRunLoopRef) -> EventLoopWaker { fn new(rl: CFRunLoopRef) -> EventLoopWaker {
extern fn wakeup_main_loop(_timer: CFRunLoopTimerRef, _info: *mut c_void) {} extern "C" fn wakeup_main_loop(_timer: CFRunLoopTimerRef, _info: *mut c_void) {}
unsafe { unsafe {
// create a timer with a 1microsec interval (1ns does not work) to mimic polling. // create a timer with a 1microsec interval (1ns does not work) to mimic polling.
// it is initially setup with a first fire time really far into the // it is initially setup with a first fire time really far into the
@ -577,4 +617,4 @@ impl EventLoopWaker {
} }
} }
} }
} }

View file

@ -1,50 +1,33 @@
use std::{mem, ptr}; use std::{
use std::collections::VecDeque; collections::VecDeque,
use std::ffi::c_void; ffi::c_void,
use std::fmt::{self, Debug, Formatter}; fmt::{self, Debug},
use std::marker::PhantomData; marker::PhantomData,
use std::sync::mpsc::{self, Sender, Receiver}; mem, ptr,
sync::mpsc::{self, Receiver, Sender},
use event::Event;
use event_loop::{
ControlFlow,
EventLoopWindowTarget as RootEventLoopWindowTarget,
EventLoopClosed,
}; };
use platform::ios::Idiom;
use platform_impl::platform::app_state::AppState; use crate::{
use platform_impl::platform::ffi::{ event::Event,
id, event_loop::{
nil, ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootEventLoopWindowTarget,
CFIndex, },
CFRelease, platform::ios::Idiom,
CFRunLoopActivity, };
CFRunLoopAddObserver,
CFRunLoopAddSource, use crate::platform_impl::platform::{
CFRunLoopGetMain, app_state::AppState,
CFRunLoopObserverCreate, ffi::{
CFRunLoopObserverRef, id, kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes,
CFRunLoopSourceContext, kCFRunLoopDefaultMode, kCFRunLoopEntry, kCFRunLoopExit, nil, CFIndex, CFRelease,
CFRunLoopSourceCreate, CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddSource, CFRunLoopGetMain,
CFRunLoopSourceInvalidate, CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext,
CFRunLoopSourceRef, CFRunLoopSourceCreate, CFRunLoopSourceInvalidate, CFRunLoopSourceRef,
CFRunLoopSourceSignal, CFRunLoopSourceSignal, CFRunLoopWakeUp, NSOperatingSystemVersion, NSString,
CFRunLoopWakeUp, UIApplicationMain, UIUserInterfaceIdiom,
kCFRunLoopCommonModes, },
kCFRunLoopDefaultMode, monitor, view, MonitorHandle,
kCFRunLoopEntry,
kCFRunLoopBeforeWaiting,
kCFRunLoopAfterWaiting,
kCFRunLoopExit,
NSOperatingSystemVersion,
NSString,
UIApplicationMain,
UIUserInterfaceIdiom,
}; };
use platform_impl::platform::monitor;
use platform_impl::platform::MonitorHandle;
use platform_impl::platform::view;
pub struct EventLoopWindowTarget<T: 'static> { pub struct EventLoopWindowTarget<T: 'static> {
receiver: Receiver<T>, receiver: Receiver<T>,
@ -67,8 +50,11 @@ impl<T: 'static> EventLoop<T> {
static mut SINGLETON_INIT: bool = false; static mut SINGLETON_INIT: bool = false;
unsafe { unsafe {
assert_main_thread!("`EventLoop` can only be created on the main thread on iOS"); assert_main_thread!("`EventLoop` can only be created on the main thread on iOS");
assert!(!SINGLETON_INIT, "Only one `EventLoop` is supported on iOS. \ assert!(
`EventLoopProxy` might be helpful"); !SINGLETON_INIT,
"Only one `EventLoop` is supported on iOS. \
`EventLoopProxy` might be helpful"
);
SINGLETON_INIT = true; SINGLETON_INIT = true;
view::create_delegate_class(); view::create_delegate_class();
} }
@ -92,25 +78,34 @@ impl<T: 'static> EventLoop<T> {
capabilities, capabilities,
}, },
_marker: PhantomData, _marker: PhantomData,
} },
} }
} }
pub fn run<F>(self, event_handler: F) -> ! pub fn run<F>(self, event_handler: F) -> !
where where
F: 'static + FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow) F: 'static + FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
{ {
unsafe { unsafe {
let application: *mut c_void = msg_send![class!(UIApplication), sharedApplication]; let application: *mut c_void = msg_send![class!(UIApplication), sharedApplication];
assert_eq!(application, ptr::null_mut(), "\ assert_eq!(
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\ application,
Note: `EventLoop::run` calls `UIApplicationMain` on iOS"); ptr::null_mut(),
"\
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
Note: `EventLoop::run` calls `UIApplicationMain` on iOS"
);
AppState::will_launch(Box::new(EventLoopHandler { AppState::will_launch(Box::new(EventLoopHandler {
f: event_handler, f: event_handler,
event_loop: self.window_target, event_loop: self.window_target,
})); }));
UIApplicationMain(0, ptr::null(), nil, NSString::alloc(nil).init_str("AppDelegate")); UIApplicationMain(
0,
ptr::null(),
nil,
NSString::alloc(nil).init_str("AppDelegate"),
);
unreachable!() unreachable!()
} }
} }
@ -121,16 +116,12 @@ impl<T: 'static> EventLoop<T> {
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> { pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
// guaranteed to be on main thread // guaranteed to be on main thread
unsafe { unsafe { monitor::uiscreens() }
monitor::uiscreens()
}
} }
pub fn primary_monitor(&self) -> MonitorHandle { pub fn primary_monitor(&self) -> MonitorHandle {
// guaranteed to be on main thread // guaranteed to be on main thread
unsafe { unsafe { monitor::main_uiscreen() }
monitor::main_uiscreen()
}
} }
pub fn window_target(&self) -> &RootEventLoopWindowTarget<T> { pub fn window_target(&self) -> &RootEventLoopWindowTarget<T> {
@ -142,9 +133,7 @@ impl<T: 'static> EventLoop<T> {
impl<T: 'static> EventLoop<T> { impl<T: 'static> EventLoop<T> {
pub fn idiom(&self) -> Idiom { pub fn idiom(&self) -> Idiom {
// guaranteed to be on main thread // guaranteed to be on main thread
unsafe { unsafe { self::get_idiom() }
self::get_idiom()
}
} }
} }
@ -183,18 +172,12 @@ impl<T> EventLoopProxy<T> {
// we want all the members of context to be zero/null, except one // we want all the members of context to be zero/null, except one
let mut context: CFRunLoopSourceContext = mem::zeroed(); let mut context: CFRunLoopSourceContext = mem::zeroed();
context.perform = event_loop_proxy_handler; context.perform = event_loop_proxy_handler;
let source = CFRunLoopSourceCreate( let source =
ptr::null_mut(), CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
CFIndex::max_value() - 1,
&mut context,
);
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes); CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl); CFRunLoopWakeUp(rl);
EventLoopProxy { EventLoopProxy { sender, source }
sender,
source,
}
} }
} }
@ -213,7 +196,7 @@ impl<T> EventLoopProxy<T> {
fn setup_control_flow_observers() { fn setup_control_flow_observers() {
unsafe { unsafe {
// begin is queued with the highest priority to ensure it is processed before other observers // begin is queued with the highest priority to ensure it is processed before other observers
extern fn control_flow_begin_handler( extern "C" fn control_flow_begin_handler(
_: CFRunLoopObserverRef, _: CFRunLoopObserverRef,
activity: CFRunLoopActivity, activity: CFRunLoopActivity,
_: *mut c_void, _: *mut c_void,
@ -230,7 +213,7 @@ fn setup_control_flow_observers() {
// end is queued with the lowest priority to ensure it is processed after other observers // end is queued with the lowest priority to ensure it is processed after other observers
// without that, LoopDestroyed will get sent after EventsCleared // without that, LoopDestroyed will get sent after EventsCleared
extern fn control_flow_end_handler( extern "C" fn control_flow_end_handler(
_: CFRunLoopObserverRef, _: CFRunLoopObserverRef,
activity: CFRunLoopActivity, activity: CFRunLoopActivity,
_: *mut c_void, _: *mut c_void,
@ -281,8 +264,8 @@ struct EventLoopHandler<F, T: 'static> {
} }
impl<F, T: 'static> Debug for EventLoopHandler<F, T> { impl<F, T: 'static> Debug for EventLoopHandler<F, T> {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.debug_struct("EventLoopHandler") f.debug_struct("EventLoopHandler")
.field("event_loop", &self.event_loop) .field("event_loop", &self.event_loop)
.finish() .finish()
} }
@ -303,11 +286,7 @@ where
fn handle_user_events(&mut self, control_flow: &mut ControlFlow) { fn handle_user_events(&mut self, control_flow: &mut ControlFlow) {
for event in self.event_loop.p.receiver.try_iter() { for event in self.event_loop.p.receiver.try_iter() {
(self.f)( (self.f)(Event::UserEvent(event), &self.event_loop, control_flow);
Event::UserEvent(event),
&self.event_loop,
control_flow,
);
} }
} }
} }
@ -325,7 +304,10 @@ pub struct Capabilities {
impl From<NSOperatingSystemVersion> for Capabilities { impl From<NSOperatingSystemVersion> for Capabilities {
fn from(os_version: NSOperatingSystemVersion) -> Capabilities { fn from(os_version: NSOperatingSystemVersion) -> Capabilities {
assert!(os_version.major >= 8, "`winit` current requires iOS version 8 or greater"); assert!(
os_version.major >= 8,
"`winit` current requires iOS version 8 or greater"
);
let supports_safe_area = os_version.major >= 11; let supports_safe_area = os_version.major >= 11;

View file

@ -1,13 +1,10 @@
#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)] #![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
use std::ffi::CString; use std::{ffi::CString, ops::BitOr, os::raw::*};
use std::ops::BitOr;
use std::os::raw::*;
use objc::{Encode, Encoding}; use objc::{runtime::Object, Encode, Encoding};
use objc::runtime::Object;
use platform::ios::{Idiom, ValidOrientations}; use crate::platform::ios::{Idiom, ValidOrientations};
pub type id = *mut Object; pub type id = *mut Object;
pub const nil: id = 0 as id; pub const nil: id = 0 as id;
@ -87,7 +84,9 @@ pub struct UIEdgeInsets {
pub struct UIUserInterfaceIdiom(NSInteger); pub struct UIUserInterfaceIdiom(NSInteger);
unsafe impl Encode for UIUserInterfaceIdiom { unsafe impl Encode for UIUserInterfaceIdiom {
fn encode() -> Encoding { NSInteger::encode() } fn encode() -> Encoding {
NSInteger::encode()
}
} }
impl UIUserInterfaceIdiom { impl UIUserInterfaceIdiom {
@ -128,7 +127,9 @@ impl Into<Idiom> for UIUserInterfaceIdiom {
pub struct UIInterfaceOrientationMask(NSUInteger); pub struct UIInterfaceOrientationMask(NSUInteger);
unsafe impl Encode for UIInterfaceOrientationMask { unsafe impl Encode for UIInterfaceOrientationMask {
fn encode() -> Encoding { NSUInteger::encode() } fn encode() -> Encoding {
NSUInteger::encode()
}
} }
impl UIInterfaceOrientationMask { impl UIInterfaceOrientationMask {
@ -136,9 +137,12 @@ impl UIInterfaceOrientationMask {
pub const PortraitUpsideDown: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 2); pub const PortraitUpsideDown: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 2);
pub const LandscapeLeft: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 4); pub const LandscapeLeft: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 4);
pub const LandscapeRight: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 3); pub const LandscapeRight: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 3);
pub const Landscape: UIInterfaceOrientationMask = UIInterfaceOrientationMask(Self::LandscapeLeft.0 | Self::LandscapeRight.0); pub const Landscape: UIInterfaceOrientationMask =
pub const AllButUpsideDown: UIInterfaceOrientationMask = UIInterfaceOrientationMask(Self::Landscape.0 | Self::Portrait.0); UIInterfaceOrientationMask(Self::LandscapeLeft.0 | Self::LandscapeRight.0);
pub const All: UIInterfaceOrientationMask = UIInterfaceOrientationMask(Self::AllButUpsideDown.0 | Self::PortraitUpsideDown.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 { impl BitOr for UIInterfaceOrientationMask {
@ -155,18 +159,23 @@ impl UIInterfaceOrientationMask {
idiom: Idiom, idiom: Idiom,
) -> UIInterfaceOrientationMask { ) -> UIInterfaceOrientationMask {
match (valid_orientations, idiom) { match (valid_orientations, idiom) {
(ValidOrientations::LandscapeAndPortrait, Idiom::Phone) => UIInterfaceOrientationMask::AllButUpsideDown, (ValidOrientations::LandscapeAndPortrait, Idiom::Phone) => {
UIInterfaceOrientationMask::AllButUpsideDown
}
(ValidOrientations::LandscapeAndPortrait, _) => UIInterfaceOrientationMask::All, (ValidOrientations::LandscapeAndPortrait, _) => UIInterfaceOrientationMask::All,
(ValidOrientations::Landscape, _) => UIInterfaceOrientationMask::Landscape, (ValidOrientations::Landscape, _) => UIInterfaceOrientationMask::Landscape,
(ValidOrientations::Portrait, Idiom::Phone) => UIInterfaceOrientationMask::Portrait, (ValidOrientations::Portrait, Idiom::Phone) => UIInterfaceOrientationMask::Portrait,
(ValidOrientations::Portrait, _) => UIInterfaceOrientationMask::Portrait | UIInterfaceOrientationMask::PortraitUpsideDown, (ValidOrientations::Portrait, _) => {
UIInterfaceOrientationMask::Portrait
| UIInterfaceOrientationMask::PortraitUpsideDown
}
} }
} }
} }
#[link(name = "UIKit", kind = "framework")] #[link(name = "UIKit", kind = "framework")]
#[link(name = "CoreFoundation", kind = "framework")] #[link(name = "CoreFoundation", kind = "framework")]
extern { extern "C" {
pub static kCFRunLoopDefaultMode: CFRunLoopMode; pub static kCFRunLoopDefaultMode: CFRunLoopMode;
pub static kCFRunLoopCommonModes: CFRunLoopMode; pub static kCFRunLoopCommonModes: CFRunLoopMode;
@ -203,15 +212,8 @@ extern {
callout: CFRunLoopTimerCallBack, callout: CFRunLoopTimerCallBack,
context: *mut CFRunLoopTimerContext, context: *mut CFRunLoopTimerContext,
) -> CFRunLoopTimerRef; ) -> CFRunLoopTimerRef;
pub fn CFRunLoopAddTimer( pub fn CFRunLoopAddTimer(rl: CFRunLoopRef, timer: CFRunLoopTimerRef, mode: CFRunLoopMode);
rl: CFRunLoopRef, pub fn CFRunLoopTimerSetNextFireDate(timer: CFRunLoopTimerRef, fireDate: CFAbsoluteTime);
timer: CFRunLoopTimerRef,
mode: CFRunLoopMode,
);
pub fn CFRunLoopTimerSetNextFireDate(
timer: CFRunLoopTimerRef,
fireDate: CFAbsoluteTime,
);
pub fn CFRunLoopTimerInvalidate(time: CFRunLoopTimerRef); pub fn CFRunLoopTimerInvalidate(time: CFRunLoopTimerRef);
pub fn CFRunLoopSourceCreate( pub fn CFRunLoopSourceCreate(
@ -219,11 +221,7 @@ extern {
order: CFIndex, order: CFIndex,
context: *mut CFRunLoopSourceContext, context: *mut CFRunLoopSourceContext,
) -> CFRunLoopSourceRef; ) -> CFRunLoopSourceRef;
pub fn CFRunLoopAddSource( pub fn CFRunLoopAddSource(rl: CFRunLoopRef, source: CFRunLoopSourceRef, mode: CFRunLoopMode);
rl: CFRunLoopRef,
source: CFRunLoopSourceRef,
mode: CFRunLoopMode,
);
pub fn CFRunLoopSourceInvalidate(source: CFRunLoopSourceRef); pub fn CFRunLoopSourceInvalidate(source: CFRunLoopSourceRef);
pub fn CFRunLoopSourceSignal(source: CFRunLoopSourceRef); pub fn CFRunLoopSourceSignal(source: CFRunLoopSourceRef);
@ -259,15 +257,9 @@ pub const kCFRunLoopBeforeWaiting: CFRunLoopActivity = 1 << 5;
pub const kCFRunLoopAfterWaiting: CFRunLoopActivity = 1 << 6; pub const kCFRunLoopAfterWaiting: CFRunLoopActivity = 1 << 6;
pub const kCFRunLoopExit: CFRunLoopActivity = 1 << 7; pub const kCFRunLoopExit: CFRunLoopActivity = 1 << 7;
pub type CFRunLoopObserverCallBack = extern "C" fn( pub type CFRunLoopObserverCallBack =
observer: CFRunLoopObserverRef, extern "C" fn(observer: CFRunLoopObserverRef, activity: CFRunLoopActivity, info: *mut c_void);
activity: CFRunLoopActivity, pub type CFRunLoopTimerCallBack = extern "C" fn(timer: CFRunLoopTimerRef, info: *mut c_void);
info: *mut c_void,
);
pub type CFRunLoopTimerCallBack = extern "C" fn(
timer: CFRunLoopTimerRef,
info: *mut c_void,
);
pub enum CFRunLoopObserverContext {} pub enum CFRunLoopObserverContext {}
pub enum CFRunLoopTimerContext {} pub enum CFRunLoopTimerContext {}
@ -299,11 +291,11 @@ pub trait NSString: Sized {
impl NSString for id { impl NSString for id {
unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id { unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id {
msg_send![self, initWithUTF8String:c_string as id] msg_send![self, initWithUTF8String: c_string as id]
} }
unsafe fn stringByAppendingString_(self, other: id) -> id { unsafe fn stringByAppendingString_(self, other: id) -> id {
msg_send![self, stringByAppendingString:other] msg_send![self, stringByAppendingString: other]
} }
unsafe fn init_str(self, string: &str) -> id { unsafe fn init_str(self, string: &str) -> id {

View file

@ -26,7 +26,6 @@
//! fn start_inner() { //! fn start_inner() {
//! ... //! ...
//! } //! }
//!
//! ``` //! ```
//! //!
//! Compile project and then drag resulting .a into Xcode project. Add winit.h to xcode. //! Compile project and then drag resulting .a into Xcode project. Add winit.h to xcode.
@ -47,8 +46,8 @@
//! //!
//! This is how those event are represented in winit: //! This is how those event are represented in winit:
//! //!
//! - applicationDidBecomeActive is Suspended(false) //! - applicationDidBecomeActive is Resumed
//! - applicationWillResignActive is Suspended(true) //! - applicationWillResignActive is Suspended
//! - applicationWillTerminate is LoopDestroyed //! - applicationWillTerminate is LoopDestroyed
//! //!
//! Keep in mind that after LoopDestroyed event is received every attempt to draw with //! Keep in mind that after LoopDestroyed event is received every attempt to draw with
@ -78,12 +77,10 @@ mod window;
use std::fmt; use std::fmt;
pub use self::event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}; pub use self::{
pub use self::monitor::MonitorHandle; event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget},
pub use self::window::{ monitor::MonitorHandle,
PlatformSpecificWindowBuilderAttributes, window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId},
Window,
WindowId,
}; };
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -106,9 +103,9 @@ unsafe impl Sync for DeviceId {}
pub enum OsError {} pub enum OsError {}
impl fmt::Display for OsError { impl fmt::Display for OsError {
fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
_ => unreachable!() _ => unreachable!(),
} }
} }
} }

View file

@ -1,17 +1,16 @@
use std::{ use std::{
collections::VecDeque, collections::{HashSet, VecDeque},
fmt, fmt,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
}; };
use dpi::{PhysicalPosition, PhysicalSize}; use crate::{
dpi::{PhysicalPosition, PhysicalSize},
monitor::VideoMode,
};
use platform_impl::platform::ffi::{ use crate::platform_impl::platform::ffi::{
id, id, nil, CGFloat, CGRect, CGSize, NSInteger, NSUInteger,
nil,
CGFloat,
CGRect,
NSUInteger,
}; };
pub struct Inner { pub struct Inner {
@ -35,7 +34,9 @@ impl Deref for MonitorHandle {
fn deref(&self) -> &Inner { fn deref(&self) -> &Inner {
unsafe { unsafe {
assert_main_thread!("`MonitorHandle` methods can only be run on the main thread on iOS"); assert_main_thread!(
"`MonitorHandle` methods can only be run on the main thread on iOS"
);
} }
&self.inner &self.inner
} }
@ -44,7 +45,9 @@ impl Deref for MonitorHandle {
impl DerefMut for MonitorHandle { impl DerefMut for MonitorHandle {
fn deref_mut(&mut self) -> &mut Inner { fn deref_mut(&mut self) -> &mut Inner {
unsafe { unsafe {
assert_main_thread!("`MonitorHandle` methods can only be run on the main thread on iOS"); assert_main_thread!(
"`MonitorHandle` methods can only be run on the main thread on iOS"
);
} }
&mut self.inner &mut self.inner
} }
@ -68,18 +71,18 @@ impl Drop for MonitorHandle {
} }
impl fmt::Debug for MonitorHandle { impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[derive(Debug)] #[derive(Debug)]
struct MonitorHandle { struct MonitorHandle {
name: Option<String>, name: Option<String>,
dimensions: PhysicalSize, size: PhysicalSize,
position: PhysicalPosition, position: PhysicalPosition,
hidpi_factor: f64, hidpi_factor: f64,
} }
let monitor_id_proxy = MonitorHandle { let monitor_id_proxy = MonitorHandle {
name: self.name(), name: self.name(),
dimensions: self.dimensions(), size: self.size(),
position: self.position(), position: self.position(),
hidpi_factor: self.hidpi_factor(), hidpi_factor: self.hidpi_factor(),
}; };
@ -94,7 +97,9 @@ impl MonitorHandle {
assert_main_thread!("`MonitorHandle` can only be cloned on the main thread on iOS"); assert_main_thread!("`MonitorHandle` can only be cloned on the main thread on iOS");
let () = msg_send![uiscreen, retain]; let () = msg_send![uiscreen, retain];
} }
MonitorHandle { inner: Inner { uiscreen } } MonitorHandle {
inner: Inner { uiscreen },
}
} }
} }
@ -114,7 +119,7 @@ impl Inner {
} }
} }
pub fn dimensions(&self) -> PhysicalSize { pub fn size(&self) -> PhysicalSize {
unsafe { unsafe {
let bounds: CGRect = msg_send![self.ui_screen(), nativeBounds]; let bounds: CGRect = msg_send![self.ui_screen(), nativeBounds];
(bounds.size.width as f64, bounds.size.height as f64).into() (bounds.size.width as f64, bounds.size.height as f64).into()
@ -134,6 +139,27 @@ impl Inner {
scale as f64 scale as f64
} }
} }
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
let refresh_rate: NSInteger = unsafe { msg_send![self.uiscreen, maximumFramesPerSecond] };
let available_modes: id = unsafe { msg_send![self.uiscreen, availableModes] };
let available_mode_count: NSUInteger = unsafe { msg_send![available_modes, count] };
let mut modes = HashSet::with_capacity(available_mode_count);
for i in 0..available_mode_count {
let mode: id = unsafe { msg_send![available_modes, objectAtIndex: i] };
let size: CGSize = unsafe { msg_send![mode, size] };
modes.insert(VideoMode {
size: (size.width as u32, size.height as u32),
bit_depth: 32,
refresh_rate: refresh_rate as u16,
});
}
modes.into_iter()
}
} }
// MonitorHandleExtIOS // MonitorHandleExtIOS
@ -164,7 +190,7 @@ pub unsafe fn uiscreens() -> VecDeque<MonitorHandle> {
loop { loop {
let screen: id = msg_send![screens_enum, nextObject]; let screen: id = msg_send![screens_enum, nextObject];
if screen == nil { if screen == nil {
break result break result;
} }
result.push_back(MonitorHandle::retained_new(screen)); result.push_back(MonitorHandle::retained_new(screen));
} }

View file

@ -1,31 +1,23 @@
use std::collections::HashMap; use std::collections::HashMap;
use objc::declare::ClassDecl; use objc::{
use objc::runtime::{BOOL, Class, NO, Object, Sel, YES}; declare::ClassDecl,
runtime::{Class, Object, Sel, BOOL, NO, YES},
use event::{
DeviceId as RootDeviceId,
Event,
Touch,
TouchPhase,
WindowEvent
}; };
use platform::ios::MonitorHandleExtIOS;
use window::{WindowAttributes, WindowId as RootWindowId};
use platform_impl::platform::app_state::AppState; use crate::{
use platform_impl::platform::DeviceId; event::{DeviceId as RootDeviceId, Event, Touch, TouchPhase, WindowEvent},
use platform_impl::platform::event_loop; platform::ios::MonitorHandleExtIOS,
use platform_impl::platform::ffi::{ window::{WindowAttributes, WindowId as RootWindowId},
id, };
nil,
CGFloat, use crate::platform_impl::platform::{
CGPoint, app_state::AppState,
CGRect, event_loop,
UIInterfaceOrientationMask, ffi::{id, nil, CGFloat, CGPoint, CGRect, UIInterfaceOrientationMask, UITouchPhase},
UITouchPhase, window::PlatformSpecificWindowBuilderAttributes,
DeviceId,
}; };
use platform_impl::platform::window::{PlatformSpecificWindowBuilderAttributes};
// requires main thread // requires main thread
unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
@ -40,10 +32,13 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
classes.entry(root_view_class).or_insert_with(move || { classes.entry(root_view_class).or_insert_with(move || {
let uiview_class = class!(UIView); let uiview_class = class!(UIView);
let is_uiview: BOOL = msg_send![root_view_class, isSubclassOfClass:uiview_class]; let is_uiview: BOOL = msg_send![root_view_class, isSubclassOfClass: uiview_class];
assert_eq!(is_uiview, YES, "`root_view_class` must inherit from `UIView`"); assert_eq!(
is_uiview, YES,
"`root_view_class` must inherit from `UIView`"
);
extern fn draw_rect(object: &Object, _: Sel, rect: CGRect) { extern "C" fn draw_rect(object: &Object, _: Sel, rect: CGRect) {
unsafe { unsafe {
let window: id = msg_send![object, window]; let window: id = msg_send![object, window];
AppState::handle_nonuser_event(Event::WindowEvent { AppState::handle_nonuser_event(Event::WindowEvent {
@ -55,13 +50,14 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
} }
} }
extern fn layout_subviews(object: &Object, _: Sel) { extern "C" fn layout_subviews(object: &Object, _: Sel) {
unsafe { unsafe {
let window: id = msg_send![object, window]; let window: id = msg_send![object, window];
let bounds: CGRect = msg_send![window, bounds]; let bounds: CGRect = msg_send![window, bounds];
let screen: id = msg_send![window, screen]; let screen: id = msg_send![window, screen];
let screen_space: id = msg_send![screen, coordinateSpace]; let screen_space: id = msg_send![screen, coordinateSpace];
let screen_frame: CGRect = msg_send![object, convertRect:bounds toCoordinateSpace:screen_space]; let screen_frame: CGRect =
msg_send![object, convertRect:bounds toCoordinateSpace:screen_space];
let size = crate::dpi::LogicalSize { let size = crate::dpi::LogicalSize {
width: screen_frame.size.width, width: screen_frame.size.width,
height: screen_frame.size.height, height: screen_frame.size.height,
@ -78,10 +74,14 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
let mut decl = ClassDecl::new(&format!("WinitUIView{}", ID), root_view_class) let mut decl = ClassDecl::new(&format!("WinitUIView{}", ID), root_view_class)
.expect("Failed to declare class `WinitUIView`"); .expect("Failed to declare class `WinitUIView`");
ID += 1; ID += 1;
decl.add_method(sel!(drawRect:), decl.add_method(
draw_rect as extern fn(&Object, Sel, CGRect)); sel!(drawRect:),
decl.add_method(sel!(layoutSubviews), draw_rect as extern "C" fn(&Object, Sel, CGRect),
layout_subviews as extern fn(&Object, Sel)); );
decl.add_method(
sel!(layoutSubviews),
layout_subviews as extern "C" fn(&Object, Sel),
);
decl.register() decl.register()
}) })
} }
@ -92,33 +92,39 @@ unsafe fn get_view_controller_class() -> &'static Class {
if CLASS.is_none() { if CLASS.is_none() {
let uiviewcontroller_class = class!(UIViewController); let uiviewcontroller_class = class!(UIViewController);
extern fn set_prefers_status_bar_hidden(object: &mut Object, _: Sel, hidden: BOOL) { extern "C" fn set_prefers_status_bar_hidden(object: &mut Object, _: Sel, hidden: BOOL) {
unsafe { unsafe {
object.set_ivar::<BOOL>("_prefers_status_bar_hidden", hidden); object.set_ivar::<BOOL>("_prefers_status_bar_hidden", hidden);
let () = msg_send![object, setNeedsStatusBarAppearanceUpdate]; let () = msg_send![object, setNeedsStatusBarAppearanceUpdate];
} }
} }
extern fn prefers_status_bar_hidden(object: &Object, _: Sel) -> BOOL { extern "C" fn prefers_status_bar_hidden(object: &Object, _: Sel) -> BOOL {
unsafe { unsafe { *object.get_ivar::<BOOL>("_prefers_status_bar_hidden") }
*object.get_ivar::<BOOL>("_prefers_status_bar_hidden")
}
} }
extern fn set_supported_orientations(object: &mut Object, _: Sel, orientations: UIInterfaceOrientationMask) { extern "C" fn set_supported_orientations(
object: &mut Object,
_: Sel,
orientations: UIInterfaceOrientationMask,
) {
unsafe { unsafe {
object.set_ivar::<UIInterfaceOrientationMask>("_supported_orientations", orientations); object.set_ivar::<UIInterfaceOrientationMask>(
"_supported_orientations",
orientations,
);
let () = msg_send![class!(UIViewController), attemptRotationToDeviceOrientation]; let () = msg_send![class!(UIViewController), attemptRotationToDeviceOrientation];
} }
} }
extern fn supported_orientations(object: &Object, _: Sel) -> UIInterfaceOrientationMask { extern "C" fn supported_orientations(
unsafe { object: &Object,
*object.get_ivar::<UIInterfaceOrientationMask>("_supported_orientations") _: Sel,
} ) -> UIInterfaceOrientationMask {
unsafe { *object.get_ivar::<UIInterfaceOrientationMask>("_supported_orientations") }
} }
extern fn should_autorotate(_: &Object, _: Sel) -> BOOL { extern "C" fn should_autorotate(_: &Object, _: Sel) -> BOOL {
YES YES
} }
@ -126,16 +132,27 @@ unsafe fn get_view_controller_class() -> &'static Class {
.expect("Failed to declare class `WinitUIViewController`"); .expect("Failed to declare class `WinitUIViewController`");
decl.add_ivar::<BOOL>("_prefers_status_bar_hidden"); decl.add_ivar::<BOOL>("_prefers_status_bar_hidden");
decl.add_ivar::<UIInterfaceOrientationMask>("_supported_orientations"); decl.add_ivar::<UIInterfaceOrientationMask>("_supported_orientations");
decl.add_method(sel!(setPrefersStatusBarHidden:), decl.add_method(
set_prefers_status_bar_hidden as extern fn(&mut Object, Sel, BOOL)); sel!(setPrefersStatusBarHidden:),
decl.add_method(sel!(prefersStatusBarHidden), set_prefers_status_bar_hidden as extern "C" fn(&mut Object, Sel, BOOL),
prefers_status_bar_hidden as extern fn(&Object, Sel) -> BOOL); );
decl.add_method(sel!(setSupportedInterfaceOrientations:), decl.add_method(
set_supported_orientations as extern fn(&mut Object, Sel, UIInterfaceOrientationMask)); sel!(prefersStatusBarHidden),
decl.add_method(sel!(supportedInterfaceOrientations), prefers_status_bar_hidden as extern "C" fn(&Object, Sel) -> BOOL,
supported_orientations as extern fn(&Object, Sel) -> UIInterfaceOrientationMask); );
decl.add_method(sel!(shouldAutorotate), decl.add_method(
should_autorotate as extern fn(&Object, Sel) -> BOOL); sel!(setSupportedInterfaceOrientations:),
set_supported_orientations
as extern "C" fn(&mut Object, Sel, UIInterfaceOrientationMask),
);
decl.add_method(
sel!(supportedInterfaceOrientations),
supported_orientations as extern "C" fn(&Object, Sel) -> UIInterfaceOrientationMask,
);
decl.add_method(
sel!(shouldAutorotate),
should_autorotate as extern "C" fn(&Object, Sel) -> BOOL,
);
CLASS = Some(decl.register()); CLASS = Some(decl.register());
} }
CLASS.unwrap() CLASS.unwrap()
@ -147,7 +164,7 @@ unsafe fn get_window_class() -> &'static Class {
if CLASS.is_none() { if CLASS.is_none() {
let uiwindow_class = class!(UIWindow); let uiwindow_class = class!(UIWindow);
extern fn become_key_window(object: &Object, _: Sel) { extern "C" fn become_key_window(object: &Object, _: Sel) {
unsafe { unsafe {
AppState::handle_nonuser_event(Event::WindowEvent { AppState::handle_nonuser_event(Event::WindowEvent {
window_id: RootWindowId(object.into()), window_id: RootWindowId(object.into()),
@ -157,7 +174,7 @@ unsafe fn get_window_class() -> &'static Class {
} }
} }
extern fn resign_key_window(object: &Object, _: Sel) { extern "C" fn resign_key_window(object: &Object, _: Sel) {
unsafe { unsafe {
AppState::handle_nonuser_event(Event::WindowEvent { AppState::handle_nonuser_event(Event::WindowEvent {
window_id: RootWindowId(object.into()), window_id: RootWindowId(object.into()),
@ -167,7 +184,7 @@ unsafe fn get_window_class() -> &'static Class {
} }
} }
extern fn handle_touches(object: &Object, _: Sel, touches: id, _:id) { extern "C" fn handle_touches(object: &Object, _: Sel, touches: id, _: id) {
unsafe { unsafe {
let uiscreen = msg_send![object, screen]; let uiscreen = msg_send![object, screen];
let touches_enum: id = msg_send![touches, objectEnumerator]; let touches_enum: id = msg_send![touches, objectEnumerator];
@ -175,9 +192,9 @@ unsafe fn get_window_class() -> &'static Class {
loop { loop {
let touch: id = msg_send![touches_enum, nextObject]; let touch: id = msg_send![touches_enum, nextObject];
if touch == nil { if touch == nil {
break break;
} }
let location: CGPoint = msg_send![touch, locationInView:nil]; let location: CGPoint = msg_send![touch, locationInView: nil];
let touch_id = touch as u64; let touch_id = touch as u64;
let phase: UITouchPhase = msg_send![touch, phase]; let phase: UITouchPhase = msg_send![touch, phase];
let phase = match phase { let phase = match phase {
@ -203,16 +220,20 @@ unsafe fn get_window_class() -> &'static Class {
} }
} }
extern fn set_content_scale_factor(object: &mut Object, _: Sel, hidpi_factor: CGFloat) { extern "C" fn set_content_scale_factor(object: &mut Object, _: Sel, hidpi_factor: CGFloat) {
unsafe { unsafe {
let () = msg_send![super(object, class!(UIWindow)), setContentScaleFactor:hidpi_factor]; let () = msg_send![
super(object, class!(UIWindow)),
setContentScaleFactor: hidpi_factor
];
let view_controller: id = msg_send![object, rootViewController]; let view_controller: id = msg_send![object, rootViewController];
let view: id = msg_send![view_controller, view]; let view: id = msg_send![view_controller, view];
let () = msg_send![view, setContentScaleFactor:hidpi_factor]; let () = msg_send![view, setContentScaleFactor: hidpi_factor];
let bounds: CGRect = msg_send![object, bounds]; let bounds: CGRect = msg_send![object, bounds];
let screen: id = msg_send![object, screen]; let screen: id = msg_send![object, screen];
let screen_space: id = msg_send![screen, coordinateSpace]; let screen_space: id = msg_send![screen, coordinateSpace];
let screen_frame: CGRect = msg_send![object, convertRect:bounds toCoordinateSpace:screen_space]; let screen_frame: CGRect =
msg_send![object, convertRect:bounds toCoordinateSpace:screen_space];
let size = crate::dpi::LogicalSize { let size = crate::dpi::LogicalSize {
width: screen_frame.size.width, width: screen_frame.size.width,
height: screen_frame.size.height, height: screen_frame.size.height,
@ -221,32 +242,47 @@ unsafe fn get_window_class() -> &'static Class {
std::iter::once(Event::WindowEvent { std::iter::once(Event::WindowEvent {
window_id: RootWindowId(object.into()), window_id: RootWindowId(object.into()),
event: WindowEvent::HiDpiFactorChanged(hidpi_factor as _), event: WindowEvent::HiDpiFactorChanged(hidpi_factor as _),
}).chain(std::iter::once(Event::WindowEvent { })
.chain(std::iter::once(Event::WindowEvent {
window_id: RootWindowId(object.into()), window_id: RootWindowId(object.into()),
event: WindowEvent::Resized(size), event: WindowEvent::Resized(size),
})) })),
); );
} }
} }
let mut decl = ClassDecl::new("WinitUIWindow", uiwindow_class) let mut decl = ClassDecl::new("WinitUIWindow", uiwindow_class)
.expect("Failed to declare class `WinitUIWindow`"); .expect("Failed to declare class `WinitUIWindow`");
decl.add_method(sel!(becomeKeyWindow), decl.add_method(
become_key_window as extern fn(&Object, Sel)); sel!(becomeKeyWindow),
decl.add_method(sel!(resignKeyWindow), become_key_window as extern "C" fn(&Object, Sel),
resign_key_window as extern fn(&Object, Sel)); );
decl.add_method(
sel!(resignKeyWindow),
resign_key_window as extern "C" fn(&Object, Sel),
);
decl.add_method(sel!(touchesBegan:withEvent:), decl.add_method(
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id)); sel!(touchesBegan:withEvent:),
decl.add_method(sel!(touchesMoved:withEvent:), handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id)); );
decl.add_method(sel!(touchesEnded:withEvent:), decl.add_method(
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id)); sel!(touchesMoved:withEvent:),
decl.add_method(sel!(touchesCancelled:withEvent:), handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
handle_touches as extern 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.add_method(sel!(setContentScaleFactor:), decl.add_method(
set_content_scale_factor as extern fn(&mut Object, Sel, CGFloat)); sel!(setContentScaleFactor:),
set_content_scale_factor as extern "C" fn(&mut Object, Sel, CGFloat),
);
CLASS = Some(decl.register()); CLASS = Some(decl.register());
} }
@ -263,9 +299,9 @@ pub unsafe fn create_view(
let view: id = msg_send![class, alloc]; let view: id = msg_send![class, alloc];
assert!(!view.is_null(), "Failed to create `UIView` instance"); assert!(!view.is_null(), "Failed to create `UIView` instance");
let view: id = msg_send![view, initWithFrame:frame]; let view: id = msg_send![view, initWithFrame: frame];
assert!(!view.is_null(), "Failed to initialize `UIView` instance"); assert!(!view.is_null(), "Failed to initialize `UIView` instance");
let () = msg_send![view, setMultipleTouchEnabled:YES]; let () = msg_send![view, setMultipleTouchEnabled: YES];
view view
} }
@ -279,9 +315,15 @@ pub unsafe fn create_view_controller(
let class = get_view_controller_class(); let class = get_view_controller_class();
let view_controller: id = msg_send![class, alloc]; let view_controller: id = msg_send![class, alloc];
assert!(!view_controller.is_null(), "Failed to create `UIViewController` instance"); assert!(
!view_controller.is_null(),
"Failed to create `UIViewController` instance"
);
let view_controller: id = msg_send![view_controller, init]; let view_controller: id = msg_send![view_controller, init];
assert!(!view_controller.is_null(), "Failed to initialize `UIViewController` instance"); assert!(
!view_controller.is_null(),
"Failed to initialize `UIViewController` instance"
);
let status_bar_hidden = if window_attributes.decorations { let status_bar_hidden = if window_attributes.decorations {
NO NO
} else { } else {
@ -292,9 +334,15 @@ pub unsafe fn create_view_controller(
platform_attributes.valid_orientations, platform_attributes.valid_orientations,
idiom, idiom,
); );
let () = msg_send![view_controller, setPrefersStatusBarHidden:status_bar_hidden]; let () = msg_send![
let () = msg_send![view_controller, setSupportedInterfaceOrientations:supported_orientations]; view_controller,
let () = msg_send![view_controller, setView:view]; setPrefersStatusBarHidden: status_bar_hidden
];
let () = msg_send![
view_controller,
setSupportedInterfaceOrientations: supported_orientations
];
let () = msg_send![view_controller, setView: view];
view_controller view_controller
} }
@ -309,11 +357,14 @@ pub unsafe fn create_window(
let window: id = msg_send![class, alloc]; let window: id = msg_send![class, alloc];
assert!(!window.is_null(), "Failed to create `UIWindow` instance"); assert!(!window.is_null(), "Failed to create `UIWindow` instance");
let window: id = msg_send![window, initWithFrame:frame]; let window: id = msg_send![window, initWithFrame: frame];
assert!(!window.is_null(), "Failed to initialize `UIWindow` instance"); assert!(
let () = msg_send![window, setRootViewController:view_controller]; !window.is_null(),
"Failed to initialize `UIWindow` instance"
);
let () = msg_send![window, setRootViewController: view_controller];
if let Some(hidpi_factor) = platform_attributes.hidpi_factor { if let Some(hidpi_factor) = platform_attributes.hidpi_factor {
let () = msg_send![window, setContentScaleFactor:hidpi_factor as CGFloat]; let () = msg_send![window, setContentScaleFactor: hidpi_factor as CGFloat];
} }
if let &Some(ref monitor) = &window_attributes.fullscreen { if let &Some(ref monitor) = &window_attributes.fullscreen {
let () = msg_send![window, setScreen:monitor.ui_screen()]; let () = msg_send![window, setScreen:monitor.ui_screen()];
@ -323,29 +374,25 @@ pub unsafe fn create_window(
} }
pub fn create_delegate_class() { pub fn create_delegate_class() {
extern fn did_finish_launching(_: &mut Object, _: Sel, _: id, _: id) -> BOOL { extern "C" fn did_finish_launching(_: &mut Object, _: Sel, _: id, _: id) -> BOOL {
unsafe { unsafe {
AppState::did_finish_launching(); AppState::did_finish_launching();
} }
YES YES
} }
extern fn did_become_active(_: &Object, _: Sel, _: id) { extern "C" fn did_become_active(_: &Object, _: Sel, _: id) {
unsafe { unsafe { AppState::handle_nonuser_event(Event::Resumed) }
AppState::handle_nonuser_event(Event::Suspended(false))
}
} }
extern fn will_resign_active(_: &Object, _: Sel, _: id) { extern "C" fn will_resign_active(_: &Object, _: Sel, _: id) {
unsafe { unsafe { AppState::handle_nonuser_event(Event::Suspended) }
AppState::handle_nonuser_event(Event::Suspended(true))
}
} }
extern fn will_enter_foreground(_: &Object, _: Sel, _: id) {} extern "C" fn will_enter_foreground(_: &Object, _: Sel, _: id) {}
extern fn did_enter_background(_: &Object, _: Sel, _: id) {} extern "C" fn did_enter_background(_: &Object, _: Sel, _: id) {}
extern fn will_terminate(_: &Object, _: Sel, _: id) { extern "C" fn will_terminate(_: &Object, _: Sel, _: id) {
unsafe { unsafe {
let app: id = msg_send![class!(UIApplication), sharedApplication]; let app: id = msg_send![class!(UIApplication), sharedApplication];
let windows: id = msg_send![app, windows]; let windows: id = msg_send![app, windows];
@ -354,9 +401,9 @@ pub fn create_delegate_class() {
loop { loop {
let window: id = msg_send![windows_enum, nextObject]; let window: id = msg_send![windows_enum, nextObject];
if window == nil { if window == nil {
break break;
} }
let is_winit_window: BOOL = msg_send![window, isKindOfClass:class!(WinitUIWindow)]; let is_winit_window: BOOL = msg_send![window, isKindOfClass: class!(WinitUIWindow)];
if is_winit_window == YES { if is_winit_window == YES {
events.push(Event::WindowEvent { events.push(Event::WindowEvent {
window_id: RootWindowId(window.into()), window_id: RootWindowId(window.into()),
@ -370,23 +417,36 @@ pub fn create_delegate_class() {
} }
let ui_responder = class!(UIResponder); let ui_responder = class!(UIResponder);
let mut decl = ClassDecl::new("AppDelegate", ui_responder).expect("Failed to declare class `AppDelegate`"); let mut decl =
ClassDecl::new("AppDelegate", ui_responder).expect("Failed to declare class `AppDelegate`");
unsafe { unsafe {
decl.add_method(sel!(application:didFinishLaunchingWithOptions:), decl.add_method(
did_finish_launching as extern fn(&mut Object, Sel, id, id) -> BOOL); sel!(application:didFinishLaunchingWithOptions:),
did_finish_launching as extern "C" fn(&mut Object, Sel, id, id) -> BOOL,
);
decl.add_method(sel!(applicationDidBecomeActive:), decl.add_method(
did_become_active as extern fn(&Object, Sel, id)); sel!(applicationDidBecomeActive:),
decl.add_method(sel!(applicationWillResignActive:), did_become_active as extern "C" fn(&Object, Sel, id),
will_resign_active as extern fn(&Object, Sel, id)); );
decl.add_method(sel!(applicationWillEnterForeground:), decl.add_method(
will_enter_foreground as extern fn(&Object, Sel, id)); sel!(applicationWillResignActive:),
decl.add_method(sel!(applicationDidEnterBackground:), will_resign_active as extern "C" fn(&Object, Sel, id),
did_enter_background as extern 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:), decl.add_method(
will_terminate as extern fn(&Object, Sel, id)); sel!(applicationWillTerminate:),
will_terminate as extern "C" fn(&Object, Sel, id),
);
decl.register(); decl.register();
} }

View file

@ -3,35 +3,21 @@ use std::{
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
}; };
use objc::runtime::{Class, NO, Object, YES}; use objc::runtime::{Class, Object, NO, YES};
use dpi::{self, LogicalPosition, LogicalSize}; use crate::{
use error::{ExternalError, NotSupportedError, OsError as RootOsError}; dpi::{self, LogicalPosition, LogicalSize},
use icon::Icon; error::{ExternalError, NotSupportedError, OsError as RootOsError},
use monitor::MonitorHandle as RootMonitorHandle; icon::Icon,
use platform::ios::{MonitorHandleExtIOS, ValidOrientations}; monitor::MonitorHandle as RootMonitorHandle,
use window::{ platform::ios::{MonitorHandleExtIOS, ValidOrientations},
CursorIcon, platform_impl::platform::{
WindowAttributes,
};
use platform_impl::{
platform::{
app_state::AppState, app_state::AppState,
event_loop, event_loop,
ffi::{ ffi::{id, CGFloat, CGPoint, CGRect, CGSize, UIEdgeInsets, UIInterfaceOrientationMask},
id, monitor, view, EventLoopWindowTarget, MonitorHandle,
CGFloat,
CGPoint,
CGRect,
CGSize,
UIEdgeInsets,
UIInterfaceOrientationMask,
},
monitor,
view,
EventLoopWindowTarget,
MonitorHandle
}, },
window::{CursorIcon, WindowAttributes},
}; };
pub struct Inner { pub struct Inner {
@ -59,10 +45,10 @@ impl Inner {
pub fn set_visible(&self, visible: bool) { pub fn set_visible(&self, visible: bool) {
match visible { match visible {
true => unsafe { true => unsafe {
let () = msg_send![self.window, setHidden:NO]; let () = msg_send![self.window, setHidden: NO];
}, },
false => unsafe { false => unsafe {
let () = msg_send![self.window, setHidden:YES]; let () = msg_send![self.window, setHidden: YES];
}, },
} }
} }
@ -104,7 +90,7 @@ impl Inner {
size: screen_frame.size, size: screen_frame.size,
}; };
let bounds = self.from_screen_space(new_screen_frame); let bounds = self.from_screen_space(new_screen_frame);
let () = msg_send![self.window, setBounds:bounds]; let () = msg_send![self.window, setBounds: bounds];
} }
} }
@ -181,9 +167,9 @@ impl Inner {
// this is pretty slow on iOS, so avoid doing it if we can // this is pretty slow on iOS, so avoid doing it if we can
if uiscreen != current { if uiscreen != current {
let () = msg_send![self.window, setScreen:uiscreen]; let () = msg_send![self.window, setScreen: uiscreen];
} }
let () = msg_send![self.window, setFrame:bounds]; let () = msg_send![self.window, setFrame: bounds];
} }
None => warn!("`Window::set_fullscreen(None)` ignored on iOS"), None => warn!("`Window::set_fullscreen(None)` ignored on iOS"),
} }
@ -213,7 +199,10 @@ impl Inner {
pub fn set_decorations(&self, decorations: bool) { pub fn set_decorations(&self, decorations: bool) {
unsafe { unsafe {
let status_bar_hidden = if decorations { NO } else { YES }; let status_bar_hidden = if decorations { NO } else { YES };
let () = msg_send![self.view_controller, setPrefersStatusBarHidden:status_bar_hidden]; let () = msg_send![
self.view_controller,
setPrefersStatusBarHidden: status_bar_hidden
];
} }
} }
@ -232,20 +221,18 @@ impl Inner {
pub fn current_monitor(&self) -> RootMonitorHandle { pub fn current_monitor(&self) -> RootMonitorHandle {
unsafe { unsafe {
let uiscreen: id = msg_send![self.window, screen]; let uiscreen: id = msg_send![self.window, screen];
RootMonitorHandle { inner: MonitorHandle::retained_new(uiscreen) } RootMonitorHandle {
inner: MonitorHandle::retained_new(uiscreen),
}
} }
} }
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> { pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
unsafe { unsafe { monitor::uiscreens() }
monitor::uiscreens()
}
} }
pub fn primary_monitor(&self) -> MonitorHandle { pub fn primary_monitor(&self) -> MonitorHandle {
unsafe { unsafe { monitor::main_uiscreen() }
monitor::main_uiscreen()
}
} }
pub fn id(&self) -> WindowId { pub fn id(&self) -> WindowId {
@ -306,7 +293,8 @@ impl Window {
// TODO: transparency, visible // TODO: transparency, visible
unsafe { unsafe {
let screen = window_attributes.fullscreen let screen = window_attributes
.fullscreen
.as_ref() .as_ref()
.map(|screen| screen.ui_screen() as _) .map(|screen| screen.ui_screen() as _)
.unwrap_or_else(|| monitor::main_uiscreen().ui_screen()); .unwrap_or_else(|| monitor::main_uiscreen().ui_screen());
@ -315,14 +303,23 @@ impl Window {
let frame = match window_attributes.inner_size { let frame = match window_attributes.inner_size {
Some(dim) => CGRect { Some(dim) => CGRect {
origin: screen_bounds.origin, origin: screen_bounds.origin,
size: CGSize { width: dim.width, height: dim.height }, size: CGSize {
width: dim.width,
height: dim.height,
},
}, },
None => screen_bounds, None => screen_bounds,
}; };
let view = view::create_view(&window_attributes, &platform_attributes, frame.clone()); let view = view::create_view(&window_attributes, &platform_attributes, frame.clone());
let view_controller = view::create_view_controller(&window_attributes, &platform_attributes, view); let view_controller =
let window = view::create_window(&window_attributes, &platform_attributes, frame, view_controller); view::create_view_controller(&window_attributes, &platform_attributes, view);
let window = view::create_window(
&window_attributes,
&platform_attributes,
frame,
view_controller,
);
let supports_safe_area = event_loop.capabilities().supports_safe_area; let supports_safe_area = event_loop.capabilities().supports_safe_area;
@ -342,23 +339,38 @@ impl Window {
// WindowExtIOS // WindowExtIOS
impl Inner { impl Inner {
pub fn ui_window(&self) -> id { self.window } pub fn ui_window(&self) -> id {
pub fn ui_view_controller(&self) -> id { self.view_controller } self.window
pub fn ui_view(&self) -> id { self.view } }
pub fn ui_view_controller(&self) -> id {
self.view_controller
}
pub fn ui_view(&self) -> id {
self.view
}
pub fn set_hidpi_factor(&self, hidpi_factor: f64) { pub fn set_hidpi_factor(&self, hidpi_factor: f64) {
unsafe { unsafe {
assert!(dpi::validate_hidpi_factor(hidpi_factor), "`WindowExtIOS::set_hidpi_factor` received an invalid hidpi factor"); assert!(
dpi::validate_hidpi_factor(hidpi_factor),
"`WindowExtIOS::set_hidpi_factor` received an invalid hidpi factor"
);
let hidpi_factor = hidpi_factor as CGFloat; let hidpi_factor = hidpi_factor as CGFloat;
let () = msg_send![self.view, setContentScaleFactor:hidpi_factor]; let () = msg_send![self.view, setContentScaleFactor: hidpi_factor];
} }
} }
pub fn set_valid_orientations(&self, valid_orientations: ValidOrientations) { pub fn set_valid_orientations(&self, valid_orientations: ValidOrientations) {
unsafe { unsafe {
let idiom = event_loop::get_idiom(); let idiom = event_loop::get_idiom();
let supported_orientations = UIInterfaceOrientationMask::from_valid_orientations_idiom(valid_orientations, idiom); let supported_orientations = UIInterfaceOrientationMask::from_valid_orientations_idiom(
msg_send![self.view_controller, setSupportedInterfaceOrientations:supported_orientations]; valid_orientations,
idiom,
);
msg_send![
self.view_controller,
setSupportedInterfaceOrientations: supported_orientations
];
} }
} }
} }
@ -411,14 +423,18 @@ impl Inner {
let screen_frame = self.to_screen_space(bounds); let screen_frame = self.to_screen_space(bounds);
let status_bar_frame: CGRect = { let status_bar_frame: CGRect = {
let app: id = msg_send![class!(UIApplication), sharedApplication]; let app: id = msg_send![class!(UIApplication), sharedApplication];
assert!(!app.is_null(), "`Window::get_inner_position` cannot be called before `EventLoop::run` on iOS"); assert!(
!app.is_null(),
"`Window::get_inner_position` cannot be called before `EventLoop::run` on iOS"
);
msg_send![app, statusBarFrame] msg_send![app, statusBarFrame]
}; };
let (y, height) = if screen_frame.origin.y > status_bar_frame.size.height { let (y, height) = if screen_frame.origin.y > status_bar_frame.size.height {
(screen_frame.origin.y, screen_frame.size.height) (screen_frame.origin.y, screen_frame.size.height)
} else { } else {
let y = status_bar_frame.size.height; let y = status_bar_frame.size.height;
let height = screen_frame.size.height - (status_bar_frame.size.height - screen_frame.origin.y); let height = screen_frame.size.height
- (status_bar_frame.size.height - screen_frame.origin.y);
(y, height) (y, height)
}; };
CGRect { CGRect {
@ -429,7 +445,7 @@ impl Inner {
size: CGSize { size: CGSize {
width: screen_frame.size.width, width: screen_frame.size.width,
height, height,
} },
} }
} }
} }
@ -453,13 +469,17 @@ unsafe impl Sync for WindowId {}
impl From<&Object> for WindowId { impl From<&Object> for WindowId {
fn from(window: &Object) -> WindowId { fn from(window: &Object) -> WindowId {
WindowId { window: window as *const _ as _ } WindowId {
window: window as *const _ as _,
}
} }
} }
impl From<&mut Object> for WindowId { impl From<&mut Object> for WindowId {
fn from(window: &mut Object) -> WindowId { fn from(window: &mut Object) -> WindowId {
WindowId { window: window as _ } WindowId {
window: window as _,
}
} }
} }

View file

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

View file

@ -1,24 +1,21 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] #![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
use std::collections::VecDeque; use std::{collections::VecDeque, env, ffi::CStr, fmt, mem, os::raw::*, sync::Arc};
use std::{env, mem, fmt};
use std::ffi::CStr;
use std::os::raw::*;
use std::sync::Arc;
use parking_lot::Mutex; use parking_lot::Mutex;
use sctk::reexports::client::ConnectError; use smithay_client_toolkit::reexports::client::ConnectError;
use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
use icon::Icon;
use error::{ExternalError, NotSupportedError, OsError as RootOsError};
use event::Event;
use event_loop::{EventLoopClosed, ControlFlow, EventLoopWindowTarget as RootELW};
use monitor::MonitorHandle as RootMonitorHandle;
use window::{WindowAttributes, CursorIcon};
use self::x11::{XConnection, XError};
use self::x11::ffi::XVisualInfo;
pub use self::x11::XNotSupported; pub use self::x11::XNotSupported;
use self::x11::{ffi::XVisualInfo, XConnection, XError};
use crate::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
event::Event,
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
icon::Icon,
monitor::{MonitorHandle as RootMonitorHandle, VideoMode},
window::{CursorIcon, WindowAttributes},
};
mod dlopen; mod dlopen;
pub mod wayland; pub mod wayland;
@ -43,14 +40,13 @@ pub struct PlatformSpecificWindowBuilderAttributes {
pub override_redirect: bool, pub override_redirect: bool,
pub x11_window_type: x11::util::WindowType, pub x11_window_type: x11::util::WindowType,
pub gtk_theme_variant: Option<String>, pub gtk_theme_variant: Option<String>,
pub app_id: Option<String> pub app_id: Option<String>,
} }
lazy_static!( lazy_static! {
pub static ref X11_BACKEND: Mutex<Result<Arc<XConnection>, XNotSupported>> = { pub static ref X11_BACKEND: Mutex<Result<Arc<XConnection>, XNotSupported>> =
Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)) { Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)) };
}; }
);
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum OsError { pub enum OsError {
@ -59,10 +55,10 @@ pub enum OsError {
} }
impl fmt::Display for OsError { impl fmt::Display for OsError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self { match self {
OsError::XError(e) => formatter.pad(&e.description), OsError::XError(e) => f.pad(&e.description),
OsError::XMisc(e) => formatter.pad(e), OsError::XMisc(e) => f.pad(e),
} }
} }
} }
@ -120,10 +116,10 @@ impl MonitorHandle {
} }
#[inline] #[inline]
pub fn dimensions(&self) -> PhysicalSize { pub fn size(&self) -> PhysicalSize {
match self { match self {
&MonitorHandle::X(ref m) => m.dimensions(), &MonitorHandle::X(ref m) => m.size(),
&MonitorHandle::Wayland(ref m) => m.dimensions(), &MonitorHandle::Wayland(ref m) => m.size(),
} }
} }
@ -142,6 +138,14 @@ impl MonitorHandle {
&MonitorHandle::Wayland(ref m) => m.hidpi_factor() as f64, &MonitorHandle::Wayland(ref m) => m.hidpi_factor() as f64,
} }
} }
#[inline]
pub fn video_modes(&self) -> Box<dyn Iterator<Item = VideoMode>> {
match self {
MonitorHandle::X(m) => Box::new(m.video_modes()),
MonitorHandle::Wayland(m) => Box::new(m.video_modes()),
}
}
} }
impl Window { impl Window {
@ -154,10 +158,10 @@ impl Window {
match *window_target { match *window_target {
EventLoopWindowTarget::Wayland(ref window_target) => { EventLoopWindowTarget::Wayland(ref window_target) => {
wayland::Window::new(window_target, attribs, pl_attribs).map(Window::Wayland) wayland::Window::new(window_target, attribs, pl_attribs).map(Window::Wayland)
}, }
EventLoopWindowTarget::X(ref window_target) => { EventLoopWindowTarget::X(ref window_target) => {
x11::Window::new(window_target, attribs, pl_attribs).map(Window::X) x11::Window::new(window_target, attribs, pl_attribs).map(Window::X)
}, }
} }
} }
@ -261,7 +265,7 @@ impl Window {
pub fn set_cursor_icon(&self, cursor: CursorIcon) { pub fn set_cursor_icon(&self, cursor: CursorIcon) {
match self { match self {
&Window::X(ref w) => w.set_cursor_icon(cursor), &Window::X(ref w) => w.set_cursor_icon(cursor),
&Window::Wayland(ref w) => w.set_cursor_icon(cursor) &Window::Wayland(ref w) => w.set_cursor_icon(cursor),
} }
} }
@ -283,7 +287,7 @@ impl Window {
#[inline] #[inline]
pub fn hidpi_factor(&self) -> f64 { pub fn hidpi_factor(&self) -> f64 {
match self { match self {
&Window::X(ref w) => w.hidpi_factor(), &Window::X(ref w) => w.hidpi_factor(),
&Window::Wayland(ref w) => w.hidpi_factor() as f64, &Window::Wayland(ref w) => w.hidpi_factor() as f64,
} }
@ -309,8 +313,9 @@ impl Window {
pub fn fullscreen(&self) -> Option<RootMonitorHandle> { pub fn fullscreen(&self) -> Option<RootMonitorHandle> {
match self { match self {
&Window::X(ref w) => w.fullscreen(), &Window::X(ref w) => w.fullscreen(),
&Window::Wayland(ref w) => w.fullscreen() &Window::Wayland(ref w) => w.fullscreen().map(|monitor_id| RootMonitorHandle {
.map(|monitor_id| RootMonitorHandle { inner: MonitorHandle::Wayland(monitor_id) }) inner: MonitorHandle::Wayland(monitor_id),
}),
} }
} }
@ -318,7 +323,7 @@ impl Window {
pub fn set_fullscreen(&self, monitor: Option<RootMonitorHandle>) { pub fn set_fullscreen(&self, monitor: Option<RootMonitorHandle>) {
match self { match self {
&Window::X(ref w) => w.set_fullscreen(monitor), &Window::X(ref w) => w.set_fullscreen(monitor),
&Window::Wayland(ref w) => w.set_fullscreen(monitor) &Window::Wayland(ref w) => w.set_fullscreen(monitor),
} }
} }
@ -326,7 +331,7 @@ impl Window {
pub fn set_decorations(&self, decorations: bool) { pub fn set_decorations(&self, decorations: bool) {
match self { match self {
&Window::X(ref w) => w.set_decorations(decorations), &Window::X(ref w) => w.set_decorations(decorations),
&Window::Wayland(ref w) => w.set_decorations(decorations) &Window::Wayland(ref w) => w.set_decorations(decorations),
} }
} }
@ -365,19 +370,25 @@ impl Window {
#[inline] #[inline]
pub fn current_monitor(&self) -> RootMonitorHandle { pub fn current_monitor(&self) -> RootMonitorHandle {
match self { match self {
&Window::X(ref window) => RootMonitorHandle { inner: MonitorHandle::X(window.current_monitor()) }, &Window::X(ref window) => RootMonitorHandle {
&Window::Wayland(ref window) => RootMonitorHandle { inner: MonitorHandle::Wayland(window.current_monitor()) }, inner: MonitorHandle::X(window.current_monitor()),
},
&Window::Wayland(ref window) => RootMonitorHandle {
inner: MonitorHandle::Wayland(window.current_monitor()),
},
} }
} }
#[inline] #[inline]
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> { pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
match self { match self {
&Window::X(ref window) => window.available_monitors() &Window::X(ref window) => window
.available_monitors()
.into_iter() .into_iter()
.map(MonitorHandle::X) .map(MonitorHandle::X)
.collect(), .collect(),
&Window::Wayland(ref window) => window.available_monitors() &Window::Wayland(ref window) => window
.available_monitors()
.into_iter() .into_iter()
.map(MonitorHandle::Wayland) .map(MonitorHandle::Wayland)
.collect(), .collect(),
@ -393,7 +404,6 @@ impl Window {
} }
} }
unsafe extern "C" fn x_error_callback( unsafe extern "C" fn x_error_callback(
display: *mut x11::ffi::Display, display: *mut x11::ffi::Display,
event: *mut x11::ffi::XErrorEvent, event: *mut x11::ffi::XErrorEvent,
@ -424,10 +434,9 @@ unsafe extern "C" fn x_error_callback(
0 0
} }
pub enum EventLoop<T: 'static> { pub enum EventLoop<T: 'static> {
Wayland(wayland::EventLoop<T>), Wayland(wayland::EventLoop<T>),
X(x11::EventLoop<T>) X(x11::EventLoop<T>),
} }
#[derive(Clone)] #[derive(Clone)]
@ -436,18 +445,17 @@ pub enum EventLoopProxy<T: 'static> {
Wayland(wayland::EventLoopProxy<T>), Wayland(wayland::EventLoopProxy<T>),
} }
impl<T:'static> EventLoop<T> { impl<T: 'static> EventLoop<T> {
pub fn new() -> EventLoop<T> { pub fn new() -> EventLoop<T> {
if let Ok(env_var) = env::var(BACKEND_PREFERENCE_ENV_VAR) { if let Ok(env_var) = env::var(BACKEND_PREFERENCE_ENV_VAR) {
match env_var.as_str() { match env_var.as_str() {
"x11" => { "x11" => {
// TODO: propagate // TODO: propagate
return EventLoop::new_x11().expect("Failed to initialize X11 backend"); return EventLoop::new_x11().expect("Failed to initialize X11 backend");
}, }
"wayland" => { "wayland" => {
return EventLoop::new_wayland() return EventLoop::new_wayland().expect("Failed to initialize Wayland backend");
.expect("Failed to initialize Wayland backend"); }
},
_ => panic!( _ => panic!(
"Unknown environment variable value for {}, try one of `x11`,`wayland`", "Unknown environment variable value for {}, try one of `x11`,`wayland`",
BACKEND_PREFERENCE_ENV_VAR, BACKEND_PREFERENCE_ENV_VAR,
@ -467,15 +475,13 @@ impl<T:'static> EventLoop<T> {
let err_string = format!( let err_string = format!(
"Failed to initialize any backend! Wayland status: {:?} X11 status: {:?}", "Failed to initialize any backend! Wayland status: {:?} X11 status: {:?}",
wayland_err, wayland_err, x11_err,
x11_err,
); );
panic!(err_string); panic!(err_string);
} }
pub fn new_wayland() -> Result<EventLoop<T>, ConnectError> { pub fn new_wayland() -> Result<EventLoop<T>, ConnectError> {
wayland::EventLoop::new() wayland::EventLoop::new().map(EventLoop::Wayland)
.map(EventLoop::Wayland)
} }
pub fn new_x11() -> Result<EventLoop<T>, XNotSupported> { pub fn new_x11() -> Result<EventLoop<T>, XNotSupported> {
@ -521,20 +527,22 @@ impl<T:'static> EventLoop<T> {
} }
pub fn run_return<F>(&mut self, callback: F) pub fn run_return<F>(&mut self, callback: F)
where F: FnMut(::event::Event<T>, &RootELW<T>, &mut ControlFlow) where
F: FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
{ {
match *self { match *self {
EventLoop::Wayland(ref mut evlp) => evlp.run_return(callback), EventLoop::Wayland(ref mut evlp) => evlp.run_return(callback),
EventLoop::X(ref mut evlp) => evlp.run_return(callback) EventLoop::X(ref mut evlp) => evlp.run_return(callback),
} }
} }
pub fn run<F>(self, callback: F) -> ! pub fn run<F>(self, callback: F) -> !
where F: 'static + FnMut(::event::Event<T>, &RootELW<T>, &mut ControlFlow) where
F: 'static + FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
{ {
match self { match self {
EventLoop::Wayland(evlp) => evlp.run(callback), EventLoop::Wayland(evlp) => evlp.run(callback),
EventLoop::X(evlp) => evlp.run(callback) EventLoop::X(evlp) => evlp.run(callback),
} }
} }
@ -546,10 +554,10 @@ impl<T:'static> EventLoop<T> {
} }
} }
pub fn window_target(&self) -> &::event_loop::EventLoopWindowTarget<T> { pub fn window_target(&self) -> &crate::event_loop::EventLoopWindowTarget<T> {
match *self { match *self {
EventLoop::Wayland(ref evl) => evl.window_target(), EventLoop::Wayland(ref evl) => evl.window_target(),
EventLoop::X(ref evl) => evl.window_target() EventLoop::X(ref evl) => evl.window_target(),
} }
} }
} }
@ -565,12 +573,16 @@ impl<T: 'static> EventLoopProxy<T> {
pub enum EventLoopWindowTarget<T> { pub enum EventLoopWindowTarget<T> {
Wayland(wayland::EventLoopWindowTarget<T>), Wayland(wayland::EventLoopWindowTarget<T>),
X(x11::EventLoopWindowTarget<T>) X(x11::EventLoopWindowTarget<T>),
} }
fn sticky_exit_callback<T, F>( fn sticky_exit_callback<T, F>(
evt: Event<T>, target: &RootELW<T>, control_flow: &mut ControlFlow, callback: &mut F evt: Event<T>,
) where F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow) target: &RootELW<T>,
control_flow: &mut ControlFlow,
callback: &mut F,
) where
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
{ {
// make ControlFlow::Exit sticky by providing a dummy // make ControlFlow::Exit sticky by providing a dummy
// control flow reference if it is already Exit. // control flow reference if it is already Exit.

View file

@ -1,46 +1,67 @@
use std::cell::RefCell; use std::{
use std::collections::VecDeque; cell::RefCell,
use std::fmt; collections::VecDeque,
use std::rc::Rc; fmt,
use std::sync::{Arc, Mutex}; rc::Rc,
use std::time::Instant; sync::{Arc, Mutex},
time::Instant,
use event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW};
use event::ModifiersState;
use dpi::{PhysicalPosition, PhysicalSize};
use platform_impl::platform::sticky_exit_callback;
use super::window::WindowStore;
use super::WindowId;
use sctk::output::OutputMgr;
use sctk::reexports::client::protocol::{
wl_keyboard, wl_output, wl_pointer, wl_registry, wl_seat, wl_touch,
}; };
use sctk::reexports::client::{ConnectError, Display, EventQueue, GlobalEvent};
use sctk::Environment;
pub struct WindowEventsSink { use smithay_client_toolkit::reexports::protocols::unstable::relative_pointer::v1::client::{
buffer: VecDeque<(::event::WindowEvent, ::window::WindowId)>, zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
zwp_relative_pointer_v1::ZwpRelativePointerV1,
};
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
event::ModifiersState,
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
monitor::VideoMode,
platform_impl::platform::sticky_exit_callback,
};
use super::{window::WindowStore, DeviceId, WindowId};
use smithay_client_toolkit::{
output::OutputMgr,
reexports::client::{
protocol::{wl_keyboard, wl_output, wl_pointer, wl_registry, wl_seat, wl_touch},
ConnectError, Display, EventQueue, GlobalEvent,
},
Environment,
};
pub struct WindowEventsSink<T> {
buffer: VecDeque<crate::event::Event<T>>,
} }
impl WindowEventsSink { impl<T> WindowEventsSink<T> {
pub fn new() -> WindowEventsSink { pub fn new() -> WindowEventsSink<T> {
WindowEventsSink { WindowEventsSink {
buffer: VecDeque::new(), buffer: VecDeque::new(),
} }
} }
pub fn send_event(&mut self, evt: ::event::WindowEvent, wid: WindowId) { pub fn send_window_event(&mut self, evt: crate::event::WindowEvent, wid: WindowId) {
self.buffer.push_back((evt, ::window::WindowId(::platform_impl::WindowId::Wayland(wid)))); self.buffer.push_back(crate::event::Event::WindowEvent {
event: evt,
window_id: crate::window::WindowId(crate::platform_impl::WindowId::Wayland(wid)),
});
} }
fn empty_with<F, T>(&mut self, mut callback: F) pub fn send_device_event(&mut self, evt: crate::event::DeviceEvent, dev_id: DeviceId) {
self.buffer.push_back(crate::event::Event::DeviceEvent {
event: evt,
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(dev_id)),
});
}
fn empty_with<F>(&mut self, mut callback: F)
where where
F: FnMut(::event::Event<T>), F: FnMut(crate::event::Event<T>),
{ {
for (evt, wid) in self.buffer.drain(..) { for evt in self.buffer.drain(..) {
callback(::event::Event::WindowEvent { event: evt, window_id: wid}) callback(evt)
} }
} }
} }
@ -53,12 +74,14 @@ pub struct EventLoop<T: 'static> {
// the output manager // the output manager
pub outputs: OutputMgr, pub outputs: OutputMgr,
// our sink, shared with some handlers, buffering the events // our sink, shared with some handlers, buffering the events
sink: Arc<Mutex<WindowEventsSink>>, sink: Arc<Mutex<WindowEventsSink<T>>>,
pending_user_events: Rc<RefCell<VecDeque<T>>>, pending_user_events: Rc<RefCell<VecDeque<T>>>,
_user_source: ::calloop::Source<::calloop::channel::Channel<T>>, _user_source: ::calloop::Source<::calloop::channel::Channel<T>>,
user_sender: ::calloop::channel::Sender<T>, user_sender: ::calloop::channel::Sender<T>,
_kbd_source: ::calloop::Source<::calloop::channel::Channel<(::event::WindowEvent, super::WindowId)>>, _kbd_source: ::calloop::Source<
window_target: RootELW<T> ::calloop::channel::Channel<(crate::event::WindowEvent, super::WindowId)>,
>,
window_target: RootELW<T>,
} }
// A handle that can be sent across threads and used to wake up the `EventLoop`. // A handle that can be sent across threads and used to wake up the `EventLoop`.
@ -66,7 +89,7 @@ pub struct EventLoop<T: 'static> {
// We should only try and wake up the `EventLoop` if it still exists, so we hold Weak ptrs. // We should only try and wake up the `EventLoop` if it still exists, so we hold Weak ptrs.
#[derive(Clone)] #[derive(Clone)]
pub struct EventLoopProxy<T: 'static> { pub struct EventLoopProxy<T: 'static> {
user_sender: ::calloop::channel::Sender<T> user_sender: ::calloop::channel::Sender<T>,
} }
pub struct EventLoopWindowTarget<T> { pub struct EventLoopWindowTarget<T> {
@ -82,7 +105,7 @@ pub struct EventLoopWindowTarget<T> {
pub display: Arc<Display>, pub display: Arc<Display>,
// The list of seats // The list of seats
pub seats: Arc<Mutex<Vec<(u32, wl_seat::WlSeat)>>>, pub seats: Arc<Mutex<Vec<(u32, wl_seat::WlSeat)>>>,
_marker: ::std::marker::PhantomData<T> _marker: ::std::marker::PhantomData<T>,
} }
impl<T: 'static> EventLoopProxy<T> { impl<T: 'static> EventLoopProxy<T> {
@ -104,14 +127,18 @@ impl<T: 'static> EventLoop<T> {
let (kbd_sender, kbd_channel) = ::calloop::channel::channel(); let (kbd_sender, kbd_channel) = ::calloop::channel::channel();
let kbd_sink = sink.clone(); let kbd_sink = sink.clone();
let kbd_source = inner_loop.handle().insert_source(kbd_channel, move |evt, &mut()| { let kbd_source = inner_loop
if let ::calloop::channel::Event::Msg((evt, wid)) = evt { .handle()
kbd_sink.lock().unwrap().send_event(evt, wid); .insert_source(kbd_channel, move |evt, &mut ()| {
} if let ::calloop::channel::Event::Msg((evt, wid)) = evt {
}).unwrap(); kbd_sink.lock().unwrap().send_window_event(evt, wid);
}
})
.unwrap();
let mut seat_manager = SeatManager { let mut seat_manager = SeatManager {
sink: sink.clone(), sink: sink.clone(),
relative_pointer_manager_proxy: None,
store: store.clone(), store: store.clone(),
seats: seats.clone(), seats: seats.clone(),
kbd_sender, kbd_sender,
@ -120,34 +147,52 @@ impl<T: 'static> EventLoop<T> {
let env = Environment::from_display_with_cb( let env = Environment::from_display_with_cb(
&display, &display,
&mut event_queue, &mut event_queue,
move |event, registry| { move |event, registry| match event {
match event { GlobalEvent::New {
GlobalEvent::New { id, ref interface, version } => { id,
if interface == "wl_seat" { ref interface,
seat_manager.add_seat(id, version, registry) version,
} } => {
}, if interface == "zwp_relative_pointer_manager_v1" {
GlobalEvent::Removed { id, ref interface } => { seat_manager.relative_pointer_manager_proxy = Some(
if interface == "wl_seat" { registry
seat_manager.remove_seat(id) .bind(version, id, move |pointer_manager| {
} pointer_manager.implement_closure(|_, _| (), ())
}, })
.unwrap(),
)
}
if interface == "wl_seat" {
seat_manager.add_seat(id, version, registry)
}
}
GlobalEvent::Removed { id, ref interface } => {
if interface == "wl_seat" {
seat_manager.remove_seat(id)
}
} }
}, },
).unwrap(); )
.unwrap();
let source = inner_loop.handle().insert_source(event_queue, |(), &mut ()| {}).unwrap(); let source = inner_loop
.handle()
.insert_source(event_queue, |(), &mut ()| {})
.unwrap();
let pending_user_events = Rc::new(RefCell::new(VecDeque::new())); let pending_user_events = Rc::new(RefCell::new(VecDeque::new()));
let pending_user_events2 = pending_user_events.clone(); let pending_user_events2 = pending_user_events.clone();
let (user_sender, user_channel) = ::calloop::channel::channel(); let (user_sender, user_channel) = ::calloop::channel::channel();
let user_source = inner_loop.handle().insert_source(user_channel, move |evt, &mut()| { let user_source = inner_loop
if let ::calloop::channel::Event::Msg(msg) = evt { .handle()
pending_user_events2.borrow_mut().push_back(msg); .insert_source(user_channel, move |evt, &mut ()| {
} if let ::calloop::channel::Event::Msg(msg) = evt {
}).unwrap(); pending_user_events2.borrow_mut().push_back(msg);
}
})
.unwrap();
Ok(EventLoop { Ok(EventLoop {
inner_loop, inner_loop,
@ -159,35 +204,37 @@ impl<T: 'static> EventLoop<T> {
user_sender, user_sender,
_kbd_source: kbd_source, _kbd_source: kbd_source,
window_target: RootELW { window_target: RootELW {
p: ::platform_impl::EventLoopWindowTarget::Wayland(EventLoopWindowTarget { p: crate::platform_impl::EventLoopWindowTarget::Wayland(EventLoopWindowTarget {
evq: RefCell::new(source), evq: RefCell::new(source),
store, store,
env, env,
cleanup_needed: Arc::new(Mutex::new(false)), cleanup_needed: Arc::new(Mutex::new(false)),
seats, seats,
display, display,
_marker: ::std::marker::PhantomData _marker: ::std::marker::PhantomData,
}), }),
_marker: ::std::marker::PhantomData _marker: ::std::marker::PhantomData,
} },
}) })
} }
pub fn create_proxy(&self) -> EventLoopProxy<T> { pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy { EventLoopProxy {
user_sender: self.user_sender.clone() user_sender: self.user_sender.clone(),
} }
} }
pub fn run<F>(mut self, callback: F) -> ! pub fn run<F>(mut self, callback: F) -> !
where F: 'static + FnMut(::event::Event<T>, &RootELW<T>, &mut ControlFlow) where
F: 'static + FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
{ {
self.run_return(callback); self.run_return(callback);
::std::process::exit(0); ::std::process::exit(0);
} }
pub fn run_return<F>(&mut self, mut callback: F) pub fn run_return<F>(&mut self, mut callback: F)
where F: FnMut(::event::Event<T>, &RootELW<T>, &mut ControlFlow) where
F: FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
{ {
// send pending events to the server // send pending events to the server
self.display.flush().expect("Wayland connection lost."); self.display.flush().expect("Wayland connection lost.");
@ -197,7 +244,11 @@ impl<T: 'static> EventLoop<T> {
let sink = self.sink.clone(); let sink = self.sink.clone();
let user_events = self.pending_user_events.clone(); let user_events = self.pending_user_events.clone();
callback(::event::Event::NewEvents(::event::StartCause::Init), &self.window_target, &mut control_flow); callback(
crate::event::Event::NewEvents(crate::event::StartCause::Init),
&self.window_target,
&mut control_flow,
);
loop { loop {
self.post_dispatch_triggers(); self.post_dispatch_triggers();
@ -206,7 +257,12 @@ impl<T: 'static> EventLoop<T> {
{ {
let mut guard = sink.lock().unwrap(); let mut guard = sink.lock().unwrap();
guard.empty_with(|evt| { guard.empty_with(|evt| {
sticky_exit_callback(evt, &self.window_target, &mut control_flow, &mut callback); sticky_exit_callback(
evt,
&self.window_target,
&mut control_flow,
&mut callback,
);
}); });
} }
// empty user events // empty user events
@ -214,10 +270,10 @@ impl<T: 'static> EventLoop<T> {
let mut guard = user_events.borrow_mut(); let mut guard = user_events.borrow_mut();
for evt in guard.drain(..) { for evt in guard.drain(..) {
sticky_exit_callback( sticky_exit_callback(
::event::Event::UserEvent(evt), crate::event::Event::UserEvent(evt),
&self.window_target, &self.window_target,
&mut control_flow, &mut control_flow,
&mut callback &mut callback,
); );
} }
} }
@ -227,16 +283,21 @@ impl<T: 'static> EventLoop<T> {
{ {
let mut guard = sink.lock().unwrap(); let mut guard = sink.lock().unwrap();
guard.empty_with(|evt| { guard.empty_with(|evt| {
sticky_exit_callback(evt, &self.window_target, &mut control_flow, &mut callback); sticky_exit_callback(
evt,
&self.window_target,
&mut control_flow,
&mut callback,
);
}); });
} }
// send Events cleared // send Events cleared
{ {
sticky_exit_callback( sticky_exit_callback(
::event::Event::EventsCleared, crate::event::Event::EventsCleared,
&self.window_target, &self.window_target,
&mut control_flow, &mut control_flow,
&mut callback &mut callback,
); );
} }
@ -247,22 +308,26 @@ impl<T: 'static> EventLoop<T> {
ControlFlow::Exit => break, ControlFlow::Exit => break,
ControlFlow::Poll => { ControlFlow::Poll => {
// non-blocking dispatch // non-blocking dispatch
self.inner_loop.dispatch(Some(::std::time::Duration::from_millis(0)), &mut ()).unwrap(); self.inner_loop
control_flow = ControlFlow::default(); .dispatch(Some(::std::time::Duration::from_millis(0)), &mut ())
callback(::event::Event::NewEvents(::event::StartCause::Poll), &self.window_target, &mut control_flow); .unwrap();
}, callback(
crate::event::Event::NewEvents(crate::event::StartCause::Poll),
&self.window_target,
&mut control_flow,
);
}
ControlFlow::Wait => { ControlFlow::Wait => {
self.inner_loop.dispatch(None, &mut ()).unwrap(); self.inner_loop.dispatch(None, &mut ()).unwrap();
control_flow = ControlFlow::default();
callback( callback(
::event::Event::NewEvents(::event::StartCause::WaitCancelled { crate::event::Event::NewEvents(crate::event::StartCause::WaitCancelled {
start: Instant::now(), start: Instant::now(),
requested_resume: None requested_resume: None,
}), }),
&self.window_target, &self.window_target,
&mut control_flow &mut control_flow,
); );
}, }
ControlFlow::WaitUntil(deadline) => { ControlFlow::WaitUntil(deadline) => {
let start = Instant::now(); let start = Instant::now();
// compute the blocking duration // compute the blocking duration
@ -272,32 +337,39 @@ impl<T: 'static> EventLoop<T> {
::std::time::Duration::from_millis(0) ::std::time::Duration::from_millis(0)
}; };
self.inner_loop.dispatch(Some(duration), &mut ()).unwrap(); self.inner_loop.dispatch(Some(duration), &mut ()).unwrap();
control_flow = ControlFlow::default();
let now = Instant::now(); let now = Instant::now();
if now < deadline { if now < deadline {
callback( callback(
::event::Event::NewEvents(::event::StartCause::WaitCancelled { crate::event::Event::NewEvents(
start, crate::event::StartCause::WaitCancelled {
requested_resume: Some(deadline) start,
}), requested_resume: Some(deadline),
},
),
&self.window_target, &self.window_target,
&mut control_flow &mut control_flow,
); );
} else { } else {
callback( callback(
::event::Event::NewEvents(::event::StartCause::ResumeTimeReached { crate::event::Event::NewEvents(
start, crate::event::StartCause::ResumeTimeReached {
requested_resume: deadline start,
}), requested_resume: deadline,
},
),
&self.window_target, &self.window_target,
&mut control_flow &mut control_flow,
); );
} }
}, }
} }
} }
callback(::event::Event::LoopDestroyed, &self.window_target, &mut control_flow); callback(
crate::event::Event::LoopDestroyed,
&self.window_target,
&mut control_flow,
);
} }
pub fn primary_monitor(&self) -> MonitorHandle { pub fn primary_monitor(&self) -> MonitorHandle {
@ -325,8 +397,8 @@ impl<T> EventLoop<T> {
fn post_dispatch_triggers(&mut self) { fn post_dispatch_triggers(&mut self) {
let mut sink = self.sink.lock().unwrap(); let mut sink = self.sink.lock().unwrap();
let window_target = match self.window_target.p { let window_target = match self.window_target.p {
::platform_impl::EventLoopWindowTarget::Wayland(ref wt) => wt, crate::platform_impl::EventLoopWindowTarget::Wayland(ref wt) => wt,
_ => unreachable!() _ => unreachable!(),
}; };
// prune possible dead windows // prune possible dead windows
{ {
@ -335,7 +407,7 @@ impl<T> EventLoop<T> {
let pruned = window_target.store.lock().unwrap().cleanup(); let pruned = window_target.store.lock().unwrap().cleanup();
*cleanup_needed = false; *cleanup_needed = false;
for wid in pruned { for wid in pruned {
sink.send_event(::event::WindowEvent::Destroyed, wid); sink.send_window_event(crate::event::WindowEvent::Destroyed, wid);
} }
} }
} }
@ -346,8 +418,11 @@ impl<T> EventLoop<T> {
if let Some((w, h)) = newsize { if let Some((w, h)) = newsize {
frame.resize(w, h); frame.resize(w, h);
frame.refresh(); frame.refresh();
let logical_size = ::dpi::LogicalSize::new(w as f64, h as f64); let logical_size = crate::dpi::LogicalSize::new(w as f64, h as f64);
sink.send_event(::event::WindowEvent::Resized(logical_size), wid); sink.send_window_event(
crate::event::WindowEvent::Resized(logical_size),
wid,
);
*size = (w, h); *size = (w, h);
} else if frame_refresh { } else if frame_refresh {
frame.refresh(); frame.refresh();
@ -357,13 +432,16 @@ impl<T> EventLoop<T> {
} }
} }
if let Some(dpi) = new_dpi { if let Some(dpi) = new_dpi {
sink.send_event(::event::WindowEvent::HiDpiFactorChanged(dpi as f64), wid); sink.send_window_event(
crate::event::WindowEvent::HiDpiFactorChanged(dpi as f64),
wid,
);
} }
if refresh { if refresh {
sink.send_event(::event::WindowEvent::RedrawRequested, wid); sink.send_window_event(crate::event::WindowEvent::RedrawRequested, wid);
} }
if closed { if closed {
sink.send_event(::event::WindowEvent::CloseRequested, wid); sink.send_window_event(crate::event::WindowEvent::CloseRequested, wid);
} }
}, },
) )
@ -374,14 +452,15 @@ impl<T> EventLoop<T> {
* Wayland protocol implementations * Wayland protocol implementations
*/ */
struct SeatManager { struct SeatManager<T: 'static> {
sink: Arc<Mutex<WindowEventsSink>>, sink: Arc<Mutex<WindowEventsSink<T>>>,
store: Arc<Mutex<WindowStore>>, store: Arc<Mutex<WindowStore>>,
seats: Arc<Mutex<Vec<(u32, wl_seat::WlSeat)>>>, seats: Arc<Mutex<Vec<(u32, wl_seat::WlSeat)>>>,
kbd_sender: ::calloop::channel::Sender<(::event::WindowEvent, super::WindowId)> kbd_sender: ::calloop::channel::Sender<(crate::event::WindowEvent, super::WindowId)>,
relative_pointer_manager_proxy: Option<ZwpRelativePointerManagerV1>,
} }
impl SeatManager { impl<T: 'static> SeatManager<T> {
fn add_seat(&mut self, id: u32, version: u32, registry: wl_registry::WlRegistry) { fn add_seat(&mut self, id: u32, version: u32, registry: wl_registry::WlRegistry) {
use std::cmp::min; use std::cmp::min;
@ -389,6 +468,8 @@ impl SeatManager {
sink: self.sink.clone(), sink: self.sink.clone(),
store: self.store.clone(), store: self.store.clone(),
pointer: None, pointer: None,
relative_pointer: None,
relative_pointer_manager_proxy: self.relative_pointer_manager_proxy.as_ref().cloned(),
keyboard: None, keyboard: None,
touch: None, touch: None,
kbd_sender: self.kbd_sender.clone(), kbd_sender: self.kbd_sender.clone(),
@ -396,9 +477,7 @@ impl SeatManager {
}; };
let seat = registry let seat = registry
.bind(min(version, 5), id, move |seat| { .bind(min(version, 5), id, move |seat| {
seat.implement_closure(move |event, seat| { seat.implement_closure(move |event, seat| seat_data.receive(event, seat), ())
seat_data.receive(event, seat)
}, ())
}) })
.unwrap(); .unwrap();
self.store.lock().unwrap().new_seat(&seat); self.store.lock().unwrap().new_seat(&seat);
@ -416,17 +495,19 @@ impl SeatManager {
} }
} }
struct SeatData { struct SeatData<T> {
sink: Arc<Mutex<WindowEventsSink>>, sink: Arc<Mutex<WindowEventsSink<T>>>,
store: Arc<Mutex<WindowStore>>, store: Arc<Mutex<WindowStore>>,
kbd_sender: ::calloop::channel::Sender<(::event::WindowEvent, super::WindowId)>, kbd_sender: ::calloop::channel::Sender<(crate::event::WindowEvent, super::WindowId)>,
pointer: Option<wl_pointer::WlPointer>, pointer: Option<wl_pointer::WlPointer>,
relative_pointer: Option<ZwpRelativePointerV1>,
relative_pointer_manager_proxy: Option<ZwpRelativePointerManagerV1>,
keyboard: Option<wl_keyboard::WlKeyboard>, keyboard: Option<wl_keyboard::WlKeyboard>,
touch: Option<wl_touch::WlTouch>, touch: Option<wl_touch::WlTouch>,
modifiers_tracker: Arc<Mutex<ModifiersState>>, modifiers_tracker: Arc<Mutex<ModifiersState>>,
} }
impl SeatData { impl<T: 'static> SeatData<T> {
fn receive(&mut self, evt: wl_seat::Event, seat: wl_seat::WlSeat) { fn receive(&mut self, evt: wl_seat::Event, seat: wl_seat::WlSeat) {
match evt { match evt {
wl_seat::Event::Name { .. } => (), wl_seat::Event::Name { .. } => (),
@ -438,7 +519,19 @@ impl SeatData {
self.sink.clone(), self.sink.clone(),
self.store.clone(), self.store.clone(),
self.modifiers_tracker.clone(), self.modifiers_tracker.clone(),
)) ));
self.relative_pointer =
self.relative_pointer_manager_proxy
.as_ref()
.and_then(|manager| {
super::pointer::implement_relative_pointer(
self.sink.clone(),
self.pointer.as_ref().unwrap(),
manager,
)
.ok()
})
} }
// destroy pointer if applicable // destroy pointer if applicable
if !capabilities.contains(wl_seat::Capability::Pointer) { if !capabilities.contains(wl_seat::Capability::Pointer) {
@ -480,13 +573,13 @@ impl SeatData {
} }
} }
} }
}, }
_ => unreachable!() _ => unreachable!(),
} }
} }
} }
impl Drop for SeatData { impl<T> Drop for SeatData<T> {
fn drop(&mut self) { fn drop(&mut self) {
if let Some(pointer) = self.pointer.take() { if let Some(pointer) = self.pointer.take() {
if pointer.as_ref().version() >= 3 { if pointer.as_ref().version() >= 3 {
@ -525,12 +618,12 @@ impl Clone for MonitorHandle {
} }
impl fmt::Debug for MonitorHandle { impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[derive(Debug)] #[derive(Debug)]
struct MonitorHandle { struct MonitorHandle {
name: Option<String>, name: Option<String>,
native_identifier: u32, native_identifier: u32,
dimensions: PhysicalSize, size: PhysicalSize,
position: PhysicalPosition, position: PhysicalPosition,
hidpi_factor: i32, hidpi_factor: i32,
} }
@ -538,7 +631,7 @@ impl fmt::Debug for MonitorHandle {
let monitor_id_proxy = MonitorHandle { let monitor_id_proxy = MonitorHandle {
name: self.name(), name: self.name(),
native_identifier: self.native_identifier(), native_identifier: self.native_identifier(),
dimensions: self.dimensions(), size: self.size(),
position: self.position(), position: self.position(),
hidpi_factor: self.hidpi_factor(), hidpi_factor: self.hidpi_factor(),
}; };
@ -559,7 +652,7 @@ impl MonitorHandle {
self.mgr.with_info(&self.proxy, |id, _| id).unwrap_or(0) self.mgr.with_info(&self.proxy, |id, _| id).unwrap_or(0)
} }
pub fn dimensions(&self) -> PhysicalSize { pub fn size(&self) -> PhysicalSize {
match self.mgr.with_info(&self.proxy, |_, info| { match self.mgr.with_info(&self.proxy, |_, info| {
info.modes info.modes
.iter() .iter()
@ -568,7 +661,8 @@ impl MonitorHandle {
}) { }) {
Some(Some((w, h))) => (w as u32, h as u32), Some(Some((w, h))) => (w as u32, h as u32),
_ => (0, 0), _ => (0, 0),
}.into() }
.into()
} }
pub fn position(&self) -> PhysicalPosition { pub fn position(&self) -> PhysicalPosition {
@ -584,6 +678,19 @@ impl MonitorHandle {
.with_info(&self.proxy, |_, info| info.scale_factor) .with_info(&self.proxy, |_, info| info.scale_factor)
.unwrap_or(1) .unwrap_or(1)
} }
#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.mgr
.with_info(&self.proxy, |_, info| info.modes.clone())
.unwrap_or(vec![])
.into_iter()
.map(|x| VideoMode {
size: (x.dimensions.0 as u32, x.dimensions.1 as u32),
refresh_rate: (x.refresh_rate as f32 / 1000.0).round() as u16,
bit_depth: 32,
})
}
} }
pub fn primary_monitor(outputs: &OutputMgr) -> MonitorHandle { pub fn primary_monitor(outputs: &OutputMgr) -> MonitorHandle {

View file

@ -1,16 +1,18 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use super::{make_wid, DeviceId}; use super::{make_wid, DeviceId};
use sctk::keyboard::{ use smithay_client_toolkit::{
self, map_keyboard_auto_with_repeat, Event as KbEvent, KeyRepeatEvent, KeyRepeatKind, keyboard::{
self, map_keyboard_auto_with_repeat, Event as KbEvent, KeyRepeatEvent, KeyRepeatKind,
},
reexports::client::protocol::{wl_keyboard, wl_seat},
}; };
use sctk::reexports::client::protocol::{wl_keyboard, wl_seat};
use event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent}; use crate::event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent};
pub fn init_keyboard( pub fn init_keyboard(
seat: &wl_seat::WlSeat, seat: &wl_seat::WlSeat,
sink: ::calloop::channel::Sender<(::event::WindowEvent, super::WindowId)>, sink: ::calloop::channel::Sender<(crate::event::WindowEvent, super::WindowId)>,
modifiers_tracker: Arc<Mutex<ModifiersState>>, modifiers_tracker: Arc<Mutex<ModifiersState>>,
) -> wl_keyboard::WlKeyboard { ) -> wl_keyboard::WlKeyboard {
// { variables to be captured by the closures // { variables to be captured by the closures
@ -23,78 +25,92 @@ pub fn init_keyboard(
let ret = map_keyboard_auto_with_repeat( let ret = map_keyboard_auto_with_repeat(
seat, seat,
KeyRepeatKind::System, KeyRepeatKind::System,
move |evt: KbEvent, _| match evt { move |evt: KbEvent<'_>, _| {
KbEvent::Enter { surface, .. } => { match evt {
let wid = make_wid(&surface); KbEvent::Enter { surface, .. } => {
my_sink.send((WindowEvent::Focused(true), wid)).unwrap(); let wid = make_wid(&surface);
*target.lock().unwrap() = Some(wid); my_sink.send((WindowEvent::Focused(true), wid)).unwrap();
} *target.lock().unwrap() = Some(wid);
KbEvent::Leave { surface, .. } => { }
let wid = make_wid(&surface); KbEvent::Leave { surface, .. } => {
my_sink.send((WindowEvent::Focused(false), wid)).unwrap(); let wid = make_wid(&surface);
*target.lock().unwrap() = None; my_sink.send((WindowEvent::Focused(false), wid)).unwrap();
} *target.lock().unwrap() = None;
KbEvent::Key { }
rawkey, KbEvent::Key {
keysym, rawkey,
state, keysym,
utf8, state,
.. utf8,
} => { ..
if let Some(wid) = *target.lock().unwrap() { } => {
let state = match state { if let Some(wid) = *target.lock().unwrap() {
wl_keyboard::KeyState::Pressed => ElementState::Pressed, let state = match state {
wl_keyboard::KeyState::Released => ElementState::Released, wl_keyboard::KeyState::Pressed => ElementState::Pressed,
_ => unreachable!(), wl_keyboard::KeyState::Released => ElementState::Released,
}; _ => unreachable!(),
let vkcode = key_to_vkey(rawkey, keysym); };
my_sink.send( let vkcode = key_to_vkey(rawkey, keysym);
(WindowEvent::KeyboardInput { my_sink
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), .send((
input: KeyboardInput { WindowEvent::KeyboardInput {
state: state, device_id: crate::event::DeviceId(
scancode: rawkey, crate::platform_impl::DeviceId::Wayland(DeviceId),
virtual_keycode: vkcode, ),
modifiers: modifiers_tracker.lock().unwrap().clone(), input: KeyboardInput {
}, state,
}, scancode: rawkey,
wid) virtual_keycode: vkcode,
).unwrap(); modifiers: modifiers_tracker.lock().unwrap().clone(),
// send char event only on key press, not release },
if let ElementState::Released = state { },
return; wid,
} ))
if let Some(txt) = utf8 { .unwrap();
for chr in txt.chars() { // send char event only on key press, not release
my_sink.send((WindowEvent::ReceivedCharacter(chr), wid)).unwrap(); if let ElementState::Released = state {
return;
}
if let Some(txt) = utf8 {
for chr in txt.chars() {
my_sink
.send((WindowEvent::ReceivedCharacter(chr), wid))
.unwrap();
}
} }
} }
} }
KbEvent::RepeatInfo { .. } => { /* Handled by smithay client toolkit */ }
KbEvent::Modifiers {
modifiers: event_modifiers,
} => *modifiers_tracker.lock().unwrap() = event_modifiers.into(),
} }
KbEvent::RepeatInfo { .. } => { /* Handled by smithay client toolkit */ }
KbEvent::Modifiers { modifiers: event_modifiers } => {
*modifiers_tracker.lock().unwrap() = event_modifiers.into()
},
}, },
move |repeat_event: KeyRepeatEvent, _| { move |repeat_event: KeyRepeatEvent, _| {
if let Some(wid) = *repeat_target.lock().unwrap() { if let Some(wid) = *repeat_target.lock().unwrap() {
let state = ElementState::Pressed; let state = ElementState::Pressed;
let vkcode = key_to_vkey(repeat_event.rawkey, repeat_event.keysym); let vkcode = key_to_vkey(repeat_event.rawkey, repeat_event.keysym);
repeat_sink.send(( repeat_sink
WindowEvent::KeyboardInput { .send((
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), WindowEvent::KeyboardInput {
input: KeyboardInput { device_id: crate::event::DeviceId(
state: state, crate::platform_impl::DeviceId::Wayland(DeviceId),
scancode: repeat_event.rawkey, ),
virtual_keycode: vkcode, input: KeyboardInput {
modifiers: my_modifiers.lock().unwrap().clone(), state,
scancode: repeat_event.rawkey,
virtual_keycode: vkcode,
modifiers: my_modifiers.lock().unwrap().clone(),
},
}, },
}, wid,
wid) ))
).unwrap(); .unwrap();
if let Some(txt) = repeat_event.utf8 { if let Some(txt) = repeat_event.utf8 {
for chr in txt.chars() { for chr in txt.chars() {
repeat_sink.send((WindowEvent::ReceivedCharacter(chr), wid)).unwrap(); repeat_sink
.send((WindowEvent::ReceivedCharacter(chr), wid))
.unwrap();
} }
} }
} }
@ -111,46 +127,55 @@ pub fn init_keyboard(
// In this case, we don't have the keymap information (it is // In this case, we don't have the keymap information (it is
// supposed to be serialized by the compositor using libxkbcommon) // supposed to be serialized by the compositor using libxkbcommon)
// { variables to be captured by the closure
let mut target = None;
let my_sink = sink;
// }
seat.get_keyboard(|keyboard| { seat.get_keyboard(|keyboard| {
keyboard.implement_closure(move |evt, _| match evt { // { variables to be captured by the closure
wl_keyboard::Event::Enter { surface, .. } => { let mut target = None;
let wid = make_wid(&surface); let my_sink = sink;
my_sink.send((WindowEvent::Focused(true), wid)).unwrap(); // }
target = Some(wid);
} keyboard.implement_closure(
wl_keyboard::Event::Leave { surface, .. } => { move |evt, _| match evt {
let wid = make_wid(&surface); wl_keyboard::Event::Enter { surface, .. } => {
my_sink.send((WindowEvent::Focused(false), wid)).unwrap(); let wid = make_wid(&surface);
target = None; my_sink.send((WindowEvent::Focused(true), wid)).unwrap();
} target = Some(wid);
wl_keyboard::Event::Key { key, state, .. } => {
if let Some(wid) = target {
let state = match state {
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
wl_keyboard::KeyState::Released => ElementState::Released,
_ => unreachable!()
};
my_sink.send((
WindowEvent::KeyboardInput {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
input: KeyboardInput {
state: state,
scancode: key,
virtual_keycode: None,
modifiers: ModifiersState::default(),
},
},
wid,
)).unwrap();
} }
} wl_keyboard::Event::Leave { surface, .. } => {
_ => (), let wid = make_wid(&surface);
}, ()) my_sink.send((WindowEvent::Focused(false), wid)).unwrap();
}).unwrap() target = None;
}
wl_keyboard::Event::Key { key, state, .. } => {
if let Some(wid) = target {
let state = match state {
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
wl_keyboard::KeyState::Released => ElementState::Released,
_ => unreachable!(),
};
my_sink
.send((
WindowEvent::KeyboardInput {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
input: KeyboardInput {
state,
scancode: key,
virtual_keycode: None,
modifiers: ModifiersState::default(),
},
},
wid,
))
.unwrap();
}
}
_ => (),
},
(),
)
})
.unwrap()
} }
} }
} }
@ -173,7 +198,7 @@ fn key_to_vkey(rawkey: u32, keysym: u32) -> Option<VirtualKeyCode> {
} }
fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> { fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
use sctk::keyboard::keysyms; use smithay_client_toolkit::keyboard::keysyms;
match keysym { match keysym {
// letters // letters
keysyms::XKB_KEY_A | keysyms::XKB_KEY_a => Some(VirtualKeyCode::A), keysyms::XKB_KEY_A | keysyms::XKB_KEY_a => Some(VirtualKeyCode::A),

View file

@ -1,15 +1,19 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", #![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd",
target_os = "netbsd", target_os = "openbsd"))] target_os = "netbsd", target_os = "openbsd"))]
pub use self::window::Window; pub use self::{
pub use self::event_loop::{EventLoop, EventLoopWindowTarget, EventLoopProxy, WindowEventsSink, MonitorHandle}; event_loop::{
EventLoop, EventLoopProxy, EventLoopWindowTarget, MonitorHandle, WindowEventsSink,
},
window::Window,
};
use sctk::reexports::client::protocol::wl_surface; use smithay_client_toolkit::reexports::client::protocol::wl_surface;
mod event_loop; mod event_loop;
mod keyboard;
mod pointer; mod pointer;
mod touch; mod touch;
mod keyboard;
mod window; mod window;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]

View file

@ -1,191 +1,243 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent, ModifiersState}; use crate::event::{
DeviceEvent, ElementState, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase,
WindowEvent,
};
use super::DeviceId; use super::{event_loop::WindowEventsSink, window::WindowStore, DeviceId};
use super::event_loop::WindowEventsSink;
use super::window::WindowStore;
use sctk::reexports::client::protocol::wl_pointer::{self, Event as PtrEvent, WlPointer}; use smithay_client_toolkit::reexports::client::protocol::{
use sctk::reexports::client::protocol::wl_seat; wl_pointer::{self, Event as PtrEvent, WlPointer},
wl_seat,
};
pub fn implement_pointer( use smithay_client_toolkit::reexports::protocols::unstable::relative_pointer::v1::client::{
zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1, zwp_relative_pointer_v1::Event,
zwp_relative_pointer_v1::ZwpRelativePointerV1,
};
pub fn implement_pointer<T: 'static>(
seat: &wl_seat::WlSeat, seat: &wl_seat::WlSeat,
sink: Arc<Mutex<WindowEventsSink>>, sink: Arc<Mutex<WindowEventsSink<T>>>,
store: Arc<Mutex<WindowStore>>, store: Arc<Mutex<WindowStore>>,
modifiers_tracker: Arc<Mutex<ModifiersState>>, modifiers_tracker: Arc<Mutex<ModifiersState>>,
) -> WlPointer { ) -> WlPointer {
let mut mouse_focus = None;
let mut axis_buffer = None;
let mut axis_discrete_buffer = None;
let mut axis_state = TouchPhase::Ended;
seat.get_pointer(|pointer| { seat.get_pointer(|pointer| {
pointer.implement_closure(move |evt, pointer| { let mut mouse_focus = None;
let mut sink = sink.lock().unwrap(); let mut axis_buffer = None;
let store = store.lock().unwrap(); let mut axis_discrete_buffer = None;
match evt { let mut axis_state = TouchPhase::Ended;
PtrEvent::Enter {
surface, pointer.implement_closure(
surface_x, move |evt, pointer| {
surface_y, let mut sink = sink.lock().unwrap();
.. let store = store.lock().unwrap();
} => { match evt {
let wid = store.find_wid(&surface); PtrEvent::Enter {
if let Some(wid) = wid { surface,
mouse_focus = Some(wid); surface_x,
sink.send_event( surface_y,
WindowEvent::CursorEntered { ..
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), } => {
}, let wid = store.find_wid(&surface);
wid, if let Some(wid) = wid {
); mouse_focus = Some(wid);
sink.send_event( sink.send_window_event(
WindowEvent::CursorMoved { WindowEvent::CursorEntered {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), device_id: crate::event::DeviceId(
position: (surface_x, surface_y).into(), crate::platform_impl::DeviceId::Wayland(DeviceId),
modifiers: modifiers_tracker.lock().unwrap().clone(), ),
},
wid,
);
}
}
PtrEvent::Leave { surface, .. } => {
mouse_focus = None;
let wid = store.find_wid(&surface);
if let Some(wid) = wid {
sink.send_event(
WindowEvent::CursorLeft {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
},
wid,
);
}
}
PtrEvent::Motion {
surface_x,
surface_y,
..
} => {
if let Some(wid) = mouse_focus {
sink.send_event(
WindowEvent::CursorMoved {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
position: (surface_x, surface_y).into(),
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
}
}
PtrEvent::Button { button, state, .. } => {
if let Some(wid) = mouse_focus {
let state = match state {
wl_pointer::ButtonState::Pressed => ElementState::Pressed,
wl_pointer::ButtonState::Released => ElementState::Released,
_ => unreachable!()
};
let button = match button {
0x110 => MouseButton::Left,
0x111 => MouseButton::Right,
0x112 => MouseButton::Middle,
// TODO figure out the translation ?
_ => return,
};
sink.send_event(
WindowEvent::MouseInput {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
state: state,
button: button,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
}
}
PtrEvent::Axis { axis, value, .. } => {
if let Some(wid) = mouse_focus {
if pointer.as_ref().version() < 5 {
let (mut x, mut y) = (0.0, 0.0);
// old seat compatibility
match axis {
// wayland vertical sign convention is the inverse of winit
wl_pointer::Axis::VerticalScroll => y -= value as f32,
wl_pointer::Axis::HorizontalScroll => x += value as f32,
_ => unreachable!()
}
sink.send_event(
WindowEvent::MouseWheel {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
phase: TouchPhase::Moved,
modifiers: modifiers_tracker.lock().unwrap().clone(),
}, },
wid, wid,
); );
} else { sink.send_window_event(
let (mut x, mut y) = axis_buffer.unwrap_or((0.0, 0.0)); WindowEvent::CursorMoved {
match axis { device_id: crate::event::DeviceId(
// wayland vertical sign convention is the inverse of winit crate::platform_impl::DeviceId::Wayland(DeviceId),
wl_pointer::Axis::VerticalScroll => y -= value as f32, ),
wl_pointer::Axis::HorizontalScroll => x += value as f32, position: (surface_x, surface_y).into(),
_ => unreachable!()
}
axis_buffer = Some((x, y));
axis_state = match axis_state {
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
_ => TouchPhase::Started,
}
}
}
}
PtrEvent::Frame => {
let axis_buffer = axis_buffer.take();
let axis_discrete_buffer = axis_discrete_buffer.take();
if let Some(wid) = mouse_focus {
if let Some((x, y)) = axis_discrete_buffer {
sink.send_event(
WindowEvent::MouseWheel {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
delta: MouseScrollDelta::LineDelta(x as f32, y as f32),
phase: axis_state,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
} else if let Some((x, y)) = axis_buffer {
sink.send_event(
WindowEvent::MouseWheel {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
phase: axis_state,
modifiers: modifiers_tracker.lock().unwrap().clone(), modifiers: modifiers_tracker.lock().unwrap().clone(),
}, },
wid, wid,
); );
} }
} }
} PtrEvent::Leave { surface, .. } => {
PtrEvent::AxisSource { .. } => (), mouse_focus = None;
PtrEvent::AxisStop { .. } => { let wid = store.find_wid(&surface);
axis_state = TouchPhase::Ended; if let Some(wid) = wid {
} sink.send_window_event(
PtrEvent::AxisDiscrete { axis, discrete } => { WindowEvent::CursorLeft {
let (mut x, mut y) = axis_discrete_buffer.unwrap_or((0, 0)); device_id: crate::event::DeviceId(
match axis { crate::platform_impl::DeviceId::Wayland(DeviceId),
// wayland vertical sign convention is the inverse of winit ),
wl_pointer::Axis::VerticalScroll => y -= discrete, },
wl_pointer::Axis::HorizontalScroll => x += discrete, wid,
_ => unreachable!() );
}
} }
axis_discrete_buffer = Some((x, y)); PtrEvent::Motion {
axis_state = match axis_state { surface_x,
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, surface_y,
_ => TouchPhase::Started, ..
} => {
if let Some(wid) = mouse_focus {
sink.send_window_event(
WindowEvent::CursorMoved {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
position: (surface_x, surface_y).into(),
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
}
} }
}, PtrEvent::Button { button, state, .. } => {
_ => unreachable!() if let Some(wid) = mouse_focus {
} let state = match state {
}, ()) wl_pointer::ButtonState::Pressed => ElementState::Pressed,
}).unwrap() wl_pointer::ButtonState::Released => ElementState::Released,
_ => unreachable!(),
};
let button = match button {
0x110 => MouseButton::Left,
0x111 => MouseButton::Right,
0x112 => MouseButton::Middle,
// TODO figure out the translation ?
_ => return,
};
sink.send_window_event(
WindowEvent::MouseInput {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
state,
button,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
}
}
PtrEvent::Axis { axis, value, .. } => {
if let Some(wid) = mouse_focus {
if pointer.as_ref().version() < 5 {
let (mut x, mut y) = (0.0, 0.0);
// old seat compatibility
match axis {
// wayland vertical sign convention is the inverse of winit
wl_pointer::Axis::VerticalScroll => y -= value as f32,
wl_pointer::Axis::HorizontalScroll => x += value as f32,
_ => unreachable!(),
}
sink.send_window_event(
WindowEvent::MouseWheel {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
delta: MouseScrollDelta::PixelDelta(
(x as f64, y as f64).into(),
),
phase: TouchPhase::Moved,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
} else {
let (mut x, mut y) = axis_buffer.unwrap_or((0.0, 0.0));
match axis {
// wayland vertical sign convention is the inverse of winit
wl_pointer::Axis::VerticalScroll => y -= value as f32,
wl_pointer::Axis::HorizontalScroll => x += value as f32,
_ => unreachable!(),
}
axis_buffer = Some((x, y));
axis_state = match axis_state {
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
_ => TouchPhase::Started,
}
}
}
}
PtrEvent::Frame => {
let axis_buffer = axis_buffer.take();
let axis_discrete_buffer = axis_discrete_buffer.take();
if let Some(wid) = mouse_focus {
if let Some((x, y)) = axis_discrete_buffer {
sink.send_window_event(
WindowEvent::MouseWheel {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
delta: MouseScrollDelta::LineDelta(x as f32, y as f32),
phase: axis_state,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
} else if let Some((x, y)) = axis_buffer {
sink.send_window_event(
WindowEvent::MouseWheel {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
delta: MouseScrollDelta::PixelDelta(
(x as f64, y as f64).into(),
),
phase: axis_state,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
}
}
}
PtrEvent::AxisSource { .. } => (),
PtrEvent::AxisStop { .. } => {
axis_state = TouchPhase::Ended;
}
PtrEvent::AxisDiscrete { axis, discrete } => {
let (mut x, mut y) = axis_discrete_buffer.unwrap_or((0, 0));
match axis {
// wayland vertical sign convention is the inverse of winit
wl_pointer::Axis::VerticalScroll => y -= discrete,
wl_pointer::Axis::HorizontalScroll => x += discrete,
_ => unreachable!(),
}
axis_discrete_buffer = Some((x, y));
axis_state = match axis_state {
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
_ => TouchPhase::Started,
}
}
_ => unreachable!(),
}
},
(),
)
})
.unwrap()
}
pub fn implement_relative_pointer<T: 'static>(
sink: Arc<Mutex<WindowEventsSink<T>>>,
pointer: &WlPointer,
manager: &ZwpRelativePointerManagerV1,
) -> Result<ZwpRelativePointerV1, ()> {
manager.get_relative_pointer(pointer, |rel_pointer| {
rel_pointer.implement_closure(
move |evt, _rel_pointer| {
let mut sink = sink.lock().unwrap();
match evt {
Event::RelativeMotion { dx, dy, .. } => sink
.send_device_event(DeviceEvent::MouseMotion { delta: (dx, dy) }, DeviceId),
_ => unreachable!(),
}
},
(),
)
})
} }

View file

@ -1,13 +1,13 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use event::{TouchPhase, WindowEvent}; use crate::event::{TouchPhase, WindowEvent};
use super::{DeviceId, WindowId}; use super::{event_loop::WindowEventsSink, window::WindowStore, DeviceId, WindowId};
use super::event_loop::WindowEventsSink;
use super::window::WindowStore;
use sctk::reexports::client::protocol::wl_touch::{Event as TouchEvent, WlTouch}; use smithay_client_toolkit::reexports::client::protocol::{
use sctk::reexports::client::protocol::wl_seat; wl_seat,
wl_touch::{Event as TouchEvent, WlTouch},
};
struct TouchPoint { struct TouchPoint {
wid: WindowId, wid: WindowId,
@ -15,82 +15,96 @@ struct TouchPoint {
id: i32, id: i32,
} }
pub(crate) fn implement_touch( pub(crate) fn implement_touch<T: 'static>(
seat: &wl_seat::WlSeat, seat: &wl_seat::WlSeat,
sink: Arc<Mutex<WindowEventsSink>>, sink: Arc<Mutex<WindowEventsSink<T>>>,
store: Arc<Mutex<WindowStore>>, store: Arc<Mutex<WindowStore>>,
) -> WlTouch { ) -> WlTouch {
let mut pending_ids = Vec::new(); let mut pending_ids = Vec::new();
seat.get_touch(|touch| { seat.get_touch(|touch| {
touch.implement_closure(move |evt, _| { touch.implement_closure(
let mut sink = sink.lock().unwrap(); move |evt, _| {
let store = store.lock().unwrap(); let mut sink = sink.lock().unwrap();
match evt { let store = store.lock().unwrap();
TouchEvent::Down { match evt {
surface, id, x, y, .. TouchEvent::Down {
} => { surface, id, x, y, ..
let wid = store.find_wid(&surface); } => {
if let Some(wid) = wid { let wid = store.find_wid(&surface);
sink.send_event( if let Some(wid) = wid {
WindowEvent::Touch(::event::Touch { sink.send_window_event(
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), WindowEvent::Touch(crate::event::Touch {
phase: TouchPhase::Started, device_id: crate::event::DeviceId(
location: (x, y).into(), crate::platform_impl::DeviceId::Wayland(DeviceId),
id: id as u64, ),
}), phase: TouchPhase::Started,
wid, location: (x, y).into(),
); id: id as u64,
pending_ids.push(TouchPoint { }),
wid: wid, wid,
location: (x, y), );
id: id, pending_ids.push(TouchPoint {
}); wid,
location: (x, y),
id,
});
}
} }
} TouchEvent::Up { id, .. } => {
TouchEvent::Up { id, .. } => { let idx = pending_ids.iter().position(|p| p.id == id);
let idx = pending_ids.iter().position(|p| p.id == id); if let Some(idx) = idx {
if let Some(idx) = idx { let pt = pending_ids.remove(idx);
let pt = pending_ids.remove(idx); sink.send_window_event(
sink.send_event( WindowEvent::Touch(crate::event::Touch {
WindowEvent::Touch(::event::Touch { device_id: crate::event::DeviceId(
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), crate::platform_impl::DeviceId::Wayland(DeviceId),
phase: TouchPhase::Ended, ),
location: pt.location.into(), phase: TouchPhase::Ended,
id: id as u64, location: pt.location.into(),
}), id: id as u64,
pt.wid, }),
); pt.wid,
);
}
} }
} TouchEvent::Motion { id, x, y, .. } => {
TouchEvent::Motion { id, x, y, .. } => { let pt = pending_ids.iter_mut().find(|p| p.id == id);
let pt = pending_ids.iter_mut().find(|p| p.id == id); if let Some(pt) = pt {
if let Some(pt) = pt { pt.location = (x, y);
pt.location = (x, y); sink.send_window_event(
sink.send_event( WindowEvent::Touch(crate::event::Touch {
WindowEvent::Touch(::event::Touch { device_id: crate::event::DeviceId(
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), crate::platform_impl::DeviceId::Wayland(DeviceId),
phase: TouchPhase::Moved, ),
location: (x, y).into(), phase: TouchPhase::Moved,
id: id as u64, location: (x, y).into(),
}), id: id as u64,
pt.wid, }),
); pt.wid,
);
}
} }
TouchEvent::Frame => (),
TouchEvent::Cancel => {
for pt in pending_ids.drain(..) {
sink.send_window_event(
WindowEvent::Touch(crate::event::Touch {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
phase: TouchPhase::Cancelled,
location: pt.location.into(),
id: pt.id as u64,
}),
pt.wid,
);
}
}
_ => unreachable!(),
} }
TouchEvent::Frame => (), },
TouchEvent::Cancel => for pt in pending_ids.drain(..) { (),
sink.send_event( )
WindowEvent::Touch(::event::Touch { })
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), .unwrap()
phase: TouchPhase::Cancelled,
location: pt.location.into(),
id: pt.id as u64,
}),
pt.wid,
);
},
_ => unreachable!()
}
}, ())
}).unwrap()
} }

View file

@ -1,20 +1,31 @@
use std::collections::VecDeque; use std::{
use std::sync::{Arc, Mutex, Weak}; collections::VecDeque,
sync::{Arc, Mutex, Weak},
};
use dpi::{LogicalPosition, LogicalSize}; use crate::{
use error::{ExternalError, NotSupportedError, OsError as RootOsError}; dpi::{LogicalPosition, LogicalSize},
use platform_impl::{MonitorHandle as PlatformMonitorHandle, PlatformSpecificWindowBuilderAttributes as PlAttributes}; error::{ExternalError, NotSupportedError, OsError as RootOsError},
use monitor::MonitorHandle as RootMonitorHandle; monitor::MonitorHandle as RootMonitorHandle,
use window::{WindowAttributes, CursorIcon}; platform_impl::{
MonitorHandle as PlatformMonitorHandle,
PlatformSpecificWindowBuilderAttributes as PlAttributes,
},
window::{CursorIcon, WindowAttributes},
};
use sctk::surface::{get_dpi_factor, get_outputs}; use smithay_client_toolkit::{
use sctk::window::{ConceptFrame, Event as WEvent, State as WState, Window as SWindow, Theme}; output::OutputMgr,
use sctk::reexports::client::Display; reexports::client::{
use sctk::reexports::client::protocol::{wl_seat, wl_surface}; protocol::{wl_seat, wl_surface},
use sctk::output::OutputMgr; Display,
},
surface::{get_dpi_factor, get_outputs},
window::{ConceptFrame, Event as WEvent, State as WState, Theme, Window as SWindow},
};
use super::{make_wid, EventLoopWindowTarget, MonitorHandle, WindowId}; use super::{make_wid, EventLoopWindowTarget, MonitorHandle, WindowId};
use platform_impl::platform::wayland::event_loop::{available_monitors, primary_monitor}; use crate::platform_impl::platform::wayland::event_loop::{available_monitors, primary_monitor};
pub struct Window { pub struct Window {
surface: wl_surface::WlSurface, surface: wl_surface::WlSurface,
@ -29,7 +40,11 @@ pub struct Window {
} }
impl Window { impl Window {
pub fn new<T>(evlp: &EventLoopWindowTarget<T>, attributes: WindowAttributes, pl_attribs: PlAttributes) -> Result<Window, RootOsError> { pub fn new<T>(
evlp: &EventLoopWindowTarget<T>,
attributes: WindowAttributes,
pl_attribs: PlAttributes,
) -> Result<Window, RootOsError> {
let (width, height) = attributes.inner_size.map(Into::into).unwrap_or((800, 600)); let (width, height) = attributes.inner_size.map(Into::into).unwrap_or((800, 600));
// Create the window // Create the window
let size = Arc::new(Mutex::new((width, height))); let size = Arc::new(Mutex::new((width, height)));
@ -81,7 +96,8 @@ impl Window {
} }
} }
}, },
).unwrap(); )
.unwrap();
if let Some(app_id) = pl_attribs.app_id { if let Some(app_id) = pl_attribs.app_id {
frame.set_app_id(app_id); frame.set_app_id(app_id);
@ -135,10 +151,10 @@ impl Window {
Ok(Window { Ok(Window {
display: evlp.display.clone(), display: evlp.display.clone(),
surface: surface, surface,
frame: frame, frame,
outputs: evlp.env.outputs.clone(), outputs: evlp.env.outputs.clone(),
size: size, size,
kill_switch: (kill_switch, evlp.cleanup_needed.clone()), kill_switch: (kill_switch, evlp.cleanup_needed.clone()),
need_frame_refresh, need_frame_refresh,
need_refresh, need_refresh,
@ -199,12 +215,18 @@ impl Window {
#[inline] #[inline]
pub fn set_min_inner_size(&self, dimensions: Option<LogicalSize>) { pub fn set_min_inner_size(&self, dimensions: Option<LogicalSize>) {
self.frame.lock().unwrap().set_min_size(dimensions.map(Into::into)); self.frame
.lock()
.unwrap()
.set_min_size(dimensions.map(Into::into));
} }
#[inline] #[inline]
pub fn set_max_inner_size(&self, dimensions: Option<LogicalSize>) { pub fn set_max_inner_size(&self, dimensions: Option<LogicalSize>) {
self.frame.lock().unwrap().set_max_size(dimensions.map(Into::into)); self.frame
.lock()
.unwrap()
.set_max_size(dimensions.map(Into::into));
} }
#[inline] #[inline]
@ -252,7 +274,6 @@ impl Window {
} }
} }
pub fn set_theme<T: Theme>(&self, theme: T) { pub fn set_theme<T: Theme>(&self, theme: T) {
self.frame.lock().unwrap().set_theme(theme) self.frame.lock().unwrap().set_theme(theme)
} }
@ -380,7 +401,16 @@ impl WindowStore {
pub fn for_each<F>(&mut self, mut f: F) pub fn for_each<F>(&mut self, mut f: F)
where where
F: FnMut(Option<(u32, u32)>, &mut (u32, u32), Option<i32>, bool, bool, bool, WindowId, Option<&mut SWindow<ConceptFrame>>), F: FnMut(
Option<(u32, u32)>,
&mut (u32, u32),
Option<i32>,
bool,
bool,
bool,
WindowId,
Option<&mut SWindow<ConceptFrame>>,
),
{ {
for window in &mut self.windows { for window in &mut self.windows {
let opt_arc = window.frame.upgrade(); let opt_arc = window.frame.upgrade();
@ -403,4 +433,3 @@ impl WindowStore {
} }
} }
} }

View file

@ -1,8 +1,10 @@
use std::io; use std::{
use std::sync::Arc; io,
use std::path::{Path, PathBuf}; os::raw::*,
use std::str::Utf8Error; path::{Path, PathBuf},
use std::os::raw::*; str::Utf8Error,
sync::Arc,
};
use percent_encoding::percent_decode; use percent_encoding::percent_decode;
@ -127,13 +129,15 @@ impl Dnd {
DndState::Accepted => (1, self.atoms.action_private as c_long), DndState::Accepted => (1, self.atoms.action_private as c_long),
DndState::Rejected => (0, self.atoms.none as c_long), DndState::Rejected => (0, self.atoms.none as c_long),
}; };
self.xconn.send_client_msg( self.xconn
target_window, .send_client_msg(
target_window, target_window,
self.atoms.status, target_window,
None, self.atoms.status,
[this_window as c_long, accepted, 0, 0, action], None,
).flush() [this_window as c_long, accepted, 0, 0, action],
)
.flush()
} }
pub unsafe fn send_finished( pub unsafe fn send_finished(
@ -146,24 +150,23 @@ impl Dnd {
DndState::Accepted => (1, self.atoms.action_private as c_long), DndState::Accepted => (1, self.atoms.action_private as c_long),
DndState::Rejected => (0, self.atoms.none as c_long), DndState::Rejected => (0, self.atoms.none as c_long),
}; };
self.xconn.send_client_msg( self.xconn
target_window, .send_client_msg(
target_window, target_window,
self.atoms.finished, target_window,
None, self.atoms.finished,
[this_window as c_long, accepted, action, 0, 0], None,
).flush() [this_window as c_long, accepted, action, 0, 0],
)
.flush()
} }
pub unsafe fn get_type_list( pub unsafe fn get_type_list(
&self, &self,
source_window: c_ulong, source_window: c_ulong,
) -> Result<Vec<ffi::Atom>, util::GetPropertyError> { ) -> Result<Vec<ffi::Atom>, util::GetPropertyError> {
self.xconn.get_property( self.xconn
source_window, .get_property(source_window, self.atoms.type_list, ffi::XA_ATOM)
self.atoms.type_list,
ffi::XA_ATOM,
)
} }
pub unsafe fn convert_selection(&self, window: c_ulong, time: c_ulong) { pub unsafe fn convert_selection(&self, window: c_ulong, time: c_ulong) {
@ -181,11 +184,8 @@ impl Dnd {
&self, &self,
window: c_ulong, window: c_ulong,
) -> Result<Vec<c_uchar>, util::GetPropertyError> { ) -> Result<Vec<c_uchar>, util::GetPropertyError> {
self.xconn.get_property( self.xconn
window, .get_property(window, self.atoms.selection, self.atoms.uri_list)
self.atoms.selection,
self.atoms.uri_list,
)
} }
pub fn parse_data(&self, data: &mut Vec<c_uchar>) -> Result<Vec<PathBuf>, DndDataParseError> { pub fn parse_data(&self, data: &mut Vec<c_uchar>) -> Result<Vec<PathBuf>, DndDataParseError> {

View file

@ -1,20 +1,18 @@
use std::cell::RefCell; use std::{cell::RefCell, collections::HashMap, ptr, rc::Rc, slice};
use std::collections::HashMap;
use std::ptr;
use std::rc::Rc;
use std::slice;
use libc::{c_int, c_uint, c_ulong, c_char, c_long}; use libc::{c_char, c_int, c_long, c_uint, c_ulong};
use super::{ use super::{
mkdid, mkwid, get_xtarget, DeviceId, WindowId, Device, ImeReceiver, XExtension, events, ffi, get_xtarget, mkdid, mkwid, monitor, util, Device, DeviceId, DeviceInfo, Dnd,
monitor, ffi, UnownedWindow, ScrollOrientation, GenericEventCookie, DndState, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId,
events, util, DndState, Dnd, DeviceInfo XExtension,
}; };
use event_loop::EventLoopWindowTarget as RootELW; use crate::{
use event::{DeviceEvent, Event, KeyboardInput, ModifiersState, WindowEvent}; dpi::{LogicalPosition, LogicalSize},
use dpi::{LogicalPosition,LogicalSize}; event::{DeviceEvent, Event, KeyboardInput, ModifiersState, WindowEvent},
event_loop::EventLoopWindowTarget as RootELW,
};
pub(super) struct EventProcessor<T: 'static> { pub(super) struct EventProcessor<T: 'static> {
pub(super) dnd: Dnd, pub(super) dnd: Dnd,
@ -22,7 +20,7 @@ pub(super) struct EventProcessor<T: 'static> {
pub(super) randr_event_offset: c_int, pub(super) randr_event_offset: c_int,
pub(super) devices: RefCell<HashMap<DeviceId, Device>>, pub(super) devices: RefCell<HashMap<DeviceId, Device>>,
pub(super) xi2ext: XExtension, pub(super) xi2ext: XExtension,
pub(super) target: Rc<RootELW<T>> pub(super) target: Rc<RootELW<T>>,
} }
impl<T: 'static> EventProcessor<T> { impl<T: 'static> EventProcessor<T> {
@ -37,12 +35,14 @@ impl<T: 'static> EventProcessor<T> {
} }
fn with_window<F, Ret>(&self, window_id: ffi::Window, callback: F) -> Option<Ret> fn with_window<F, Ret>(&self, window_id: ffi::Window, callback: F) -> Option<Ret>
where F: Fn(&UnownedWindow) -> Ret where
F: Fn(&UnownedWindow) -> Ret,
{ {
let mut deleted = false; let mut deleted = false;
let window_id = WindowId(window_id); let window_id = WindowId(window_id);
let wt = get_xtarget(&self.target); let wt = get_xtarget(&self.target);
let result = wt.windows let result = wt
.windows
.borrow() .borrow()
.get(&window_id) .get(&window_id)
.and_then(|window| { .and_then(|window| {
@ -62,7 +62,7 @@ impl<T: 'static> EventProcessor<T> {
self.with_window(window_id, |_| ()).is_some() self.with_window(window_id, |_| ()).is_some()
} }
pub(super) unsafe fn poll_one_event(&mut self, event_ptr : *mut ffi::XEvent) -> bool { pub(super) unsafe fn poll_one_event(&mut self, event_ptr: *mut ffi::XEvent) -> bool {
let wt = get_xtarget(&self.target); let wt = get_xtarget(&self.target);
// This function is used to poll and remove a single event // This function is used to poll and remove a single event
// from the Xlib event queue in a non-blocking, atomic way. // from the Xlib event queue in a non-blocking, atomic way.
@ -73,7 +73,8 @@ impl<T: 'static> EventProcessor<T> {
unsafe extern "C" fn predicate( unsafe extern "C" fn predicate(
_display: *mut ffi::Display, _display: *mut ffi::Display,
_event: *mut ffi::XEvent, _event: *mut ffi::XEvent,
_arg : *mut c_char) -> c_int { _arg: *mut c_char,
) -> c_int {
// This predicate always returns "true" (1) to accept all events // This predicate always returns "true" (1) to accept all events
1 1
} }
@ -82,31 +83,41 @@ impl<T: 'static> EventProcessor<T> {
wt.xconn.display, wt.xconn.display,
event_ptr, event_ptr,
Some(predicate), Some(predicate),
std::ptr::null_mut()); std::ptr::null_mut(),
);
result != 0 result != 0
} }
pub(super) fn process_event<F>(&mut self, xev: &mut ffi::XEvent, mut callback: F) pub(super) fn process_event<F>(&mut self, xev: &mut ffi::XEvent, mut callback: F)
where F: FnMut(Event<T>) where
F: FnMut(Event<T>),
{ {
let wt = get_xtarget(&self.target); let wt = get_xtarget(&self.target);
// XFilterEvent tells us when an event has been discarded by the input method. // XFilterEvent tells us when an event has been discarded by the input method.
// Specifically, this involves all of the KeyPress events in compose/pre-edit sequences, // Specifically, this involves all of the KeyPress events in compose/pre-edit sequences,
// along with an extra copy of the KeyRelease events. This also prevents backspace and // along with an extra copy of the KeyRelease events. This also prevents backspace and
// arrow keys from being detected twice. // arrow keys from being detected twice.
if ffi::True == unsafe { (wt.xconn.xlib.XFilterEvent)( if ffi::True
xev, == unsafe {
{ let xev: &ffi::XAnyEvent = xev.as_ref(); xev.window } (wt.xconn.xlib.XFilterEvent)(xev, {
) } { let xev: &ffi::XAnyEvent = xev.as_ref();
xev.window
})
}
{
return; return;
} }
let event_type = xev.get_type(); let event_type = xev.get_type();
match event_type { match event_type {
ffi::MappingNotify => { ffi::MappingNotify => {
unsafe { (wt.xconn.xlib.XRefreshKeyboardMapping)(xev.as_mut()); } unsafe {
wt.xconn.check_errors().expect("Failed to call XRefreshKeyboardMapping"); (wt.xconn.xlib.XRefreshKeyboardMapping)(xev.as_mut());
}
wt.xconn
.check_errors()
.expect("Failed to call XRefreshKeyboardMapping");
} }
ffi::ClientMessage => { ffi::ClientMessage => {
@ -116,7 +127,20 @@ impl<T: 'static> EventProcessor<T> {
let window_id = mkwid(window); let window_id = mkwid(window);
if client_msg.data.get_long(0) as ffi::Atom == wt.wm_delete_window { if client_msg.data.get_long(0) as ffi::Atom == wt.wm_delete_window {
callback(Event::WindowEvent { window_id, event: WindowEvent::CloseRequested }); callback(Event::WindowEvent {
window_id,
event: WindowEvent::CloseRequested,
});
} else if client_msg.data.get_long(0) as ffi::Atom == wt.net_wm_ping {
let response_msg: &mut ffi::XClientMessageEvent = xev.as_mut();
response_msg.window = wt.root;
wt.xconn
.send_event(
wt.root,
Some(ffi::SubstructureNotifyMask | ffi::SubstructureRedirectMask),
*response_msg,
)
.queue();
} else if client_msg.message_type == self.dnd.atoms.enter { } else if client_msg.message_type == self.dnd.atoms.enter {
let source_window = client_msg.data.get_long(0) as c_ulong; let source_window = client_msg.data.get_long(0) as c_ulong;
let flags = client_msg.data.get_long(1); let flags = client_msg.data.get_long(1);
@ -127,10 +151,11 @@ impl<T: 'static> EventProcessor<T> {
let type_list = vec![ let type_list = vec![
client_msg.data.get_long(2) as c_ulong, client_msg.data.get_long(2) as c_ulong,
client_msg.data.get_long(3) as c_ulong, client_msg.data.get_long(3) as c_ulong,
client_msg.data.get_long(4) as c_ulong client_msg.data.get_long(4) as c_ulong,
]; ];
self.dnd.type_list = Some(type_list); self.dnd.type_list = Some(type_list);
} else if let Ok(more_types) = unsafe { self.dnd.get_type_list(source_window) } { } else if let Ok(more_types) = unsafe { self.dnd.get_type_list(source_window) }
{
self.dnd.type_list = Some(more_types); self.dnd.type_list = Some(more_types);
} }
} else if client_msg.message_type == self.dnd.atoms.position { } else if client_msg.message_type == self.dnd.atoms.position {
@ -178,18 +203,21 @@ impl<T: 'static> EventProcessor<T> {
// This results in the `SelectionNotify` event below // This results in the `SelectionNotify` event below
self.dnd.convert_selection(window, time); self.dnd.convert_selection(window, time);
} }
self.dnd.send_status(window, source_window, DndState::Accepted) self.dnd
.send_status(window, source_window, DndState::Accepted)
.expect("Failed to send `XdndStatus` message."); .expect("Failed to send `XdndStatus` message.");
} }
} else { } else {
unsafe { unsafe {
self.dnd.send_status(window, source_window, DndState::Rejected) self.dnd
.send_status(window, source_window, DndState::Rejected)
.expect("Failed to send `XdndStatus` message."); .expect("Failed to send `XdndStatus` message.");
} }
self.dnd.reset(); self.dnd.reset();
} }
} else if client_msg.message_type == self.dnd.atoms.drop { } else if client_msg.message_type == self.dnd.atoms.drop {
let (source_window, state) = if let Some(source_window) = self.dnd.source_window { let (source_window, state) = if let Some(source_window) = self.dnd.source_window
{
if let Some(Ok(ref path_list)) = self.dnd.result { if let Some(Ok(ref path_list)) = self.dnd.result {
for path in path_list { for path in path_list {
callback(Event::WindowEvent { callback(Event::WindowEvent {
@ -206,7 +234,8 @@ impl<T: 'static> EventProcessor<T> {
(source_window, DndState::Rejected) (source_window, DndState::Rejected)
}; };
unsafe { unsafe {
self.dnd.send_finished(window, source_window, state) self.dnd
.send_finished(window, source_window, state)
.expect("Failed to send `XdndFinished` message."); .expect("Failed to send `XdndFinished` message.");
} }
self.dnd.reset(); self.dnd.reset();
@ -274,15 +303,22 @@ impl<T: 'static> EventProcessor<T> {
let mut shared_state_lock = window.shared_state.lock(); let mut shared_state_lock = window.shared_state.lock();
let (mut resized, moved) = { let (mut resized, moved) = {
let resized = util::maybe_change(&mut shared_state_lock.size, new_inner_size); let resized =
util::maybe_change(&mut shared_state_lock.size, new_inner_size);
let moved = if is_synthetic { let moved = if is_synthetic {
util::maybe_change(&mut shared_state_lock.inner_position, new_inner_position) util::maybe_change(
&mut shared_state_lock.inner_position,
new_inner_position,
)
} else { } else {
// Detect when frame extents change. // Detect when frame extents change.
// Since this isn't synthetic, as per the notes above, this position is relative to the // Since this isn't synthetic, as per the notes above, this position is relative to the
// parent window. // parent window.
let rel_parent = new_inner_position; let rel_parent = new_inner_position;
if util::maybe_change(&mut shared_state_lock.inner_position_rel_parent, rel_parent) { if util::maybe_change(
&mut shared_state_lock.inner_position_rel_parent,
rel_parent,
) {
// This ensures we process the next `Moved`. // This ensures we process the next `Moved`.
shared_state_lock.inner_position = None; shared_state_lock.inner_position = None;
// Extra insurance against stale frame extents. // Extra insurance against stale frame extents.
@ -297,18 +333,22 @@ impl<T: 'static> EventProcessor<T> {
let new_outer_position = if moved || shared_state_lock.position.is_none() { let new_outer_position = if moved || shared_state_lock.position.is_none() {
// We need to convert client area position to window position. // We need to convert client area position to window position.
let frame_extents = shared_state_lock.frame_extents let frame_extents = shared_state_lock
.frame_extents
.as_ref() .as_ref()
.cloned() .cloned()
.unwrap_or_else(|| { .unwrap_or_else(|| {
let frame_extents = wt.xconn.get_frame_extents_heuristic(xwindow, wt.root); let frame_extents =
wt.xconn.get_frame_extents_heuristic(xwindow, wt.root);
shared_state_lock.frame_extents = Some(frame_extents.clone()); shared_state_lock.frame_extents = Some(frame_extents.clone());
frame_extents frame_extents
}); });
let outer = frame_extents.inner_pos_to_outer(new_inner_position.0, new_inner_position.1); let outer = frame_extents
.inner_pos_to_outer(new_inner_position.0, new_inner_position.1);
shared_state_lock.position = Some(outer); shared_state_lock.position = Some(outer);
if moved { if moved {
let logical_position = LogicalPosition::from_physical(outer, monitor.hidpi_factor); let logical_position =
LogicalPosition::from_physical(outer, monitor.hidpi_factor);
events.moved = Some(WindowEvent::Moved(logical_position)); events.moved = Some(WindowEvent::Moved(logical_position));
} }
outer outer
@ -319,12 +359,13 @@ impl<T: 'static> EventProcessor<T> {
if is_synthetic { if is_synthetic {
// If we don't use the existing adjusted value when available, then the user can screw up the // If we don't use the existing adjusted value when available, then the user can screw up the
// resizing by dragging across monitors *without* dropping the window. // resizing by dragging across monitors *without* dropping the window.
let (width, height) = shared_state_lock.dpi_adjusted let (width, height) = shared_state_lock
.dpi_adjusted
.unwrap_or_else(|| (xev.width as f64, xev.height as f64)); .unwrap_or_else(|| (xev.width as f64, xev.height as f64));
let last_hidpi_factor = shared_state_lock.guessed_dpi let last_hidpi_factor =
.take() shared_state_lock.guessed_dpi.take().unwrap_or_else(|| {
.unwrap_or_else(|| { shared_state_lock
shared_state_lock.last_monitor .last_monitor
.as_ref() .as_ref()
.map(|last_monitor| last_monitor.hidpi_factor) .map(|last_monitor| last_monitor.hidpi_factor)
.unwrap_or(1.0) .unwrap_or(1.0)
@ -337,7 +378,8 @@ impl<T: 'static> EventProcessor<T> {
new_hidpi_factor new_hidpi_factor
}; };
if last_hidpi_factor != new_hidpi_factor { if last_hidpi_factor != new_hidpi_factor {
events.dpi_changed = Some(WindowEvent::HiDpiFactorChanged(new_hidpi_factor)); events.dpi_changed =
Some(WindowEvent::HiDpiFactorChanged(new_hidpi_factor));
let (new_width, new_height, flusher) = window.adjust_for_dpi( let (new_width, new_height, flusher) = window.adjust_for_dpi(
last_hidpi_factor, last_hidpi_factor,
new_hidpi_factor, new_hidpi_factor,
@ -357,7 +399,10 @@ impl<T: 'static> EventProcessor<T> {
// WMs constrain the window size, making the resize fail. This would cause an endless stream of // WMs constrain the window size, making the resize fail. This would cause an endless stream of
// XResizeWindow requests, making Xorg, the winit client, and the WM consume 100% of CPU. // XResizeWindow requests, making Xorg, the winit client, and the WM consume 100% of CPU.
if let Some(adjusted_size) = shared_state_lock.dpi_adjusted { if let Some(adjusted_size) = shared_state_lock.dpi_adjusted {
let rounded_size = (adjusted_size.0.round() as u32, adjusted_size.1.round() as u32); let rounded_size = (
adjusted_size.0.round() as u32,
adjusted_size.1.round() as u32,
);
if new_inner_size == rounded_size || !util::wm_name_is_one_of(&["Xfwm4"]) { if new_inner_size == rounded_size || !util::wm_name_is_one_of(&["Xfwm4"]) {
// When this finally happens, the event will not be synthetic. // When this finally happens, the event will not be synthetic.
shared_state_lock.dpi_adjusted = None; shared_state_lock.dpi_adjusted = None;
@ -374,7 +419,8 @@ impl<T: 'static> EventProcessor<T> {
} }
if resized { if resized {
let logical_size = LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor); let logical_size =
LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor);
events.resized = Some(WindowEvent::Resized(logical_size)); events.resized = Some(WindowEvent::Resized(logical_size));
} }
@ -427,7 +473,10 @@ impl<T: 'static> EventProcessor<T> {
.remove_context(window) .remove_context(window)
.expect("Failed to destroy input context"); .expect("Failed to destroy input context");
callback(Event::WindowEvent { window_id, event: WindowEvent::Destroyed }); callback(Event::WindowEvent {
window_id,
event: WindowEvent::Destroyed,
});
} }
ffi::Expose => { ffi::Expose => {
@ -436,11 +485,14 @@ impl<T: 'static> EventProcessor<T> {
let window = xev.window; let window = xev.window;
let window_id = mkwid(window); let window_id = mkwid(window);
callback(Event::WindowEvent { window_id, event: WindowEvent::RedrawRequested }); callback(Event::WindowEvent {
window_id,
event: WindowEvent::RedrawRequested,
});
} }
ffi::KeyPress | ffi::KeyRelease => { ffi::KeyPress | ffi::KeyRelease => {
use event::ElementState::{Pressed, Released}; use crate::event::ElementState::{Pressed, Released};
// Note that in compose/pre-edit sequences, this will always be Released. // Note that in compose/pre-edit sequences, this will always be Released.
let state = if xev.get_type() == ffi::KeyPress { let state = if xev.get_type() == ffi::KeyPress {
@ -493,7 +545,7 @@ impl<T: 'static> EventProcessor<T> {
virtual_keycode, virtual_keycode,
modifiers, modifiers,
}, },
} },
}); });
} }
@ -515,17 +567,26 @@ impl<T: 'static> EventProcessor<T> {
} }
ffi::GenericEvent => { ffi::GenericEvent => {
let guard = if let Some(e) = GenericEventCookie::from_event(&wt.xconn, *xev) { e } else { return }; let guard = if let Some(e) = GenericEventCookie::from_event(&wt.xconn, *xev) {
e
} else {
return;
};
let xev = &guard.cookie; let xev = &guard.cookie;
if self.xi2ext.opcode != xev.extension { if self.xi2ext.opcode != xev.extension {
return; return;
} }
use event::WindowEvent::{Focused, CursorEntered, MouseInput, CursorLeft, CursorMoved, MouseWheel, AxisMotion}; use crate::event::{
use event::ElementState::{Pressed, Released}; ElementState::{Pressed, Released},
use event::MouseButton::{Left, Right, Middle, Other}; MouseButton::{Left, Middle, Other, Right},
use event::MouseScrollDelta::LineDelta; MouseScrollDelta::LineDelta,
use event::{Touch, TouchPhase}; Touch, TouchPhase,
WindowEvent::{
AxisMotion, CursorEntered, CursorLeft, CursorMoved, Focused, MouseInput,
MouseWheel,
},
};
match xev.evtype { match xev.evtype {
ffi::XI_ButtonPress | ffi::XI_ButtonRelease => { ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
@ -576,23 +637,25 @@ impl<T: 'static> EventProcessor<T> {
// Suppress emulated scroll wheel clicks, since we handle the real motion events for those. // Suppress emulated scroll wheel clicks, since we handle the real motion events for those.
// In practice, even clicky scroll wheels appear to be reported by evdev (and XInput2 in // In practice, even clicky scroll wheels appear to be reported by evdev (and XInput2 in
// turn) as axis motion, so we don't otherwise special-case these button presses. // turn) as axis motion, so we don't otherwise special-case these button presses.
4 | 5 | 6 | 7 => if xev.flags & ffi::XIPointerEmulated == 0 { 4 | 5 | 6 | 7 => {
callback(Event::WindowEvent { if xev.flags & ffi::XIPointerEmulated == 0 {
window_id, callback(Event::WindowEvent {
event: MouseWheel { window_id,
device_id, event: MouseWheel {
delta: match xev.detail { device_id,
4 => LineDelta(0.0, 1.0), delta: match xev.detail {
5 => LineDelta(0.0, -1.0), 4 => LineDelta(0.0, 1.0),
6 => LineDelta(-1.0, 0.0), 5 => LineDelta(0.0, -1.0),
7 => LineDelta(1.0, 0.0), 6 => LineDelta(-1.0, 0.0),
_ => unreachable!(), 7 => LineDelta(1.0, 0.0),
_ => unreachable!(),
},
phase: TouchPhase::Moved,
modifiers,
}, },
phase: TouchPhase::Moved, });
modifiers, }
}, }
});
},
x => callback(Event::WindowEvent { x => callback(Event::WindowEvent {
window_id, window_id,
@ -618,9 +681,8 @@ impl<T: 'static> EventProcessor<T> {
util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos) util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
}); });
if cursor_moved == Some(true) { if cursor_moved == Some(true) {
let dpi_factor = self.with_window(xev.event, |window| { let dpi_factor =
window.hidpi_factor() self.with_window(xev.event, |window| window.hidpi_factor());
});
if let Some(dpi_factor) = dpi_factor { if let Some(dpi_factor) = dpi_factor {
let position = LogicalPosition::from_physical( let position = LogicalPosition::from_physical(
(xev.event_x as f64, xev.event_y as f64), (xev.event_x as f64, xev.event_y as f64),
@ -644,7 +706,12 @@ impl<T: 'static> EventProcessor<T> {
// More gymnastics, for self.devices // More gymnastics, for self.devices
let mut events = Vec::new(); let mut events = Vec::new();
{ {
let mask = unsafe { slice::from_raw_parts(xev.valuators.mask, xev.valuators.mask_len as usize) }; let mask = unsafe {
slice::from_raw_parts(
xev.valuators.mask,
xev.valuators.mask_len as usize,
)
};
let mut devices = self.devices.borrow_mut(); let mut devices = self.devices.borrow_mut();
let physical_device = match devices.get_mut(&DeviceId(xev.sourceid)) { let physical_device = match devices.get_mut(&DeviceId(xev.sourceid)) {
Some(device) => device, Some(device) => device,
@ -652,10 +719,14 @@ impl<T: 'static> EventProcessor<T> {
}; };
let mut value = xev.valuators.values; let mut value = xev.valuators.values;
for i in 0..xev.valuators.mask_len*8 { for i in 0..xev.valuators.mask_len * 8 {
if ffi::XIMaskIsSet(mask, i) { if ffi::XIMaskIsSet(mask, i) {
let x = unsafe { *value }; let x = unsafe { *value };
if let Some(&mut (_, ref mut info)) = physical_device.scroll_axes.iter_mut().find(|&&mut (axis, _)| axis == i) { if let Some(&mut (_, ref mut info)) = physical_device
.scroll_axes
.iter_mut()
.find(|&&mut (axis, _)| axis == i)
{
let delta = (x - info.position) / info.increment; let delta = (x - info.position) / info.increment;
info.position = x; info.position = x;
events.push(Event::WindowEvent { events.push(Event::WindowEvent {
@ -663,9 +734,13 @@ impl<T: 'static> EventProcessor<T> {
event: MouseWheel { event: MouseWheel {
device_id, device_id,
delta: match info.orientation { delta: match info.orientation {
ScrollOrientation::Horizontal => LineDelta(delta as f32, 0.0), ScrollOrientation::Horizontal => {
LineDelta(delta as f32, 0.0)
}
// X11 vertical scroll coordinates are opposite to winit's // X11 vertical scroll coordinates are opposite to winit's
ScrollOrientation::Vertical => LineDelta(0.0, -delta as f32), ScrollOrientation::Vertical => {
LineDelta(0.0, -delta as f32)
}
}, },
phase: TouchPhase::Moved, phase: TouchPhase::Moved,
modifiers, modifiers,
@ -704,7 +779,8 @@ impl<T: 'static> EventProcessor<T> {
// presumably some other WMs. On those, `XI_Enter` doesn't include // presumably some other WMs. On those, `XI_Enter` doesn't include
// the physical device ID, so both `sourceid` and `deviceid` are // the physical device ID, so both `sourceid` and `deviceid` are
// the virtual device. // the virtual device.
|| device_info.attachment == xev.sourceid { || device_info.attachment == xev.sourceid
{
let device_id = DeviceId(device_info.deviceid); let device_id = DeviceId(device_info.deviceid);
if let Some(device) = devices.get_mut(&device_id) { if let Some(device) = devices.get_mut(&device_id) {
device.reset_scroll_position(device_info); device.reset_scroll_position(device_info);
@ -717,9 +793,9 @@ impl<T: 'static> EventProcessor<T> {
event: CursorEntered { device_id }, event: CursorEntered { device_id },
}); });
if let Some(dpi_factor) = self.with_window(xev.event, |window| { if let Some(dpi_factor) =
window.hidpi_factor() self.with_window(xev.event, |window| window.hidpi_factor())
}) { {
let position = LogicalPosition::from_physical( let position = LogicalPosition::from_physical(
(xev.event_x as f64, xev.event_y as f64), (xev.event_x as f64, xev.event_y as f64),
dpi_factor, dpi_factor,
@ -732,7 +808,8 @@ impl<T: 'static> EventProcessor<T> {
// This needs to only be done after confirming the window still exists, // This needs to only be done after confirming the window still exists,
// since otherwise we risk getting a `BadWindow` error if the window was // since otherwise we risk getting a `BadWindow` error if the window was
// dropped with queued events. // dropped with queued events.
let modifiers = wt.xconn let modifiers = wt
.xconn
.query_pointer(xev.event, xev.deviceid) .query_pointer(xev.event, xev.deviceid)
.expect("Failed to query pointer device") .expect("Failed to query pointer device")
.get_modifier_state(); .get_modifier_state();
@ -756,19 +833,20 @@ impl<T: 'static> EventProcessor<T> {
if !window_closed { if !window_closed {
callback(Event::WindowEvent { callback(Event::WindowEvent {
window_id: mkwid(xev.event), window_id: mkwid(xev.event),
event: CursorLeft { device_id: mkdid(xev.deviceid) }, event: CursorLeft {
device_id: mkdid(xev.deviceid),
},
}); });
} }
} }
ffi::XI_FocusIn => { ffi::XI_FocusIn => {
let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) }; let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
let dpi_factor = match self.with_window(xev.event, |window| { let dpi_factor =
window.hidpi_factor() match self.with_window(xev.event, |window| window.hidpi_factor()) {
}) { Some(dpi_factor) => dpi_factor,
Some(dpi_factor) => dpi_factor, None => return,
None => return, };
};
let window_id = mkwid(xev.event); let window_id = mkwid(xev.event);
wt.ime wt.ime
@ -776,11 +854,15 @@ impl<T: 'static> EventProcessor<T> {
.focus(xev.event) .focus(xev.event)
.expect("Failed to focus input context"); .expect("Failed to focus input context");
callback(Event::WindowEvent { window_id, event: Focused(true) }); callback(Event::WindowEvent {
window_id,
event: Focused(true),
});
// The deviceid for this event is for a keyboard instead of a pointer, // The deviceid for this event is for a keyboard instead of a pointer,
// so we have to do a little extra work. // so we have to do a little extra work.
let pointer_id = self.devices let pointer_id = self
.devices
.borrow() .borrow()
.get(&DeviceId(xev.deviceid)) .get(&DeviceId(xev.deviceid))
.map(|device| device.attachment) .map(|device| device.attachment)
@ -796,12 +878,14 @@ impl<T: 'static> EventProcessor<T> {
device_id: mkdid(pointer_id), device_id: mkdid(pointer_id),
position, position,
modifiers: ModifiersState::from(xev.mods), modifiers: ModifiersState::from(xev.mods),
} },
}); });
} }
ffi::XI_FocusOut => { ffi::XI_FocusOut => {
let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) }; let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
if !self.window_exists(xev.event) { return; } if !self.window_exists(xev.event) {
return;
}
wt.ime wt.ime
.borrow_mut() .borrow_mut()
.unfocus(xev.event) .unfocus(xev.event)
@ -819,11 +903,10 @@ impl<T: 'static> EventProcessor<T> {
ffi::XI_TouchBegin => TouchPhase::Started, ffi::XI_TouchBegin => TouchPhase::Started,
ffi::XI_TouchUpdate => TouchPhase::Moved, ffi::XI_TouchUpdate => TouchPhase::Moved,
ffi::XI_TouchEnd => TouchPhase::Ended, ffi::XI_TouchEnd => TouchPhase::Ended,
_ => unreachable!() _ => unreachable!(),
}; };
let dpi_factor = self.with_window(xev.event, |window| { let dpi_factor =
window.hidpi_factor() self.with_window(xev.event, |window| window.hidpi_factor());
});
if let Some(dpi_factor) = dpi_factor { if let Some(dpi_factor) = dpi_factor {
let location = LogicalPosition::from_physical( let location = LogicalPosition::from_physical(
(xev.event_x as f64, xev.event_y as f64), (xev.event_x as f64, xev.event_y as f64),
@ -844,14 +927,17 @@ impl<T: 'static> EventProcessor<T> {
ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => { ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => {
let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) }; let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
if xev.flags & ffi::XIPointerEmulated == 0 { if xev.flags & ffi::XIPointerEmulated == 0 {
callback(Event::DeviceEvent { device_id: mkdid(xev.deviceid), event: DeviceEvent::Button { callback(Event::DeviceEvent {
button: xev.detail as u32, device_id: mkdid(xev.deviceid),
state: match xev.evtype { event: DeviceEvent::Button {
ffi::XI_RawButtonPress => Pressed, button: xev.detail as u32,
ffi::XI_RawButtonRelease => Released, state: match xev.evtype {
_ => unreachable!(), ffi::XI_RawButtonPress => Pressed,
ffi::XI_RawButtonRelease => Released,
_ => unreachable!(),
},
}, },
}}); });
} }
} }
@ -859,11 +945,16 @@ impl<T: 'static> EventProcessor<T> {
let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) }; let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
let did = mkdid(xev.deviceid); let did = mkdid(xev.deviceid);
let mask = unsafe { slice::from_raw_parts(xev.valuators.mask, xev.valuators.mask_len as usize) }; let mask = unsafe {
slice::from_raw_parts(
xev.valuators.mask,
xev.valuators.mask_len as usize,
)
};
let mut value = xev.raw_values; let mut value = xev.raw_values;
let mut mouse_delta = (0.0, 0.0); let mut mouse_delta = (0.0, 0.0);
let mut scroll_delta = (0.0, 0.0); let mut scroll_delta = (0.0, 0.0);
for i in 0..xev.valuators.mask_len*8 { for i in 0..xev.valuators.mask_len * 8 {
if ffi::XIMaskIsSet(mask, i) { if ffi::XIMaskIsSet(mask, i) {
let x = unsafe { *value }; let x = unsafe { *value };
// We assume that every XInput2 device with analog axes is a pointing device emitting // We assume that every XInput2 device with analog axes is a pointing device emitting
@ -873,24 +964,31 @@ impl<T: 'static> EventProcessor<T> {
1 => mouse_delta.1 = x, 1 => mouse_delta.1 = x,
2 => scroll_delta.0 = x as f32, 2 => scroll_delta.0 = x as f32,
3 => scroll_delta.1 = x as f32, 3 => scroll_delta.1 = x as f32,
_ => {}, _ => {}
} }
callback(Event::DeviceEvent { device_id: did, event: DeviceEvent::Motion { callback(Event::DeviceEvent {
axis: i as u32, device_id: did,
value: x, event: DeviceEvent::Motion {
}}); axis: i as u32,
value: x,
},
});
value = unsafe { value.offset(1) }; value = unsafe { value.offset(1) };
} }
} }
if mouse_delta != (0.0, 0.0) { if mouse_delta != (0.0, 0.0) {
callback(Event::DeviceEvent { device_id: did, event: DeviceEvent::MouseMotion { callback(Event::DeviceEvent {
delta: mouse_delta, device_id: did,
}}); event: DeviceEvent::MouseMotion { delta: mouse_delta },
});
} }
if scroll_delta != (0.0, 0.0) { if scroll_delta != (0.0, 0.0) {
callback(Event::DeviceEvent { device_id: did, event: DeviceEvent::MouseWheel { callback(Event::DeviceEvent {
delta: LineDelta(scroll_delta.0, scroll_delta.1), device_id: did,
}}); event: DeviceEvent::MouseWheel {
delta: LineDelta(scroll_delta.0, scroll_delta.1),
},
});
} }
} }
@ -905,7 +1003,9 @@ impl<T: 'static> EventProcessor<T> {
let device_id = xev.sourceid; let device_id = xev.sourceid;
let keycode = xev.detail; let keycode = xev.detail;
if keycode < 8 { return; } if keycode < 8 {
return;
}
let scancode = (keycode - 8) as u32; let scancode = (keycode - 8) as u32;
let keysym = unsafe { let keysym = unsafe {
@ -915,7 +1015,9 @@ impl<T: 'static> EventProcessor<T> {
0, 0,
) )
}; };
wt.xconn.check_errors().expect("Failed to lookup raw keysym"); wt.xconn
.check_errors()
.expect("Failed to lookup raw keysym");
let virtual_keycode = events::keysym_to_element(keysym as c_uint); let virtual_keycode = events::keysym_to_element(keysym as c_uint);
@ -937,12 +1039,21 @@ impl<T: 'static> EventProcessor<T> {
ffi::XI_HierarchyChanged => { ffi::XI_HierarchyChanged => {
let xev: &ffi::XIHierarchyEvent = unsafe { &*(xev.data as *const _) }; let xev: &ffi::XIHierarchyEvent = unsafe { &*(xev.data as *const _) };
for info in unsafe { slice::from_raw_parts(xev.info, xev.num_info as usize) } { for info in
unsafe { slice::from_raw_parts(xev.info, xev.num_info as usize) }
{
if 0 != info.flags & (ffi::XISlaveAdded | ffi::XIMasterAdded) { if 0 != info.flags & (ffi::XISlaveAdded | ffi::XIMasterAdded) {
self.init_device(info.deviceid); self.init_device(info.deviceid);
callback(Event::DeviceEvent { device_id: mkdid(info.deviceid), event: DeviceEvent::Added }); callback(Event::DeviceEvent {
} else if 0 != info.flags & (ffi::XISlaveRemoved | ffi::XIMasterRemoved) { device_id: mkdid(info.deviceid),
callback(Event::DeviceEvent { device_id: mkdid(info.deviceid), event: DeviceEvent::Removed }); event: DeviceEvent::Added,
});
} else if 0 != info.flags & (ffi::XISlaveRemoved | ffi::XIMasterRemoved)
{
callback(Event::DeviceEvent {
device_id: mkdid(info.deviceid),
event: DeviceEvent::Removed,
});
let mut devices = self.devices.borrow_mut(); let mut devices = self.devices.borrow_mut();
devices.remove(&DeviceId(info.deviceid)); devices.remove(&DeviceId(info.deviceid));
} }
@ -951,7 +1062,7 @@ impl<T: 'static> EventProcessor<T> {
_ => {} _ => {}
} }
}, }
_ => { _ => {
if event_type == self.randr_event_offset { if event_type == self.randr_event_offset {
// In the future, it would be quite easy to emit monitor hotplug events. // In the future, it would be quite easy to emit monitor hotplug events.
@ -972,10 +1083,11 @@ impl<T: 'static> EventProcessor<T> {
callback(Event::WindowEvent { callback(Event::WindowEvent {
window_id: mkwid(window_id.0), window_id: mkwid(window_id.0),
event: WindowEvent::HiDpiFactorChanged( event: WindowEvent::HiDpiFactorChanged(
new_monitor.hidpi_factor new_monitor.hidpi_factor,
), ),
}); });
let (width, height) = window.inner_size_physical(); let (width, height) =
window.inner_size_physical();
let (_, _, flusher) = window.adjust_for_dpi( let (_, _, flusher) = window.adjust_for_dpi(
prev_monitor.hidpi_factor, prev_monitor.hidpi_factor,
new_monitor.hidpi_factor, new_monitor.hidpi_factor,
@ -991,13 +1103,13 @@ impl<T: 'static> EventProcessor<T> {
} }
} }
} }
}, }
} }
match self.ime_receiver.try_recv() { match self.ime_receiver.try_recv() {
Ok((window_id, x, y)) => { Ok((window_id, x, y)) => {
wt.ime.borrow_mut().send_xim_spot(window_id, x, y); wt.ime.borrow_mut().send_xim_spot(window_id, x, y);
}, }
Err(_) => (), Err(_) => (),
} }
} }

View file

@ -1,6 +1,6 @@
use libc;
use super::ffi; use super::ffi;
use event::VirtualKeyCode; use crate::event::VirtualKeyCode;
use libc;
pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> { pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> {
Some(match keysym { Some(match keysym {
@ -1003,6 +1003,6 @@ pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> {
ffi::XF86XK_Copy => VirtualKeyCode::Copy, ffi::XF86XK_Copy => VirtualKeyCode::Copy,
ffi::XF86XK_Paste => VirtualKeyCode::Paste, ffi::XF86XK_Paste => VirtualKeyCode::Paste,
ffi::XF86XK_Cut => VirtualKeyCode::Cut, ffi::XF86XK_Cut => VirtualKeyCode::Cut,
_ => return None _ => return None,
}) })
} }

View file

@ -1,9 +1,4 @@
pub use x11_dl::keysym::*; pub use x11_dl::{
pub use x11_dl::xcursor::*; error::OpenError, keysym::*, xcursor::*, xinput::*, xinput2::*, xlib::*, xlib_xcb::*,
pub use x11_dl::xlib::*; xrandr::*, xrender::*,
pub use x11_dl::xinput::*; };
pub use x11_dl::xinput2::*;
pub use x11_dl::xlib_xcb::*;
pub use x11_dl::error::OpenError;
pub use x11_dl::xrandr::*;
pub use x11_dl::xrender::*;

View file

@ -1,13 +1,12 @@
use std::ptr; use std::{collections::HashMap, os::raw::c_char, ptr, sync::Arc};
use std::sync::Arc;
use std::collections::HashMap;
use std::os::raw::c_char;
use super::{ffi, XConnection, XError}; use super::{ffi, XConnection, XError};
use super::inner::{close_im, ImeInner}; use super::{
use super::input_method::PotentialInputMethods; context::{ImeContext, ImeContextCreationError},
use super::context::{ImeContextCreationError, ImeContext}; inner::{close_im, ImeInner},
input_method::PotentialInputMethods,
};
pub unsafe fn xim_set_callback( pub unsafe fn xim_set_callback(
xconn: &Arc<XConnection>, xconn: &Arc<XConnection>,
@ -17,12 +16,7 @@ pub unsafe fn xim_set_callback(
) -> Result<(), XError> { ) -> Result<(), XError> {
// It's advisable to wrap variadic FFI functions in our own functions, as we want to minimize // It's advisable to wrap variadic FFI functions in our own functions, as we want to minimize
// access that isn't type-checked. // access that isn't type-checked.
(xconn.xlib.XSetIMValues)( (xconn.xlib.XSetIMValues)(xim, field, callback, ptr::null_mut::<()>());
xim,
field,
callback,
ptr::null_mut::<()>(),
);
xconn.check_errors() xconn.check_errors()
} }
@ -107,18 +101,14 @@ unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
let _ = close_im(xconn, new_im.im); let _ = close_im(xconn, new_im.im);
} }
result result
}.map_err(ReplaceImError::SetDestroyCallbackFailed)?; }
.map_err(ReplaceImError::SetDestroyCallbackFailed)?;
let mut new_contexts = HashMap::new(); let mut new_contexts = HashMap::new();
for (window, old_context) in (*inner).contexts.iter() { for (window, old_context) in (*inner).contexts.iter() {
let spot = old_context.as_ref().map(|old_context| old_context.ic_spot); let spot = old_context.as_ref().map(|old_context| old_context.ic_spot);
let new_context = { let new_context = {
let result = ImeContext::new( let result = ImeContext::new(xconn, new_im.im, *window, spot);
xconn,
new_im.im,
*window,
spot,
);
if result.is_err() { if result.is_err() {
let _ = close_im(xconn, new_im.im); let _ = close_im(xconn, new_im.im);
} }
@ -137,7 +127,7 @@ unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
Ok(()) Ok(())
} }
pub unsafe extern fn xim_instantiate_callback( pub unsafe extern "C" fn xim_instantiate_callback(
_display: *mut ffi::Display, _display: *mut ffi::Display,
client_data: ffi::XPointer, client_data: ffi::XPointer,
// This field is unsupplied. // This field is unsupplied.
@ -160,7 +150,7 @@ pub unsafe extern fn xim_instantiate_callback(
// This callback is triggered when the input method is closed on the server end. When this // This callback is triggered when the input method is closed on the server end. When this
// happens, XCloseIM/XDestroyIC doesn't need to be called, as the resources have already been // happens, XCloseIM/XDestroyIC doesn't need to be called, as the resources have already been
// free'd (attempting to do so causes our connection to freeze). // free'd (attempting to do so causes our connection to freeze).
pub unsafe extern fn xim_destroy_callback( pub unsafe extern "C" fn xim_destroy_callback(
_xim: ffi::XIM, _xim: ffi::XIM,
client_data: ffi::XPointer, client_data: ffi::XPointer,
// This field is unsupplied. // This field is unsupplied.

View file

@ -1,6 +1,8 @@
use std::ptr; use std::{
use std::sync::Arc; os::raw::{c_short, c_void},
use std::os::raw::{c_short, c_void}; ptr,
sync::Arc,
};
use super::{ffi, util, XConnection, XError}; use super::{ffi, util, XConnection, XError};
@ -22,7 +24,8 @@ unsafe fn create_pre_edit_attr<'a>(
ic_spot, ic_spot,
ptr::null_mut::<()>(), ptr::null_mut::<()>(),
), ),
).expect("XVaCreateNestedList returned NULL") )
.expect("XVaCreateNestedList returned NULL")
} }
// WARNING: this struct doesn't destroy its XIC resource when dropped. // WARNING: this struct doesn't destroy its XIC resource when dropped.
@ -49,7 +52,9 @@ impl ImeContext {
}; };
let ic = ic.ok_or(ImeContextCreationError::Null)?; let ic = ic.ok_or(ImeContextCreationError::Null)?;
xconn.check_errors().map_err(ImeContextCreationError::XError)?; xconn
.check_errors()
.map_err(ImeContextCreationError::XError)?;
Ok(ImeContext { Ok(ImeContext {
ic, ic,

View file

@ -1,12 +1,8 @@
use std::mem; use std::{collections::HashMap, mem, ptr, sync::Arc};
use std::ptr;
use std::sync::Arc;
use std::collections::HashMap;
use super::{ffi, XConnection, XError}; use super::{ffi, XConnection, XError};
use super::input_method::PotentialInputMethods; use super::{context::ImeContext, input_method::PotentialInputMethods};
use super::context::ImeContext;
pub unsafe fn close_im(xconn: &Arc<XConnection>, im: ffi::XIM) -> Result<(), XError> { pub unsafe fn close_im(xconn: &Arc<XConnection>, im: ffi::XIM) -> Result<(), XError> {
(xconn.xlib.XCloseIM)(im); (xconn.xlib.XCloseIM)(im);
@ -33,10 +29,7 @@ pub struct ImeInner {
} }
impl ImeInner { impl ImeInner {
pub fn new( pub fn new(xconn: Arc<XConnection>, potential_input_methods: PotentialInputMethods) -> Self {
xconn: Arc<XConnection>,
potential_input_methods: PotentialInputMethods,
) -> Self {
ImeInner { ImeInner {
xconn, xconn,
im: ptr::null_mut(), im: ptr::null_mut(),

View file

@ -1,9 +1,11 @@
use std::env; use std::{
use std::fmt; env,
use std::ptr; ffi::{CStr, CString, IntoStringError},
use std::sync::Arc; fmt,
use std::os::raw::c_char; os::raw::c_char,
use std::ffi::{CStr, CString, IntoStringError}; ptr,
sync::Arc,
};
use parking_lot::Mutex; use parking_lot::Mutex;
@ -13,10 +15,7 @@ lazy_static! {
static ref GLOBAL_LOCK: Mutex<()> = Default::default(); static ref GLOBAL_LOCK: Mutex<()> = Default::default();
} }
unsafe fn open_im( unsafe fn open_im(xconn: &Arc<XConnection>, locale_modifiers: &CStr) -> Option<ffi::XIM> {
xconn: &Arc<XConnection>,
locale_modifiers: &CStr,
) -> Option<ffi::XIM> {
let _lock = GLOBAL_LOCK.lock(); let _lock = GLOBAL_LOCK.lock();
// XSetLocaleModifiers returns... // XSetLocaleModifiers returns...
@ -97,11 +96,9 @@ unsafe fn get_xim_servers(xconn: &Arc<XConnection>) -> Result<Vec<String>, GetXi
let root = (xconn.xlib.XDefaultRootWindow)(xconn.display); let root = (xconn.xlib.XDefaultRootWindow)(xconn.display);
let mut atoms: Vec<ffi::Atom> = xconn.get_property( let mut atoms: Vec<ffi::Atom> = xconn
root, .get_property(root, servers_atom, ffi::XA_ATOM)
servers_atom, .map_err(GetXimServersError::GetPropertyError)?;
ffi::XA_ATOM,
).map_err(GetXimServersError::GetPropertyError)?;
let mut names: Vec<*const c_char> = Vec::with_capacity(atoms.len()); let mut names: Vec<*const c_char> = Vec::with_capacity(atoms.len());
(xconn.xlib.XGetAtomNames)( (xconn.xlib.XGetAtomNames)(
@ -135,15 +132,12 @@ impl InputMethodName {
pub fn from_string(string: String) -> Self { pub fn from_string(string: String) -> Self {
let c_string = CString::new(string.clone()) let c_string = CString::new(string.clone())
.expect("String used to construct CString contained null byte"); .expect("String used to construct CString contained null byte");
InputMethodName { InputMethodName { c_string, string }
c_string,
string,
}
} }
pub fn from_str(string: &str) -> Self { pub fn from_str(string: &str) -> Self {
let c_string = CString::new(string) let c_string =
.expect("String used to construct CString contained null byte"); CString::new(string).expect("String used to construct CString contained null byte");
InputMethodName { InputMethodName {
c_string, c_string,
string: string.to_owned(), string: string.to_owned(),
@ -152,7 +146,7 @@ impl InputMethodName {
} }
impl fmt::Debug for InputMethodName { impl fmt::Debug for InputMethodName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.string.fmt(f) self.string.fmt(f)
} }
} }
@ -254,7 +248,7 @@ impl PotentialInputMethods {
pub fn open_im( pub fn open_im(
&mut self, &mut self,
xconn: &Arc<XConnection>, xconn: &Arc<XConnection>,
callback: Option<&Fn() -> ()>, callback: Option<&dyn Fn() -> ()>,
) -> InputMethodResult { ) -> InputMethodResult {
use self::InputMethodResult::*; use self::InputMethodResult::*;

View file

@ -1,20 +1,24 @@
// Important: all XIM calls need to happen from the same thread! // Important: all XIM calls need to happen from the same thread!
mod callbacks;
mod context;
mod inner; mod inner;
mod input_method; mod input_method;
mod context;
mod callbacks;
use std::sync::Arc; use std::sync::{
use std::sync::mpsc::{Receiver, Sender}; mpsc::{Receiver, Sender},
Arc,
};
use super::{ffi, util, XConnection, XError}; use super::{ffi, util, XConnection, XError};
use self::inner::{close_im, ImeInner};
use self::input_method::PotentialInputMethods;
use self::context::ImeContext;
use self::callbacks::*;
pub use self::context::ImeContextCreationError; pub use self::context::ImeContextCreationError;
use self::{
callbacks::*,
context::ImeContext,
inner::{close_im, ImeInner},
input_method::PotentialInputMethods,
};
pub type ImeReceiver = Receiver<(ffi::Window, i16, i16)>; pub type ImeReceiver = Receiver<(ffi::Window, i16, i16)>;
pub type ImeSender = Sender<(ffi::Window, i16, i16)>; pub type ImeSender = Sender<(ffi::Window, i16, i16)>;
@ -37,10 +41,7 @@ impl Ime {
let potential_input_methods = PotentialInputMethods::new(&xconn); let potential_input_methods = PotentialInputMethods::new(&xconn);
let (mut inner, client_data) = { let (mut inner, client_data) = {
let mut inner = Box::new(ImeInner::new( let mut inner = Box::new(ImeInner::new(xconn, potential_input_methods));
xconn,
potential_input_methods,
));
let inner_ptr = Box::into_raw(inner); let inner_ptr = Box::into_raw(inner);
let client_data = inner_ptr as _; let client_data = inner_ptr as _;
let destroy_callback = ffi::XIMCallback { let destroy_callback = ffi::XIMCallback {
@ -54,9 +55,12 @@ impl Ime {
let xconn = Arc::clone(&inner.xconn); let xconn = Arc::clone(&inner.xconn);
let input_method = inner.potential_input_methods.open_im(&xconn, Some(&|| { let input_method = inner.potential_input_methods.open_im(
let _ = unsafe { set_instantiate_callback(&xconn, client_data) }; &xconn,
})); Some(&|| {
let _ = unsafe { set_instantiate_callback(&xconn, client_data) };
}),
);
let is_fallback = input_method.is_fallback(); let is_fallback = input_method.is_fallback();
if let Some(input_method) = input_method.ok() { if let Some(input_method) = input_method.ok() {
@ -84,19 +88,12 @@ impl Ime {
// Ok(_) indicates that nothing went wrong internally // Ok(_) indicates that nothing went wrong internally
// Ok(true) indicates that the action was actually performed // Ok(true) indicates that the action was actually performed
// Ok(false) indicates that the action is not presently applicable // Ok(false) indicates that the action is not presently applicable
pub fn create_context(&mut self, window: ffi::Window) pub fn create_context(&mut self, window: ffi::Window) -> Result<bool, ImeContextCreationError> {
-> Result<bool, ImeContextCreationError>
{
let context = if self.is_destroyed() { let context = if self.is_destroyed() {
// Create empty entry in map, so that when IME is rebuilt, this window has a context. // Create empty entry in map, so that when IME is rebuilt, this window has a context.
None None
} else { } else {
Some(unsafe { ImeContext::new( Some(unsafe { ImeContext::new(&self.inner.xconn, self.inner.im, window, None) }?)
&self.inner.xconn,
self.inner.im,
window,
None,
) }?)
}; };
self.inner.contexts.insert(window, context); self.inner.contexts.insert(window, context);
Ok(!self.is_destroyed()) Ok(!self.is_destroyed())

View file

@ -1,49 +1,58 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] #![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
pub mod ffi; mod dnd;
mod event_processor;
mod events; mod events;
pub mod ffi;
mod ime;
mod monitor; mod monitor;
pub mod util;
mod window; mod window;
mod xdisplay; mod xdisplay;
mod dnd;
mod ime;
pub mod util;
mod event_processor;
pub use self::monitor::MonitorHandle; pub use self::{
pub use self::window::UnownedWindow; monitor::MonitorHandle,
pub use self::xdisplay::{XConnection, XNotSupported, XError}; window::UnownedWindow,
xdisplay::{XConnection, XError, XNotSupported},
};
use std::{mem, slice}; use std::{
use std::cell::RefCell; cell::RefCell,
use std::collections::{VecDeque, HashMap, HashSet}; collections::{HashMap, HashSet, VecDeque},
use std::ffi::CStr; ffi::CStr,
use std::ops::Deref; mem,
use std::os::raw::*; ops::Deref,
use std::rc::Rc; os::raw::*,
use std::sync::{Arc, mpsc, Weak, Mutex}; rc::Rc,
slice,
sync::{mpsc, Arc, Mutex, Weak},
};
use libc::{self, setlocale, LC_CTYPE}; use libc::{self, setlocale, LC_CTYPE};
use error::OsError as RootOsError; use self::{
use event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}; dnd::{Dnd, DndState},
use event::{WindowEvent, Event}; event_processor::EventProcessor,
use platform_impl::PlatformSpecificWindowBuilderAttributes; ime::{Ime, ImeCreationError, ImeReceiver, ImeSender},
use platform_impl::platform::sticky_exit_callback; };
use window::{WindowAttributes}; use crate::{
use self::dnd::{Dnd, DndState}; error::OsError as RootOsError,
use self::ime::{ImeReceiver, ImeSender, ImeCreationError, Ime}; event::{Event, WindowEvent},
use self::event_processor::EventProcessor; event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
platform_impl::{platform::sticky_exit_callback, PlatformSpecificWindowBuilderAttributes},
window::WindowAttributes,
};
pub struct EventLoopWindowTarget<T> { pub struct EventLoopWindowTarget<T> {
xconn: Arc<XConnection>, xconn: Arc<XConnection>,
wm_delete_window: ffi::Atom, wm_delete_window: ffi::Atom,
net_wm_ping: ffi::Atom,
ime_sender: ImeSender, ime_sender: ImeSender,
root: ffi::Window, root: ffi::Window,
ime: RefCell<Ime>, ime: RefCell<Ime>,
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>, windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
pending_redraws: Arc<Mutex<HashSet<WindowId>>>, pending_redraws: Arc<Mutex<HashSet<WindowId>>>,
_marker: ::std::marker::PhantomData<T> _marker: ::std::marker::PhantomData<T>,
} }
pub struct EventLoop<T: 'static> { pub struct EventLoop<T: 'static> {
@ -53,7 +62,7 @@ pub struct EventLoop<T: 'static> {
pending_user_events: Rc<RefCell<VecDeque<T>>>, pending_user_events: Rc<RefCell<VecDeque<T>>>,
user_sender: ::calloop::channel::Sender<T>, user_sender: ::calloop::channel::Sender<T>,
pending_events: Rc<RefCell<VecDeque<Event<T>>>>, pending_events: Rc<RefCell<VecDeque<Event<T>>>>,
target: Rc<RootELW<T>> target: Rc<RootELW<T>>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -67,13 +76,17 @@ impl<T: 'static> EventLoop<T> {
let wm_delete_window = unsafe { xconn.get_atom_unchecked(b"WM_DELETE_WINDOW\0") }; let wm_delete_window = unsafe { xconn.get_atom_unchecked(b"WM_DELETE_WINDOW\0") };
let net_wm_ping = unsafe { xconn.get_atom_unchecked(b"_NET_WM_PING\0") };
let dnd = Dnd::new(Arc::clone(&xconn)) let dnd = Dnd::new(Arc::clone(&xconn))
.expect("Failed to call XInternAtoms when initializing drag and drop"); .expect("Failed to call XInternAtoms when initializing drag and drop");
let (ime_sender, ime_receiver) = mpsc::channel(); let (ime_sender, ime_receiver) = mpsc::channel();
// Input methods will open successfully without setting the locale, but it won't be // Input methods will open successfully without setting the locale, but it won't be
// possible to actually commit pre-edit sequences. // possible to actually commit pre-edit sequences.
unsafe { setlocale(LC_CTYPE, b"\0".as_ptr() as *const _); } unsafe {
setlocale(LC_CTYPE, b"\0".as_ptr() as *const _);
}
let ime = RefCell::new({ let ime = RefCell::new({
let result = Ime::new(Arc::clone(&xconn)); let result = Ime::new(Arc::clone(&xconn));
if let Err(ImeCreationError::OpenFailure(ref state)) = result { if let Err(ImeCreationError::OpenFailure(ref state)) = result {
@ -82,7 +95,8 @@ impl<T: 'static> EventLoop<T> {
result.expect("Failed to set input method destruction callback") result.expect("Failed to set input method destruction callback")
}); });
let randr_event_offset = xconn.select_xrandr_input(root) let randr_event_offset = xconn
.select_xrandr_input(root)
.expect("Failed to query XRandR extension"); .expect("Failed to query XRandR extension");
let xi2ext = unsafe { let xi2ext = unsafe {
@ -96,7 +110,8 @@ impl<T: 'static> EventLoop<T> {
b"XInputExtension\0".as_ptr() as *const c_char, b"XInputExtension\0".as_ptr() as *const c_char,
&mut result.opcode as *mut c_int, &mut result.opcode as *mut c_int,
&mut result.first_event_id as *mut c_int, &mut result.first_event_id as *mut c_int,
&mut result.first_error_id as *mut c_int); &mut result.first_error_id as *mut c_int,
);
if res == ffi::False { if res == ffi::False {
panic!("X server missing XInput extension"); panic!("X server missing XInput extension");
} }
@ -110,18 +125,18 @@ impl<T: 'static> EventLoop<T> {
xconn.display, xconn.display,
&mut xinput_major_ver, &mut xinput_major_ver,
&mut xinput_minor_ver, &mut xinput_minor_ver,
) != ffi::Success as libc::c_int { ) != ffi::Success as libc::c_int
{
panic!( panic!(
"X server has XInput extension {}.{} but does not support XInput2", "X server has XInput extension {}.{} but does not support XInput2",
xinput_major_ver, xinput_major_ver, xinput_minor_ver,
xinput_minor_ver,
); );
} }
} }
xconn.update_cached_wm_info(root); xconn.update_cached_wm_info(root);
let target = Rc::new(RootELW{ let target = Rc::new(RootELW {
p: super::EventLoopWindowTarget::X(EventLoopWindowTarget { p: super::EventLoopWindowTarget::X(EventLoopWindowTarget {
ime, ime,
root, root,
@ -130,9 +145,10 @@ impl<T: 'static> EventLoop<T> {
ime_sender, ime_sender,
xconn, xconn,
wm_delete_window, wm_delete_window,
net_wm_ping,
pending_redraws: Default::default(), pending_redraws: Default::default(),
}), }),
_marker: ::std::marker::PhantomData _marker: ::std::marker::PhantomData,
}); });
// A calloop event loop to drive us // A calloop event loop to drive us
@ -144,11 +160,14 @@ impl<T: 'static> EventLoop<T> {
let (user_sender, user_channel) = ::calloop::channel::channel(); let (user_sender, user_channel) = ::calloop::channel::channel();
let _user_source = inner_loop.handle().insert_source(user_channel, move |evt, &mut()| { let _user_source = inner_loop
if let ::calloop::channel::Event::Msg(msg) = evt { .handle()
pending_user_events2.borrow_mut().push_back(msg); .insert_source(user_channel, move |evt, &mut ()| {
} if let ::calloop::channel::Event::Msg(msg) = evt {
}).unwrap(); pending_user_events2.borrow_mut().push_back(msg);
}
})
.unwrap();
// Handle X11 events // Handle X11 events
let pending_events: Rc<RefCell<VecDeque<_>>> = Default::default(); let pending_events: Rc<RefCell<VecDeque<_>>> = Default::default();
@ -164,20 +183,20 @@ impl<T: 'static> EventLoop<T> {
// Register for device hotplug events // Register for device hotplug events
// (The request buffer is flushed during `init_device`) // (The request buffer is flushed during `init_device`)
get_xtarget(&target).xconn.select_xinput_events( get_xtarget(&target)
root, .xconn
ffi::XIAllDevices, .select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask)
ffi::XI_HierarchyChangedMask, .queue();
).queue();
processor.init_device(ffi::XIAllDevices); processor.init_device(ffi::XIAllDevices);
// Setup the X11 event source // Setup the X11 event source
let mut x11_events = ::calloop::generic::Generic::from_raw_fd(get_xtarget(&target).xconn.x11_fd); let mut x11_events =
::calloop::generic::Generic::from_raw_fd(get_xtarget(&target).xconn.x11_fd);
x11_events.set_interest(::calloop::mio::Ready::readable()); x11_events.set_interest(::calloop::mio::Ready::readable());
let _x11_source = inner_loop.handle().insert_source( let _x11_source = inner_loop
x11_events, .handle()
{ .insert_source(x11_events, {
let pending_events = pending_events.clone(); let pending_events = pending_events.clone();
let mut callback = move |event| { let mut callback = move |event| {
pending_events.borrow_mut().push_back(event); pending_events.borrow_mut().push_back(event);
@ -191,8 +210,8 @@ impl<T: 'static> EventLoop<T> {
} }
} }
} }
} })
).unwrap(); .unwrap();
let result = EventLoop { let result = EventLoop {
inner_loop, inner_loop,
@ -201,7 +220,7 @@ impl<T: 'static> EventLoop<T> {
_user_source, _user_source,
user_sender, user_sender,
pending_user_events, pending_user_events,
target target,
}; };
result result
@ -224,7 +243,8 @@ impl<T: 'static> EventLoop<T> {
} }
pub fn run_return<F>(&mut self, mut callback: F) pub fn run_return<F>(&mut self, mut callback: F)
where F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow) where
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
{ {
let mut control_flow = ControlFlow::default(); let mut control_flow = ControlFlow::default();
let wt = get_xtarget(&self.target); let wt = get_xtarget(&self.target);
@ -243,10 +263,10 @@ impl<T: 'static> EventLoop<T> {
let mut guard = self.pending_user_events.borrow_mut(); let mut guard = self.pending_user_events.borrow_mut();
for evt in guard.drain(..) { for evt in guard.drain(..) {
sticky_exit_callback( sticky_exit_callback(
::event::Event::UserEvent(evt), crate::event::Event::UserEvent(evt),
&self.target, &self.target,
&mut control_flow, &mut control_flow,
&mut callback &mut callback,
); );
} }
} }
@ -256,48 +276,49 @@ impl<T: 'static> EventLoop<T> {
for wid in guard.drain() { for wid in guard.drain() {
sticky_exit_callback( sticky_exit_callback(
Event::WindowEvent { Event::WindowEvent {
window_id: ::window::WindowId(super::WindowId::X(wid)), window_id: crate::window::WindowId(super::WindowId::X(wid)),
event: WindowEvent::RedrawRequested event: WindowEvent::RedrawRequested,
}, },
&self.target, &self.target,
&mut control_flow, &mut control_flow,
&mut callback &mut callback,
); );
} }
} }
// send Events cleared // send Events cleared
{ {
sticky_exit_callback( sticky_exit_callback(
::event::Event::EventsCleared, crate::event::Event::EventsCleared,
&self.target, &self.target,
&mut control_flow, &mut control_flow,
&mut callback &mut callback,
); );
} }
// flush the X11 connection
unsafe { (wt.xconn.xlib.XFlush)(wt.xconn.display); }
match control_flow { match control_flow {
ControlFlow::Exit => break, ControlFlow::Exit => break,
ControlFlow::Poll => { ControlFlow::Poll => {
// non-blocking dispatch // non-blocking dispatch
self.inner_loop.dispatch(Some(::std::time::Duration::from_millis(0)), &mut ()).unwrap(); self.inner_loop
control_flow = ControlFlow::default(); .dispatch(Some(::std::time::Duration::from_millis(0)), &mut ())
callback(::event::Event::NewEvents(::event::StartCause::Poll), &self.target, &mut control_flow); .unwrap();
}, callback(
crate::event::Event::NewEvents(crate::event::StartCause::Poll),
&self.target,
&mut control_flow,
);
}
ControlFlow::Wait => { ControlFlow::Wait => {
self.inner_loop.dispatch(None, &mut ()).unwrap(); self.inner_loop.dispatch(None, &mut ()).unwrap();
control_flow = ControlFlow::default();
callback( callback(
::event::Event::NewEvents(::event::StartCause::WaitCancelled { crate::event::Event::NewEvents(crate::event::StartCause::WaitCancelled {
start: ::std::time::Instant::now(), start: ::std::time::Instant::now(),
requested_resume: None requested_resume: None,
}), }),
&self.target, &self.target,
&mut control_flow &mut control_flow,
); );
}, }
ControlFlow::WaitUntil(deadline) => { ControlFlow::WaitUntil(deadline) => {
let start = ::std::time::Instant::now(); let start = ::std::time::Instant::now();
// compute the blocking duration // compute the blocking duration
@ -307,36 +328,44 @@ impl<T: 'static> EventLoop<T> {
::std::time::Duration::from_millis(0) ::std::time::Duration::from_millis(0)
}; };
self.inner_loop.dispatch(Some(duration), &mut ()).unwrap(); self.inner_loop.dispatch(Some(duration), &mut ()).unwrap();
control_flow = ControlFlow::default();
let now = std::time::Instant::now(); let now = std::time::Instant::now();
if now < deadline { if now < deadline {
callback( callback(
::event::Event::NewEvents(::event::StartCause::WaitCancelled { crate::event::Event::NewEvents(
start, crate::event::StartCause::WaitCancelled {
requested_resume: Some(deadline) start,
}), requested_resume: Some(deadline),
},
),
&self.target, &self.target,
&mut control_flow &mut control_flow,
); );
} else { } else {
callback( callback(
::event::Event::NewEvents(::event::StartCause::ResumeTimeReached { crate::event::Event::NewEvents(
start, crate::event::StartCause::ResumeTimeReached {
requested_resume: deadline start,
}), requested_resume: deadline,
},
),
&self.target, &self.target,
&mut control_flow &mut control_flow,
); );
} }
} }
} }
} }
callback(::event::Event::LoopDestroyed, &self.target, &mut control_flow); callback(
crate::event::Event::LoopDestroyed,
&self.target,
&mut control_flow,
);
} }
pub fn run<F>(mut self, callback: F) -> ! pub fn run<F>(mut self, callback: F) -> !
where F: 'static + FnMut(Event<T>, &RootELW<T>, &mut ControlFlow) where
F: 'static + FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
{ {
self.run_return(callback); self.run_return(callback);
::std::process::exit(0); ::std::process::exit(0);
@ -345,10 +374,10 @@ impl<T: 'static> EventLoop<T> {
fn get_xtarget<T>(rt: &RootELW<T>) -> &EventLoopWindowTarget<T> { fn get_xtarget<T>(rt: &RootELW<T>) -> &EventLoopWindowTarget<T> {
if let super::EventLoopWindowTarget::X(ref target) = rt.p { if let super::EventLoopWindowTarget::X(ref target) = rt.p {
target target
} else { } else {
unreachable!(); unreachable!();
} }
} }
impl<T: 'static> EventLoopProxy<T> { impl<T: 'static> EventLoopProxy<T> {
@ -368,19 +397,17 @@ impl<'a> DeviceInfo<'a> {
unsafe { unsafe {
let mut count = mem::uninitialized(); let mut count = mem::uninitialized();
let info = (xconn.xinput2.XIQueryDevice)(xconn.display, device, &mut count); let info = (xconn.xinput2.XIQueryDevice)(xconn.display, device, &mut count);
xconn.check_errors() xconn.check_errors().ok().and_then(|_| {
.ok() if info.is_null() || count == 0 {
.and_then(|_| { None
if info.is_null() || count == 0 { } else {
None Some(DeviceInfo {
} else { xconn,
Some(DeviceInfo { info,
xconn, count: count as usize,
info, })
count: count as usize, }
}) })
}
})
} }
} }
} }
@ -431,10 +458,11 @@ impl Window {
pub fn new<T>( pub fn new<T>(
event_loop: &EventLoopWindowTarget<T>, event_loop: &EventLoopWindowTarget<T>,
attribs: WindowAttributes, attribs: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, RootOsError> { ) -> Result<Self, RootOsError> {
let window = Arc::new(UnownedWindow::new(&event_loop, attribs, pl_attribs)?); let window = Arc::new(UnownedWindow::new(&event_loop, attribs, pl_attribs)?);
event_loop.windows event_loop
.windows
.borrow_mut() .borrow_mut()
.insert(window.id(), Arc::downgrade(&window)); .insert(window.id(), Arc::downgrade(&window));
Ok(Window(window)) Ok(Window(window))
@ -457,11 +485,14 @@ impl Drop for Window {
/// extract the cookie from a GenericEvent XEvent and release the cookie data once it has been processed /// extract the cookie from a GenericEvent XEvent and release the cookie data once it has been processed
struct GenericEventCookie<'a> { struct GenericEventCookie<'a> {
xconn: &'a XConnection, xconn: &'a XConnection,
cookie: ffi::XGenericEventCookie cookie: ffi::XGenericEventCookie,
} }
impl<'a> GenericEventCookie<'a> { impl<'a> GenericEventCookie<'a> {
fn from_event<'b>(xconn: &'b XConnection, event: ffi::XEvent) -> Option<GenericEventCookie<'b>> { fn from_event<'b>(
xconn: &'b XConnection,
event: ffi::XEvent,
) -> Option<GenericEventCookie<'b>> {
unsafe { unsafe {
let mut cookie: ffi::XGenericEventCookie = From::from(event); let mut cookie: ffi::XGenericEventCookie = From::from(event);
if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == ffi::True { if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == ffi::True {
@ -488,8 +519,12 @@ struct XExtension {
first_error_id: c_int, first_error_id: c_int,
} }
fn mkwid(w: ffi::Window) -> ::window::WindowId { ::window::WindowId(::platform_impl::WindowId::X(WindowId(w))) } fn mkwid(w: ffi::Window) -> crate::window::WindowId {
fn mkdid(w: c_int) -> ::event::DeviceId { ::event::DeviceId(::platform_impl::DeviceId::X(DeviceId(w))) } crate::window::WindowId(crate::platform_impl::WindowId::X(WindowId(w)))
}
fn mkdid(w: c_int) -> crate::event::DeviceId {
crate::event::DeviceId(crate::platform_impl::DeviceId::X(DeviceId(w)))
}
#[derive(Debug)] #[derive(Debug)]
struct Device { struct Device {
@ -528,23 +563,30 @@ impl Device {
| ffi::XI_RawKeyPressMask | ffi::XI_RawKeyPressMask
| ffi::XI_RawKeyReleaseMask; | ffi::XI_RawKeyReleaseMask;
// The request buffer is flushed when we poll for events // The request buffer is flushed when we poll for events
wt.xconn.select_xinput_events(wt.root, info.deviceid, mask).queue(); wt.xconn
.select_xinput_events(wt.root, info.deviceid, mask)
.queue();
// Identify scroll axes // Identify scroll axes
for class_ptr in Device::classes(info) { for class_ptr in Device::classes(info) {
let class = unsafe { &**class_ptr }; let class = unsafe { &**class_ptr };
match class._type { match class._type {
ffi::XIScrollClass => { ffi::XIScrollClass => {
let info = unsafe { mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIScrollClassInfo>(class) }; let info = unsafe {
scroll_axes.push((info.number, ScrollAxis { mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIScrollClassInfo>(class)
increment: info.increment, };
orientation: match info.scroll_type { scroll_axes.push((
ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal, info.number,
ffi::XIScrollTypeVertical => ScrollOrientation::Vertical, ScrollAxis {
_ => { unreachable!() } increment: info.increment,
orientation: match info.scroll_type {
ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal,
ffi::XIScrollTypeVertical => ScrollOrientation::Vertical,
_ => unreachable!(),
},
position: 0.0,
}, },
position: 0.0, ));
}));
} }
_ => {} _ => {}
} }
@ -553,7 +595,7 @@ impl Device {
let mut device = Device { let mut device = Device {
name: name.into_owned(), name: name.into_owned(),
scroll_axes: scroll_axes, scroll_axes,
attachment: info.attachment, attachment: info.attachment,
}; };
device.reset_scroll_position(info); device.reset_scroll_position(info);
@ -566,8 +608,14 @@ impl Device {
let class = unsafe { &**class_ptr }; let class = unsafe { &**class_ptr };
match class._type { match class._type {
ffi::XIValuatorClass => { ffi::XIValuatorClass => {
let info = unsafe { mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIValuatorClassInfo>(class) }; let info = unsafe {
if let Some(&mut (_, ref mut axis)) = self.scroll_axes.iter_mut().find(|&&mut (axis, _)| axis == info.number) { mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIValuatorClassInfo>(class)
};
if let Some(&mut (_, ref mut axis)) = self
.scroll_axes
.iter_mut()
.find(|&&mut (axis, _)| axis == info.number)
{
axis.position = info.value; axis.position = info.value;
} }
} }
@ -579,11 +627,18 @@ impl Device {
#[inline] #[inline]
fn physical_device(info: &ffi::XIDeviceInfo) -> bool { fn physical_device(info: &ffi::XIDeviceInfo) -> bool {
info._use == ffi::XISlaveKeyboard || info._use == ffi::XISlavePointer || info._use == ffi::XIFloatingSlave info._use == ffi::XISlaveKeyboard
|| info._use == ffi::XISlavePointer
|| info._use == ffi::XIFloatingSlave
} }
#[inline] #[inline]
fn classes(info: &ffi::XIDeviceInfo) -> &[*const ffi::XIAnyClassInfo] { fn classes(info: &ffi::XIDeviceInfo) -> &[*const ffi::XIAnyClassInfo] {
unsafe { slice::from_raw_parts(info.classes as *const *const ffi::XIAnyClassInfo, info.num_classes as usize) } unsafe {
slice::from_raw_parts(
info.classes as *const *const ffi::XIAnyClassInfo,
info.num_classes as usize,
)
}
} }
} }

View file

@ -2,15 +2,16 @@ use std::os::raw::*;
use parking_lot::Mutex; use parking_lot::Mutex;
use dpi::{PhysicalPosition, PhysicalSize}; use super::{
use super::{util, XConnection, XError}; ffi::{
use super::ffi::{ RRCrtcChangeNotifyMask, RROutputPropertyNotifyMask, RRScreenChangeNotifyMask, True, Window,
RRCrtcChangeNotifyMask, XRRScreenResources,
RROutputPropertyNotifyMask, },
RRScreenChangeNotifyMask, util, XConnection, XError,
True, };
Window, use crate::{
XRRScreenResources, dpi::{PhysicalPosition, PhysicalSize},
monitor::VideoMode,
}; };
// Used to test XRandR < 1.5 code path. This should always be committed as false. // Used to test XRandR < 1.5 code path. This should always be committed as false.
@ -56,6 +57,8 @@ pub struct MonitorHandle {
pub(crate) hidpi_factor: f64, pub(crate) hidpi_factor: f64,
/// Used to determine which windows are on this monitor /// Used to determine which windows are on this monitor
pub(crate) rect: util::AaRect, pub(crate) rect: util::AaRect,
/// Supported video modes on this monitor
video_modes: Vec<VideoMode>,
} }
impl MonitorHandle { impl MonitorHandle {
@ -66,8 +69,8 @@ impl MonitorHandle {
repr: util::MonitorRepr, repr: util::MonitorRepr,
primary: bool, primary: bool,
) -> Option<Self> { ) -> Option<Self> {
let (name, hidpi_factor) = unsafe { xconn.get_output_info(resources, &repr)? }; let (name, hidpi_factor, video_modes) = unsafe { xconn.get_output_info(resources, &repr)? };
let (dimensions, position) = unsafe { (repr.dimensions(), repr.position()) }; let (dimensions, position) = unsafe { (repr.size(), repr.position()) };
let rect = util::AaRect::new(position, dimensions); let rect = util::AaRect::new(position, dimensions);
Some(MonitorHandle { Some(MonitorHandle {
id, id,
@ -77,6 +80,7 @@ impl MonitorHandle {
position, position,
primary, primary,
rect, rect,
video_modes,
}) })
} }
@ -89,7 +93,7 @@ impl MonitorHandle {
self.id as u32 self.id as u32
} }
pub fn dimensions(&self) -> PhysicalSize { pub fn size(&self) -> PhysicalSize {
self.dimensions.into() self.dimensions.into()
} }
@ -101,6 +105,11 @@ impl MonitorHandle {
pub fn hidpi_factor(&self) -> f64 { pub fn hidpi_factor(&self) -> f64 {
self.hidpi_factor self.hidpi_factor
} }
#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.video_modes.clone().into_iter()
}
} }
impl XConnection { impl XConnection {
@ -151,7 +160,8 @@ impl XConnection {
// videowalls. // videowalls.
let xrandr_1_5 = self.xrandr_1_5.as_ref().unwrap(); let xrandr_1_5 = self.xrandr_1_5.as_ref().unwrap();
let mut monitor_count = 0; let mut monitor_count = 0;
let monitors = (xrandr_1_5.XRRGetMonitors)(self.display, root, 1, &mut monitor_count); let monitors =
(xrandr_1_5.XRRGetMonitors)(self.display, root, 1, &mut monitor_count);
assert!(monitor_count >= 0); assert!(monitor_count >= 0);
available = Vec::with_capacity(monitor_count as usize); available = Vec::with_capacity(monitor_count as usize);
for monitor_index in 0..monitor_count { for monitor_index in 0..monitor_count {
@ -164,7 +174,8 @@ impl XConnection {
monitor_index as u32, monitor_index as u32,
monitor.into(), monitor.into(),
is_primary, is_primary,
).map(|monitor_id| available.push(monitor_id)); )
.map(|monitor_id| available.push(monitor_id));
} }
(xrandr_1_5.XRRFreeMonitors)(monitors); (xrandr_1_5.XRRFreeMonitors)(monitors);
} else { } else {
@ -181,13 +192,8 @@ impl XConnection {
let crtc = util::MonitorRepr::from(crtc); let crtc = util::MonitorRepr::from(crtc);
let is_primary = crtc.get_output() == primary; let is_primary = crtc.get_output() == primary;
has_primary |= is_primary; has_primary |= is_primary;
MonitorHandle::from_repr( MonitorHandle::from_repr(self, resources, crtc_id as u32, crtc, is_primary)
self, .map(|monitor_id| available.push(monitor_id));
resources,
crtc_id as u32,
crtc,
is_primary,
).map(|monitor_id| available.push(monitor_id));
} }
(self.xrandr.XRRFreeCrtcInfo)(crtc); (self.xrandr.XRRFreeCrtcInfo)(crtc);
} }
@ -235,13 +241,8 @@ impl XConnection {
if version_lock.is_none() { if version_lock.is_none() {
let mut major = 0; let mut major = 0;
let mut minor = 0; let mut minor = 0;
let has_extension = unsafe { let has_extension =
(self.xrandr.XRRQueryVersion)( unsafe { (self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor) };
self.display,
&mut major,
&mut minor,
)
};
if has_extension != True { if has_extension != True {
panic!("[winit] XRandR extension not available."); panic!("[winit] XRandR extension not available.");
} }
@ -252,11 +253,7 @@ impl XConnection {
let mut event_offset = 0; let mut event_offset = 0;
let mut error_offset = 0; let mut error_offset = 0;
let status = unsafe { let status = unsafe {
(self.xrandr.XRRQueryExtension)( (self.xrandr.XRRQueryExtension)(self.display, &mut event_offset, &mut error_offset)
self.display,
&mut event_offset,
&mut error_offset,
)
}; };
if status != True { if status != True {
@ -264,9 +261,7 @@ impl XConnection {
unreachable!("[winit] `XRRQueryExtension` failed but no error was received."); unreachable!("[winit] `XRRQueryExtension` failed but no error was received.");
} }
let mask = RRCrtcChangeNotifyMask let mask = RRCrtcChangeNotifyMask | RROutputPropertyNotifyMask | RRScreenChangeNotifyMask;
| RROutputPropertyNotifyMask
| RRScreenChangeNotifyMask;
unsafe { (self.xrandr.XRRSelectInput)(self.display, root, mask) }; unsafe { (self.xrandr.XRRSelectInput)(self.display, root, mask) };
Ok(event_offset) Ok(event_offset)

View file

@ -1,7 +1,9 @@
use std::collections::HashMap; use std::{
use std::ffi::{CStr, CString}; collections::HashMap,
use std::fmt::Debug; ffi::{CStr, CString},
use std::os::raw::*; fmt::Debug,
os::raw::*,
};
use parking_lot::Mutex; use parking_lot::Mutex;
@ -21,11 +23,9 @@ impl XConnection {
if let Some(atom) = cached_atom { if let Some(atom) = cached_atom {
atom atom
} else { } else {
let atom = unsafe { (self.xlib.XInternAtom)( let atom = unsafe {
self.display, (self.xlib.XInternAtom)(self.display, name.as_ptr() as *const c_char, ffi::False)
name.as_ptr() as *const c_char, };
ffi::False,
) };
if atom == 0 { if atom == 0 {
let msg = format!( let msg = format!(
"`XInternAtom` failed, which really shouldn't happen. Atom: {:?}, Error: {:#?}", "`XInternAtom` failed, which really shouldn't happen. Atom: {:?}, Error: {:#?}",
@ -52,7 +52,7 @@ impl XConnection {
// Note: this doesn't use caching, for the sake of simplicity. // Note: this doesn't use caching, for the sake of simplicity.
// If you're dealing with this many atoms, you'll usually want to cache them locally anyway. // If you're dealing with this many atoms, you'll usually want to cache them locally anyway.
pub unsafe fn get_atoms(&self, names: &[*mut c_char]) -> Result<Vec<ffi::Atom>, XError> { pub unsafe fn get_atoms(&self, names: &[*mut c_char]) -> Result<Vec<ffi::Atom>, XError> {
let mut atoms = Vec::with_capacity(names.len()); let mut atoms = Vec::with_capacity(names.len());
(self.xlib.XInternAtoms)( (self.xlib.XInternAtoms)(
self.display, self.display,

View file

@ -8,7 +8,7 @@ impl XConnection {
target_window: c_ulong, target_window: c_ulong,
event_mask: Option<c_long>, event_mask: Option<c_long>,
event: T, event: T,
) -> Flusher { ) -> Flusher<'_> {
let event_mask = event_mask.unwrap_or(ffi::NoEventMask); let event_mask = event_mask.unwrap_or(ffi::NoEventMask);
unsafe { unsafe {
(self.xlib.XSendEvent)( (self.xlib.XSendEvent)(
@ -24,12 +24,12 @@ impl XConnection {
pub fn send_client_msg( pub fn send_client_msg(
&self, &self,
window: c_ulong, // The window this is "about"; not necessarily this window window: c_ulong, // The window this is "about"; not necessarily this window
target_window: c_ulong, // The window we're sending to target_window: c_ulong, // The window we're sending to
message_type: ffi::Atom, message_type: ffi::Atom,
event_mask: Option<c_long>, event_mask: Option<c_long>,
data: ClientMsgPayload, data: ClientMsgPayload,
) -> Flusher { ) -> Flusher<'_> {
let mut event: ffi::XClientMessageEvent = unsafe { mem::uninitialized() }; let mut event: ffi::XClientMessageEvent = unsafe { mem::uninitialized() };
event.type_ = ffi::ClientMessage; event.type_ = ffi::ClientMessage;
event.display = self.display; event.display = self.display;
@ -45,12 +45,12 @@ impl XConnection {
// to send more than one message worth of data. // to send more than one message worth of data.
pub fn send_client_msg_multi<T: Formattable>( pub fn send_client_msg_multi<T: Formattable>(
&self, &self,
window: c_ulong, // The window this is "about"; not necessarily this window window: c_ulong, // The window this is "about"; not necessarily this window
target_window: c_ulong, // The window we're sending to target_window: c_ulong, // The window we're sending to
message_type: ffi::Atom, message_type: ffi::Atom,
event_mask: Option<c_long>, event_mask: Option<c_long>,
data: &[T], data: &[T],
) -> Flusher { ) -> Flusher<'_> {
let format = T::FORMAT; let format = T::FORMAT;
let size_of_t = mem::size_of::<T>(); let size_of_t = mem::size_of::<T>();
debug_assert_eq!(size_of_t, format.get_actual_size()); debug_assert_eq!(size_of_t, format.get_actual_size());

View file

@ -1,6 +1,4 @@
use std::fmt::Debug; use std::{fmt::Debug, mem, os::raw::*};
use std::mem;
use std::os::raw::*;
// This isn't actually the number of the bits in the format. // This isn't actually the number of the bits in the format.
// X11 does a match on this value to determine which type to call sizeof on. // X11 does a match on this value to determine which type to call sizeof on.
@ -50,9 +48,21 @@ pub trait Formattable: Debug + Clone + Copy + PartialEq + PartialOrd {
} }
// You might be surprised by the absence of c_int, but not as surprised as X11 would be by the presence of it. // You might be surprised by the absence of c_int, but not as surprised as X11 would be by the presence of it.
impl Formattable for c_schar { const FORMAT: Format = Format::Char; } impl Formattable for c_schar {
impl Formattable for c_uchar { const FORMAT: Format = Format::Char; } const FORMAT: Format = Format::Char;
impl Formattable for c_short { const FORMAT: Format = Format::Short; } }
impl Formattable for c_ushort { const FORMAT: Format = Format::Short; } impl Formattable for c_uchar {
impl Formattable for c_long { const FORMAT: Format = Format::Long; } const FORMAT: Format = Format::Char;
impl Formattable for c_ulong { const FORMAT: Format = Format::Long; } }
impl Formattable for c_short {
const FORMAT: Format = Format::Short;
}
impl Formattable for c_ushort {
const FORMAT: Format = Format::Short;
}
impl Formattable for c_long {
const FORMAT: Format = Format::Long;
}
impl Formattable for c_ulong {
const FORMAT: Format = Format::Long;
}

View file

@ -1,7 +1,7 @@
use std::cmp; use std::cmp;
use super::*; use super::*;
use dpi::{LogicalPosition, LogicalSize}; use crate::dpi::{LogicalPosition, LogicalSize};
// Friendly neighborhood axis-aligned rectangle // Friendly neighborhood axis-aligned rectangle
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -16,7 +16,12 @@ impl AaRect {
pub fn new((x, y): (i32, i32), (width, height): (u32, u32)) -> Self { pub fn new((x, y): (i32, i32), (width, height): (u32, u32)) -> Self {
let (x, y) = (x as i64, y as i64); let (x, y) = (x as i64, y as i64);
let (width, height) = (width as i64, height as i64); let (width, height) = (width as i64, height as i64);
AaRect { x, y, width, height } AaRect {
x,
y,
width,
height,
}
} }
pub fn contains_point(&self, x: i64, y: i64) -> bool { pub fn contains_point(&self, x: i64, y: i64) -> bool {
@ -73,7 +78,12 @@ pub struct FrameExtents {
impl FrameExtents { impl FrameExtents {
pub fn new(left: c_ulong, right: c_ulong, top: c_ulong, bottom: c_ulong) -> Self { pub fn new(left: c_ulong, right: c_ulong, top: c_ulong, bottom: c_ulong) -> Self {
FrameExtents { left, right, top, bottom } FrameExtents {
left,
right,
top,
bottom,
}
} }
pub fn from_border(border: c_ulong) -> Self { pub fn from_border(border: c_ulong) -> Self {
@ -116,13 +126,20 @@ impl FrameExtentsHeuristic {
pub fn inner_pos_to_outer(&self, x: i32, y: i32) -> (i32, i32) { pub fn inner_pos_to_outer(&self, x: i32, y: i32) -> (i32, i32) {
use self::FrameExtentsHeuristicPath::*; use self::FrameExtentsHeuristicPath::*;
if self.heuristic_path != UnsupportedBordered { if self.heuristic_path != UnsupportedBordered {
(x - self.frame_extents.left as i32, y - self.frame_extents.top as i32) (
x - self.frame_extents.left as i32,
y - self.frame_extents.top as i32,
)
} else { } else {
(x, y) (x, y)
} }
} }
pub fn inner_pos_to_outer_logical(&self, mut logical: LogicalPosition, factor: f64) -> LogicalPosition { pub fn inner_pos_to_outer_logical(
&self,
mut logical: LogicalPosition,
factor: f64,
) -> LogicalPosition {
use self::FrameExtentsHeuristicPath::*; use self::FrameExtentsHeuristicPath::*;
if self.heuristic_path != UnsupportedBordered { if self.heuristic_path != UnsupportedBordered {
let frame_extents = self.frame_extents.as_logical(factor); let frame_extents = self.frame_extents.as_logical(factor);
@ -135,15 +152,23 @@ impl FrameExtentsHeuristic {
pub fn inner_size_to_outer(&self, width: u32, height: u32) -> (u32, u32) { pub fn inner_size_to_outer(&self, width: u32, height: u32) -> (u32, u32) {
( (
width.saturating_add( width.saturating_add(
self.frame_extents.left.saturating_add(self.frame_extents.right) as u32 self.frame_extents
.left
.saturating_add(self.frame_extents.right) as u32,
), ),
height.saturating_add( height.saturating_add(
self.frame_extents.top.saturating_add(self.frame_extents.bottom) as u32 self.frame_extents
.top
.saturating_add(self.frame_extents.bottom) as u32,
), ),
) )
} }
pub fn inner_size_to_outer_logical(&self, mut logical: LogicalSize, factor: f64) -> LogicalSize { pub fn inner_size_to_outer_logical(
&self,
mut logical: LogicalSize,
factor: f64,
) -> LogicalSize {
let frame_extents = self.frame_extents.as_logical(factor); let frame_extents = self.frame_extents.as_logical(factor);
logical.width += frame_extents.left + frame_extents.right; logical.width += frame_extents.left + frame_extents.right;
logical.height += frame_extents.top + frame_extents.bottom; logical.height += frame_extents.top + frame_extents.bottom;
@ -153,7 +178,11 @@ impl FrameExtentsHeuristic {
impl XConnection { impl XConnection {
// This is adequate for inner_position // This is adequate for inner_position
pub fn translate_coords(&self, window: ffi::Window, root: ffi::Window) -> Result<TranslatedCoords, XError> { pub fn translate_coords(
&self,
window: ffi::Window,
root: ffi::Window,
) -> Result<TranslatedCoords, XError> {
let mut translated_coords: TranslatedCoords = unsafe { mem::uninitialized() }; let mut translated_coords: TranslatedCoords = unsafe { mem::uninitialized() };
unsafe { unsafe {
(self.xlib.XTranslateCoordinates)( (self.xlib.XTranslateCoordinates)(
@ -201,11 +230,9 @@ impl XConnection {
// Of the WMs tested, xmonad, i3, dwm, IceWM (1.3.x and earlier), and blackbox don't // Of the WMs tested, xmonad, i3, dwm, IceWM (1.3.x and earlier), and blackbox don't
// support this. As this is part of EWMH (Extended Window Manager Hints), it's likely to // support this. As this is part of EWMH (Extended Window Manager Hints), it's likely to
// be unsupported by many smaller WMs. // be unsupported by many smaller WMs.
let extents: Option<Vec<c_ulong>> = self.get_property( let extents: Option<Vec<c_ulong>> = self
window, .get_property(window, extents_atom, ffi::XA_CARDINAL)
extents_atom, .ok();
ffi::XA_CARDINAL,
).ok();
extents.and_then(|extents| { extents.and_then(|extents| {
if extents.len() >= 4 { if extents.len() >= 4 {
@ -228,11 +255,9 @@ impl XConnection {
return None; return None;
} }
let client_list: Option<Vec<ffi::Window>> = self.get_property( let client_list: Option<Vec<ffi::Window>> = self
root, .get_property(root, client_list_atom, ffi::XA_WINDOW)
client_list_atom, .ok();
ffi::XA_WINDOW,
).ok();
client_list.map(|client_list| client_list.contains(&window)) client_list.map(|client_list| client_list.contains(&window))
} }
@ -264,7 +289,11 @@ impl XConnection {
self.check_errors().map(|_| parent) self.check_errors().map(|_| parent)
} }
fn climb_hierarchy(&self, window: ffi::Window, root: ffi::Window) -> Result<ffi::Window, XError> { fn climb_hierarchy(
&self,
window: ffi::Window,
root: ffi::Window,
) -> Result<ffi::Window, XError> {
let mut outer_window = window; let mut outer_window = window;
loop { loop {
let candidate = self.get_parent_window(outer_window)?; let candidate = self.get_parent_window(outer_window)?;
@ -276,7 +305,11 @@ impl XConnection {
Ok(outer_window) Ok(outer_window)
} }
pub fn get_frame_extents_heuristic(&self, window: ffi::Window, root: ffi::Window) -> FrameExtentsHeuristic { pub fn get_frame_extents_heuristic(
&self,
window: ffi::Window,
root: ffi::Window,
) -> FrameExtentsHeuristic {
use self::FrameExtentsHeuristicPath::*; use self::FrameExtentsHeuristicPath::*;
// Position relative to root window. // Position relative to root window.
@ -284,15 +317,16 @@ impl XConnection {
// isn't nested are outlined in the comments throghout this function, but in addition to // isn't nested are outlined in the comments throghout this function, but in addition to
// that, fullscreen windows often aren't nested. // that, fullscreen windows often aren't nested.
let (inner_y_rel_root, child) = { let (inner_y_rel_root, child) = {
let coords = self.translate_coords(window, root).expect("Failed to translate window coordinates"); let coords = self
( .translate_coords(window, root)
coords.y_rel_root, .expect("Failed to translate window coordinates");
coords.child, (coords.y_rel_root, coords.child)
)
}; };
let (width, height, border) = { let (width, height, border) = {
let inner_geometry = self.get_geometry(window).expect("Failed to get inner window geometry"); let inner_geometry = self
.get_geometry(window)
.expect("Failed to get inner window geometry");
( (
inner_geometry.width, inner_geometry.width,
inner_geometry.height, inner_geometry.height,
@ -343,9 +377,13 @@ impl XConnection {
// If the position value we have is for a nested window used as the client area, we'll // If the position value we have is for a nested window used as the client area, we'll
// just climb up the hierarchy and get the geometry of the outermost window we're // just climb up the hierarchy and get the geometry of the outermost window we're
// nested in. // nested in.
let outer_window = self.climb_hierarchy(window, root).expect("Failed to climb window hierarchy"); let outer_window = self
.climb_hierarchy(window, root)
.expect("Failed to climb window hierarchy");
let (outer_y, outer_width, outer_height) = { let (outer_y, outer_width, outer_height) = {
let outer_geometry = self.get_geometry(outer_window).expect("Failed to get outer window geometry"); let outer_geometry = self
.get_geometry(outer_window)
.expect("Failed to get outer window geometry");
( (
outer_geometry.y_rel_parent, outer_geometry.y_rel_parent,
outer_geometry.width, outer_geometry.width,
@ -364,12 +402,8 @@ impl XConnection {
let top = offset_y; let top = offset_y;
let bottom = diff_y.saturating_sub(offset_y); let bottom = diff_y.saturating_sub(offset_y);
let frame_extents = FrameExtents::new( let frame_extents =
left.into(), FrameExtents::new(left.into(), right.into(), top.into(), bottom.into());
right.into(),
top.into(),
bottom.into(),
);
FrameExtentsHeuristic { FrameExtentsHeuristic {
frame_extents, frame_extents,
heuristic_path: UnsupportedNested, heuristic_path: UnsupportedNested,

View file

@ -1,9 +1,8 @@
use std::slice;
use std::sync::Arc; use std::sync::Arc;
use super::*; use super::*;
pub const MWM_HINTS_DECORATIONS: c_ulong = 2;
#[derive(Debug)] #[derive(Debug)]
pub enum StateOperation { pub enum StateOperation {
Remove = 0, // _NET_WM_STATE_REMOVE Remove = 0, // _NET_WM_STATE_REMOVE
@ -93,13 +92,101 @@ impl WindowType {
} }
} }
pub struct MotifHints {
hints: MwmHints,
}
#[repr(C)]
struct MwmHints {
flags: c_ulong,
functions: c_ulong,
decorations: c_ulong,
input_mode: c_long,
status: c_ulong,
}
#[allow(dead_code)]
mod mwm {
use libc::c_ulong;
// Motif WM hints are obsolete, but still widely supported.
// https://stackoverflow.com/a/1909708
pub const MWM_HINTS_FUNCTIONS: c_ulong = 1 << 0;
pub const MWM_HINTS_DECORATIONS: c_ulong = 1 << 1;
pub const MWM_FUNC_ALL: c_ulong = 1 << 0;
pub const MWM_FUNC_RESIZE: c_ulong = 1 << 1;
pub const MWM_FUNC_MOVE: c_ulong = 1 << 2;
pub const MWM_FUNC_MINIMIZE: c_ulong = 1 << 3;
pub const MWM_FUNC_MAXIMIZE: c_ulong = 1 << 4;
pub const MWM_FUNC_CLOSE: c_ulong = 1 << 5;
}
impl MotifHints {
pub fn new() -> MotifHints {
MotifHints {
hints: MwmHints {
flags: 0,
functions: 0,
decorations: 0,
input_mode: 0,
status: 0,
},
}
}
pub fn set_decorations(&mut self, decorations: bool) {
self.hints.flags |= mwm::MWM_HINTS_DECORATIONS;
self.hints.decorations = decorations as c_ulong;
}
pub fn set_maximizable(&mut self, maximizable: bool) {
if maximizable {
self.add_func(mwm::MWM_FUNC_MAXIMIZE);
} else {
self.remove_func(mwm::MWM_FUNC_MAXIMIZE);
}
}
fn add_func(&mut self, func: c_ulong) {
if self.hints.flags & mwm::MWM_HINTS_FUNCTIONS != 0 {
if self.hints.functions & mwm::MWM_FUNC_ALL != 0 {
self.hints.functions &= !func;
} else {
self.hints.functions |= func;
}
}
}
fn remove_func(&mut self, func: c_ulong) {
if self.hints.flags & mwm::MWM_HINTS_FUNCTIONS == 0 {
self.hints.flags |= mwm::MWM_HINTS_FUNCTIONS;
self.hints.functions = mwm::MWM_FUNC_ALL;
}
if self.hints.functions & mwm::MWM_FUNC_ALL != 0 {
self.hints.functions |= func;
} else {
self.hints.functions &= !func;
}
}
}
impl MwmHints {
fn as_slice(&self) -> &[c_ulong] {
unsafe { slice::from_raw_parts(self as *const _ as *const c_ulong, 5) }
}
}
pub struct NormalHints<'a> { pub struct NormalHints<'a> {
size_hints: XSmartPointer<'a, ffi::XSizeHints>, size_hints: XSmartPointer<'a, ffi::XSizeHints>,
} }
impl<'a> NormalHints<'a> { impl<'a> NormalHints<'a> {
pub fn new(xconn: &'a XConnection) -> Self { pub fn new(xconn: &'a XConnection) -> Self {
NormalHints { size_hints: xconn.alloc_size_hints() } NormalHints {
size_hints: xconn.alloc_size_hints(),
}
} }
pub fn has_flag(&self, flag: c_long) -> bool { pub fn has_flag(&self, flag: c_long) -> bool {
@ -130,7 +217,11 @@ impl<'a> NormalHints<'a> {
} }
pub fn get_max_size(&self) -> Option<(u32, u32)> { pub fn get_max_size(&self) -> Option<(u32, u32)> {
self.getter(ffi::PMaxSize, &self.size_hints.max_width, &self.size_hints.max_height) self.getter(
ffi::PMaxSize,
&self.size_hints.max_width,
&self.size_hints.max_height,
)
} }
pub fn set_max_size(&mut self, max_size: Option<(u32, u32)>) { pub fn set_max_size(&mut self, max_size: Option<(u32, u32)>) {
@ -144,7 +235,11 @@ impl<'a> NormalHints<'a> {
} }
pub fn get_min_size(&self) -> Option<(u32, u32)> { pub fn get_min_size(&self) -> Option<(u32, u32)> {
self.getter(ffi::PMinSize, &self.size_hints.min_width, &self.size_hints.min_height) self.getter(
ffi::PMinSize,
&self.size_hints.min_width,
&self.size_hints.min_height,
)
} }
pub fn set_min_size(&mut self, min_size: Option<(u32, u32)>) { pub fn set_min_size(&mut self, min_size: Option<(u32, u32)>) {
@ -158,7 +253,11 @@ impl<'a> NormalHints<'a> {
} }
pub fn get_resize_increments(&self) -> Option<(u32, u32)> { pub fn get_resize_increments(&self) -> Option<(u32, u32)> {
self.getter(ffi::PResizeInc, &self.size_hints.width_inc, &self.size_hints.height_inc) self.getter(
ffi::PResizeInc,
&self.size_hints.width_inc,
&self.size_hints.height_inc,
)
} }
pub fn set_resize_increments(&mut self, resize_increments: Option<(u32, u32)>) { pub fn set_resize_increments(&mut self, resize_increments: Option<(u32, u32)>) {
@ -172,7 +271,11 @@ impl<'a> NormalHints<'a> {
} }
pub fn get_base_size(&self) -> Option<(u32, u32)> { pub fn get_base_size(&self) -> Option<(u32, u32)> {
self.getter(ffi::PBaseSize, &self.size_hints.base_width, &self.size_hints.base_height) self.getter(
ffi::PBaseSize,
&self.size_hints.base_width,
&self.size_hints.base_height,
)
} }
pub fn set_base_size(&mut self, base_size: Option<(u32, u32)>) { pub fn set_base_size(&mut self, base_size: Option<(u32, u32)>) {
@ -187,7 +290,10 @@ impl<'a> NormalHints<'a> {
} }
impl XConnection { impl XConnection {
pub fn get_wm_hints(&self, window: ffi::Window) -> Result<XSmartPointer<ffi::XWMHints>, XError> { pub fn get_wm_hints(
&self,
window: ffi::Window,
) -> Result<XSmartPointer<'_, ffi::XWMHints>, XError> {
let wm_hints = unsafe { (self.xlib.XGetWMHints)(self.display, window) }; let wm_hints = unsafe { (self.xlib.XGetWMHints)(self.display, window) };
self.check_errors()?; self.check_errors()?;
let wm_hints = if wm_hints.is_null() { let wm_hints = if wm_hints.is_null() {
@ -198,18 +304,18 @@ impl XConnection {
Ok(wm_hints) Ok(wm_hints)
} }
pub fn set_wm_hints(&self, window: ffi::Window, wm_hints: XSmartPointer<ffi::XWMHints>) -> Flusher { pub fn set_wm_hints(
&self,
window: ffi::Window,
wm_hints: XSmartPointer<'_, ffi::XWMHints>,
) -> Flusher<'_> {
unsafe { unsafe {
(self.xlib.XSetWMHints)( (self.xlib.XSetWMHints)(self.display, window, wm_hints.ptr);
self.display,
window,
wm_hints.ptr,
);
} }
Flusher::new(self) Flusher::new(self)
} }
pub fn get_normal_hints(&self, window: ffi::Window) -> Result<NormalHints, XError> { pub fn get_normal_hints(&self, window: ffi::Window) -> Result<NormalHints<'_>, XError> {
let size_hints = self.alloc_size_hints(); let size_hints = self.alloc_size_hints();
let mut supplied_by_user: c_long = unsafe { mem::uninitialized() }; let mut supplied_by_user: c_long = unsafe { mem::uninitialized() };
unsafe { unsafe {
@ -223,14 +329,42 @@ impl XConnection {
self.check_errors().map(|_| NormalHints { size_hints }) self.check_errors().map(|_| NormalHints { size_hints })
} }
pub fn set_normal_hints(&self, window: ffi::Window, normal_hints: NormalHints) -> Flusher { pub fn set_normal_hints(
&self,
window: ffi::Window,
normal_hints: NormalHints<'_>,
) -> Flusher<'_> {
unsafe { unsafe {
(self.xlib.XSetWMNormalHints)( (self.xlib.XSetWMNormalHints)(self.display, window, normal_hints.size_hints.ptr);
self.display,
window,
normal_hints.size_hints.ptr,
);
} }
Flusher::new(self) Flusher::new(self)
} }
pub fn get_motif_hints(&self, window: ffi::Window) -> MotifHints {
let motif_hints = unsafe { self.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") };
let mut hints = MotifHints::new();
if let Ok(props) = self.get_property::<c_ulong>(window, motif_hints, motif_hints) {
hints.hints.flags = props.get(0).cloned().unwrap_or(0);
hints.hints.functions = props.get(1).cloned().unwrap_or(0);
hints.hints.decorations = props.get(2).cloned().unwrap_or(0);
hints.hints.input_mode = props.get(3).cloned().unwrap_or(0) as c_long;
hints.hints.status = props.get(4).cloned().unwrap_or(0);
}
hints
}
pub fn set_motif_hints(&self, window: ffi::Window, hints: &MotifHints) -> Flusher<'_> {
let motif_hints = unsafe { self.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") };
self.change_property(
window,
motif_hints,
motif_hints,
PropMode::Replace,
hints.hints.as_slice(),
)
}
} }

View file

@ -1,5 +1,5 @@
use window::{Icon, Pixel, PIXEL_SIZE};
use super::*; use super::*;
use crate::window::{Icon, Pixel, PIXEL_SIZE};
impl Pixel { impl Pixel {
pub fn to_packed_argb(&self) -> Cardinal { pub fn to_packed_argb(&self) -> Cardinal {

View file

@ -1,7 +1,7 @@
use std::str; use std::str;
use super::*; use super::*;
use event::ModifiersState; use crate::event::ModifiersState;
pub const VIRTUAL_CORE_POINTER: c_int = 2; pub const VIRTUAL_CORE_POINTER: c_int = 2;
pub const VIRTUAL_CORE_KEYBOARD: c_int = 3; pub const VIRTUAL_CORE_KEYBOARD: c_int = 3;
@ -55,7 +55,12 @@ impl<'a> Drop for PointerState<'a> {
} }
impl XConnection { impl XConnection {
pub fn select_xinput_events(&self, window: c_ulong, device_id: c_int, mask: i32) -> Flusher { pub fn select_xinput_events(
&self,
window: c_ulong,
device_id: c_int,
mask: i32,
) -> Flusher<'_> {
let mut event_mask = ffi::XIEventMask { let mut event_mask = ffi::XIEventMask {
deviceid: device_id, deviceid: device_id,
mask: &mask as *const _ as *mut c_uchar, mask: &mask as *const _ as *mut c_uchar,
@ -73,15 +78,8 @@ impl XConnection {
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn select_xkb_events(&self, device_id: c_uint, mask: c_ulong) -> Option<Flusher> { pub fn select_xkb_events(&self, device_id: c_uint, mask: c_ulong) -> Option<Flusher<'_>> {
let status = unsafe { let status = unsafe { (self.xlib.XkbSelectEvents)(self.display, device_id, mask, mask) };
(self.xlib.XkbSelectEvents)(
self.display,
device_id,
mask,
mask,
)
};
if status == ffi::True { if status == ffi::True {
Some(Flusher::new(self)) Some(Flusher::new(self))
} else { } else {
@ -89,9 +87,13 @@ impl XConnection {
} }
} }
pub fn query_pointer(&self, window: ffi::Window, device_id: c_int) -> Result<PointerState, XError> { pub fn query_pointer(
&self,
window: ffi::Window,
device_id: c_int,
) -> Result<PointerState<'_>, XError> {
unsafe { unsafe {
let mut pointer_state: PointerState = mem::uninitialized(); let mut pointer_state: PointerState<'_> = mem::uninitialized();
pointer_state.xconn = self; pointer_state.xconn = self;
pointer_state.relative_to_window = (self.xinput2.XIQueryPointer)( pointer_state.relative_to_window = (self.xinput2.XIQueryPointer)(
self.display, self.display,

View file

@ -12,10 +12,7 @@ impl<'a, T> XSmartPointer<'a, T> {
// Returns None if ptr is null. // Returns None if ptr is null.
pub fn new(xconn: &'a XConnection, ptr: *mut T) -> Option<Self> { pub fn new(xconn: &'a XConnection, ptr: *mut T) -> Option<Self> {
if !ptr.is_null() { if !ptr.is_null() {
Some(XSmartPointer { Some(XSmartPointer { xconn, ptr })
xconn,
ptr,
})
} else { } else {
None None
} }
@ -45,17 +42,17 @@ impl<'a, T> Drop for XSmartPointer<'a, T> {
} }
impl XConnection { impl XConnection {
pub fn alloc_class_hint(&self) -> XSmartPointer<ffi::XClassHint> { pub fn alloc_class_hint(&self) -> XSmartPointer<'_, ffi::XClassHint> {
XSmartPointer::new(self, unsafe { (self.xlib.XAllocClassHint)() }) XSmartPointer::new(self, unsafe { (self.xlib.XAllocClassHint)() })
.expect("`XAllocClassHint` returned null; out of memory") .expect("`XAllocClassHint` returned null; out of memory")
} }
pub fn alloc_size_hints(&self) -> XSmartPointer<ffi::XSizeHints> { pub fn alloc_size_hints(&self) -> XSmartPointer<'_, ffi::XSizeHints> {
XSmartPointer::new(self, unsafe { (self.xlib.XAllocSizeHints)() }) XSmartPointer::new(self, unsafe { (self.xlib.XAllocSizeHints)() })
.expect("`XAllocSizeHints` returned null; out of memory") .expect("`XAllocSizeHints` returned null; out of memory")
} }
pub fn alloc_wm_hints(&self) -> XSmartPointer<ffi::XWMHints> { pub fn alloc_wm_hints(&self) -> XSmartPointer<'_, ffi::XWMHints> {
XSmartPointer::new(self, unsafe { (self.xlib.XAllocWMHints)() }) XSmartPointer::new(self, unsafe { (self.xlib.XAllocWMHints)() })
.expect("`XAllocWMHints` returned null; out of memory") .expect("`XAllocWMHints` returned null; out of memory")
} }

View file

@ -13,22 +13,12 @@ mod randr;
mod window_property; mod window_property;
mod wm; mod wm;
pub use self::atom::*; pub use self::{
pub use self::client_msg::*; atom::*, client_msg::*, format::*, geometry::*, hint::*, icon::*, input::*, memory::*,
pub use self::format::*; randr::*, window_property::*, wm::*,
pub use self::geometry::*; };
pub use self::hint::*;
pub use self::icon::*;
pub use self::input::*;
pub use self::memory::*;
pub use self::randr::*;
pub use self::window_property::*;
pub use self::wm::*;
use std::mem; use std::{mem, ops::BitAnd, os::raw::*, ptr};
use std::ptr;
use std::ops::BitAnd;
use std::os::raw::*;
use super::{ffi, XConnection, XError}; use super::{ffi, XConnection, XError};
@ -48,8 +38,8 @@ pub fn maybe_change<T: PartialEq>(field: &mut Option<T>, value: T) -> bool {
} }
pub fn has_flag<T>(bitset: T, flag: T) -> bool pub fn has_flag<T>(bitset: T, flag: T) -> bool
where T: where
Copy + PartialEq + BitAnd<T, Output = T> T: Copy + PartialEq + BitAnd<T, Output = T>,
{ {
bitset & flag == flag bitset & flag == flag
} }

View file

@ -1,8 +1,7 @@
use std::{env, slice}; use std::{env, slice, str::FromStr};
use std::str::FromStr;
use dpi::validate_hidpi_factor;
use super::*; use super::*;
use crate::{dpi::validate_hidpi_factor, monitor::VideoMode};
pub fn calc_dpi_factor( pub fn calc_dpi_factor(
(width_px, height_px): (u32, u32), (width_px, height_px): (u32, u32),
@ -23,14 +22,12 @@ pub fn calc_dpi_factor(
} }
// See http://xpra.org/trac/ticket/728 for more information. // See http://xpra.org/trac/ticket/728 for more information.
if width_mm == 0 || width_mm == 0 { if width_mm == 0 || height_mm == 0 {
warn!("XRandR reported that the display's 0mm in size, which is certifiably insane"); warn!("XRandR reported that the display's 0mm in size, which is certifiably insane");
return 1.0; return 1.0;
} }
let ppmm = ( let ppmm = ((width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)).sqrt();
(width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)
).sqrt();
// Quantize 1/12 step size // Quantize 1/12 step size
let dpi_factor = ((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0); let dpi_factor = ((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0);
assert!(validate_hidpi_factor(dpi_factor)); assert!(validate_hidpi_factor(dpi_factor));
@ -51,7 +48,7 @@ impl MonitorRepr {
} }
} }
pub unsafe fn dimensions(&self) -> (u32, u32) { pub unsafe fn size(&self) -> (u32, u32) {
match *self { match *self {
MonitorRepr::Monitor(monitor) => ((*monitor).width as u32, (*monitor).height as u32), MonitorRepr::Monitor(monitor) => ((*monitor).width as u32, (*monitor).height as u32),
MonitorRepr::Crtc(crtc) => ((*crtc).width as u32, (*crtc).height as u32), MonitorRepr::Crtc(crtc) => ((*crtc).width as u32, (*crtc).height as u32),
@ -87,7 +84,7 @@ impl XConnection {
return None; return None;
} }
if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() { if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() {
let name : &str = "Xft.dpi:\t"; let name: &str = "Xft.dpi:\t";
for pair in res.split("\n") { for pair in res.split("\n") {
if pair.starts_with(&name) { if pair.starts_with(&name) {
let res = &pair[name.len()..]; let res = &pair[name.len()..];
@ -101,12 +98,9 @@ impl XConnection {
&self, &self,
resources: *mut ffi::XRRScreenResources, resources: *mut ffi::XRRScreenResources,
repr: &MonitorRepr, repr: &MonitorRepr,
) -> Option<(String, f64)> { ) -> Option<(String, f64, Vec<VideoMode>)> {
let output_info = (self.xrandr.XRRGetOutputInfo)( let output_info =
self.display, (self.xrandr.XRRGetOutputInfo)(self.display, resources, repr.get_output());
resources,
repr.get_output(),
);
if output_info.is_null() { if output_info.is_null() {
// When calling `XRRGetOutputInfo` on a virtual monitor (versus a physical display) // When calling `XRRGetOutputInfo` on a virtual monitor (versus a physical display)
// it's possible for it to return null. // it's possible for it to return null.
@ -114,6 +108,33 @@ impl XConnection {
let _ = self.check_errors(); // discard `BadRROutput` error let _ = self.check_errors(); // discard `BadRROutput` error
return None; return None;
} }
let screen = (self.xlib.XDefaultScreen)(self.display);
let bit_depth = (self.xlib.XDefaultDepth)(self.display, screen);
let output_modes =
slice::from_raw_parts((*output_info).modes, (*output_info).nmode as usize);
let resource_modes = slice::from_raw_parts((*resources).modes, (*resources).nmode as usize);
let modes = resource_modes
.iter()
// XRROutputInfo contains an array of mode ids that correspond to
// modes in the array in XRRScreenResources
.filter(|x| output_modes.iter().any(|id| x.id == *id))
.map(|x| {
let refresh_rate = if x.dotClock > 0 && x.hTotal > 0 && x.vTotal > 0 {
x.dotClock as u64 * 1000 / (x.hTotal as u64 * x.vTotal as u64)
} else {
0
};
VideoMode {
size: (x.width, x.height),
refresh_rate: (refresh_rate as f32 / 1000.0).round() as u16,
bit_depth: bit_depth as u16,
}
});
let name_slice = slice::from_raw_parts( let name_slice = slice::from_raw_parts(
(*output_info).name as *mut u8, (*output_info).name as *mut u8,
(*output_info).nameLen as usize, (*output_info).nameLen as usize,
@ -123,12 +144,15 @@ impl XConnection {
dpi / 96. dpi / 96.
} else { } else {
calc_dpi_factor( calc_dpi_factor(
repr.dimensions(), repr.size(),
((*output_info).mm_width as u64, (*output_info).mm_height as u64), (
(*output_info).mm_width as u64,
(*output_info).mm_height as u64,
),
) )
}; };
(self.xrandr.XRRFreeOutputInfo)(output_info); (self.xrandr.XRRFreeOutputInfo)(output_info);
Some((name, hidpi_factor)) Some((name, hidpi_factor, modes.collect()))
} }
} }

View file

@ -84,10 +84,8 @@ impl XConnection {
if !buf.is_null() { if !buf.is_null() {
offset += PROPERTY_BUFFER_SIZE; offset += PROPERTY_BUFFER_SIZE;
let new_data = std::slice::from_raw_parts( let new_data =
buf as *mut T, std::slice::from_raw_parts(buf as *mut T, quantity_returned as usize);
quantity_returned as usize,
);
/*println!( /*println!(
"XGetWindowProperty prop:{:?} fmt:{:02} len:{:02} off:{:02} out:{:02}, buf:{:?}", "XGetWindowProperty prop:{:?} fmt:{:02} len:{:02} off:{:02} out:{:02}, buf:{:?}",
property, property,

View file

@ -28,11 +28,8 @@ impl XConnection {
fn get_supported_hints(&self, root: ffi::Window) -> Vec<ffi::Atom> { fn get_supported_hints(&self, root: ffi::Window) -> Vec<ffi::Atom> {
let supported_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTED\0") }; let supported_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTED\0") };
self.get_property( self.get_property(root, supported_atom, ffi::XA_ATOM)
root, .unwrap_or_else(|_| Vec::with_capacity(0))
supported_atom,
ffi::XA_ATOM,
).unwrap_or_else(|_| Vec::with_capacity(0))
} }
fn get_wm_name(&self, root: ffi::Window) -> Option<String> { fn get_wm_name(&self, root: ffi::Window) -> Option<String> {
@ -61,15 +58,9 @@ impl XConnection {
// Querying this property on the root window will give us the ID of a child window created by // Querying this property on the root window will give us the ID of a child window created by
// the WM. // the WM.
let root_window_wm_check = { let root_window_wm_check = {
let result = self.get_property( let result = self.get_property(root, check_atom, ffi::XA_WINDOW);
root,
check_atom,
ffi::XA_WINDOW,
);
let wm_check = result let wm_check = result.ok().and_then(|wm_check| wm_check.get(0).cloned());
.ok()
.and_then(|wm_check| wm_check.get(0).cloned());
if let Some(wm_check) = wm_check { if let Some(wm_check) = wm_check {
wm_check wm_check
@ -81,15 +72,9 @@ impl XConnection {
// Querying the same property on the child window we were given, we should get this child // Querying the same property on the child window we were given, we should get this child
// window's ID again. // window's ID again.
let child_window_wm_check = { let child_window_wm_check = {
let result = self.get_property( let result = self.get_property(root_window_wm_check, check_atom, ffi::XA_WINDOW);
root_window_wm_check,
check_atom,
ffi::XA_WINDOW,
);
let wm_check = result let wm_check = result.ok().and_then(|wm_check| wm_check.get(0).cloned());
.ok()
.and_then(|wm_check| wm_check.get(0).cloned());
if let Some(wm_check) = wm_check { if let Some(wm_check) = wm_check {
wm_check wm_check
@ -107,11 +92,7 @@ impl XConnection {
let wm_name = { let wm_name = {
let utf8_string_atom = unsafe { self.get_atom_unchecked(b"UTF8_STRING\0") }; let utf8_string_atom = unsafe { self.get_atom_unchecked(b"UTF8_STRING\0") };
let result = self.get_property( let result = self.get_property(root_window_wm_check, wm_name_atom, utf8_string_atom);
root_window_wm_check,
wm_name_atom,
utf8_string_atom,
);
// IceWM requires this. IceWM was also the only WM tested that returns a null-terminated // IceWM requires this. IceWM was also the only WM tested that returns a null-terminated
// string. For more fun trivia, IceWM is also unique in including version and uname // string. For more fun trivia, IceWM is also unique in including version and uname
@ -126,15 +107,12 @@ impl XConnection {
}; };
if no_utf8 { if no_utf8 {
self.get_property( self.get_property(root_window_wm_check, wm_name_atom, ffi::XA_STRING)
root_window_wm_check,
wm_name_atom,
ffi::XA_STRING,
)
} else { } else {
result result
} }
}.ok(); }
.ok();
wm_name.and_then(|wm_name| String::from_utf8(wm_name).ok()) wm_name.and_then(|wm_name| String::from_utf8(wm_name).ok())
} }

View file

@ -1,23 +1,20 @@
use std::{cmp, env, mem}; use std::{cmp, collections::HashSet, env, ffi::CString, mem, os::raw::*, path::Path, sync::Arc};
use std::collections::HashSet;
use std::ffi::CString;
use std::os::raw::*;
use std::path::Path;
use std::sync::Arc;
use libc; use libc;
use parking_lot::Mutex; use parking_lot::Mutex;
use error::{ExternalError, NotSupportedError, OsError as RootOsError}; use crate::{
use window::{Icon, CursorIcon, WindowAttributes}; dpi::{LogicalPosition, LogicalSize},
use dpi::{LogicalPosition, LogicalSize}; error::{ExternalError, NotSupportedError, OsError as RootOsError},
use platform_impl::MonitorHandle as PlatformMonitorHandle; monitor::MonitorHandle as RootMonitorHandle,
use platform_impl::{OsError, PlatformSpecificWindowBuilderAttributes}; platform_impl::{
use platform_impl::x11::ime::ImeContextCreationError; x11::{ime::ImeContextCreationError, MonitorHandle as X11MonitorHandle},
use platform_impl::x11::MonitorHandle as X11MonitorHandle; MonitorHandle as PlatformMonitorHandle, OsError, PlatformSpecificWindowBuilderAttributes,
use monitor::MonitorHandle as RootMonitorHandle; },
window::{CursorIcon, Icon, WindowAttributes},
};
use super::{ffi, util, ImeSender, XConnection, XError, WindowId, EventLoopWindowTarget}; use super::{ffi, util, EventLoopWindowTarget, ImeSender, WindowId, XConnection, XError};
unsafe extern "C" fn visibility_predicate( unsafe extern "C" fn visibility_predicate(
_display: *mut ffi::Display, _display: *mut ffi::Display,
@ -60,9 +57,9 @@ unsafe impl Sync for UnownedWindow {}
pub struct UnownedWindow { pub struct UnownedWindow {
pub xconn: Arc<XConnection>, // never changes pub xconn: Arc<XConnection>, // never changes
xwindow: ffi::Window, // never changes xwindow: ffi::Window, // never changes
root: ffi::Window, // never changes root: ffi::Window, // never changes
screen_id: i32, // never changes screen_id: i32, // never changes
cursor: Mutex<CursorIcon>, cursor: Mutex<CursorIcon>,
cursor_grabbed: Mutex<bool>, cursor_grabbed: Mutex<bool>,
cursor_visible: Mutex<bool>, cursor_visible: Mutex<bool>,
@ -89,7 +86,8 @@ impl UnownedWindow {
} }
} }
dpi_factor.unwrap_or_else(|| { dpi_factor.unwrap_or_else(|| {
xconn.query_pointer(root, util::VIRTUAL_CORE_POINTER) xconn
.query_pointer(root, util::VIRTUAL_CORE_POINTER)
.ok() .ok()
.and_then(|pointer_state| { .and_then(|pointer_state| {
let (x, y) = (pointer_state.root_x as i64, pointer_state.root_y as i64); let (x, y) = (pointer_state.root_x as i64, pointer_state.root_y as i64);
@ -110,17 +108,18 @@ impl UnownedWindow {
info!("Guessed window DPI factor: {}", dpi_factor); info!("Guessed window DPI factor: {}", dpi_factor);
let max_inner_size: Option<(u32, u32)> = window_attrs.max_inner_size.map(|size| { let max_inner_size: Option<(u32, u32)> = window_attrs
size.to_physical(dpi_factor).into() .max_inner_size
}); .map(|size| size.to_physical(dpi_factor).into());
let min_inner_size: Option<(u32, u32)> = window_attrs.min_inner_size.map(|size| { let min_inner_size: Option<(u32, u32)> = window_attrs
size.to_physical(dpi_factor).into() .min_inner_size
}); .map(|size| size.to_physical(dpi_factor).into());
let dimensions = { let dimensions = {
// x11 only applies constraints when the window is actively resized // x11 only applies constraints when the window is actively resized
// by the user, so we have to manually apply the initial constraints // by the user, so we have to manually apply the initial constraints
let mut dimensions: (u32, u32) = window_attrs.inner_size let mut dimensions: (u32, u32) = window_attrs
.inner_size
.or_else(|| Some((800, 600).into())) .or_else(|| Some((800, 600).into()))
.map(|size| size.to_physical(dpi_factor)) .map(|size| size.to_physical(dpi_factor))
.map(Into::into) .map(Into::into)
@ -133,7 +132,10 @@ impl UnownedWindow {
dimensions.0 = cmp::max(dimensions.0, min.0); dimensions.0 = cmp::max(dimensions.0, min.0);
dimensions.1 = cmp::max(dimensions.1, min.1); dimensions.1 = cmp::max(dimensions.1, min.1);
} }
debug!("Calculated physical dimensions: {}x{}", dimensions.0, dimensions.1); debug!(
"Calculated physical dimensions: {}x{}",
dimensions.0, dimensions.1
);
dimensions dimensions
}; };
@ -150,7 +152,9 @@ impl UnownedWindow {
let visual = vi.visual; let visual = vi.visual;
(xconn.xlib.XCreateColormap)(xconn.display, root, visual, ffi::AllocNone) (xconn.xlib.XCreateColormap)(xconn.display, root, visual, ffi::AllocNone)
} }
} else { 0 }; } else {
0
};
swa.event_mask = ffi::ExposureMask swa.event_mask = ffi::ExposureMask
| ffi::StructureNotifyMask | ffi::StructureNotifyMask
| ffi::VisibilityChangeMask | ffi::VisibilityChangeMask
@ -220,7 +224,9 @@ impl UnownedWindow {
// title to determine placement/etc., so doing this after mapping would cause the WM to // title to determine placement/etc., so doing this after mapping would cause the WM to
// act on the wrong title state. // act on the wrong title state.
window.set_title_inner(&window_attrs.title).queue(); window.set_title_inner(&window_attrs.title).queue();
window.set_decorations_inner(window_attrs.decorations).queue(); window
.set_decorations_inner(window_attrs.decorations)
.queue();
{ {
// Enable drag and drop (TODO: extend API to make this toggleable) // Enable drag and drop (TODO: extend API to make this toggleable)
@ -234,15 +240,16 @@ impl UnownedWindow {
util::PropMode::Replace, util::PropMode::Replace,
version, version,
) )
}.queue(); }
.queue();
// WM_CLASS must be set *before* mapping the window, as per ICCCM! // WM_CLASS must be set *before* mapping the window, as per ICCCM!
{ {
let (class, instance) = if let Some((instance, class)) = pl_attribs.class { let (class, instance) = if let Some((instance, class)) = pl_attribs.class {
let instance = CString::new(instance.as_str()) let instance = CString::new(instance.as_str())
.expect("`WM_CLASS` instance contained null byte"); .expect("`WM_CLASS` instance contained null byte");
let class = CString::new(class.as_str()) let class =
.expect("`WM_CLASS` class contained null byte"); CString::new(class.as_str()).expect("`WM_CLASS` class contained null byte");
(instance, class) (instance, class)
} else { } else {
let class = env::args() let class = env::args()
@ -269,12 +276,8 @@ impl UnownedWindow {
(*class_hint).res_class = instance.as_ptr() as *mut c_char; (*class_hint).res_class = instance.as_ptr() as *mut c_char;
unsafe { unsafe {
(xconn.xlib.XSetClassHint)( (xconn.xlib.XSetClassHint)(xconn.display, window.xwindow, class_hint.ptr);
xconn.display, } //.queue();
window.xwindow,
class_hint.ptr,
);
}//.queue();
} }
window.set_pid().map(|flusher| flusher.queue()); window.set_pid().map(|flusher| flusher.queue());
@ -289,9 +292,11 @@ impl UnownedWindow {
// set size hints // set size hints
{ {
let mut min_inner_size = window_attrs.min_inner_size let mut min_inner_size = window_attrs
.min_inner_size
.map(|size| size.to_physical(dpi_factor)); .map(|size| size.to_physical(dpi_factor));
let mut max_inner_size = window_attrs.max_inner_size let mut max_inner_size = window_attrs
.max_inner_size
.map(|size| size.to_physical(dpi_factor)); .map(|size| size.to_physical(dpi_factor));
if !window_attrs.resizable { if !window_attrs.resizable {
if util::wm_name_is_one_of(&["Xfwm4"]) { if util::wm_name_is_one_of(&["Xfwm4"]) {
@ -325,16 +330,17 @@ impl UnownedWindow {
(xconn.xlib.XSetWMProtocols)( (xconn.xlib.XSetWMProtocols)(
xconn.display, xconn.display,
window.xwindow, window.xwindow,
&event_loop.wm_delete_window as *const ffi::Atom as *mut ffi::Atom, &[event_loop.wm_delete_window, event_loop.net_wm_ping] as *const ffi::Atom
1, as *mut ffi::Atom,
2,
); );
}//.queue(); } //.queue();
// Set visibility (map window) // Set visibility (map window)
if window_attrs.visible { if window_attrs.visible {
unsafe { unsafe {
(xconn.xlib.XMapRaised)(xconn.display, window.xwindow); (xconn.xlib.XMapRaised)(xconn.display, window.xwindow);
}//.queue(); } //.queue();
} }
// Attempt to make keyboard input repeat detectable // Attempt to make keyboard input repeat detectable
@ -346,7 +352,9 @@ impl UnownedWindow {
&mut supported_ptr, &mut supported_ptr,
); );
if supported_ptr == ffi::False { if supported_ptr == ffi::False {
return Err(os_error!(OsError::XMisc("`XkbSetDetectableAutoRepeat` failed"))); return Err(os_error!(OsError::XMisc(
"`XkbSetDetectableAutoRepeat` failed"
)));
} }
} }
@ -366,16 +374,18 @@ impl UnownedWindow {
| ffi::XI_TouchEndMask; | ffi::XI_TouchEndMask;
mask mask
}; };
xconn.select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask).queue(); xconn
.select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask)
.queue();
{ {
let result = event_loop.ime let result = event_loop.ime.borrow_mut().create_context(window.xwindow);
.borrow_mut()
.create_context(window.xwindow);
if let Err(err) = result { if let Err(err) = result {
let e = match err { let e = match err {
ImeContextCreationError::XError(err) => OsError::XError(err), ImeContextCreationError::XError(err) => OsError::XError(err),
ImeContextCreationError::Null => OsError::XMisc("IME Context creation failed"), ImeContextCreationError::Null => {
OsError::XMisc("IME Context creation failed")
}
}; };
return Err(os_error!(e)); return Err(os_error!(e));
} }
@ -386,10 +396,14 @@ impl UnownedWindow {
window.set_maximized_inner(window_attrs.maximized).queue(); window.set_maximized_inner(window_attrs.maximized).queue();
} }
if window_attrs.fullscreen.is_some() { if window_attrs.fullscreen.is_some() {
window.set_fullscreen_inner(window_attrs.fullscreen.clone()).queue(); window
.set_fullscreen_inner(window_attrs.fullscreen.clone())
.queue();
} }
if window_attrs.always_on_top { if window_attrs.always_on_top {
window.set_always_on_top_inner(window_attrs.always_on_top).queue(); window
.set_always_on_top_inner(window_attrs.always_on_top)
.queue();
} }
if window_attrs.visible { if window_attrs.visible {
@ -397,7 +411,8 @@ impl UnownedWindow {
// XSetInputFocus generates an error if the window is not visible, so we wait // XSetInputFocus generates an error if the window is not visible, so we wait
// until we receive VisibilityNotify. // until we receive VisibilityNotify.
let mut event = mem::uninitialized(); let mut event = mem::uninitialized();
(xconn.xlib.XIfEvent)( // This will flush the request buffer IF it blocks. (xconn.xlib.XIfEvent)(
// This will flush the request buffer IF it blocks.
xconn.display, xconn.display,
&mut event as *mut ffi::XEvent, &mut event as *mut ffi::XEvent,
Some(visibility_predicate), Some(visibility_predicate),
@ -414,7 +429,8 @@ impl UnownedWindow {
} }
// We never want to give the user a broken window, since by then, it's too late to handle. // We never want to give the user a broken window, since by then, it's too late to handle.
xconn.sync_with_server() xconn
.sync_with_server()
.map(|_| window) .map(|_| window)
.map_err(|x_err| os_error!(OsError::XError(x_err))) .map_err(|x_err| os_error!(OsError::XError(x_err)))
} }
@ -429,7 +445,7 @@ impl UnownedWindow {
LogicalSize::from_physical((width, height), dpi) LogicalSize::from_physical((width, height), dpi)
} }
fn set_pid(&self) -> Option<util::Flusher> { fn set_pid(&self) -> Option<util::Flusher<'_>> {
let pid_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_PID\0") }; let pid_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_PID\0") };
let client_machine_atom = unsafe { self.xconn.get_atom_unchecked(b"WM_CLIENT_MACHINE\0") }; let client_machine_atom = unsafe { self.xconn.get_atom_unchecked(b"WM_CLIENT_MACHINE\0") };
unsafe { unsafe {
@ -439,18 +455,22 @@ impl UnownedWindow {
const MAXHOSTNAMELEN: usize = 256; const MAXHOSTNAMELEN: usize = 256;
let mut hostname: [c_char; MAXHOSTNAMELEN] = mem::uninitialized(); let mut hostname: [c_char; MAXHOSTNAMELEN] = mem::uninitialized();
let status = libc::gethostname(hostname.as_mut_ptr(), hostname.len()); let status = libc::gethostname(hostname.as_mut_ptr(), hostname.len());
if status != 0 { return None; } if status != 0 {
return None;
}
hostname[MAXHOSTNAMELEN - 1] = '\0' as c_char; // a little extra safety hostname[MAXHOSTNAMELEN - 1] = '\0' as c_char; // a little extra safety
let hostname_length = libc::strlen(hostname.as_ptr()); let hostname_length = libc::strlen(hostname.as_ptr());
(hostname, hostname_length as usize) (hostname, hostname_length as usize)
}; };
self.xconn.change_property( self.xconn
self.xwindow, .change_property(
pid_atom, self.xwindow,
ffi::XA_CARDINAL, pid_atom,
util::PropMode::Replace, ffi::XA_CARDINAL,
&[libc::getpid() as util::Cardinal], util::PropMode::Replace,
).queue(); &[libc::getpid() as util::Cardinal],
)
.queue();
let flusher = self.xconn.change_property( let flusher = self.xconn.change_property(
self.xwindow, self.xwindow,
client_machine_atom, client_machine_atom,
@ -462,7 +482,7 @@ impl UnownedWindow {
} }
} }
fn set_window_type(&self, window_type: util::WindowType) -> util::Flusher { fn set_window_type(&self, window_type: util::WindowType) -> util::Flusher<'_> {
let hint_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_WINDOW_TYPE\0") }; let hint_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_WINDOW_TYPE\0") };
let window_type_atom = window_type.as_atom(&self.xconn); let window_type_atom = window_type.as_atom(&self.xconn);
self.xconn.change_property( self.xconn.change_property(
@ -474,7 +494,7 @@ impl UnownedWindow {
) )
} }
fn set_gtk_theme_variant(&self, variant: String) -> util::Flusher { fn set_gtk_theme_variant(&self, variant: String) -> util::Flusher<'_> {
let hint_atom = unsafe { self.xconn.get_atom_unchecked(b"_GTK_THEME_VARIANT\0") }; let hint_atom = unsafe { self.xconn.get_atom_unchecked(b"_GTK_THEME_VARIANT\0") };
let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") }; let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") };
let variant = CString::new(variant).expect("`_GTK_THEME_VARIANT` contained null byte"); let variant = CString::new(variant).expect("`_GTK_THEME_VARIANT` contained null byte");
@ -489,20 +509,26 @@ impl UnownedWindow {
#[inline] #[inline]
pub fn set_urgent(&self, is_urgent: bool) { pub fn set_urgent(&self, is_urgent: bool) {
let mut wm_hints = self.xconn.get_wm_hints(self.xwindow).expect("`XGetWMHints` failed"); let mut wm_hints = self
.xconn
.get_wm_hints(self.xwindow)
.expect("`XGetWMHints` failed");
if is_urgent { if is_urgent {
(*wm_hints).flags |= ffi::XUrgencyHint; (*wm_hints).flags |= ffi::XUrgencyHint;
} else { } else {
(*wm_hints).flags &= !ffi::XUrgencyHint; (*wm_hints).flags &= !ffi::XUrgencyHint;
} }
self.xconn.set_wm_hints(self.xwindow, wm_hints).flush().expect("Failed to set urgency hint"); self.xconn
.set_wm_hints(self.xwindow, wm_hints)
.flush()
.expect("Failed to set urgency hint");
} }
fn set_netwm( fn set_netwm(
&self, &self,
operation: util::StateOperation, operation: util::StateOperation,
properties: (c_long, c_long, c_long, c_long), properties: (c_long, c_long, c_long, c_long),
) -> util::Flusher { ) -> util::Flusher<'_> {
let state_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE\0") }; let state_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE\0") };
self.xconn.send_client_msg( self.xconn.send_client_msg(
self.xwindow, self.xwindow,
@ -519,12 +545,13 @@ impl UnownedWindow {
) )
} }
fn set_fullscreen_hint(&self, fullscreen: bool) -> util::Flusher { fn set_fullscreen_hint(&self, fullscreen: bool) -> util::Flusher<'_> {
let fullscreen_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_FULLSCREEN\0") }; let fullscreen_atom =
unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_FULLSCREEN\0") };
self.set_netwm(fullscreen.into(), (fullscreen_atom as c_long, 0, 0, 0)) self.set_netwm(fullscreen.into(), (fullscreen_atom as c_long, 0, 0, 0))
} }
fn set_fullscreen_inner(&self, monitor: Option<RootMonitorHandle>) -> util::Flusher { fn set_fullscreen_inner(&self, monitor: Option<RootMonitorHandle>) -> util::Flusher<'_> {
match monitor { match monitor {
None => { None => {
let flusher = self.set_fullscreen_hint(false); let flusher = self.set_fullscreen_hint(false);
@ -532,12 +559,15 @@ impl UnownedWindow {
self.set_position_inner(position.0, position.1).queue(); self.set_position_inner(position.0, position.1).queue();
} }
flusher flusher
}, }
Some(RootMonitorHandle { inner: PlatformMonitorHandle::X(monitor) }) => { Some(RootMonitorHandle {
inner: PlatformMonitorHandle::X(monitor),
}) => {
let window_position = self.outer_position_physical(); let window_position = self.outer_position_physical();
self.shared_state.lock().restore_position = Some(window_position); self.shared_state.lock().restore_position = Some(window_position);
let monitor_origin: (i32, i32) = monitor.position().into(); let monitor_origin: (i32, i32) = monitor.position().into();
self.set_position_inner(monitor_origin.0, monitor_origin.1).queue(); self.set_position_inner(monitor_origin.0, monitor_origin.1)
.queue();
self.set_fullscreen_hint(true) self.set_fullscreen_hint(true)
} }
_ => unreachable!(), _ => unreachable!(),
@ -567,17 +597,15 @@ impl UnownedWindow {
#[inline] #[inline]
pub fn current_monitor(&self) -> X11MonitorHandle { pub fn current_monitor(&self) -> X11MonitorHandle {
let monitor = self.shared_state let monitor = self.shared_state.lock().last_monitor.as_ref().cloned();
.lock() monitor.unwrap_or_else(|| {
.last_monitor let monitor = self
.as_ref() .xconn
.cloned(); .get_monitor_for_window(Some(self.get_rect()))
monitor .to_owned();
.unwrap_or_else(|| { self.shared_state.lock().last_monitor = Some(monitor.clone());
let monitor = self.xconn.get_monitor_for_window(Some(self.get_rect())).to_owned(); monitor
self.shared_state.lock().last_monitor = Some(monitor.clone()); })
monitor
})
} }
pub fn available_monitors(&self) -> Vec<X11MonitorHandle> { pub fn available_monitors(&self) -> Vec<X11MonitorHandle> {
@ -588,10 +616,19 @@ impl UnownedWindow {
self.xconn.primary_monitor() self.xconn.primary_monitor()
} }
fn set_maximized_inner(&self, maximized: bool) -> util::Flusher { fn set_maximized_inner(&self, maximized: bool) -> util::Flusher<'_> {
let horz_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_HORZ\0") }; let horz_atom = unsafe {
let vert_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_VERT\0") }; self.xconn
self.set_netwm(maximized.into(), (horz_atom as c_long, vert_atom as c_long, 0, 0)) .get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_HORZ\0")
};
let vert_atom = unsafe {
self.xconn
.get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_VERT\0")
};
self.set_netwm(
maximized.into(),
(horz_atom as c_long, vert_atom as c_long, 0, 0),
)
} }
#[inline] #[inline]
@ -602,7 +639,7 @@ impl UnownedWindow {
self.invalidate_cached_frame_extents(); self.invalidate_cached_frame_extents();
} }
fn set_title_inner(&self, title: &str) -> util::Flusher { fn set_title_inner(&self, title: &str) -> util::Flusher<'_> {
let wm_name_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_NAME\0") }; let wm_name_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_NAME\0") };
let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") }; let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") };
let title = CString::new(title).expect("Window title contained null byte"); let title = CString::new(title).expect("Window title contained null byte");
@ -629,21 +666,12 @@ impl UnownedWindow {
.expect("Failed to set window title"); .expect("Failed to set window title");
} }
fn set_decorations_inner(&self, decorations: bool) -> util::Flusher { fn set_decorations_inner(&self, decorations: bool) -> util::Flusher<'_> {
let wm_hints = unsafe { self.xconn.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") }; let mut hints = self.xconn.get_motif_hints(self.xwindow);
self.xconn.change_property(
self.xwindow, hints.set_decorations(decorations);
wm_hints,
wm_hints, self.xconn.set_motif_hints(self.xwindow, &hints)
util::PropMode::Replace,
&[
util::MWM_HINTS_DECORATIONS, // flags
0, // functions
decorations as c_ulong, // decorations
0, // input mode
0, // status
],
)
} }
#[inline] #[inline]
@ -654,7 +682,15 @@ impl UnownedWindow {
self.invalidate_cached_frame_extents(); self.invalidate_cached_frame_extents();
} }
fn set_always_on_top_inner(&self, always_on_top: bool) -> util::Flusher { fn set_maximizable_inner(&self, maximizable: bool) -> util::Flusher<'_> {
let mut hints = self.xconn.get_motif_hints(self.xwindow);
hints.set_maximizable(maximizable);
self.xconn.set_motif_hints(self.xwindow, &hints)
}
fn set_always_on_top_inner(&self, always_on_top: bool) -> util::Flusher<'_> {
let above_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_ABOVE\0") }; let above_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_ABOVE\0") };
self.set_netwm(always_on_top.into(), (above_atom as c_long, 0, 0, 0)) self.set_netwm(always_on_top.into(), (above_atom as c_long, 0, 0, 0))
} }
@ -666,7 +702,7 @@ impl UnownedWindow {
.expect("Failed to set always-on-top state"); .expect("Failed to set always-on-top state");
} }
fn set_icon_inner(&self, icon: Icon) -> util::Flusher { fn set_icon_inner(&self, icon: Icon) -> util::Flusher<'_> {
let icon_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_ICON\0") }; let icon_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_ICON\0") };
let data = icon.to_cardinals(); let data = icon.to_cardinals();
self.xconn.change_property( self.xconn.change_property(
@ -678,7 +714,7 @@ impl UnownedWindow {
) )
} }
fn unset_icon_inner(&self) -> util::Flusher { fn unset_icon_inner(&self) -> util::Flusher<'_> {
let icon_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_ICON\0") }; let icon_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_ICON\0") };
let empty_data: [util::Cardinal; 0] = []; let empty_data: [util::Cardinal; 0] = [];
self.xconn.change_property( self.xconn.change_property(
@ -695,7 +731,9 @@ impl UnownedWindow {
match icon { match icon {
Some(icon) => self.set_icon_inner(icon), Some(icon) => self.set_icon_inner(icon),
None => self.unset_icon_inner(), None => self.unset_icon_inner(),
}.flush().expect("Failed to set icons"); }
.flush()
.expect("Failed to set icons");
} }
#[inline] #[inline]
@ -703,19 +741,23 @@ impl UnownedWindow {
match visible { match visible {
true => unsafe { true => unsafe {
(self.xconn.xlib.XMapRaised)(self.xconn.display, self.xwindow); (self.xconn.xlib.XMapRaised)(self.xconn.display, self.xwindow);
self.xconn.flush_requests() self.xconn
.flush_requests()
.expect("Failed to call XMapRaised"); .expect("Failed to call XMapRaised");
}, },
false => unsafe { false => unsafe {
(self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow); (self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow);
self.xconn.flush_requests() self.xconn
.flush_requests()
.expect("Failed to call XUnmapWindow"); .expect("Failed to call XUnmapWindow");
} },
} }
} }
fn update_cached_frame_extents(&self) { fn update_cached_frame_extents(&self) {
let extents = self.xconn.get_frame_extents_heuristic(self.xwindow, self.root); let extents = self
.xconn
.get_frame_extents_heuristic(self.xwindow, self.root);
(*self.shared_state.lock()).frame_extents = Some(extents); (*self.shared_state.lock()).frame_extents = Some(extents);
} }
@ -749,7 +791,8 @@ impl UnownedWindow {
pub(crate) fn inner_position_physical(&self) -> (i32, i32) { pub(crate) fn inner_position_physical(&self) -> (i32, i32) {
// This should be okay to unwrap since the only error XTranslateCoordinates can return // This should be okay to unwrap since the only error XTranslateCoordinates can return
// is BadWindow, and if the window handle is bad we have bigger problems. // is BadWindow, and if the window handle is bad we have bigger problems.
self.xconn.translate_coords(self.xwindow, self.root) self.xconn
.translate_coords(self.xwindow, self.root)
.map(|coords| (coords.x_rel_root, coords.y_rel_root)) .map(|coords| (coords.x_rel_root, coords.y_rel_root))
.unwrap() .unwrap()
} }
@ -759,7 +802,7 @@ impl UnownedWindow {
Ok(self.logicalize_coords(self.inner_position_physical())) Ok(self.logicalize_coords(self.inner_position_physical()))
} }
pub(crate) fn set_position_inner(&self, mut x: i32, mut y: i32) -> util::Flusher { pub(crate) fn set_position_inner(&self, mut x: i32, mut y: i32) -> util::Flusher<'_> {
// There are a few WMs that set client area position rather than window position, so // There are a few WMs that set client area position rather than window position, so
// we'll translate for consistency. // we'll translate for consistency.
if util::wm_name_is_one_of(&["Enlightenment", "FVWM"]) { if util::wm_name_is_one_of(&["Enlightenment", "FVWM"]) {
@ -773,12 +816,7 @@ impl UnownedWindow {
} }
} }
unsafe { unsafe {
(self.xconn.xlib.XMoveWindow)( (self.xconn.xlib.XMoveWindow)(self.xconn.display, self.xwindow, x as c_int, y as c_int);
self.xconn.display,
self.xwindow,
x as c_int,
y as c_int,
);
} }
util::Flusher::new(&self.xconn) util::Flusher::new(&self.xconn)
} }
@ -798,7 +836,8 @@ impl UnownedWindow {
pub(crate) fn inner_size_physical(&self) -> (u32, u32) { pub(crate) fn inner_size_physical(&self) -> (u32, u32) {
// This should be okay to unwrap since the only error XGetGeometry can return // This should be okay to unwrap since the only error XGetGeometry can return
// is BadWindow, and if the window handle is bad we have bigger problems. // is BadWindow, and if the window handle is bad we have bigger problems.
self.xconn.get_geometry(self.xwindow) self.xconn
.get_geometry(self.xwindow)
.map(|geo| (geo.width, geo.height)) .map(|geo| (geo.width, geo.height))
.unwrap() .unwrap()
} }
@ -840,7 +879,8 @@ impl UnownedWindow {
height as c_uint, height as c_uint,
); );
self.xconn.flush_requests() self.xconn.flush_requests()
}.expect("Failed to call `XResizeWindow`"); }
.expect("Failed to call `XResizeWindow`");
} }
#[inline] #[inline]
@ -851,11 +891,14 @@ impl UnownedWindow {
} }
fn update_normal_hints<F>(&self, callback: F) -> Result<(), XError> fn update_normal_hints<F>(&self, callback: F) -> Result<(), XError>
where F: FnOnce(&mut util::NormalHints) -> () where
F: FnOnce(&mut util::NormalHints<'_>) -> (),
{ {
let mut normal_hints = self.xconn.get_normal_hints(self.xwindow)?; let mut normal_hints = self.xconn.get_normal_hints(self.xwindow)?;
callback(&mut normal_hints); callback(&mut normal_hints);
self.xconn.set_normal_hints(self.xwindow, normal_hints).flush() self.xconn
.set_normal_hints(self.xwindow, normal_hints)
.flush()
} }
pub(crate) fn set_min_inner_size_physical(&self, dimensions: Option<(u32, u32)>) { pub(crate) fn set_min_inner_size_physical(&self, dimensions: Option<(u32, u32)>) {
@ -866,9 +909,8 @@ impl UnownedWindow {
#[inline] #[inline]
pub fn set_min_inner_size(&self, logical_dimensions: Option<LogicalSize>) { pub fn set_min_inner_size(&self, logical_dimensions: Option<LogicalSize>) {
self.shared_state.lock().min_inner_size = logical_dimensions; self.shared_state.lock().min_inner_size = logical_dimensions;
let physical_dimensions = logical_dimensions.map(|logical_dimensions| { let physical_dimensions = logical_dimensions
logical_dimensions.to_physical(self.hidpi_factor()).into() .map(|logical_dimensions| logical_dimensions.to_physical(self.hidpi_factor()).into());
});
self.set_min_inner_size_physical(physical_dimensions); self.set_min_inner_size_physical(physical_dimensions);
} }
@ -880,9 +922,8 @@ impl UnownedWindow {
#[inline] #[inline]
pub fn set_max_inner_size(&self, logical_dimensions: Option<LogicalSize>) { pub fn set_max_inner_size(&self, logical_dimensions: Option<LogicalSize>) {
self.shared_state.lock().max_inner_size = logical_dimensions; self.shared_state.lock().max_inner_size = logical_dimensions;
let physical_dimensions = logical_dimensions.map(|logical_dimensions| { let physical_dimensions = logical_dimensions
logical_dimensions.to_physical(self.hidpi_factor()).into() .map(|logical_dimensions| logical_dimensions.to_physical(self.hidpi_factor()).into());
});
self.set_max_inner_size_physical(physical_dimensions); self.set_max_inner_size_physical(physical_dimensions);
} }
@ -892,7 +933,7 @@ impl UnownedWindow {
new_dpi_factor: f64, new_dpi_factor: f64,
width: f64, width: f64,
height: f64, height: f64,
) -> (f64, f64, util::Flusher) { ) -> (f64, f64, util::Flusher<'_>) {
let scale_factor = new_dpi_factor / old_dpi_factor; let scale_factor = new_dpi_factor / old_dpi_factor;
let new_width = width * scale_factor; let new_width = width * scale_factor;
let new_height = height * scale_factor; let new_height = height * scale_factor;
@ -910,7 +951,8 @@ impl UnownedWindow {
normal_hints.set_min_size(min_size); normal_hints.set_min_size(min_size);
normal_hints.set_resize_increments(resize_increments); normal_hints.set_resize_increments(resize_increments);
normal_hints.set_base_size(base_size); normal_hints.set_base_size(base_size);
}).expect("Failed to update normal hints"); })
.expect("Failed to update normal hints");
unsafe { unsafe {
(self.xconn.xlib.XResizeWindow)( (self.xconn.xlib.XResizeWindow)(
self.xconn.display, self.xconn.display,
@ -933,12 +975,17 @@ impl UnownedWindow {
let (logical_min, logical_max) = if resizable { let (logical_min, logical_max) = if resizable {
let shared_state_lock = self.shared_state.lock(); let shared_state_lock = self.shared_state.lock();
(shared_state_lock.min_inner_size, shared_state_lock.max_inner_size) (
shared_state_lock.min_inner_size,
shared_state_lock.max_inner_size,
)
} else { } else {
let window_size = Some(self.inner_size()); let window_size = Some(self.inner_size());
(window_size.clone(), window_size) (window_size.clone(), window_size)
}; };
self.set_maximizable_inner(resizable).queue();
let dpi_factor = self.hidpi_factor(); let dpi_factor = self.hidpi_factor();
let min_inner_size = logical_min let min_inner_size = logical_min
.map(|logical_size| logical_size.to_physical(dpi_factor)) .map(|logical_size| logical_size.to_physical(dpi_factor))
@ -949,7 +996,8 @@ impl UnownedWindow {
self.update_normal_hints(|normal_hints| { self.update_normal_hints(|normal_hints| {
normal_hints.set_min_size(min_inner_size); normal_hints.set_min_size(min_inner_size);
normal_hints.set_max_size(max_inner_size); normal_hints.set_max_size(max_inner_size);
}).expect("Failed to call `XSetWMNormalHints`"); })
.expect("Failed to call `XSetWMNormalHints`");
} }
#[inline] #[inline]
@ -974,9 +1022,7 @@ impl UnownedWindow {
#[inline] #[inline]
pub fn xcb_connection(&self) -> *mut c_void { pub fn xcb_connection(&self) -> *mut c_void {
unsafe { unsafe { (self.xconn.xlib_xcb.XGetXCBConnection)(self.xconn.display) as *mut _ }
(self.xconn.xlib_xcb.XGetXCBConnection)(self.xconn.display) as *mut _
}
} }
fn load_cursor(&self, name: &[u8]) -> ffi::Cursor { fn load_cursor(&self, name: &[u8]) -> ffi::Cursor {
@ -999,13 +1045,9 @@ impl UnownedWindow {
} }
fn get_cursor(&self, cursor: CursorIcon) -> ffi::Cursor { fn get_cursor(&self, cursor: CursorIcon) -> ffi::Cursor {
let load = |name: &[u8]| { let load = |name: &[u8]| self.load_cursor(name);
self.load_cursor(name)
};
let loadn = |names: &[&[u8]]| { let loadn = |names: &[&[u8]]| self.load_first_existing_cursor(names);
self.load_first_existing_cursor(names)
};
// Try multiple names in some cases where the name // Try multiple names in some cases where the name
// differs on the desktop environments or themes. // differs on the desktop environments or themes.
@ -1030,7 +1072,6 @@ impl UnownedWindow {
CursorIcon::NoDrop => loadn(&[b"no-drop\0", b"circle\0"]), CursorIcon::NoDrop => loadn(&[b"no-drop\0", b"circle\0"]),
CursorIcon::NotAllowed => load(b"crossed_circle\0"), CursorIcon::NotAllowed => load(b"crossed_circle\0"),
// Resize cursors // Resize cursors
CursorIcon::EResize => load(b"right_side\0"), CursorIcon::EResize => load(b"right_side\0"),
CursorIcon::NResize => load(b"top_side\0"), CursorIcon::NResize => load(b"top_side\0"),
@ -1063,7 +1104,9 @@ impl UnownedWindow {
if cursor != 0 { if cursor != 0 {
(self.xconn.xlib.XFreeCursor)(self.xconn.display, cursor); (self.xconn.xlib.XFreeCursor)(self.xconn.display, cursor);
} }
self.xconn.flush_requests().expect("Failed to set or free the cursor"); self.xconn
.flush_requests()
.expect("Failed to set or free the cursor");
} }
} }
@ -1081,13 +1124,7 @@ impl UnownedWindow {
fn create_empty_cursor(&self) -> Option<ffi::Cursor> { fn create_empty_cursor(&self) -> Option<ffi::Cursor> {
let data = 0; let data = 0;
let pixmap = unsafe { let pixmap = unsafe {
(self.xconn.xlib.XCreateBitmapFromData)( (self.xconn.xlib.XCreateBitmapFromData)(self.xconn.display, self.xwindow, &data, 1, 1)
self.xconn.display,
self.xwindow,
&data,
1,
1,
)
}; };
if pixmap == 0 { if pixmap == 0 {
// Failed to allocate // Failed to allocate
@ -1116,7 +1153,9 @@ impl UnownedWindow {
#[inline] #[inline]
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
let mut grabbed_lock = self.cursor_grabbed.lock(); let mut grabbed_lock = self.cursor_grabbed.lock();
if grab == *grabbed_lock { return Ok(()); } if grab == *grabbed_lock {
return Ok(());
}
unsafe { unsafe {
// We ungrab before grabbing to prevent passive grabs from causing `AlreadyGrabbed`. // We ungrab before grabbing to prevent passive grabs from causing `AlreadyGrabbed`.
// Therefore, this is common to both codepaths. // Therefore, this is common to both codepaths.
@ -1128,8 +1167,7 @@ impl UnownedWindow {
self.xconn.display, self.xconn.display,
self.xwindow, self.xwindow,
ffi::True, ffi::True,
( (ffi::ButtonPressMask
ffi::ButtonPressMask
| ffi::ButtonReleaseMask | ffi::ButtonReleaseMask
| ffi::EnterWindowMask | ffi::EnterWindowMask
| ffi::LeaveWindowMask | ffi::LeaveWindowMask
@ -1141,8 +1179,7 @@ impl UnownedWindow {
| ffi::Button4MotionMask | ffi::Button4MotionMask
| ffi::Button5MotionMask | ffi::Button5MotionMask
| ffi::ButtonMotionMask | ffi::ButtonMotionMask
| ffi::KeymapStateMask | ffi::KeymapStateMask) as c_uint,
) as c_uint,
ffi::GrabModeAsync, ffi::GrabModeAsync,
ffi::GrabModeAsync, ffi::GrabModeAsync,
self.xwindow, self.xwindow,
@ -1153,14 +1190,20 @@ impl UnownedWindow {
match result { match result {
ffi::GrabSuccess => Ok(()), ffi::GrabSuccess => Ok(()),
ffi::AlreadyGrabbed => Err("Cursor could not be grabbed: already grabbed by another client"), ffi::AlreadyGrabbed => {
Err("Cursor could not be grabbed: already grabbed by another client")
}
ffi::GrabInvalidTime => Err("Cursor could not be grabbed: invalid time"), ffi::GrabInvalidTime => Err("Cursor could not be grabbed: invalid time"),
ffi::GrabNotViewable => Err("Cursor could not be grabbed: grab location not viewable"), ffi::GrabNotViewable => {
Err("Cursor could not be grabbed: grab location not viewable")
}
ffi::GrabFrozen => Err("Cursor could not be grabbed: frozen by another client"), ffi::GrabFrozen => Err("Cursor could not be grabbed: frozen by another client"),
_ => unreachable!(), _ => unreachable!(),
}.map_err(|err| ExternalError::Os(os_error!(OsError::XMisc(err)))) }
.map_err(|err| ExternalError::Os(os_error!(OsError::XMisc(err))))
} else { } else {
self.xconn.flush_requests() self.xconn
.flush_requests()
.map_err(|err| ExternalError::Os(os_error!(OsError::XError(err)))) .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err))))
}; };
if result.is_ok() { if result.is_ok() {
@ -1172,11 +1215,14 @@ impl UnownedWindow {
#[inline] #[inline]
pub fn set_cursor_visible(&self, visible: bool) { pub fn set_cursor_visible(&self, visible: bool) {
let mut visible_lock = self.cursor_visible.lock(); let mut visible_lock = self.cursor_visible.lock();
if visible == *visible_lock {return; } if visible == *visible_lock {
return;
}
let cursor = if visible { let cursor = if visible {
self.get_cursor(*self.cursor.lock()) self.get_cursor(*self.cursor.lock())
} else { } else {
self.create_empty_cursor().expect("Failed to create empty cursor") self.create_empty_cursor()
.expect("Failed to create empty cursor")
}; };
*visible_lock = visible; *visible_lock = visible;
drop(visible_lock); drop(visible_lock);
@ -1190,29 +1236,25 @@ impl UnownedWindow {
pub fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ExternalError> { pub fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ExternalError> {
unsafe { unsafe {
(self.xconn.xlib.XWarpPointer)( (self.xconn.xlib.XWarpPointer)(self.xconn.display, 0, self.xwindow, 0, 0, 0, 0, x, y);
self.xconn.display, self.xconn
0, .flush_requests()
self.xwindow, .map_err(|e| ExternalError::Os(os_error!(OsError::XError(e))))
0,
0,
0,
0,
x,
y,
);
self.xconn.flush_requests().map_err(|e| ExternalError::Os(os_error!(OsError::XError(e))))
} }
} }
#[inline] #[inline]
pub fn set_cursor_position(&self, logical_position: LogicalPosition) -> Result<(), ExternalError> { pub fn set_cursor_position(
&self,
logical_position: LogicalPosition,
) -> Result<(), ExternalError> {
let (x, y) = logical_position.to_physical(self.hidpi_factor()).into(); let (x, y) = logical_position.to_physical(self.hidpi_factor()).into();
self.set_cursor_position_physical(x, y) self.set_cursor_position_physical(x, y)
} }
pub(crate) fn set_ime_position_physical(&self, x: i32, y: i32) { pub(crate) fn set_ime_position_physical(&self, x: i32, y: i32) {
let _ = self.ime_sender let _ = self
.ime_sender
.lock() .lock()
.send((self.xwindow, x as i16, y as i16)); .send((self.xwindow, x as i16, y as i16));
} }
@ -1224,10 +1266,15 @@ impl UnownedWindow {
} }
#[inline] #[inline]
pub fn id(&self) -> WindowId { WindowId(self.xwindow) } pub fn id(&self) -> WindowId {
WindowId(self.xwindow)
}
#[inline] #[inline]
pub fn request_redraw(&self) { pub fn request_redraw(&self) {
self.pending_redraws.lock().unwrap().insert(WindowId(self.xwindow)); self.pending_redraws
.lock()
.unwrap()
.insert(WindowId(self.xwindow));
} }
} }

View file

@ -1,7 +1,4 @@
use std::ptr; use std::{error::Error, fmt, os::raw::c_int, ptr};
use std::fmt;
use std::error::Error;
use std::os::raw::c_int;
use libc; use libc;
use parking_lot::Mutex; use parking_lot::Mutex;
@ -27,7 +24,8 @@ pub struct XConnection {
unsafe impl Send for XConnection {} unsafe impl Send for XConnection {}
unsafe impl Sync for XConnection {} unsafe impl Sync for XConnection {}
pub type XErrorHandler = Option<unsafe extern fn(*mut ffi::Display, *mut ffi::XErrorEvent) -> libc::c_int>; pub type XErrorHandler =
Option<unsafe extern "C" fn(*mut ffi::Display, *mut ffi::XErrorEvent) -> libc::c_int>;
impl XConnection { impl XConnection {
pub fn new(error_handler: XErrorHandler) -> Result<XConnection, XNotSupported> { pub fn new(error_handler: XErrorHandler) -> Result<XConnection, XNotSupported> {
@ -53,9 +51,7 @@ impl XConnection {
}; };
// Get X11 socket file descriptor // Get X11 socket file descriptor
let fd = unsafe { let fd = unsafe { (xlib.XConnectionNumber)(display) };
(xlib.XConnectionNumber)(display)
};
Ok(XConnection { Ok(XConnection {
xlib, xlib,
@ -90,7 +86,7 @@ impl XConnection {
} }
impl fmt::Debug for XConnection { impl fmt::Debug for XConnection {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display.fmt(f) self.display.fmt(f)
} }
} }
@ -119,9 +115,12 @@ impl Error for XError {
} }
impl fmt::Display for XError { impl fmt::Display for XError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(formatter, "X error: {} (code: {}, request code: {}, minor code: {})", write!(
self.description, self.error_code, self.request_code, self.minor_code) formatter,
"X error: {} (code: {}, request code: {}, minor code: {})",
self.description, self.error_code, self.request_code, self.minor_code
)
} }
} }
@ -131,7 +130,7 @@ pub enum XNotSupported {
/// Failed to load one or several shared libraries. /// Failed to load one or several shared libraries.
LibraryOpenError(ffi::OpenError), LibraryOpenError(ffi::OpenError),
/// Connecting to the X server with `XOpenDisplay` failed. /// Connecting to the X server with `XOpenDisplay` failed.
XOpenDisplayFailed, // TODO: add better message XOpenDisplayFailed, // TODO: add better message
} }
impl From<ffi::OpenError> for XNotSupported { impl From<ffi::OpenError> for XNotSupported {
@ -151,16 +150,16 @@ impl Error for XNotSupported {
} }
#[inline] #[inline]
fn cause(&self) -> Option<&Error> { fn cause(&self) -> Option<&dyn Error> {
match *self { match *self {
XNotSupported::LibraryOpenError(ref err) => Some(err), XNotSupported::LibraryOpenError(ref err) => Some(err),
_ => None _ => None,
} }
} }
} }
impl fmt::Display for XNotSupported { 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()) formatter.write_str(self.description())
} }
} }

View file

@ -1,10 +1,18 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use cocoa::{appkit::{self, NSEvent}, base::id}; use cocoa::{
use objc::{declare::ClassDecl, runtime::{Class, Object, Sel}}; appkit::{self, NSEvent},
base::id,
};
use objc::{
declare::ClassDecl,
runtime::{Class, Object, Sel},
};
use event::{DeviceEvent, Event}; use crate::{
use platform_impl::platform::{app_state::AppState, DEVICE_ID, util}; event::{DeviceEvent, ElementState, Event},
platform_impl::platform::{app_state::AppState, util, DEVICE_ID},
};
pub struct AppClass(pub *const Class); pub struct AppClass(pub *const Class);
unsafe impl Send for AppClass {} unsafe impl Send for AppClass {}
@ -17,7 +25,7 @@ lazy_static! {
decl.add_method( decl.add_method(
sel!(sendEvent:), sel!(sendEvent:),
send_event as extern fn(&Object, Sel, id), send_event as extern "C" fn(&Object, Sel, id),
); );
AppClass(decl.register()) AppClass(decl.register())
@ -27,23 +35,25 @@ lazy_static! {
// Normally, holding Cmd + any key never sends us a `keyUp` event for that key. // Normally, holding Cmd + any key never sends us a `keyUp` event for that key.
// Overriding `sendEvent:` like this fixes that. (https://stackoverflow.com/a/15294196) // Overriding `sendEvent:` like this fixes that. (https://stackoverflow.com/a/15294196)
// Fun fact: Firefox still has this bug! (https://bugzilla.mozilla.org/show_bug.cgi?id=1299553) // Fun fact: Firefox still has this bug! (https://bugzilla.mozilla.org/show_bug.cgi?id=1299553)
extern fn send_event(this: &Object, _sel: Sel, event: id) { extern "C" fn send_event(this: &Object, _sel: Sel, event: id) {
unsafe { unsafe {
// For posterity, there are some undocumented event types // For posterity, there are some undocumented event types
// (https://github.com/servo/cocoa-rs/issues/155) // (https://github.com/servo/cocoa-rs/issues/155)
// but that doesn't really matter here. // but that doesn't really matter here.
let event_type = event.eventType(); let event_type = event.eventType();
let modifier_flags = event.modifierFlags(); let modifier_flags = event.modifierFlags();
if event_type == appkit::NSKeyUp && util::has_flag( if event_type == appkit::NSKeyUp
modifier_flags, && util::has_flag(
appkit::NSEventModifierFlags::NSCommandKeyMask, modifier_flags,
) { appkit::NSEventModifierFlags::NSCommandKeyMask,
)
{
let key_window: id = msg_send![this, keyWindow]; let key_window: id = msg_send![this, keyWindow];
let _: () = msg_send![key_window, sendEvent:event]; let _: () = msg_send![key_window, sendEvent: event];
} else { } else {
maybe_dispatch_device_event(event); maybe_dispatch_device_event(event);
let superclass = util::superclass(this); let superclass = util::superclass(this);
let _: () = msg_send![super(this, superclass), sendEvent:event]; let _: () = msg_send![super(this, superclass), sendEvent: event];
} }
} }
} }
@ -51,10 +61,10 @@ extern fn send_event(this: &Object, _sel: Sel, event: id) {
unsafe fn maybe_dispatch_device_event(event: id) { unsafe fn maybe_dispatch_device_event(event: id) {
let event_type = event.eventType(); let event_type = event.eventType();
match event_type { match event_type {
appkit::NSMouseMoved | appkit::NSMouseMoved
appkit::NSLeftMouseDragged | | appkit::NSLeftMouseDragged
appkit::NSOtherMouseDragged | | appkit::NSOtherMouseDragged
appkit::NSRightMouseDragged => { | appkit::NSRightMouseDragged => {
let mut events = VecDeque::with_capacity(3); let mut events = VecDeque::with_capacity(3);
let delta_x = event.deltaX() as f64; let delta_x = event.deltaX() as f64;
@ -63,26 +73,60 @@ unsafe fn maybe_dispatch_device_event(event: id) {
if delta_x != 0.0 { if delta_x != 0.0 {
events.push_back(Event::DeviceEvent { events.push_back(Event::DeviceEvent {
device_id: DEVICE_ID, device_id: DEVICE_ID,
event: DeviceEvent::Motion { axis: 0, value: delta_x }, event: DeviceEvent::Motion {
axis: 0,
value: delta_x,
},
}); });
} }
if delta_y != 0.0 { if delta_y != 0.0 {
events.push_back(Event::DeviceEvent { events.push_back(Event::DeviceEvent {
device_id: DEVICE_ID, device_id: DEVICE_ID,
event: DeviceEvent::Motion { axis: 1, value: delta_y }, event: DeviceEvent::Motion {
axis: 1,
value: delta_y,
},
}); });
} }
if delta_x != 0.0 || delta_y != 0.0 { if delta_x != 0.0 || delta_y != 0.0 {
events.push_back(Event::DeviceEvent { events.push_back(Event::DeviceEvent {
device_id: DEVICE_ID, device_id: DEVICE_ID,
event: DeviceEvent::MouseMotion { delta: (delta_x, delta_y) }, event: DeviceEvent::MouseMotion {
delta: (delta_x, delta_y),
},
}); });
} }
AppState::queue_events(events); AppState::queue_events(events);
}, }
appkit::NSLeftMouseDown | appkit::NSRightMouseDown | appkit::NSOtherMouseDown => {
let mut events = VecDeque::with_capacity(1);
events.push_back(Event::DeviceEvent {
device_id: DEVICE_ID,
event: DeviceEvent::Button {
button: event.buttonNumber() as u32,
state: ElementState::Pressed,
},
});
AppState::queue_events(events);
}
appkit::NSLeftMouseUp | appkit::NSRightMouseUp | appkit::NSOtherMouseUp => {
let mut events = VecDeque::with_capacity(1);
events.push_back(Event::DeviceEvent {
device_id: DEVICE_ID,
event: DeviceEvent::Button {
button: event.buttonNumber() as u32,
state: ElementState::Released,
},
});
AppState::queue_events(events);
}
_ => (), _ => (),
} }
} }

View file

@ -1,7 +1,10 @@
use cocoa::base::id; use cocoa::base::id;
use objc::{runtime::{Class, Object, Sel, BOOL, YES}, declare::ClassDecl}; use objc::{
declare::ClassDecl,
runtime::{Class, Object, Sel, BOOL, YES},
};
use platform_impl::platform::app_state::AppState; use crate::platform_impl::platform::app_state::AppState;
pub struct AppDelegateClass(pub *const Class); pub struct AppDelegateClass(pub *const Class);
unsafe impl Send for AppDelegateClass {} unsafe impl Send for AppDelegateClass {}
@ -14,67 +17,67 @@ lazy_static! {
decl.add_method( decl.add_method(
sel!(applicationDidFinishLaunching:), sel!(applicationDidFinishLaunching:),
did_finish_launching as extern fn(&Object, Sel, id) -> BOOL, did_finish_launching as extern "C" fn(&Object, Sel, id) -> BOOL,
); );
decl.add_method( decl.add_method(
sel!(applicationDidBecomeActive:), sel!(applicationDidBecomeActive:),
did_become_active as extern fn(&Object, Sel, id), did_become_active as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(applicationWillResignActive:), sel!(applicationWillResignActive:),
will_resign_active as extern fn(&Object, Sel, id), will_resign_active as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(applicationWillEnterForeground:), sel!(applicationWillEnterForeground:),
will_enter_foreground as extern fn(&Object, Sel, id), will_enter_foreground as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(applicationDidEnterBackground:), sel!(applicationDidEnterBackground:),
did_enter_background as extern fn(&Object, Sel, id), did_enter_background as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(applicationWillTerminate:), sel!(applicationWillTerminate:),
will_terminate as extern fn(&Object, Sel, id), will_terminate as extern "C" fn(&Object, Sel, id),
); );
AppDelegateClass(decl.register()) AppDelegateClass(decl.register())
}; };
} }
extern fn did_finish_launching(_: &Object, _: Sel, _: id) -> BOOL { extern "C" fn did_finish_launching(_: &Object, _: Sel, _: id) -> BOOL {
trace!("Triggered `didFinishLaunching`"); trace!("Triggered `didFinishLaunching`");
AppState::launched(); AppState::launched();
trace!("Completed `didFinishLaunching`"); trace!("Completed `didFinishLaunching`");
YES YES
} }
extern fn did_become_active(_: &Object, _: Sel, _: id) { extern "C" fn did_become_active(_: &Object, _: Sel, _: id) {
trace!("Triggered `didBecomeActive`"); trace!("Triggered `didBecomeActive`");
/*unsafe { /*unsafe {
HANDLER.lock().unwrap().handle_nonuser_event(Event::Suspended(false)) HANDLER.lock().unwrap().handle_nonuser_event(Event::Resumed)
}*/ }*/
trace!("Completed `didBecomeActive`"); trace!("Completed `didBecomeActive`");
} }
extern fn will_resign_active(_: &Object, _: Sel, _: id) { extern "C" fn will_resign_active(_: &Object, _: Sel, _: id) {
trace!("Triggered `willResignActive`"); trace!("Triggered `willResignActive`");
/*unsafe { /*unsafe {
HANDLER.lock().unwrap().handle_nonuser_event(Event::Suspended(true)) HANDLER.lock().unwrap().handle_nonuser_event(Event::Suspended)
}*/ }*/
trace!("Completed `willResignActive`"); trace!("Completed `willResignActive`");
} }
extern fn will_enter_foreground(_: &Object, _: Sel, _: id) { extern "C" fn will_enter_foreground(_: &Object, _: Sel, _: id) {
trace!("Triggered `willEnterForeground`"); trace!("Triggered `willEnterForeground`");
trace!("Completed `willEnterForeground`"); trace!("Completed `willEnterForeground`");
} }
extern fn did_enter_background(_: &Object, _: Sel, _: id) { extern "C" fn did_enter_background(_: &Object, _: Sel, _: id) {
trace!("Triggered `didEnterBackground`"); trace!("Triggered `didEnterBackground`");
trace!("Completed `didEnterBackground`"); trace!("Completed `didEnterBackground`");
} }
extern fn will_terminate(_: &Object, _: Sel, _: id) { extern "C" fn will_terminate(_: &Object, _: Sel, _: id) {
trace!("Triggered `willTerminate`"); trace!("Triggered `willTerminate`");
/*unsafe { /*unsafe {
let app: id = msg_send![class!(UIApplication), sharedApplication]; let app: id = msg_send![class!(UIApplication), sharedApplication];

View file

@ -1,17 +1,23 @@
use std::{ use std::{
collections::VecDeque, fmt::{self, Debug, Formatter}, collections::VecDeque,
hint::unreachable_unchecked, mem, fmt::{self, Debug},
sync::{atomic::{AtomicBool, Ordering}, Mutex, MutexGuard}, time::Instant, hint::unreachable_unchecked,
mem,
sync::{
atomic::{AtomicBool, Ordering},
Mutex, MutexGuard,
},
time::Instant,
}; };
use cocoa::{appkit::NSApp, base::nil}; use cocoa::{appkit::NSApp, base::nil};
use { use crate::{
event::{Event, StartCause, WindowEvent}, event::{Event, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoopWindowTarget as RootWindowTarget}, event_loop::{ControlFlow, EventLoopWindowTarget as RootWindowTarget},
platform_impl::platform::{observer::EventLoopWaker, util::Never},
window::WindowId, window::WindowId,
}; };
use platform_impl::platform::{observer::EventLoopWaker, util::Never};
lazy_static! { lazy_static! {
static ref HANDLER: Handler = Default::default(); static ref HANDLER: Handler = Default::default();
@ -38,8 +44,9 @@ struct EventLoopHandler<F, T: 'static> {
} }
impl<F, T> Debug for EventLoopHandler<F, T> { impl<F, T> Debug for EventLoopHandler<F, T> {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.debug_struct("EventLoopHandler") formatter
.debug_struct("EventLoopHandler")
.field("window_target", &self.window_target) .field("window_target", &self.window_target)
.finish() .finish()
} }
@ -51,11 +58,7 @@ where
T: 'static, T: 'static,
{ {
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow) { fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow) {
(self.callback)( (self.callback)(event.userify(), &self.window_target, control_flow);
event.userify(),
&self.window_target,
control_flow,
);
self.will_exit |= *control_flow == ControlFlow::Exit; self.will_exit |= *control_flow == ControlFlow::Exit;
if self.will_exit { if self.will_exit {
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;
@ -65,11 +68,7 @@ where
fn handle_user_events(&mut self, control_flow: &mut ControlFlow) { fn handle_user_events(&mut self, control_flow: &mut ControlFlow) {
let mut will_exit = self.will_exit; let mut will_exit = self.will_exit;
for event in self.window_target.p.receiver.try_iter() { for event in self.window_target.p.receiver.try_iter() {
(self.callback)( (self.callback)(Event::UserEvent(event), &self.window_target, control_flow);
Event::UserEvent(event),
&self.window_target,
control_flow,
);
will_exit |= *control_flow == ControlFlow::Exit; will_exit |= *control_flow == ControlFlow::Exit;
if will_exit { if will_exit {
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;
@ -167,18 +166,13 @@ impl Handler {
fn handle_nonuser_event(&self, event: Event<Never>) { fn handle_nonuser_event(&self, event: Event<Never>) {
if let Some(ref mut callback) = *self.callback.lock().unwrap() { if let Some(ref mut callback) = *self.callback.lock().unwrap() {
callback.handle_nonuser_event( callback.handle_nonuser_event(event, &mut *self.control_flow.lock().unwrap());
event,
&mut *self.control_flow.lock().unwrap(),
);
} }
} }
fn handle_user_events(&self) { fn handle_user_events(&self) {
if let Some(ref mut callback) = *self.callback.lock().unwrap() { if let Some(ref mut callback) = *self.callback.lock().unwrap() {
callback.handle_user_events( callback.handle_user_events(&mut *self.control_flow.lock().unwrap());
&mut *self.control_flow.lock().unwrap(),
);
} }
} }
} }
@ -213,7 +207,9 @@ impl AppState {
} }
pub fn wakeup() { pub fn wakeup() {
if !HANDLER.is_ready() { return } if !HANDLER.is_ready() {
return;
}
let start = HANDLER.get_start_time().unwrap(); let start = HANDLER.get_start_time().unwrap();
let cause = match HANDLER.get_control_flow_and_update_prev() { let cause = match HANDLER.get_control_flow_and_update_prev() {
ControlFlow::Poll => StartCause::Poll, ControlFlow::Poll => StartCause::Poll,
@ -233,8 +229,8 @@ impl AppState {
requested_resume: Some(requested_resume), requested_resume: Some(requested_resume),
} }
} }
}, }
ControlFlow::Exit => StartCause::Poll,//panic!("unexpected `ControlFlow::Exit`"), ControlFlow::Exit => StartCause::Poll, //panic!("unexpected `ControlFlow::Exit`"),
}; };
HANDLER.set_in_callback(true); HANDLER.set_in_callback(true);
HANDLER.handle_nonuser_event(Event::NewEvents(cause)); HANDLER.handle_nonuser_event(Event::NewEvents(cause));
@ -278,7 +274,9 @@ impl AppState {
} }
pub fn cleared() { pub fn cleared() {
if !HANDLER.is_ready() { return } if !HANDLER.is_ready() {
return;
}
if !HANDLER.get_in_callback() { if !HANDLER.get_in_callback() {
HANDLER.set_in_callback(true); HANDLER.set_in_callback(true);
HANDLER.handle_user_events(); HANDLER.handle_user_events();
@ -295,8 +293,8 @@ impl AppState {
HANDLER.set_in_callback(false); HANDLER.set_in_callback(false);
} }
if HANDLER.should_exit() { if HANDLER.should_exit() {
let _: () = unsafe { msg_send![NSApp(), stop:nil] }; let _: () = unsafe { msg_send![NSApp(), stop: nil] };
return return;
} }
HANDLER.update_start_time(); HANDLER.update_start_time();
match HANDLER.get_old_and_new_control_flow() { match HANDLER.get_old_and_new_control_flow() {

View file

@ -1,12 +1,14 @@
use std::os::raw::c_ushort; use std::os::raw::c_ushort;
use cocoa::{appkit::{NSEvent, NSEventModifierFlags}, base::id}; use cocoa::{
appkit::{NSEvent, NSEventModifierFlags},
use event::{ base::id,
ElementState, KeyboardInput, };
ModifiersState, VirtualKeyCode, WindowEvent,
use crate::{
event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent},
platform_impl::platform::DEVICE_ID,
}; };
use platform_impl::platform::DEVICE_ID;
pub fn char_to_keycode(c: char) -> Option<VirtualKeyCode> { pub fn char_to_keycode(c: char) -> Option<VirtualKeyCode> {
// We only translate keys that are affected by keyboard layout. // We only translate keys that are affected by keyboard layout.
@ -57,9 +59,9 @@ pub fn char_to_keycode(c: char) -> Option<VirtualKeyCode> {
'-' | '_' => VirtualKeyCode::Minus, '-' | '_' => VirtualKeyCode::Minus,
']' | '}' => VirtualKeyCode::RBracket, ']' | '}' => VirtualKeyCode::RBracket,
'[' | '{' => VirtualKeyCode::LBracket, '[' | '{' => VirtualKeyCode::LBracket,
'\''| '"' => VirtualKeyCode::Apostrophe, '\'' | '"' => VirtualKeyCode::Apostrophe,
';' | ':' => VirtualKeyCode::Semicolon, ';' | ':' => VirtualKeyCode::Semicolon,
'\\'| '|' => VirtualKeyCode::Backslash, '\\' | '|' => VirtualKeyCode::Backslash,
',' | '<' => VirtualKeyCode::Comma, ',' | '<' => VirtualKeyCode::Comma,
'/' | '?' => VirtualKeyCode::Slash, '/' | '?' => VirtualKeyCode::Slash,
'.' | '>' => VirtualKeyCode::Period, '.' | '>' => VirtualKeyCode::Period,
@ -198,7 +200,6 @@ pub fn scancode_to_keycode(scancode: c_ushort) -> Option<VirtualKeyCode> {
0x7d => VirtualKeyCode::Down, 0x7d => VirtualKeyCode::Down,
0x7e => VirtualKeyCode::Up, 0x7e => VirtualKeyCode::Up,
//0x7f => unkown, //0x7f => unkown,
0xa => VirtualKeyCode::Caret, 0xa => VirtualKeyCode::Caret,
_ => return None, _ => return None,
}) })
@ -215,16 +216,14 @@ pub fn check_function_keys(string: &String) -> Option<VirtualKeyCode> {
0xf71a => VirtualKeyCode::F23, 0xf71a => VirtualKeyCode::F23,
0xf71b => VirtualKeyCode::F24, 0xf71b => VirtualKeyCode::F24,
_ => return None, _ => return None,
}) });
} }
None None
} }
pub fn event_mods(event: id) -> ModifiersState { pub fn event_mods(event: id) -> ModifiersState {
let flags = unsafe { let flags = unsafe { NSEvent::modifierFlags(event) };
NSEvent::modifierFlags(event)
};
ModifiersState { ModifiersState {
shift: flags.contains(NSEventModifierFlags::NSShiftKeyMask), shift: flags.contains(NSEventModifierFlags::NSShiftKeyMask),
ctrl: flags.contains(NSEventModifierFlags::NSControlKeyMask), ctrl: flags.contains(NSEventModifierFlags::NSControlKeyMask),
@ -238,9 +237,7 @@ pub fn get_scancode(event: cocoa::base::id) -> c_ushort {
// and there is no easy way to navtively retrieve the layout-dependent character. // and there is no easy way to navtively retrieve the layout-dependent character.
// In winit, we use keycode to refer to the key's character, and so this function aligns // In winit, we use keycode to refer to the key's character, and so this function aligns
// AppKit's terminology with ours. // AppKit's terminology with ours.
unsafe { unsafe { msg_send![event, keyCode] }
msg_send![event, keyCode]
}
} }
pub unsafe fn modifier_event( pub unsafe fn modifier_event(
@ -249,7 +246,8 @@ pub unsafe fn modifier_event(
was_key_pressed: bool, was_key_pressed: bool,
) -> Option<WindowEvent> { ) -> Option<WindowEvent> {
if !was_key_pressed && NSEvent::modifierFlags(ns_event).contains(keymask) if !was_key_pressed && NSEvent::modifierFlags(ns_event).contains(keymask)
|| was_key_pressed && !NSEvent::modifierFlags(ns_event).contains(keymask) { || was_key_pressed && !NSEvent::modifierFlags(ns_event).contains(keymask)
{
let state = if was_key_pressed { let state = if was_key_pressed {
ElementState::Released ElementState::Released
} else { } else {

View file

@ -1,17 +1,24 @@
use std::{ use std::{
collections::VecDeque, mem, os::raw::c_void, process, ptr, sync::mpsc, marker::PhantomData collections::VecDeque, marker::PhantomData, mem, os::raw::c_void, process, ptr, sync::mpsc,
}; };
use cocoa::{appkit::NSApp, base::{id, nil}, foundation::NSAutoreleasePool}; use cocoa::{
appkit::NSApp,
base::{id, nil},
foundation::NSAutoreleasePool,
};
use { use crate::{
event::Event, event::Event,
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootWindowTarget}, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootWindowTarget},
}; platform_impl::platform::{
use platform_impl::platform::{ app::APP_CLASS,
app::APP_CLASS, app_delegate::APP_DELEGATE_CLASS, app_delegate::APP_DELEGATE_CLASS,
app_state::AppState, monitor::{self, MonitorHandle}, app_state::AppState,
observer::*, util::IdRef, monitor::{self, MonitorHandle},
observer::*,
util::IdRef,
},
}; };
pub struct EventLoopWindowTarget<T: 'static> { pub struct EventLoopWindowTarget<T: 'static> {
@ -75,7 +82,8 @@ impl<T> EventLoop<T> {
} }
pub fn run<F>(self, callback: F) -> ! pub fn run<F>(self, callback: F) -> !
where F: 'static + FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow), where
F: 'static + FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
{ {
unsafe { unsafe {
let _pool = NSAutoreleasePool::new(nil); let _pool = NSAutoreleasePool::new(nil);
@ -89,7 +97,8 @@ impl<T> EventLoop<T> {
} }
pub fn run_return<F>(&mut self, _callback: F) pub fn run_return<F>(&mut self, _callback: F)
where F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow), where
F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
{ {
unimplemented!(); unimplemented!();
} }
@ -119,11 +128,8 @@ impl<T> Proxy<T> {
let rl = CFRunLoopGetMain(); let rl = CFRunLoopGetMain();
let mut context: CFRunLoopSourceContext = mem::zeroed(); let mut context: CFRunLoopSourceContext = mem::zeroed();
context.perform = event_loop_proxy_handler; context.perform = event_loop_proxy_handler;
let source = CFRunLoopSourceCreate( let source =
ptr::null_mut(), CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
CFIndex::max_value() - 1,
&mut context,
);
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes); CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl); CFRunLoopWakeUp(rl);

View file

@ -2,8 +2,10 @@
#![allow(dead_code, non_snake_case, non_upper_case_globals)] #![allow(dead_code, non_snake_case, non_upper_case_globals)]
use cocoa::base::id; use cocoa::{
use cocoa::foundation::{NSInteger, NSUInteger}; base::id,
foundation::{NSInteger, NSUInteger},
};
use objc; use objc;
pub const NSNotFound: NSInteger = NSInteger::max_value(); pub const NSNotFound: NSInteger = NSInteger::max_value();
@ -53,11 +55,11 @@ impl NSMutableAttributedString for id {
} }
unsafe fn initWithString(self, string: id) -> id { unsafe fn initWithString(self, string: id) -> id {
msg_send![self, initWithString:string] msg_send![self, initWithString: string]
} }
unsafe fn initWithAttributedString(self, string: id) -> id { unsafe fn initWithAttributedString(self, string: id) -> id {
msg_send![self, initWithAttributedString:string] msg_send![self, initWithAttributedString: string]
} }
unsafe fn string(self) -> id { unsafe fn string(self) -> id {

View file

@ -15,16 +15,13 @@ mod window_delegate;
use std::{fmt, ops::Deref, sync::Arc}; use std::{fmt, ops::Deref, sync::Arc};
use {
event::DeviceId as RootDeviceId, window::WindowAttributes,
error::OsError as RootOsError,
};
pub use self::{ pub use self::{
event_loop::{EventLoop, EventLoopWindowTarget, Proxy as EventLoopProxy}, event_loop::{EventLoop, EventLoopWindowTarget, Proxy as EventLoopProxy},
monitor::MonitorHandle, monitor::MonitorHandle,
window::{ window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, UnownedWindow},
Id as WindowId, PlatformSpecificWindowBuilderAttributes, UnownedWindow, };
}, use crate::{
error::OsError as RootOsError, event::DeviceId as RootDeviceId, window::WindowAttributes,
}; };
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -48,7 +45,7 @@ pub struct Window {
#[derive(Debug)] #[derive(Debug)]
pub enum OsError { pub enum OsError {
CGError(core_graphics::base::CGError), CGError(core_graphics::base::CGError),
CreationError(&'static str) CreationError(&'static str),
} }
unsafe impl Send for Window {} unsafe impl Send for Window {}
@ -74,7 +71,7 @@ impl Window {
} }
impl fmt::Display for OsError { impl fmt::Display for OsError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
OsError::CGError(e) => f.pad(&format!("CGError {}", e)), OsError::CGError(e) => f.pad(&format!("CGError {}", e)),
OsError::CreationError(e) => f.pad(e), OsError::CreationError(e) => f.pad(e),

View file

@ -1,10 +1,21 @@
use std::{collections::VecDeque, fmt}; use std::{collections::VecDeque, fmt};
use cocoa::{appkit::NSScreen, base::{id, nil}, foundation::{NSString, NSUInteger}}; use cocoa::{
use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds}; appkit::NSScreen,
base::{id, nil},
foundation::{NSString, NSUInteger},
};
use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds, CGDisplayMode};
use core_video_sys::{
kCVReturnSuccess, kCVTimeIsIndefinite, CVDisplayLinkCreateWithCGDisplay,
CVDisplayLinkGetNominalOutputVideoRefreshPeriod, CVDisplayLinkRelease,
};
use dpi::{PhysicalPosition, PhysicalSize}; use crate::{
use platform_impl::platform::util::IdRef; dpi::{PhysicalPosition, PhysicalSize},
monitor::VideoMode,
platform_impl::platform::util::IdRef,
};
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct MonitorHandle(CGDirectDisplayID); pub struct MonitorHandle(CGDirectDisplayID);
@ -26,13 +37,13 @@ pub fn primary_monitor() -> MonitorHandle {
} }
impl fmt::Debug for MonitorHandle { impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// TODO: Do this using the proper fmt API // TODO: Do this using the proper fmt API
#[derive(Debug)] #[derive(Debug)]
struct MonitorHandle { struct MonitorHandle {
name: Option<String>, name: Option<String>,
native_identifier: u32, native_identifier: u32,
dimensions: PhysicalSize, size: PhysicalSize,
position: PhysicalPosition, position: PhysicalPosition,
hidpi_factor: f64, hidpi_factor: f64,
} }
@ -40,7 +51,7 @@ impl fmt::Debug for MonitorHandle {
let monitor_id_proxy = MonitorHandle { let monitor_id_proxy = MonitorHandle {
name: self.name(), name: self.name(),
native_identifier: self.native_identifier(), native_identifier: self.native_identifier(),
dimensions: self.dimensions(), size: self.size(),
position: self.position(), position: self.position(),
hidpi_factor: self.hidpi_factor(), hidpi_factor: self.hidpi_factor(),
}; };
@ -65,15 +76,12 @@ impl MonitorHandle {
self.0 self.0
} }
pub fn dimensions(&self) -> PhysicalSize { pub fn size(&self) -> PhysicalSize {
let MonitorHandle(display_id) = *self; let MonitorHandle(display_id) = *self;
let display = CGDisplay::new(display_id); let display = CGDisplay::new(display_id);
let height = display.pixels_high(); let height = display.pixels_high();
let width = display.pixels_wide(); let width = display.pixels_wide();
PhysicalSize::from_logical( PhysicalSize::from_logical((width as f64, height as f64), self.hidpi_factor())
(width as f64, height as f64),
self.hidpi_factor(),
)
} }
#[inline] #[inline]
@ -86,14 +94,52 @@ impl MonitorHandle {
} }
pub fn hidpi_factor(&self) -> f64 { pub fn hidpi_factor(&self) -> f64 {
let screen = match self.nsscreen() { let screen = match self.ns_screen() {
Some(screen) => screen, Some(screen) => screen,
None => return 1.0, // default to 1.0 when we can't find the screen None => return 1.0, // default to 1.0 when we can't find the screen
}; };
unsafe { NSScreen::backingScaleFactor(screen) as f64 } unsafe { NSScreen::backingScaleFactor(screen) as f64 }
} }
pub(crate) fn nsscreen(&self) -> Option<id> { pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
let cv_refresh_rate = unsafe {
let mut display_link = std::ptr::null_mut();
assert_eq!(
CVDisplayLinkCreateWithCGDisplay(self.0, &mut display_link),
kCVReturnSuccess
);
let time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(display_link);
CVDisplayLinkRelease(display_link);
// This value is indefinite if an invalid display link was specified
assert!(time.flags & kCVTimeIsIndefinite == 0);
time.timeScale as i64 / time.timeValue
};
CGDisplayMode::all_display_modes(self.0, std::ptr::null())
.expect("failed to obtain list of display modes")
.into_iter()
.map(move |mode| {
let cg_refresh_rate = mode.refresh_rate().round() as i64;
// CGDisplayModeGetRefreshRate returns 0.0 for any display that
// isn't a CRT
let refresh_rate = if cg_refresh_rate > 0 {
cg_refresh_rate
} else {
cv_refresh_rate
};
VideoMode {
size: (mode.width() as u32, mode.height() as u32),
refresh_rate: refresh_rate as u16,
bit_depth: mode.bit_depth() as u16,
}
})
}
pub(crate) fn ns_screen(&self) -> Option<id> {
unsafe { unsafe {
let native_id = self.native_identifier(); let native_id = self.native_identifier();
let screens = NSScreen::screens(nil); let screens = NSScreen::screens(nil);

View file

@ -1,9 +1,9 @@
use std::{self, ptr, os::raw::*, time::Instant}; use std::{self, os::raw::*, ptr, time::Instant};
use platform_impl::platform::app_state::AppState; use crate::platform_impl::platform::app_state::AppState;
#[link(name = "CoreFoundation", kind = "framework")] #[link(name = "CoreFoundation", kind = "framework")]
extern { extern "C" {
pub static kCFRunLoopDefaultMode: CFRunLoopMode; pub static kCFRunLoopDefaultMode: CFRunLoopMode;
pub static kCFRunLoopCommonModes: CFRunLoopMode; pub static kCFRunLoopCommonModes: CFRunLoopMode;
@ -33,15 +33,8 @@ extern {
callout: CFRunLoopTimerCallBack, callout: CFRunLoopTimerCallBack,
context: *mut CFRunLoopTimerContext, context: *mut CFRunLoopTimerContext,
) -> CFRunLoopTimerRef; ) -> CFRunLoopTimerRef;
pub fn CFRunLoopAddTimer( pub fn CFRunLoopAddTimer(rl: CFRunLoopRef, timer: CFRunLoopTimerRef, mode: CFRunLoopMode);
rl: CFRunLoopRef, pub fn CFRunLoopTimerSetNextFireDate(timer: CFRunLoopTimerRef, fireDate: CFAbsoluteTime);
timer: CFRunLoopTimerRef,
mode: CFRunLoopMode,
);
pub fn CFRunLoopTimerSetNextFireDate(
timer: CFRunLoopTimerRef,
fireDate: CFAbsoluteTime,
);
pub fn CFRunLoopTimerInvalidate(time: CFRunLoopTimerRef); pub fn CFRunLoopTimerInvalidate(time: CFRunLoopTimerRef);
pub fn CFRunLoopSourceCreate( pub fn CFRunLoopSourceCreate(
@ -49,11 +42,8 @@ extern {
order: CFIndex, order: CFIndex,
context: *mut CFRunLoopSourceContext, context: *mut CFRunLoopSourceContext,
) -> CFRunLoopSourceRef; ) -> CFRunLoopSourceRef;
pub fn CFRunLoopAddSource( pub fn CFRunLoopAddSource(rl: CFRunLoopRef, source: CFRunLoopSourceRef, mode: CFRunLoopMode);
rl: CFRunLoopRef, #[allow(dead_code)]
source: CFRunLoopSourceRef,
mode: CFRunLoopMode,
);
pub fn CFRunLoopSourceInvalidate(source: CFRunLoopSourceRef); pub fn CFRunLoopSourceInvalidate(source: CFRunLoopSourceRef);
pub fn CFRunLoopSourceSignal(source: CFRunLoopSourceRef); pub fn CFRunLoopSourceSignal(source: CFRunLoopSourceRef);
@ -62,6 +52,7 @@ extern {
} }
pub type Boolean = u8; pub type Boolean = u8;
#[allow(dead_code)]
const FALSE: Boolean = 0; const FALSE: Boolean = 0;
const TRUE: Boolean = 1; const TRUE: Boolean = 1;
@ -87,24 +78,23 @@ pub type CFRunLoopActivity = CFOptionFlags;
pub type CFAbsoluteTime = CFTimeInterval; pub type CFAbsoluteTime = CFTimeInterval;
pub type CFTimeInterval = f64; pub type CFTimeInterval = f64;
#[allow(non_upper_case_globals)]
pub const kCFRunLoopEntry: CFRunLoopActivity = 0; pub const kCFRunLoopEntry: CFRunLoopActivity = 0;
#[allow(non_upper_case_globals)]
pub const kCFRunLoopBeforeWaiting: CFRunLoopActivity = 1 << 5; pub const kCFRunLoopBeforeWaiting: CFRunLoopActivity = 1 << 5;
#[allow(non_upper_case_globals)]
pub const kCFRunLoopAfterWaiting: CFRunLoopActivity = 1 << 6; pub const kCFRunLoopAfterWaiting: CFRunLoopActivity = 1 << 6;
#[allow(non_upper_case_globals)]
pub const kCFRunLoopExit: CFRunLoopActivity = 1 << 7; pub const kCFRunLoopExit: CFRunLoopActivity = 1 << 7;
pub type CFRunLoopObserverCallBack = extern "C" fn( pub type CFRunLoopObserverCallBack =
observer: CFRunLoopObserverRef, extern "C" fn(observer: CFRunLoopObserverRef, activity: CFRunLoopActivity, info: *mut c_void);
activity: CFRunLoopActivity, pub type CFRunLoopTimerCallBack = extern "C" fn(timer: CFRunLoopTimerRef, info: *mut c_void);
info: *mut c_void,
);
pub type CFRunLoopTimerCallBack = extern "C" fn(
timer: CFRunLoopTimerRef,
info: *mut c_void
);
pub enum CFRunLoopObserverContext {} pub enum CFRunLoopObserverContext {}
pub enum CFRunLoopTimerContext {} pub enum CFRunLoopTimerContext {}
#[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct CFRunLoopSourceContext { pub struct CFRunLoopSourceContext {
pub version: CFIndex, pub version: CFIndex,
@ -120,7 +110,7 @@ pub struct CFRunLoopSourceContext {
} }
// begin is queued with the highest priority to ensure it is processed before other observers // begin is queued with the highest priority to ensure it is processed before other observers
extern fn control_flow_begin_handler( extern "C" fn control_flow_begin_handler(
_: CFRunLoopObserverRef, _: CFRunLoopObserverRef,
activity: CFRunLoopActivity, activity: CFRunLoopActivity,
_: *mut c_void, _: *mut c_void,
@ -131,7 +121,7 @@ extern fn control_flow_begin_handler(
//trace!("Triggered `CFRunLoopAfterWaiting`"); //trace!("Triggered `CFRunLoopAfterWaiting`");
AppState::wakeup(); AppState::wakeup();
//trace!("Completed `CFRunLoopAfterWaiting`"); //trace!("Completed `CFRunLoopAfterWaiting`");
}, }
kCFRunLoopEntry => unimplemented!(), // not expected to ever happen kCFRunLoopEntry => unimplemented!(), // not expected to ever happen
_ => unreachable!(), _ => unreachable!(),
} }
@ -139,7 +129,7 @@ extern fn control_flow_begin_handler(
// end is queued with the lowest priority to ensure it is processed after other observers // end is queued with the lowest priority to ensure it is processed after other observers
// without that, LoopDestroyed would get sent after EventsCleared // without that, LoopDestroyed would get sent after EventsCleared
extern fn control_flow_end_handler( extern "C" fn control_flow_end_handler(
_: CFRunLoopObserverRef, _: CFRunLoopObserverRef,
activity: CFRunLoopActivity, activity: CFRunLoopActivity,
_: *mut c_void, _: *mut c_void,
@ -150,8 +140,8 @@ extern fn control_flow_end_handler(
//trace!("Triggered `CFRunLoopBeforeWaiting`"); //trace!("Triggered `CFRunLoopBeforeWaiting`");
AppState::cleared(); AppState::cleared();
//trace!("Completed `CFRunLoopBeforeWaiting`"); //trace!("Completed `CFRunLoopBeforeWaiting`");
}, }
kCFRunLoopExit => (),//unimplemented!(), // not expected to ever happen kCFRunLoopExit => (), //unimplemented!(), // not expected to ever happen
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -172,7 +162,7 @@ impl RunLoop {
let observer = CFRunLoopObserverCreate( let observer = CFRunLoopObserverCreate(
ptr::null_mut(), ptr::null_mut(),
flags, flags,
TRUE, // Indicates we want this to run repeatedly TRUE, // Indicates we want this to run repeatedly
priority, // The lower the value, the sooner this will run priority, // The lower the value, the sooner this will run
handler, handler,
ptr::null_mut(), ptr::null_mut(),
@ -197,7 +187,6 @@ pub fn setup_control_flow_observers() {
} }
} }
pub struct EventLoopWaker { pub struct EventLoopWaker {
timer: CFRunLoopTimerRef, timer: CFRunLoopTimerRef,
} }
@ -213,7 +202,7 @@ impl Drop for EventLoopWaker {
impl Default for EventLoopWaker { impl Default for EventLoopWaker {
fn default() -> EventLoopWaker { fn default() -> EventLoopWaker {
extern fn wakeup_main_loop(_timer: CFRunLoopTimerRef, _info: *mut c_void) {} extern "C" fn wakeup_main_loop(_timer: CFRunLoopTimerRef, _info: *mut c_void) {}
unsafe { unsafe {
// create a timer with a 1µs interval (1ns does not work) to mimic polling. // create a timer with a 1µs interval (1ns does not work) to mimic polling.
// it is initially setup with a first fire time really far into the // it is initially setup with a first fire time really far into the
@ -250,8 +239,8 @@ impl EventLoopWaker {
unsafe { unsafe {
let current = CFAbsoluteTimeGetCurrent(); let current = CFAbsoluteTimeGetCurrent();
let duration = instant - now; let duration = instant - now;
let fsecs = duration.subsec_nanos() as f64 / 1_000_000_000.0 let fsecs =
+ duration.as_secs() as f64; duration.subsec_nanos() as f64 / 1_000_000_000.0 + duration.as_secs() as f64;
CFRunLoopTimerSetNextFireDate(self.timer, current + fsecs) CFRunLoopTimerSetNextFireDate(self.timer, current + fsecs)
} }
} }

View file

@ -1,42 +1,47 @@
use std::{os::raw::c_void, sync::{Mutex, Weak}}; use std::{
os::raw::c_void,
sync::{Mutex, Weak},
};
use cocoa::{ use cocoa::{
appkit::{CGFloat, NSWindow, NSWindowStyleMask}, appkit::{CGFloat, NSScreen, NSWindow, NSWindowStyleMask},
base::{id, nil}, base::{id, nil},
foundation::{NSAutoreleasePool, NSPoint, NSSize}, foundation::{NSAutoreleasePool, NSPoint, NSSize, NSString},
}; };
use dispatch::ffi::{dispatch_async_f, dispatch_get_main_queue, dispatch_sync_f}; use dispatch::ffi::{dispatch_async_f, dispatch_get_main_queue, dispatch_sync_f};
use dpi::LogicalSize; use crate::{
use platform_impl::platform::{ffi, window::SharedState}; dpi::LogicalSize,
platform_impl::platform::{ffi, util::IdRef, window::SharedState},
};
unsafe fn set_style_mask(nswindow: id, nsview: id, mask: NSWindowStyleMask) { unsafe fn set_style_mask(ns_window: id, ns_view: id, mask: NSWindowStyleMask) {
nswindow.setStyleMask_(mask); ns_window.setStyleMask_(mask);
// If we don't do this, key handling will break // If we don't do this, key handling will break
// (at least until the window is clicked again/etc.) // (at least until the window is clicked again/etc.)
nswindow.makeFirstResponder_(nsview); ns_window.makeFirstResponder_(ns_view);
} }
struct SetStyleMaskData { struct SetStyleMaskData {
nswindow: id, ns_window: id,
nsview: id, ns_view: id,
mask: NSWindowStyleMask, mask: NSWindowStyleMask,
} }
impl SetStyleMaskData { impl SetStyleMaskData {
fn new_ptr( fn new_ptr(ns_window: id, ns_view: id, mask: NSWindowStyleMask) -> *mut Self {
nswindow: id, Box::into_raw(Box::new(SetStyleMaskData {
nsview: id, ns_window,
mask: NSWindowStyleMask, ns_view,
) -> *mut Self { mask,
Box::into_raw(Box::new(SetStyleMaskData { nswindow, nsview, mask })) }))
} }
} }
extern fn set_style_mask_callback(context: *mut c_void) { extern "C" fn set_style_mask_callback(context: *mut c_void) {
unsafe { unsafe {
let context_ptr = context as *mut SetStyleMaskData; let context_ptr = context as *mut SetStyleMaskData;
{ {
let context = &*context_ptr; let context = &*context_ptr;
set_style_mask(context.nswindow, context.nsview, context.mask); set_style_mask(context.ns_window, context.ns_view, context.mask);
} }
Box::from_raw(context_ptr); Box::from_raw(context_ptr);
} }
@ -45,16 +50,16 @@ extern fn set_style_mask_callback(context: *mut c_void) {
// `setStyleMask:` isn't thread-safe, so we have to use Grand Central Dispatch. // `setStyleMask:` isn't thread-safe, so we have to use Grand Central Dispatch.
// Otherwise, this would vomit out errors about not being on the main thread // Otherwise, this would vomit out errors about not being on the main thread
// and fail to do anything. // and fail to do anything.
pub unsafe fn set_style_mask_async(nswindow: id, nsview: id, mask: NSWindowStyleMask) { pub unsafe fn set_style_mask_async(ns_window: id, ns_view: id, mask: NSWindowStyleMask) {
let context = SetStyleMaskData::new_ptr(nswindow, nsview, mask); let context = SetStyleMaskData::new_ptr(ns_window, ns_view, mask);
dispatch_async_f( dispatch_async_f(
dispatch_get_main_queue(), dispatch_get_main_queue(),
context as *mut _, context as *mut _,
set_style_mask_callback, set_style_mask_callback,
); );
} }
pub unsafe fn set_style_mask_sync(nswindow: id, nsview: id, mask: NSWindowStyleMask) { pub unsafe fn set_style_mask_sync(ns_window: id, ns_view: id, mask: NSWindowStyleMask) {
let context = SetStyleMaskData::new_ptr(nswindow, nsview, mask); let context = SetStyleMaskData::new_ptr(ns_window, ns_view, mask);
dispatch_sync_f( dispatch_sync_f(
dispatch_get_main_queue(), dispatch_get_main_queue(),
context as *mut _, context as *mut _,
@ -63,24 +68,21 @@ pub unsafe fn set_style_mask_sync(nswindow: id, nsview: id, mask: NSWindowStyleM
} }
struct SetContentSizeData { struct SetContentSizeData {
nswindow: id, ns_window: id,
size: LogicalSize, size: LogicalSize,
} }
impl SetContentSizeData { impl SetContentSizeData {
fn new_ptr( fn new_ptr(ns_window: id, size: LogicalSize) -> *mut Self {
nswindow: id, Box::into_raw(Box::new(SetContentSizeData { ns_window, size }))
size: LogicalSize,
) -> *mut Self {
Box::into_raw(Box::new(SetContentSizeData { nswindow, size }))
} }
} }
extern fn set_content_size_callback(context: *mut c_void) { extern "C" fn set_content_size_callback(context: *mut c_void) {
unsafe { unsafe {
let context_ptr = context as *mut SetContentSizeData; let context_ptr = context as *mut SetContentSizeData;
{ {
let context = &*context_ptr; let context = &*context_ptr;
NSWindow::setContentSize_( NSWindow::setContentSize_(
context.nswindow, context.ns_window,
NSSize::new( NSSize::new(
context.size.width as CGFloat, context.size.width as CGFloat,
context.size.height as CGFloat, context.size.height as CGFloat,
@ -92,8 +94,8 @@ extern fn set_content_size_callback(context: *mut c_void) {
} }
// `setContentSize:` isn't thread-safe either, though it doesn't log any errors // `setContentSize:` isn't thread-safe either, though it doesn't log any errors
// and just fails silently. Anyway, GCD to the rescue! // and just fails silently. Anyway, GCD to the rescue!
pub unsafe fn set_content_size_async(nswindow: id, size: LogicalSize) { pub unsafe fn set_content_size_async(ns_window: id, size: LogicalSize) {
let context = SetContentSizeData::new_ptr(nswindow, size); let context = SetContentSizeData::new_ptr(ns_window, size);
dispatch_async_f( dispatch_async_f(
dispatch_get_main_queue(), dispatch_get_main_queue(),
context as *mut _, context as *mut _,
@ -102,31 +104,28 @@ pub unsafe fn set_content_size_async(nswindow: id, size: LogicalSize) {
} }
struct SetFrameTopLeftPointData { struct SetFrameTopLeftPointData {
nswindow: id, ns_window: id,
point: NSPoint, point: NSPoint,
} }
impl SetFrameTopLeftPointData { impl SetFrameTopLeftPointData {
fn new_ptr( fn new_ptr(ns_window: id, point: NSPoint) -> *mut Self {
nswindow: id, Box::into_raw(Box::new(SetFrameTopLeftPointData { ns_window, point }))
point: NSPoint,
) -> *mut Self {
Box::into_raw(Box::new(SetFrameTopLeftPointData { nswindow, point }))
} }
} }
extern fn set_frame_top_left_point_callback(context: *mut c_void) { extern "C" fn set_frame_top_left_point_callback(context: *mut c_void) {
unsafe { unsafe {
let context_ptr = context as *mut SetFrameTopLeftPointData; let context_ptr = context as *mut SetFrameTopLeftPointData;
{ {
let context = &*context_ptr; let context = &*context_ptr;
NSWindow::setFrameTopLeftPoint_(context.nswindow, context.point); NSWindow::setFrameTopLeftPoint_(context.ns_window, context.point);
} }
Box::from_raw(context_ptr); Box::from_raw(context_ptr);
} }
} }
// `setFrameTopLeftPoint:` isn't thread-safe, but fortunately has the courtesy // `setFrameTopLeftPoint:` isn't thread-safe, but fortunately has the courtesy
// to log errors. // to log errors.
pub unsafe fn set_frame_top_left_point_async(nswindow: id, point: NSPoint) { pub unsafe fn set_frame_top_left_point_async(ns_window: id, point: NSPoint) {
let context = SetFrameTopLeftPointData::new_ptr(nswindow, point); let context = SetFrameTopLeftPointData::new_ptr(ns_window, point);
dispatch_async_f( dispatch_async_f(
dispatch_get_main_queue(), dispatch_get_main_queue(),
context as *mut _, context as *mut _,
@ -135,30 +134,27 @@ pub unsafe fn set_frame_top_left_point_async(nswindow: id, point: NSPoint) {
} }
struct SetLevelData { struct SetLevelData {
nswindow: id, ns_window: id,
level: ffi::NSWindowLevel, level: ffi::NSWindowLevel,
} }
impl SetLevelData { impl SetLevelData {
fn new_ptr( fn new_ptr(ns_window: id, level: ffi::NSWindowLevel) -> *mut Self {
nswindow: id, Box::into_raw(Box::new(SetLevelData { ns_window, level }))
level: ffi::NSWindowLevel,
) -> *mut Self {
Box::into_raw(Box::new(SetLevelData { nswindow, level }))
} }
} }
extern fn set_level_callback(context: *mut c_void) { extern "C" fn set_level_callback(context: *mut c_void) {
unsafe { unsafe {
let context_ptr = context as *mut SetLevelData; let context_ptr = context as *mut SetLevelData;
{ {
let context = &*context_ptr; let context = &*context_ptr;
context.nswindow.setLevel_(context.level as _); context.ns_window.setLevel_(context.level as _);
} }
Box::from_raw(context_ptr); Box::from_raw(context_ptr);
} }
} }
// `setFrameTopLeftPoint:` isn't thread-safe, and fails silently. // `setFrameTopLeftPoint:` isn't thread-safe, and fails silently.
pub unsafe fn set_level_async(nswindow: id, level: ffi::NSWindowLevel) { pub unsafe fn set_level_async(ns_window: id, level: ffi::NSWindowLevel) {
let context = SetLevelData::new_ptr(nswindow, level); let context = SetLevelData::new_ptr(ns_window, level);
dispatch_async_f( dispatch_async_f(
dispatch_get_main_queue(), dispatch_get_main_queue(),
context as *mut _, context as *mut _,
@ -167,27 +163,27 @@ pub unsafe fn set_level_async(nswindow: id, level: ffi::NSWindowLevel) {
} }
struct ToggleFullScreenData { struct ToggleFullScreenData {
nswindow: id, ns_window: id,
nsview: id, ns_view: id,
not_fullscreen: bool, not_fullscreen: bool,
shared_state: Weak<Mutex<SharedState>>, shared_state: Weak<Mutex<SharedState>>,
} }
impl ToggleFullScreenData { impl ToggleFullScreenData {
fn new_ptr( fn new_ptr(
nswindow: id, ns_window: id,
nsview: id, ns_view: id,
not_fullscreen: bool, not_fullscreen: bool,
shared_state: Weak<Mutex<SharedState>>, shared_state: Weak<Mutex<SharedState>>,
) -> *mut Self { ) -> *mut Self {
Box::into_raw(Box::new(ToggleFullScreenData { Box::into_raw(Box::new(ToggleFullScreenData {
nswindow, ns_window,
nsview, ns_view,
not_fullscreen, not_fullscreen,
shared_state, shared_state,
})) }))
} }
} }
extern fn toggle_full_screen_callback(context: *mut c_void) { extern "C" fn toggle_full_screen_callback(context: *mut c_void) {
unsafe { unsafe {
let context_ptr = context as *mut ToggleFullScreenData; let context_ptr = context as *mut ToggleFullScreenData;
{ {
@ -197,11 +193,11 @@ extern fn toggle_full_screen_callback(context: *mut c_void) {
// set a normal style temporarily. The previous state will be // set a normal style temporarily. The previous state will be
// restored in `WindowDelegate::window_did_exit_fullscreen`. // restored in `WindowDelegate::window_did_exit_fullscreen`.
if context.not_fullscreen { if context.not_fullscreen {
let curr_mask = context.nswindow.styleMask(); let curr_mask = context.ns_window.styleMask();
let required = NSWindowStyleMask::NSTitledWindowMask let required = NSWindowStyleMask::NSTitledWindowMask
| NSWindowStyleMask::NSResizableWindowMask; | NSWindowStyleMask::NSResizableWindowMask;
if !curr_mask.contains(required) { if !curr_mask.contains(required) {
set_style_mask(context.nswindow, context.nsview, required); set_style_mask(context.ns_window, context.ns_view, required);
if let Some(shared_state) = context.shared_state.upgrade() { if let Some(shared_state) = context.shared_state.upgrade() {
trace!("Locked shared state in `toggle_full_screen_callback`"); trace!("Locked shared state in `toggle_full_screen_callback`");
let mut shared_state_lock = shared_state.lock().unwrap(); let mut shared_state_lock = shared_state.lock().unwrap();
@ -211,7 +207,7 @@ extern fn toggle_full_screen_callback(context: *mut c_void) {
} }
} }
context.nswindow.toggleFullScreen_(nil); context.ns_window.toggleFullScreen_(nil);
} }
Box::from_raw(context_ptr); Box::from_raw(context_ptr);
} }
@ -219,17 +215,12 @@ extern fn toggle_full_screen_callback(context: *mut c_void) {
// `toggleFullScreen` is thread-safe, but our additional logic to account for // `toggleFullScreen` is thread-safe, but our additional logic to account for
// window styles isn't. // window styles isn't.
pub unsafe fn toggle_full_screen_async( pub unsafe fn toggle_full_screen_async(
nswindow: id, ns_window: id,
nsview: id, ns_view: id,
not_fullscreen: bool, not_fullscreen: bool,
shared_state: Weak<Mutex<SharedState>>, shared_state: Weak<Mutex<SharedState>>,
) { ) {
let context = ToggleFullScreenData::new_ptr( let context = ToggleFullScreenData::new_ptr(ns_window, ns_view, not_fullscreen, shared_state);
nswindow,
nsview,
not_fullscreen,
shared_state,
);
dispatch_async_f( dispatch_async_f(
dispatch_get_main_queue(), dispatch_get_main_queue(),
context as *mut _, context as *mut _,
@ -237,28 +228,105 @@ pub unsafe fn toggle_full_screen_async(
); );
} }
struct OrderOutData { struct SetMaximizedData {
nswindow: id, ns_window: id,
is_zoomed: bool,
maximized: bool,
shared_state: Weak<Mutex<SharedState>>,
} }
impl OrderOutData { impl SetMaximizedData {
fn new_ptr(nswindow: id) -> *mut Self { fn new_ptr(
Box::into_raw(Box::new(OrderOutData { nswindow })) ns_window: id,
is_zoomed: bool,
maximized: bool,
shared_state: Weak<Mutex<SharedState>>,
) -> *mut Self {
Box::into_raw(Box::new(SetMaximizedData {
ns_window,
is_zoomed,
maximized,
shared_state,
}))
} }
} }
extern fn order_out_callback(context: *mut c_void) { extern "C" fn set_maximized_callback(context: *mut c_void) {
unsafe {
let context_ptr = context as *mut SetMaximizedData;
{
let context = &*context_ptr;
if let Some(shared_state) = context.shared_state.upgrade() {
trace!("Locked shared state in `set_maximized`");
let mut shared_state_lock = shared_state.lock().unwrap();
// Save the standard frame sized if it is not zoomed
if !context.is_zoomed {
shared_state_lock.standard_frame = Some(NSWindow::frame(context.ns_window));
}
shared_state_lock.maximized = context.maximized;
let curr_mask = context.ns_window.styleMask();
if shared_state_lock.fullscreen.is_some() {
// Handle it in window_did_exit_fullscreen
return;
} else if curr_mask.contains(NSWindowStyleMask::NSResizableWindowMask) {
// Just use the native zoom if resizable
context.ns_window.zoom_(nil);
} else {
// if it's not resizable, we set the frame directly
let new_rect = if context.maximized {
let screen = NSScreen::mainScreen(nil);
NSScreen::visibleFrame(screen)
} else {
shared_state_lock.saved_standard_frame()
};
context.ns_window.setFrame_display_(new_rect, 0);
}
trace!("Unlocked shared state in `set_maximized`");
}
}
Box::from_raw(context_ptr);
}
}
// `setMaximized` is not thread-safe
pub unsafe fn set_maximized_async(
ns_window: id,
is_zoomed: bool,
maximized: bool,
shared_state: Weak<Mutex<SharedState>>,
) {
let context = SetMaximizedData::new_ptr(ns_window, is_zoomed, maximized, shared_state);
dispatch_async_f(
dispatch_get_main_queue(),
context as *mut _,
set_maximized_callback,
);
}
struct OrderOutData {
ns_window: id,
}
impl OrderOutData {
fn new_ptr(ns_window: id) -> *mut Self {
Box::into_raw(Box::new(OrderOutData { ns_window }))
}
}
extern "C" fn order_out_callback(context: *mut c_void) {
unsafe { unsafe {
let context_ptr = context as *mut OrderOutData; let context_ptr = context as *mut OrderOutData;
{ {
let context = &*context_ptr; let context = &*context_ptr;
context.nswindow.orderOut_(nil); context.ns_window.orderOut_(nil);
} }
Box::from_raw(context_ptr); Box::from_raw(context_ptr);
} }
} }
// `orderOut:` isn't thread-safe. Calling it from another thread actually works, // `orderOut:` isn't thread-safe. Calling it from another thread actually works,
// but with an odd delay. // but with an odd delay.
pub unsafe fn order_out_async(nswindow: id) { pub unsafe fn order_out_async(ns_window: id) {
let context = OrderOutData::new_ptr(nswindow); let context = OrderOutData::new_ptr(ns_window);
dispatch_async_f( dispatch_async_f(
dispatch_get_main_queue(), dispatch_get_main_queue(),
context as *mut _, context as *mut _,
@ -267,27 +335,27 @@ pub unsafe fn order_out_async(nswindow: id) {
} }
struct MakeKeyAndOrderFrontData { struct MakeKeyAndOrderFrontData {
nswindow: id, ns_window: id,
} }
impl MakeKeyAndOrderFrontData { impl MakeKeyAndOrderFrontData {
fn new_ptr(nswindow: id) -> *mut Self { fn new_ptr(ns_window: id) -> *mut Self {
Box::into_raw(Box::new(MakeKeyAndOrderFrontData { nswindow })) Box::into_raw(Box::new(MakeKeyAndOrderFrontData { ns_window }))
} }
} }
extern fn make_key_and_order_front_callback(context: *mut c_void) { extern "C" fn make_key_and_order_front_callback(context: *mut c_void) {
unsafe { unsafe {
let context_ptr = context as *mut MakeKeyAndOrderFrontData; let context_ptr = context as *mut MakeKeyAndOrderFrontData;
{ {
let context = &*context_ptr; let context = &*context_ptr;
context.nswindow.makeKeyAndOrderFront_(nil); context.ns_window.makeKeyAndOrderFront_(nil);
} }
Box::from_raw(context_ptr); Box::from_raw(context_ptr);
} }
} }
// `makeKeyAndOrderFront:` isn't thread-safe. Calling it from another thread // `makeKeyAndOrderFront:` isn't thread-safe. Calling it from another thread
// actually works, but with an odd delay. // actually works, but with an odd delay.
pub unsafe fn make_key_and_order_front_async(nswindow: id) { pub unsafe fn make_key_and_order_front_async(ns_window: id) {
let context = MakeKeyAndOrderFrontData::new_ptr(nswindow); let context = MakeKeyAndOrderFrontData::new_ptr(ns_window);
dispatch_async_f( dispatch_async_f(
dispatch_get_main_queue(), dispatch_get_main_queue(),
context as *mut _, context as *mut _,
@ -295,21 +363,53 @@ pub unsafe fn make_key_and_order_front_async(nswindow: id) {
); );
} }
struct CloseData { struct SetTitleData {
nswindow: id, ns_window: id,
title: String,
} }
impl CloseData { impl SetTitleData {
fn new_ptr(nswindow: id) -> *mut Self { fn new_ptr(ns_window: id, title: String) -> *mut Self {
Box::into_raw(Box::new(CloseData { nswindow })) Box::into_raw(Box::new(SetTitleData { ns_window, title }))
} }
} }
extern fn close_callback(context: *mut c_void) { extern "C" fn set_title_callback(context: *mut c_void) {
unsafe {
let context_ptr = context as *mut SetTitleData;
{
let context = &*context_ptr;
let title = IdRef::new(NSString::alloc(nil).init_str(&context.title));
context.ns_window.setTitle_(*title);
}
Box::from_raw(context_ptr);
}
}
// `setTitle:` isn't thread-safe. Calling it from another thread invalidates the
// window drag regions, which throws an exception when not done in the main
// thread
pub unsafe fn set_title_async(ns_window: id, title: String) {
let context = SetTitleData::new_ptr(ns_window, title);
dispatch_async_f(
dispatch_get_main_queue(),
context as *mut _,
set_title_callback,
);
}
struct CloseData {
ns_window: id,
}
impl CloseData {
fn new_ptr(ns_window: id) -> *mut Self {
Box::into_raw(Box::new(CloseData { ns_window }))
}
}
extern "C" fn close_callback(context: *mut c_void) {
unsafe { unsafe {
let context_ptr = context as *mut CloseData; let context_ptr = context as *mut CloseData;
{ {
let context = &*context_ptr; let context = &*context_ptr;
let pool = NSAutoreleasePool::new(nil); let pool = NSAutoreleasePool::new(nil);
context.nswindow.close(); context.ns_window.close();
pool.drain(); pool.drain();
} }
Box::from_raw(context_ptr); Box::from_raw(context_ptr);
@ -317,11 +417,7 @@ extern fn close_callback(context: *mut c_void) {
} }
// `close:` is thread-safe, but we want the event to be triggered from the main // `close:` is thread-safe, but we want the event to be triggered from the main
// thread. Though, it's a good idea to look into that more... // thread. Though, it's a good idea to look into that more...
pub unsafe fn close_async(nswindow: id) { pub unsafe fn close_async(ns_window: id) {
let context = CloseData::new_ptr(nswindow); let context = CloseData::new_ptr(ns_window);
dispatch_async_f( dispatch_async_f(dispatch_get_main_queue(), context as *mut _, close_callback);
dispatch_get_main_queue(),
context as *mut _,
close_callback,
);
} }

View file

@ -1,10 +1,11 @@
use cocoa::{ use cocoa::{
appkit::NSImage, base::{id, nil}, appkit::NSImage,
base::{id, nil},
foundation::{NSDictionary, NSPoint, NSString}, foundation::{NSDictionary, NSPoint, NSString},
}; };
use objc::runtime::Sel; use objc::runtime::Sel;
use window::CursorIcon; use crate::window::CursorIcon;
pub enum Cursor { pub enum Cursor {
Native(&'static str), Native(&'static str),
@ -22,7 +23,9 @@ impl From<CursorIcon> for Cursor {
CursorIcon::VerticalText => Cursor::Native("IBeamCursorForVerticalLayout"), CursorIcon::VerticalText => Cursor::Native("IBeamCursorForVerticalLayout"),
CursorIcon::Copy => Cursor::Native("dragCopyCursor"), CursorIcon::Copy => Cursor::Native("dragCopyCursor"),
CursorIcon::Alias => Cursor::Native("dragLinkCursor"), CursorIcon::Alias => Cursor::Native("dragLinkCursor"),
CursorIcon::NotAllowed | CursorIcon::NoDrop => Cursor::Native("operationNotAllowedCursor"), CursorIcon::NotAllowed | CursorIcon::NoDrop => {
Cursor::Native("operationNotAllowedCursor")
}
CursorIcon::ContextMenu => Cursor::Native("contextualMenuCursor"), CursorIcon::ContextMenu => Cursor::Native("contextualMenuCursor"),
CursorIcon::Crosshair => Cursor::Native("crosshairCursor"), CursorIcon::Crosshair => Cursor::Native("crosshairCursor"),
CursorIcon::EResize => Cursor::Native("resizeRightCursor"), CursorIcon::EResize => Cursor::Native("resizeRightCursor"),
@ -52,7 +55,9 @@ impl From<CursorIcon> for Cursor {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=522349 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=522349
// This is the wrong semantics for `Wait`, but it's the same as // This is the wrong semantics for `Wait`, but it's the same as
// what's used in Safari and Chrome. // what's used in Safari and Chrome.
CursorIcon::Wait | CursorIcon::Progress => Cursor::Undocumented("busyButClickableCursor"), CursorIcon::Wait | CursorIcon::Progress => {
Cursor::Undocumented("busyButClickableCursor")
}
// For the rest, we can just snatch the cursors from WebKit... // For the rest, we can just snatch the cursors from WebKit...
// They fit the style of the native cursors, and will seem // They fit the style of the native cursors, and will seem
@ -75,19 +80,19 @@ impl Cursor {
match self { match self {
Cursor::Native(cursor_name) => { Cursor::Native(cursor_name) => {
let sel = Sel::register(cursor_name); let sel = Sel::register(cursor_name);
msg_send![class!(NSCursor), performSelector:sel] msg_send![class!(NSCursor), performSelector: sel]
}, }
Cursor::Undocumented(cursor_name) => { Cursor::Undocumented(cursor_name) => {
let class = class!(NSCursor); let class = class!(NSCursor);
let sel = Sel::register(cursor_name); let sel = Sel::register(cursor_name);
let sel = if msg_send![class, respondsToSelector:sel] { let sel = if msg_send![class, respondsToSelector: sel] {
sel sel
} else { } else {
warn!("Cursor `{}` appears to be invalid", cursor_name); warn!("Cursor `{}` appears to be invalid", cursor_name);
sel!(arrowCursor) sel!(arrowCursor)
}; };
msg_send![class, performSelector:sel] msg_send![class, performSelector: sel]
}, }
Cursor::WebKit(cursor_name) => load_webkit_cursor(cursor_name), Cursor::WebKit(cursor_name) => load_webkit_cursor(cursor_name),
} }
} }
@ -104,27 +109,15 @@ pub unsafe fn load_webkit_cursor(cursor_name: &str) -> id {
let key_x = NSString::alloc(nil).init_str("hotx"); let key_x = NSString::alloc(nil).init_str("hotx");
let key_y = NSString::alloc(nil).init_str("hoty"); let key_y = NSString::alloc(nil).init_str("hoty");
let cursor_path: id = msg_send![cursor_root, let cursor_path: id = msg_send![cursor_root, stringByAppendingPathComponent: cursor_name];
stringByAppendingPathComponent:cursor_name let pdf_path: id = msg_send![cursor_path, stringByAppendingPathComponent: cursor_pdf];
]; let info_path: id = msg_send![cursor_path, stringByAppendingPathComponent: cursor_plist];
let pdf_path: id = msg_send![cursor_path,
stringByAppendingPathComponent:cursor_pdf
];
let info_path: id = msg_send![cursor_path,
stringByAppendingPathComponent:cursor_plist
];
let image = NSImage::alloc(nil).initByReferencingFile_(pdf_path); let image = NSImage::alloc(nil).initByReferencingFile_(pdf_path);
let info = NSDictionary::dictionaryWithContentsOfFile_( let info = NSDictionary::dictionaryWithContentsOfFile_(nil, info_path);
nil,
info_path,
);
let x = info.valueForKey_(key_x); let x = info.valueForKey_(key_x);
let y = info.valueForKey_(key_y); let y = info.valueForKey_(key_y);
let point = NSPoint::new( let point = NSPoint::new(msg_send![x, doubleValue], msg_send![y, doubleValue]);
msg_send![x, doubleValue],
msg_send![y, doubleValue],
);
let cursor: id = msg_send![class!(NSCursor), alloc]; let cursor: id = msg_send![class!(NSCursor), alloc];
msg_send![cursor, msg_send![cursor,
initWithImage:image initWithImage:image

View file

@ -1,14 +0,0 @@
use cocoa::base::{id, nil};
pub trait IntoOption: Sized {
fn into_option(self) -> Option<Self>;
}
impl IntoOption for id {
fn into_option(self) -> Option<Self> {
match self != nil {
true => Some(self),
false => None,
}
}
}

View file

@ -1,10 +1,9 @@
mod async; mod r#async;
mod cursor; mod cursor;
pub use self::{async::*, cursor::*}; pub use self::{cursor::*, r#async::*};
use std::ops::Deref; use std::ops::{BitAnd, Deref};
use std::ops::BitAnd;
use cocoa::{ use cocoa::{
appkit::{NSApp, NSWindowStyleMask}, appkit::{NSApp, NSWindowStyleMask},
@ -12,17 +11,17 @@ use cocoa::{
foundation::{NSAutoreleasePool, NSRect, NSUInteger}, foundation::{NSAutoreleasePool, NSRect, NSUInteger},
}; };
use core_graphics::display::CGDisplay; use core_graphics::display::CGDisplay;
use objc::runtime::{BOOL, Class, Object, Sel, YES}; use objc::runtime::{Class, Object, Sel, BOOL, YES};
use platform_impl::platform::ffi; use crate::platform_impl::platform::ffi;
// Replace with `!` once stable // Replace with `!` once stable
#[derive(Debug)] #[derive(Debug)]
pub enum Never {} pub enum Never {}
pub fn has_flag<T>(bitset: T, flag: T) -> bool pub fn has_flag<T>(bitset: T, flag: T) -> bool
where T: where
Copy + PartialEq + BitAnd<T, Output = T> T: Copy + PartialEq + BitAnd<T, Output = T>,
{ {
bitset & flag == flag bitset & flag == flag
} }
@ -48,7 +47,11 @@ impl IdRef {
} }
pub fn non_nil(self) -> Option<IdRef> { pub fn non_nil(self) -> Option<IdRef> {
if self.0 == nil { None } else { Some(self) } if self.0 == nil {
None
} else {
Some(self)
}
} }
} }
@ -94,16 +97,16 @@ pub unsafe fn superclass<'a>(this: &'a Object) -> &'a Class {
pub unsafe fn create_input_context(view: id) -> IdRef { pub unsafe fn create_input_context(view: id) -> IdRef {
let input_context: id = msg_send![class!(NSTextInputContext), alloc]; let input_context: id = msg_send![class!(NSTextInputContext), alloc];
let input_context: id = msg_send![input_context, initWithClient:view]; let input_context: id = msg_send![input_context, initWithClient: view];
IdRef::new(input_context) IdRef::new(input_context)
} }
#[allow(dead_code)] #[allow(dead_code)]
pub unsafe fn open_emoji_picker() { pub unsafe fn open_emoji_picker() {
let () = msg_send![NSApp(), orderFrontCharacterPalette:nil]; let () = msg_send![NSApp(), orderFrontCharacterPalette: nil];
} }
pub extern fn yes(_: &Object, _: Sel) -> BOOL { pub extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
YES YES
} }
@ -120,4 +123,3 @@ pub unsafe fn toggle_style_mask(window: id, view: id, mask: NSWindowStyleMask, o
// If we don't do this, key handling will break. Therefore, never call `setStyleMask` directly! // If we don't do this, key handling will break. Therefore, never call `setStyleMask` directly!
window.makeFirstResponder_(view); window.makeFirstResponder_(view);
} }

View file

@ -1,26 +1,39 @@
use std::{ use std::{
boxed::Box, collections::VecDeque, os::raw::*, slice, str, boxed::Box,
collections::VecDeque,
os::raw::*,
slice, str,
sync::{Arc, Mutex, Weak}, sync::{Arc, Mutex, Weak},
}; };
use cocoa::{ use cocoa::{
appkit::{NSApp, NSEvent, NSEventModifierFlags, NSEventPhase, NSView, NSWindow}, appkit::{NSApp, NSEvent, NSEventModifierFlags, NSEventPhase, NSView, NSWindow},
base::{id, nil}, foundation::{NSPoint, NSRect, NSSize, NSString, NSUInteger}, base::{id, nil},
foundation::{NSPoint, NSRect, NSSize, NSString, NSUInteger},
};
use objc::{
declare::ClassDecl,
runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
}; };
use objc::{declare::ClassDecl, runtime::{BOOL, Class, NO, Object, Protocol, Sel, YES}};
use { use crate::{
event::{ event::{
DeviceEvent, ElementState, Event, KeyboardInput, MouseButton, DeviceEvent, ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, TouchPhase,
MouseScrollDelta, TouchPhase, VirtualKeyCode, WindowEvent, VirtualKeyCode, WindowEvent,
},
platform_impl::platform::{
app_state::AppState,
event::{
char_to_keycode, check_function_keys, event_mods, get_scancode, modifier_event,
scancode_to_keycode,
},
ffi::*,
util::{self, IdRef},
window::get_window_id,
DEVICE_ID,
}, },
window::WindowId, window::WindowId,
}; };
use platform_impl::platform::{
app_state::AppState, DEVICE_ID,
event::{check_function_keys, event_mods, modifier_event, char_to_keycode, get_scancode, scancode_to_keycode},
util::{self, IdRef}, ffi::*, window::get_window_id,
};
#[derive(Default)] #[derive(Default)]
struct Modifiers { struct Modifiers {
@ -31,7 +44,7 @@ struct Modifiers {
} }
struct ViewState { struct ViewState {
nswindow: id, ns_window: id,
pub cursor: Arc<Mutex<util::Cursor>>, pub cursor: Arc<Mutex<util::Cursor>>,
ime_spot: Option<(f64, f64)>, ime_spot: Option<(f64, f64)>,
raw_characters: Option<String>, raw_characters: Option<String>,
@ -39,11 +52,11 @@ struct ViewState {
modifiers: Modifiers, modifiers: Modifiers,
} }
pub fn new_view(nswindow: id) -> (IdRef, Weak<Mutex<util::Cursor>>) { pub fn new_view(ns_window: id) -> (IdRef, Weak<Mutex<util::Cursor>>) {
let cursor = Default::default(); let cursor = Default::default();
let cursor_access = Arc::downgrade(&cursor); let cursor_access = Arc::downgrade(&cursor);
let state = ViewState { let state = ViewState {
nswindow, ns_window,
cursor, cursor,
ime_spot: None, ime_spot: None,
raw_characters: None, raw_characters: None,
@ -53,18 +66,19 @@ pub fn new_view(nswindow: id) -> (IdRef, Weak<Mutex<util::Cursor>>) {
unsafe { unsafe {
// This is free'd in `dealloc` // This is free'd in `dealloc`
let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void; let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void;
let nsview: id = msg_send![VIEW_CLASS.0, alloc]; let ns_view: id = msg_send![VIEW_CLASS.0, alloc];
(IdRef::new(msg_send![nsview, initWithWinit:state_ptr]), cursor_access) (
IdRef::new(msg_send![ns_view, initWithWinit: state_ptr]),
cursor_access,
)
} }
} }
pub unsafe fn set_ime_position(nsview: id, input_context: id, x: f64, y: f64) { pub unsafe fn set_ime_position(ns_view: id, input_context: id, x: f64, y: f64) {
let state_ptr: *mut c_void = *(*nsview).get_mut_ivar("winitState"); let state_ptr: *mut c_void = *(*ns_view).get_mut_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState); let state = &mut *(state_ptr as *mut ViewState);
let content_rect = NSWindow::contentRectForFrameRect_( let content_rect =
state.nswindow, NSWindow::contentRectForFrameRect_(state.ns_window, NSWindow::frame(state.ns_window));
NSWindow::frame(state.nswindow),
);
let base_x = content_rect.origin.x as f64; let base_x = content_rect.origin.x as f64;
let base_y = (content_rect.origin.y + content_rect.size.height) as f64; let base_y = (content_rect.origin.y + content_rect.size.height) as f64;
state.ime_spot = Some((base_x + x, base_y - y)); state.ime_spot = Some((base_x + x, base_y - y));
@ -79,161 +93,148 @@ lazy_static! {
static ref VIEW_CLASS: ViewClass = unsafe { static ref VIEW_CLASS: ViewClass = unsafe {
let superclass = class!(NSView); let superclass = class!(NSView);
let mut decl = ClassDecl::new("WinitView", superclass).unwrap(); let mut decl = ClassDecl::new("WinitView", superclass).unwrap();
decl.add_method( decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel));
sel!(dealloc),
dealloc as extern fn(&Object, Sel),
);
decl.add_method( decl.add_method(
sel!(initWithWinit:), sel!(initWithWinit:),
init_with_winit as extern fn(&Object, Sel, *mut c_void) -> id, init_with_winit as extern "C" fn(&Object, Sel, *mut c_void) -> id,
); );
decl.add_method( decl.add_method(
sel!(viewDidMoveToWindow), sel!(viewDidMoveToWindow),
view_did_move_to_window as extern fn(&Object, Sel), view_did_move_to_window as extern "C" fn(&Object, Sel),
); );
decl.add_method( decl.add_method(
sel!(drawRect:), sel!(drawRect:),
draw_rect as extern fn(&Object, Sel, id), draw_rect as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(acceptsFirstResponder), sel!(acceptsFirstResponder),
accepts_first_responder as extern fn(&Object, Sel) -> BOOL, accepts_first_responder as extern "C" fn(&Object, Sel) -> BOOL,
); );
decl.add_method( decl.add_method(
sel!(touchBar), sel!(touchBar),
touch_bar as extern fn(&Object, Sel) -> BOOL, touch_bar as extern "C" fn(&Object, Sel) -> BOOL,
); );
decl.add_method( decl.add_method(
sel!(resetCursorRects), sel!(resetCursorRects),
reset_cursor_rects as extern fn(&Object, Sel), reset_cursor_rects as extern "C" fn(&Object, Sel),
); );
decl.add_method( decl.add_method(
sel!(hasMarkedText), sel!(hasMarkedText),
has_marked_text as extern fn(&Object, Sel) -> BOOL, has_marked_text as extern "C" fn(&Object, Sel) -> BOOL,
); );
decl.add_method( decl.add_method(
sel!(markedRange), sel!(markedRange),
marked_range as extern fn(&Object, Sel) -> NSRange, marked_range as extern "C" fn(&Object, Sel) -> NSRange,
); );
decl.add_method( decl.add_method(
sel!(selectedRange), sel!(selectedRange),
selected_range as extern fn(&Object, Sel) -> NSRange, selected_range as extern "C" fn(&Object, Sel) -> NSRange,
); );
decl.add_method( decl.add_method(
sel!(setMarkedText:selectedRange:replacementRange:), sel!(setMarkedText:selectedRange:replacementRange:),
set_marked_text as extern fn(&mut Object, Sel, id, NSRange, NSRange), set_marked_text as extern "C" fn(&mut Object, Sel, id, NSRange, NSRange),
);
decl.add_method(
sel!(unmarkText),
unmark_text as extern fn(&Object, Sel),
); );
decl.add_method(sel!(unmarkText), unmark_text as extern "C" fn(&Object, Sel));
decl.add_method( decl.add_method(
sel!(validAttributesForMarkedText), sel!(validAttributesForMarkedText),
valid_attributes_for_marked_text as extern fn(&Object, Sel) -> id, valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id,
); );
decl.add_method( decl.add_method(
sel!(attributedSubstringForProposedRange:actualRange:), sel!(attributedSubstringForProposedRange:actualRange:),
attributed_substring_for_proposed_range as extern fn(&Object, Sel, NSRange, *mut c_void) -> id, attributed_substring_for_proposed_range
as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> id,
); );
decl.add_method( decl.add_method(
sel!(insertText:replacementRange:), sel!(insertText:replacementRange:),
insert_text as extern fn(&Object, Sel, id, NSRange), insert_text as extern "C" fn(&Object, Sel, id, NSRange),
); );
decl.add_method( decl.add_method(
sel!(characterIndexForPoint:), sel!(characterIndexForPoint:),
character_index_for_point as extern fn(&Object, Sel, NSPoint) -> NSUInteger, character_index_for_point as extern "C" fn(&Object, Sel, NSPoint) -> NSUInteger,
); );
decl.add_method( decl.add_method(
sel!(firstRectForCharacterRange:actualRange:), sel!(firstRectForCharacterRange:actualRange:),
first_rect_for_character_range as extern fn(&Object, Sel, NSRange, *mut c_void) -> NSRect, first_rect_for_character_range
as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> NSRect,
); );
decl.add_method( decl.add_method(
sel!(doCommandBySelector:), sel!(doCommandBySelector:),
do_command_by_selector as extern fn(&Object, Sel, Sel), do_command_by_selector as extern "C" fn(&Object, Sel, Sel),
);
decl.add_method(
sel!(keyDown:),
key_down as extern fn(&Object, Sel, id),
);
decl.add_method(
sel!(keyUp:),
key_up as extern fn(&Object, Sel, id),
); );
decl.add_method(sel!(keyDown:), key_down as extern "C" fn(&Object, Sel, id));
decl.add_method(sel!(keyUp:), key_up as extern "C" fn(&Object, Sel, id));
decl.add_method( decl.add_method(
sel!(flagsChanged:), sel!(flagsChanged:),
flags_changed as extern fn(&Object, Sel, id), flags_changed as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(insertTab:), sel!(insertTab:),
insert_tab as extern fn(&Object, Sel, id), insert_tab as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(insertBackTab:), sel!(insertBackTab:),
insert_back_tab as extern fn(&Object, Sel, id), insert_back_tab as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(mouseDown:), sel!(mouseDown:),
mouse_down as extern fn(&Object, Sel, id), mouse_down as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(mouseUp:),
mouse_up as extern fn(&Object, Sel, id),
); );
decl.add_method(sel!(mouseUp:), mouse_up as extern "C" fn(&Object, Sel, id));
decl.add_method( decl.add_method(
sel!(rightMouseDown:), sel!(rightMouseDown:),
right_mouse_down as extern fn(&Object, Sel, id), right_mouse_down as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(rightMouseUp:), sel!(rightMouseUp:),
right_mouse_up as extern fn(&Object, Sel, id), right_mouse_up as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(otherMouseDown:), sel!(otherMouseDown:),
other_mouse_down as extern fn(&Object, Sel, id), other_mouse_down as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(otherMouseUp:), sel!(otherMouseUp:),
other_mouse_up as extern fn(&Object, Sel, id), other_mouse_up as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(mouseMoved:), sel!(mouseMoved:),
mouse_moved as extern fn(&Object, Sel, id), mouse_moved as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(mouseDragged:), sel!(mouseDragged:),
mouse_dragged as extern fn(&Object, Sel, id), mouse_dragged as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(rightMouseDragged:), sel!(rightMouseDragged:),
right_mouse_dragged as extern fn(&Object, Sel, id), right_mouse_dragged as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(otherMouseDragged:), sel!(otherMouseDragged:),
other_mouse_dragged as extern fn(&Object, Sel, id), other_mouse_dragged as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(mouseEntered:), sel!(mouseEntered:),
mouse_entered as extern fn(&Object, Sel, id), mouse_entered as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(mouseExited:), sel!(mouseExited:),
mouse_exited as extern fn(&Object, Sel, id), mouse_exited as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(scrollWheel:), sel!(scrollWheel:),
scroll_wheel as extern fn(&Object, Sel, id), scroll_wheel as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(pressureChangeWithEvent:), sel!(pressureChangeWithEvent:),
pressure_change_with_event as extern fn(&Object, Sel, id), pressure_change_with_event as extern "C" fn(&Object, Sel, id),
); );
decl.add_method( decl.add_method(
sel!(_wantsKeyDownForEvent:), sel!(_wantsKeyDownForEvent:),
wants_key_down_for_event as extern fn(&Object, Sel, id) -> BOOL, wants_key_down_for_event as extern "C" fn(&Object, Sel, id) -> BOOL,
); );
decl.add_method( decl.add_method(
sel!(cancelOperation:), sel!(cancelOperation:),
cancel_operation as extern fn(&Object, Sel, id), cancel_operation as extern "C" fn(&Object, Sel, id),
); );
decl.add_ivar::<*mut c_void>("winitState"); decl.add_ivar::<*mut c_void>("winitState");
decl.add_ivar::<id>("markedText"); decl.add_ivar::<id>("markedText");
@ -243,7 +244,7 @@ lazy_static! {
}; };
} }
extern fn dealloc(this: &Object, _sel: Sel) { extern "C" fn dealloc(this: &Object, _sel: Sel) {
unsafe { unsafe {
let state: *mut c_void = *this.get_ivar("winitState"); let state: *mut c_void = *this.get_ivar("winitState");
let marked_text: id = *this.get_ivar("markedText"); let marked_text: id = *this.get_ivar("markedText");
@ -252,21 +253,20 @@ extern fn dealloc(this: &Object, _sel: Sel) {
} }
} }
extern fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> id { extern "C" fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> id {
unsafe { unsafe {
let this: id = msg_send![this, init]; let this: id = msg_send![this, init];
if this != nil { if this != nil {
(*this).set_ivar("winitState", state); (*this).set_ivar("winitState", state);
let marked_text = <id as NSMutableAttributedString>::init( let marked_text =
NSMutableAttributedString::alloc(nil), <id as NSMutableAttributedString>::init(NSMutableAttributedString::alloc(nil));
);
(*this).set_ivar("markedText", marked_text); (*this).set_ivar("markedText", marked_text);
} }
this this
} }
} }
extern fn view_did_move_to_window(this: &Object, _sel: Sel) { extern "C" fn view_did_move_to_window(this: &Object, _sel: Sel) {
trace!("Triggered `viewDidMoveToWindow`"); trace!("Triggered `viewDidMoveToWindow`");
unsafe { unsafe {
let rect: NSRect = msg_send![this, visibleRect]; let rect: NSRect = msg_send![this, visibleRect];
@ -280,30 +280,30 @@ extern fn view_did_move_to_window(this: &Object, _sel: Sel) {
trace!("Completed `viewDidMoveToWindow`"); trace!("Completed `viewDidMoveToWindow`");
} }
extern fn draw_rect(this: &Object, _sel: Sel, rect: id) { extern "C" fn draw_rect(this: &Object, _sel: Sel, rect: id) {
unsafe { unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState); let state = &mut *(state_ptr as *mut ViewState);
AppState::queue_redraw(WindowId(get_window_id(state.nswindow))); AppState::queue_redraw(WindowId(get_window_id(state.ns_window)));
let superclass = util::superclass(this); let superclass = util::superclass(this);
let () = msg_send![super(this, superclass), drawRect:rect]; let () = msg_send![super(this, superclass), drawRect: rect];
} }
} }
extern fn accepts_first_responder(_this: &Object, _sel: Sel) -> BOOL { extern "C" fn accepts_first_responder(_this: &Object, _sel: Sel) -> BOOL {
YES YES
} }
// This is necessary to prevent a beefy terminal error on MacBook Pros: // This is necessary to prevent a beefy terminal error on MacBook Pros:
// IMKInputSession [0x7fc573576ff0 presentFunctionRowItemTextInputViewWithEndpoint:completionHandler:] : [self textInputContext]=0x7fc573558e10 *NO* NSRemoteViewController to client, NSError=Error Domain=NSCocoaErrorDomain Code=4099 "The connection from pid 0 was invalidated from this process." UserInfo={NSDebugDescription=The connection from pid 0 was invalidated from this process.}, com.apple.inputmethod.EmojiFunctionRowItem // IMKInputSession [0x7fc573576ff0 presentFunctionRowItemTextInputViewWithEndpoint:completionHandler:] : [self textInputContext]=0x7fc573558e10 *NO* NSRemoteViewController to client, NSError=Error Domain=NSCocoaErrorDomain Code=4099 "The connection from pid 0 was invalidated from this process." UserInfo={NSDebugDescription=The connection from pid 0 was invalidated from this process.}, com.apple.inputmethod.EmojiFunctionRowItem
// TODO: Add an API extension for using `NSTouchBar` // TODO: Add an API extension for using `NSTouchBar`
extern fn touch_bar(_this: &Object, _sel: Sel) -> BOOL { extern "C" fn touch_bar(_this: &Object, _sel: Sel) -> BOOL {
NO NO
} }
extern fn reset_cursor_rects(this: &Object, _sel: Sel) { extern "C" fn reset_cursor_rects(this: &Object, _sel: Sel) {
unsafe { unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState); let state = &mut *(state_ptr as *mut ViewState);
@ -317,8 +317,7 @@ extern fn reset_cursor_rects(this: &Object, _sel: Sel) {
} }
} }
extern "C" fn has_marked_text(this: &Object, _sel: Sel) -> BOOL {
extern fn has_marked_text(this: &Object, _sel: Sel) -> BOOL {
unsafe { unsafe {
trace!("Triggered `hasMarkedText`"); trace!("Triggered `hasMarkedText`");
let marked_text: id = *this.get_ivar("markedText"); let marked_text: id = *this.get_ivar("markedText");
@ -327,7 +326,7 @@ extern fn has_marked_text(this: &Object, _sel: Sel) -> BOOL {
} }
} }
extern fn marked_range(this: &Object, _sel: Sel) -> NSRange { extern "C" fn marked_range(this: &Object, _sel: Sel) -> NSRange {
unsafe { unsafe {
trace!("Triggered `markedRange`"); trace!("Triggered `markedRange`");
let marked_text: id = *this.get_ivar("markedText"); let marked_text: id = *this.get_ivar("markedText");
@ -341,13 +340,13 @@ extern fn marked_range(this: &Object, _sel: Sel) -> NSRange {
} }
} }
extern fn selected_range(_this: &Object, _sel: Sel) -> NSRange { extern "C" fn selected_range(_this: &Object, _sel: Sel) -> NSRange {
trace!("Triggered `selectedRange`"); trace!("Triggered `selectedRange`");
trace!("Completed `selectedRange`"); trace!("Completed `selectedRange`");
util::EMPTY_RANGE util::EMPTY_RANGE
} }
extern fn set_marked_text( extern "C" fn set_marked_text(
this: &mut Object, this: &mut Object,
_sel: Sel, _sel: Sel,
string: id, string: id,
@ -359,7 +358,7 @@ extern fn set_marked_text(
let marked_text_ref: &mut id = this.get_mut_ivar("markedText"); let marked_text_ref: &mut id = this.get_mut_ivar("markedText");
let _: () = msg_send![(*marked_text_ref), release]; let _: () = msg_send![(*marked_text_ref), release];
let marked_text = NSMutableAttributedString::alloc(nil); let marked_text = NSMutableAttributedString::alloc(nil);
let has_attr = msg_send![string, isKindOfClass:class!(NSAttributedString)]; let has_attr = msg_send![string, isKindOfClass: class!(NSAttributedString)];
if has_attr { if has_attr {
marked_text.initWithAttributedString(string); marked_text.initWithAttributedString(string);
} else { } else {
@ -370,7 +369,7 @@ extern fn set_marked_text(
trace!("Completed `setMarkedText`"); trace!("Completed `setMarkedText`");
} }
extern fn unmark_text(this: &Object, _sel: Sel) { extern "C" fn unmark_text(this: &Object, _sel: Sel) {
trace!("Triggered `unmarkText`"); trace!("Triggered `unmarkText`");
unsafe { unsafe {
let marked_text: id = *this.get_ivar("markedText"); let marked_text: id = *this.get_ivar("markedText");
@ -382,13 +381,13 @@ extern fn unmark_text(this: &Object, _sel: Sel) {
trace!("Completed `unmarkText`"); trace!("Completed `unmarkText`");
} }
extern fn valid_attributes_for_marked_text(_this: &Object, _sel: Sel) -> id { extern "C" fn valid_attributes_for_marked_text(_this: &Object, _sel: Sel) -> id {
trace!("Triggered `validAttributesForMarkedText`"); trace!("Triggered `validAttributesForMarkedText`");
trace!("Completed `validAttributesForMarkedText`"); trace!("Completed `validAttributesForMarkedText`");
unsafe { msg_send![class!(NSArray), array] } unsafe { msg_send![class!(NSArray), array] }
} }
extern fn attributed_substring_for_proposed_range( extern "C" fn attributed_substring_for_proposed_range(
_this: &Object, _this: &Object,
_sel: Sel, _sel: Sel,
_range: NSRange, _range: NSRange,
@ -399,13 +398,13 @@ extern fn attributed_substring_for_proposed_range(
nil nil
} }
extern fn character_index_for_point(_this: &Object, _sel: Sel, _point: NSPoint) -> NSUInteger { extern "C" fn character_index_for_point(_this: &Object, _sel: Sel, _point: NSPoint) -> NSUInteger {
trace!("Triggered `characterIndexForPoint`"); trace!("Triggered `characterIndexForPoint`");
trace!("Completed `characterIndexForPoint`"); trace!("Completed `characterIndexForPoint`");
0 0
} }
extern fn first_rect_for_character_range( extern "C" fn first_rect_for_character_range(
this: &Object, this: &Object,
_sel: Sel, _sel: Sel,
_range: NSRange, _range: NSRange,
@ -417,28 +416,25 @@ extern fn first_rect_for_character_range(
let state = &mut *(state_ptr as *mut ViewState); let state = &mut *(state_ptr as *mut ViewState);
let (x, y) = state.ime_spot.unwrap_or_else(|| { let (x, y) = state.ime_spot.unwrap_or_else(|| {
let content_rect = NSWindow::contentRectForFrameRect_( let content_rect = NSWindow::contentRectForFrameRect_(
state.nswindow, state.ns_window,
NSWindow::frame(state.nswindow), NSWindow::frame(state.ns_window),
); );
let x = content_rect.origin.x; let x = content_rect.origin.x;
let y = util::bottom_left_to_top_left(content_rect); let y = util::bottom_left_to_top_left(content_rect);
(x, y) (x, y)
}); });
trace!("Completed `firstRectForCharacterRange`"); trace!("Completed `firstRectForCharacterRange`");
NSRect::new( NSRect::new(NSPoint::new(x as _, y as _), NSSize::new(0.0, 0.0))
NSPoint::new(x as _, y as _),
NSSize::new(0.0, 0.0),
)
} }
} }
extern fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange) { extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange) {
trace!("Triggered `insertText`"); trace!("Triggered `insertText`");
unsafe { unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState); let state = &mut *(state_ptr as *mut ViewState);
let has_attr = msg_send![string, isKindOfClass:class!(NSAttributedString)]; let has_attr = msg_send![string, isKindOfClass: class!(NSAttributedString)];
let characters = if has_attr { let characters = if has_attr {
// This is a *mut NSAttributedString // This is a *mut NSAttributedString
msg_send![string, string] msg_send![string, string]
@ -447,10 +443,8 @@ extern fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range:
string string
}; };
let slice = slice::from_raw_parts( let slice =
characters.UTF8String() as *const c_uchar, slice::from_raw_parts(characters.UTF8String() as *const c_uchar, characters.len());
characters.len(),
);
let string = str::from_utf8_unchecked(slice); let string = str::from_utf8_unchecked(slice);
state.is_key_down = true; state.is_key_down = true;
@ -460,7 +454,7 @@ extern fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range:
let mut events = VecDeque::with_capacity(characters.len()); let mut events = VecDeque::with_capacity(characters.len());
for character in string.chars() { for character in string.chars() {
events.push_back(Event::WindowEvent { events.push_back(Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)), window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::ReceivedCharacter(character), event: WindowEvent::ReceivedCharacter(character),
}); });
} }
@ -470,7 +464,7 @@ extern fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range:
trace!("Completed `insertText`"); trace!("Completed `insertText`");
} }
extern fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) { extern "C" fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
trace!("Triggered `doCommandBySelector`"); trace!("Triggered `doCommandBySelector`");
// Basically, we're sent this message whenever a keyboard event that doesn't generate a "human readable" character // Basically, we're sent this message whenever a keyboard event that doesn't generate a "human readable" character
// happens, i.e. newlines, tabs, and Ctrl+C. // happens, i.e. newlines, tabs, and Ctrl+C.
@ -484,7 +478,7 @@ extern fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
// 1) as a reminder for how `doCommandBySelector` works // 1) as a reminder for how `doCommandBySelector` works
// 2) to make our use of carriage return explicit // 2) to make our use of carriage return explicit
events.push_back(Event::WindowEvent { events.push_back(Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)), window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::ReceivedCharacter('\r'), event: WindowEvent::ReceivedCharacter('\r'),
}); });
} else { } else {
@ -492,7 +486,7 @@ extern fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
if let Some(raw_characters) = raw_characters { if let Some(raw_characters) = raw_characters {
for character in raw_characters.chars() { for character in raw_characters.chars() {
events.push_back(Event::WindowEvent { events.push_back(Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)), window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::ReceivedCharacter(character), event: WindowEvent::ReceivedCharacter(character),
}); });
} }
@ -513,10 +507,8 @@ fn get_characters(event: id, ignore_modifiers: bool) -> String {
}; };
assert_ne!(characters, nil); assert_ne!(characters, nil);
let slice = slice::from_raw_parts( let slice =
characters.UTF8String() as *const c_uchar, slice::from_raw_parts(characters.UTF8String() as *const c_uchar, characters.len());
characters.len(),
);
let string = str::from_utf8_unchecked(slice); let string = str::from_utf8_unchecked(slice);
string.to_owned() string.to_owned()
@ -528,15 +520,15 @@ fn retrieve_keycode(event: id) -> Option<VirtualKeyCode> {
#[inline] #[inline]
fn get_code(ev: id, raw: bool) -> Option<VirtualKeyCode> { fn get_code(ev: id, raw: bool) -> Option<VirtualKeyCode> {
let characters = get_characters(ev, raw); let characters = get_characters(ev, raw);
characters.chars().next().map_or(None, |c| char_to_keycode(c)) characters
.chars()
.next()
.map_or(None, |c| char_to_keycode(c))
} }
// Cmd switches Roman letters for Dvorak-QWERTY layout, so we try modified characters first. // Cmd switches Roman letters for Dvorak-QWERTY layout, so we try modified characters first.
// If we don't get a match, then we fall back to unmodified characters. // If we don't get a match, then we fall back to unmodified characters.
let code = get_code(event, false) let code = get_code(event, false).or_else(|| get_code(event, true));
.or_else(|| {
get_code(event, true)
});
// We've checked all layout related keys, so fall through to scancode. // We've checked all layout related keys, so fall through to scancode.
// Reaching this code means that the key is layout-independent (e.g. Backspace, Return). // Reaching this code means that the key is layout-independent (e.g. Backspace, Return).
@ -546,19 +538,16 @@ fn retrieve_keycode(event: id) -> Option<VirtualKeyCode> {
// in characters property. // in characters property.
code.or_else(|| { code.or_else(|| {
let scancode = get_scancode(event); let scancode = get_scancode(event);
scancode_to_keycode(scancode) scancode_to_keycode(scancode).or_else(|| check_function_keys(&get_characters(event, true)))
.or_else(|| {
check_function_keys(&get_characters(event, true))
})
}) })
} }
extern fn key_down(this: &Object, _sel: Sel, event: id) { extern "C" fn key_down(this: &Object, _sel: Sel, event: id) {
trace!("Triggered `keyDown`"); trace!("Triggered `keyDown`");
unsafe { unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState); let state = &mut *(state_ptr as *mut ViewState);
let window_id = WindowId(get_window_id(state.nswindow)); let window_id = WindowId(get_window_id(state.ns_window));
let characters = get_characters(event, false); let characters = get_characters(event, false);
state.raw_characters = Some(characters.clone()); state.raw_characters = Some(characters.clone());
@ -601,14 +590,14 @@ extern fn key_down(this: &Object, _sel: Sel, event: id) {
// Some keys (and only *some*, with no known reason) don't trigger `insertText`, while others do... // Some keys (and only *some*, with no known reason) don't trigger `insertText`, while others do...
// So, we don't give repeats the opportunity to trigger that, since otherwise our hack will cause some // So, we don't give repeats the opportunity to trigger that, since otherwise our hack will cause some
// keys to generate twice as many characters. // keys to generate twice as many characters.
let array: id = msg_send![class!(NSArray), arrayWithObject:event]; let array: id = msg_send![class!(NSArray), arrayWithObject: event];
let _: () = msg_send![this, interpretKeyEvents:array]; let _: () = msg_send![this, interpretKeyEvents: array];
} }
} }
trace!("Completed `keyDown`"); trace!("Completed `keyDown`");
} }
extern fn key_up(this: &Object, _sel: Sel, event: id) { extern "C" fn key_up(this: &Object, _sel: Sel, event: id) {
trace!("Triggered `keyUp`"); trace!("Triggered `keyUp`");
unsafe { unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state_ptr: *mut c_void = *this.get_ivar("winitState");
@ -620,7 +609,7 @@ extern fn key_up(this: &Object, _sel: Sel, event: id) {
let virtual_keycode = retrieve_keycode(event); let virtual_keycode = retrieve_keycode(event);
let window_event = Event::WindowEvent { let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)), window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::KeyboardInput { event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID, device_id: DEVICE_ID,
input: KeyboardInput { input: KeyboardInput {
@ -637,7 +626,7 @@ extern fn key_up(this: &Object, _sel: Sel, event: id) {
trace!("Completed `keyUp`"); trace!("Completed `keyUp`");
} }
extern fn flags_changed(this: &Object, _sel: Sel, event: id) { extern "C" fn flags_changed(this: &Object, _sel: Sel, event: id) {
trace!("Triggered `flagsChanged`"); trace!("Triggered `flagsChanged`");
unsafe { unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state_ptr: *mut c_void = *this.get_ivar("winitState");
@ -683,7 +672,7 @@ extern fn flags_changed(this: &Object, _sel: Sel, event: id) {
for event in events { for event in events {
AppState::queue_event(Event::WindowEvent { AppState::queue_event(Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)), window_id: WindowId(get_window_id(state.ns_window)),
event, event,
}); });
} }
@ -691,31 +680,31 @@ extern fn flags_changed(this: &Object, _sel: Sel, event: id) {
trace!("Completed `flagsChanged`"); trace!("Completed `flagsChanged`");
} }
extern fn insert_tab(this: &Object, _sel: Sel, _sender: id) { extern "C" fn insert_tab(this: &Object, _sel: Sel, _sender: id) {
unsafe { unsafe {
let window: id = msg_send![this, window]; let window: id = msg_send![this, window];
let first_responder: id = msg_send![window, firstResponder]; let first_responder: id = msg_send![window, firstResponder];
let this_ptr = this as *const _ as *mut _; let this_ptr = this as *const _ as *mut _;
if first_responder == this_ptr { if first_responder == this_ptr {
let (): _ = msg_send![window, selectNextKeyView:this]; let (): _ = msg_send![window, selectNextKeyView: this];
} }
} }
} }
extern fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) { extern "C" fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) {
unsafe { unsafe {
let window: id = msg_send![this, window]; let window: id = msg_send![this, window];
let first_responder: id = msg_send![window, firstResponder]; let first_responder: id = msg_send![window, firstResponder];
let this_ptr = this as *const _ as *mut _; let this_ptr = this as *const _ as *mut _;
if first_responder == this_ptr { if first_responder == this_ptr {
let (): _ = msg_send![window, selectPreviousKeyView:this]; let (): _ = msg_send![window, selectPreviousKeyView: this];
} }
} }
} }
// Allows us to receive Cmd-. (the shortcut for closing a dialog) // Allows us to receive Cmd-. (the shortcut for closing a dialog)
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6
extern fn cancel_operation(this: &Object, _sel: Sel, _sender: id) { extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
trace!("Triggered `cancelOperation`"); trace!("Triggered `cancelOperation`");
unsafe { unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state_ptr: *mut c_void = *this.get_ivar("winitState");
@ -728,7 +717,7 @@ extern fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
let event: id = msg_send![NSApp(), currentEvent]; let event: id = msg_send![NSApp(), currentEvent];
let window_event = Event::WindowEvent { let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)), window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::KeyboardInput { event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID, device_id: DEVICE_ID,
input: KeyboardInput { input: KeyboardInput {
@ -751,7 +740,7 @@ fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: Elem
let state = &mut *(state_ptr as *mut ViewState); let state = &mut *(state_ptr as *mut ViewState);
let window_event = Event::WindowEvent { let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)), window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::MouseInput { event: WindowEvent::MouseInput {
device_id: DEVICE_ID, device_id: DEVICE_ID,
state: button_state, state: button_state,
@ -764,27 +753,27 @@ fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: Elem
} }
} }
extern fn mouse_down(this: &Object, _sel: Sel, event: id) { extern "C" fn mouse_down(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Left, ElementState::Pressed); mouse_click(this, event, MouseButton::Left, ElementState::Pressed);
} }
extern fn mouse_up(this: &Object, _sel: Sel, event: id) { extern "C" fn mouse_up(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Left, ElementState::Released); mouse_click(this, event, MouseButton::Left, ElementState::Released);
} }
extern fn right_mouse_down(this: &Object, _sel: Sel, event: id) { extern "C" fn right_mouse_down(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Right, ElementState::Pressed); mouse_click(this, event, MouseButton::Right, ElementState::Pressed);
} }
extern fn right_mouse_up(this: &Object, _sel: Sel, event: id) { extern "C" fn right_mouse_up(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Right, ElementState::Released); mouse_click(this, event, MouseButton::Right, ElementState::Released);
} }
extern fn other_mouse_down(this: &Object, _sel: Sel, event: id) { extern "C" fn other_mouse_down(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Middle, ElementState::Pressed); mouse_click(this, event, MouseButton::Middle, ElementState::Pressed);
} }
extern fn other_mouse_up(this: &Object, _sel: Sel, event: id) { extern "C" fn other_mouse_up(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Middle, ElementState::Released); mouse_click(this, event, MouseButton::Middle, ElementState::Released);
} }
@ -801,9 +790,10 @@ fn mouse_motion(this: &Object, event: id) {
let view_rect = NSView::frame(view); let view_rect = NSView::frame(view);
if view_point.x.is_sign_negative() if view_point.x.is_sign_negative()
|| view_point.y.is_sign_negative() || view_point.y.is_sign_negative()
|| view_point.x > view_rect.size.width || view_point.x > view_rect.size.width
|| view_point.y > view_rect.size.height { || view_point.y > view_rect.size.height
{
// Point is outside of the client area (view) // Point is outside of the client area (view)
return; return;
} }
@ -812,7 +802,7 @@ fn mouse_motion(this: &Object, event: id) {
let y = view_rect.size.height as f64 - view_point.y as f64; let y = view_rect.size.height as f64 - view_point.y as f64;
let window_event = Event::WindowEvent { let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)), window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::CursorMoved { event: WindowEvent::CursorMoved {
device_id: DEVICE_ID, device_id: DEVICE_ID,
position: (x, y).into(), position: (x, y).into(),
@ -824,31 +814,33 @@ fn mouse_motion(this: &Object, event: id) {
} }
} }
extern fn mouse_moved(this: &Object, _sel: Sel, event: id) { extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) {
mouse_motion(this, event); mouse_motion(this, event);
} }
extern fn mouse_dragged(this: &Object, _sel: Sel, event: id) { extern "C" fn mouse_dragged(this: &Object, _sel: Sel, event: id) {
mouse_motion(this, event); mouse_motion(this, event);
} }
extern fn right_mouse_dragged(this: &Object, _sel: Sel, event: id) { extern "C" fn right_mouse_dragged(this: &Object, _sel: Sel, event: id) {
mouse_motion(this, event); mouse_motion(this, event);
} }
extern fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) { extern "C" fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) {
mouse_motion(this, event); mouse_motion(this, event);
} }
extern fn mouse_entered(this: &Object, _sel: Sel, event: id) { extern "C" fn mouse_entered(this: &Object, _sel: Sel, event: id) {
trace!("Triggered `mouseEntered`"); trace!("Triggered `mouseEntered`");
unsafe { unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState); let state = &mut *(state_ptr as *mut ViewState);
let enter_event = Event::WindowEvent { let enter_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)), window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::CursorEntered { device_id: DEVICE_ID }, event: WindowEvent::CursorEntered {
device_id: DEVICE_ID,
},
}; };
let move_event = { let move_event = {
@ -861,12 +853,12 @@ extern fn mouse_entered(this: &Object, _sel: Sel, event: id) {
let x = view_point.x as f64; let x = view_point.x as f64;
let y = (view_rect.size.height - view_point.y) as f64; let y = (view_rect.size.height - view_point.y) as f64;
Event::WindowEvent { Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)), window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::CursorMoved { event: WindowEvent::CursorMoved {
device_id: DEVICE_ID, device_id: DEVICE_ID,
position: (x, y).into(), position: (x, y).into(),
modifiers: event_mods(event), modifiers: event_mods(event),
} },
} }
}; };
@ -876,15 +868,17 @@ extern fn mouse_entered(this: &Object, _sel: Sel, event: id) {
trace!("Completed `mouseEntered`"); trace!("Completed `mouseEntered`");
} }
extern fn mouse_exited(this: &Object, _sel: Sel, _event: id) { extern "C" fn mouse_exited(this: &Object, _sel: Sel, _event: id) {
trace!("Triggered `mouseExited`"); trace!("Triggered `mouseExited`");
unsafe { unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState); let state = &mut *(state_ptr as *mut ViewState);
let window_event = Event::WindowEvent { let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)), window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::CursorLeft { device_id: DEVICE_ID }, event: WindowEvent::CursorLeft {
device_id: DEVICE_ID,
},
}; };
AppState::queue_event(window_event); AppState::queue_event(window_event);
@ -892,7 +886,7 @@ extern fn mouse_exited(this: &Object, _sel: Sel, _event: id) {
trace!("Completed `mouseExited`"); trace!("Completed `mouseExited`");
} }
extern fn scroll_wheel(this: &Object, _sel: Sel, event: id) { extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
trace!("Triggered `scrollWheel`"); trace!("Triggered `scrollWheel`");
unsafe { unsafe {
let delta = { let delta = {
@ -904,7 +898,9 @@ extern fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
} }
}; };
let phase = match event.phase() { let phase = match event.phase() {
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => TouchPhase::Started, NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => {
TouchPhase::Started
}
NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended, NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended,
_ => TouchPhase::Moved, _ => TouchPhase::Moved,
}; };
@ -918,7 +914,7 @@ extern fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
let state = &mut *(state_ptr as *mut ViewState); let state = &mut *(state_ptr as *mut ViewState);
let window_event = Event::WindowEvent { let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)), window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::MouseWheel { event: WindowEvent::MouseWheel {
device_id: DEVICE_ID, device_id: DEVICE_ID,
delta, delta,
@ -933,7 +929,7 @@ extern fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
trace!("Completed `scrollWheel`"); trace!("Completed `scrollWheel`");
} }
extern fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) { extern "C" fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) {
trace!("Triggered `pressureChangeWithEvent`"); trace!("Triggered `pressureChangeWithEvent`");
unsafe { unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state_ptr: *mut c_void = *this.get_ivar("winitState");
@ -943,7 +939,7 @@ extern fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) {
let stage = event.stage(); let stage = event.stage();
let window_event = Event::WindowEvent { let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)), window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::TouchpadPressure { event: WindowEvent::TouchpadPressure {
device_id: DEVICE_ID, device_id: DEVICE_ID,
pressure, pressure,
@ -959,6 +955,6 @@ extern fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) {
// Allows us to receive Ctrl-Tab and Ctrl-Esc. // Allows us to receive Ctrl-Tab and Ctrl-Esc.
// Note that this *doesn't* help with any missing Cmd inputs. // Note that this *doesn't* help with any missing Cmd inputs.
// https://github.com/chromium/chromium/blob/a86a8a6bcfa438fa3ac2eba6f02b3ad1f8e0756f/ui/views/cocoa/bridged_content_view.mm#L816 // https://github.com/chromium/chromium/blob/a86a8a6bcfa438fa3ac2eba6f02b3ad1f8e0756f/ui/views/cocoa/bridged_content_view.mm#L816
extern fn wants_key_down_for_event(_this: &Object, _sel: Sel, _event: id) -> BOOL { extern "C" fn wants_key_down_for_event(_this: &Object, _sel: Sel, _event: id) -> BOOL {
YES YES
} }

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