diff --git a/.changes/tao.md b/.changes/tao.md new file mode 100644 index 000000000..94a4c17cc --- /dev/null +++ b/.changes/tao.md @@ -0,0 +1,5 @@ +--- +"wry": patch +--- + +Add tao as window dependency. diff --git a/Cargo.toml b/Cargo.toml index 57b4e4873..ab273d952 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,16 +37,14 @@ thiserror = "1.0" url = "2.2" image = "0.23" infer = "0.4" +tao = { git = "https://github.com/wusyong/tao", branch = "dev" } [target."cfg(target_os = \"linux\")".dependencies] -bitflags = "1.2" -cairo-rs = "0.9" webkit2gtk = { version = "0.11", features = [ "v2_10" ] } gio = "0.9" glib = "0.10" gtk = "0.9" gdk = "0.13" -gdk-pixbuf = "0.9" [target."cfg(target_os = \"windows\")".dependencies] webview2 = { version = "0.1.0", optional = true } @@ -54,11 +52,9 @@ webview2-sys = { version = "0.1.0", optional = true } winapi = { version = "0.3", features = [ "libloaderapi", "oleidl" ], optional = true } windows-webview2 = { version = "0.1", optional = true } windows = { version = "0.7", optional = true } -tauri-winit = "0.24" [target."cfg(target_os = \"macos\")".dependencies] cocoa = "0.24" core-graphics = "0.22" objc = "0.2" objc_id = "0.1" -tauri-winit = "0.24" diff --git a/src/application/dpi.rs b/src/application/dpi.rs deleted file mode 100644 index ff7f06a89..000000000 --- a/src/application/dpi.rs +++ /dev/null @@ -1,502 +0,0 @@ -//! UI scaling is important, so read the docs for this module if you don't want to be confused. -//! -//! ## Why should I care about UI scaling? -//! -//! Modern computer screens don't have a consistent relationship between resolution and size. -//! 1920x1080 is a common resolution for both desktop and mobile screens, despite mobile screens -//! normally being less than a quarter the size of their desktop counterparts. What's more, neither -//! desktop nor mobile screens are consistent resolutions within their own size classes - common -//! mobile screens range from below 720p to above 1440p, and desktop screens range from 720p to 5K -//! and beyond. -//! -//! Given that, it's a mistake to assume that 2D content will only be displayed on screens with -//! a consistent pixel density. If you were to render a 96-pixel-square image on a 1080p screen, -//! then render the same image on a similarly-sized 4K screen, the 4K rendition would only take up -//! about a quarter of the physical space as it did on the 1080p screen. That issue is especially -//! problematic with text rendering, where quarter-sized text becomes a significant legibility -//! problem. -//! -//! Failure to account for the scale factor can create a significantly degraded user experience. -//! Most notably, it can make users feel like they have bad eyesight, which will potentially cause -//! them to think about growing elderly, resulting in them having an existential crisis. Once users -//! enter that state, they will no longer be focused on your application. -//! -//! ## How should I handle it? -//! -//! The solution to this problem is to account for the device's *scale factor*. The scale factor is -//! the factor UI elements should be scaled by to be consistent with the rest of the user's system - -//! for example, a button that's normally 50 pixels across would be 100 pixels across on a device -//! with a scale factor of `2.0`, or 75 pixels across with a scale factor of `1.5`. -//! -//! Many UI systems, such as CSS, expose DPI-dependent units like [points] or [picas]. That's -//! usually a mistake, since there's no consistent mapping between the scale factor and the screen's -//! actual DPI. Unless you're printing to a physical medium, you should work in scaled pixels rather -//! than any DPI-dependent units. -//! -//! ### Position and Size types -//! -//! Winit's `Physical(Position|Size)` types correspond with the actual pixels on the device, and the -//! `Logical(Position|Size)` types correspond to the physical pixels divided by the scale factor. -//! All of Winit's functions return physical types, but can take either logical or physical -//! coordinates as input, allowing you to use the most convenient coordinate system for your -//! particular application. -//! -//! Winit's position and size types types are generic over their exact pixel type, `P`, to allow the -//! API to have integer precision where appropriate (e.g. most window manipulation functions) and -//! floating precision when necessary (e.g. logical sizes for fractional scale factors and touch -//! input). If `P` is a floating-point type, please do not cast the values with `as {int}`. Doing so -//! will truncate the fractional part of the float, rather than properly round to the nearest -//! integer. Use the provided `cast` function or `From`/`Into` conversions, which handle the -//! rounding properly. Note that precision loss will still occur when rounding from a float to an -//! int, although rounding lessens the problem. -//! -//! ### Events -//! -//! Winit will dispatch a [`ScaleFactorChanged`](crate::event::WindowEvent::ScaleFactorChanged) -//! event whenever a window's scale factor has changed. This can happen if the user drags their -//! window from a standard-resolution monitor to a high-DPI monitor, or if the user changes their -//! DPI settings. This gives you a chance to rescale your application's UI elements and adjust how -//! the platform changes the window's size to reflect the new scale factor. If a window hasn't -//! received a [`ScaleFactorChanged`](crate::event::WindowEvent::ScaleFactorChanged) event, -//! then its scale factor is `1.0`. -//! -//! ## How is the scale factor calculated? -//! -//! Scale factor is calculated differently on different platforms: -//! -//! - **Windows:** On Windows 8 and 10, per-monitor scaling is readily configured by users from the -//! display settings. While users are free to select any option they want, they're only given a -//! selection of "nice" scale factors, i.e. 1.0, 1.25, 1.5... on Windows 7, the scale factor is -//! global and changing it requires logging out. See [this article][windows_1] for technical -//! details. -//! - **macOS:** Recent versions of macOS allow the user to change the scaling factor for certain -//! displays. When this is available, the user may pick a per-monitor scaling factor from a set -//! of pre-defined settings. All "retina displays" have a scaling factor above 1.0 by default but -//! the specific value varies across devices. -//! - **X11:** Many man-hours have been spent trying to figure out how to handle DPI in X11. Winit -//! currently uses a three-pronged approach: -//! + Use the value in the `WINIT_X11_SCALE_FACTOR` environment variable, if present. -//! + If not present, use the value set in `Xft.dpi` in Xresources. -//! + Otherwise, calcuate the scale factor based on the millimeter monitor dimensions provided by XRandR. -//! -//! If `WINIT_X11_SCALE_FACTOR` is set to `randr`, it'll ignore the `Xft.dpi` field and use the -//! XRandR scaling method. Generally speaking, you should try to configure the standard system -//! variables to do what you want before resorting to `WINIT_X11_SCALE_FACTOR`. -//! - **Wayland:** On Wayland, scale factors are set per-screen by the server, and are always -//! integers (most often 1 or 2). -//! - **iOS:** Scale factors are set by Apple to the value that best suits the device, and range -//! from `1.0` to `3.0`. See [this article][apple_1] and [this article][apple_2] for more -//! information. -//! - **Android:** Scale factors are set by the manufacturer to the value that best suits the -//! device, and range from `1.0` to `4.0`. See [this article][android_1] for more information. -//! - **Web:** The scale factor is the ratio between CSS pixels and the physical device pixels. -//! In other words, it is the value of [`window.devicePixelRatio`][web_1]. It is affected by -//! both the screen scaling and the browser zoom level and can go below `1.0`. -//! -//! [points]: https://en.wikipedia.org/wiki/Point_(typography) -//! [picas]: https://en.wikipedia.org/wiki/Pica_(typography) -//! [windows_1]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows -//! [apple_1]: https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html -//! [apple_2]: https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/image-size-and-resolution/ -//! [android_1]: https://developer.android.com/training/multiscreen/screendensities -//! [web_1]: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio - -pub trait Pixel: Copy + Into { - fn from_f64(f: f64) -> Self; - fn cast(self) -> P { - P::from_f64(self.into()) - } -} - -impl Pixel for u8 { - fn from_f64(f: f64) -> Self { - f.round() as u8 - } -} -impl Pixel for u16 { - fn from_f64(f: f64) -> Self { - f.round() as u16 - } -} -impl Pixel for u32 { - fn from_f64(f: f64) -> Self { - f.round() as u32 - } -} -impl Pixel for i8 { - fn from_f64(f: f64) -> Self { - f.round() as i8 - } -} -impl Pixel for i16 { - fn from_f64(f: f64) -> Self { - f.round() as i16 - } -} -impl Pixel for i32 { - fn from_f64(f: f64) -> Self { - f.round() as i32 - } -} -impl Pixel for f32 { - fn from_f64(f: f64) -> Self { - f as f32 - } -} -impl Pixel for f64 { - fn from_f64(f: f64) -> Self { - f - } -} - -/// Checks that the scale factor is a normal positive `f64`. -/// -/// All functions that take a scale factor assert that this will return `true`. If you're sourcing scale factors from -/// anywhere other than winit, it's recommended to validate them using this function before passing them to winit; -/// otherwise, you risk panics. -#[inline] -pub fn validate_scale_factor(scale_factor: f64) -> bool { - scale_factor.is_sign_positive() && scale_factor.is_normal() -} - -/// A position represented in logical pixels. -/// -/// The position is stored as floats, so please be careful. Casting floats to integers truncates the -/// fractional part, which can cause noticable issues. To help with that, an `Into<(i32, i32)>` -/// implementation is provided which does the rounding for you. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct LogicalPosition

{ - pub x: P, - pub y: P, -} - -impl

LogicalPosition

{ - #[inline] - pub const fn new(x: P, y: P) -> Self { - LogicalPosition { x, y } - } -} - -impl LogicalPosition

{ - #[inline] - pub fn from_physical>, X: Pixel>( - physical: T, - scale_factor: f64, - ) -> Self { - physical.into().to_logical(scale_factor) - } - - #[inline] - pub fn to_physical(&self, scale_factor: f64) -> PhysicalPosition { - assert!(validate_scale_factor(scale_factor)); - let x = self.x.into() * scale_factor; - let y = self.y.into() * scale_factor; - PhysicalPosition::new(x, y).cast() - } - - #[inline] - pub fn cast(&self) -> LogicalPosition { - LogicalPosition { - x: self.x.cast(), - y: self.y.cast(), - } - } -} - -impl From<(X, X)> for LogicalPosition

{ - fn from((x, y): (X, X)) -> LogicalPosition

{ - LogicalPosition::new(x.cast(), y.cast()) - } -} - -impl Into<(X, X)> for LogicalPosition

{ - fn into(self: Self) -> (X, X) { - (self.x.cast(), self.y.cast()) - } -} - -impl From<[X; 2]> for LogicalPosition

{ - fn from([x, y]: [X; 2]) -> LogicalPosition

{ - LogicalPosition::new(x.cast(), y.cast()) - } -} - -impl Into<[X; 2]> for LogicalPosition

{ - fn into(self: Self) -> [X; 2] { - [self.x.cast(), self.y.cast()] - } -} - -/// A position represented in physical pixels. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PhysicalPosition

{ - pub x: P, - pub y: P, -} - -impl

PhysicalPosition

{ - #[inline] - pub const fn new(x: P, y: P) -> Self { - PhysicalPosition { x, y } - } -} - -impl PhysicalPosition

{ - #[inline] - pub fn from_logical>, X: Pixel>( - logical: T, - scale_factor: f64, - ) -> Self { - logical.into().to_physical(scale_factor) - } - - #[inline] - pub fn to_logical(&self, scale_factor: f64) -> LogicalPosition { - assert!(validate_scale_factor(scale_factor)); - let x = self.x.into() / scale_factor; - let y = self.y.into() / scale_factor; - LogicalPosition::new(x, y).cast() - } - - #[inline] - pub fn cast(&self) -> PhysicalPosition { - PhysicalPosition { - x: self.x.cast(), - y: self.y.cast(), - } - } -} - -impl From<(X, X)> for PhysicalPosition

{ - fn from((x, y): (X, X)) -> PhysicalPosition

{ - PhysicalPosition::new(x.cast(), y.cast()) - } -} - -impl Into<(X, X)> for PhysicalPosition

{ - fn into(self: Self) -> (X, X) { - (self.x.cast(), self.y.cast()) - } -} - -impl From<[X; 2]> for PhysicalPosition

{ - fn from([x, y]: [X; 2]) -> PhysicalPosition

{ - PhysicalPosition::new(x.cast(), y.cast()) - } -} - -impl Into<[X; 2]> for PhysicalPosition

{ - fn into(self: Self) -> [X; 2] { - [self.x.cast(), self.y.cast()] - } -} - -/// A size represented in logical pixels. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct LogicalSize

{ - pub width: P, - pub height: P, -} - -impl

LogicalSize

{ - #[inline] - pub const fn new(width: P, height: P) -> Self { - LogicalSize { width, height } - } -} - -impl LogicalSize

{ - #[inline] - pub fn from_physical>, X: Pixel>(physical: T, scale_factor: f64) -> Self { - physical.into().to_logical(scale_factor) - } - - #[inline] - pub fn to_physical(&self, scale_factor: f64) -> PhysicalSize { - assert!(validate_scale_factor(scale_factor)); - let width = self.width.into() * scale_factor; - let height = self.height.into() * scale_factor; - PhysicalSize::new(width, height).cast() - } - - #[inline] - pub fn cast(&self) -> LogicalSize { - LogicalSize { - width: self.width.cast(), - height: self.height.cast(), - } - } -} - -impl From<(X, X)> for LogicalSize

{ - fn from((x, y): (X, X)) -> LogicalSize

{ - LogicalSize::new(x.cast(), y.cast()) - } -} - -impl Into<(X, X)> for LogicalSize

{ - fn into(self: LogicalSize

) -> (X, X) { - (self.width.cast(), self.height.cast()) - } -} - -impl From<[X; 2]> for LogicalSize

{ - fn from([x, y]: [X; 2]) -> LogicalSize

{ - LogicalSize::new(x.cast(), y.cast()) - } -} - -impl Into<[X; 2]> for LogicalSize

{ - fn into(self: Self) -> [X; 2] { - [self.width.cast(), self.height.cast()] - } -} - -/// A size represented in physical pixels. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PhysicalSize

{ - pub width: P, - pub height: P, -} - -impl

PhysicalSize

{ - #[inline] - pub const fn new(width: P, height: P) -> Self { - PhysicalSize { width, height } - } -} - -impl PhysicalSize

{ - #[inline] - pub fn from_logical>, X: Pixel>(logical: T, scale_factor: f64) -> Self { - logical.into().to_physical(scale_factor) - } - - #[inline] - pub fn to_logical(&self, scale_factor: f64) -> LogicalSize { - assert!(validate_scale_factor(scale_factor)); - let width = self.width.into() / scale_factor; - let height = self.height.into() / scale_factor; - LogicalSize::new(width, height).cast() - } - - #[inline] - pub fn cast(&self) -> PhysicalSize { - PhysicalSize { - width: self.width.cast(), - height: self.height.cast(), - } - } -} - -impl From<(X, X)> for PhysicalSize

{ - fn from((x, y): (X, X)) -> PhysicalSize

{ - PhysicalSize::new(x.cast(), y.cast()) - } -} - -impl Into<(X, X)> for PhysicalSize

{ - fn into(self: Self) -> (X, X) { - (self.width.cast(), self.height.cast()) - } -} - -impl From<[X; 2]> for PhysicalSize

{ - fn from([x, y]: [X; 2]) -> PhysicalSize

{ - PhysicalSize::new(x.cast(), y.cast()) - } -} - -impl Into<[X; 2]> for PhysicalSize

{ - fn into(self: Self) -> [X; 2] { - [self.width.cast(), self.height.cast()] - } -} - -/// A size that's either physical or logical. -#[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum Size { - Physical(PhysicalSize), - Logical(LogicalSize), -} - -impl Size { - pub fn new>(size: S) -> Size { - size.into() - } - - pub fn to_logical(&self, scale_factor: f64) -> LogicalSize

{ - match *self { - Size::Physical(size) => size.to_logical(scale_factor), - Size::Logical(size) => size.cast(), - } - } - - pub fn to_physical(&self, scale_factor: f64) -> PhysicalSize

{ - match *self { - Size::Physical(size) => size.cast(), - Size::Logical(size) => size.to_physical(scale_factor), - } - } -} - -impl From> for Size { - #[inline] - fn from(size: PhysicalSize

) -> Size { - Size::Physical(size.cast()) - } -} - -impl From> for Size { - #[inline] - fn from(size: LogicalSize

) -> Size { - Size::Logical(size.cast()) - } -} - -/// A position that's either physical or logical. -#[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum Position { - Physical(PhysicalPosition), - Logical(LogicalPosition), -} - -impl Position { - pub fn new>(position: S) -> Position { - position.into() - } - - pub fn to_logical(&self, scale_factor: f64) -> LogicalPosition

{ - match *self { - Position::Physical(position) => position.to_logical(scale_factor), - Position::Logical(position) => position.cast(), - } - } - - pub fn to_physical(&self, scale_factor: f64) -> PhysicalPosition

{ - match *self { - Position::Physical(position) => position.cast(), - Position::Logical(position) => position.to_physical(scale_factor), - } - } -} - -impl From> for Position { - #[inline] - fn from(position: PhysicalPosition

) -> Position { - Position::Physical(position.cast()) - } -} - -impl From> for Position { - #[inline] - fn from(position: LogicalPosition

) -> Position { - Position::Logical(position.cast()) - } -} diff --git a/src/application/error.rs b/src/application/error.rs deleted file mode 100644 index c271110f8..000000000 --- a/src/application/error.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2019-2021 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -use std::{error, fmt}; - -/// An error whose cause it outside Winit's control. -#[derive(Debug)] -pub enum ExternalError { - /// The operation is not supported by the backend. - NotSupported(NotSupportedError), - /// The OS cannot perform the operation. - Os(OsError), -} - -/// The error type for when the requested operation is not supported by the backend. -#[derive(Clone)] -pub struct NotSupportedError { - _marker: (), -} - -/// The error type for when the OS cannot perform the requested operation. -#[derive(Debug)] -pub struct OsError { - line: u32, - file: &'static str, - error: &'static str, -} - -impl NotSupportedError { - #[inline] - #[allow(dead_code)] - pub(crate) fn new() -> NotSupportedError { - NotSupportedError { _marker: () } - } -} - -impl OsError { - #[allow(dead_code)] - pub(crate) fn new(line: u32, file: &'static str, error: &'static str) -> OsError { - OsError { line, file, error } - } -} - -#[allow(unused_macros)] -macro_rules! os_error { - ($error:expr) => {{ - crate::error::OsError::new(line!(), file!(), $error) - }}; -} - -impl fmt::Display for OsError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.pad(&format!( - "os error at {}:{}: {}", - self.file, self.line, self.error - )) - } -} - -impl fmt::Display for ExternalError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match self { - ExternalError::NotSupported(e) => e.fmt(f), - ExternalError::Os(e) => e.fmt(f), - } - } -} - -impl fmt::Debug for NotSupportedError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.debug_struct("NotSupportedError").finish() - } -} - -impl fmt::Display for NotSupportedError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.pad("the requested operation is not supported by Winit") - } -} - -impl error::Error for OsError {} -impl error::Error for ExternalError {} -impl error::Error for NotSupportedError {} diff --git a/src/application/event.rs b/src/application/event.rs deleted file mode 100644 index 8f72b28c7..000000000 --- a/src/application/event.rs +++ /dev/null @@ -1,1071 +0,0 @@ -// Copyright 2019-2021 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -//! The `Event` enum and assorted supporting types. -//! -//! These are sent to the closure given to [`EventLoop::run(...)`][event_loop_run], where they get -//! processed and used to modify the program state. For more details, see the root-level documentation. -//! -//! Some of these events represent different "parts" of a traditional event-handling loop. You could -//! approximate the basic ordering loop of [`EventLoop::run(...)`][event_loop_run] like this: -//! -//! ```rust,ignore -//! let mut control_flow = ControlFlow::Poll; -//! let mut start_cause = StartCause::Init; -//! -//! while control_flow != ControlFlow::Exit { -//! event_handler(NewEvents(start_cause), ..., &mut control_flow); -//! -//! for e in (window events, user events, device events) { -//! event_handler(e, ..., &mut control_flow); -//! } -//! event_handler(MainEventsCleared, ..., &mut control_flow); -//! -//! for w in (redraw windows) { -//! event_handler(RedrawRequested(w), ..., &mut control_flow); -//! } -//! event_handler(RedrawEventsCleared, ..., &mut control_flow); -//! -//! start_cause = wait_if_necessary(control_flow); -//! } -//! -//! event_handler(LoopDestroyed, ..., &mut control_flow); -//! ``` -//! -//! This leaves out timing details like `ControlFlow::WaitUntil` but hopefully -//! describes what happens in what order. -//! -//! [event_loop_run]: crate::event_loop::EventLoop::run -use std::{path::PathBuf, time::Instant}; - -use super::{ - dpi::{PhysicalPosition, PhysicalSize}, - window::{Theme, WindowId}, -}; - -/// Describes a generic event. -/// -/// See the module-level docs for more information on the event loop manages each event. -#[derive(Debug, PartialEq)] -pub enum Event<'a, T: 'static> { - /// Emitted when new events arrive from the OS to be processed. - /// - /// This event type is useful as a place to put code that should be done before you start - /// processing events, such as updating frame timing information for benchmarking or checking - /// the [`StartCause`][crate::event::StartCause] to see if a timer set by - /// [`ControlFlow::WaitUntil`](crate::event_loop::ControlFlow::WaitUntil) has elapsed. - NewEvents(StartCause), - - /// Emitted when the OS sends an event to a winit window. - WindowEvent { - window_id: WindowId, - event: WindowEvent<'a>, - }, - - /// Emitted when the OS sends an event to a device. - DeviceEvent { - device_id: DeviceId, - event: DeviceEvent, - }, - - /// Emitted when an event is sent from [`EventLoopProxy::send_event`](crate::event_loop::EventLoopProxy::send_event) - UserEvent(T), - - /// Emitted when the application has been suspended. - Suspended, - - /// Emitted when the application has been resumed. - Resumed, - - /// Emitted when all of the event loop's input events have been processed and redraw processing - /// is about to begin. - /// - /// This event is useful as a place to put your code that should be run after all - /// state-changing events have been handled and you want to do stuff (updating state, performing - /// calculations, etc) that happens as the "main body" of your event loop. If your program only draws - /// graphics when something changes, it's usually better to do it in response to - /// [`Event::RedrawRequested`](crate::event::Event::RedrawRequested), which gets emitted - /// immediately after this event. Programs that draw graphics continuously, like most games, - /// can render here unconditionally for simplicity. - MainEventsCleared, - - /// Emitted after `MainEventsCleared` when a window should be redrawn. - /// - /// This gets triggered in two scenarios: - /// - The OS has performed an operation that's invalidated the window's contents (such as - /// resizing the window). - /// - The application has explicitly requested a redraw via - /// [`Window::request_redraw`](crate::window::Window::request_redraw). - /// - /// During each iteration of the event loop, Winit will aggregate duplicate redraw requests - /// into a single event, to help avoid duplicating rendering work. - /// - /// Mainly of interest to applications with mostly-static graphics that avoid redrawing unless - /// something changes, like most non-game GUIs. - RedrawRequested(WindowId), - - /// Emitted after all `RedrawRequested` events have been processed and control flow is about to - /// be taken away from the program. If there are no `RedrawRequested` events, it is emitted - /// immediately after `MainEventsCleared`. - /// - /// This event is useful for doing any cleanup or bookkeeping work after all the rendering - /// tasks have been completed. - RedrawEventsCleared, - - /// Emitted when the event loop is being shut down. - /// - /// This is irreversable - if this event is emitted, it is guaranteed to be the last event that - /// gets emitted. You generally want to treat this as an "do on quit" event. - LoopDestroyed, -} - -impl Clone for Event<'static, T> { - fn clone(&self) -> Self { - use self::Event::*; - match self { - WindowEvent { window_id, event } => WindowEvent { - window_id: *window_id, - event: event.clone(), - }, - UserEvent(event) => UserEvent(event.clone()), - DeviceEvent { device_id, event } => DeviceEvent { - device_id: *device_id, - event: event.clone(), - }, - NewEvents(cause) => NewEvents(*cause), - MainEventsCleared => MainEventsCleared, - RedrawRequested(wid) => RedrawRequested(*wid), - RedrawEventsCleared => RedrawEventsCleared, - LoopDestroyed => LoopDestroyed, - Suspended => Suspended, - Resumed => Resumed, - } - } -} - -impl<'a, T> Event<'a, T> { - pub fn map_nonuser_event(self) -> Result, Event<'a, T>> { - use self::Event::*; - match self { - UserEvent(_) => Err(self), - WindowEvent { window_id, event } => Ok(WindowEvent { window_id, event }), - DeviceEvent { device_id, event } => Ok(DeviceEvent { device_id, event }), - NewEvents(cause) => Ok(NewEvents(cause)), - MainEventsCleared => Ok(MainEventsCleared), - RedrawRequested(wid) => Ok(RedrawRequested(wid)), - RedrawEventsCleared => Ok(RedrawEventsCleared), - LoopDestroyed => Ok(LoopDestroyed), - Suspended => Ok(Suspended), - Resumed => Ok(Resumed), - } - } - - /// If the event doesn't contain a reference, turn it into an event with a `'static` lifetime. - /// Otherwise, return `None`. - pub fn to_static(self) -> Option> { - use self::Event::*; - match self { - WindowEvent { window_id, event } => event - .to_static() - .map(|event| WindowEvent { window_id, event }), - UserEvent(event) => Some(UserEvent(event)), - DeviceEvent { device_id, event } => Some(DeviceEvent { device_id, event }), - NewEvents(cause) => Some(NewEvents(cause)), - MainEventsCleared => Some(MainEventsCleared), - RedrawRequested(wid) => Some(RedrawRequested(wid)), - RedrawEventsCleared => Some(RedrawEventsCleared), - LoopDestroyed => Some(LoopDestroyed), - Suspended => Some(Suspended), - Resumed => Some(Resumed), - } - } -} - -/// Describes the reason the event loop is resuming. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum StartCause { - /// Sent if the time specified by `ControlFlow::WaitUntil` has been reached. Contains the - /// moment the timeout was requested and the requested resume time. The actual resume time is - /// guaranteed to be equal to or after the requested resume time. - ResumeTimeReached { - start: Instant, - requested_resume: Instant, - }, - - /// 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. - WaitCancelled { - start: Instant, - requested_resume: Option, - }, - - /// Sent if the event loop is being resumed after the loop's control flow was set to - /// `ControlFlow::Poll`. - Poll, - - /// Sent once, immediately after `run` is called. Indicates that the loop was just initialized. - Init, -} - -/// Describes an event from a `Window`. -#[derive(Debug, PartialEq)] -pub enum WindowEvent<'a> { - /// The size of the window has changed. Contains the client area's new dimensions. - Resized(PhysicalSize), - - /// The position of the window has changed. Contains the window's new position. - Moved(PhysicalPosition), - - /// The window has been requested to close. - CloseRequested, - - /// The window has been destroyed. - Destroyed, - - /// A file has been dropped into the window. - /// - /// When the user drops multiple files at once, this event will be emitted for each file - /// separately. - DroppedFile(PathBuf), - - /// A file is being hovered over the window. - /// - /// When the user hovers multiple files at once, this event will be emitted for each file - /// separately. - HoveredFile(PathBuf), - - /// A file was hovered, but has exited the window. - /// - /// There will be a single `HoveredFileCancelled` event triggered even if multiple files were - /// hovered. - HoveredFileCancelled, - - /// The window received a unicode character. - ReceivedCharacter(char), - - /// The window gained or lost focus. - /// - /// The parameter is true if the window has gained focus, and false if it has lost focus. - Focused(bool), - - /// An event from the keyboard has been received. - KeyboardInput { - device_id: DeviceId, - input: KeyboardInput, - /// If `true`, the event was generated synthetically by winit - /// in one of the following circumstances: - /// - /// * Synthetic key press events are generated for all keys pressed - /// when a window gains focus. Likewise, synthetic key release events - /// are generated for all keys pressed when a window goes out of focus. - /// ***Currently, this is only functional on X11 and Windows*** - /// - /// Otherwise, this value is always `false`. - is_synthetic: bool, - }, - - /// The keyboard modifiers have changed. - /// - /// Platform-specific behavior: - /// - **Web**: This API is currently unimplemented on the web. This isn't by design - it's an - /// issue, and it should get fixed - but it's the current state of the API. - ModifiersChanged(ModifiersState), - - /// The cursor has moved on the window. - CursorMoved { - device_id: DeviceId, - - /// (x,y) coords in pixels relative to the top-left corner of the window. Because the range of this data is - /// limited by the display area and it may have been transformed by the OS to implement effects such as cursor - /// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control. - position: PhysicalPosition, - #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"] - modifiers: ModifiersState, - }, - - /// The cursor has entered the window. - CursorEntered { device_id: DeviceId }, - - /// The cursor has left the window. - CursorLeft { device_id: DeviceId }, - - /// A mouse wheel movement or touchpad scroll occurred. - MouseWheel { - device_id: DeviceId, - delta: MouseScrollDelta, - phase: TouchPhase, - #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"] - modifiers: ModifiersState, - }, - - /// An mouse button press has been received. - MouseInput { - device_id: DeviceId, - state: ElementState, - button: MouseButton, - #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"] - modifiers: ModifiersState, - }, - - /// Touchpad pressure event. - /// - /// At the moment, only supported on Apple forcetouch-capable macbooks. - /// The parameters are: pressure level (value between 0 and 1 representing how hard the touchpad - /// is being pressed) and stage (integer representing the click level). - TouchpadPressure { - device_id: DeviceId, - pressure: f32, - stage: i64, - }, - - /// Motion on some analog axis. May report data redundant to other, more specific events. - AxisMotion { - device_id: DeviceId, - axis: AxisId, - value: f64, - }, - - /// Touch event has been received - Touch(Touch), - - /// The window's scale factor has changed. - /// - /// The following user actions can cause DPI changes: - /// - /// * Changing the display's resolution. - /// * Changing the display's scale factor (e.g. in Control Panel on Windows). - /// * Moving the window to a display with a different scale factor. - /// - /// After this event callback has been processed, the window will be resized to whatever value - /// is pointed to by the `new_inner_size` reference. By default, this will contain the size suggested - /// by the OS, but it can be changed to any value. - /// - /// For more information about DPI in general, see the [`dpi`](crate::dpi) module. - ScaleFactorChanged { - scale_factor: f64, - new_inner_size: &'a mut PhysicalSize, - }, - - /// The system window theme has changed. - /// - /// Applications might wish to react to this to change the theme of the content of the window - /// when the system changes the window theme. - /// - /// At the moment this is only supported on Windows. - ThemeChanged(Theme), -} - -impl Clone for WindowEvent<'static> { - fn clone(&self) -> Self { - use self::WindowEvent::*; - return match self { - Resized(size) => Resized(*size), - Moved(pos) => Moved(*pos), - CloseRequested => CloseRequested, - Destroyed => Destroyed, - DroppedFile(file) => DroppedFile(file.clone()), - HoveredFile(file) => HoveredFile(file.clone()), - HoveredFileCancelled => HoveredFileCancelled, - ReceivedCharacter(c) => ReceivedCharacter(*c), - Focused(f) => Focused(*f), - KeyboardInput { - device_id, - input, - is_synthetic, - } => KeyboardInput { - device_id: *device_id, - input: *input, - is_synthetic: *is_synthetic, - }, - - ModifiersChanged(modifiers) => ModifiersChanged(*modifiers), - #[allow(deprecated)] - CursorMoved { - device_id, - position, - modifiers, - } => CursorMoved { - device_id: *device_id, - position: *position, - modifiers: *modifiers, - }, - CursorEntered { device_id } => CursorEntered { - device_id: *device_id, - }, - CursorLeft { device_id } => CursorLeft { - device_id: *device_id, - }, - #[allow(deprecated)] - MouseWheel { - device_id, - delta, - phase, - modifiers, - } => MouseWheel { - device_id: *device_id, - delta: *delta, - phase: *phase, - modifiers: *modifiers, - }, - #[allow(deprecated)] - MouseInput { - device_id, - state, - button, - modifiers, - } => MouseInput { - device_id: *device_id, - state: *state, - button: *button, - modifiers: *modifiers, - }, - TouchpadPressure { - device_id, - pressure, - stage, - } => TouchpadPressure { - device_id: *device_id, - pressure: *pressure, - stage: *stage, - }, - AxisMotion { - device_id, - axis, - value, - } => AxisMotion { - device_id: *device_id, - axis: *axis, - value: *value, - }, - Touch(touch) => Touch(*touch), - ThemeChanged(theme) => ThemeChanged(*theme), - ScaleFactorChanged { .. } => { - unreachable!("Static event can't be about scale factor changing") - } - }; - } -} - -impl<'a> WindowEvent<'a> { - pub fn to_static(self) -> Option> { - use self::WindowEvent::*; - match self { - Resized(size) => Some(Resized(size)), - Moved(position) => Some(Moved(position)), - CloseRequested => Some(CloseRequested), - Destroyed => Some(Destroyed), - DroppedFile(file) => Some(DroppedFile(file)), - HoveredFile(file) => Some(HoveredFile(file)), - HoveredFileCancelled => Some(HoveredFileCancelled), - ReceivedCharacter(c) => Some(ReceivedCharacter(c)), - Focused(focused) => Some(Focused(focused)), - KeyboardInput { - device_id, - input, - is_synthetic, - } => Some(KeyboardInput { - device_id, - input, - is_synthetic, - }), - ModifiersChanged(modifiers) => Some(ModifiersChanged(modifiers)), - #[allow(deprecated)] - CursorMoved { - device_id, - position, - modifiers, - } => Some(CursorMoved { - device_id, - position, - modifiers, - }), - CursorEntered { device_id } => Some(CursorEntered { device_id }), - CursorLeft { device_id } => Some(CursorLeft { device_id }), - #[allow(deprecated)] - MouseWheel { - device_id, - delta, - phase, - modifiers, - } => Some(MouseWheel { - device_id, - delta, - phase, - modifiers, - }), - #[allow(deprecated)] - MouseInput { - device_id, - state, - button, - modifiers, - } => Some(MouseInput { - device_id, - state, - button, - modifiers, - }), - TouchpadPressure { - device_id, - pressure, - stage, - } => Some(TouchpadPressure { - device_id, - pressure, - stage, - }), - AxisMotion { - device_id, - axis, - value, - } => Some(AxisMotion { - device_id, - axis, - value, - }), - Touch(touch) => Some(Touch(touch)), - ThemeChanged(theme) => Some(ThemeChanged(theme)), - ScaleFactorChanged { .. } => None, - } - } -} - -/// Identifier of an input device. -/// -/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which -/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or -/// physical. Virtual devices typically aggregate inputs from multiple physical devices. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId(pub(crate) usize); - -impl DeviceId { - /// Returns a dummy `DeviceId`, useful for unit testing. The only guarantee made about the return - /// value of this function is that it will always be equal to itself and to future values returned - /// by this function. No other guarantees are made. This may be equal to a real `DeviceId`. - /// - /// # Safety - /// **Passing this into a winit function will result in undefined behavior.** - pub unsafe fn dummy() -> Self { - DeviceId(0) - } -} - -/// Represents raw hardware events that are not associated with any particular window. -/// -/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person -/// game controls. Many physical actions, such as mouse movement, can produce both device and window events. Because -/// window events typically arise from virtual devices (corresponding to GUI cursors and keyboard focus) the device IDs -/// may not match. -/// -/// Note that these events are delivered regardless of input focus. -#[derive(Clone, Debug, PartialEq)] -pub enum DeviceEvent { - Added, - Removed, - - /// Change in physical position of a pointing device. - /// - /// This represents raw, unfiltered physical motion. Not to be confused with `WindowEvent::CursorMoved`. - MouseMotion { - /// (x, y) change in position in unspecified units. - /// - /// Different devices may use different units. - delta: (f64, f64), - }, - - /// Physical scroll event - MouseWheel { - delta: MouseScrollDelta, - }, - - /// Motion on some analog axis. This event will be reported for all arbitrary input devices - /// that winit supports on this platform, including mouse devices. If the device is a mouse - /// device then this will be reported alongside the MouseMotion event. - Motion { - axis: AxisId, - value: f64, - }, - - Button { - button: ButtonId, - state: ElementState, - }, - - Key(KeyboardInput), - - Text { - codepoint: char, - }, -} - -/// Describes a keyboard input event. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct KeyboardInput { - /// Identifies the physical key pressed - /// - /// This should not change if the user adjusts the host's keyboard map. Use when the physical location of the - /// key is more important than the key's host GUI semantics, such as for movement controls in a first-person - /// game. - pub scancode: ScanCode, - - pub state: ElementState, - - /// Identifies the semantic meaning of the key - /// - /// Use when the semantics of the key are more important than the physical location of the key, such as when - /// implementing appropriate behavior for "page up." - pub virtual_keycode: Option, - - /// Modifier keys active at the time of this input. - /// - /// This is tracked internally to avoid tracking errors arising from modifier key state changes when events from - /// this device are not being delivered to the application, e.g. due to keyboard focus being elsewhere. - #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"] - pub modifiers: ModifiersState, -} - -/// Describes touch-screen input state. -#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum TouchPhase { - Started, - Moved, - Ended, - Cancelled, -} - -/// Represents a touch event -/// -/// Every time the user touches the screen, a new `Start` event with an unique -/// identifier for the finger is generated. When the finger is lifted, an `End` -/// event is generated with the same finger id. -/// -/// After a `Start` event has been emitted, there may be zero or more `Move` -/// events when the finger is moved or the touch pressure changes. -/// -/// The finger id may be reused by the system after an `End` event. The user -/// should assume that a new `Start` event received with the same id has nothing -/// to do with the old finger and is a new finger. -/// -/// A `Cancelled` event is emitted when the system has canceled tracking this -/// touch, such as when the window loses focus, or on iOS if the user moves the -/// device against their face. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct Touch { - pub device_id: DeviceId, - pub phase: TouchPhase, - pub location: PhysicalPosition, - /// Describes how hard the screen was pressed. May be `None` if the platform - /// does not support pressure sensitivity. - /// - /// ## Platform-specific - /// - /// - Only available on **iOS** 9.0+ and **Windows** 8+. - pub force: Option, - /// Unique identifier of a finger. - pub id: u64, -} - -/// Describes the force of a touch event -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Force { - /// On iOS, the force is calibrated so that the same number corresponds to - /// roughly the same amount of pressure on the screen regardless of the - /// device. - Calibrated { - /// The force of the touch, where a value of 1.0 represents the force of - /// an average touch (predetermined by the system, not user-specific). - /// - /// The force reported by Apple Pencil is measured along the axis of the - /// pencil. If you want a force perpendicular to the device, you need to - /// calculate this value using the `altitude_angle` value. - force: f64, - /// The maximum possible force for a touch. - /// - /// The value of this field is sufficiently high to provide a wide - /// dynamic range for values of the `force` field. - max_possible_force: f64, - /// The altitude (in radians) of the stylus. - /// - /// A value of 0 radians indicates that the stylus is parallel to the - /// surface. The value of this property is Pi/2 when the stylus is - /// perpendicular to the surface. - altitude_angle: Option, - }, - /// If the platform reports the force as normalized, we have no way of - /// knowing how much pressure 1.0 corresponds to – we know it's the maximum - /// amount of force, but as to how much force, you might either have to - /// press really really hard, or not hard at all, depending on the device. - Normalized(f64), -} - -impl Force { - /// Returns the force normalized to the range between 0.0 and 1.0 inclusive. - /// Instead of normalizing the force, you should prefer to handle - /// `Force::Calibrated` so that the amount of force the user has to apply is - /// consistent across devices. - pub fn normalized(&self) -> f64 { - match self { - Force::Calibrated { - force, - max_possible_force, - altitude_angle, - } => { - let force = match altitude_angle { - Some(altitude_angle) => force / altitude_angle.sin(), - None => *force, - }; - force / max_possible_force - } - Force::Normalized(force) => *force, - } - } -} - -/// Hardware-dependent keyboard scan code. -pub type ScanCode = u32; - -/// Identifier for a specific analog axis on some device. -pub type AxisId = u32; - -/// Identifier for a specific button on some device. -pub type ButtonId = u32; - -/// Describes the input state of a key. -#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum ElementState { - Pressed, - Released, -} - -/// Describes a button of a mouse controller. -#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum MouseButton { - Left, - Right, - Middle, - Other(u16), -} - -/// Describes a difference in the mouse scroll wheel state. -#[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum MouseScrollDelta { - /// Amount in lines or rows to scroll in the horizontal - /// and vertical directions. - /// - /// Positive values indicate movement forward - /// (away from the user) or rightwards. - LineDelta(f32, f32), - /// Amount in pixels to scroll in the horizontal and - /// vertical direction. - /// - /// Scroll events are expressed as a PixelDelta if - /// supported by the device (eg. a touchpad) and - /// platform. - PixelDelta(PhysicalPosition), -} - -/// Symbolic name for a keyboard key. -#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)] -#[repr(u32)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum VirtualKeyCode { - /// The '1' key over the letters. - Key1, - /// The '2' key over the letters. - Key2, - /// The '3' key over the letters. - Key3, - /// The '4' key over the letters. - Key4, - /// The '5' key over the letters. - Key5, - /// The '6' key over the letters. - Key6, - /// The '7' key over the letters. - Key7, - /// The '8' key over the letters. - Key8, - /// The '9' key over the letters. - Key9, - /// The '0' key over the 'O' and 'P' keys. - Key0, - - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, - P, - Q, - R, - S, - T, - U, - V, - W, - X, - Y, - Z, - - /// The Escape key, next to F1. - Escape, - - F1, - F2, - F3, - F4, - F5, - F6, - F7, - F8, - F9, - F10, - F11, - F12, - F13, - F14, - F15, - F16, - F17, - F18, - F19, - F20, - F21, - F22, - F23, - F24, - - /// Print Screen/SysRq. - Snapshot, - /// Scroll Lock. - Scroll, - /// Pause/Break key, next to Scroll lock. - Pause, - - /// `Insert`, next to Backspace. - Insert, - Home, - Delete, - End, - PageDown, - PageUp, - - Left, - Up, - Right, - Down, - - /// The Backspace key, right over Enter. - // TODO: rename - Back, - /// The Enter key. - Return, - /// The space bar. - Space, - - /// The "Compose" key on Linux. - Compose, - - Caret, - - Numlock, - Numpad0, - Numpad1, - Numpad2, - Numpad3, - Numpad4, - Numpad5, - Numpad6, - Numpad7, - Numpad8, - Numpad9, - NumpadAdd, - NumpadDivide, - NumpadDecimal, - NumpadComma, - NumpadEnter, - NumpadEquals, - NumpadMultiply, - NumpadSubtract, - - AbntC1, - AbntC2, - Apostrophe, - Apps, - Asterisk, - At, - Ax, - Backslash, - Calculator, - Capital, - Colon, - Comma, - Convert, - Equals, - Grave, - Kana, - Kanji, - LAlt, - LBracket, - LControl, - LShift, - LWin, - Mail, - MediaSelect, - MediaStop, - Minus, - Mute, - MyComputer, - // also called "Next" - NavigateForward, - // also called "Prior" - NavigateBackward, - NextTrack, - NoConvert, - OEM102, - Period, - PlayPause, - Plus, - Power, - PrevTrack, - RAlt, - RBracket, - RControl, - RShift, - RWin, - Semicolon, - Slash, - Sleep, - Stop, - Sysrq, - Tab, - Underline, - Unlabeled, - VolumeDown, - VolumeUp, - Wake, - WebBack, - WebFavorites, - WebForward, - WebHome, - WebRefresh, - WebSearch, - WebStop, - Yen, - Copy, - Paste, - Cut, -} - -impl ModifiersState { - /// Returns `true` if the shift key is pressed. - pub fn shift(&self) -> bool { - self.intersects(Self::SHIFT) - } - /// Returns `true` if the control key is pressed. - pub fn ctrl(&self) -> bool { - self.intersects(Self::CTRL) - } - /// Returns `true` if the alt key is pressed. - pub fn alt(&self) -> bool { - self.intersects(Self::ALT) - } - /// Returns `true` if the logo key is pressed. - pub fn logo(&self) -> bool { - self.intersects(Self::LOGO) - } -} - -bitflags! { - /// Represents the current state of the keyboard modifiers - /// - /// Each flag represents a modifier and is set if this modifier is active. - #[derive(Default)] - pub struct ModifiersState: u32 { - // left and right modifiers are currently commented out, but we should be able to support - // them in a future release - /// The "shift" key. - const SHIFT = 0b100; - // const LSHIFT = 0b010 << 0; - // const RSHIFT = 0b001 << 0; - /// The "control" key. - const CTRL = 0b100 << 3; - // const LCTRL = 0b010 << 3; - // const RCTRL = 0b001 << 3; - /// The "alt" key. - const ALT = 0b100 << 6; - // const LALT = 0b010 << 6; - // const RALT = 0b001 << 6; - /// This is the "windows" key on PC and "command" key on Mac. - const LOGO = 0b100 << 9; - // const LLOGO = 0b010 << 9; - // const RLOGO = 0b001 << 9; - } -} - -#[cfg(feature = "serde")] -mod modifiers_serde { - use super::ModifiersState; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - #[derive(Default, Serialize, Deserialize)] - #[serde(default)] - #[serde(rename = "ModifiersState")] - pub struct ModifiersStateSerialize { - pub shift: bool, - pub ctrl: bool, - pub alt: bool, - pub logo: bool, - } - - impl Serialize for ModifiersState { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let s = ModifiersStateSerialize { - shift: self.shift(), - ctrl: self.ctrl(), - alt: self.alt(), - logo: self.logo(), - }; - s.serialize(serializer) - } - } - - impl<'de> Deserialize<'de> for ModifiersState { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let ModifiersStateSerialize { - shift, - ctrl, - alt, - logo, - } = ModifiersStateSerialize::deserialize(deserializer)?; - let mut m = ModifiersState::empty(); - m.set(ModifiersState::SHIFT, shift); - m.set(ModifiersState::CTRL, ctrl); - m.set(ModifiersState::ALT, alt); - m.set(ModifiersState::LOGO, logo); - Ok(m) - } - } -} diff --git a/src/application/event_loop.rs b/src/application/event_loop.rs deleted file mode 100644 index 270c4829a..000000000 --- a/src/application/event_loop.rs +++ /dev/null @@ -1,689 +0,0 @@ -// Copyright 2019-2021 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -//! The `EventLoop` struct and assorted supporting types, including `ControlFlow`. -//! -//! If you want to send custom events to the event loop, use [`EventLoop::create_proxy()`][create_proxy] -//! to acquire an [`EventLoopProxy`][event_loop_proxy] and call its [`send_event`][send_event] method. -//! -//! See the root-level documentation for information on how to create and use an event loop to -//! handle events. -//! -//! [create_proxy]: crate::event_loop::EventLoop::create_proxy -//! [event_loop_proxy]: crate::event_loop::EventLoopProxy -//! [send_event]: crate::event_loop::EventLoopProxy::send_event -use std::{ - cell::RefCell, - collections::HashSet, - error::Error, - fmt, - ops::Deref, - process, - rc::Rc, - sync::mpsc::{channel, Receiver, SendError, Sender}, - time::Instant, -}; - -use gdk::{Cursor, CursorType, WindowExt, WindowState}; -use gio::{prelude::*, Cancellable}; -use glib::{source::idle_add_local, Continue, MainContext}; -use gtk::{prelude::*, ApplicationWindow, Inhibit}; - -use super::{ - dpi::{PhysicalPosition, PhysicalSize}, - event::{DeviceId, ElementState, Event, ModifiersState, MouseButton, StartCause, WindowEvent}, - window::{CursorIcon, WindowId, WindowRequest}, -}; - -/// Target that associates windows with an `EventLoop`. -/// -/// This type exists to allow you to create new windows while Winit executes -/// your callback. `EventLoop` will coerce into this type (`impl Deref for -/// EventLoop`), so functions that take this as a parameter can also take -/// `&EventLoop`. -pub struct EventLoopWindowTarget { - /// Gtk application - pub(crate) app: gtk::Application, - /// Window Ids of the application - pub(crate) windows: Rc>>, - /// Window requests sender - pub(crate) window_requests_tx: Sender<(WindowId, WindowRequest)>, - /// Window requests receiver - pub(crate) window_requests_rx: Receiver<(WindowId, WindowRequest)>, - _marker: std::marker::PhantomData, - _unsafe: std::marker::PhantomData<*mut ()>, // Not Send nor Sync -} - -/// Provides a way to retrieve events from the system and from the windows that were registered to -/// the events loop. -/// -/// An `EventLoop` can be seen more or less as a "context". Calling `EventLoop::new()` -/// initializes everything that will be required to create windows. For example on Linux creating -/// an event loop opens a connection to the X or Wayland server. -/// -/// To wake up an `EventLoop` from a another thread, see the `EventLoopProxy` docs. -/// -/// Note that the `EventLoop` cannot be shared across threads (due to platform-dependant logic -/// forbidding it), as such it is neither `Send` nor `Sync`. If you need cross-thread access, the -/// `Window` created from this `EventLoop` _can_ be sent to an other thread, and the -/// `EventLoopProxy` allows you to wake up an `EventLoop` from another thread. -pub struct EventLoop { - /// Window target. - window_target: EventLoopWindowTarget, - /// User event sender for EventLoopProxy - user_event_tx: Sender, - /// User event receiver - user_event_rx: Receiver, - _unsafe: std::marker::PhantomData<*mut ()>, // Not Send nor Sync -} - -impl fmt::Debug for EventLoop { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("EventLoop { .. }") - } -} - -impl fmt::Debug for EventLoopWindowTarget { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("EventLoopWindowTarget { .. }") - } -} - -/// Set by the user callback given to the `EventLoop::run` method. -/// -/// Indicates the desired behavior of the event loop after [`Event::RedrawEventsCleared`][events_cleared] -/// is emitted. Defaults to `Poll`. -/// -/// ## Persistency -/// Almost every change is persistent between multiple calls to the event loop closure within a -/// given run loop. The only exception to this is `Exit` which, once set, cannot be unset. Changes -/// are **not** persistent between multiple calls to `run_return` - issuing a new call will reset -/// the control flow to `Poll`. -/// -/// [events_cleared]: crate::event::Event::RedrawEventsCleared -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum ControlFlow { - /// When the current loop iteration finishes, immediately begin a new iteration regardless of - /// whether or not new events are available to process. - /// - /// ## Platform-specific - /// - **Web:** Events are queued and usually sent when `requestAnimationFrame` fires but sometimes - /// the events in the queue may be sent before the next `requestAnimationFrame` callback, for - /// example when the scaling of the page has changed. This should be treated as an implementation - /// detail which should not be relied on. - Poll, - /// When the current loop iteration finishes, suspend the thread until another event arrives. - Wait, - /// When the current loop iteration finishes, suspend the thread until either another event - /// arrives or the given time is reached. - WaitUntil(Instant), - /// Send a `LoopDestroyed` event and stop the event loop. This variant is *sticky* - once set, - /// `control_flow` cannot be changed from `Exit`, and any future attempts to do so will result - /// in the `control_flow` parameter being reset to `Exit`. - Exit, -} - -impl Default for ControlFlow { - #[inline(always)] - fn default() -> ControlFlow { - ControlFlow::Poll - } -} - -impl EventLoop<()> { - /// Builds a new event loop with a `()` as the user event type. - /// - /// ***For cross-platform compatibility, the `EventLoop` must be created on the main thread.*** - /// Attempting to create the event loop on a different thread will panic. This restriction isn't - /// strictly necessary on all platforms, but is imposed to eliminate any nasty surprises when - /// porting to platforms that require it. `EventLoopExt::new_any_thread` functions are exposed - /// in the relevant `platform` module if the target platform supports creating an event loop on - /// any thread. - /// - /// Usage will result in display backend initialisation, this can be controlled on linux - /// using an environment variable `WINIT_UNIX_BACKEND`. Legal values are `x11` and `wayland`. - /// If it is not set, winit will try to connect to a wayland connection, and if it fails will - /// fallback on x11. If this variable is set with any other value, winit will panic. - /// - /// ## Platform-specific - /// - /// - **iOS:** Can only be called on the main thread. - pub fn new() -> EventLoop<()> { - EventLoop::<()>::with_user_event() - } -} - -impl EventLoop { - /// Builds a new event loop. - /// - /// All caveats documented in [`EventLoop::new`] apply to this function. - /// - /// ## Platform-specific - /// - /// - **iOS:** Can only be called on the main thread. - pub fn with_user_event() -> EventLoop { - assert_is_main_thread("new_any_thread"); - EventLoop::new_any_thread() - } - - pub(crate) fn new_any_thread() -> EventLoop { - EventLoop::new_gtk_any_thread().expect("Failed to initialize any backend!") - } - - fn new_gtk_any_thread() -> Result, Box> { - let app = gtk::Application::new(Some("org.tauri.wry"), gio::ApplicationFlags::empty())?; - let cancellable: Option<&Cancellable> = None; - app.register(cancellable)?; - - // Create event loop window target. - let (window_requests_tx, window_requests_rx) = channel(); - let window_target = EventLoopWindowTarget { - app, - windows: Rc::new(RefCell::new(HashSet::new())), - window_requests_tx, - window_requests_rx, - _marker: std::marker::PhantomData, - _unsafe: std::marker::PhantomData, - }; - - // Create user event channel - let (user_event_tx, user_event_rx) = channel(); - - // Create event loop itself. - let event_loop = Self { - window_target, - user_event_tx, - user_event_rx, - _unsafe: std::marker::PhantomData, - }; - - Ok(event_loop) - } - - /// Hijacks the calling thread and initializes the winit event loop with the provided - /// closure. Since the closure is `'static`, it must be a `move` closure if it needs to - /// access any data from the calling context. - /// - /// See the [`ControlFlow`] docs for information on how changes to `&mut ControlFlow` impact the - /// event loop's behavior. - /// - /// Any values not passed to this function will *not* be dropped. - /// - /// [`ControlFlow`]: crate::event_loop::ControlFlow - #[inline] - pub fn run(self, callback: F) -> ! - where - F: FnMut(Event<'_, T>, &EventLoopWindowTarget, &mut ControlFlow) + 'static, - { - self.run_return(callback); - process::exit(0) - } - - pub(crate) fn run_return(self, mut callback: F) - where - F: FnMut(Event<'_, T>, &EventLoopWindowTarget, &mut ControlFlow) + 'static, - { - let mut control_flow = ControlFlow::default(); - let window_target = self.window_target; - let (event_tx, event_rx) = channel::>(); - - // Send StartCause::Init event - let tx_clone = event_tx.clone(); - window_target.app.connect_activate(move |_| { - if let Err(e) = tx_clone.send(Event::NewEvents(StartCause::Init)) { - log::warn!("Failed to send init event to event channel: {}", e); - } - }); - window_target.app.activate(); - - let context = MainContext::default(); - context.push_thread_default(); - let keep_running = Rc::new(RefCell::new(true)); - let keep_running_ = keep_running.clone(); - let user_event_rx = self.user_event_rx; - idle_add_local(move || { - // User event - if let Ok(event) = user_event_rx.try_recv() { - if let Err(e) = event_tx.send(Event::UserEvent(event)) { - log::warn!("Failed to send user event to event channel: {}", e); - } - } - - // Widnow Request - if let Ok((id, request)) = window_target.window_requests_rx.try_recv() { - let window = window_target - .app - .get_window_by_id(id.0) - .expect("Failed to send closed window event!"); - - match request { - WindowRequest::Title(title) => window.set_title(&title), - WindowRequest::Position((x, y)) => window.move_(x, y), - WindowRequest::Size((w, h)) => window.resize(w, h), - WindowRequest::MinSize((min_width, min_height)) => window - .set_geometry_hints::( - None, - Some(&gdk::Geometry { - min_width, - min_height, - max_width: 0, - max_height: 0, - base_width: 0, - base_height: 0, - width_inc: 0, - height_inc: 0, - min_aspect: 0f64, - max_aspect: 0f64, - win_gravity: gdk::Gravity::Center, - }), - gdk::WindowHints::MIN_SIZE, - ), - WindowRequest::MaxSize((max_width, max_height)) => window - .set_geometry_hints::( - None, - Some(&gdk::Geometry { - min_width: 0, - min_height: 0, - max_width, - max_height, - base_width: 0, - base_height: 0, - width_inc: 0, - height_inc: 0, - min_aspect: 0f64, - max_aspect: 0f64, - win_gravity: gdk::Gravity::Center, - }), - gdk::WindowHints::MAX_SIZE, - ), - WindowRequest::Visible(visible) => { - if visible { - window.show_all(); - } else { - window.hide(); - } - } - WindowRequest::Resizable(resizable) => window.set_resizable(resizable), - WindowRequest::Minimized(minimized) => { - if minimized { - window.iconify(); - } else { - window.deiconify(); - } - } - WindowRequest::Maximized(maximized) => { - if maximized { - window.maximize(); - } else { - window.unmaximize(); - } - } - WindowRequest::DragWindow => { - let display = window.get_display(); - if let Some(cursor) = display - .get_device_manager() - .and_then(|device_manager| device_manager.get_client_pointer()) - { - let (_, x, y) = cursor.get_position(); - window.begin_move_drag(1, x, y, 0); - } - } - WindowRequest::Fullscreen(fullscreen) => match fullscreen { - Some(_) => window.fullscreen(), - None => window.unfullscreen(), - }, - WindowRequest::Decorations(decorations) => window.set_decorated(decorations), - WindowRequest::AlwaysOnTop(always_on_top) => window.set_keep_above(always_on_top), - WindowRequest::WindowIcon(window_icon) => { - if let Some(icon) = window_icon { - window.set_icon(Some(&icon.into())); - } - } - WindowRequest::UserAttention(request_type) => { - if request_type.is_some() { - window.set_urgency_hint(true) - } - } - WindowRequest::SkipTaskbar => window.set_skip_taskbar_hint(true), - WindowRequest::CursorIcon(cursor) => { - if let Some(gdk_window) = window.get_window() { - let display = window.get_display(); - match cursor { - Some(cr) => gdk_window.set_cursor( - Cursor::from_name( - &display, - match cr { - CursorIcon::Crosshair => "crosshair", - CursorIcon::Hand => "pointer", - CursorIcon::Arrow => "crosshair", - CursorIcon::Move => "move", - CursorIcon::Text => "text", - CursorIcon::Wait => "wait", - CursorIcon::Help => "help", - CursorIcon::Progress => "progress", - CursorIcon::NotAllowed => "not-allowed", - CursorIcon::ContextMenu => "context-menu", - CursorIcon::Cell => "cell", - CursorIcon::VerticalText => "vertical-text", - CursorIcon::Alias => "alias", - CursorIcon::Copy => "copy", - CursorIcon::NoDrop => "no-drop", - CursorIcon::Grab => "grab", - CursorIcon::Grabbing => "grabbing", - CursorIcon::AllScroll => "all-scroll", - CursorIcon::ZoomIn => "zoom-in", - CursorIcon::ZoomOut => "zoom-out", - CursorIcon::EResize => "e-resize", - CursorIcon::NResize => "n-resize", - CursorIcon::NeResize => "ne-resize", - CursorIcon::NwResize => "nw-resize", - CursorIcon::SResize => "s-resize", - CursorIcon::SeResize => "se-resize", - CursorIcon::SwResize => "sw-resize", - CursorIcon::WResize => "w-resize", - CursorIcon::EwResize => "ew-resize", - CursorIcon::NsResize => "ns-resize", - CursorIcon::NeswResize => "nesw-resize", - CursorIcon::NwseResize => "nwse-resize", - CursorIcon::ColResize => "col-resize", - CursorIcon::RowResize => "row-resize", - CursorIcon::Default => "default", - }, - ) - .as_ref(), - ), - None => gdk_window.set_cursor(Some(&Cursor::new_for_display( - &display, - CursorType::BlankCursor, - ))), - } - }; - } - WindowRequest::WireUpEvents => { - let windows_rc = window_target.windows.clone(); - let tx_clone = event_tx.clone(); - - window.connect_delete_event(move |_, _| { - windows_rc.borrow_mut().remove(&id); - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: id, - event: WindowEvent::CloseRequested, - }) { - log::warn!("Failed to send window close event to event channel: {}", e); - } - Inhibit(false) - }); - - let tx_clone = event_tx.clone(); - window.connect_configure_event(move |_, event| { - let (x, y) = event.get_position(); - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: id, - event: WindowEvent::Moved(PhysicalPosition::new(x, y)), - }) { - log::warn!("Failed to send window moved event to event channel: {}", e); - } - - let (w, h) = event.get_size(); - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: id, - event: WindowEvent::Resized(PhysicalSize::new(w, h)), - }) { - log::warn!( - "Failed to send window resized event to event channel: {}", - e - ); - } - false - }); - - let tx_clone = event_tx.clone(); - window.connect_window_state_event(move |_window, event| { - let state = event.get_new_window_state(); - - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: id, - event: WindowEvent::Focused(state.contains(WindowState::FOCUSED)), - }) { - log::warn!( - "Failed to send window focused event to event channel: {}", - e - ); - } - Inhibit(false) - }); - - let tx_clone = event_tx.clone(); - window.connect_destroy_event(move |_, _| { - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: id, - event: WindowEvent::Destroyed, - }) { - log::warn!( - "Failed to send window destroyed event to event channel: {}", - e - ); - } - Inhibit(false) - }); - - let tx_clone = event_tx.clone(); - window.connect_enter_notify_event(move |_, _| { - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: id, - event: WindowEvent::CursorEntered { - // FIXME: currently we use a dummy device id, find if we can get device id from gtk - device_id: DeviceId(0), - }, - }) { - log::warn!( - "Failed to send cursor entered event to event channel: {}", - e - ); - } - Inhibit(false) - }); - - let tx_clone = event_tx.clone(); - window.connect_motion_notify_event(move |window, _| { - let display = window.get_display(); - if let Some(cursor) = display - .get_device_manager() - .and_then(|device_manager| device_manager.get_client_pointer()) - { - let (_, x, y) = cursor.get_position(); - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: id, - event: WindowEvent::CursorMoved { - position: PhysicalPosition::new(x as f64, y as f64), - // FIXME: currently we use a dummy device id, find if we can get device id from gtk - device_id: DeviceId(0), - // this field is depracted so it is fine to pass empty state - modifiers: ModifiersState::empty(), - }, - }) { - log::warn!("Failed to send cursor moved event to event channel: {}", e); - } - } - Inhibit(false) - }); - - let tx_clone = event_tx.clone(); - window.connect_leave_notify_event(move |_, _| { - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: id, - event: WindowEvent::CursorLeft { - // FIXME: currently we use a dummy device id, find if we can get device id from gtk - device_id: DeviceId(0), - }, - }) { - log::warn!("Failed to send cursor left event to event channel: {}", e); - } - Inhibit(false) - }); - - let tx_clone = event_tx.clone(); - window.connect_button_press_event(move |_, event| { - let button = event.get_button(); - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: id, - event: WindowEvent::MouseInput { - button: match button { - 1 => MouseButton::Left, - 2 => MouseButton::Middle, - 3 => MouseButton::Right, - _ => MouseButton::Other(button as u16), - }, - state: ElementState::Pressed, - // FIXME: currently we use a dummy device id, find if we can get device id from gtk - device_id: DeviceId(0), - // this field is depracted so it is fine to pass empty state - modifiers: ModifiersState::empty(), - }, - }) { - log::warn!( - "Failed to send mouse input preseed event to event channel: {}", - e - ); - } - Inhibit(false) - }); - - let tx_clone = event_tx.clone(); - window.connect_button_release_event(move |_, event| { - let button = event.get_button(); - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: id, - event: WindowEvent::MouseInput { - button: match button { - 1 => MouseButton::Left, - 2 => MouseButton::Middle, - 3 => MouseButton::Right, - _ => MouseButton::Other(button as u16), - }, - state: ElementState::Released, - // FIXME: currently we use a dummy device id, find if we can get device id from gtk - device_id: DeviceId(0), - // this field is depracted so it is fine to pass empty state - modifiers: ModifiersState::empty(), - }, - }) { - log::warn!( - "Failed to send mouse input released event to event channel: {}", - e - ); - } - Inhibit(false) - }); - } - WindowRequest::Redraw => window.queue_draw(), - } - } - - // Event control flow - match control_flow { - ControlFlow::Exit => { - keep_running_.replace(false); - Continue(false) - } - // TODO better control flow handling - _ => { - if let Ok(event) = event_rx.try_recv() { - callback(event, &window_target, &mut control_flow); - } else { - callback(Event::MainEventsCleared, &window_target, &mut control_flow); - } - Continue(true) - } - } - }); - context.pop_thread_default(); - - while *keep_running.borrow() { - gtk::main_iteration(); - } - } - - #[inline] - pub fn window_target(&self) -> &EventLoopWindowTarget { - &self.window_target - } - - /// Creates an `EventLoopProxy` that can be used to dispatch user events to the main event loop. - pub fn create_proxy(&self) -> EventLoopProxy { - EventLoopProxy { - user_event_tx: self.user_event_tx.clone(), - } - } -} - -impl Deref for EventLoop { - type Target = EventLoopWindowTarget; - fn deref(&self) -> &EventLoopWindowTarget { - self.window_target() - } -} - -/// Used to send custom events to `EventLoop`. -#[derive(Debug, Clone)] -pub struct EventLoopProxy { - user_event_tx: Sender, -} - -impl EventLoopProxy { - /// Send an event to the `EventLoop` from which this proxy was created. This emits a - /// `UserEvent(event)` event in the event loop, where `event` is the value passed to this - /// function. - /// - /// Returns an `Err` if the associated `EventLoop` no longer exists. - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self - .user_event_tx - .send(event) - .map_err(|SendError(error)| EventLoopClosed(error)) - } -} - -fn assert_is_main_thread(suggested_method: &str) { - if !is_main_thread() { - panic!( - "Initializing the event loop outside of the main thread is a significant \ - cross-platform compatibility hazard. If you really, absolutely need to create an \ - EventLoop on a different thread, please use the `EventLoopExtUnix::{}` function.", - suggested_method - ); - } -} - -#[cfg(target_os = "linux")] -fn is_main_thread() -> bool { - use libc::{c_long, getpid, syscall, SYS_gettid}; - - unsafe { syscall(SYS_gettid) == getpid() as c_long } -} - -#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))] -fn is_main_thread() -> bool { - use libc::pthread_main_np; - - unsafe { pthread_main_np() == 1 } -} - -#[cfg(target_os = "netbsd")] -fn is_main_thread() -> bool { - std::thread::current().name() == Some("main") -} - -/// The error that is returned when an `EventLoopProxy` attempts to wake up an `EventLoop` that -/// no longer exists. Contains the original event given to `send_event`. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct EventLoopClosed(pub T); - -impl fmt::Display for EventLoopClosed { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Tried to wake up a closed `EventLoop`") - } -} - -impl Error for EventLoopClosed {} diff --git a/src/application/icon.rs b/src/application/icon.rs deleted file mode 100644 index 5a60609c8..000000000 --- a/src/application/icon.rs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2019-2021 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -use std::{error::Error, fmt, io}; - -use gdk_pixbuf::Pixbuf; - -#[derive(Debug)] -/// An error produced when using `Icon::from_rgba` with invalid arguments. -pub enum BadIcon { - /// Produced when the length of the `rgba` argument isn't divisible by 4, thus `rgba` can't be - /// safely interpreted as 32bpp RGBA pixels. - ByteCountNotDivisibleBy4 { byte_count: usize }, - /// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`. - /// At least one of your arguments is incorrect. - DimensionsVsPixelCount { - width: u32, - height: u32, - width_x_height: usize, - pixel_count: usize, - }, - /// Produced when underlying OS functionality failed to create the icon - OsError(io::Error), -} - -impl fmt::Display for BadIcon { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - BadIcon::ByteCountNotDivisibleBy4 { byte_count } => write!(f, - "The length of the `rgba` argument ({:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels.", - byte_count, - ), - BadIcon::DimensionsVsPixelCount { - width, - height, - width_x_height, - pixel_count, - } => write!(f, - "The specified dimensions ({:?}x{:?}) don't match the number of pixels supplied by the `rgba` argument ({:?}). For those dimensions, the expected pixel count is {:?}.", - width, height, pixel_count, width_x_height, - ), - BadIcon::OsError(e) => write!(f, "OS error when instantiating the icon: {:?}", e), - } - } -} - -impl Error for BadIcon { - fn source(&self) -> Option<&(dyn Error + 'static)> { - Some(self) - } -} - -/// An icon used for the window titlebar, taskbar, etc. -#[derive(Debug, Clone)] -pub struct Icon { - raw: Vec, - width: i32, - height: i32, - row_stride: i32, -} - -impl From for Pixbuf { - fn from(icon: Icon) -> Self { - Pixbuf::from_mut_slice( - icon.raw, - gdk_pixbuf::Colorspace::Rgb, - true, - 8, - icon.width, - icon.height, - icon.row_stride, - ) - } -} - -impl Icon { - /// Creates an `Icon` from 32bpp RGBA data. - /// - /// The length of `rgba` must be divisible by 4, and `width * height` must equal - /// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error. - pub fn from_rgba(rgba: Vec, width: u32, height: u32) -> Result { - let image = image::load_from_memory(&rgba) - .map_err(|_| { - BadIcon::OsError(io::Error::new( - io::ErrorKind::InvalidData, - "Invalid icon data!", - )) - })? - .into_rgba8(); - let row_stride = image.sample_layout().height_stride; - Ok(Icon { - raw: image.into_raw(), - width: width as i32, - height: height as i32, - row_stride: row_stride as i32, - }) - } -} diff --git a/src/application/mod.rs b/src/application/mod.rs index 7cd581319..50ac22b58 100644 --- a/src/application/mod.rs +++ b/src/application/mod.rs @@ -17,123 +17,5 @@ //! missing. Feel free to open an issue or PR. //! //! [winit]: https://crates.io/crates/winit -#[cfg(target_os = "linux")] -pub mod dpi; -#[cfg(target_os = "linux")] -pub mod error; -#[cfg(target_os = "linux")] -pub mod event; -#[cfg(target_os = "linux")] -pub mod event_loop; -#[cfg(target_os = "linux")] -mod icon; -#[cfg(target_os = "linux")] -pub mod monitor; -#[cfg(target_os = "linux")] -pub mod platform; -#[cfg(target_os = "linux")] -pub mod window; -#[cfg(not(target_os = "linux"))] -pub use original::*; -#[cfg(not(target_os = "linux"))] -mod original { - pub use winit::{dpi, error, event, event_loop, monitor, window}; - - #[cfg(target_os = "macos")] - pub use winit::platform; - #[cfg(target_os = "windows")] - pub mod platform { - pub use winit::platform::run_return; - - pub mod windows { - use winapi::Interface; - use winit::platform::windows::WindowExtWindows as WindowExtWindows_; - pub use winit::platform::windows::{ - DeviceIdExtWindows, EventLoopExtWindows, IconExtWindows, MonitorHandleExtWindows, - WindowBuilderExtWindows, - }; - - #[cfg(feature = "winrt")] - use windows_webview2::Windows::Win32::{Shell as shell, WindowsAndMessaging::HWND}; - use winit::window::{Icon, Theme, Window}; - #[cfg(feature = "win32")] - use { - std::ptr, - winapi::{ - shared::windef::HWND, - um::{ - combaseapi::{CoCreateInstance, CLSCTX_SERVER}, - shobjidl_core::{CLSID_TaskbarList, ITaskbarList}, - }, - }, - }; - - /// Additional methods on `Window` that are specific to Windows. - pub trait WindowExtWindows { - /// Returns the HINSTANCE of the window - fn hinstance(&self) -> *mut libc::c_void; - /// Returns the native handle that is used by this window. - /// - /// The pointer will become invalid when the native window was destroyed. - fn hwnd(&self) -> *mut libc::c_void; - - /// This sets `ICON_BIG`. A good ceiling here is 256x256. - fn set_taskbar_icon(&self, taskbar_icon: Option); - - /// This removes taskbar icon of the application. - fn skip_taskbar(&self); - - /// Returns the current window theme. - fn theme(&self) -> Theme; - } - - impl WindowExtWindows for Window { - #[inline] - fn hinstance(&self) -> *mut libc::c_void { - WindowExtWindows_::hinstance(self) - } - - #[inline] - fn hwnd(&self) -> *mut libc::c_void { - WindowExtWindows_::hwnd(self) - } - - #[inline] - fn set_taskbar_icon(&self, taskbar_icon: Option) { - WindowExtWindows_::set_taskbar_icon(self, taskbar_icon) - } - - #[inline] - fn skip_taskbar(&self) { - #[cfg(feature = "winrt")] - unsafe { - if let Ok(taskbar_list) = - windows::create_instance::(&shell::TaskbarList) - { - let _ = taskbar_list.DeleteTab(HWND(WindowExtWindows_::hwnd(self) as _)); - } - } - #[cfg(feature = "win32")] - unsafe { - let mut taskbar_list: *mut ITaskbarList = std::mem::zeroed(); - CoCreateInstance( - &CLSID_TaskbarList, - ptr::null_mut(), - CLSCTX_SERVER, - &ITaskbarList::uuidof(), - &mut taskbar_list as *mut _ as *mut _, - ); - (*taskbar_list).DeleteTab(WindowExtWindows_::hwnd(self) as HWND); - (*taskbar_list).Release(); - } - } - - #[inline] - fn theme(&self) -> Theme { - WindowExtWindows_::theme(self) - } - } - } - } -} +pub use tao::*; diff --git a/src/application/monitor.rs b/src/application/monitor.rs deleted file mode 100644 index 4a7595b7f..000000000 --- a/src/application/monitor.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019-2021 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -//! Types useful for interacting with a user's monitors. -//! -//! If you want to get basic information about a monitor, you can use the [`MonitorHandle`][monitor_handle] -//! type. This is retrieved from one of the following methods, which return an iterator of -//! [`MonitorHandle`][monitor_handle]: -//! - [`EventLoopWindowTarget::available_monitors`][loop_get] -//! - [`Window::available_monitors`][window_get]. -//! -//! [monitor_handle]: crate::monitor::MonitorHandle -//! [loop_get]: crate::event_loop::EventLoopWindowTarget::available_monitors -//! [window_get]: crate::window::Window::available_monitors - -/// Describes a fullscreen video mode of a monitor. -/// -/// Can be acquired with: -/// - [`MonitorHandle::video_modes`][monitor_get]. -/// -/// [monitor_get]: crate::monitor::MonitorHandle::video_modes -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct VideoMode {} - -/// Handle to a monitor. -/// -/// Allows you to retrieve information about a given monitor and can be used in [`Window`] creation. -/// -/// [`Window`]: crate::window::Window -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct MonitorHandle {} - -// TODO impl methods diff --git a/src/application/platform.rs b/src/application/platform.rs deleted file mode 100644 index 5e53d536d..000000000 --- a/src/application/platform.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019-2021 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -pub mod unix { - use crate::application::window::{Window, WindowRequest}; - - pub trait WindowExtUnix { - fn skip_taskbar(&self); - } - - impl WindowExtUnix for Window { - fn skip_taskbar(&self) { - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::SkipTaskbar)) - { - log::warn!("Fail to send skip taskbar request: {}", e); - } - } - } -} diff --git a/src/application/window.rs b/src/application/window.rs deleted file mode 100644 index 9d7381519..000000000 --- a/src/application/window.rs +++ /dev/null @@ -1,989 +0,0 @@ -// Copyright 2019-2021 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -//! The `Window` struct and associated types. - -use std::{ - cell::RefCell, - fmt, - rc::Rc, - sync::{ - atomic::{AtomicBool, AtomicI32, Ordering}, - mpsc::Sender, - }, -}; - -use gdk::{Cursor, EventMask, WindowEdge, WindowExt, WindowState}; -use gtk::{prelude::*, ApplicationWindow}; - -use super::{ - dpi::{PhysicalPosition, PhysicalSize, Position, Size}, - error::{ExternalError, NotSupportedError, OsError}, - event_loop::EventLoopWindowTarget, - monitor::{MonitorHandle, VideoMode}, -}; - -pub use super::icon::{BadIcon, Icon}; - -/// Identifier of a window. Unique for each window. -/// -/// Can be obtained with `window.id()`. -/// -/// Whenever you receive an event specific to a window, this event contains a `WindowId` which you -/// can then compare to the ids of your windows. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct WindowId(pub(crate) u32); - -impl WindowId { - /// Returns a dummy `WindowId`, useful for unit testing. The only guarantee made about the return - /// value of this function is that it will always be equal to itself and to future values returned - /// by this function. No other guarantees are made. This may be equal to a real `WindowId`. - /// - /// # Safety - /// **Passing this into a winit function will result in undefined behavior.** - pub unsafe fn dummy() -> Self { - WindowId(0) - } -} - -/// Attributes to use when creating a window. -#[derive(Debug, Clone)] -pub struct WindowAttributes { - /// The dimensions of the window. If this is `None`, some platform-specific dimensions will be - /// used. - /// - /// The default is `None`. - pub inner_size: Option, - - /// The minimum dimensions a window can be, If this is `None`, the window will have no minimum dimensions (aside from reserved). - /// - /// The default is `None`. - pub min_inner_size: Option, - - /// The maximum dimensions a window can be, If this is `None`, the maximum will have no maximum or will be set to the primary monitor's dimensions by the platform. - /// - /// The default is `None`. - pub max_inner_size: Option, - - /// The desired position of the window. If this is `None`, some platform-specific position - /// will be chosen. - /// - /// The default is `None`. - /// - /// ## Platform-specific - /// - /// - **macOS**: The top left corner position of the window content, the window's "inner" - /// position. The window title bar will be placed above it. - /// The window will be positioned such that it fits on screen, maintaining - /// set `inner_size` if any. - /// If you need to precisely position the top left corner of the whole window you have to - /// use [`Window::set_outer_position`] after creating the window. - /// - **Windows**: The top left corner position of the window title bar, the window's "outer" - /// position. - /// There may be a small gap between this position and the window due to the specifics of the - /// Window Manager. - /// - **X11**: The top left corner of the window, the window's "outer" position. - /// - **Others**: Ignored. - /// - /// See [`Window::set_outer_position`]. - /// - /// [`Window::set_outer_position`]: crate::window::Window::set_outer_position - pub position: Option, - - /// Whether the window is resizable or not. - /// - /// The default is `true`. - pub resizable: bool, - - /// Whether the window should be set as fullscreen upon creation. - /// - /// The default is `None`. - pub fullscreen: Option, - - /// The title of the window in the title bar. - /// - /// The default is `"winit window"`. - pub title: String, - - /// Whether the window should be maximized upon creation. - /// - /// The default is `false`. - pub maximized: bool, - - /// Whether the window should be immediately visible upon creation. - /// - /// The default is `true`. - pub visible: bool, - - /// Whether the the window should be transparent. If this is true, writing colors - /// with alpha values different than `1.0` will produce a transparent window. - /// - /// The default is `false`. - pub transparent: bool, - - /// Whether the window should have borders and bars. - /// - /// The default is `true`. - pub decorations: bool, - - /// Whether the window should always be on top of other windows. - /// - /// The default is `false`. - pub always_on_top: bool, - - /// The window icon. - /// - /// The default is `None`. - pub window_icon: Option, -} - -impl Default for WindowAttributes { - #[inline] - fn default() -> WindowAttributes { - WindowAttributes { - inner_size: None, - min_inner_size: None, - max_inner_size: None, - position: None, - resizable: true, - title: "winit window".to_owned(), - maximized: false, - fullscreen: None, - visible: true, - transparent: false, - decorations: true, - always_on_top: false, - window_icon: None, - } - } -} - -/// Object that allows you to build windows. -#[derive(Clone, Default)] -pub struct WindowBuilder { - /// The attributes to use to create the window. - pub window: WindowAttributes, -} - -impl fmt::Debug for WindowBuilder { - fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result { - fmtr - .debug_struct("WindowBuilder") - .field("window", &self.window) - .finish() - } -} - -impl WindowBuilder { - /// Initializes a new `WindowBuilder` with default values. - #[inline] - pub fn new() -> Self { - Default::default() - } - - /// Requests the window to be of specific dimensions. - /// - /// See [`Window::set_inner_size`] for details. - /// - /// [`Window::set_inner_size`]: crate::window::Window::set_inner_size - #[inline] - pub fn with_inner_size>(mut self, size: S) -> Self { - self.window.inner_size = Some(size.into()); - self - } - - /// Sets a minimum dimension size for the window. - /// - /// See [`Window::set_min_inner_size`] for details. - /// - /// [`Window::set_min_inner_size`]: crate::window::Window::set_min_inner_size - #[inline] - pub fn with_min_inner_size>(mut self, min_size: S) -> Self { - self.window.min_inner_size = Some(min_size.into()); - self - } - - /// Sets a maximum dimension size for the window. - /// - /// See [`Window::set_max_inner_size`] for details. - /// - /// [`Window::set_max_inner_size`]: crate::window::Window::set_max_inner_size - #[inline] - pub fn with_max_inner_size>(mut self, max_size: S) -> Self { - self.window.max_inner_size = Some(max_size.into()); - self - } - - /// Sets a desired initial position for the window. - /// - /// See [`WindowAttributes::position`] for details. - /// - /// [`WindowAttributes::position`]: crate::window::WindowAttributes::position - #[inline] - pub fn with_position>(mut self, position: P) -> Self { - self.window.position = Some(position.into()); - self - } - - /// Sets whether the window is resizable or not. - /// - /// See [`Window::set_resizable`] for details. - /// - /// [`Window::set_resizable`]: crate::window::Window::set_resizable - #[inline] - pub fn with_resizable(mut self, resizable: bool) -> Self { - self.window.resizable = resizable; - self - } - - /// Requests a specific title for the window. - /// - /// See [`Window::set_title`] for details. - /// - /// [`Window::set_title`]: crate::window::Window::set_title - #[inline] - pub fn with_title>(mut self, title: T) -> Self { - self.window.title = title.into(); - self - } - - /// Sets the window fullscreen state. - /// - /// See [`Window::set_fullscreen`] for details. - /// - /// [`Window::set_fullscreen`]: crate::window::Window::set_fullscreen - #[inline] - pub fn with_fullscreen(mut self, fullscreen: Option) -> Self { - self.window.fullscreen = fullscreen; - self - } - - /// Requests maximized mode. - /// - /// See [`Window::set_maximized`] for details. - /// - /// [`Window::set_maximized`]: crate::window::Window::set_maximized - #[inline] - pub fn with_maximized(mut self, maximized: bool) -> Self { - self.window.maximized = maximized; - self - } - - /// Sets whether the window will be initially hidden or visible. - /// - /// See [`Window::set_visible`] for details. - /// - /// [`Window::set_visible`]: crate::window::Window::set_visible - #[inline] - pub fn with_visible(mut self, visible: bool) -> Self { - self.window.visible = visible; - self - } - - /// Sets whether the background of the window should be transparent. - #[inline] - pub fn with_transparent(mut self, transparent: bool) -> Self { - self.window.transparent = transparent; - self - } - - /// Sets whether the window should have a border, a title bar, etc. - /// - /// See [`Window::set_decorations`] for details. - /// - /// [`Window::set_decorations`]: crate::window::Window::set_decorations - #[inline] - pub fn with_decorations(mut self, decorations: bool) -> Self { - self.window.decorations = decorations; - self - } - - /// Sets whether or not the window will always be on top of other windows. - /// - /// See [`Window::set_always_on_top`] for details. - /// - /// [`Window::set_always_on_top`]: crate::window::Window::set_always_on_top - #[inline] - pub fn with_always_on_top(mut self, always_on_top: bool) -> Self { - self.window.always_on_top = always_on_top; - self - } - - /// Sets the window icon. - /// - /// See [`Window::set_window_icon`] for details. - /// - /// [`Window::set_window_icon`]: crate::window::Window::set_window_icon - #[inline] - pub fn with_window_icon(mut self, window_icon: Option) -> Self { - self.window.window_icon = window_icon; - self - } - - /// Builds the window. - /// - /// Possible causes of error include denied permission, incompatible system, and lack of memory. - /// - /// Platform-specific behavior: - /// - **Web**: The window is created but not inserted into the web page automatically. Please - /// see the web platform module for more information. - #[inline] - pub fn build( - self, - window_target: &EventLoopWindowTarget, - ) -> Result { - Window::new(window_target, self.window) - } -} - -/// Represents a window. -pub struct Window { - /// Window id. - pub(crate) window_id: WindowId, - /// Gtk application window. - pub(crate) window: gtk::ApplicationWindow, - /// Window requests sender - pub(crate) window_requests_tx: Sender<(WindowId, WindowRequest)>, - scale_factor: Rc, - position: Rc<(AtomicI32, AtomicI32)>, - size: Rc<(AtomicI32, AtomicI32)>, - maximized: Rc, - fullscreen: RefCell>, -} - -impl Window { - pub(crate) fn new( - event_loop_window_target: &EventLoopWindowTarget, - attributes: WindowAttributes, - ) -> Result { - let app = &event_loop_window_target.app; - let window = gtk::ApplicationWindow::new(app); - let window_id = WindowId(window.get_id()); - event_loop_window_target - .windows - .borrow_mut() - .insert(window_id); - - // Set Width/Height & Resizable - let win_scale_factor = window.get_scale_factor(); - let (width, height) = attributes - .inner_size - .map(|size| size.to_logical::(win_scale_factor as f64).into()) - .unwrap_or((800, 600)); - window.set_resizable(attributes.resizable); - if attributes.resizable { - window.set_default_size(width, height); - } else { - window.set_size_request(width, height); - } - - // Set Min/Max Size - let geom_mask = (if attributes.min_inner_size.is_some() { - gdk::WindowHints::MIN_SIZE - } else { - gdk::WindowHints::empty() - }) | (if attributes.max_inner_size.is_some() { - gdk::WindowHints::MAX_SIZE - } else { - gdk::WindowHints::empty() - }); - let (min_width, min_height) = attributes - .min_inner_size - .map(|size| size.to_logical::(win_scale_factor as f64).into()) - .unwrap_or_default(); - let (max_width, max_height) = attributes - .max_inner_size - .map(|size| size.to_logical::(win_scale_factor as f64).into()) - .unwrap_or_default(); - window.set_geometry_hints::( - None, - Some(&gdk::Geometry { - min_width, - min_height, - max_width, - max_height, - base_width: 0, - base_height: 0, - width_inc: 0, - height_inc: 0, - min_aspect: 0f64, - max_aspect: 0f64, - win_gravity: gdk::Gravity::Center, - }), - geom_mask, - ); - - // Set Position - if let Some(position) = attributes.position { - let (x, y): (i32, i32) = position.to_physical::(win_scale_factor as f64).into(); - window.move_(x, y); - } - - // Set Transparent - if attributes.transparent { - if let Some(screen) = window.get_screen() { - if let Some(visual) = screen.get_rgba_visual() { - window.set_visual(Some(&visual)); - } - } - - window.connect_draw(|_, cr| { - cr.set_source_rgba(0., 0., 0., 0.); - cr.set_operator(cairo::Operator::Source); - cr.paint(); - cr.set_operator(cairo::Operator::Over); - Inhibit(false) - }); - window.set_app_paintable(true); - } - - // Rest attributes - window.set_title(&attributes.title); - if attributes.fullscreen.is_some() { - window.fullscreen(); - } - if attributes.maximized { - window.maximize(); - } - window.set_decorated(attributes.decorations); - - if !attributes.decorations && attributes.resizable { - window.add_events(EventMask::POINTER_MOTION_MASK | EventMask::BUTTON_MOTION_MASK); - - window.connect_motion_notify_event(|window, event| { - if let Some(gdk_window) = window.get_window() { - let (cx, cy) = event.get_root(); - hit_test(&gdk_window, cx, cy); - } - Inhibit(false) - }); - - window.connect_button_press_event(|window, event| { - if event.get_button() == 1 { - if let Some(gdk_window) = window.get_window() { - let (cx, cy) = event.get_root(); - let result = hit_test(&gdk_window, cx, cy); - - // this check is necessary, otherwise the window won't recieve the click properly when resize isn't needed - if result != WindowEdge::__Unknown(8) { - window.begin_resize_drag(result, 1, cx as i32, cy as i32, event.get_time()); - } - } - } - Inhibit(false) - }); - } - - window.set_keep_above(attributes.always_on_top); - if let Some(icon) = attributes.window_icon { - window.set_icon(Some(&icon.into())); - } - - if attributes.visible { - window.show_all(); - } else { - window.hide(); - } - - let window_requests_tx = event_loop_window_target.window_requests_tx.clone(); - - let w_pos = window.get_position(); - let position: Rc<(AtomicI32, AtomicI32)> = Rc::new((w_pos.0.into(), w_pos.1.into())); - let position_clone = position.clone(); - - let w_size = window.get_size(); - let size: Rc<(AtomicI32, AtomicI32)> = Rc::new((w_size.0.into(), w_size.1.into())); - let size_clone = size.clone(); - - window.connect_configure_event(move |_window, event| { - let (x, y) = event.get_position(); - position_clone.0.store(x, Ordering::Release); - position_clone.1.store(y, Ordering::Release); - - let (w, h) = event.get_size(); - size_clone.0.store(w as i32, Ordering::Release); - size_clone.1.store(h as i32, Ordering::Release); - - false - }); - - let w_max = window.get_property_is_maximized(); - let maximized: Rc = Rc::new(w_max.into()); - let max_clone = maximized.clone(); - - window.connect_window_state_event(move |_window, event| { - let state = event.get_new_window_state(); - max_clone.store(state.contains(WindowState::MAXIMIZED), Ordering::Release); - - Inhibit(false) - }); - - let scale_factor: Rc = Rc::new(win_scale_factor.into()); - let scale_factor_clone = scale_factor.clone(); - window.connect_property_scale_factor_notify(move |window| { - scale_factor_clone.store(window.get_scale_factor(), Ordering::Release); - }); - - if let Err(e) = window_requests_tx.send((window_id, WindowRequest::WireUpEvents)) { - log::warn!("Fail to send wire up events request: {}", e); - } - - window.queue_draw(); - Ok(Self { - window_id, - window, - window_requests_tx, - scale_factor, - position, - size, - maximized, - fullscreen: RefCell::new(attributes.fullscreen), - }) - } - - pub fn id(&self) -> WindowId { - self.window_id - } - - pub fn scale_factor(&self) -> f64 { - self.scale_factor.load(Ordering::Acquire) as f64 - } - - pub(crate) fn close(&self) { - self.window.close(); - } - - pub fn request_redraw(&self) { - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::Redraw)) - { - log::warn!("Fail to send redraw request: {}", e); - } - } - - pub fn inner_position(&self) -> Result, NotSupportedError> { - let (x, y) = &*self.position; - Ok(PhysicalPosition::new( - x.load(Ordering::Acquire), - y.load(Ordering::Acquire), - )) - } - - pub fn outer_position(&self) -> Result, NotSupportedError> { - let (x, y) = &*self.position; - Ok(PhysicalPosition::new( - x.load(Ordering::Acquire), - y.load(Ordering::Acquire), - )) - } - - pub fn set_outer_position>(&self, position: P) { - let (x, y): (i32, i32) = position - .into() - .to_physical::(self.scale_factor()) - .into(); - - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::Position((x, y)))) - { - log::warn!("Fail to send position request: {}", e); - } - } - - pub fn inner_size(&self) -> PhysicalSize { - let (width, height) = &*self.size; - - PhysicalSize::new( - width.load(Ordering::Acquire) as u32, - height.load(Ordering::Acquire) as u32, - ) - } - - pub fn set_inner_size>(&self, size: S) { - let (width, height) = size.into().to_logical::(self.scale_factor()).into(); - - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::Size((width, height)))) - { - log::warn!("Fail to send size request: {}", e); - } - } - - pub fn outer_size(&self) -> PhysicalSize { - let (width, height) = &*self.size; - - PhysicalSize::new( - width.load(Ordering::Acquire) as u32, - height.load(Ordering::Acquire) as u32, - ) - } - - pub fn set_min_inner_size>(&self, min_size: Option) { - if let Some(size) = min_size { - let (min_width, min_height) = size.into().to_logical::(self.scale_factor()).into(); - - if let Err(e) = self.window_requests_tx.send(( - self.window_id, - WindowRequest::MinSize((min_width, min_height)), - )) { - log::warn!("Fail to send min size request: {}", e); - } - } - } - pub fn set_max_inner_size>(&self, max_size: Option) { - if let Some(size) = max_size { - let (max_width, max_height) = size.into().to_logical::(self.scale_factor()).into(); - - if let Err(e) = self.window_requests_tx.send(( - self.window_id, - WindowRequest::MaxSize((max_width, max_height)), - )) { - log::warn!("Fail to send max size request: {}", e); - } - } - } - - pub fn set_title(&self, title: &str) { - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::Title(title.to_string()))) - { - log::warn!("Fail to send title request: {}", e); - } - } - - pub fn set_visible(&self, visible: bool) { - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::Visible(visible))) - { - log::warn!("Fail to send visible request: {}", e); - } - } - - pub fn set_resizable(&self, resizable: bool) { - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::Resizable(resizable))) - { - log::warn!("Fail to send resizable request: {}", e); - } - } - - pub fn set_minimized(&self, minimized: bool) { - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::Minimized(minimized))) - { - log::warn!("Fail to send minimized request: {}", e); - } - } - - pub fn set_maximized(&self, maximized: bool) { - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::Maximized(maximized))) - { - log::warn!("Fail to send maximized request: {}", e); - } - } - - pub fn is_maximized(&self) -> bool { - self.maximized.load(Ordering::Acquire) - } - - pub fn drag_window(&self) -> Result<(), ExternalError> { - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::DragWindow)) - { - log::warn!("Fail to send drag window request: {}", e); - } - Ok(()) - } - - pub fn set_fullscreen(&self, fullscreen: Option) { - self.fullscreen.replace(fullscreen.clone()); - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::Fullscreen(fullscreen))) - { - log::warn!("Fail to send fullscreen request: {}", e); - } - } - - pub fn fullscreen(&self) -> Option { - self.fullscreen.borrow().clone() - } - - pub fn set_decorations(&self, decorations: bool) { - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::Decorations(decorations))) - { - log::warn!("Fail to send decorations request: {}", e); - } - } - - pub fn set_always_on_top(&self, always_on_top: bool) { - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::AlwaysOnTop(always_on_top))) - { - log::warn!("Fail to send always on top request: {}", e); - } - } - - pub fn set_window_icon(&self, window_icon: Option) { - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::WindowIcon(window_icon))) - { - log::warn!("Fail to send window icon request: {}", e); - } - } - - pub fn set_ime_position>(&self, _position: P) { - todo!() - } - - pub fn request_user_attention(&self, request_type: Option) { - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::UserAttention(request_type))) - { - log::warn!("Fail to send user attention request: {}", e); - } - } - - pub fn set_cursor_icon(&self, cursor: CursorIcon) { - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::CursorIcon(Some(cursor)))) - { - log::warn!("Fail to send cursor icon request: {}", e); - } - } - - pub fn set_cursor_position>(&self, _position: P) -> Result<(), ExternalError> { - todo!() - } - - pub fn set_cursor_visible(&self, visible: bool) { - let cursor = if visible { - Some(CursorIcon::Default) - } else { - None - }; - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::CursorIcon(cursor))) - { - log::warn!("Fail to send cursor visibility request: {}", e); - } - } - - pub fn current_monitor(&self) -> Option { - todo!() - } - - // pub fn available_monitors(&self) -> impl Iterator { - // todo!() - // } - - pub fn primary_monitor(&self) -> Option { - todo!() - } -} - -// We need GtkWindow to initialize WebView, so we have to keep it in the field. -// It is called on any method. -unsafe impl Send for Window {} -unsafe impl Sync for Window {} - -/// Fullscreen modes. -#[derive(Clone, Debug, PartialEq)] -pub enum Fullscreen { - Exclusive(VideoMode), - - /// Providing `None` to `Borderless` will fullscreen on the current monitor. - Borderless(Option), -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum Theme { - Light, - Dark, -} - -pub(crate) enum WindowRequest { - Title(String), - Position((i32, i32)), - Size((i32, i32)), - MinSize((i32, i32)), - MaxSize((i32, i32)), - Visible(bool), - Resizable(bool), - Minimized(bool), - Maximized(bool), - DragWindow, - Fullscreen(Option), - Decorations(bool), - AlwaysOnTop(bool), - WindowIcon(Option), - UserAttention(Option), - SkipTaskbar, - CursorIcon(Option), - WireUpEvents, - Redraw, -} - -pub(crate) fn hit_test(window: &gdk::Window, cx: f64, cy: f64) -> WindowEdge { - let (left, top) = window.get_position(); - let (w, h) = (window.get_width(), window.get_height()); - let (right, bottom) = (left + w, top + h); - let (cx, cy) = (cx as i32, cy as i32); - - let fake_border = 5; // change this to manipulate how far inside the window, the resize can happen - - let display = window.get_display(); - - const LEFT: i32 = 0b00001; - const RIGHT: i32 = 0b0010; - const TOP: i32 = 0b0100; - const BOTTOM: i32 = 0b1000; - const TOPLEFT: i32 = TOP | LEFT; - const TOPRIGHT: i32 = TOP | RIGHT; - const BOTTOMLEFT: i32 = BOTTOM | LEFT; - const BOTTOMRIGHT: i32 = BOTTOM | RIGHT; - - let result = (LEFT * (if cx < (left + fake_border) { 1 } else { 0 })) - | (RIGHT * (if cx >= (right - fake_border) { 1 } else { 0 })) - | (TOP * (if cy < (top + fake_border) { 1 } else { 0 })) - | (BOTTOM * (if cy >= (bottom - fake_border) { 1 } else { 0 })); - - let edge = match result { - LEFT => WindowEdge::West, - TOP => WindowEdge::North, - RIGHT => WindowEdge::East, - BOTTOM => WindowEdge::South, - TOPLEFT => WindowEdge::NorthWest, - TOPRIGHT => WindowEdge::NorthEast, - BOTTOMLEFT => WindowEdge::SouthWest, - BOTTOMRIGHT => WindowEdge::SouthEast, - // has to be bigger than 7. otherwise it will match the number with a variant of WindowEdge enum and we don't want to do that - // also if the number ever change, makke sure to change it in the connect_button_press_event for window and webview - _ => WindowEdge::__Unknown(8), - }; - - // FIXME: calling `window.begin_resize_drag` seems to revert the cursor back to normal style - window.set_cursor( - Cursor::from_name( - &display, - match edge { - WindowEdge::North => "n-resize", - WindowEdge::South => "s-resize", - WindowEdge::East => "e-resize", - WindowEdge::West => "w-resize", - WindowEdge::NorthWest => "nw-resize", - WindowEdge::NorthEast => "ne-resize", - WindowEdge::SouthEast => "se-resize", - WindowEdge::SouthWest => "sw-resize", - _ => "default", - }, - ) - .as_ref(), - ); - - edge -} - -/// Describes the appearance of the mouse cursor. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum CursorIcon { - /// The platform-dependent default cursor. - Default, - /// A simple crosshair. - Crosshair, - /// A hand (often used to indicate links in web browsers). - Hand, - /// Self explanatory. - Arrow, - /// Indicates something is to be moved. - Move, - /// Indicates text that may be selected or edited. - Text, - /// Program busy indicator. - Wait, - /// Help indicator (often rendered as a "?") - Help, - /// Progress indicator. Shows that processing is being done. But in contrast - /// with "Wait" the user may still interact with the program. Often rendered - /// as a spinning beach ball, or an arrow with a watch or hourglass. - Progress, - - /// Cursor showing that something cannot be done. - NotAllowed, - ContextMenu, - Cell, - VerticalText, - Alias, - Copy, - NoDrop, - /// Indicates something can be grabbed. - Grab, - /// Indicates something is grabbed. - Grabbing, - AllScroll, - ZoomIn, - ZoomOut, - - /// Indicate that some edge is to be moved. For example, the 'SeResize' cursor - /// is used when the movement starts from the south-east corner of the box. - EResize, - NResize, - NeResize, - NwResize, - SResize, - SeResize, - SwResize, - WResize, - EwResize, - NsResize, - NeswResize, - NwseResize, - ColResize, - RowResize, -} - -impl Default for CursorIcon { - fn default() -> Self { - CursorIcon::Default - } -} - -/// ## Platform-specific -/// -/// - **X11:** Sets the WM's `XUrgencyHint`. No distinction between `Critical` and `Informational`. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum UserAttentionType { - /// ## Platform-specific - /// - **macOS:** Bounces the dock icon until the application is in focus. - /// - **Windows:** Flashes both the window and the taskbar button until the application is in focus. - Critical, - /// ## Platform-specific - /// - **macOS:** Bounces the dock icon once. - /// - **Windows:** Flashes the taskbar button until the application is in focus. - Informational, -} - -impl Default for UserAttentionType { - fn default() -> Self { - UserAttentionType::Informational - } -} diff --git a/src/lib.rs b/src/lib.rs index 8ae1f68d4..fc988e87d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,9 +71,6 @@ #![allow(clippy::unit_cmp)] #![allow(clippy::upper_case_acronyms)] -#[cfg(target_os = "linux")] -#[macro_use] -extern crate bitflags; #[macro_use] extern crate serde; #[macro_use] @@ -84,10 +81,10 @@ extern crate objc; use std::sync::mpsc::{RecvError, SendError}; +#[cfg(not(target_os = "linux"))] +use crate::application::window::BadIcon; pub use serde_json::Value; use url::ParseError; -#[cfg(not(target_os = "linux"))] -use winit::window::BadIcon; pub mod application; pub mod webview; diff --git a/src/webview/linux/mod.rs b/src/webview/linux/mod.rs index 5cc2ec4cf..87f7b728f 100644 --- a/src/webview/linux/mod.rs +++ b/src/webview/linux/mod.rs @@ -17,7 +17,7 @@ use webkit2gtk::{ }; use crate::{ - application::window::Window, + application::{platform::unix::*, window::Window}, webview::{mimetype::MimeType, FileDropEvent, RpcRequest, RpcResponse}, Error, Result, }; @@ -43,7 +43,7 @@ impl InnerWebView { data_directory: Option, ) -> Result { let window_rc = Rc::clone(&window); - let window = &window.window; + let window = &window.gtk_window(); // Webview widget let manager = UserContentManager::new(); let mut context_builder = WebContextBuilder::new(); @@ -99,7 +99,7 @@ impl InnerWebView { if event.get_button() == 1 { let (cx, cy) = event.get_root(); if let Some(window) = webview.get_parent_window() { - let result = crate::application::window::hit_test(&window, cx, cy); + let result = crate::application::platform::unix::hit_test(&window, cx, cy); // this check is necessary, otherwise the webview won't recieve the click properly when resize isn't needed if result != WindowEdge::__Unknown(8) { diff --git a/src/webview/mod.rs b/src/webview/mod.rs index 62df997c0..9088a34fd 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -38,12 +38,12 @@ use std::{ use serde_json::Value; use url::Url; +#[cfg(target_os = "windows")] +use crate::application::platform::windows::WindowExtWindows; use crate::application::window::Window; #[cfg(target_os = "windows")] #[cfg(feature = "winrt")] use windows_webview2::Windows::Win32::WindowsAndMessaging::HWND; -#[cfg(target_os = "windows")] -use winit::platform::windows::WindowExtWindows; // Helper so all platforms handle RPC messages consistently. fn rpc_proxy( @@ -291,7 +291,11 @@ pub struct WebView { impl Drop for WebView { fn drop(&mut self) { #[cfg(target_os = "linux")] - self.window.close(); + { + use crate::application::platform::unix::WindowExtUnix; + use gtk::GtkWindowExt; + self.window.gtk_window().close(); + } #[cfg(target_os = "windows")] unsafe { use winapi::{shared::windef::HWND, um::winuser::DestroyWindow}; diff --git a/src/webview/win32/file_drop.rs b/src/webview/win32/file_drop.rs index e0417f13b..f2f5e9fbd 100644 --- a/src/webview/win32/file_drop.rs +++ b/src/webview/win32/file_drop.rs @@ -172,7 +172,7 @@ impl IDropTarget { pub unsafe extern "system" fn QueryInterface( _this: *mut unknwnbase::IUnknown, _riid: REFIID, - _ppvObject: *mut *mut c_void, + _ppvObject: *mut *mut winapi::ctypes::c_void, ) -> HRESULT { // This function doesn't appear to be required for an `IDropTarget`. // An implementation would be nice however.