Redox OS support (#2588)

* Add Redox OS support

* Simplify control flow usage

* Apply more recommendations

* Update naming to indicate that Orbital is a platform

* Adjust import order
This commit is contained in:
Jeremy Soller 2023-01-05 06:58:08 -07:00 committed by GitHub
parent 2f52c23fa9
commit 66ca445caa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1476 additions and 85 deletions

4
.github/CODEOWNERS vendored
View file

@ -35,3 +35,7 @@
# Windows # Windows
/src/platform/windows.rs @msiglreith /src/platform/windows.rs @msiglreith
/src/platform_impl/windows @msiglreith /src/platform_impl/windows @msiglreith
# Orbital (Redox OS)
/src/platform/orbital.rs @jackpot51
/src/platform_impl/orbital @jackpot51

View file

@ -34,6 +34,7 @@ jobs:
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: x11 } - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: x11 }
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: "wayland,wayland-dlopen" } - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: "wayland,wayland-dlopen" }
- { target: aarch64-linux-android, os: ubuntu-latest, options: -p winit, cmd: 'apk --', features: "android-native-activity" } - { target: aarch64-linux-android, os: ubuntu-latest, options: -p winit, cmd: 'apk --', features: "android-native-activity" }
- { target: x86_64-unknown-redox, os: ubuntu-latest, }
- { target: x86_64-apple-darwin, os: macos-latest, } - { target: x86_64-apple-darwin, os: macos-latest, }
- { target: x86_64-apple-ios, os: macos-latest, } - { target: x86_64-apple-ios, os: macos-latest, }
- { target: aarch64-apple-ios, os: macos-latest, } - { target: aarch64-apple-ios, os: macos-latest, }
@ -83,7 +84,9 @@ jobs:
- name: Build tests - name: Build tests
shell: bash shell: bash
if: matrix.rust_version != '1.60.0' if: >
!contains(matrix.platform.target, 'redox') &&
matrix.rust_version != '1.60.0'
run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
- name: Run tests - name: Run tests
@ -92,6 +95,7 @@ jobs:
!contains(matrix.platform.target, 'android') && !contains(matrix.platform.target, 'android') &&
!contains(matrix.platform.target, 'ios') && !contains(matrix.platform.target, 'ios') &&
!contains(matrix.platform.target, 'wasm32') && !contains(matrix.platform.target, 'wasm32') &&
!contains(matrix.platform.target, 'redox') &&
matrix.rust_version != '1.60.0' matrix.rust_version != '1.60.0'
run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
@ -102,7 +106,9 @@ jobs:
- name: Build tests with serde enabled - name: Build tests with serde enabled
shell: bash shell: bash
if: matrix.rust_version != '1.60.0' if: >
!contains(matrix.platform.target, 'redox') &&
matrix.rust_version != '1.60.0'
run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES
- name: Run tests with serde enabled - name: Run tests with serde enabled
shell: bash shell: bash
@ -110,5 +116,6 @@ jobs:
!contains(matrix.platform.target, 'android') && !contains(matrix.platform.target, 'android') &&
!contains(matrix.platform.target, 'ios') && !contains(matrix.platform.target, 'ios') &&
!contains(matrix.platform.target, 'wasm32') && !contains(matrix.platform.target, 'wasm32') &&
!contains(matrix.platform.target, 'redox') &&
matrix.rust_version != '1.60.0' matrix.rust_version != '1.60.0'
run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES

View file

@ -50,6 +50,7 @@ And please only add new entries to the top of this list, right below the `# Unre
- **Breaking:** On Android, switched to using [`android-activity`](https://github.com/rib/android-activity) crate as a glue layer instead of [`ndk-glue`](https://github.com/rust-windowing/android-ndk-rs/tree/master/ndk-glue). See [README.md#Android](https://github.com/rust-windowing/winit#Android) for more details. ([#2444](https://github.com/rust-windowing/winit/pull/2444)) - **Breaking:** On Android, switched to using [`android-activity`](https://github.com/rib/android-activity) crate as a glue layer instead of [`ndk-glue`](https://github.com/rust-windowing/android-ndk-rs/tree/master/ndk-glue). See [README.md#Android](https://github.com/rust-windowing/winit#Android) for more details. ([#2444](https://github.com/rust-windowing/winit/pull/2444))
- **Breaking:** Removed support for `raw-window-handle` version `0.4` - **Breaking:** Removed support for `raw-window-handle` version `0.4`
- On Wayland, `RedrawRequested` not emitted during resize. - On Wayland, `RedrawRequested` not emitted during resize.
- Added Orbital support for Redox OS
# 0.27.5 # 0.27.5

View file

@ -113,6 +113,10 @@ x11-dl = { version = "2.18.5", optional = true }
percent-encoding = { version = "2.0", optional = true } percent-encoding = { version = "2.0", optional = true }
libc = "0.2.64" libc = "0.2.64"
[target.'cfg(target_os = "redox")'.dependencies]
orbclient = { version = "0.3.42", default-features = false }
redox_syscall = "0.3"
[target.'cfg(target_arch = "wasm32")'.dependencies.web_sys] [target.'cfg(target_arch = "wasm32")'.dependencies.web_sys]
package = "web-sys" package = "web-sys"
version = "0.3.22" version = "0.3.22"

View file

@ -8,6 +8,7 @@ be used to create both games and applications. It supports the main graphical pl
- Unix - Unix
- via X11 - via X11
- via Wayland - via Wayland
- Redox OS, via Orbital
- Mobile - Mobile
- iOS - iOS
- Android - Android
@ -171,62 +172,62 @@ Legend:
- ❓: Unknown status - ❓: Unknown status
### Windowing ### Windowing
|Feature |Windows|MacOS |Linux x11 |Linux Wayland |Android|iOS |WASM | |Feature |Windows|MacOS |Linux x11 |Linux Wayland |Android|iOS |WASM |Redox OS|
|-------------------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | |-------------------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|Window initialization |✔️ |✔️ |▢[#5] |✔️ |▢[#33]|▢[#33] |✔️ | |Window initialization |✔️ |✔️ |▢[#5] |✔️ |▢[#33]|▢[#33] |✔️ |✔️ |
|Providing pointer to init OpenGL |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**| |Providing pointer to init OpenGL |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |
|Providing pointer to init Vulkan |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |**N/A**| |Providing pointer to init Vulkan |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |**N/A**|**N/A** |
|Window decorations |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**| |Window decorations |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|✔️ |
|Window decorations toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**| |Window decorations toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|Window resizing |✔️ |▢[#219]|✔️ |▢[#306] |**N/A**|**N/A**|✔️ | |Window resizing |✔️ |▢[#219]|✔️ |▢[#306] |**N/A**|**N/A**|✔️ |✔️ |
|Window resize increments |❌ |✔️ |✔️ |❌ |**N/A**|**N/A**|**N/A**| |Window resize increments |❌ |✔️ |✔️ |❌ |**N/A**|**N/A**|**N/A**|**N/A** |
|Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A | |Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A |✔️ |
|Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**| |Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|Window maximization toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**| |Window maximization toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|Window minimization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**| |Window minimization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|Fullscreen |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ | |Fullscreen |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |**N/A** |
|Fullscreen toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ | |Fullscreen toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |**N/A** |
|Exclusive fullscreen |✔️ |✔️ |✔️ |**N/A** |❌ |✔️ |**N/A**| |Exclusive fullscreen |✔️ |✔️ |✔️ |**N/A** |❌ |✔️ |**N/A**|**N/A** |
|HiDPI support |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ | |HiDPI support |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❌ |
|Popup windows |❌ |❌ |❌ |❌ |❌ |❌ |**N/A**| |Popup windows |❌ |❌ |❌ |❌ |❌ |❌ |**N/A**|**N/A** |
### System information ### System information
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM | |Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |Redox OS|
|---------------- | ----- | ---- | ------- | ----------- | ----- | ------- | -------- | |---------------- | ----- | ---- | ------- | ----------- | ----- | ------- | -------- | ------ |
|Monitor list |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**| |Monitor list |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|❌ |
|Video mode query |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**| |Video mode query |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|❌ |
### Input handling ### Input handling
|Feature |Windows |MacOS |Linux x11|Linux Wayland|Android|iOS |WASM | |Feature |Windows |MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |Redox OS|
|----------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | |----------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|Mouse events |✔️ |▢[#63] |✔️ |✔️ |**N/A**|**N/A**|✔️ | |Mouse events |✔️ |▢[#63] |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ |
|Mouse set location |✔️ |✔️ |✔️ |✔️(when locked) |**N/A**|**N/A**|**N/A**| |Mouse set location |✔️ |✔️ |✔️ |✔️(when locked) |**N/A**|**N/A**|**N/A**|**N/A** |
|Cursor locking |❌ |✔️ |❌ |✔️ |**N/A**|**N/A**|✔️ | |Cursor locking |❌ |✔️ |❌ |✔️ |**N/A**|**N/A**|✔️ |❌ |
|Cursor confining |✔️ |❌ |✔️ |✔️ |**N/A**|**N/A**|❌ | |Cursor confining |✔️ |❌ |✔️ |✔️ |**N/A**|**N/A**|❌ |❌ |
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ | |Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |**N/A** |
|Cursor hittest |✔️ |✔️ |❌ |✔️ |**N/A**|**N/A**|❌ | |Cursor hittest |✔️ |✔️ |❌ |✔️ |**N/A**|**N/A**|❌ |❌ |
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |✔️ | |Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A** |
|Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |✔️ | |Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |✔️ |**N/A** |
|Multitouch |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ | |Multitouch |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ |**N/A** |
|Keyboard events |✔️ |✔️ |✔️ |✔️ |✔️ |❌ |✔️ | |Keyboard events |✔️ |✔️ |✔️ |✔️ |✔️ |❌ |✔️ |✔️ |
|Drag & Drop |▢[#720] |▢[#720] |▢[#720] |❌[#306] |**N/A**|**N/A**|❓ | |Drag & Drop |▢[#720] |▢[#720] |▢[#720] |❌[#306] |**N/A**|**N/A**|❓ |**N/A** |
|Raw Device Events |▢[#750] |▢[#750] |▢[#750] |❌ |❌ |❌ |❓ | |Raw Device Events |▢[#750] |▢[#750] |▢[#750] |❌ |❌ |❌ |❓ |**N/A** |
|Gamepad/Joystick events |❌[#804] |❌ |❌ |❌ |❌ |❌ |❓ | |Gamepad/Joystick events |❌[#804] |❌ |❌ |❌ |❌ |❌ |❓ |**N/A** |
|Device movement events |❓ |❓ |❓ |❓ |❌ |❌ |❓ | |Device movement events |❓ |❓ |❓ |❓ |❌ |❌ |❓ |**N/A** |
|Drag window with cursor |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** | |Drag window with cursor |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |**N/A** |
### Pending API Reworks ### Pending API Reworks
Changes in the API that have been agreed upon but aren't implemented across all platforms. Changes in the API that have been agreed upon but aren't implemented across all platforms.
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM | |Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |Redox OS|
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | |------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|New API for HiDPI ([#315] [#319]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ | |New API for HiDPI ([#315] [#319]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |❓ |
|Event Loop 2.0 ([#459]) |✔️ |✔️ |❌ |✔️ |✔️ |✔️ |❓ | |Event Loop 2.0 ([#459]) |✔️ |✔️ |❌ |✔️ |✔️ |✔️ |❓ |❓ |
|Keyboard Input ([#812]) |❌ |❌ |❌ |❌ |❌ |❌ |❓ | |Keyboard Input ([#812]) |❌ |❌ |❌ |❌ |❌ |❌ |❓ |❓ |
### Completed API Reworks ### Completed API Reworks
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM | |Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |Redox OS|
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | |------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
[#165]: https://github.com/rust-windowing/winit/issues/165 [#165]: https://github.com/rust-windowing/winit/issues/165
[#219]: https://github.com/rust-windowing/winit/issues/219 [#219]: https://github.com/rust-windowing/winit/issues/219

View file

@ -209,3 +209,8 @@ inside `Event::NewEvents(StartCause::Init)`.
[#2051]: https://github.com/rust-windowing/winit/issues/2051 [#2051]: https://github.com/rust-windowing/winit/issues/2051
[#2087]: https://github.com/rust-windowing/winit/issues/2087 [#2087]: https://github.com/rust-windowing/winit/issues/2087
[#1705]: https://github.com/rust-windowing/winit/issues/1705 [#1705]: https://github.com/rust-windowing/winit/issues/1705
#### Redox OS
Redox OS has some functionality not present yet, that will be implemented when
its orbital display server provides it.

View file

@ -13,9 +13,11 @@ fn main() {
windows_platform: { target_os = "windows" }, windows_platform: { target_os = "windows" },
apple: { any(target_os = "ios", target_os = "macos") }, apple: { any(target_os = "ios", target_os = "macos") },
free_unix: { all(unix, not(apple), not(android_platform)) }, free_unix: { all(unix, not(apple), not(android_platform)) },
redox: { target_os = "redox" },
// Native displays. // Native displays.
x11_platform: { all(feature = "x11", free_unix, not(wasm)) }, x11_platform: { all(feature = "x11", free_unix, not(wasm), not(redox)) },
wayland_platform: { all(feature = "wayland", free_unix, not(wasm)) }, wayland_platform: { all(feature = "wayland", free_unix, not(wasm), not(redox)) },
orbital_platform: { redox },
} }
} }

View file

@ -6,7 +6,8 @@
macos_platform, macos_platform,
x11_platform, x11_platform,
wayland_platform, wayland_platform,
android_platform android_platform,
orbital_platform,
))] ))]
fn main() { fn main() {
use std::{thread::sleep, time::Duration}; use std::{thread::sleep, time::Duration};

View file

@ -391,7 +391,7 @@ pub enum WindowEvent<'a> {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
Ime(Ime), Ime(Ime),
/// The cursor has moved on the window. /// The cursor has moved on the window.
@ -508,7 +508,7 @@ pub enum WindowEvent<'a> {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / X11 / Wayland:** Unsupported. /// - **iOS / Android / X11 / Wayland / Orbital:** Unsupported.
ThemeChanged(Theme), ThemeChanged(Theme),
/// The window has been occluded (completely hidden from view). /// The window has been occluded (completely hidden from view).
@ -517,7 +517,7 @@ pub enum WindowEvent<'a> {
/// minimised, set invisible, or fully occluded by another window. /// minimised, set invisible, or fully occluded by another window.
/// ///
/// Platform-specific behavior: /// Platform-specific behavior:
/// - **iOS / Android / Web / Wayland / Windows:** Unsupported. /// - **iOS / Android / Web / Wayland / Windows / Orbital:** Unsupported.
Occluded(bool), Occluded(bool),
} }

View file

@ -335,7 +335,7 @@ impl<T> EventLoopWindowTarget<T> {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Wayland / macOS / iOS / Android / Web:** Unsupported. /// - **Wayland / macOS / iOS / Android / Web / Orbital:** Unsupported.
/// ///
/// [`DeviceEvent`]: crate::event::DeviceEvent /// [`DeviceEvent`]: crate::event::DeviceEvent
pub fn set_device_event_filter(&self, _filter: DeviceEventFilter) { pub fn set_device_event_filter(&self, _filter: DeviceEventFilter) {

View file

@ -61,7 +61,7 @@ impl VideoMode {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Wayland:** Always returns 32. /// - **Wayland / Orbital:** Always returns 32.
/// - **iOS:** Always returns 32. /// - **iOS:** Always returns 32.
#[inline] #[inline]
pub fn bit_depth(&self) -> u16 { pub fn bit_depth(&self) -> u16 {

View file

@ -21,6 +21,8 @@ pub mod android;
pub mod ios; pub mod ios;
#[cfg(macos_platform)] #[cfg(macos_platform)]
pub mod macos; pub mod macos;
#[cfg(orbital_platform)]
pub mod orbital;
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
pub mod wayland; pub mod wayland;
#[cfg(wasm_platform)] #[cfg(wasm_platform)]
@ -35,6 +37,7 @@ pub mod x11;
macos_platform, macos_platform,
android_platform, android_platform,
x11_platform, x11_platform,
wayland_platform wayland_platform,
orbital_platform
))] ))]
pub mod run_return; pub mod run_return;

1
src/platform/orbital.rs Normal file
View file

@ -0,0 +1 @@
// There are no Orbital specific traits yet.

View file

@ -19,6 +19,9 @@ mod platform;
#[cfg(wasm_platform)] #[cfg(wasm_platform)]
#[path = "web/mod.rs"] #[path = "web/mod.rs"]
mod platform; mod platform;
#[cfg(orbital_platform)]
#[path = "orbital/mod.rs"]
mod platform;
pub use self::platform::*; pub use self::platform::*;
@ -59,5 +62,6 @@ impl From<Fullscreen> for RootFullscreen {
not(x11_platform), not(x11_platform),
not(wayland_platform), not(wayland_platform),
not(wasm_platform), not(wasm_platform),
not(orbital_platform),
))] ))]
compile_error!("The platform you're compiling for is not supported by winit"); compile_error!("The platform you're compiling for is not supported by winit");

View file

@ -0,0 +1,709 @@
use std::{
collections::VecDeque,
mem, slice,
sync::{mpsc, Arc, Mutex},
time::Instant,
};
use orbclient::{
ButtonEvent, EventOption, FocusEvent, HoverEvent, KeyEvent, MouseEvent, MoveEvent, QuitEvent,
ResizeEvent, ScrollEvent, TextInputEvent,
};
use raw_window_handle::{OrbitalDisplayHandle, RawDisplayHandle};
use crate::{
event::{self, StartCause, VirtualKeyCode},
event_loop::{self, ControlFlow},
window::WindowId as RootWindowId,
};
use super::{
DeviceId, MonitorHandle, PlatformSpecificEventLoopAttributes, RedoxSocket, TimeSocket,
WindowId, WindowProperties,
};
fn convert_scancode(scancode: u8) -> Option<VirtualKeyCode> {
match scancode {
orbclient::K_A => Some(VirtualKeyCode::A),
orbclient::K_B => Some(VirtualKeyCode::B),
orbclient::K_C => Some(VirtualKeyCode::C),
orbclient::K_D => Some(VirtualKeyCode::D),
orbclient::K_E => Some(VirtualKeyCode::E),
orbclient::K_F => Some(VirtualKeyCode::F),
orbclient::K_G => Some(VirtualKeyCode::G),
orbclient::K_H => Some(VirtualKeyCode::H),
orbclient::K_I => Some(VirtualKeyCode::I),
orbclient::K_J => Some(VirtualKeyCode::J),
orbclient::K_K => Some(VirtualKeyCode::K),
orbclient::K_L => Some(VirtualKeyCode::L),
orbclient::K_M => Some(VirtualKeyCode::M),
orbclient::K_N => Some(VirtualKeyCode::N),
orbclient::K_O => Some(VirtualKeyCode::O),
orbclient::K_P => Some(VirtualKeyCode::P),
orbclient::K_Q => Some(VirtualKeyCode::Q),
orbclient::K_R => Some(VirtualKeyCode::R),
orbclient::K_S => Some(VirtualKeyCode::S),
orbclient::K_T => Some(VirtualKeyCode::T),
orbclient::K_U => Some(VirtualKeyCode::U),
orbclient::K_V => Some(VirtualKeyCode::V),
orbclient::K_W => Some(VirtualKeyCode::W),
orbclient::K_X => Some(VirtualKeyCode::X),
orbclient::K_Y => Some(VirtualKeyCode::Y),
orbclient::K_Z => Some(VirtualKeyCode::Z),
orbclient::K_0 => Some(VirtualKeyCode::Key0),
orbclient::K_1 => Some(VirtualKeyCode::Key1),
orbclient::K_2 => Some(VirtualKeyCode::Key2),
orbclient::K_3 => Some(VirtualKeyCode::Key3),
orbclient::K_4 => Some(VirtualKeyCode::Key4),
orbclient::K_5 => Some(VirtualKeyCode::Key5),
orbclient::K_6 => Some(VirtualKeyCode::Key6),
orbclient::K_7 => Some(VirtualKeyCode::Key7),
orbclient::K_8 => Some(VirtualKeyCode::Key8),
orbclient::K_9 => Some(VirtualKeyCode::Key9),
orbclient::K_TICK => Some(VirtualKeyCode::Grave),
orbclient::K_MINUS => Some(VirtualKeyCode::Minus),
orbclient::K_EQUALS => Some(VirtualKeyCode::Equals),
orbclient::K_BACKSLASH => Some(VirtualKeyCode::Backslash),
orbclient::K_BRACE_OPEN => Some(VirtualKeyCode::LBracket),
orbclient::K_BRACE_CLOSE => Some(VirtualKeyCode::RBracket),
orbclient::K_SEMICOLON => Some(VirtualKeyCode::Semicolon),
orbclient::K_QUOTE => Some(VirtualKeyCode::Apostrophe),
orbclient::K_COMMA => Some(VirtualKeyCode::Comma),
orbclient::K_PERIOD => Some(VirtualKeyCode::Period),
orbclient::K_SLASH => Some(VirtualKeyCode::Slash),
orbclient::K_BKSP => Some(VirtualKeyCode::Back),
orbclient::K_SPACE => Some(VirtualKeyCode::Space),
orbclient::K_TAB => Some(VirtualKeyCode::Tab),
//orbclient::K_CAPS => Some(VirtualKeyCode::CAPS),
orbclient::K_LEFT_SHIFT => Some(VirtualKeyCode::LShift),
orbclient::K_RIGHT_SHIFT => Some(VirtualKeyCode::RShift),
orbclient::K_CTRL => Some(VirtualKeyCode::LControl),
orbclient::K_ALT => Some(VirtualKeyCode::LAlt),
orbclient::K_ENTER => Some(VirtualKeyCode::Return),
orbclient::K_ESC => Some(VirtualKeyCode::Escape),
orbclient::K_F1 => Some(VirtualKeyCode::F1),
orbclient::K_F2 => Some(VirtualKeyCode::F2),
orbclient::K_F3 => Some(VirtualKeyCode::F3),
orbclient::K_F4 => Some(VirtualKeyCode::F4),
orbclient::K_F5 => Some(VirtualKeyCode::F5),
orbclient::K_F6 => Some(VirtualKeyCode::F6),
orbclient::K_F7 => Some(VirtualKeyCode::F7),
orbclient::K_F8 => Some(VirtualKeyCode::F8),
orbclient::K_F9 => Some(VirtualKeyCode::F9),
orbclient::K_F10 => Some(VirtualKeyCode::F10),
orbclient::K_HOME => Some(VirtualKeyCode::Home),
orbclient::K_UP => Some(VirtualKeyCode::Up),
orbclient::K_PGUP => Some(VirtualKeyCode::PageUp),
orbclient::K_LEFT => Some(VirtualKeyCode::Left),
orbclient::K_RIGHT => Some(VirtualKeyCode::Right),
orbclient::K_END => Some(VirtualKeyCode::End),
orbclient::K_DOWN => Some(VirtualKeyCode::Down),
orbclient::K_PGDN => Some(VirtualKeyCode::PageDown),
orbclient::K_DEL => Some(VirtualKeyCode::Delete),
orbclient::K_F11 => Some(VirtualKeyCode::F11),
orbclient::K_F12 => Some(VirtualKeyCode::F12),
_ => None,
}
}
fn element_state(pressed: bool) -> event::ElementState {
if pressed {
event::ElementState::Pressed
} else {
event::ElementState::Released
}
}
bitflags! {
#[derive(Default)]
struct KeyboardModifierState: u8 {
const LSHIFT = 1 << 0;
const RSHIFT = 1 << 1;
const LCTRL = 1 << 2;
const RCTRL = 1 << 3;
const LALT = 1 << 4;
const RALT = 1 << 5;
const LSUPER = 1 << 6;
const RSUPER = 1 << 7;
}
}
bitflags! {
#[derive(Default)]
struct MouseButtonState: u8 {
const LEFT = 1 << 0;
const MIDDLE = 1 << 1;
const RIGHT = 1 << 2;
}
}
#[derive(Default)]
struct EventState {
keyboard: KeyboardModifierState,
mouse: MouseButtonState,
resize_opt: Option<(u32, u32)>,
}
impl EventState {
fn key(&mut self, vk: VirtualKeyCode, pressed: bool) {
match vk {
VirtualKeyCode::LShift => self.keyboard.set(KeyboardModifierState::LSHIFT, pressed),
VirtualKeyCode::RShift => self.keyboard.set(KeyboardModifierState::RSHIFT, pressed),
VirtualKeyCode::LControl => self.keyboard.set(KeyboardModifierState::LCTRL, pressed),
VirtualKeyCode::RControl => self.keyboard.set(KeyboardModifierState::RCTRL, pressed),
VirtualKeyCode::LAlt => self.keyboard.set(KeyboardModifierState::LALT, pressed),
VirtualKeyCode::RAlt => self.keyboard.set(KeyboardModifierState::RALT, pressed),
VirtualKeyCode::LWin => self.keyboard.set(KeyboardModifierState::LSUPER, pressed),
VirtualKeyCode::RWin => self.keyboard.set(KeyboardModifierState::RSUPER, pressed),
_ => (),
}
}
fn mouse(
&mut self,
left: bool,
middle: bool,
right: bool,
) -> Option<(event::MouseButton, event::ElementState)> {
if self.mouse.contains(MouseButtonState::LEFT) != left {
self.mouse.set(MouseButtonState::LEFT, left);
return Some((event::MouseButton::Left, element_state(left)));
}
if self.mouse.contains(MouseButtonState::MIDDLE) != middle {
self.mouse.set(MouseButtonState::MIDDLE, middle);
return Some((event::MouseButton::Middle, element_state(middle)));
}
if self.mouse.contains(MouseButtonState::RIGHT) != right {
self.mouse.set(MouseButtonState::RIGHT, right);
return Some((event::MouseButton::Right, element_state(right)));
}
None
}
fn modifiers(&self) -> event::ModifiersState {
let mut modifiers = event::ModifiersState::empty();
if self
.keyboard
.intersects(KeyboardModifierState::LSHIFT | KeyboardModifierState::RSHIFT)
{
modifiers |= event::ModifiersState::SHIFT;
}
if self
.keyboard
.intersects(KeyboardModifierState::LCTRL | KeyboardModifierState::RCTRL)
{
modifiers |= event::ModifiersState::CTRL;
}
if self
.keyboard
.intersects(KeyboardModifierState::LALT | KeyboardModifierState::RALT)
{
modifiers |= event::ModifiersState::ALT;
}
if self
.keyboard
.intersects(KeyboardModifierState::LSUPER | KeyboardModifierState::RSUPER)
{
modifiers |= event::ModifiersState::LOGO
}
modifiers
}
}
pub struct EventLoop<T: 'static> {
windows: Vec<(Arc<RedoxSocket>, EventState)>,
window_target: event_loop::EventLoopWindowTarget<T>,
}
impl<T: 'static> EventLoop<T> {
pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> Self {
let (user_events_sender, user_events_receiver) = mpsc::channel();
let event_socket = Arc::new(RedoxSocket::event().unwrap());
let wake_socket = Arc::new(TimeSocket::open().unwrap());
event_socket
.write(&syscall::Event {
id: wake_socket.0.fd,
flags: syscall::EventFlags::EVENT_READ,
data: wake_socket.0.fd,
})
.unwrap();
Self {
windows: Vec::new(),
window_target: event_loop::EventLoopWindowTarget {
p: EventLoopWindowTarget {
user_events_sender,
user_events_receiver,
creates: Mutex::new(VecDeque::new()),
redraws: Arc::new(Mutex::new(VecDeque::new())),
destroys: Arc::new(Mutex::new(VecDeque::new())),
event_socket,
wake_socket,
},
_marker: std::marker::PhantomData,
},
}
}
pub fn run<F>(mut self, event_handler: F) -> !
where
F: 'static
+ FnMut(event::Event<'_, T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
{
let exit_code = self.run_return(event_handler);
::std::process::exit(exit_code);
}
fn process_event<F>(
window_id: WindowId,
event_option: EventOption,
event_state: &mut EventState,
mut event_handler: F,
) where
F: FnMut(event::Event<'_, T>),
{
match event_option {
EventOption::Key(KeyEvent {
character: _,
scancode,
pressed,
}) => {
if scancode != 0 {
let vk_opt = convert_scancode(scancode);
if let Some(vk) = vk_opt {
event_state.key(vk, pressed);
}
event_handler(
#[allow(deprecated)]
event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::KeyboardInput {
device_id: event::DeviceId(DeviceId),
input: event::KeyboardInput {
scancode: scancode as u32,
state: element_state(pressed),
virtual_keycode: vk_opt,
modifiers: event_state.modifiers(),
},
is_synthetic: false,
},
},
);
}
}
EventOption::TextInput(TextInputEvent { character }) => {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::ReceivedCharacter(character),
});
}
EventOption::Mouse(MouseEvent { x, y }) => {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::CursorMoved {
device_id: event::DeviceId(DeviceId),
position: (x, y).into(),
modifiers: event_state.modifiers(),
},
});
}
EventOption::Button(ButtonEvent {
left,
middle,
right,
}) => {
while let Some((button, state)) = event_state.mouse(left, middle, right) {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::MouseInput {
device_id: event::DeviceId(DeviceId),
state,
button,
modifiers: event_state.modifiers(),
},
});
}
}
EventOption::Scroll(ScrollEvent { x, y }) => {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::MouseWheel {
device_id: event::DeviceId(DeviceId),
delta: event::MouseScrollDelta::LineDelta(x as f32, y as f32),
phase: event::TouchPhase::Moved,
modifiers: event_state.modifiers(),
},
});
}
EventOption::Quit(QuitEvent {}) => {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::CloseRequested,
});
}
EventOption::Focus(FocusEvent { focused }) => {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::Focused(focused),
});
}
EventOption::Move(MoveEvent { x, y }) => {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::Moved((x, y).into()),
});
}
EventOption::Resize(ResizeEvent { width, height }) => {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::Resized((width, height).into()),
});
// Acknowledge resize after event loop.
event_state.resize_opt = Some((width, height));
}
//TODO: Clipboard
EventOption::Hover(HoverEvent { entered }) => {
if entered {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::CursorEntered {
device_id: event::DeviceId(DeviceId),
},
});
} else {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::CursorLeft {
device_id: event::DeviceId(DeviceId),
},
});
}
}
other => {
warn!("unhandled event: {:?}", other);
}
}
}
pub fn run_return<F>(&mut self, mut event_handler_inner: F) -> i32
where
F: FnMut(event::Event<'_, T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
{
// Wrapper for event handler function that prevents ExitWithCode from being unset.
let mut event_handler =
move |event: event::Event<'_, T>,
window_target: &event_loop::EventLoopWindowTarget<T>,
control_flow: &mut ControlFlow| {
if let ControlFlow::ExitWithCode(code) = control_flow {
event_handler_inner(
event,
window_target,
&mut ControlFlow::ExitWithCode(*code),
);
} else {
event_handler_inner(event, window_target, control_flow);
}
};
let mut control_flow = ControlFlow::default();
let mut start_cause = StartCause::Init;
let code = loop {
event_handler(
event::Event::NewEvents(start_cause),
&self.window_target,
&mut control_flow,
);
if start_cause == StartCause::Init {
event_handler(
event::Event::Resumed,
&self.window_target,
&mut control_flow,
);
}
// Handle window creates.
while let Some(window) = {
let mut creates = self.window_target.p.creates.lock().unwrap();
creates.pop_front()
} {
let window_id = WindowId {
fd: window.fd as u64,
};
let mut buf: [u8; 4096] = [0; 4096];
let path = window.fpath(&mut buf).expect("failed to read properties");
let properties = WindowProperties::new(path);
self.windows.push((window, EventState::default()));
// Send resize event on create to indicate first size.
event_handler(
event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::Resized((properties.w, properties.h).into()),
},
&self.window_target,
&mut control_flow,
);
// Send resize event on create to indicate first position.
event_handler(
event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::Moved((properties.x, properties.y).into()),
},
&self.window_target,
&mut control_flow,
);
}
// Handle window destroys.
while let Some(destroy_id) = {
let mut destroys = self.window_target.p.destroys.lock().unwrap();
destroys.pop_front()
} {
event_handler(
event::Event::WindowEvent {
window_id: RootWindowId(destroy_id),
event: event::WindowEvent::Destroyed,
},
&self.window_target,
&mut control_flow,
);
self.windows
.retain(|(window, _event_state)| window.fd as u64 != destroy_id.fd);
}
// Handle window events.
let mut i = 0;
// While loop is used here because the same window may be processed more than once.
while let Some((window, event_state)) = self.windows.get_mut(i) {
let window_id = WindowId {
fd: window.fd as u64,
};
let mut event_buf = [0u8; 16 * mem::size_of::<orbclient::Event>()];
let count =
syscall::read(window.fd, &mut event_buf).expect("failed to read window events");
// Safety: orbclient::Event is a packed struct designed to be transferred over a socket.
let events = unsafe {
slice::from_raw_parts(
event_buf.as_ptr() as *const orbclient::Event,
count / mem::size_of::<orbclient::Event>(),
)
};
for orbital_event in events {
Self::process_event(
window_id,
orbital_event.to_option(),
event_state,
|event| event_handler(event, &self.window_target, &mut control_flow),
);
}
if count == event_buf.len() {
// If event buf was full, process same window again to ensure all events are drained.
continue;
}
// Acknowledge the latest resize event.
if let Some((w, h)) = event_state.resize_opt.take() {
window
.write(format!("S,{},{}", w, h).as_bytes())
.expect("failed to acknowledge resize");
// Require redraw after resize.
let mut redraws = self.window_target.p.redraws.lock().unwrap();
if !redraws.contains(&window_id) {
redraws.push_back(window_id);
}
}
// Move to next window.
i += 1;
}
while let Ok(event) = self.window_target.p.user_events_receiver.try_recv() {
event_handler(
event::Event::UserEvent(event),
&self.window_target,
&mut control_flow,
);
}
event_handler(
event::Event::MainEventsCleared,
&self.window_target,
&mut control_flow,
);
// To avoid deadlocks the redraws lock is not held during event processing.
while let Some(window_id) = {
let mut redraws = self.window_target.p.redraws.lock().unwrap();
redraws.pop_front()
} {
event_handler(
event::Event::RedrawRequested(RootWindowId(window_id)),
&self.window_target,
&mut control_flow,
);
}
event_handler(
event::Event::RedrawEventsCleared,
&self.window_target,
&mut control_flow,
);
let requested_resume = match control_flow {
ControlFlow::Poll => {
start_cause = StartCause::Poll;
continue;
}
ControlFlow::Wait => None,
ControlFlow::WaitUntil(instant) => Some(instant),
ControlFlow::ExitWithCode(code) => break code,
};
// Re-using wake socket caused extra wake events before because there were leftover
// timeouts, and then new timeouts were added each time a spurious timeout expired.
let timeout_socket = TimeSocket::open().unwrap();
self.window_target
.p
.event_socket
.write(&syscall::Event {
id: timeout_socket.0.fd,
flags: syscall::EventFlags::EVENT_READ,
data: 0,
})
.unwrap();
let start = Instant::now();
if let Some(instant) = requested_resume {
let mut time = timeout_socket.current_time().unwrap();
if let Some(duration) = instant.checked_duration_since(start) {
time.tv_sec += duration.as_secs() as i64;
time.tv_nsec += duration.subsec_nanos() as i32;
// Normalize timespec so tv_nsec is not greater than one second.
while time.tv_nsec >= 1_000_000_000 {
time.tv_sec += 1;
time.tv_nsec -= 1_000_000_000;
}
}
timeout_socket.timeout(&time).unwrap();
}
// Wait for event if needed.
let mut event = syscall::Event::default();
self.window_target.p.event_socket.read(&mut event).unwrap();
// TODO: handle spurious wakeups (redraw caused wakeup but redraw already handled)
match requested_resume {
Some(requested_resume) if event.id == timeout_socket.0.fd => {
// If the event is from the special timeout socket, report that resume
// time was reached.
start_cause = StartCause::ResumeTimeReached {
start,
requested_resume,
};
}
_ => {
// Normal window event or spurious timeout.
start_cause = StartCause::WaitCancelled {
start,
requested_resume,
};
}
}
};
event_handler(
event::Event::LoopDestroyed,
&self.window_target,
&mut control_flow,
);
code
}
pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget<T> {
&self.window_target
}
pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy {
user_events_sender: self.window_target.p.user_events_sender.clone(),
wake_socket: self.window_target.p.wake_socket.clone(),
}
}
}
pub struct EventLoopProxy<T: 'static> {
user_events_sender: mpsc::Sender<T>,
wake_socket: Arc<TimeSocket>,
}
impl<T> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopClosed<T>> {
self.user_events_sender
.send(event)
.map_err(|mpsc::SendError(x)| event_loop::EventLoopClosed(x))?;
self.wake_socket.wake().unwrap();
Ok(())
}
}
impl<T> Clone for EventLoopProxy<T> {
fn clone(&self) -> Self {
Self {
user_events_sender: self.user_events_sender.clone(),
wake_socket: self.wake_socket.clone(),
}
}
}
impl<T> Unpin for EventLoopProxy<T> {}
pub struct EventLoopWindowTarget<T: 'static> {
pub(super) user_events_sender: mpsc::Sender<T>,
pub(super) user_events_receiver: mpsc::Receiver<T>,
pub(super) creates: Mutex<VecDeque<Arc<RedoxSocket>>>,
pub(super) redraws: Arc<Mutex<VecDeque<WindowId>>>,
pub(super) destroys: Arc<Mutex<VecDeque<WindowId>>>,
pub(super) event_socket: Arc<RedoxSocket>,
pub(super) wake_socket: Arc<TimeSocket>,
}
impl<T: 'static> EventLoopWindowTarget<T> {
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
Some(MonitorHandle)
}
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
let mut v = VecDeque::with_capacity(1);
v.push_back(MonitorHandle);
v
}
pub fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::Orbital(OrbitalDisplayHandle::empty())
}
}

View file

@ -0,0 +1,253 @@
#![cfg(target_os = "redox")]
use std::str;
use crate::dpi::{PhysicalPosition, PhysicalSize};
pub use self::event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget};
mod event_loop;
pub use self::window::Window;
mod window;
struct RedoxSocket {
fd: usize,
}
impl RedoxSocket {
fn event() -> syscall::Result<Self> {
Self::open_raw("event:")
}
fn orbital(properties: &WindowProperties<'_>) -> syscall::Result<Self> {
Self::open_raw(&format!("{}", properties))
}
// Paths should be checked to ensure they are actually sockets and not normal files. If a
// non-socket path is used, it could cause read and write to not function as expected. For
// example, the seek would change in a potentially unpredictable way if either read or write
// were called at the same time by multiple threads.
fn open_raw(path: &str) -> syscall::Result<Self> {
let fd = syscall::open(path, syscall::O_RDWR | syscall::O_CLOEXEC)?;
Ok(Self { fd })
}
fn read(&self, buf: &mut [u8]) -> syscall::Result<()> {
let count = syscall::read(self.fd, buf)?;
if count == buf.len() {
Ok(())
} else {
Err(syscall::Error::new(syscall::EINVAL))
}
}
fn write(&self, buf: &[u8]) -> syscall::Result<()> {
let count = syscall::write(self.fd, buf)?;
if count == buf.len() {
Ok(())
} else {
Err(syscall::Error::new(syscall::EINVAL))
}
}
fn fpath<'a>(&self, buf: &'a mut [u8]) -> syscall::Result<&'a str> {
let count = syscall::fpath(self.fd, buf)?;
str::from_utf8(&buf[..count]).map_err(|_err| syscall::Error::new(syscall::EINVAL))
}
}
impl Drop for RedoxSocket {
fn drop(&mut self) {
let _ = syscall::close(self.fd);
}
}
pub struct TimeSocket(RedoxSocket);
impl TimeSocket {
fn open() -> syscall::Result<Self> {
RedoxSocket::open_raw("time:4").map(Self)
}
// Read current time.
fn current_time(&self) -> syscall::Result<syscall::TimeSpec> {
let mut timespec = syscall::TimeSpec::default();
self.0.read(&mut timespec)?;
Ok(timespec)
}
// Write a timeout.
fn timeout(&self, timespec: &syscall::TimeSpec) -> syscall::Result<()> {
self.0.write(timespec)
}
// Wake immediately.
fn wake(&self) -> syscall::Result<()> {
// Writing a default TimeSpec will always trigger a time event.
self.timeout(&syscall::TimeSpec::default())
}
}
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) struct PlatformSpecificEventLoopAttributes {}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct WindowId {
fd: u64,
}
impl WindowId {
pub const fn dummy() -> Self {
WindowId {
fd: u64::max_value(),
}
}
}
impl From<WindowId> for u64 {
fn from(id: WindowId) -> Self {
id.fd
}
}
impl From<u64> for WindowId {
fn from(fd: u64) -> Self {
Self { fd }
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DeviceId;
impl DeviceId {
pub const fn dummy() -> Self {
DeviceId
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct PlatformSpecificWindowBuilderAttributes;
struct WindowProperties<'a> {
flags: &'a str,
x: i32,
y: i32,
w: u32,
h: u32,
title: &'a str,
}
impl<'a> WindowProperties<'a> {
fn new(path: &'a str) -> Self {
// orbital:flags/x/y/w/h/t
let mut parts = path.splitn(6, '/');
let flags = parts.next().unwrap_or("");
let x = parts
.next()
.map_or(0, |part| part.parse::<i32>().unwrap_or(0));
let y = parts
.next()
.map_or(0, |part| part.parse::<i32>().unwrap_or(0));
let w = parts
.next()
.map_or(0, |part| part.parse::<u32>().unwrap_or(0));
let h = parts
.next()
.map_or(0, |part| part.parse::<u32>().unwrap_or(0));
let title = parts.next().unwrap_or("");
Self {
flags,
x,
y,
w,
h,
title,
}
}
}
impl<'a> fmt::Display for WindowProperties<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"orbital:{}/{}/{}/{}/{}/{}",
self.flags, self.x, self.y, self.w, self.h, self.title
)
}
}
#[derive(Default, Clone, Debug)]
pub struct OsError;
use std::fmt::{self, Display, Formatter};
impl Display for OsError {
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), fmt::Error> {
write!(fmt, "Redox OS Error")
}
}
pub(crate) use crate::icon::NoIcon as PlatformIcon;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct MonitorHandle;
impl MonitorHandle {
pub fn name(&self) -> Option<String> {
Some("Redox Device".to_owned())
}
pub fn size(&self) -> PhysicalSize<u32> {
PhysicalSize::new(0, 0) // TODO
}
pub fn position(&self) -> PhysicalPosition<i32> {
(0, 0).into()
}
pub fn scale_factor(&self) -> f64 {
1.0 // TODO
}
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
// FIXME no way to get real refresh rate for now.
None
}
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
let size = self.size().into();
// FIXME this is not the real refresh rate
// (it is guaranteed to support 32 bit color though)
std::iter::once(VideoMode {
size,
bit_depth: 32,
refresh_rate_millihertz: 60000,
monitor: self.clone(),
})
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct VideoMode {
size: (u32, u32),
bit_depth: u16,
refresh_rate_millihertz: u32,
monitor: MonitorHandle,
}
impl VideoMode {
pub fn size(&self) -> PhysicalSize<u32> {
self.size.into()
}
pub fn bit_depth(&self) -> u16 {
self.bit_depth
}
pub fn refresh_rate_millihertz(&self) -> u32 {
self.refresh_rate_millihertz
}
pub fn monitor(&self) -> MonitorHandle {
self.monitor.clone()
}
}

View file

@ -0,0 +1,396 @@
use std::{
collections::VecDeque,
sync::{Arc, Mutex},
};
use raw_window_handle::{
OrbitalDisplayHandle, OrbitalWindowHandle, RawDisplayHandle, RawWindowHandle,
};
use crate::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error,
platform_impl::Fullscreen,
window,
};
use super::{
EventLoopWindowTarget, MonitorHandle, PlatformSpecificWindowBuilderAttributes, RedoxSocket,
TimeSocket, WindowId, WindowProperties,
};
// These values match the values uses in the `window_new` function in orbital:
// https://gitlab.redox-os.org/redox-os/orbital/-/blob/master/src/scheme.rs
const ORBITAL_FLAG_ASYNC: char = 'a';
const ORBITAL_FLAG_BACK: char = 'b';
const ORBITAL_FLAG_FRONT: char = 'f';
const ORBITAL_FLAG_BORDERLESS: char = 'l';
const ORBITAL_FLAG_RESIZABLE: char = 'r';
const ORBITAL_FLAG_TRANSPARENT: char = 't';
pub struct Window {
window_socket: Arc<RedoxSocket>,
redraws: Arc<Mutex<VecDeque<WindowId>>>,
destroys: Arc<Mutex<VecDeque<WindowId>>>,
wake_socket: Arc<TimeSocket>,
}
impl Window {
pub(crate) fn new<T: 'static>(
el: &EventLoopWindowTarget<T>,
attrs: window::WindowAttributes,
_: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, error::OsError> {
let scale = MonitorHandle.scale_factor();
let (x, y) = if let Some(pos) = attrs.position {
pos.to_physical::<i32>(scale).into()
} else {
// These coordinates are a special value to center the window.
(-1, -1)
};
let (w, h): (u32, u32) = if let Some(size) = attrs.inner_size {
size.to_physical::<u32>(scale).into()
} else {
(1024, 768)
};
//TODO: min/max inner_size
// Async by default.
let mut flag_str = ORBITAL_FLAG_ASYNC.to_string();
if attrs.resizable {
flag_str.push(ORBITAL_FLAG_RESIZABLE);
}
//TODO: maximized, fullscreen, visible
if attrs.transparent {
flag_str.push(ORBITAL_FLAG_TRANSPARENT);
}
if !attrs.decorations {
flag_str.push(ORBITAL_FLAG_BORDERLESS);
}
match attrs.window_level {
window::WindowLevel::AlwaysOnBottom => {
flag_str.push(ORBITAL_FLAG_BACK);
}
window::WindowLevel::Normal => {}
window::WindowLevel::AlwaysOnTop => {
flag_str.push(ORBITAL_FLAG_FRONT);
}
}
//TODO: window_icon
// Open window.
let window = RedoxSocket::orbital(&WindowProperties {
flags: &flag_str,
x,
y,
w,
h,
title: &attrs.title,
})
.expect("failed to open window");
// Add to event socket.
el.event_socket
.write(&syscall::Event {
id: window.fd,
flags: syscall::EventFlags::EVENT_READ,
data: window.fd,
})
.unwrap();
let window_socket = Arc::new(window);
// Notify event thread that this window was created, it will send some default events.
{
let mut creates = el.creates.lock().unwrap();
creates.push_back(window_socket.clone());
}
el.wake_socket.wake().unwrap();
Ok(Self {
window_socket,
redraws: el.redraws.clone(),
destroys: el.destroys.clone(),
wake_socket: el.wake_socket.clone(),
})
}
#[inline]
pub fn id(&self) -> WindowId {
WindowId {
fd: self.window_socket.fd as u64,
}
}
#[inline]
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
Some(MonitorHandle)
}
#[inline]
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
let mut v = VecDeque::with_capacity(1);
v.push_back(MonitorHandle);
v
}
#[inline]
pub fn current_monitor(&self) -> Option<MonitorHandle> {
Some(MonitorHandle)
}
#[inline]
pub fn scale_factor(&self) -> f64 {
MonitorHandle.scale_factor()
}
#[inline]
pub fn request_redraw(&self) {
let window_id = self.id();
let mut redraws = self.redraws.lock().unwrap();
if !redraws.contains(&window_id) {
redraws.push_back(window_id);
self.wake_socket.wake().unwrap();
}
}
#[inline]
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, error::NotSupportedError> {
let mut buf: [u8; 4096] = [0; 4096];
let path = self
.window_socket
.fpath(&mut buf)
.expect("failed to read properties");
let properties = WindowProperties::new(path);
Ok((properties.x, properties.y).into())
}
#[inline]
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, error::NotSupportedError> {
//TODO: adjust for window decorations
self.inner_position()
}
#[inline]
pub fn set_outer_position(&self, position: Position) {
//TODO: adjust for window decorations
let (x, y): (i32, i32) = position.to_physical::<i32>(self.scale_factor()).into();
self.window_socket
.write(format!("P,{},{}", x, y).as_bytes())
.expect("failed to set position");
}
#[inline]
pub fn inner_size(&self) -> PhysicalSize<u32> {
let mut buf: [u8; 4096] = [0; 4096];
let path = self
.window_socket
.fpath(&mut buf)
.expect("failed to read properties");
let properties = WindowProperties::new(path);
(properties.w, properties.h).into()
}
#[inline]
pub fn set_inner_size(&self, size: Size) {
let (w, h): (u32, u32) = size.to_physical::<u32>(self.scale_factor()).into();
self.window_socket
.write(format!("S,{},{}", w, h).as_bytes())
.expect("failed to set size");
}
#[inline]
pub fn outer_size(&self) -> PhysicalSize<u32> {
//TODO: adjust for window decorations
self.inner_size()
}
#[inline]
pub fn set_min_inner_size(&self, _: Option<Size>) {}
#[inline]
pub fn set_max_inner_size(&self, _: Option<Size>) {}
#[inline]
pub fn title(&self) -> String {
let mut buf: [u8; 4096] = [0; 4096];
let path = self
.window_socket
.fpath(&mut buf)
.expect("failed to read properties");
let properties = WindowProperties::new(path);
properties.title.to_string()
}
#[inline]
pub fn set_title(&self, title: &str) {
self.window_socket
.write(format!("T,{}", title).as_bytes())
.expect("failed to set title");
}
#[inline]
pub fn set_visible(&self, _visibility: bool) {}
#[inline]
pub fn is_visible(&self) -> Option<bool> {
None
}
#[inline]
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
None
}
#[inline]
pub fn set_resize_increments(&self, _increments: Option<Size>) {}
#[inline]
pub fn set_resizable(&self, _resizeable: bool) {}
#[inline]
pub fn is_resizable(&self) -> bool {
let mut buf: [u8; 4096] = [0; 4096];
let path = self
.window_socket
.fpath(&mut buf)
.expect("failed to read properties");
let properties = WindowProperties::new(path);
properties.flags.contains(ORBITAL_FLAG_RESIZABLE)
}
#[inline]
pub fn set_minimized(&self, _minimized: bool) {}
#[inline]
pub fn set_maximized(&self, _maximized: bool) {}
#[inline]
pub fn is_maximized(&self) -> bool {
false
}
#[inline]
pub(crate) fn set_fullscreen(&self, _monitor: Option<Fullscreen>) {}
#[inline]
pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
None
}
#[inline]
pub fn set_decorations(&self, _decorations: bool) {}
#[inline]
pub fn is_decorated(&self) -> bool {
let mut buf: [u8; 4096] = [0; 4096];
let path = self
.window_socket
.fpath(&mut buf)
.expect("failed to read properties");
let properties = WindowProperties::new(path);
!properties.flags.contains(ORBITAL_FLAG_BORDERLESS)
}
#[inline]
pub fn set_window_level(&self, _level: window::WindowLevel) {}
#[inline]
pub fn set_window_icon(&self, _window_icon: Option<crate::icon::Icon>) {}
#[inline]
pub fn set_ime_position(&self, _position: Position) {}
#[inline]
pub fn set_ime_allowed(&self, _allowed: bool) {}
#[inline]
pub fn focus_window(&self) {}
#[inline]
pub fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {}
#[inline]
pub fn set_cursor_icon(&self, _: window::CursorIcon) {}
#[inline]
pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
}
#[inline]
pub fn set_cursor_grab(&self, _: window::CursorGrabMode) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
}
#[inline]
pub fn set_cursor_visible(&self, _: bool) {}
#[inline]
pub fn drag_window(&self) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
}
#[inline]
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
}
#[inline]
pub fn raw_window_handle(&self) -> RawWindowHandle {
let mut handle = OrbitalWindowHandle::empty();
handle.window = self.window_socket.fd as *mut _;
RawWindowHandle::Orbital(handle)
}
#[inline]
pub fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::Orbital(OrbitalDisplayHandle::empty())
}
#[inline]
pub fn set_enabled_buttons(&self, _buttons: window::WindowButtons) {}
#[inline]
pub fn enabled_buttons(&self) -> window::WindowButtons {
window::WindowButtons::all()
}
#[inline]
pub fn theme(&self) -> Option<window::Theme> {
None
}
#[inline]
pub fn set_theme(&self, _theme: Option<window::Theme>) {}
}
impl Drop for Window {
fn drop(&mut self) {
{
let mut destroys = self.destroys.lock().unwrap();
destroys.push_back(self.id());
}
self.wake_socket.wake().unwrap();
}
}

View file

@ -368,7 +368,7 @@ impl WindowBuilder {
/// - **Wayland:** This control only CSD. You can also use `WINIT_WAYLAND_CSD_THEME` env variable to set the theme. /// - **Wayland:** This control only CSD. You can also use `WINIT_WAYLAND_CSD_THEME` env variable to set the theme.
/// Possible values for env variable are: "dark" and light". /// Possible values for env variable are: "dark" and light".
/// - **x11:** Build window with `_GTK_THEME_VARIANT` hint set to `dark` or `light`. /// - **x11:** Build window with `_GTK_THEME_VARIANT` hint set to `dark` or `light`.
/// - **iOS / Android / Web / x11:** Ignored. /// - **iOS / Android / Web / x11 / Orbital:** Ignored.
#[inline] #[inline]
pub fn with_theme(mut self, theme: Option<Theme>) -> Self { pub fn with_theme(mut self, theme: Option<Theme>) -> Self {
self.window.preferred_theme = theme; self.window.preferred_theme = theme;
@ -394,7 +394,7 @@ impl WindowBuilder {
/// ///
/// - **macOS**: if `false`, [`NSWindowSharingNone`] is used but doesn't completely /// - **macOS**: if `false`, [`NSWindowSharingNone`] is used but doesn't completely
/// prevent all apps from reading the window content, for instance, QuickTime. /// prevent all apps from reading the window content, for instance, QuickTime.
/// - **iOS / Android / Web / x11:** Ignored. /// - **iOS / Android / Web / x11 / Orbital:** Ignored.
/// ///
/// [`NSWindowSharingNone`]: https://developer.apple.com/documentation/appkit/nswindowsharingtype/nswindowsharingnone /// [`NSWindowSharingNone`]: https://developer.apple.com/documentation/appkit/nswindowsharingtype/nswindowsharingnone
#[inline] #[inline]
@ -667,7 +667,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
#[inline] #[inline]
pub fn set_min_inner_size<S: Into<Size>>(&self, min_size: Option<S>) { pub fn set_min_inner_size<S: Into<Size>>(&self, min_size: Option<S>) {
self.window.set_min_inner_size(min_size.map(|s| s.into())) self.window.set_min_inner_size(min_size.map(|s| s.into()))
@ -690,7 +690,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
#[inline] #[inline]
pub fn set_max_inner_size<S: Into<Size>>(&self, max_size: Option<S>) { pub fn set_max_inner_size<S: Into<Size>>(&self, max_size: Option<S>) {
self.window.set_max_inner_size(max_size.map(|s| s.into())) self.window.set_max_inner_size(max_size.map(|s| s.into()))
@ -700,7 +700,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web / Wayland / Windows:** Always returns [`None`]. /// - **iOS / Android / Web / Wayland / Windows / Orbital:** Always returns [`None`].
#[inline] #[inline]
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> { pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
self.window.resize_increments() self.window.resize_increments()
@ -715,7 +715,7 @@ impl Window {
/// ///
/// - **macOS:** Increments are converted to logical size and then macOS rounds them to whole numbers. /// - **macOS:** Increments are converted to logical size and then macOS rounds them to whole numbers.
/// - **Wayland / Windows:** Not implemented. /// - **Wayland / Windows:** Not implemented.
/// - **iOS / Android / Web:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
#[inline] #[inline]
pub fn set_resize_increments<S: Into<Size>>(&self, increments: Option<S>) { pub fn set_resize_increments<S: Into<Size>>(&self, increments: Option<S>) {
self.window self.window
@ -795,7 +795,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Wayland / X11:** Not implemented. /// - **Wayland / X11 / Orbital:** Not implemented.
/// - **Web / iOS / Android:** Unsupported. /// - **Web / iOS / Android:** Unsupported.
pub fn set_enabled_buttons(&self, buttons: WindowButtons) { pub fn set_enabled_buttons(&self, buttons: WindowButtons) {
self.window.set_enabled_buttons(buttons) self.window.set_enabled_buttons(buttons)
@ -805,7 +805,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Wayland / X11:** Not implemented. Always returns [`WindowButtons::all`]. /// - **Wayland / X11 / Orbital:** Not implemented. Always returns [`WindowButtons::all`].
/// - **Web / iOS / Android:** Unsupported. Always returns [`WindowButtons::all`]. /// - **Web / iOS / Android:** Unsupported. Always returns [`WindowButtons::all`].
pub fn enabled_buttons(&self) -> WindowButtons { pub fn enabled_buttons(&self) -> WindowButtons {
self.window.enabled_buttons() self.window.enabled_buttons()
@ -815,7 +815,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
/// - **Wayland:** Un-minimize is unsupported. /// - **Wayland:** Un-minimize is unsupported.
#[inline] #[inline]
pub fn set_minimized(&self, minimized: bool) { pub fn set_minimized(&self, minimized: bool) {
@ -826,7 +826,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
#[inline] #[inline]
pub fn set_maximized(&self, maximized: bool) { pub fn set_maximized(&self, maximized: bool) {
self.window.set_maximized(maximized) self.window.set_maximized(maximized)
@ -836,7 +836,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
#[inline] #[inline]
pub fn is_maximized(&self) -> bool { pub fn is_maximized(&self) -> bool {
self.window.is_maximized() self.window.is_maximized()
@ -861,7 +861,7 @@ impl Window {
/// - **iOS:** Can only be called on the main thread. /// - **iOS:** Can only be called on the main thread.
/// - **Wayland:** Does not support exclusive fullscreen mode and will no-op a request. /// - **Wayland:** Does not support exclusive fullscreen mode and will no-op a request.
/// - **Windows:** Screen saver is disabled in fullscreen mode. /// - **Windows:** Screen saver is disabled in fullscreen mode.
/// - **Android:** Unsupported. /// - **Android / Orbital:** Unsupported.
#[inline] #[inline]
pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) { pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
self.window.set_fullscreen(fullscreen.map(|f| f.into())) self.window.set_fullscreen(fullscreen.map(|f| f.into()))
@ -872,7 +872,7 @@ impl Window {
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS:** Can only be called on the main thread. /// - **iOS:** Can only be called on the main thread.
/// - **Android:** Will always return `None`. /// - **Android / Orbital:** Will always return `None`.
/// - **Wayland:** Can return `Borderless(None)` when there are no monitors. /// - **Wayland:** Can return `Borderless(None)` when there are no monitors.
#[inline] #[inline]
pub fn fullscreen(&self) -> Option<Fullscreen> { pub fn fullscreen(&self) -> Option<Fullscreen> {
@ -916,7 +916,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web / Wayland / macOS:** Unsupported. /// - **iOS / Android / Web / Wayland / macOS / Orbital:** Unsupported.
/// ///
/// - **Windows:** Sets `ICON_SMALL`. The base size for a window icon is 16x16, but it's /// - **Windows:** Sets `ICON_SMALL`. The base size for a window icon is 16x16, but it's
/// recommended to account for screen scaling and pick a multiple of that, i.e. 32x32. /// recommended to account for screen scaling and pick a multiple of that, i.e. 32x32.
@ -952,7 +952,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
/// ///
/// [chinese]: https://support.apple.com/guide/chinese-input-method/use-the-candidate-window-cim12992/104/mac/12.0 /// [chinese]: https://support.apple.com/guide/chinese-input-method/use-the-candidate-window-cim12992/104/mac/12.0
/// [japanese]: https://support.apple.com/guide/japanese-input-method/use-the-candidate-window-jpim10262/6.3/mac/12.0 /// [japanese]: https://support.apple.com/guide/japanese-input-method/use-the-candidate-window-jpim10262/6.3/mac/12.0
@ -978,7 +978,7 @@ impl Window {
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **macOS:** IME must be enabled to receive text-input where dead-key sequences are combined. /// - **macOS:** IME must be enabled to receive text-input where dead-key sequences are combined.
/// - **iOS / Android / Web:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
/// ///
/// [`Ime`]: crate::event::WindowEvent::Ime /// [`Ime`]: crate::event::WindowEvent::Ime
/// [`KeyboardInput`]: crate::event::WindowEvent::KeyboardInput /// [`KeyboardInput`]: crate::event::WindowEvent::KeyboardInput
@ -997,7 +997,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web / Wayland:** Unsupported. /// - **iOS / Android / Web / Wayland / Orbital:** Unsupported.
#[inline] #[inline]
pub fn focus_window(&self) { pub fn focus_window(&self) {
self.window.focus_window() self.window.focus_window()
@ -1012,7 +1012,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
/// - **macOS:** `None` has no effect. /// - **macOS:** `None` has no effect.
/// - **X11:** Requests for user attention must be manually cleared. /// - **X11:** Requests for user attention must be manually cleared.
/// - **Wayland:** Requires `xdg_activation_v1` protocol, `None` has no effect. /// - **Wayland:** Requires `xdg_activation_v1` protocol, `None` has no effect.
@ -1029,7 +1029,7 @@ impl Window {
/// - **Wayland:** You can also use `WINIT_WAYLAND_CSD_THEME` env variable to set the theme. /// - **Wayland:** You can also use `WINIT_WAYLAND_CSD_THEME` env variable to set the theme.
/// Possible values for env variable are: "dark" and light". When unspecified, a theme is automatically selected. /// Possible values for env variable are: "dark" and light". When unspecified, a theme is automatically selected.
/// -**x11:** Sets `_GTK_THEME_VARIANT` hint to `dark` or `light` and if `None` is used, it will default to [`Theme::Dark`]. /// -**x11:** Sets `_GTK_THEME_VARIANT` hint to `dark` or `light` and if `None` is used, it will default to [`Theme::Dark`].
/// - **iOS / Android / Web / x11:** Unsupported. /// - **iOS / Android / Web / x11 / Orbital:** Unsupported.
#[inline] #[inline]
pub fn set_theme(&self, theme: Option<Theme>) { pub fn set_theme(&self, theme: Option<Theme>) {
self.window.set_theme(theme) self.window.set_theme(theme)
@ -1040,7 +1040,7 @@ impl Window {
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **macOS:** This is an app-wide setting. /// - **macOS:** This is an app-wide setting.
/// - **iOS / Android / Web / Wayland / x11:** Unsupported. /// - **iOS / Android / Web / Wayland / x11 / Orbital:** Unsupported.
#[inline] #[inline]
pub fn theme(&self) -> Option<Theme> { pub fn theme(&self) -> Option<Theme> {
self.window.theme() self.window.theme()
@ -1052,7 +1052,7 @@ impl Window {
/// ///
/// - **macOS**: if `false`, [`NSWindowSharingNone`] is used but doesn't completely /// - **macOS**: if `false`, [`NSWindowSharingNone`] is used but doesn't completely
/// prevent all apps from reading the window content, for instance, QuickTime. /// prevent all apps from reading the window content, for instance, QuickTime.
/// - **iOS / Android / x11 / Wayland / Web:** Unsupported. /// - **iOS / Android / x11 / Wayland / Web / Orbital:** Unsupported.
/// ///
/// [`NSWindowSharingNone`]: https://developer.apple.com/documentation/appkit/nswindowsharingtype/nswindowsharingnone /// [`NSWindowSharingNone`]: https://developer.apple.com/documentation/appkit/nswindowsharingtype/nswindowsharingnone
pub fn set_content_protected(&self, _protected: bool) { pub fn set_content_protected(&self, _protected: bool) {
@ -1077,7 +1077,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android:** Unsupported. /// - **iOS / Android / Orbital:** Unsupported.
#[inline] #[inline]
pub fn set_cursor_icon(&self, cursor: CursorIcon) { pub fn set_cursor_icon(&self, cursor: CursorIcon) {
self.window.set_cursor_icon(cursor); self.window.set_cursor_icon(cursor);
@ -1100,7 +1100,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web / Wayland:** Always returns an [`ExternalError::NotSupported`]. /// - **iOS / Android / Web / Wayland / Orbital:** Always returns an [`ExternalError::NotSupported`].
#[inline] #[inline]
pub fn set_cursor_position<P: Into<Position>>(&self, position: P) -> Result<(), ExternalError> { pub fn set_cursor_position<P: Into<Position>>(&self, position: P) -> Result<(), ExternalError> {
self.window.set_cursor_position(position.into()) self.window.set_cursor_position(position.into())
@ -1137,7 +1137,7 @@ impl Window {
/// - **Wayland:** The cursor is only hidden within the confines of the window. /// - **Wayland:** The cursor is only hidden within the confines of the window.
/// - **macOS:** The cursor is hidden as long as the window has input focus, even if the cursor is /// - **macOS:** The cursor is hidden as long as the window has input focus, even if the cursor is
/// outside of the window. /// outside of the window.
/// - **iOS / Android:** Unsupported. /// - **iOS / Android / Orbital:** Unsupported.
#[inline] #[inline]
pub fn set_cursor_visible(&self, visible: bool) { pub fn set_cursor_visible(&self, visible: bool) {
self.window.set_cursor_visible(visible) self.window.set_cursor_visible(visible)
@ -1153,7 +1153,7 @@ impl Window {
/// - **X11:** Un-grabs the cursor. /// - **X11:** Un-grabs the cursor.
/// - **Wayland:** Requires the cursor to be inside the window to be dragged. /// - **Wayland:** Requires the cursor to be inside the window to be dragged.
/// - **macOS:** May prevent the button release event to be triggered. /// - **macOS:** May prevent the button release event to be triggered.
/// - **iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`]. /// - **iOS / Android / Web / Orbital:** Always returns an [`ExternalError::NotSupported`].
#[inline] #[inline]
pub fn drag_window(&self) -> Result<(), ExternalError> { pub fn drag_window(&self) -> Result<(), ExternalError> {
self.window.drag_window() self.window.drag_window()
@ -1166,7 +1166,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web / X11:** Always returns an [`ExternalError::NotSupported`]. /// - **iOS / Android / Web / X11 / Orbital:** Always returns an [`ExternalError::NotSupported`].
#[inline] #[inline]
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> { pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
self.window.set_cursor_hittest(hittest) self.window.set_cursor_hittest(hittest)
@ -1273,7 +1273,7 @@ pub enum CursorGrabMode {
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **macOS:** Not implemented. Always returns [`ExternalError::NotSupported`] for now. /// - **macOS:** Not implemented. Always returns [`ExternalError::NotSupported`] for now.
/// - **iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`]. /// - **iOS / Android / Web / Orbital:** Always returns an [`ExternalError::NotSupported`].
Confined, Confined,
/// The cursor is locked inside the window area to the certain position. /// The cursor is locked inside the window area to the certain position.
@ -1284,7 +1284,7 @@ pub enum CursorGrabMode {
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **X11 / Windows:** Not implemented. Always returns [`ExternalError::NotSupported`] for now. /// - **X11 / Windows:** Not implemented. Always returns [`ExternalError::NotSupported`] for now.
/// - **iOS / Android:** Always returns an [`ExternalError::NotSupported`]. /// - **iOS / Android / Orbital:** Always returns an [`ExternalError::NotSupported`].
Locked, Locked,
} }