diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40728a1410..f6d7a9f543 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,29 @@ jobs: Check_Formatting: runs-on: ubuntu-latest steps: + + # FIXME + - uses: actions/checkout@v1 + with: + repository: rust-windowing/glutin_interface + path: glutin_interface + ref: master + - uses: actions/checkout@v1 + with: + repository: rust-windowing/glutin_x11_sym + path: glutin_x11_sym + ref: master + - uses: actions/checkout@v1 + with: + repository: rust-windowing/winit_types + path: winit_types + ref: master + - uses: actions/checkout@v1 + with: + repository: rust-windowing/glutin_sys + path: glutin_sys + ref: master + - uses: actions/checkout@v1 - uses: hecrj/setup-rust-action@v1 with: @@ -54,6 +77,29 @@ jobs: runs-on: ${{ matrix.platform.os }} steps: + + # FIXME + - uses: actions/checkout@v1 + with: + repository: rust-windowing/glutin_interface + path: glutin_interface + ref: master + - uses: actions/checkout@v1 + with: + repository: rust-windowing/glutin_x11_sym + path: glutin_x11_sym + ref: master + - uses: actions/checkout@v1 + with: + repository: rust-windowing/winit_types + path: winit_types + ref: master + - uses: actions/checkout@v1 + with: + repository: rust-windowing/glutin_sys + path: glutin_sys + ref: master + - uses: actions/checkout@v1 # Used to cache cargo-web - name: Cache cargo folder @@ -95,12 +141,12 @@ jobs: - name: Build with serde enabled shell: bash - run: cargo $WEB build --verbose --target ${{ matrix.platform.target }} --features serde,$FEATURES + run: cargo $WEB build --verbose --target ${{ matrix.platform.target }} --features serde_feature,$FEATURES - name: Build tests with serde enabled shell: bash - run: cargo $WEB test --no-run --verbose --target ${{ matrix.platform.target }} --features serde,$FEATURES + run: cargo $WEB test --no-run --verbose --target ${{ matrix.platform.target }} --features serde_feature,$FEATURES - name: Run tests with serde enabled shell: bash if: (!contains(matrix.platform.target, 'ios') && !contains(matrix.platform.target, 'wasm32')) - run: cargo $WEB test --verbose --target ${{ matrix.platform.target }} --features serde,$FEATURES + run: cargo $WEB test --verbose --target ${{ matrix.platform.target }} --features serde_feature,$FEATURES diff --git a/CHANGELOG.md b/CHANGELOG.md index f5711e5a94..ff2e749ef6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ - On Wayland, fix coordinates in touch events when scale factor isn't 1. - On Wayland, fix color from `close_button_icon_color` not applying. +- **Breaking:** Renamed `serde` feature to `serde_feature`. +- **Breaking:** On X11, the function `xlib_screen_id` was renamed to `xlib_screen`. Also added `x11_screen` to `MonitorHandle`. `MonitorHandle`'s `native_id` now returns an `Option`. +- **Breaking:** The types in `winit::dpi` and `winit::error` have been moved to the new `winit_types` crate. +- **Breaking:** Unified all functions to return the same error type, `winit_types::error::Error`. +- Winit's panic and log messages now begin with "[winit]" as to help distinguish them from glutin's. +- **Breaking:** `Window::set_fullscreen` now returns an `Result<(), Error>` instead of panicking or doing nothing silently. +- On X11, if the RandR extension is not present, winit will no longer panic. Instead it will return the list of X11 screens, augmenting it with information from Xinerama if present. # 0.21.0 (2020-02-04) diff --git a/Cargo.toml b/Cargo.toml index 47443b6f6f..32eb6a2a30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,37 +12,40 @@ documentation = "https://docs.rs/winit" categories = ["gui"] [package.metadata.docs.rs] -features = ["serde"] +features = ["serde_feature"] [features] web-sys = ["web_sys", "wasm-bindgen", "instant/wasm-bindgen"] stdweb = ["std_web", "instant/stdweb"] +serde_feature = ["winit_types/serde_feature", "serde"] [dependencies] -instant = "0.1" -lazy_static = "1" -libc = "0.2.64" -log = "0.4" -serde = { version = "1", optional = true, features = ["serde_derive"] } -raw-window-handle = "0.3" -bitflags = "1" +instant = "0.1.2" +lazy_static = "1.4.0" +libc = "0.2.66" +log = "0.4.8" +serde = { version = "1.0.104", optional = true, features = ["serde_derive"] } +raw-window-handle = "0.3.3" +winit_types = { version = "0.1.0", path = "../winit_types" } +glutin_interface = { version = "0.1.0", path = "../glutin_interface" } +bitflags = "1.2.1" [dev-dependencies] -image = "0.21" -simple_logger = "1" +image = "0.22.4" +simple_logger = "1.4.0" -[target.'cfg(target_os = "android")'.dependencies.android_glue] -version = "0.2" +[target.'cfg(target_os = "android")'.dependencies] +android_glue = "0.2.3" [target.'cfg(target_os = "ios")'.dependencies] -objc = "0.2.3" +objc = "0.2.7" [target.'cfg(target_os = "macos")'.dependencies] -cocoa = "0.19.1" -core-foundation = "0.6" -core-graphics = "0.17.3" +cocoa = "0.20.0" +core-foundation = "0.7.0" +core-graphics = "0.19.0" dispatch = "0.2.0" -objc = "0.2.6" +objc = "0.2.7" [target.'cfg(target_os = "macos")'.dependencies.core-video-sys] version = "0.1.3" @@ -50,7 +53,7 @@ default_features = false features = ["display_link"] [target.'cfg(target_os = "windows")'.dependencies.winapi] -version = "0.3.6" +version = "0.3.8" features = [ "combaseapi", "commctrl", @@ -75,14 +78,15 @@ features = [ [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies] wayland-client = { version = "0.23.0", features = [ "dlopen", "egl", "cursor", "eventloop"] } -mio = "0.6" -mio-extras = "2.0" -smithay-client-toolkit = "0.6" -x11-dl = "2.18.3" -percent-encoding = "2.0" +mio = "0.6.21" +mio-extras = "2.0.6" +smithay-client-toolkit = "0.6.4" +glutin_x11_sym = { version = "0.1.0", path = "../glutin_x11_sym" } +percent-encoding = "2.1.0" +x11-dl = "2.18.4" -[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "windows"))'.dependencies.parking_lot] -version = "0.10" +[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "windows"))'.dependencies] +parking_lot = "0.10.0" [target.'cfg(target_arch = "wasm32")'.dependencies.web_sys] package = "web-sys" @@ -109,7 +113,7 @@ features = [ ] [target.'cfg(target_arch = "wasm32")'.dependencies.wasm-bindgen] -version = "0.2.45" +version = "0.2.58" optional = true [target.'cfg(target_arch = "wasm32")'.dependencies.std_web] @@ -119,4 +123,4 @@ optional = true features = ["experimental_features_which_may_break_on_minor_version_bumps"] [target.'cfg(target_arch = "wasm32")'.dev-dependencies] -console_log = "0.1" +console_log = "0.1.2" diff --git a/FEATURES.md b/FEATURES.md index 3d43958807..8bf9790bcd 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -150,7 +150,7 @@ If your PR makes notable changes to Winit's features, please update this section * Getting the preferred video mode ## Usability -* `serde`: Enables serialization/deserialization of certain types with Serde. (Maintainer: @Osspial) +* `serde_feature`: Enables serialization/deserialization of certain types with Serde. (Maintainer: @Osspial) ## Compatibility Matrix diff --git a/README.md b/README.md index 1a3bdecfb2..fbbdc69f95 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Winit is only officially supported on the latest stable version of the Rust comp ### Cargo Features Winit provides the following features, which can be enabled in your `Cargo.toml` file: -* `serde`: Enables serialization/deserialization of certain types with [Serde](https://crates.io/crates/serde). +* `serde_feature`: Enables serialization/deserialization of certain types with [Serde](https://crates.io/crates/serde). ### Platform-specific usage diff --git a/examples/custom_events.rs b/examples/custom_events.rs index c59299cab4..b58f31dc74 100644 --- a/examples/custom_events.rs +++ b/examples/custom_events.rs @@ -48,5 +48,5 @@ fn main() { #[cfg(target_arch = "wasm32")] fn main() { - panic!("This example is not supported on web."); + panic!("[winit] This example is not supported on web."); } diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 4cdb7b63ed..bb29f74a35 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -18,7 +18,7 @@ fn main() { let fullscreen = Some(match num { 1 => Fullscreen::Exclusive(prompt_for_video_mode(&prompt_for_monitor(&event_loop))), 2 => Fullscreen::Borderless(prompt_for_monitor(&event_loop)), - _ => panic!("Please enter a valid number"), + _ => panic!("[winit] Please enter a valid number"), }); let mut is_maximized = false; @@ -48,9 +48,9 @@ fn main() { (VirtualKeyCode::Escape, _) => *control_flow = ControlFlow::Exit, (VirtualKeyCode::F, ElementState::Pressed) => { if window.fullscreen().is_some() { - window.set_fullscreen(None); + window.set_fullscreen(None).unwrap(); } else { - window.set_fullscreen(fullscreen.clone()); + window.set_fullscreen(fullscreen.clone()).unwrap(); } } (VirtualKeyCode::S, ElementState::Pressed) => { diff --git a/examples/min_max_size.rs b/examples/min_max_size.rs index f4fd00c1bb..9cdbde0a41 100644 --- a/examples/min_max_size.rs +++ b/examples/min_max_size.rs @@ -1,9 +1,9 @@ use winit::{ - dpi::LogicalSize, event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; +use winit_types::dpi::LogicalSize; fn main() { simple_logger::init().unwrap(); diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index 60f9d802de..2cffc5b032 100644 --- a/examples/multithreaded.rs +++ b/examples/multithreaded.rs @@ -3,11 +3,11 @@ fn main() { use std::{collections::HashMap, sync::mpsc, thread, time::Duration}; use winit::{ - dpi::{PhysicalPosition, PhysicalSize, Position, Size}, event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{CursorIcon, Fullscreen, WindowBuilder}, }; + use winit_types::dpi::{PhysicalPosition, PhysicalSize, Position, Size}; const WINDOW_COUNT: usize = 3; const WINDOW_SIZE: PhysicalSize = PhysicalSize::new(600, 400); @@ -81,15 +81,17 @@ fn main() { video_modes.iter().nth(video_mode_id).unwrap() ); } - F => window.set_fullscreen(match (state, modifiers.alt()) { - (true, false) => { - Some(Fullscreen::Borderless(window.current_monitor())) - } - (true, true) => Some(Fullscreen::Exclusive( - video_modes.iter().nth(video_mode_id).unwrap().clone(), - )), - (false, _) => None, - }), + F => window + .set_fullscreen(match (state, modifiers.alt()) { + (true, false) => { + Some(Fullscreen::Borderless(window.current_monitor())) + } + (true, true) => Some(Fullscreen::Exclusive( + video_modes.iter().nth(video_mode_id).unwrap().clone(), + )), + (false, _) => None, + }) + .unwrap(), G => window.set_cursor_grab(state).unwrap(), H => window.set_cursor_visible(!state), I => { @@ -181,5 +183,5 @@ fn main() { #[cfg(target_arch = "wasm32")] fn main() { - panic!("Example not supported on Wasm"); + panic!("[winit] Example not supported on Wasm"); } diff --git a/examples/resizable.rs b/examples/resizable.rs index 6a90391937..e38844b454 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -1,9 +1,9 @@ use winit::{ - dpi::LogicalSize, event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; +use winit_types::dpi::LogicalSize; fn main() { simple_logger::init().unwrap(); diff --git a/examples/window.rs b/examples/window.rs index c028a50f3f..843ea24cc7 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -3,6 +3,7 @@ use winit::{ event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; +use winit_types::dpi::LogicalSize; fn main() { simple_logger::init().unwrap(); @@ -10,7 +11,7 @@ fn main() { let window = WindowBuilder::new() .with_title("A fantastic window!") - .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0)) + .with_inner_size(LogicalSize::new(128.0, 128.0)) .build(&event_loop) .unwrap(); diff --git a/examples/window_debug.rs b/examples/window_debug.rs index f6e960a794..646f184453 100644 --- a/examples/window_debug.rs +++ b/examples/window_debug.rs @@ -1,11 +1,11 @@ // This example is used by developers to test various window functions. use winit::{ - dpi::{LogicalSize, PhysicalSize}, event::{DeviceEvent, ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{Fullscreen, WindowBuilder}, }; +use winit_types::dpi::{LogicalSize, PhysicalSize}; fn main() { simple_logger::init().unwrap(); @@ -75,17 +75,21 @@ fn main() { .video_modes() .max_by(|a, b| area(a.size()).cmp(&area(b.size()))) { - window.set_fullscreen(Some(Fullscreen::Exclusive(mode))); + window + .set_fullscreen(Some(Fullscreen::Exclusive(mode))) + .unwrap(); } else { eprintln!("no video modes available"); } } VirtualKeyCode::F => { if window.fullscreen().is_some() { - window.set_fullscreen(None); + window.set_fullscreen(None).unwrap(); } else { let monitor = window.current_monitor(); - window.set_fullscreen(Some(Fullscreen::Borderless(monitor))); + window + .set_fullscreen(Some(Fullscreen::Borderless(monitor))) + .unwrap(); } } VirtualKeyCode::M => { diff --git a/examples/window_icon.rs b/examples/window_icon.rs index b7c84679bb..bd90818c02 100644 --- a/examples/window_icon.rs +++ b/examples/window_icon.rs @@ -50,7 +50,7 @@ fn load_icon(path: &Path) -> Icon { let (width, height) = image.dimensions(); let mut rgba = Vec::with_capacity((width * height) as usize * 4); for (_, _, pixel) in image.pixels() { - rgba.extend_from_slice(&pixel.to_rgba().data); + rgba.extend_from_slice(&pixel.to_rgba().0); } (rgba, width, height) }; diff --git a/src/dpi.rs b/src/dpi.rs deleted file mode 100644 index 2057d01283..0000000000 --- a/src/dpi.rs +++ /dev/null @@ -1,498 +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:** "retina displays" have a scale factor of 2.0. Otherwise, the scale factor is 1.0. -//! Intermediate scale factors are never used. It's possible for any display to use that 2.0 scale -//! factor, given the use of the command line. -//! - **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. -//! -//! [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 - -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(dpi_factor: f64) -> bool { - dpi_factor.is_sign_positive() && dpi_factor.is_normal() -} - -/// A position represented in logical pixels. -/// -/// The position is stored as floats, so please be careful. Casting floats to integers truncates the -/// fractional part, which can cause noticable issues. To help with that, an `Into<(i32, i32)>` -/// implementation is provided which does the rounding for you. -#[derive(Debug, Copy, Clone, PartialEq)] -#[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, - dpi_factor: f64, - ) -> Self { - physical.into().to_logical(dpi_factor) - } - - #[inline] - pub fn to_physical(&self, dpi_factor: f64) -> PhysicalPosition { - assert!(validate_scale_factor(dpi_factor)); - let x = self.x.into() * dpi_factor; - let y = self.y.into() * dpi_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, PartialEq)] -#[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, - dpi_factor: f64, - ) -> Self { - logical.into().to_physical(dpi_factor) - } - - #[inline] - pub fn to_logical(&self, dpi_factor: f64) -> LogicalPosition { - assert!(validate_scale_factor(dpi_factor)); - let x = self.x.into() / dpi_factor; - let y = self.y.into() / dpi_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, PartialEq)] -#[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, dpi_factor: f64) -> Self { - physical.into().to_logical(dpi_factor) - } - - #[inline] - pub fn to_physical(&self, dpi_factor: f64) -> PhysicalSize { - assert!(validate_scale_factor(dpi_factor)); - let width = self.width.into() * dpi_factor; - let height = self.height.into() * dpi_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, PartialEq, Eq, 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, dpi_factor: f64) -> Self { - logical.into().to_physical(dpi_factor) - } - - #[inline] - pub fn to_logical(&self, dpi_factor: f64) -> LogicalSize { - assert!(validate_scale_factor(dpi_factor)); - let width = self.width.into() / dpi_factor; - let height = self.height.into() / dpi_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, dpi_factor: f64) -> LogicalSize

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

{ - match *self { - Size::Physical(size) => size.cast(), - Size::Logical(size) => size.to_physical(dpi_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, dpi_factor: f64) -> LogicalPosition

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

{ - match *self { - Position::Physical(position) => position.cast(), - Position::Logical(position) => position.to_physical(dpi_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/error.rs b/src/error.rs deleted file mode 100644 index c039f3b868..0000000000 --- a/src/error.rs +++ /dev/null @@ -1,82 +0,0 @@ -use std::{error, fmt}; - -use crate::platform_impl; - -/// An error whose cause it outside Winit's control. -#[derive(Debug)] -pub enum ExternalError { - /// The operation is not supported by the backend. - NotSupported(NotSupportedError), - /// The OS cannot perform the operation. - Os(OsError), -} - -/// The error type for when the requested operation is not supported by the backend. -#[derive(Clone)] -pub struct NotSupportedError { - _marker: (), -} - -/// The error type for when the OS cannot perform the requested operation. -#[derive(Debug)] -pub struct OsError { - line: u32, - file: &'static str, - error: platform_impl::OsError, -} - -impl NotSupportedError { - #[inline] - #[allow(dead_code)] - pub(crate) fn new() -> NotSupportedError { - NotSupportedError { _marker: () } - } -} - -impl OsError { - #[allow(dead_code)] - pub(crate) fn new(line: u32, file: &'static str, error: platform_impl::OsError) -> OsError { - OsError { line, file, error } - } -} - -#[allow(unused_macros)] -macro_rules! os_error { - ($error:expr) => {{ - crate::error::OsError::new(line!(), file!(), $error) - }}; -} - -impl fmt::Display for OsError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.pad(&format!( - "os error at {}:{}: {}", - self.file, self.line, self.error - )) - } -} - -impl fmt::Display for ExternalError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match self { - ExternalError::NotSupported(e) => e.fmt(f), - ExternalError::Os(e) => e.fmt(f), - } - } -} - -impl fmt::Debug for NotSupportedError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.debug_struct("NotSupportedError").finish() - } -} - -impl fmt::Display for NotSupportedError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.pad("the requested operation is not supported by Winit") - } -} - -impl error::Error for OsError {} -impl error::Error for ExternalError {} -impl error::Error for NotSupportedError {} diff --git a/src/event.rs b/src/event.rs index 10ab621709..5d3bf15dc9 100644 --- a/src/event.rs +++ b/src/event.rs @@ -35,9 +35,9 @@ //! [event_loop_run]: crate::event_loop::EventLoop::run use instant::Instant; use std::path::PathBuf; +use winit_types::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; use crate::{ - dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}, platform_impl, window::{Theme, WindowId}, }; @@ -304,7 +304,7 @@ pub enum WindowEvent<'a> { /// 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. + /// For more information about DPI in general, see the `dpi` module in the crate `winit_types`. ScaleFactorChanged { scale_factor: f64, new_inner_size: &'a mut PhysicalSize, @@ -481,7 +481,7 @@ pub enum DeviceEvent { /// Describes a keyboard input event. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde_feature", derive(Serialize, Deserialize))] pub struct KeyboardInput { /// Identifies the physical key pressed /// @@ -508,7 +508,7 @@ pub struct KeyboardInput { /// Describes touch-screen input state. #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde_feature", derive(Serialize, Deserialize))] pub enum TouchPhase { Started, Moved, @@ -615,7 +615,7 @@ 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))] +#[cfg_attr(feature = "serde_feature", derive(Serialize, Deserialize))] pub enum ElementState { Pressed, Released, @@ -623,7 +623,7 @@ pub enum ElementState { /// Describes a button of a mouse controller. #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde_feature", derive(Serialize, Deserialize))] pub enum MouseButton { Left, Right, @@ -633,7 +633,7 @@ pub enum MouseButton { /// Describes a difference in the mouse scroll wheel state. #[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde_feature", derive(Serialize, Deserialize))] pub enum MouseScrollDelta { /// Amount in lines or rows to scroll in the horizontal /// and vertical directions. @@ -653,7 +653,7 @@ pub enum MouseScrollDelta { /// Symbolic name for a keyboard key. #[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)] #[repr(u32)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde_feature", derive(Serialize, Deserialize))] pub enum VirtualKeyCode { /// The '1' key over the letters. Key1, @@ -895,7 +895,7 @@ bitflags! { } } -#[cfg(feature = "serde")] +#[cfg(feature = "serde_feature")] mod modifiers_serde { use super::ModifiersState; use serde::{Deserialize, Deserializer, Serialize, Serializer}; diff --git a/src/lib.rs b/src/lib.rs index 6011203e0b..f36924ae86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -126,7 +126,7 @@ extern crate lazy_static; #[allow(unused_imports)] #[macro_use] extern crate log; -#[cfg(feature = "serde")] +#[cfg(feature = "serde_feature")] #[macro_use] extern crate serde; #[macro_use] @@ -136,10 +136,18 @@ extern crate bitflags; extern crate objc; #[cfg(all(target_arch = "wasm32", feature = "std_web"))] extern crate std_web as stdweb; - -pub mod dpi; +#[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] +#[macro_use] +extern crate glutin_x11_sym; #[macro_use] -pub mod error; +extern crate winit_types; + pub mod event; pub mod event_loop; mod icon; diff --git a/src/monitor.rs b/src/monitor.rs index 8977c11a4a..9130554a8e 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -9,10 +9,8 @@ //! [monitor_handle]: crate::monitor::MonitorHandle //! [loop_get]: crate::event_loop::EventLoop::available_monitors //! [window_get]: crate::window::Window::available_monitors -use crate::{ - dpi::{PhysicalPosition, PhysicalSize}, - platform_impl, -}; +use crate::platform_impl; +use winit_types::dpi::{PhysicalPosition, PhysicalSize}; /// Describes a fullscreen video mode of a monitor. /// @@ -150,7 +148,7 @@ impl MonitorHandle { /// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa. /// - /// See the [`dpi`](crate::dpi) module for more information. + /// See the `dpi` module in the `winit_types` crate for more information. /// /// ## Platform-specific /// diff --git a/src/platform/macos.rs b/src/platform/macos.rs index b275c7538d..730bf4e3f2 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -2,8 +2,9 @@ use std::os::raw::c_void; +use winit_types::dpi::LogicalSize; + use crate::{ - dpi::LogicalSize, event_loop::EventLoopWindowTarget, monitor::MonitorHandle, window::{Window, WindowBuilder}, diff --git a/src/platform/unix.rs b/src/platform/unix.rs index 6ab2d7dff4..9cc7f0f300 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -1,27 +1,27 @@ #![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] -use std::{os::raw, ptr, sync::Arc}; +use std::{os::raw, ptr}; +use glutin_interface::{ + GbmWindowParts, NativeDisplay, NativeWindow, NativeWindowSource, RawDisplay, RawWindow, Seal, + WaylandWindowParts, X11WindowParts, +}; use smithay_client_toolkit::window::{ButtonState as SCTKButtonState, Theme as SCTKTheme}; +use winit_types::dpi::{PhysicalSize, Size}; +use winit_types::error::{Error, ErrorType}; use crate::{ - dpi::Size, event_loop::{EventLoop, EventLoopWindowTarget}, monitor::MonitorHandle, window::{Window, WindowBuilder}, }; use crate::platform_impl::{ - x11::{ffi::XVisualInfo, XConnection}, - EventLoop as LinuxEventLoop, EventLoopWindowTarget as LinuxEventLoopWindowTarget, - Window as LinuxWindow, + x11::ffi::XVisualInfo, EventLoop as LinuxEventLoop, + EventLoopWindowTarget as LinuxEventLoopWindowTarget, Window as LinuxWindow, }; -// TODO: stupid hack so that glutin can do its work -#[doc(hidden)] -pub use crate::platform_impl::x11; - -pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupported}; +pub use crate::platform_impl::x11::util::WindowType as XWindowType; /// Additional methods on `EventLoopWindowTarget` that are specific to Unix. pub trait EventLoopWindowTargetExtUnix { @@ -31,16 +31,23 @@ pub trait EventLoopWindowTargetExtUnix { /// True if the `EventLoopWindowTarget` uses X11. fn is_x11(&self) -> bool; - #[doc(hidden)] - fn xlib_xconnection(&self) -> Option>; + /// Returns a pointer to the `wl_display` object of Wayland that is used by + /// this `EventLoopWindowTarget`. + /// + /// Returns `None` if the `EventLoop` doesn't use Wayland (if it uses Xlib + /// for example). + /// + /// The pointer will become invalid when the winit `EventLoop` is destroyed. + fn wayland_display(&self) -> Option<*mut raw::c_void>; - /// Returns a pointer to the `wl_display` object of wayland that is used by this + /// Returns a pointer to the `Display` object of Xlib that is used by this /// `EventLoopWindowTarget`. /// - /// Returns `None` if the `EventLoop` doesn't use wayland (if it uses xlib for example). + /// Returns `None` if the `EventLoop` doesn't use Xlib (if it uses Wayland + /// for example). /// /// The pointer will become invalid when the winit `EventLoop` is destroyed. - fn wayland_display(&self) -> Option<*mut raw::c_void>; + fn xlib_display(&self) -> Option<*mut raw::c_void>; } impl EventLoopWindowTargetExtUnix for EventLoopWindowTarget { @@ -55,25 +62,104 @@ impl EventLoopWindowTargetExtUnix for EventLoopWindowTarget { } #[inline] - #[doc(hidden)] - fn xlib_xconnection(&self) -> Option> { + fn wayland_display(&self) -> Option<*mut raw::c_void> { match self.p { - LinuxEventLoopWindowTarget::X(ref e) => Some(e.x_connection().clone()), + LinuxEventLoopWindowTarget::Wayland(ref p) => { + Some(p.display().get_display_ptr() as *mut _) + } _ => None, } } #[inline] - fn wayland_display(&self) -> Option<*mut raw::c_void> { + fn xlib_display(&self) -> Option<*mut raw::c_void> { match self.p { - LinuxEventLoopWindowTarget::Wayland(ref p) => { - Some(p.display().get_display_ptr() as *mut _) - } + LinuxEventLoopWindowTarget::X(ref p) => Some(**p.x_connection().display as *mut _), _ => None, } } } +impl NativeWindow for Window { + fn raw_window(&self) -> RawWindow { + self.wayland_surface() + .map(|wl_surface| RawWindow::Wayland { + wl_surface, + _non_exhaustive_do_not_use: Seal, + }) + .unwrap_or_else(|| RawWindow::Xlib { + window: self.xlib_window().unwrap(), + _non_exhaustive_do_not_use: Seal, + }) + } + + fn size(&self) -> PhysicalSize { + self.inner_size() + } + + fn scale_factor(&self) -> f64 { + self.scale_factor() + } +} + +impl NativeDisplay for EventLoopWindowTarget { + fn raw_display(&self) -> RawDisplay { + self.wayland_display() + .map(|wl_display| RawDisplay::Wayland { + wl_display: Some(wl_display), + _non_exhaustive_do_not_use: Seal, + }) + .unwrap_or_else(|| RawDisplay::Xlib { + display: self.xlib_display().unwrap(), + screen: None, + _non_exhaustive_do_not_use: Seal, + }) + } +} + +impl NativeWindowSource for EventLoopWindowTarget { + type Window = Window; + type WindowBuilder = WindowBuilder; + + fn build_wayland( + &self, + wb: Self::WindowBuilder, + _wwp: WaylandWindowParts, + ) -> Result { + if !self.is_wayland() { + return Err(make_error!(ErrorType::BadApiUsage( + "Trying to make Wayland window with non-Wayland display.".to_string() + ))); + } + + wb.build(self) + } + + fn build_x11( + &self, + wb: Self::WindowBuilder, + xwp: X11WindowParts, + ) -> Result { + if !self.is_x11() { + return Err(make_error!(ErrorType::BadApiUsage( + "Trying to make X11 window with non-X11 display.".to_string() + ))); + } + + wb.with_x11_visual(xwp.x_visual_info) + .with_x11_screen(xwp.screen) + .build(self) + } + + fn build_gbm( + &self, + _wb: Self::WindowBuilder, + _gbmwp: GbmWindowParts, + ) -> Result { + unimplemented!("Winit does not provide GBM support") + } +} + /// Additional methods on `EventLoop` that are specific to Unix. pub trait EventLoopExtUnix { /// Builds a new `EventLoop` that is forced to use X11. @@ -82,7 +168,7 @@ pub trait EventLoopExtUnix { /// /// If called outside the main thread. To initialize an X11 event loop outside /// the main thread, use [`new_x11_any_thread`](#tymethod.new_x11_any_thread). - fn new_x11() -> Result + fn new_x11() -> Result where Self: Sized; @@ -108,7 +194,7 @@ pub trait EventLoopExtUnix { /// /// This method bypasses the cross-platform compatibility requirement /// that `EventLoop` be created on the main thread. - fn new_x11_any_thread() -> Result + fn new_x11_any_thread() -> Result where Self: Sized; @@ -135,7 +221,7 @@ impl EventLoopExtUnix for EventLoop { } #[inline] - fn new_x11_any_thread() -> Result { + fn new_x11_any_thread() -> Result { LinuxEventLoop::new_x11_any_thread().map(wrap_ev) } @@ -144,12 +230,12 @@ impl EventLoopExtUnix for EventLoop { wrap_ev( LinuxEventLoop::new_wayland_any_thread() // TODO: propagate - .expect("failed to open Wayland connection"), + .expect("[winit] failed to open Wayland connection"), ) } #[inline] - fn new_x11() -> Result { + fn new_x11() -> Result { LinuxEventLoop::new_x11().map(wrap_ev) } @@ -158,7 +244,7 @@ impl EventLoopExtUnix for EventLoop { wrap_ev( LinuxEventLoop::new_wayland() // TODO: propagate - .expect("failed to open Wayland connection"), + .expect("[winit] failed to open Wayland connection"), ) } } @@ -174,13 +260,10 @@ pub trait WindowExtUnix { /// /// Returns `None` if the window doesn't use xlib (if it uses wayland for example). /// - /// The pointer will become invalid when the glutin `Window` is destroyed. + /// The pointer will become invalid when the winit `Window` is destroyed. fn xlib_display(&self) -> Option<*mut raw::c_void>; - fn xlib_screen_id(&self) -> Option; - - #[doc(hidden)] - fn xlib_xconnection(&self) -> Option>; + fn xlib_screen(&self) -> Option; /// Set window urgency hint (`XUrgencyHint`). Only relevant on X. fn set_urgent(&self, is_urgent: bool); @@ -189,21 +272,21 @@ pub trait WindowExtUnix { /// /// Returns `None` if the window doesn't use xlib (if it uses wayland for example). /// - /// The pointer will become invalid when the glutin `Window` is destroyed. + /// The pointer will become invalid when the winit `Window` is destroyed. fn xcb_connection(&self) -> Option<*mut raw::c_void>; /// Returns a pointer to the `wl_surface` object of wayland that is used by this window. /// /// Returns `None` if the window doesn't use wayland (if it uses xlib for example). /// - /// The pointer will become invalid when the glutin `Window` is destroyed. + /// The pointer will become invalid when the winit `Window` is destroyed. fn wayland_surface(&self) -> Option<*mut raw::c_void>; /// Returns a pointer to the `wl_display` object of wayland that is used by this window. /// /// Returns `None` if the window doesn't use wayland (if it uses xlib for example). /// - /// The pointer will become invalid when the glutin `Window` is destroyed. + /// The pointer will become invalid when the winit `Window` is destroyed. fn wayland_display(&self) -> Option<*mut raw::c_void>; /// Sets the color theme of the client side window decorations on wayland @@ -237,18 +320,9 @@ impl WindowExtUnix for Window { } #[inline] - fn xlib_screen_id(&self) -> Option { - match self.window { - LinuxWindow::X(ref w) => Some(w.xlib_screen_id()), - _ => None, - } - } - - #[inline] - #[doc(hidden)] - fn xlib_xconnection(&self) -> Option> { + fn xlib_screen(&self) -> Option { match self.window { - LinuxWindow::X(ref w) => Some(w.xlib_xconnection()), + LinuxWindow::X(ref w) => Some(w.xlib_screen()), _ => None, } } @@ -301,7 +375,7 @@ impl WindowExtUnix for Window { /// Additional methods on `WindowBuilder` that are specific to Unix. pub trait WindowBuilderExtUnix { fn with_x11_visual(self, visual_infos: *const T) -> Self; - fn with_x11_screen(self, screen_id: i32) -> Self; + fn with_x11_screen(self, screen: raw::c_int) -> Self; /// Build window with `WM_CLASS` hint; defaults to the name of the binary. Only relevant on X11. fn with_class(self, class: String, instance: String) -> Self; @@ -327,14 +401,14 @@ pub trait WindowBuilderExtUnix { impl WindowBuilderExtUnix for WindowBuilder { #[inline] fn with_x11_visual(mut self, visual_infos: *const T) -> Self { - self.platform_specific.visual_infos = - Some(unsafe { ptr::read(visual_infos as *const XVisualInfo) }); + let visual_infos: XVisualInfo = unsafe { ptr::read(visual_infos as *const XVisualInfo) }; + self.platform_specific.visual_infos = Some(visual_infos); self } #[inline] - fn with_x11_screen(mut self, screen_id: i32) -> Self { - self.platform_specific.screen_id = Some(screen_id); + fn with_x11_screen(mut self, screen: raw::c_int) -> Self { + self.platform_specific.screen = Some(screen); self } @@ -384,13 +458,24 @@ impl WindowBuilderExtUnix for WindowBuilder { /// Additional methods on `MonitorHandle` that are specific to Linux. pub trait MonitorHandleExtUnix { /// Returns the inner identifier of the monitor. - fn native_id(&self) -> u32; + /// + /// On X11, `None` if RandR extension not in use. + fn native_id(&self) -> Option; + + /// Returns the X11 screen of the monitor. None if using Wayland or is a + /// dummy window. + fn x11_screen(&self) -> Option; } impl MonitorHandleExtUnix for MonitorHandle { #[inline] - fn native_id(&self) -> u32 { - self.inner.native_identifier() + fn native_id(&self) -> Option { + self.inner.native_id() + } + + #[inline] + fn x11_screen(&self) -> Option { + self.inner.x11_screen() } } diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index a4cf29faf0..582cbf4190 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -22,8 +22,6 @@ use crate::{ use raw_window_handle::{android::AndroidHandle, RawWindowHandle}; use CreationError::OsError; -pub type OsError = std::io::Error; - pub struct EventLoop { event_rx: Receiver, suspend_callback: RefCell ()>>>, @@ -382,9 +380,10 @@ impl Window { } #[inline] - pub fn set_fullscreen(&self, _monitor: Option) { - // N/A - // Android has single screen maximized apps so nothing to do + pub fn set_fullscreen(&self, _monitor: Option) -> Result<(), Error> { + Err(make_error!(ErrorType::NotSupported( + "Android has single screen maximized apps so nothing to do".to_string() + ))) } #[inline] diff --git a/src/platform_impl/ios/app_state.rs b/src/platform_impl/ios/app_state.rs index 0d388c509a..35f41f9e38 100644 --- a/src/platform_impl/ios/app_state.rs +++ b/src/platform_impl/ios/app_state.rs @@ -10,9 +10,9 @@ use std::{ }; use objc::runtime::{BOOL, YES}; +use winit_types::dpi::LogicalSize; use crate::{ - dpi::LogicalSize, event::{Event, StartCause, WindowEvent}, event_loop::ControlFlow, platform_impl::platform::{ @@ -29,13 +29,13 @@ use crate::{ macro_rules! bug { ($($msg:tt)*) => { - panic!("winit iOS bug, file an issue: {}", format!($($msg)*)) + panic!("[winit] winit iOS bug, file an issue: {}", format!($($msg)*)) }; } macro_rules! bug_assert { ($test:expr, $($msg:tt)*) => { - assert!($test, "winit iOS bug, file an issue: {}", format!($($msg)*)) + assert!($test, "[winit] winit iOS bug, file an issue: {}", format!($($msg)*)) }; } @@ -138,7 +138,7 @@ impl AppState { if cfg!(debug_assertions) { assert_main_thread!( - "bug in winit: `AppState::get_mut()` can only be called on the main thread" + "[winit] bug in winit: `AppState::get_mut()` can only be called on the main thread" ); } let mut guard = APP_STATE.borrow_mut(); @@ -454,7 +454,7 @@ impl AppState { (_, ControlFlow::Exit) => { // https://developer.apple.com/library/archive/qa/qa1561/_index.html // it is not possible to quit an iOS app gracefully and programatically - warn!("`ControlFlow::Exit` ignored on iOS"); + warn!("[winit] `ControlFlow::Exit` ignored on iOS"); self.control_flow = old } } @@ -494,7 +494,7 @@ pub unsafe fn set_key_window(window: id) { | s @ &mut AppStateImpl::Waiting { .. } | s @ &mut AppStateImpl::PollFinished { .. } => bug!("unexpected state {:?}", s), &mut AppStateImpl::Terminated => { - panic!("Attempt to create a `Window` after the app has terminated") + panic!("[winit] Attempt to create a `Window` after the app has terminated") } } drop(this); @@ -533,7 +533,7 @@ pub unsafe fn queue_gl_or_metal_redraw(window: id) { | s @ &mut AppStateImpl::Waiting { .. } | s @ &mut AppStateImpl::PollFinished { .. } => bug!("unexpected state {:?}", s), &mut AppStateImpl::Terminated => { - panic!("Attempt to create a `Window` after the app has terminated") + panic!("[winit] Attempt to create a `Window` after the app has terminated") } } drop(this); @@ -649,10 +649,10 @@ pub unsafe fn handle_nonuser_events>(events match wrapper { EventWrapper::StaticEvent(event) => { if !processing_redraws && event.is_redraw() { - log::info!("processing `RedrawRequested` during the main event loop"); + log::info!("[winit] processing `RedrawRequested` during the main event loop"); } else if processing_redraws && !event.is_redraw() { log::warn!( - "processing non `RedrawRequested` event after the main event loop: {:#?}", + "[winit] processing non `RedrawRequested` event after the main event loop: {:#?}", event ); } @@ -671,7 +671,7 @@ pub unsafe fn handle_nonuser_events>(events ref mut queued_events, queued_gpu_redraws: _, } => mem::replace(queued_events, Vec::new()), - s => bug!("unexpected state {:?}", s), + s => bug!("[winit] unexpected state {:?}", s), }; if queued_events.is_empty() { let queued_gpu_redraws = match this.take_state() { @@ -706,10 +706,12 @@ pub unsafe fn handle_nonuser_events>(events match wrapper { EventWrapper::StaticEvent(event) => { if !processing_redraws && event.is_redraw() { - log::info!("processing `RedrawRequested` during the main event loop"); + log::info!( + "[winit] processing `RedrawRequested` during the main event loop" + ); } else if processing_redraws && !event.is_redraw() { log::warn!( - "processing non-`RedrawRequested` event after the main event loop: {:#?}", + "[winit] processing non-`RedrawRequested` event after the main event loop: {:#?}", event ); } @@ -982,7 +984,7 @@ macro_rules! os_capabilities { $(#[$attr])* pub fn $error_name(&self, extra_msg: &str) { log::warn!( - concat!("`", $objc_call, "` requires iOS {}.{}+. This device is running iOS {}.{}.{}. {}"), + concat!("[winit] `", $objc_call, "` requires iOS {}.{}+. This device is running iOS {}.{}.{}. {}"), $major, $minor, self.os_version.major, self.os_version.minor, self.os_version.patch, extra_msg ) @@ -1035,7 +1037,7 @@ pub fn os_capabilities() -> OSCapabilities { // The minimum required iOS version is likely to grow in the future. assert!( atleast_ios_8 == YES, - "`winit` requires iOS version 8 or greater" + "[winit] `winit` requires iOS version 8 or greater" ); msg_send![process_info, operatingSystemVersion] }; diff --git a/src/platform_impl/ios/event_loop.rs b/src/platform_impl/ios/event_loop.rs index 61b389d43a..b7e24ab5ff 100644 --- a/src/platform_impl/ios/event_loop.rs +++ b/src/platform_impl/ios/event_loop.rs @@ -7,8 +7,9 @@ use std::{ sync::mpsc::{self, Receiver, Sender}, }; +use winit_types::dpi::LogicalSize; + use crate::{ - dpi::LogicalSize, event::Event, event_loop::{ ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootEventLoopWindowTarget, @@ -57,10 +58,12 @@ impl EventLoop { pub fn new() -> EventLoop { static mut SINGLETON_INIT: bool = false; unsafe { - assert_main_thread!("`EventLoop` can only be created on the main thread on iOS"); + assert_main_thread!( + "[winit] `EventLoop` can only be created on the main thread on iOS" + ); assert!( !SINGLETON_INIT, - "Only one `EventLoop` is supported on iOS. \ + "[winit] Only one `EventLoop` is supported on iOS. \ `EventLoopProxy` might be helpful" ); SINGLETON_INIT = true; @@ -92,8 +95,7 @@ impl EventLoop { assert_eq!( application, ptr::null_mut(), - "\ - `EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\ + "[winit] `EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\ Note: `EventLoop::run` calls `UIApplicationMain` on iOS" ); app_state::will_launch(Box::new(EventLoopHandler { diff --git a/src/platform_impl/ios/ffi.rs b/src/platform_impl/ios/ffi.rs index 1c464b066a..dec2569811 100644 --- a/src/platform_impl/ios/ffi.rs +++ b/src/platform_impl/ios/ffi.rs @@ -3,11 +3,9 @@ use std::{convert::TryInto, ffi::CString, ops::BitOr, os::raw::*}; use objc::{runtime::Object, Encode, Encoding}; +use winit_types::dpi::LogicalSize; -use crate::{ - dpi::LogicalSize, - platform::ios::{Idiom, ScreenEdge, ValidOrientations}, -}; +use crate::platform::ios::{Idiom, ScreenEdge, ValidOrientations}; pub type id = *mut Object; pub const nil: id = 0 as id; @@ -224,7 +222,7 @@ impl From for UIRectEdge { assert_eq!( screen_edge.bits() & !ScreenEdge::ALL.bits(), 0, - "invalid `ScreenEdge`" + "[winit] invalid `ScreenEdge`" ); UIRectEdge(screen_edge.bits().into()) } @@ -232,8 +230,8 @@ impl From for UIRectEdge { impl Into for UIRectEdge { fn into(self) -> ScreenEdge { - let bits: u8 = self.0.try_into().expect("invalid `UIRectEdge`"); - ScreenEdge::from_bits(bits).expect("invalid `ScreenEdge`") + let bits: u8 = self.0.try_into().expect("[winit] invalid `UIRectEdge`"); + ScreenEdge::from_bits(bits).expect("[winit] invalid `ScreenEdge`") } } diff --git a/src/platform_impl/ios/mod.rs b/src/platform_impl/ios/mod.rs index 3141dea4cc..42a6dc2fdc 100644 --- a/src/platform_impl/ios/mod.rs +++ b/src/platform_impl/ios/mod.rs @@ -75,8 +75,6 @@ mod monitor; mod view; mod window; -use std::fmt; - pub use self::{ event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}, monitor::{MonitorHandle, VideoMode}, @@ -98,14 +96,3 @@ impl DeviceId { unsafe impl Send for DeviceId {} unsafe impl Sync for DeviceId {} - -#[derive(Debug)] -pub enum OsError {} - -impl fmt::Display for OsError { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - _ => unreachable!(), - } - } -} diff --git a/src/platform_impl/ios/monitor.rs b/src/platform_impl/ios/monitor.rs index 42c71ffc7a..8daf8473e0 100644 --- a/src/platform_impl/ios/monitor.rs +++ b/src/platform_impl/ios/monitor.rs @@ -4,8 +4,9 @@ use std::{ ops::{Deref, DerefMut}, }; +use winit_types::dpi::{PhysicalPosition, PhysicalSize}; + use crate::{ - dpi::{PhysicalPosition, PhysicalSize}, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, platform_impl::platform::{ app_state, @@ -58,7 +59,7 @@ impl Clone for VideoMode { impl VideoMode { unsafe fn retained_new(uiscreen: id, screen_mode: id) -> VideoMode { - assert_main_thread!("`VideoMode` can only be created on the main thread on iOS"); + assert_main_thread!("[winit] `VideoMode` can only be created on the main thread on iOS"); let os_capabilities = app_state::os_capabilities(); let refresh_rate: NSInteger = if os_capabilities.maximum_frames_per_second { msg_send![uiscreen, maximumFramesPerSecond] @@ -131,7 +132,7 @@ impl Deref for MonitorHandle { fn deref(&self) -> &Inner { unsafe { assert_main_thread!( - "`MonitorHandle` methods can only be run on the main thread on iOS" + "[winit] `MonitorHandle` methods can only be run on the main thread on iOS" ); } &self.inner @@ -142,7 +143,7 @@ impl DerefMut for MonitorHandle { fn deref_mut(&mut self) -> &mut Inner { unsafe { assert_main_thread!( - "`MonitorHandle` methods can only be run on the main thread on iOS" + "[winit] `MonitorHandle` methods can only be run on the main thread on iOS" ); } &mut self.inner @@ -161,7 +162,9 @@ impl Clone for MonitorHandle { impl Drop for MonitorHandle { fn drop(&mut self) { unsafe { - assert_main_thread!("`MonitorHandle` can only be dropped on the main thread on iOS"); + assert_main_thread!( + "[winit] `MonitorHandle` can only be dropped on the main thread on iOS" + ); } } } @@ -190,7 +193,9 @@ impl fmt::Debug for MonitorHandle { impl MonitorHandle { pub fn retained_new(uiscreen: id) -> MonitorHandle { unsafe { - assert_main_thread!("`MonitorHandle` can only be cloned on the main thread on iOS"); + assert_main_thread!( + "[winit] `MonitorHandle` can only be cloned on the main thread on iOS" + ); let () = msg_send![uiscreen, retain]; } MonitorHandle { diff --git a/src/platform_impl/ios/view.rs b/src/platform_impl/ios/view.rs index 33ecac37a4..0be1d64791 100644 --- a/src/platform_impl/ios/view.rs +++ b/src/platform_impl/ios/view.rs @@ -95,7 +95,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { let is_uiview: BOOL = msg_send![root_view_class, isSubclassOfClass: uiview_class]; assert_eq!( is_uiview, YES, - "`root_view_class` must inherit from `UIView`" + "[winit] `root_view_class` must inherit from `UIView`" ); extern "C" fn draw_rect(object: &Object, _: Sel, rect: CGRect) { @@ -128,7 +128,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { let screen_frame: CGRect = msg_send![object, convertRect:bounds toCoordinateSpace:screen_space]; let dpi_factor: CGFloat = msg_send![screen, scale]; - let size = crate::dpi::LogicalSize { + let size = winit_types::dpi::LogicalSize { width: screen_frame.size.width as f64, height: screen_frame.size.height as f64, } @@ -168,7 +168,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { && dpi_factor.is_finite() && dpi_factor.is_sign_positive() && dpi_factor > 0.0, - "invalid scale_factor set on UIView", + "[winit] invalid scale_factor set on UIView", ); let scale_factor: f64 = dpi_factor.into(); let bounds: CGRect = msg_send![object, bounds]; @@ -176,7 +176,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { let screen_space: id = msg_send![screen, coordinateSpace]; let screen_frame: CGRect = msg_send![object, convertRect:bounds toCoordinateSpace:screen_space]; - let size = crate::dpi::LogicalSize { + let size = winit_types::dpi::LogicalSize { width: screen_frame.size.width as _, height: screen_frame.size.height as _, }; @@ -245,7 +245,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { // 2 is UITouchPhase::Stationary and is not expected here UITouchPhase::Ended => TouchPhase::Ended, UITouchPhase::Cancelled => TouchPhase::Cancelled, - _ => panic!("unexpected touch phase: {:?}", phase as i32), + _ => panic!("[winit] unexpected touch phase: {:?}", phase as i32), }; touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent { @@ -264,7 +264,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { } let mut decl = ClassDecl::new(&format!("WinitUIView{}", ID), root_view_class) - .expect("Failed to declare class `WinitUIView`"); + .expect("[winit] Failed to declare class `WinitUIView`"); ID += 1; decl.add_method( sel!(drawRect:), @@ -313,7 +313,7 @@ unsafe fn get_view_controller_class() -> &'static Class { } let mut decl = ClassDecl::new("WinitUIViewController", uiviewcontroller_class) - .expect("Failed to declare class `WinitUIViewController`"); + .expect("[winit] Failed to declare class `WinitUIViewController`"); decl.add_method( sel!(shouldAutorotate), should_autorotate as extern "C" fn(&Object, Sel) -> BOOL, @@ -396,7 +396,7 @@ unsafe fn get_window_class() -> &'static Class { } let mut decl = ClassDecl::new("WinitUIWindow", uiwindow_class) - .expect("Failed to declare class `WinitUIWindow`"); + .expect("[winit] Failed to declare class `WinitUIWindow`"); decl.add_method( sel!(becomeKeyWindow), become_key_window as extern "C" fn(&Object, Sel), @@ -420,9 +420,15 @@ pub unsafe fn create_view( let class = get_view_class(platform_attributes.root_view_class); let view: id = msg_send![class, alloc]; - assert!(!view.is_null(), "Failed to create `UIView` instance"); + assert!( + !view.is_null(), + "[winit] Failed to create `UIView` instance" + ); let view: id = msg_send![view, initWithFrame: frame]; - assert!(!view.is_null(), "Failed to initialize `UIView` instance"); + assert!( + !view.is_null(), + "[winit] Failed to initialize `UIView` instance" + ); let () = msg_send![view, setMultipleTouchEnabled: YES]; if let Some(scale_factor) = platform_attributes.scale_factor { let () = msg_send![view, setContentScaleFactor: scale_factor as CGFloat]; @@ -442,12 +448,12 @@ pub unsafe fn create_view_controller( let view_controller: id = msg_send![class, alloc]; assert!( !view_controller.is_null(), - "Failed to create `UIViewController` instance" + "[winit] Failed to create `UIViewController` instance" ); let view_controller: id = msg_send![view_controller, init]; assert!( !view_controller.is_null(), - "Failed to initialize `UIViewController` instance" + "[winit] Failed to initialize `UIViewController` instance" ); let status_bar_hidden = if platform_attributes.prefers_status_bar_hidden { YES @@ -497,11 +503,14 @@ pub unsafe fn create_window( let class = get_window_class(); let window: id = msg_send![class, alloc]; - assert!(!window.is_null(), "Failed to create `UIWindow` instance"); + assert!( + !window.is_null(), + "[winit] Failed to create `UIWindow` instance" + ); let window: id = msg_send![window, initWithFrame: frame]; assert!( !window.is_null(), - "Failed to initialize `UIWindow` instance" + "[winit] Failed to initialize `UIWindow` instance" ); let () = msg_send![window, setRootViewController: view_controller]; match window_attributes.fullscreen { @@ -563,8 +572,8 @@ pub fn create_delegate_class() { } let ui_responder = class!(UIResponder); - let mut decl = - ClassDecl::new("AppDelegate", ui_responder).expect("Failed to declare class `AppDelegate`"); + let mut decl = ClassDecl::new("AppDelegate", ui_responder) + .expect("[winit] Failed to declare class `AppDelegate`"); unsafe { decl.add_method( diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index a5d3e89337..f5ee28cedb 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -7,8 +7,6 @@ use std::{ use objc::runtime::{Class, Object, BOOL, NO, YES}; use crate::{ - dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, - error::{ExternalError, NotSupportedError, OsError as RootOsError}, event::{Event, WindowEvent}, icon::Icon, monitor::MonitorHandle as RootMonitorHandle, @@ -24,6 +22,10 @@ use crate::{ }, window::{CursorIcon, Fullscreen, WindowAttributes, WindowId as RootWindowId}, }; +use winit_types::dpi::{ + self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, +}; +use winit_types::error::{Error, ErrorType}; pub struct Inner { pub window: id, @@ -44,7 +46,7 @@ impl Drop for Inner { impl Inner { pub fn set_title(&self, _title: &str) { - debug!("`Window::set_title` is ignored on iOS") + debug!("[winit] `Window::set_title` is ignored on iOS") } pub fn set_visible(&self, visible: bool) { @@ -76,7 +78,7 @@ impl Inner { } } - pub fn inner_position(&self) -> Result, NotSupportedError> { + pub fn inner_position(&self) -> Result, Error> { unsafe { let safe_area = self.safe_area_screen_space(); let position = LogicalPosition { @@ -88,7 +90,7 @@ impl Inner { } } - pub fn outer_position(&self) -> Result, NotSupportedError> { + pub fn outer_position(&self) -> Result, Error> { unsafe { let screen_frame = self.screen_frame(); let position = LogicalPosition { @@ -146,15 +148,15 @@ impl Inner { } pub fn set_min_inner_size(&self, _dimensions: Option) { - warn!("`Window::set_min_inner_size` is ignored on iOS") + warn!("[winit] `Window::set_min_inner_size` is ignored on iOS") } pub fn set_max_inner_size(&self, _dimensions: Option) { - warn!("`Window::set_max_inner_size` is ignored on iOS") + warn!("[winit] `Window::set_max_inner_size` is ignored on iOS") } pub fn set_resizable(&self, _resizable: bool) { - warn!("`Window::set_resizable` is ignored on iOS") + warn!("[winit] `Window::set_resizable` is ignored on iOS") } pub fn scale_factor(&self) -> f64 { @@ -165,30 +167,34 @@ impl Inner { } pub fn set_cursor_icon(&self, _cursor: CursorIcon) { - debug!("`Window::set_cursor_icon` ignored on iOS") + debug!("[winit] `Window::set_cursor_icon` ignored on iOS") } - pub fn set_cursor_position(&self, _position: Position) -> Result<(), ExternalError> { - Err(ExternalError::NotSupported(NotSupportedError::new())) + pub fn set_cursor_position(&self, _position: Position) -> Result<(), Error> { + Err(make_error!(ErrorType::NotSupported( + "Setting the cursor position is not supported on iOS.".to_string() + ))) } - pub fn set_cursor_grab(&self, _grab: bool) -> Result<(), ExternalError> { - Err(ExternalError::NotSupported(NotSupportedError::new())) + pub fn set_cursor_grab(&self, _grab: bool) -> Result<(), Error> { + Err(make_error!(ErrorType::NotSupported( + "Setting the cursor grab is not supported on iOS.".to_string() + ))) } pub fn set_cursor_visible(&self, _visible: bool) { - debug!("`Window::set_cursor_visible` is ignored on iOS") + debug!("[winit] `Window::set_cursor_visible` is ignored on iOS") } pub fn set_minimized(&self, _minimized: bool) { - warn!("`Window::set_minimized` is ignored on iOS") + warn!("[winit] `Window::set_minimized` is ignored on iOS") } pub fn set_maximized(&self, _maximized: bool) { - warn!("`Window::set_maximized` is ignored on iOS") + warn!("[winit] `Window::set_maximized` is ignored on iOS") } - pub fn set_fullscreen(&self, monitor: Option) { + pub fn set_fullscreen(&self, monitor: Option) -> Result<(), Error> { unsafe { let uiscreen = match monitor { Some(Fullscreen::Exclusive(video_mode)) => { @@ -198,8 +204,9 @@ impl Inner { } Some(Fullscreen::Borderless(monitor)) => monitor.ui_screen() as id, None => { - warn!("`Window::set_fullscreen(None)` ignored on iOS"); - return; + return Err(make_error!(ErrorType::NotSupported( + "`Window::set_fullscreen(None) is not supported on iOS.".to_string() + ))); } }; @@ -219,6 +226,8 @@ impl Inner { uiscreen, setOverscanCompensation: UIScreenOverscanCompensation::None ]; + + Ok(()) } } @@ -243,19 +252,19 @@ impl Inner { } pub fn set_decorations(&self, _decorations: bool) { - warn!("`Window::set_decorations` is ignored on iOS") + warn!("[winit] `Window::set_decorations` is ignored on iOS") } pub fn set_always_on_top(&self, _always_on_top: bool) { - warn!("`Window::set_always_on_top` is ignored on iOS") + warn!("[winit] `Window::set_always_on_top` is ignored on iOS") } pub fn set_window_icon(&self, _icon: Option) { - warn!("`Window::set_window_icon` is ignored on iOS") + warn!("[winit] `Window::set_window_icon` is ignored on iOS") } pub fn set_ime_position(&self, _position: Position) { - warn!("`Window::set_ime_position` is ignored on iOS") + warn!("[winit] `Window::set_ime_position` is ignored on iOS") } pub fn current_monitor(&self) -> RootMonitorHandle { @@ -297,7 +306,7 @@ pub struct Window { impl Drop for Window { fn drop(&mut self) { unsafe { - assert_main_thread!("`Window::drop` can only be run on the main thread on iOS"); + assert_main_thread!("[winit] `Window::drop` can only be run on the main thread on iOS"); } } } @@ -310,7 +319,9 @@ impl Deref for Window { fn deref(&self) -> &Inner { unsafe { - assert_main_thread!("`Window` methods can only be run on the main thread on iOS"); + assert_main_thread!( + "[winit] `Window` methods can only be run on the main thread on iOS" + ); } &self.inner } @@ -319,7 +330,9 @@ impl Deref for Window { impl DerefMut for Window { fn deref_mut(&mut self) -> &mut Inner { unsafe { - assert_main_thread!("`Window` methods can only be run on the main thread on iOS"); + assert_main_thread!( + "[winit] `Window` methods can only be run on the main thread on iOS" + ); } &mut self.inner } @@ -330,15 +343,15 @@ impl Window { _event_loop: &EventLoopWindowTarget, window_attributes: WindowAttributes, platform_attributes: PlatformSpecificWindowBuilderAttributes, - ) -> Result { + ) -> Result { if let Some(_) = window_attributes.min_inner_size { - warn!("`WindowAttributes::min_inner_size` is ignored on iOS"); + warn!("[winit] `WindowAttributes::min_inner_size` is ignored on iOS"); } if let Some(_) = window_attributes.max_inner_size { - warn!("`WindowAttributes::max_inner_size` is ignored on iOS"); + warn!("[winit] `WindowAttributes::max_inner_size` is ignored on iOS"); } if window_attributes.always_on_top { - warn!("`WindowAttributes::always_on_top` is unsupported on iOS"); + warn!("[winit] `WindowAttributes::always_on_top` is unsupported on iOS"); } // TODO: transparency, visible @@ -408,7 +421,7 @@ impl Window { let screen_space: id = msg_send![screen, coordinateSpace]; let screen_frame: CGRect = msg_send![view, convertRect:bounds toCoordinateSpace:screen_space]; - let size = crate::dpi::LogicalSize { + let size = winit_types::dpi::LogicalSize { width: screen_frame.size.width as _, height: screen_frame.size.height as _, }; @@ -448,7 +461,7 @@ impl Inner { unsafe { assert!( dpi::validate_scale_factor(scale_factor), - "`WindowExtIOS::set_scale_factor` received an invalid hidpi factor" + "[winit] `WindowExtIOS::set_scale_factor` received an invalid hidpi factor" ); let scale_factor = scale_factor as CGFloat; let () = msg_send![self.view, setContentScaleFactor: scale_factor]; @@ -550,7 +563,7 @@ impl Inner { let app: id = msg_send![class!(UIApplication), sharedApplication]; assert!( !app.is_null(), - "`Window::get_inner_position` cannot be called before `EventLoop::run` on iOS" + "[winit] `Window::get_inner_position` cannot be called before `EventLoop::run` on iOS" ); msg_send![app, statusBarFrame] }; diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index dfb18341fd..da4442f2e8 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -1,16 +1,14 @@ #![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] -use std::{collections::VecDeque, env, ffi::CStr, fmt, mem::MaybeUninit, os::raw::*, sync::Arc}; +use std::{collections::VecDeque, env, os::raw, sync::Arc}; use parking_lot::Mutex; use raw_window_handle::RawWindowHandle; -use smithay_client_toolkit::reexports::client::ConnectError; +use winit_types::dpi::{PhysicalPosition, PhysicalSize, Position, Size}; +use winit_types::error::Error; -pub use self::x11::XNotSupported; -use self::x11::{ffi::XVisualInfo, util::WindowType as XWindowType, XConnection, XError}; +use self::x11::{ffi::XVisualInfo, util::WindowType as XWindowType, XConnection}; use crate::{ - dpi::{PhysicalPosition, PhysicalSize, Position, Size}, - error::{ExternalError, NotSupportedError, OsError as RootOsError}, event::Event, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, icon::Icon, @@ -33,7 +31,7 @@ const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND"; #[derive(Clone)] pub struct PlatformSpecificWindowBuilderAttributes { pub visual_infos: Option, - pub screen_id: Option, + pub screen: Option, pub resize_increments: Option, pub base_size: Option, pub class: Option<(String, String)>, @@ -47,7 +45,7 @@ impl Default for PlatformSpecificWindowBuilderAttributes { fn default() -> Self { Self { visual_infos: None, - screen_id: None, + screen: None, resize_increments: None, base_size: None, class: None, @@ -60,23 +58,8 @@ impl Default for PlatformSpecificWindowBuilderAttributes { } lazy_static! { - pub static ref X11_BACKEND: Mutex, XNotSupported>> = - { Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)) }; -} - -#[derive(Debug, Clone)] -pub enum OsError { - XError(XError), - XMisc(&'static str), -} - -impl fmt::Display for OsError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match self { - OsError::XError(e) => f.pad(&e.description), - OsError::XMisc(e) => f.pad(e), - } - } + pub static ref X11_BACKEND: Mutex, Error>> = + { Mutex::new(XConnection::new().map(Arc::new)) }; } pub enum Window { @@ -124,10 +107,18 @@ impl MonitorHandle { } #[inline] - pub fn native_identifier(&self) -> u32 { + pub fn native_id(&self) -> Option { match self { - &MonitorHandle::X(ref m) => m.native_identifier(), - &MonitorHandle::Wayland(ref m) => m.native_identifier(), + &MonitorHandle::X(ref m) => m.native_id(), + &MonitorHandle::Wayland(ref m) => Some(m.native_id()), + } + } + + #[inline] + pub fn x11_screen(&self) -> Option { + match self { + &MonitorHandle::X(ref m) => m.x11_screen(), + &MonitorHandle::Wayland(_) => None, } } @@ -210,7 +201,7 @@ impl Window { window_target: &EventLoopWindowTarget, attribs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, - ) -> Result { + ) -> Result { match *window_target { EventLoopWindowTarget::Wayland(ref window_target) => { wayland::Window::new(window_target, attribs, pl_attribs).map(Window::Wayland) @@ -246,7 +237,7 @@ impl Window { } #[inline] - pub fn outer_position(&self) -> Result, NotSupportedError> { + pub fn outer_position(&self) -> Result, Error> { match self { &Window::X(ref w) => w.outer_position(), &Window::Wayland(ref w) => w.outer_position(), @@ -254,7 +245,7 @@ impl Window { } #[inline] - pub fn inner_position(&self) -> Result, NotSupportedError> { + pub fn inner_position(&self) -> Result, Error> { match self { &Window::X(ref m) => m.inner_position(), &Window::Wayland(ref m) => m.inner_position(), @@ -326,7 +317,7 @@ impl Window { } #[inline] - pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { + pub fn set_cursor_grab(&self, grab: bool) -> Result<(), Error> { match self { &Window::X(ref window) => window.set_cursor_grab(grab), &Window::Wayland(ref window) => window.set_cursor_grab(grab), @@ -350,7 +341,7 @@ impl Window { } #[inline] - pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> { + pub fn set_cursor_position(&self, position: Position) -> Result<(), Error> { match self { &Window::X(ref w) => w.set_cursor_position(position), &Window::Wayland(ref w) => w.set_cursor_position(position), @@ -382,7 +373,7 @@ impl Window { } #[inline] - pub fn set_fullscreen(&self, monitor: Option) { + pub fn set_fullscreen(&self, monitor: Option) -> Result<(), Error> { match self { &Window::X(ref w) => w.set_fullscreen(monitor), &Window::Wayland(ref w) => w.set_fullscreen(monitor), @@ -473,38 +464,6 @@ impl Window { } } -unsafe extern "C" fn x_error_callback( - display: *mut x11::ffi::Display, - event: *mut x11::ffi::XErrorEvent, -) -> c_int { - let xconn_lock = X11_BACKEND.lock(); - if let Ok(ref xconn) = *xconn_lock { - // `assume_init` is safe here because the array consists of `MaybeUninit` values, - // which do not require initialization. - let mut buf: [MaybeUninit; 1024] = MaybeUninit::uninit().assume_init(); - (xconn.xlib.XGetErrorText)( - display, - (*event).error_code as c_int, - buf.as_mut_ptr() as *mut c_char, - buf.len() as c_int, - ); - let description = CStr::from_ptr(buf.as_ptr() as *const c_char).to_string_lossy(); - - let error = XError { - description: description.into_owned(), - error_code: (*event).error_code, - request_code: (*event).request_code, - minor_code: (*event).minor_code, - }; - - error!("X11 error: {:#?}", error); - - *xconn.latest_error.lock() = Some(error); - } - // Fun fact: this return value is completely ignored. - 0 -} - pub enum EventLoop { Wayland(wayland::EventLoop), X(x11::EventLoop), @@ -537,14 +496,14 @@ impl EventLoop { "x11" => { // TODO: propagate return EventLoop::new_x11_any_thread() - .expect("Failed to initialize X11 backend"); + .expect("[winit] Failed to initialize X11 backend"); } "wayland" => { return EventLoop::new_wayland_any_thread() - .expect("Failed to initialize Wayland backend"); + .expect("[winit] Failed to initialize Wayland backend"); } _ => panic!( - "Unknown environment variable value for {}, try one of `x11`,`wayland`", + "[winit] Unknown environment variable value for {}, try one of `x11`,`wayland`", BACKEND_PREFERENCE_ENV_VAR, ), } @@ -560,30 +519,29 @@ impl EventLoop { Err(err) => err, }; - let err_string = format!( - "Failed to initialize any backend! Wayland status: {:?} X11 status: {:?}", + panic!( + "[winit] Failed to initialize any backend! Wayland status: {:?} X11 status: {:?}", wayland_err, x11_err, ); - panic!(err_string); } - pub fn new_wayland() -> Result, ConnectError> { + pub fn new_wayland() -> Result, Error> { assert_is_main_thread("new_wayland_any_thread"); EventLoop::new_wayland_any_thread() } - pub fn new_wayland_any_thread() -> Result, ConnectError> { + pub fn new_wayland_any_thread() -> Result, Error> { wayland::EventLoop::new().map(EventLoop::Wayland) } - pub fn new_x11() -> Result, XNotSupported> { + pub fn new_x11() -> Result, Error> { assert_is_main_thread("new_x11_any_thread"); EventLoop::new_x11_any_thread() } - pub fn new_x11_any_thread() -> Result, XNotSupported> { + pub fn new_x11_any_thread() -> Result, Error> { X11_BACKEND .lock() .as_ref() @@ -700,7 +658,7 @@ fn sticky_exit_callback( 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 \ + "[winit] 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 diff --git a/src/platform_impl/linux/wayland/event_loop.rs b/src/platform_impl/linux/wayland/event_loop.rs index 1ceedf19af..2652115779 100644 --- a/src/platform_impl/linux/wayland/event_loop.rs +++ b/src/platform_impl/linux/wayland/event_loop.rs @@ -24,9 +24,9 @@ use smithay_client_toolkit::pointer::{AutoPointer, AutoThemer}; use smithay_client_toolkit::reexports::client::protocol::{ wl_compositor::WlCompositor, wl_shm::WlShm, wl_surface::WlSurface, }; +use winit_types::dpi::{LogicalSize, PhysicalPosition, PhysicalSize}; use crate::{ - dpi::{LogicalSize, PhysicalPosition, PhysicalSize}, event::{ DeviceEvent, DeviceId as RootDeviceId, Event, ModifiersState, StartCause, WindowEvent, }, @@ -45,11 +45,14 @@ use smithay_client_toolkit::{ output::OutputMgr, reexports::client::{ protocol::{wl_keyboard, wl_output, wl_pointer, wl_registry, wl_seat, wl_touch}, - ConnectError, Display, EventQueue, GlobalEvent, + Display, EventQueue, GlobalEvent, }, Environment, }; +use winit_types::error::Error; +use winit_types::platform::OsError; + const KBD_TOKEN: Token = Token(0); const USER_TOKEN: Token = Token(1); const EVQ_TOKEN: Token = Token(2); @@ -105,10 +108,9 @@ impl CursorManager { } fn register_pointer(&mut self, pointer: wl_pointer::WlPointer) { - let auto_themer = self - .auto_themer - .as_ref() - .expect("AutoThemer not initialized. Server did not advertise shm or compositor?"); + let auto_themer = self.auto_themer.as_ref().expect( + "[winit] AutoThemer not initialized. Server did not advertise shm or compositor?", + ); self.pointers.push(auto_themer.theme_pointer(pointer)); } @@ -291,8 +293,9 @@ impl EventLoopProxy { } impl EventLoop { - pub fn new() -> Result, ConnectError> { - let (display, mut event_queue) = Display::connect_to_env()?; + pub fn new() -> Result, Error> { + let (display, mut event_queue) = Display::connect_to_env() + .map_err(|err| make_oserror!(OsError::WaylandConnectError(Arc::new(err))))?; let display = Arc::new(display); let store = Arc::new(Mutex::new(WindowStore::new())); @@ -451,7 +454,9 @@ impl EventLoop { F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow), { // send pending events to the server - self.display.flush().expect("Wayland connection lost."); + self.display + .flush() + .expect("[winit] Wayland connection lost."); let mut control_flow = ControlFlow::default(); let mut events = Events::with_capacity(8); @@ -468,17 +473,17 @@ impl EventLoop { let mut evq = get_target(&self.window_target).evq.borrow_mut(); evq.dispatch_pending() - .expect("failed to dispatch wayland events"); + .expect("[winit] failed to dispatch wayland events"); if let Some(read) = evq.prepare_read() { if let Err(e) = read.read_events() { if e.kind() != ErrorKind::WouldBlock { - panic!("failed to read wayland events: {}", e); + panic!("[winit] failed to read wayland events: {}", e); } } evq.dispatch_pending() - .expect("failed to dispatch wayland events"); + .expect("[winit] failed to dispatch wayland events"); } } @@ -533,7 +538,9 @@ impl EventLoop { } // send pending events to the server - self.display.flush().expect("Wayland connection lost."); + self.display + .flush() + .expect("[winit] Wayland connection lost."); // During the run of the user callback, some other code monitoring and reading the // wayland socket may have been run (mesa for example does this with vsync), if that @@ -550,7 +557,7 @@ impl EventLoop { .evq .borrow_mut() .dispatch_pending() - .expect("Wayland connection lost."); + .expect("[winit] Wayland connection lost."); dispatched > 0 }; @@ -715,7 +722,7 @@ impl EventLoop { // Don't send resize event downstream if the new size is identical to the // current one. if (w, h) != *window.size { - let logical_size = crate::dpi::LogicalSize::new(w as f64, h as f64); + let logical_size = winit_types::dpi::LogicalSize::new(w as f64, h as f64); let physical_size = logical_size .to_physical(window.new_dpi.unwrap_or(window.prev_dpi) as f64); @@ -975,7 +982,7 @@ pub struct MonitorHandle { impl PartialEq for MonitorHandle { fn eq(&self, other: &Self) -> bool { - self.native_identifier() == other.native_identifier() + self.native_id() == other.native_id() } } @@ -989,13 +996,13 @@ impl PartialOrd for MonitorHandle { impl Ord for MonitorHandle { fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.native_identifier().cmp(&other.native_identifier()) + self.native_id().cmp(&other.native_id()) } } impl std::hash::Hash for MonitorHandle { fn hash(&self, state: &mut H) { - self.native_identifier().hash(state); + self.native_id().hash(state); } } @@ -1004,7 +1011,7 @@ impl fmt::Debug for MonitorHandle { #[derive(Debug)] struct MonitorHandle { name: Option, - native_identifier: u32, + native_id: u32, size: PhysicalSize, position: PhysicalPosition, scale_factor: i32, @@ -1012,7 +1019,7 @@ impl fmt::Debug for MonitorHandle { let monitor_id_proxy = MonitorHandle { name: self.name(), - native_identifier: self.native_identifier(), + native_id: self.native_id(), size: self.size(), position: self.position(), scale_factor: self.scale_factor(), @@ -1030,7 +1037,7 @@ impl MonitorHandle { } #[inline] - pub fn native_identifier(&self) -> u32 { + pub fn native_id(&self) -> u32 { self.mgr.with_info(&self.proxy, |id, _| id).unwrap_or(0) } @@ -1088,7 +1095,7 @@ pub fn primary_monitor(outputs: &OutputMgr) -> MonitorHandle { mgr: outputs.clone(), } } else { - panic!("No monitor is available.") + panic!("[winit] No monitor is available.") } }) } diff --git a/src/platform_impl/linux/wayland/pointer.rs b/src/platform_impl/linux/wayland/pointer.rs index 5f93099f4a..f953a4d185 100644 --- a/src/platform_impl/linux/wayland/pointer.rs +++ b/src/platform_impl/linux/wayland/pointer.rs @@ -1,6 +1,5 @@ use std::sync::{Arc, Mutex}; -use crate::dpi::LogicalPosition; use crate::event::{ DeviceEvent, ElementState, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent, @@ -14,6 +13,7 @@ use super::{ }; use smithay_client_toolkit::surface; +use winit_types::dpi::LogicalPosition; use smithay_client_toolkit::reexports::client::protocol::{ wl_pointer::{self, Event as PtrEvent, WlPointer}, diff --git a/src/platform_impl/linux/wayland/touch.rs b/src/platform_impl/linux/wayland/touch.rs index ce4e32c284..b4337b7102 100644 --- a/src/platform_impl/linux/wayland/touch.rs +++ b/src/platform_impl/linux/wayland/touch.rs @@ -1,11 +1,11 @@ use std::sync::{Arc, Mutex}; -use crate::dpi::LogicalPosition; use crate::event::{TouchPhase, WindowEvent}; use super::{event_loop::EventsSink, make_wid, window::WindowStore, DeviceId}; use smithay_client_toolkit::surface; +use winit_types::dpi::LogicalPosition; use smithay_client_toolkit::reexports::client::protocol::{ wl_seat, diff --git a/src/platform_impl/linux/wayland/window.rs b/src/platform_impl/linux/wayland/window.rs index 03b58b3807..6976093d20 100644 --- a/src/platform_impl/linux/wayland/window.rs +++ b/src/platform_impl/linux/wayland/window.rs @@ -6,8 +6,6 @@ use std::{ }; use crate::{ - dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, - error::{ExternalError, NotSupportedError, OsError as RootOsError}, monitor::MonitorHandle as RootMonitorHandle, platform_impl::{ platform::wayland::event_loop::{available_monitors, primary_monitor}, @@ -27,6 +25,9 @@ use smithay_client_toolkit::{ window::{ConceptFrame, Event as WEvent, State as WState, Theme, Window as SWindow}, }; +use winit_types::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}; +use winit_types::error::{Error, ErrorType}; + use super::{event_loop::CursorManager, make_wid, EventLoopWindowTarget, MonitorHandle, WindowId}; pub struct Window { @@ -48,7 +49,7 @@ impl Window { evlp: &EventLoopWindowTarget, attributes: WindowAttributes, pl_attribs: PlAttributes, - ) -> Result { + ) -> Result { // Create the surface first to get initial DPI let window_store = evlp.store.clone(); let cursor_manager = evlp.cursor_manager.clone(); @@ -122,7 +123,7 @@ impl Window { // Check for fullscreen requirements match attributes.fullscreen { Some(Fullscreen::Exclusive(_)) => { - panic!("Wayland doesn't support exclusive fullscreen") + panic!("[winit] Wayland doesn't support exclusive fullscreen") } Some(Fullscreen::Borderless(RootMonitorHandle { inner: PlatformMonitorHandle::Wayland(ref monitor_id), @@ -206,13 +207,17 @@ impl Window { } #[inline] - pub fn outer_position(&self) -> Result, NotSupportedError> { - Err(NotSupportedError::new()) + pub fn outer_position(&self) -> Result, Error> { + Err(make_error!(ErrorType::NotSupported( + "Getting the outer position is not supported on Wayland.".to_string() + ))) } #[inline] - pub fn inner_position(&self) -> Result, NotSupportedError> { - Err(NotSupportedError::new()) + pub fn inner_position(&self) -> Result, Error> { + Err(make_error!(ErrorType::NotSupported( + "Getting the inner position is not supported on Wayland.".to_string() + ))) } #[inline] @@ -306,10 +311,12 @@ impl Window { } } - pub fn set_fullscreen(&self, fullscreen: Option) { + pub fn set_fullscreen(&self, fullscreen: Option) -> Result<(), Error> { match fullscreen { Some(Fullscreen::Exclusive(_)) => { - panic!("Wayland doesn't support exclusive fullscreen") + return Err(make_error!(ErrorType::NotSupported( + "Wayland doesn't support exclusive fullscreen".to_string() + ))); } Some(Fullscreen::Borderless(RootMonitorHandle { inner: PlatformMonitorHandle::Wayland(ref monitor_id), @@ -322,6 +329,8 @@ impl Window { Some(Fullscreen::Borderless(_)) => unreachable!(), None => self.frame.lock().unwrap().unset_fullscreen(), } + + Ok(()) } pub fn set_theme(&self, theme: T) { @@ -341,14 +350,16 @@ impl Window { } #[inline] - pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { + pub fn set_cursor_grab(&self, grab: bool) -> Result<(), Error> { *self.cursor_grab_changed.lock().unwrap() = Some(grab); Ok(()) } #[inline] - pub fn set_cursor_position(&self, _pos: Position) -> Result<(), ExternalError> { - Err(ExternalError::NotSupported(NotSupportedError::new())) + pub fn set_cursor_position(&self, _pos: Position) -> Result<(), Error> { + Err(make_error!(ErrorType::NotSupported( + "Setting the cursor position is not supported on Wayland.".to_string() + ))) } pub fn display(&self) -> &Display { diff --git a/src/platform_impl/linux/x11/dnd.rs b/src/platform_impl/linux/x11/dnd.rs index 997228cd59..586930a85b 100644 --- a/src/platform_impl/linux/x11/dnd.rs +++ b/src/platform_impl/linux/x11/dnd.rs @@ -7,8 +7,9 @@ use std::{ }; use percent_encoding::percent_decode; +use winit_types::error::Error; -use super::{ffi, util, XConnection, XError}; +use super::{ffi, util, XConnection}; #[derive(Debug)] pub struct DndAtoms { @@ -27,7 +28,7 @@ pub struct DndAtoms { } impl DndAtoms { - pub fn new(xconn: &Arc) -> Result { + pub fn new(xconn: &Arc) -> Result { let names = [ b"XdndAware\0".as_ptr() as *mut c_char, b"XdndEnter\0".as_ptr() as *mut c_char, @@ -100,7 +101,7 @@ pub struct Dnd { } impl Dnd { - pub fn new(xconn: Arc) -> Result { + pub fn new(xconn: Arc) -> Result { let atoms = DndAtoms::new(&xconn)?; Ok(Dnd { xconn, @@ -124,7 +125,7 @@ impl Dnd { this_window: c_ulong, target_window: c_ulong, state: DndState, - ) -> Result<(), XError> { + ) -> Result<(), Error> { let (accepted, action) = match state { DndState::Accepted => (1, self.atoms.action_private as c_long), DndState::Rejected => (0, self.atoms.none as c_long), @@ -145,7 +146,7 @@ impl Dnd { this_window: c_ulong, target_window: c_ulong, state: DndState, - ) -> Result<(), XError> { + ) -> Result<(), Error> { let (accepted, action) = match state { DndState::Accepted => (1, self.atoms.action_private as c_long), DndState::Rejected => (0, self.atoms.none as c_long), @@ -170,8 +171,9 @@ impl Dnd { } pub unsafe fn convert_selection(&self, window: c_ulong, time: c_ulong) { - (self.xconn.xlib.XConvertSelection)( - self.xconn.display, + let xlib = syms!(XLIB); + (xlib.XConvertSelection)( + **self.xconn.display, self.atoms.selection, self.atoms.uri_list, self.atoms.selection, diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 424bb89c35..2b4e9c5148 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1,6 +1,7 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc, slice, sync::Arc}; use libc::{c_char, c_int, c_long, c_uint, c_ulong}; +use winit_types::dpi::{PhysicalPosition, PhysicalSize}; use parking_lot::MutexGuard; @@ -13,7 +14,6 @@ use super::{ use util::modifiers::{ModifierKeyState, ModifierKeymap}; use crate::{ - dpi::{PhysicalPosition, PhysicalSize}, event::{ DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, TouchPhase, WindowEvent, }, @@ -23,7 +23,7 @@ use crate::{ pub(super) struct EventProcessor { pub(super) dnd: Dnd, pub(super) ime_receiver: ImeReceiver, - pub(super) randr_event_offset: c_int, + pub(super) randr_event_offset: Option, pub(super) devices: RefCell>, pub(super) xi2ext: XExtension, pub(super) target: Rc>, @@ -45,9 +45,9 @@ impl EventProcessor { } } - fn with_window(&self, window_id: ffi::Window, callback: F) -> Option + fn with_window(&self, window_id: ffi::Window, mut callback: F) -> Option where - F: Fn(&Arc) -> Ret, + F: FnMut(&Arc) -> Ret, { let mut deleted = false; let window_id = WindowId(window_id); @@ -74,13 +74,15 @@ impl EventProcessor { } pub(super) fn poll(&self) -> bool { + let xlib = syms!(XLIB); let wt = get_xtarget(&self.target); - let result = unsafe { (wt.xconn.xlib.XPending)(wt.xconn.display) }; + let result = unsafe { (xlib.XPending)(**wt.xconn.display) }; result != 0 } pub(super) unsafe fn poll_one_event(&mut self, event_ptr: *mut ffi::XEvent) -> bool { + let xlib = syms!(XLIB); let wt = get_xtarget(&self.target); // This function is used to poll and remove a single event // from the Xlib event queue in a non-blocking, atomic way. @@ -97,8 +99,8 @@ impl EventProcessor { 1 } - let result = (wt.xconn.xlib.XCheckIfEvent)( - wt.xconn.display, + let result = (xlib.XCheckIfEvent)( + **wt.xconn.display, event_ptr, Some(predicate), std::ptr::null_mut(), @@ -111,6 +113,7 @@ impl EventProcessor { where F: FnMut(Event<'_, T>), { + let xlib = syms!(XLIB); let wt = get_xtarget(&self.target); // XFilterEvent tells us when an event has been discarded by the input method. // Specifically, this involves all of the KeyPress events in compose/pre-edit sequences, @@ -118,7 +121,7 @@ impl EventProcessor { // arrow keys from being detected twice. if ffi::True == unsafe { - (wt.xconn.xlib.XFilterEvent)(xev, { + (xlib.XFilterEvent)(xev, { let xev: &ffi::XAnyEvent = xev.as_ref(); xev.window }) @@ -156,11 +159,12 @@ impl EventProcessor { || mapping.request == ffi::MappingKeyboard { unsafe { - (wt.xconn.xlib.XRefreshKeyboardMapping)(xev.as_mut()); + (xlib.XRefreshKeyboardMapping)(xev.as_mut()); } wt.xconn + .display .check_errors() - .expect("Failed to call XRefreshKeyboardMapping"); + .expect("[winit] Failed to call XRefreshKeyboardMapping"); self.mod_keymap.reset_from_x_connection(&wt.xconn); self.device_mod_state.update_keymap(&self.mod_keymap); @@ -180,14 +184,20 @@ impl EventProcessor { }); } else if client_msg.data.get_long(0) as ffi::Atom == wt.net_wm_ping { let response_msg: &mut ffi::XClientMessageEvent = xev.as_mut(); - response_msg.window = wt.root; - wt.xconn - .send_event( - wt.root, - Some(ffi::SubstructureNotifyMask | ffi::SubstructureRedirectMask), - *response_msg, - ) - .queue(); + // We should send the message back to the window's root, not + // the default, as that's what makes the most sense, especially + // given that different window managers can be controlling + // different screens. + self.with_window(response_msg.window, |window| { + response_msg.window = window.root; + wt.xconn + .send_event( + window.root, + Some(ffi::SubstructureNotifyMask | ffi::SubstructureRedirectMask), + *response_msg, + ) + .queue(); + }); } else if client_msg.message_type == self.dnd.atoms.enter { let source_window = client_msg.data.get_long(0) as c_ulong; let flags = client_msg.data.get_long(1); @@ -252,13 +262,13 @@ impl EventProcessor { } self.dnd .send_status(window, source_window, DndState::Accepted) - .expect("Failed to send `XdndStatus` message."); + .expect("[winit] Failed to send `XdndStatus` message."); } } else { unsafe { self.dnd .send_status(window, source_window, DndState::Rejected) - .expect("Failed to send `XdndStatus` message."); + .expect("[winit] Failed to send `XdndStatus` message."); } self.dnd.reset(); } @@ -283,7 +293,7 @@ impl EventProcessor { unsafe { self.dnd .send_finished(window, source_window, state) - .expect("Failed to send `XdndFinished` message."); + .expect("[winit] Failed to send `XdndFinished` message."); } self.dnd.reset(); } else if client_msg.message_type == self.dnd.atoms.leave { @@ -377,8 +387,13 @@ impl EventProcessor { .as_ref() .cloned() .unwrap_or_else(|| { + // We must use the window's root, not the default screen's + // root else we will sometimes (but not always) get a bad + // match error. + // + // It appears to be very timing specific. let frame_extents = - wt.xconn.get_frame_extents_heuristic(xwindow, wt.root); + wt.xconn.get_frame_extents_heuristic(xwindow, window.screen); shared_state_lock.frame_extents = Some(frame_extents.clone()); frame_extents }); @@ -403,13 +418,15 @@ impl EventProcessor { // If we don't use the existing adjusted value when available, then the user can screw up the // resizing by dragging across monitors *without* dropping the window. let (width, height) = shared_state_lock - .dpi_adjusted + .scale_factor_adjusted .unwrap_or_else(|| (xev.width as u32, xev.height as u32)); let last_scale_factor = shared_state_lock.last_monitor.scale_factor; let new_scale_factor = { let window_rect = util::AaRect::new(new_outer_position, new_inner_size); - let monitor = wt.xconn.get_monitor_for_window(Some(window_rect)); + let monitor = wt + .xconn + .get_monitor_for_window(Some(window_rect), window.screen); if monitor.is_dummy() { // Avoid updating monitor using a dummy monitor handle @@ -420,7 +437,7 @@ impl EventProcessor { } }; if last_scale_factor != new_scale_factor { - let (new_width, new_height) = window.adjust_for_dpi( + let (new_width, new_height) = window.adjust_for_scale_factor( last_scale_factor, new_scale_factor, width, @@ -447,7 +464,8 @@ impl EventProcessor { new_inner_size.width, new_inner_size.height, ); - shared_state_lock.dpi_adjusted = Some(new_inner_size.into()); + shared_state_lock.scale_factor_adjusted = + Some(new_inner_size.into()); // if the DPI factor changed, force a resize event to ensure the logical // size is computed with the right DPI factor resized = true; @@ -459,10 +477,12 @@ impl EventProcessor { // doesn't need this, but Xfwm does. The hack should not be run on other WMs, since tiling // WMs constrain the window size, making the resize fail. This would cause an endless stream of // XResizeWindow requests, making Xorg, the winit client, and the WM consume 100% of CPU. - if let Some(adjusted_size) = shared_state_lock.dpi_adjusted { - if new_inner_size == adjusted_size || !util::wm_name_is_one_of(&["Xfwm4"]) { + if let Some(adjusted_size) = shared_state_lock.scale_factor_adjusted { + if new_inner_size == adjusted_size + || !util::wm_name_is_one_of(&["Xfwm4"], window.screen) + { // When this finally happens, the event will not be synthetic. - shared_state_lock.dpi_adjusted = None; + shared_state_lock.scale_factor_adjusted = None; } else { window.set_inner_size_physical(adjusted_size.0, adjusted_size.1); } @@ -488,7 +508,7 @@ impl EventProcessor { // (which is almost all of them). Failing to correctly update WM info doesn't // really have much impact, since on the WMs affected (xmonad, dwm, etc.) the only // effect is that we waste some time trying to query unsupported properties. - wt.xconn.update_cached_wm_info(wt.root); + wt.xconn.update_cached_wm_info(); self.with_window(xev.window, |window| { window.invalidate_cached_frame_extents(); @@ -510,7 +530,7 @@ impl EventProcessor { wt.ime .borrow_mut() .remove_context(window) - .expect("Failed to destroy input context"); + .expect("[winit] Failed to destroy input context"); callback(Event::WindowEvent { window_id, @@ -841,7 +861,7 @@ impl EventProcessor { let modifiers = wt .xconn .query_pointer(xev.event, xev.deviceid) - .expect("Failed to query pointer device") + .expect("[winit] Failed to query pointer device") .get_modifier_state(); callback(Event::WindowEvent { @@ -877,7 +897,7 @@ impl EventProcessor { wt.ime .borrow_mut() .focus(xev.event) - .expect("Failed to focus input context"); + .expect("[winit] Failed to focus input context"); callback(Event::WindowEvent { window_id, @@ -919,7 +939,7 @@ impl EventProcessor { wt.ime .borrow_mut() .unfocus(xev.event) - .expect("Failed to unfocus input context"); + .expect("[winit] Failed to unfocus input context"); let window_id = mkwid(xev.event); @@ -1119,7 +1139,9 @@ impl EventProcessor { } } _ => { - if event_type == self.randr_event_offset { + // With Xinerama, hotpluging is _NOT_ supported, as the configuration + // is static. + if Some(event_type) == self.randr_event_offset { // In the future, it would be quite easy to emit monitor hotplug events. let prev_list = monitor::invalidate_cached_monitor_list(); if let Some(prev_list) = prev_list { @@ -1138,7 +1160,7 @@ impl EventProcessor { let (width, height) = window.inner_size_physical(); let (new_width, new_height) = window - .adjust_for_dpi( + .adjust_for_scale_factor( prev_monitor.scale_factor, new_monitor.scale_factor, width, diff --git a/src/platform_impl/linux/x11/ffi.rs b/src/platform_impl/linux/x11/ffi.rs index 6d7c20894b..de2d45a626 100644 --- a/src/platform_impl/linux/x11/ffi.rs +++ b/src/platform_impl/linux/x11/ffi.rs @@ -1,4 +1,5 @@ +pub use glutin_x11_sym::{XCURSOR, XINERAMA, XINPUT2, XLIB, XLIB_XCB, XRANDR_2_2_0}; pub use x11_dl::{ - error::OpenError, keysym::*, xcursor::*, xinput::*, xinput2::*, xlib::*, xlib_xcb::*, - xrandr::*, xrender::*, + error::OpenError, keysym::*, xcursor::*, xinerama::*, xinput::*, xinput2::*, xlib::*, + xlib_xcb::*, xrandr::*, xrender::*, }; diff --git a/src/platform_impl/linux/x11/ime/callbacks.rs b/src/platform_impl/linux/x11/ime/callbacks.rs index f254a04e55..247fba7838 100644 --- a/src/platform_impl/linux/x11/ime/callbacks.rs +++ b/src/platform_impl/linux/x11/ime/callbacks.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, os::raw::c_char, ptr, sync::Arc}; -use super::{ffi, XConnection, XError}; +use super::{ffi, XConnection}; use super::{ context::{ImeContext, ImeContextCreationError}, @@ -8,16 +8,19 @@ use super::{ input_method::PotentialInputMethods, }; +use winit_types::error::Error; + pub unsafe fn xim_set_callback( xconn: &Arc, xim: ffi::XIM, field: *const c_char, callback: *mut ffi::XIMCallback, -) -> Result<(), XError> { +) -> Result<(), Error> { + let xlib = syms!(XLIB); // It's advisable to wrap variadic FFI functions in our own functions, as we want to minimize // access that isn't type-checked. - (xconn.xlib.XSetIMValues)(xim, field, callback, ptr::null_mut::<()>()); - xconn.check_errors() + (xlib.XSetIMValues)(xim, field, callback, ptr::null_mut::<()>()); + xconn.display.check_errors() } // Set a callback for when an input method matching the current locale modifiers becomes @@ -29,38 +32,40 @@ pub unsafe fn xim_set_callback( pub unsafe fn set_instantiate_callback( xconn: &Arc, client_data: ffi::XPointer, -) -> Result<(), XError> { - (xconn.xlib.XRegisterIMInstantiateCallback)( - xconn.display, +) -> Result<(), Error> { + let xlib = syms!(XLIB); + (xlib.XRegisterIMInstantiateCallback)( + **xconn.display, ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), Some(xim_instantiate_callback), client_data, ); - xconn.check_errors() + xconn.display.check_errors() } pub unsafe fn unset_instantiate_callback( xconn: &Arc, client_data: ffi::XPointer, -) -> Result<(), XError> { - (xconn.xlib.XUnregisterIMInstantiateCallback)( - xconn.display, +) -> Result<(), Error> { + let xlib = syms!(XLIB); + (xlib.XUnregisterIMInstantiateCallback)( + **xconn.display, ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), Some(xim_instantiate_callback), client_data, ); - xconn.check_errors() + xconn.display.check_errors() } pub unsafe fn set_destroy_callback( xconn: &Arc, im: ffi::XIM, inner: &ImeInner, -) -> Result<(), XError> { +) -> Result<(), Error> { xim_set_callback( &xconn, im, @@ -73,7 +78,7 @@ pub unsafe fn set_destroy_callback( enum ReplaceImError { MethodOpenFailed(PotentialInputMethods), ContextCreationFailed(ImeContextCreationError), - SetDestroyCallbackFailed(XError), + SetDestroyCallbackFailed(Error), } // Attempt to replace current IM (which may or may not be presently valid) with a new one. This @@ -142,7 +147,7 @@ pub unsafe extern "C" fn xim_instantiate_callback( (*inner).is_fallback = false; } else if result.is_err() && (*inner).is_destroyed { // We have no usable input methods! - result.expect("Failed to reopen input method"); + result.expect("[winit] Failed to reopen input method"); } } } @@ -168,7 +173,7 @@ pub unsafe extern "C" fn xim_destroy_callback( (*inner).is_fallback = true; } else { // We have no usable input methods! - result.expect("Failed to open fallback input method"); + result.expect("[winit] Failed to open fallback input method"); } } } diff --git a/src/platform_impl/linux/x11/ime/context.rs b/src/platform_impl/linux/x11/ime/context.rs index 8c2ff4cf50..328a40d994 100644 --- a/src/platform_impl/linux/x11/ime/context.rs +++ b/src/platform_impl/linux/x11/ime/context.rs @@ -4,28 +4,25 @@ use std::{ sync::Arc, }; -use super::{ffi, util, XConnection, XError}; +use super::{ffi, util, XConnection}; + +use winit_types::error::Error; #[derive(Debug)] pub enum ImeContextCreationError { - XError(XError), + Error(Error), Null, } -unsafe fn create_pre_edit_attr<'a>( - xconn: &'a Arc, - ic_spot: &'a ffi::XPoint, -) -> util::XSmartPointer<'a, c_void> { - util::XSmartPointer::new( - xconn, - (xconn.xlib.XVaCreateNestedList)( - 0, - ffi::XNSpotLocation_0.as_ptr() as *const _, - ic_spot, - ptr::null_mut::<()>(), - ), - ) - .expect("XVaCreateNestedList returned NULL") +unsafe fn create_pre_edit_attr<'a>(ic_spot: &'a ffi::XPoint) -> util::XSmartPointer { + let xlib = syms!(XLIB); + util::XSmartPointer::new((xlib.XVaCreateNestedList)( + 0, + ffi::XNSpotLocation_0.as_ptr() as *const _, + ic_spot, + ptr::null_mut::<()>(), + )) + .expect("[winit] XVaCreateNestedList returned NULL") } // WARNING: this struct doesn't destroy its XIC resource when dropped. @@ -46,15 +43,16 @@ impl ImeContext { ic_spot: Option, ) -> Result { let ic = if let Some(ic_spot) = ic_spot { - ImeContext::create_ic_with_spot(xconn, im, window, ic_spot) + ImeContext::create_ic_with_spot(im, window, ic_spot) } else { - ImeContext::create_ic(xconn, im, window) + ImeContext::create_ic(im, window) }; let ic = ic.ok_or(ImeContextCreationError::Null)?; xconn + .display .check_errors() - .map_err(ImeContextCreationError::XError)?; + .map_err(ImeContextCreationError::Error)?; Ok(ImeContext { ic, @@ -62,12 +60,9 @@ impl ImeContext { }) } - unsafe fn create_ic( - xconn: &Arc, - im: ffi::XIM, - window: ffi::Window, - ) -> Option { - let ic = (xconn.xlib.XCreateIC)( + unsafe fn create_ic(im: ffi::XIM, window: ffi::Window) -> Option { + let xlib = syms!(XLIB); + let ic = (xlib.XCreateIC)( im, ffi::XNInputStyle_0.as_ptr() as *const _, ffi::XIMPreeditNothing | ffi::XIMStatusNothing, @@ -83,13 +78,13 @@ impl ImeContext { } unsafe fn create_ic_with_spot( - xconn: &Arc, im: ffi::XIM, window: ffi::Window, ic_spot: ffi::XPoint, ) -> Option { - let pre_edit_attr = create_pre_edit_attr(xconn, &ic_spot); - let ic = (xconn.xlib.XCreateIC)( + let xlib = syms!(XLIB); + let pre_edit_attr = create_pre_edit_attr(&ic_spot); + let ic = (xlib.XCreateIC)( im, ffi::XNInputStyle_0.as_ptr() as *const _, ffi::XIMPreeditNothing | ffi::XIMStatusNothing, @@ -106,29 +101,32 @@ impl ImeContext { } } - pub fn focus(&self, xconn: &Arc) -> Result<(), XError> { + pub fn focus(&self, xconn: &Arc) -> Result<(), Error> { + let xlib = syms!(XLIB); unsafe { - (xconn.xlib.XSetICFocus)(self.ic); + (xlib.XSetICFocus)(self.ic); } - xconn.check_errors() + xconn.display.check_errors() } - pub fn unfocus(&self, xconn: &Arc) -> Result<(), XError> { + pub fn unfocus(&self, xconn: &Arc) -> Result<(), Error> { + let xlib = syms!(XLIB); unsafe { - (xconn.xlib.XUnsetICFocus)(self.ic); + (xlib.XUnsetICFocus)(self.ic); } - xconn.check_errors() + xconn.display.check_errors() } - pub fn set_spot(&mut self, xconn: &Arc, x: c_short, y: c_short) { + pub fn set_spot(&mut self, x: c_short, y: c_short) { + let xlib = syms!(XLIB); if self.ic_spot.x == x && self.ic_spot.y == y { return; } self.ic_spot = ffi::XPoint { x, y }; unsafe { - let pre_edit_attr = create_pre_edit_attr(xconn, &self.ic_spot); - (xconn.xlib.XSetICValues)( + let pre_edit_attr = create_pre_edit_attr(&self.ic_spot); + (xlib.XSetICValues)( self.ic, ffi::XNPreeditAttributes_0.as_ptr() as *const _, pre_edit_attr.ptr, diff --git a/src/platform_impl/linux/x11/ime/inner.rs b/src/platform_impl/linux/x11/ime/inner.rs index 011e22aa13..db0972b1dc 100644 --- a/src/platform_impl/linux/x11/ime/inner.rs +++ b/src/platform_impl/linux/x11/ime/inner.rs @@ -1,17 +1,21 @@ use std::{collections::HashMap, mem, ptr, sync::Arc}; -use super::{ffi, XConnection, XError}; +use super::{ffi, XConnection}; use super::{context::ImeContext, input_method::PotentialInputMethods}; -pub unsafe fn close_im(xconn: &Arc, im: ffi::XIM) -> Result<(), XError> { - (xconn.xlib.XCloseIM)(im); - xconn.check_errors() +use winit_types::error::Error; + +pub unsafe fn close_im(xconn: &Arc, im: ffi::XIM) -> Result<(), Error> { + let xlib = syms!(XLIB); + (xlib.XCloseIM)(im); + xconn.display.check_errors() } -pub unsafe fn destroy_ic(xconn: &Arc, ic: ffi::XIC) -> Result<(), XError> { - (xconn.xlib.XDestroyIC)(ic); - xconn.check_errors() +pub unsafe fn destroy_ic(xconn: &Arc, ic: ffi::XIC) -> Result<(), Error> { + let xlib = syms!(XLIB); + (xlib.XDestroyIC)(ic); + xconn.display.check_errors() } pub struct ImeInner { @@ -41,7 +45,7 @@ impl ImeInner { } } - pub unsafe fn close_im_if_necessary(&self) -> Result { + pub unsafe fn close_im_if_necessary(&self) -> Result { if !self.is_destroyed { close_im(&self.xconn, self.im).map(|_| true) } else { @@ -49,7 +53,7 @@ impl ImeInner { } } - pub unsafe fn destroy_ic_if_necessary(&self, ic: ffi::XIC) -> Result { + pub unsafe fn destroy_ic_if_necessary(&self, ic: ffi::XIC) -> Result { if !self.is_destroyed { destroy_ic(&self.xconn, ic).map(|_| true) } else { @@ -57,7 +61,7 @@ impl ImeInner { } } - pub unsafe fn destroy_all_contexts_if_necessary(&self) -> Result { + pub unsafe fn destroy_all_contexts_if_necessary(&self) -> Result { for context in self.contexts.values() { if let &Some(ref context) = context { self.destroy_ic_if_necessary(context.ic)?; diff --git a/src/platform_impl/linux/x11/ime/input_method.rs b/src/platform_impl/linux/x11/ime/input_method.rs index 42c4033efb..9335c95f16 100644 --- a/src/platform_impl/linux/x11/ime/input_method.rs +++ b/src/platform_impl/linux/x11/ime/input_method.rs @@ -8,24 +8,26 @@ use std::{ }; use parking_lot::Mutex; +use winit_types::error::Error; -use super::{ffi, util, XConnection, XError}; +use super::{ffi, util, XConnection}; lazy_static! { static ref GLOBAL_LOCK: Mutex<()> = Default::default(); } unsafe fn open_im(xconn: &Arc, locale_modifiers: &CStr) -> Option { + let xlib = syms!(XLIB); let _lock = GLOBAL_LOCK.lock(); // XSetLocaleModifiers returns... // * The current locale modifiers if it's given a NULL pointer. // * The new locale modifiers if we succeeded in setting them. // * NULL if the locale modifiers string is malformed. - (xconn.xlib.XSetLocaleModifiers)(locale_modifiers.as_ptr()); + (xlib.XSetLocaleModifiers)(locale_modifiers.as_ptr()); - let im = (xconn.xlib.XOpenIM)( - xconn.display, + let im = (xlib.XOpenIM)( + **xconn.display, ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), @@ -80,7 +82,7 @@ impl InputMethodResult { #[derive(Debug, Clone)] enum GetXimServersError { - XError(XError), + Error(Error), GetPropertyError(util::GetPropertyError), InvalidUtf8(IntoStringError), } @@ -92,17 +94,19 @@ enum GetXimServersError { // modifiers, since we don't want a user who's looking at logs to ask "am I supposed to set // XMODIFIERS to `@server=ibus`?!?" unsafe fn get_xim_servers(xconn: &Arc) -> Result, GetXimServersError> { + let xlib = syms!(XLIB); let servers_atom = xconn.get_atom_unchecked(b"XIM_SERVERS\0"); - let root = (xconn.xlib.XDefaultRootWindow)(xconn.display); + // TODO: Default root probably works with multihead setups, right? + let root = (xlib.XDefaultRootWindow)(**xconn.display); let mut atoms: Vec = xconn .get_property(root, servers_atom, ffi::XA_ATOM) .map_err(GetXimServersError::GetPropertyError)?; let mut names: Vec<*const c_char> = Vec::with_capacity(atoms.len()); - (xconn.xlib.XGetAtomNames)( - xconn.display, + (xlib.XGetAtomNames)( + **xconn.display, atoms.as_mut_ptr(), atoms.len() as _, names.as_mut_ptr() as _, @@ -115,10 +119,13 @@ unsafe fn get_xim_servers(xconn: &Arc) -> Result, GetXi .to_owned() .into_string() .map_err(GetXimServersError::InvalidUtf8)?; - (xconn.xlib.XFree)(name as _); + (xlib.XFree)(name as _); formatted_names.push(string.replace("@server=", "@im=")); } - xconn.check_errors().map_err(GetXimServersError::XError)?; + xconn + .display + .check_errors() + .map_err(GetXimServersError::Error)?; Ok(formatted_names) } @@ -131,13 +138,13 @@ struct InputMethodName { impl InputMethodName { pub fn from_string(string: String) -> Self { let c_string = CString::new(string.clone()) - .expect("String used to construct CString contained null byte"); + .expect("[winit] String used to construct CString contained null byte"); InputMethodName { c_string, string } } pub fn from_str(string: &str) -> Self { - let c_string = - CString::new(string).expect("String used to construct CString contained null byte"); + let c_string = CString::new(string) + .expect("[winit] String used to construct CString contained null byte"); InputMethodName { c_string, string: string.to_owned(), diff --git a/src/platform_impl/linux/x11/ime/mod.rs b/src/platform_impl/linux/x11/ime/mod.rs index b95da71101..cc3045c1c4 100644 --- a/src/platform_impl/linux/x11/ime/mod.rs +++ b/src/platform_impl/linux/x11/ime/mod.rs @@ -10,7 +10,7 @@ use std::sync::{ Arc, }; -use super::{ffi, util, XConnection, XError}; +use super::{ffi, util, XConnection}; pub use self::context::ImeContextCreationError; use self::{ @@ -20,13 +20,15 @@ use self::{ input_method::PotentialInputMethods, }; +use winit_types::error::Error; + pub type ImeReceiver = Receiver<(ffi::Window, i16, i16)>; pub type ImeSender = Sender<(ffi::Window, i16, i16)>; #[derive(Debug)] pub enum ImeCreationError { OpenFailure(PotentialInputMethods), - SetDestroyCallbackFailed(XError), + SetDestroyCallbackFailed(Error), } pub struct Ime { @@ -110,7 +112,7 @@ impl Ime { } } - pub fn remove_context(&mut self, window: ffi::Window) -> Result { + pub fn remove_context(&mut self, window: ffi::Window) -> Result { if let Some(Some(context)) = self.inner.contexts.remove(&window) { unsafe { self.inner.destroy_ic_if_necessary(context.ic)?; @@ -121,7 +123,7 @@ impl Ime { } } - pub fn focus(&mut self, window: ffi::Window) -> Result { + pub fn focus(&mut self, window: ffi::Window) -> Result { if self.is_destroyed() { return Ok(false); } @@ -132,7 +134,7 @@ impl Ime { } } - pub fn unfocus(&mut self, window: ffi::Window) -> Result { + pub fn unfocus(&mut self, window: ffi::Window) -> Result { if self.is_destroyed() { return Ok(false); } @@ -148,7 +150,7 @@ impl Ime { return; } if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) { - context.set_spot(&self.xconn, x as _, y as _); + context.set_spot(x as _, y as _); } } } diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 35c1987e29..b596c19fd0 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -19,7 +19,7 @@ mod xdisplay; pub use self::{ monitor::{MonitorHandle, VideoMode}, window::UnownedWindow, - xdisplay::{XConnection, XError, XNotSupported}, + xdisplay::XConnection, }; use std::{ @@ -36,6 +36,7 @@ use std::{ }; use libc::{self, setlocale, LC_CTYPE}; +use winit_types::error::Error; use mio::{unix::EventedFd, Events, Poll, PollOpt, Ready, Token}; @@ -45,10 +46,10 @@ use self::{ dnd::{Dnd, DndState}, event_processor::EventProcessor, ime::{Ime, ImeCreationError, ImeReceiver, ImeSender}, + monitor::MonitorInfoSource, util::modifiers::ModifierKeymap, }; use crate::{ - error::OsError as RootOsError, event::{Event, StartCause}, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, platform_impl::{platform::sticky_exit_callback, PlatformSpecificWindowBuilderAttributes}, @@ -63,7 +64,6 @@ pub struct EventLoopWindowTarget { wm_delete_window: ffi::Atom, net_wm_ping: ffi::Atom, ime_sender: ImeSender, - root: ffi::Window, ime: RefCell, windows: RefCell>>, pending_redraws: Arc>>, @@ -92,14 +92,14 @@ impl Clone for EventLoopProxy { impl EventLoop { pub fn new(xconn: Arc) -> EventLoop { - let root = unsafe { (xconn.xlib.XDefaultRootWindow)(xconn.display) }; + let (xlib, xinput2) = syms!(XLIB, XINPUT2); let wm_delete_window = unsafe { xconn.get_atom_unchecked(b"WM_DELETE_WINDOW\0") }; let net_wm_ping = unsafe { xconn.get_atom_unchecked(b"_NET_WM_PING\0") }; let dnd = Dnd::new(Arc::clone(&xconn)) - .expect("Failed to call XInternAtoms when initializing drag and drop"); + .expect("[winit] Failed to call XInternAtoms when initializing drag and drop"); let (ime_sender, ime_receiver) = mpsc::channel(); // Input methods will open successfully without setting the locale, but it won't be @@ -108,22 +108,37 @@ impl EventLoop { setlocale(LC_CTYPE, b"\0".as_ptr() as *const _); } let ime = RefCell::new({ - let result = Ime::new(Arc::clone(&xconn)); - if let Err(ImeCreationError::OpenFailure(ref state)) = result { - panic!(format!("Failed to open input method: {:#?}", state)); + match Ime::new(Arc::clone(&xconn)) { + Err(ImeCreationError::OpenFailure(err)) => { + panic!("[winit] Failed to open input method: {:#?}", err) + } + Err(ImeCreationError::SetDestroyCallbackFailed(err)) => panic!( + "[winit] Failed to set input method destruction callback: {:#?}", + err + ), + Ok(result) => result, } - result.expect("Failed to set input method destruction callback") }); - let randr_event_offset = xconn - .select_xrandr_input(root) - .expect("Failed to query XRandR extension"); + let randr_event_offset = match xconn.monitor_info_source { + MonitorInfoSource::XRandR => { + // With RandR there is only ever one screen, therefore only one + // root, so using the default is okay here. + let root = unsafe { (xlib.XDefaultRootWindow)(**xconn.display) }; + Some( + xconn + .select_xrandr_input(root) + .expect("Failed to query XRandR extension"), + ) + } + _ => None, + }; let xi2ext = unsafe { let mut ext = XExtension::default(); - let res = (xconn.xlib.XQueryExtension)( - xconn.display, + let res = (xlib.XQueryExtension)( + **xconn.display, b"XInputExtension\0".as_ptr() as *const c_char, &mut ext.opcode, &mut ext.first_event_id, @@ -131,7 +146,7 @@ impl EventLoop { ); if res == ffi::False { - panic!("X server missing XInput extension"); + panic!("[winit] X server missing XInput extension"); } ext @@ -140,20 +155,20 @@ impl EventLoop { unsafe { let mut xinput_major_ver = ffi::XI_2_Major; let mut xinput_minor_ver = ffi::XI_2_Minor; - if (xconn.xinput2.XIQueryVersion)( - xconn.display, + if (xinput2.XIQueryVersion)( + **xconn.display, &mut xinput_major_ver, &mut xinput_minor_ver, ) != ffi::Success as libc::c_int { panic!( - "X server has XInput extension {}.{} but does not support XInput2", + "[winit] X server has XInput extension {}.{} but does not support XInput2", xinput_major_ver, xinput_minor_ver, ); } } - xconn.update_cached_wm_info(root); + xconn.update_cached_wm_info(); let pending_redraws: Arc>> = Default::default(); @@ -163,7 +178,6 @@ impl EventLoop { let target = Rc::new(RootELW { p: super::EventLoopWindowTarget::X(EventLoopWindowTarget { ime, - root, windows: Default::default(), _marker: ::std::marker::PhantomData, ime_sender, @@ -210,10 +224,16 @@ impl EventLoop { // Register for device hotplug events // (The request buffer is flushed during `init_device`) - get_xtarget(&target) - .xconn - .select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask) - .queue(); + // + // Hierarchy changed events should arrive to all roots, so using default + // is okay here. + { + let root = unsafe { (xlib.XDefaultRootWindow)(**get_xtarget(&target).xconn.display) }; + get_xtarget(&target) + .xconn + .select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask) + .queue(); + } event_processor.init_device(ffi::XIAllDevices); @@ -446,24 +466,23 @@ impl EventLoopProxy { } } -struct DeviceInfo<'a> { - xconn: &'a XConnection, +struct DeviceInfo { info: *const ffi::XIDeviceInfo, count: usize, } -impl<'a> DeviceInfo<'a> { - fn get(xconn: &'a XConnection, device: c_int) -> Option { +impl DeviceInfo { + fn get(xconn: &XConnection, device: c_int) -> Option { + let xinput2 = syms!(XINPUT2); unsafe { let mut count = 0; - let info = (xconn.xinput2.XIQueryDevice)(xconn.display, device, &mut count); - xconn.check_errors().ok()?; + let info = (xinput2.XIQueryDevice)(**xconn.display, device, &mut count); + xconn.display.check_errors().ok()?; if info.is_null() || count == 0 { None } else { Some(DeviceInfo { - xconn, info, count: count as usize, }) @@ -472,14 +491,15 @@ impl<'a> DeviceInfo<'a> { } } -impl<'a> Drop for DeviceInfo<'a> { +impl Drop for DeviceInfo { fn drop(&mut self) { + let xinput2 = syms!(XINPUT2); assert!(!self.info.is_null()); - unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) }; + unsafe { (xinput2.XIFreeDeviceInfo)(self.info as *mut _) }; } } -impl<'a> Deref for DeviceInfo<'a> { +impl Deref for DeviceInfo { type Target = [ffi::XIDeviceInfo]; fn deref(&self) -> &Self::Target { unsafe { slice::from_raw_parts(self.info, self.count) } @@ -507,7 +527,7 @@ impl Window { event_loop: &EventLoopWindowTarget, attribs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, - ) -> Result { + ) -> Result { let window = Arc::new(UnownedWindow::new(&event_loop, attribs, pl_attribs)?); event_loop .windows @@ -521,10 +541,11 @@ impl Drop for Window { fn drop(&mut self) { let window = self.deref(); let xconn = &window.xconn; + let xlib = syms!(XLIB); unsafe { - (xconn.xlib.XDestroyWindow)(xconn.display, window.id().0); + (xlib.XDestroyWindow)(**xconn.display, window.id().0); // If the window was somehow already destroyed, we'll get a `BadWindow` error, which we don't care about. - let _ = xconn.check_errors(); + let _ = xconn.display.check_errors(); } } } @@ -541,9 +562,10 @@ impl<'a> GenericEventCookie<'a> { xconn: &'b XConnection, event: ffi::XEvent, ) -> Option> { + let xlib = syms!(XLIB); unsafe { let mut cookie: ffi::XGenericEventCookie = From::from(event); - if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == ffi::True { + if (xlib.XGetEventData)(**xconn.display, &mut cookie) == ffi::True { Some(GenericEventCookie { xconn, cookie }) } else { None @@ -554,8 +576,9 @@ impl<'a> GenericEventCookie<'a> { impl<'a> Drop for GenericEventCookie<'a> { fn drop(&mut self) { + let xlib = syms!(XLIB); unsafe { - (self.xconn.xlib.XFreeEventData)(self.xconn.display, &mut self.cookie); + (xlib.XFreeEventData)(**self.xconn.display, &mut self.cookie); } } } @@ -611,9 +634,21 @@ impl Device { | ffi::XI_RawKeyPressMask | ffi::XI_RawKeyReleaseMask; // The request buffer is flushed when we poll for events - wt.xconn - .select_xinput_events(wt.root, info.deviceid, mask) - .queue(); + { + // The raw motions events appear to still happen even when the + // focus is on a different screen on multihead systems. + // + // I (Freya) haven't tested if it breaks on multihead systems + // with two different window managers, but we can't support + // everything, can we? That just strikes me as a painful setup + // that would have bigger problems than some missing device + // events. + let xlib = syms!(XLIB); + let root = unsafe { (xlib.XDefaultRootWindow)(**wt.xconn.display) }; + wt.xconn + .select_xinput_events(root, info.deviceid, mask) + .queue(); + } // Identify scroll axes for class_ptr in Device::classes(info) { diff --git a/src/platform_impl/linux/x11/monitor.rs b/src/platform_impl/linux/x11/monitor.rs index 274e0cda9f..a5653aa1c6 100644 --- a/src/platform_impl/linux/x11/monitor.rs +++ b/src/platform_impl/linux/x11/monitor.rs @@ -1,20 +1,19 @@ -use std::os::raw::*; - -use parking_lot::Mutex; +use std::os::raw; +use std::{env, ptr, str::FromStr}; use super::{ - ffi::{ - RRCrtc, RRCrtcChangeNotifyMask, RRMode, RROutputPropertyNotifyMask, - RRScreenChangeNotifyMask, True, Window, XRRCrtcInfo, XRRScreenResources, - }, - util, XConnection, XError, + ffi::{XRRCrtcInfo, XRROutputInfo}, + util, XConnection, }; use crate::{ - dpi::{PhysicalPosition, PhysicalSize}, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, platform_impl::{MonitorHandle as PlatformMonitorHandle, VideoMode as PlatformVideoMode}, }; +use parking_lot::Mutex; +use winit_types::dpi::{self, PhysicalPosition, PhysicalSize}; +use x11_dl::xrandr::{RRCrtc, RRMode}; + // Used for testing. This should always be committed as false. const DISABLE_MONITOR_LIST_CACHING: bool = false; @@ -32,8 +31,84 @@ pub struct VideoMode { pub(crate) size: (u32, u32), pub(crate) bit_depth: u16, pub(crate) refresh_rate: u16, - pub(crate) native_mode: RRMode, pub(crate) monitor: Option, + /// RandR only. None otherwise. + pub(crate) native_mode: Option, +} + +/// How we are going to get the scale factor. +#[derive(PartialEq, Debug, Copy, Clone)] +pub enum ScaleFactorSource { + Scale(f64), + Xft, + XRandR, + Xlib, +} + +impl From for ScaleFactorSource { + fn from(mis: MonitorInfoSource) -> Self { + match mis { + MonitorInfoSource::XRandR => ScaleFactorSource::XRandR, + MonitorInfoSource::Xinerama => ScaleFactorSource::Xlib, + MonitorInfoSource::Xlib => ScaleFactorSource::Xlib, + } + } +} + +impl From for Vec { + fn from(mis: MonitorInfoSource) -> Self { + // Override DPI if `WINIT_X11_SCALE_FACTOR` variable is set + let deprecated_scale_override = env::var("WINIT_HIDPI_FACTOR").ok(); + if deprecated_scale_override.is_some() { + warn!( + "[winit] The WINIT_HIDPI_FACTOR environment variable is deprecated; use WINIT_X11_SCALE_FACTOR" + ) + } + + let sfc: ScaleFactorSource = mis.into(); + let default = vec![ScaleFactorSource::Xft, sfc]; + env::var("WINIT_X11_SCALE_FACTOR").ok().map_or_else( + || default.clone(), + |var| match var.trim() { + "" => default.clone(), + var => { + var + .to_lowercase() + .split(",") + .map(|var| match var { + "randr" => ScaleFactorSource::XRandR, + "xlib" => ScaleFactorSource::Xlib, + "xft" => ScaleFactorSource::Xft, + _ => { + if let Ok(scale) = f64::from_str(&var) { + if !dpi::validate_scale_factor(scale) { + panic!( + "[winit] `WINIT_X11_SCALE_FACTOR` invalid; Scale factors must be either normal floats greater than 0, or `randr`. Got `{}`", + scale, + ); + } + ScaleFactorSource::Scale(scale) + } else { + panic!( + "[winit] `WINIT_X11_SCALE_FACTOR` invalid; Scale factors must be either normal floats greater than 0, or `randr`. Got `{}`", + var + ); + } + } + }) + .collect() + } + }, + ) + } +} + +/// How we are going to get monitor info. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub enum MonitorInfoSource { + XRandR, + Xinerama, + Xlib, } impl VideoMode { @@ -62,22 +137,24 @@ impl VideoMode { #[derive(Debug, Clone)] pub struct MonitorHandle { - /// The actual id - pub(crate) id: RRCrtc, + /// The actual id, RandR only. + pub(crate) id: Option, + /// X11 screen. None if dummy. + pub(crate) screen: Option, /// The name of the monitor pub(crate) name: String, /// The size of the monitor - dimensions: (u32, u32), + pub(crate) dimensions: (u32, u32), /// The position of the monitor in the X screen - position: (i32, i32), + pub(crate) position: (i32, i32), /// If the monitor is the primary one - primary: bool, + pub(crate) primary: bool, /// The DPI scale factor pub(crate) scale_factor: f64, /// Used to determine which windows are on this monitor pub(crate) rect: util::AaRect, /// Supported video modes on this monitor - video_modes: Vec, + pub(crate) video_modes: Vec, } impl PartialEq for MonitorHandle { @@ -107,32 +184,9 @@ impl std::hash::Hash for MonitorHandle { } impl MonitorHandle { - fn new( - xconn: &XConnection, - resources: *mut XRRScreenResources, - id: RRCrtc, - crtc: *mut XRRCrtcInfo, - primary: bool, - ) -> Option { - let (name, scale_factor, video_modes) = unsafe { xconn.get_output_info(resources, crtc)? }; - let dimensions = unsafe { ((*crtc).width as u32, (*crtc).height as u32) }; - let position = unsafe { ((*crtc).x as i32, (*crtc).y as i32) }; - let rect = util::AaRect::new(position, dimensions); - Some(MonitorHandle { - id, - name, - scale_factor, - dimensions, - position, - primary, - rect, - video_modes, - }) - } - pub fn dummy() -> Self { MonitorHandle { - id: 0, + id: Some(0), name: "".into(), scale_factor: 1.0, dimensions: (1, 1), @@ -140,12 +194,13 @@ impl MonitorHandle { primary: true, rect: util::AaRect::new((0, 0), (1, 1)), video_modes: Vec::new(), + screen: None, } } pub(crate) fn is_dummy(&self) -> bool { // Zero is an invalid XID value; no real monitor will have it - self.id == 0 + self.id == Some(0) } pub fn name(&self) -> Option { @@ -153,8 +208,13 @@ impl MonitorHandle { } #[inline] - pub fn native_identifier(&self) -> u32 { - self.id as u32 + pub fn native_id(&self) -> Option { + self.id.map(|id| id as u32) + } + + #[inline] + pub fn x11_screen(&self) -> Option { + self.screen } pub fn size(&self) -> PhysicalSize { @@ -183,7 +243,11 @@ impl MonitorHandle { } impl XConnection { - pub fn get_monitor_for_window(&self, window_rect: Option) -> MonitorHandle { + pub fn get_monitor_for_window( + &self, + window_rect: Option, + window_screen: raw::c_int, + ) -> MonitorHandle { let monitors = self.available_monitors(); if monitors.is_empty() { @@ -201,6 +265,9 @@ impl XConnection { let mut largest_overlap = 0; let mut matched_monitor = default; for monitor in &monitors { + if monitor.screen != Some(window_screen) { + continue; + }; let overlapping_area = window_rect.get_overlapping_area(&monitor.rect); if overlapping_area > largest_overlap { largest_overlap = overlapping_area; @@ -212,52 +279,10 @@ impl XConnection { } fn query_monitor_list(&self) -> Vec { - unsafe { - let mut major = 0; - let mut minor = 0; - (self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor); - - let root = (self.xlib.XDefaultRootWindow)(self.display); - let resources = if (major == 1 && minor >= 3) || major > 1 { - (self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root) - } else { - // WARNING: this function is supposedly very slow, on the order of hundreds of ms. - // Upon failure, `resources` will be null. - (self.xrandr.XRRGetScreenResources)(self.display, root) - }; - - if resources.is_null() { - panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist."); - } - - let mut available; - let mut has_primary = false; - - let primary = (self.xrandr.XRRGetOutputPrimary)(self.display, root); - available = Vec::with_capacity((*resources).ncrtc as usize); - for crtc_index in 0..(*resources).ncrtc { - let crtc_id = *((*resources).crtcs.offset(crtc_index as isize)); - let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id); - let is_active = (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0; - if is_active { - let is_primary = *(*crtc).outputs.offset(0) == primary; - has_primary |= is_primary; - MonitorHandle::new(self, resources, crtc_id, crtc, is_primary) - .map(|monitor_id| available.push(monitor_id)); - } - (self.xrandr.XRRFreeCrtcInfo)(crtc); - } - - // If no monitors were detected as being primary, we just pick one ourselves! - if !has_primary { - if let Some(ref mut fallback) = available.first_mut() { - // Setting this here will come in handy if we ever add an `is_primary` method. - fallback.primary = true; - } - } - - (self.xrandr.XRRFreeScreenResources)(resources); - available + match self.monitor_info_source { + MonitorInfoSource::XRandR => self.query_monitor_list_xrandr(), + MonitorInfoSource::Xinerama => self.query_monitor_list_xinerama(), + MonitorInfoSource::Xlib => self.query_monitor_list_none(), } } @@ -283,32 +308,103 @@ impl XConnection { .find(|monitor| monitor.primary) .unwrap_or_else(MonitorHandle::dummy) } +} - pub fn select_xrandr_input(&self, root: Window) -> Result { - let has_xrandr = unsafe { - let mut major = 0; - let mut minor = 0; - (self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor) - }; - assert!( - has_xrandr == True, - "[winit] XRandR extension not available." +pub fn calc_scale_factor( + (width_px, height_px): (u32, u32), + (width_mm, height_mm): (u64, u64), +) -> f64 { + // See http://xpra.org/trac/ticket/728 for more information. + if width_mm == 0 || height_mm == 0 { + warn!( + "[winit] XRandR reported that the display's 0mm in size, which is certifiably insane" ); + return 1.0; + } - let mut event_offset = 0; - let mut error_offset = 0; - let status = unsafe { - (self.xrandr.XRRQueryExtension)(self.display, &mut event_offset, &mut error_offset) - }; + let ppmm = ((width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)).sqrt(); + // Quantize 1/12 step size + let scale_factor = ((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0); + assert!(dpi::validate_scale_factor(scale_factor)); + scale_factor +} - if status != True { - self.check_errors()?; - unreachable!("[winit] `XRRQueryExtension` failed but no error was received."); +impl XConnection { + // Retrieve DPI from Xft.dpi property + pub fn get_xft_scale(&self) -> Option { + unsafe { + let xlib = syms!(XLIB); + (xlib.XrmInitialize)(); + let resource_manager_str = (xlib.XResourceManagerString)(**self.display); + if resource_manager_str == ptr::null_mut() { + return None; + } + if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() { + let name: &str = "Xft.dpi:\t"; + for pair in res.split("\n") { + if pair.starts_with(&name) { + let res = &pair[name.len()..]; + return f64::from_str(&res).ok(); + } + } + } + None } + } - let mask = RRCrtcChangeNotifyMask | RROutputPropertyNotifyMask | RRScreenChangeNotifyMask; - unsafe { (self.xrandr.XRRSelectInput)(self.display, root, mask) }; + pub fn get_xlib_dims(&self, screen: raw::c_int) -> ((u32, u32), (u64, u64)) { + unsafe { + let xlib = syms!(XLIB); + let screen_ptr = (xlib.XScreenOfDisplay)(**self.display, screen); + let dimensions = ( + (xlib.XWidthOfScreen)(screen_ptr) as u32, + (xlib.XHeightOfScreen)(screen_ptr) as u32, + ); + let dimensions_mm = ( + (xlib.XWidthMMOfScreen)(screen_ptr) as u64, + (xlib.XHeightMMOfScreen)(screen_ptr) as u64, + ); + (dimensions, dimensions_mm) + } + } + + pub fn acquire_scale_factor( + &self, + xrandr_parts: Option<(*mut XRROutputInfo, *mut XRRCrtcInfo)>, + screen: raw::c_int, + ) -> Option { + let scale_factor_sources: Vec = self.monitor_info_source.into(); + + for sfc in scale_factor_sources { + match sfc { + ScaleFactorSource::Scale(s) => return Some(s), + ScaleFactorSource::Xft => { + if let Some(s) = self.get_xft_scale() { + return Some(s / 96.0); + } + } + ScaleFactorSource::Xlib => { + let (dimensions, dimensions_mm) = self.get_xlib_dims(screen); + return Some(calc_scale_factor(dimensions, dimensions_mm)); + } + ScaleFactorSource::XRandR => unsafe { + if let Some((output_info, crtc)) = xrandr_parts { + return Some(calc_scale_factor( + ((*crtc).width as u32, (*crtc).height as u32), + ( + (*output_info).mm_width as u64, + (*output_info).mm_height as u64, + ), + )); + } else { + panic!( + "[winit] `WINIT_X11_SCALE_FACTOR` had `randr`, but system does not have RANDR ext.", + ); + } + }, + } + } - Ok(event_offset) + None } } diff --git a/src/platform_impl/linux/x11/util/atom.rs b/src/platform_impl/linux/x11/util/atom.rs index c311c5d237..3600832550 100644 --- a/src/platform_impl/linux/x11/util/atom.rs +++ b/src/platform_impl/linux/x11/util/atom.rs @@ -6,6 +6,7 @@ use std::{ }; use parking_lot::Mutex; +use winit_types::error::Error; use super::*; @@ -17,6 +18,7 @@ lazy_static! { impl XConnection { pub fn get_atom + Debug>(&self, name: T) -> ffi::Atom { + let xlib = syms!(XLIB); let name = name.as_ref(); let mut atom_cache_lock = ATOM_CACHE.lock(); let cached_atom = (*atom_cache_lock).get(name).cloned(); @@ -24,15 +26,14 @@ impl XConnection { atom } else { let atom = unsafe { - (self.xlib.XInternAtom)(self.display, name.as_ptr() as *const c_char, ffi::False) + (xlib.XInternAtom)(**self.display, name.as_ptr() as *const c_char, ffi::False) }; if atom == 0 { - let msg = format!( - "`XInternAtom` failed, which really shouldn't happen. Atom: {:?}, Error: {:#?}", + panic!( + "[winit] `XInternAtom` failed, which really shouldn't happen. Atom: {:?}, Error: {:#?}", name, - self.check_errors(), + self.display.check_errors(), ); - panic!(msg); } /*println!( "XInternAtom name:{:?} atom:{:?}", @@ -52,16 +53,17 @@ impl XConnection { // Note: this doesn't use caching, for the sake of simplicity. // If you're dealing with this many atoms, you'll usually want to cache them locally anyway. - pub unsafe fn get_atoms(&self, names: &[*mut c_char]) -> Result, XError> { + pub unsafe fn get_atoms(&self, names: &[*mut c_char]) -> Result, Error> { + let xlib = syms!(XLIB); let mut atoms = Vec::with_capacity(names.len()); - (self.xlib.XInternAtoms)( - self.display, + (xlib.XInternAtoms)( + **self.display, names.as_ptr() as *mut _, names.len() as c_int, ffi::False, atoms.as_mut_ptr(), ); - self.check_errors()?; + self.display.check_errors()?; atoms.set_len(names.len()); /*println!( "XInternAtoms atoms:{:?}", diff --git a/src/platform_impl/linux/x11/util/client_msg.rs b/src/platform_impl/linux/x11/util/client_msg.rs index 2f2b7edf9c..2558da35ee 100644 --- a/src/platform_impl/linux/x11/util/client_msg.rs +++ b/src/platform_impl/linux/x11/util/client_msg.rs @@ -9,10 +9,11 @@ impl XConnection { event_mask: Option, event: T, ) -> Flusher<'_> { + let xlib = syms!(XLIB); let event_mask = event_mask.unwrap_or(ffi::NoEventMask); unsafe { - (self.xlib.XSendEvent)( - self.display, + (xlib.XSendEvent)( + **self.display, target_window, ffi::False, event_mask, @@ -32,7 +33,7 @@ impl XConnection { ) -> Flusher<'_> { let event = ffi::XClientMessageEvent { type_: ffi::ClientMessage, - display: self.display, + display: **self.display, window, message_type, format: c_long::FORMAT as c_int, diff --git a/src/platform_impl/linux/x11/util/cursor.rs b/src/platform_impl/linux/x11/util/cursor.rs index 684af49da9..d5cac33a45 100644 --- a/src/platform_impl/linux/x11/util/cursor.rs +++ b/src/platform_impl/linux/x11/util/cursor.rs @@ -3,34 +3,36 @@ use crate::window::CursorIcon; use super::*; impl XConnection { - pub fn set_cursor_icon(&self, window: ffi::Window, cursor: Option) { + pub fn set_cursor_icon( + &self, + window: ffi::Window, + root: ffi::Window, + cursor: Option, + ) { let cursor = *self .cursor_cache .lock() .entry(cursor) - .or_insert_with(|| self.get_cursor(cursor)); + .or_insert_with(|| self.get_cursor(cursor, root)); self.update_cursor(window, cursor); } - fn create_empty_cursor(&self) -> ffi::Cursor { + fn create_empty_cursor(&self, root: ffi::Window) -> ffi::Cursor { + let xlib = syms!(XLIB); let data = 0; - let pixmap = unsafe { - let screen = (self.xlib.XDefaultScreen)(self.display); - let window = (self.xlib.XRootWindow)(self.display, screen); - (self.xlib.XCreateBitmapFromData)(self.display, window, &data, 1, 1) - }; + let pixmap = unsafe { (xlib.XCreateBitmapFromData)(**self.display, root, &data, 1, 1) }; if pixmap == 0 { - panic!("failed to allocate pixmap for cursor"); + panic!("[winit] failed to allocate pixmap for cursor"); } unsafe { // We don't care about this color, since it only fills bytes // in the pixmap which are not 0 in the mask. let mut dummy_color = MaybeUninit::uninit(); - let cursor = (self.xlib.XCreatePixmapCursor)( - self.display, + let cursor = (xlib.XCreatePixmapCursor)( + **self.display, pixmap, pixmap, dummy_color.as_mut_ptr(), @@ -38,15 +40,16 @@ impl XConnection { 0, 0, ); - (self.xlib.XFreePixmap)(self.display, pixmap); + (xlib.XFreePixmap)(**self.display, pixmap); cursor } } fn load_cursor(&self, name: &[u8]) -> ffi::Cursor { + let xcursor = syms!(XCURSOR); unsafe { - (self.xcursor.XcursorLibraryLoadCursor)(self.display, name.as_ptr() as *const c_char) + (xcursor.XcursorLibraryLoadCursor)(**self.display, name.as_ptr() as *const c_char) } } @@ -60,10 +63,10 @@ impl XConnection { 0 } - fn get_cursor(&self, cursor: Option) -> ffi::Cursor { + fn get_cursor(&self, cursor: Option, root: ffi::Window) -> ffi::Cursor { let cursor = match cursor { Some(cursor) => cursor, - None => return self.create_empty_cursor(), + None => return self.create_empty_cursor(root), }; let load = |name: &[u8]| self.load_cursor(name); @@ -120,10 +123,12 @@ impl XConnection { } fn update_cursor(&self, window: ffi::Window, cursor: ffi::Cursor) { + let xlib = syms!(XLIB); unsafe { - (self.xlib.XDefineCursor)(self.display, window, cursor); + (xlib.XDefineCursor)(**self.display, window, cursor); - self.flush_requests().expect("Failed to set the cursor"); + self.flush_requests() + .expect("[winit] Failed to set the cursor"); } } } diff --git a/src/platform_impl/linux/x11/util/geometry.rs b/src/platform_impl/linux/x11/util/geometry.rs index d8f466fc2a..a32919deec 100644 --- a/src/platform_impl/linux/x11/util/geometry.rs +++ b/src/platform_impl/linux/x11/util/geometry.rs @@ -1,4 +1,7 @@ use std::cmp; +use std::os::raw; + +use winit_types::error::Error; use super::*; @@ -146,12 +149,13 @@ impl XConnection { &self, window: ffi::Window, root: ffi::Window, - ) -> Result { + ) -> Result { + let xlib = syms!(XLIB); let mut coords = TranslatedCoords::default(); unsafe { - (self.xlib.XTranslateCoordinates)( - self.display, + (xlib.XTranslateCoordinates)( + **self.display, window, root, 0, @@ -162,17 +166,18 @@ impl XConnection { ); } - self.check_errors()?; + self.display.check_errors()?; Ok(coords) } // This is adequate for inner_size - pub fn get_geometry(&self, window: ffi::Window) -> Result { + pub fn get_geometry(&self, window: ffi::Window) -> Result { + let xlib = syms!(XLIB); let mut geometry = Geometry::default(); let _status = unsafe { - (self.xlib.XGetGeometry)( - self.display, + (xlib.XGetGeometry)( + **self.display, window, &mut geometry.root, &mut geometry.x_rel_parent, @@ -184,14 +189,14 @@ impl XConnection { ) }; - self.check_errors()?; + self.display.check_errors()?; Ok(geometry) } - fn get_frame_extents(&self, window: ffi::Window) -> Option { + fn get_frame_extents(&self, window: ffi::Window, screen: raw::c_int) -> Option { let extents_atom = unsafe { self.get_atom_unchecked(b"_NET_FRAME_EXTENTS\0") }; - if !hint_is_supported(extents_atom) { + if !hint_is_supported(extents_atom, screen) { return None; } @@ -216,13 +221,15 @@ impl XConnection { }) } - pub fn is_top_level(&self, window: ffi::Window, root: ffi::Window) -> Option { + pub fn is_top_level(&self, window: ffi::Window, screen: raw::c_int) -> Option { + let xlib = syms!(XLIB); let client_list_atom = unsafe { self.get_atom_unchecked(b"_NET_CLIENT_LIST\0") }; - if !hint_is_supported(client_list_atom) { + if !hint_is_supported(client_list_atom, screen) { return None; } + let root = unsafe { (xlib.XRootWindow)(**self.display, screen) }; let client_list: Option> = self .get_property(root, client_list_atom, ffi::XA_WINDOW) .ok(); @@ -230,7 +237,8 @@ impl XConnection { client_list.map(|client_list| client_list.contains(&window)) } - fn get_parent_window(&self, window: ffi::Window) -> Result { + fn get_parent_window(&self, window: ffi::Window) -> Result { + let xlib = syms!(XLIB); let parent = unsafe { let mut root = 0; let mut parent = 0; @@ -238,8 +246,8 @@ impl XConnection { let mut nchildren = 0; // What's filled into `parent` if `window` is the root window? - let _status = (self.xlib.XQueryTree)( - self.display, + let _status = (xlib.XQueryTree)( + **self.display, window, &mut root, &mut parent, @@ -249,19 +257,19 @@ impl XConnection { // The list of children isn't used if children != ptr::null_mut() { - (self.xlib.XFree)(children as *mut _); + (xlib.XFree)(children as *mut _); } parent }; - self.check_errors().map(|_| parent) + self.display.check_errors().map(|_| parent) } fn climb_hierarchy( &self, window: ffi::Window, root: ffi::Window, - ) -> Result { + ) -> Result { let mut outer_window = window; loop { let candidate = self.get_parent_window(outer_window)?; @@ -276,25 +284,27 @@ impl XConnection { pub fn get_frame_extents_heuristic( &self, window: ffi::Window, - root: ffi::Window, + screen: raw::c_int, ) -> FrameExtentsHeuristic { use self::FrameExtentsHeuristicPath::*; + let xlib = syms!(XLIB); // Position relative to root window. // With rare exceptions, this is the position of a nested window. Cases where the window // isn't nested are outlined in the comments throghout this function, but in addition to // that, fullscreen windows often aren't nested. + let root = unsafe { (xlib.XRootWindow)(**self.display, screen) }; let (inner_y_rel_root, child) = { let coords = self .translate_coords(window, root) - .expect("Failed to translate window coordinates"); + .expect("[winit] Failed to translate window coordinates"); (coords.y_rel_root, coords.child) }; let (width, height, border) = { let inner_geometry = self .get_geometry(window) - .expect("Failed to get inner window geometry"); + .expect("[winit] Failed to get inner window geometry"); ( inner_geometry.width, inner_geometry.height, @@ -307,10 +317,10 @@ impl XConnection { // when y is on the range [0, 2] and if the window has been unfocused since being // undecorated (or was undecorated upon construction), the first condition is true, // requiring us to rely on the second condition. - let nested = !(window == child || self.is_top_level(child, root) == Some(true)); + let nested = !(window == child || self.is_top_level(child, screen) == Some(true)); // Hopefully the WM supports EWMH, allowing us to get exact info on the window frames. - if let Some(mut frame_extents) = self.get_frame_extents(window) { + if let Some(mut frame_extents) = self.get_frame_extents(window, screen) { // Mutter/Muffin/Budgie and Marco preserve their decorated frame extents when // decorations are disabled, but since the window becomes un-nested, it's easy to // catch. @@ -347,11 +357,11 @@ impl XConnection { // nested in. let outer_window = self .climb_hierarchy(window, root) - .expect("Failed to climb window hierarchy"); + .expect("[winit] Failed to climb window hierarchy"); let (outer_y, outer_width, outer_height) = { let outer_geometry = self .get_geometry(outer_window) - .expect("Failed to get outer window geometry"); + .expect("[winit] Failed to get outer window geometry"); ( outer_geometry.y_rel_parent, outer_geometry.width, diff --git a/src/platform_impl/linux/x11/util/hint.rs b/src/platform_impl/linux/x11/util/hint.rs index 809f3063b0..b992f0bc3d 100644 --- a/src/platform_impl/linux/x11/util/hint.rs +++ b/src/platform_impl/linux/x11/util/hint.rs @@ -1,6 +1,8 @@ use std::slice; use std::sync::Arc; +use winit_types::error::Error; + use super::*; #[derive(Debug)] @@ -24,7 +26,7 @@ impl From for StateOperation { /// X window type. Maps directly to /// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html). #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde_feature", derive(Serialize, Deserialize))] pub enum WindowType { /// A desktop feature. This can include a single window containing desktop icons with the same dimensions as the /// screen, allowing the desktop environment to have full control of the desktop, without the need for proxying @@ -179,12 +181,12 @@ impl MwmHints { } } -pub struct NormalHints<'a> { - size_hints: XSmartPointer<'a, ffi::XSizeHints>, +pub struct NormalHints { + size_hints: XSmartPointer, } -impl<'a> NormalHints<'a> { - pub fn new(xconn: &'a XConnection) -> Self { +impl<'a> NormalHints { + pub fn new(xconn: &XConnection) -> Self { NormalHints { size_hints: xconn.alloc_size_hints(), } @@ -243,16 +245,14 @@ impl<'a> NormalHints<'a> { } impl XConnection { - pub fn get_wm_hints( - &self, - window: ffi::Window, - ) -> Result, XError> { - let wm_hints = unsafe { (self.xlib.XGetWMHints)(self.display, window) }; - self.check_errors()?; + pub fn get_wm_hints(&self, window: ffi::Window) -> Result, Error> { + let xlib = syms!(XLIB); + let wm_hints = unsafe { (xlib.XGetWMHints)(**self.display, window) }; + self.display.check_errors()?; let wm_hints = if wm_hints.is_null() { self.alloc_wm_hints() } else { - XSmartPointer::new(self, wm_hints).unwrap() + XSmartPointer::new(wm_hints).unwrap() }; Ok(wm_hints) } @@ -260,35 +260,36 @@ impl XConnection { pub fn set_wm_hints( &self, window: ffi::Window, - wm_hints: XSmartPointer<'_, ffi::XWMHints>, + wm_hints: XSmartPointer, ) -> Flusher<'_> { + let xlib = syms!(XLIB); unsafe { - (self.xlib.XSetWMHints)(self.display, window, wm_hints.ptr); + (xlib.XSetWMHints)(**self.display, window, wm_hints.ptr); } Flusher::new(self) } - pub fn get_normal_hints(&self, window: ffi::Window) -> Result, XError> { + pub fn get_normal_hints(&self, window: ffi::Window) -> Result { + let xlib = syms!(XLIB); let size_hints = self.alloc_size_hints(); let mut supplied_by_user = MaybeUninit::uninit(); unsafe { - (self.xlib.XGetWMNormalHints)( - self.display, + (xlib.XGetWMNormalHints)( + **self.display, window, size_hints.ptr, supplied_by_user.as_mut_ptr(), ); } - self.check_errors().map(|_| NormalHints { size_hints }) + self.display + .check_errors() + .map(|_| NormalHints { size_hints }) } - pub fn set_normal_hints( - &self, - window: ffi::Window, - normal_hints: NormalHints<'_>, - ) -> Flusher<'_> { + pub fn set_normal_hints(&self, window: ffi::Window, normal_hints: NormalHints) -> Flusher<'_> { + let xlib = syms!(XLIB); unsafe { - (self.xlib.XSetWMNormalHints)(self.display, window, normal_hints.size_hints.ptr); + (xlib.XSetWMNormalHints)(**self.display, window, normal_hints.size_hints.ptr); } Flusher::new(self) } diff --git a/src/platform_impl/linux/x11/util/input.rs b/src/platform_impl/linux/x11/util/input.rs index e9f45aee1c..88967e0702 100644 --- a/src/platform_impl/linux/x11/util/input.rs +++ b/src/platform_impl/linux/x11/util/input.rs @@ -1,5 +1,7 @@ use std::{slice, str}; +use winit_types::error::Error; + use super::*; use crate::event::ModifiersState; @@ -27,8 +29,7 @@ impl ModifiersState { } // NOTE: Some of these fields are not used, but may be of use in the future. -pub struct PointerState<'a> { - xconn: &'a XConnection, +pub struct PointerState { pub root: ffi::Window, pub child: ffi::Window, pub root_x: c_double, @@ -41,18 +42,19 @@ pub struct PointerState<'a> { pub relative_to_window: bool, } -impl<'a> PointerState<'a> { +impl PointerState { pub fn get_modifier_state(&self) -> ModifiersState { ModifiersState::from_x11(&self.modifiers) } } -impl<'a> Drop for PointerState<'a> { +impl Drop for PointerState { fn drop(&mut self) { + let xlib = syms!(XLIB); if !self.buttons.mask.is_null() { unsafe { // This is why you need to read the docs carefully... - (self.xconn.xlib.XFree)(self.buttons.mask as _); + (xlib.XFree)(self.buttons.mask as _); } } } @@ -65,14 +67,15 @@ impl XConnection { device_id: c_int, mask: i32, ) -> Flusher<'_> { + let xinput2 = syms!(XINPUT2); let mut event_mask = ffi::XIEventMask { deviceid: device_id, mask: &mask as *const _ as *mut c_uchar, mask_len: mem::size_of_val(&mask) as c_int, }; unsafe { - (self.xinput2.XISelectEvents)( - self.display, + (xinput2.XISelectEvents)( + **self.display, window, &mut event_mask as *mut ffi::XIEventMask, 1, // number of masks to read from pointer above @@ -83,7 +86,8 @@ impl XConnection { #[allow(dead_code)] pub fn select_xkb_events(&self, device_id: c_uint, mask: c_ulong) -> Option> { - let status = unsafe { (self.xlib.XkbSelectEvents)(self.display, device_id, mask, mask) }; + let xlib = syms!(XLIB); + let status = unsafe { (xlib.XkbSelectEvents)(**self.display, device_id, mask, mask) }; if status == ffi::True { Some(Flusher::new(self)) } else { @@ -95,7 +99,8 @@ impl XConnection { &self, window: ffi::Window, device_id: c_int, - ) -> Result, XError> { + ) -> Result { + let xinput2 = syms!(XINPUT2); unsafe { let mut root = 0; let mut child = 0; @@ -107,8 +112,8 @@ impl XConnection { let mut modifiers = Default::default(); let mut group = Default::default(); - let relative_to_window = (self.xinput2.XIQueryPointer)( - self.display, + let relative_to_window = (xinput2.XIQueryPointer)( + **self.display, device_id, window, &mut root, @@ -122,10 +127,9 @@ impl XConnection { &mut group, ) == ffi::True; - self.check_errors()?; + self.display.check_errors()?; Ok(PointerState { - xconn: self, root, child, root_x, @@ -147,10 +151,11 @@ impl XConnection { buffer: *mut u8, size: usize, ) -> (ffi::KeySym, ffi::Status, c_int) { + let xlib = syms!(XLIB); let mut keysym: ffi::KeySym = 0; let mut status: ffi::Status = 0; let count = unsafe { - (self.xlib.Xutf8LookupString)( + (xlib.Xutf8LookupString)( ic, key_event, buffer as *mut c_char, diff --git a/src/platform_impl/linux/x11/util/keys.rs b/src/platform_impl/linux/x11/util/keys.rs index fc0c9d9062..ace9a9f0db 100644 --- a/src/platform_impl/linux/x11/util/keys.rs +++ b/src/platform_impl/linux/x11/util/keys.rs @@ -63,24 +63,27 @@ impl Iterator for KeymapIter<'_> { impl XConnection { pub fn keycode_to_keysym(&self, keycode: ffi::KeyCode) -> ffi::KeySym { - unsafe { (self.xlib.XKeycodeToKeysym)(self.display, keycode, 0) } + let xlib = syms!(XLIB); + unsafe { (xlib.XKeycodeToKeysym)(**self.display, keycode, 0) } } pub fn lookup_keysym(&self, xkev: &mut ffi::XKeyEvent) -> ffi::KeySym { + let xlib = syms!(XLIB); let mut keysym = 0; unsafe { - (self.xlib.XLookupString)(xkev, ptr::null_mut(), 0, &mut keysym, ptr::null_mut()); + (xlib.XLookupString)(xkev, ptr::null_mut(), 0, &mut keysym, ptr::null_mut()); } keysym } pub fn query_keymap(&self) -> Keymap { + let xlib = syms!(XLIB); let mut keys = [0; 32]; unsafe { - (self.xlib.XQueryKeymap)(self.display, keys.as_mut_ptr() as *mut c_char); + (xlib.XQueryKeymap)(**self.display, keys.as_mut_ptr() as *mut c_char); } Keymap { keys } diff --git a/src/platform_impl/linux/x11/util/memory.rs b/src/platform_impl/linux/x11/util/memory.rs index c85cc37139..bf8964ca82 100644 --- a/src/platform_impl/linux/x11/util/memory.rs +++ b/src/platform_impl/linux/x11/util/memory.rs @@ -2,24 +2,23 @@ use std::ops::{Deref, DerefMut}; use super::*; -pub struct XSmartPointer<'a, T> { - xconn: &'a XConnection, +pub struct XSmartPointer { pub ptr: *mut T, } -impl<'a, T> XSmartPointer<'a, T> { +impl XSmartPointer { // You're responsible for only passing things to this that should be XFree'd. // Returns None if ptr is null. - pub fn new(xconn: &'a XConnection, ptr: *mut T) -> Option { + pub fn new(ptr: *mut T) -> Option { if !ptr.is_null() { - Some(XSmartPointer { xconn, ptr }) + Some(XSmartPointer { ptr }) } else { None } } } -impl<'a, T> Deref for XSmartPointer<'a, T> { +impl Deref for XSmartPointer { type Target = T; fn deref(&self) -> &T { @@ -27,33 +26,37 @@ impl<'a, T> Deref for XSmartPointer<'a, T> { } } -impl<'a, T> DerefMut for XSmartPointer<'a, T> { +impl DerefMut for XSmartPointer { fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.ptr } } } -impl<'a, T> Drop for XSmartPointer<'a, T> { +impl Drop for XSmartPointer { fn drop(&mut self) { + let xlib = syms!(XLIB); unsafe { - (self.xconn.xlib.XFree)(self.ptr as *mut _); + (xlib.XFree)(self.ptr as *mut _); } } } impl XConnection { - pub fn alloc_class_hint(&self) -> XSmartPointer<'_, ffi::XClassHint> { - XSmartPointer::new(self, unsafe { (self.xlib.XAllocClassHint)() }) - .expect("`XAllocClassHint` returned null; out of memory") + pub fn alloc_class_hint(&self) -> XSmartPointer { + let xlib = syms!(XLIB); + XSmartPointer::new(unsafe { (xlib.XAllocClassHint)() }) + .expect("[winit] `XAllocClassHint` returned null; out of memory") } - pub fn alloc_size_hints(&self) -> XSmartPointer<'_, ffi::XSizeHints> { - XSmartPointer::new(self, unsafe { (self.xlib.XAllocSizeHints)() }) - .expect("`XAllocSizeHints` returned null; out of memory") + pub fn alloc_size_hints(&self) -> XSmartPointer { + let xlib = syms!(XLIB); + XSmartPointer::new(unsafe { (xlib.XAllocSizeHints)() }) + .expect("[winit] `XAllocSizeHints` returned null; out of memory") } - pub fn alloc_wm_hints(&self) -> XSmartPointer<'_, ffi::XWMHints> { - XSmartPointer::new(self, unsafe { (self.xlib.XAllocWMHints)() }) - .expect("`XAllocWMHints` returned null; out of memory") + pub fn alloc_wm_hints(&self) -> XSmartPointer { + let xlib = syms!(XLIB); + XSmartPointer::new(unsafe { (xlib.XAllocWMHints)() }) + .expect("[winit] `XAllocWMHints` returned null; out of memory") } } diff --git a/src/platform_impl/linux/x11/util/mod.rs b/src/platform_impl/linux/x11/util/mod.rs index 4bd1420655..4f562bf2ca 100644 --- a/src/platform_impl/linux/x11/util/mod.rs +++ b/src/platform_impl/linux/x11/util/mod.rs @@ -12,6 +12,7 @@ mod input; pub mod keys; mod memory; pub mod modifiers; +mod non_randr; mod randr; mod window_property; mod wm; @@ -27,7 +28,9 @@ use std::{ ptr, }; -use super::{ffi, XConnection, XError}; +use super::{ffi, XConnection}; + +use winit_types::error::Error; pub fn maybe_change(field: &mut Option, value: T) -> bool { let wrapped = Some(value); @@ -50,12 +53,12 @@ impl<'a> Flusher<'a> { } // "I want this request sent now!" - pub fn flush(self) -> Result<(), XError> { + pub fn flush(self) -> Result<(), Error> { self.xconn.flush_requests() } // "I want the response now too!" - pub fn sync(self) -> Result<(), XError> { + pub fn sync(self) -> Result<(), Error> { self.xconn.sync_with_server() } @@ -73,17 +76,19 @@ impl XConnection { // 4. Calls that have a return dependent on a response (i.e. `XGetWindowProperty`) sync internally. // When in doubt, check the X11 source; if a function calls `_XReply`, it flushes and waits. // All util functions that abstract an async function will return a `Flusher`. - pub fn flush_requests(&self) -> Result<(), XError> { - unsafe { (self.xlib.XFlush)(self.display) }; + pub fn flush_requests(&self) -> Result<(), Error> { + let xlib = syms!(XLIB); + unsafe { (xlib.XFlush)(**self.display) }; //println!("XFlush"); // This isn't necessarily a useful time to check for errors (since our request hasn't // necessarily been processed yet) - self.check_errors() + self.display.check_errors() } - pub fn sync_with_server(&self) -> Result<(), XError> { - unsafe { (self.xlib.XSync)(self.display, ffi::False) }; + pub fn sync_with_server(&self) -> Result<(), Error> { + let xlib = syms!(XLIB); + unsafe { (xlib.XSync)(**self.display, ffi::False) }; //println!("XSync"); - self.check_errors() + self.display.check_errors() } } diff --git a/src/platform_impl/linux/x11/util/modifiers.rs b/src/platform_impl/linux/x11/util/modifiers.rs index 7c951997ad..23408111fd 100644 --- a/src/platform_impl/linux/x11/util/modifiers.rs +++ b/src/platform_impl/linux/x11/util/modifiers.rs @@ -48,16 +48,17 @@ impl ModifierKeymap { } pub fn reset_from_x_connection(&mut self, xconn: &XConnection) { + let xlib = syms!(XLIB); unsafe { - let keymap = (xconn.xlib.XGetModifierMapping)(xconn.display); + let keymap = (xlib.XGetModifierMapping)(**xconn.display); if keymap.is_null() { - panic!("failed to allocate XModifierKeymap"); + panic!("[winit] failed to allocate XModifierKeymap"); } self.reset_from_x_keymap(&*keymap); - (xconn.xlib.XFreeModifiermap)(keymap); + (xlib.XFreeModifiermap)(keymap); } } diff --git a/src/platform_impl/linux/x11/util/non_randr.rs b/src/platform_impl/linux/x11/util/non_randr.rs new file mode 100644 index 0000000000..f73205fb29 --- /dev/null +++ b/src/platform_impl/linux/x11/util/non_randr.rs @@ -0,0 +1,163 @@ +//! For use when RandR is not supported. +use std::os::raw; + +use super::*; +use crate::platform_impl::platform::x11::{monitor::MonitorHandle, VideoMode}; + +fn get_depths(xconn: &XConnection, screen: raw::c_int) -> Vec { + let xlib = syms!(XLIB); + unsafe { + let mut num_depths = 0; + let depths = (xlib.XListDepths)(**xconn.display, screen, &mut num_depths); + + if depths.is_null() { + let default_depth = (xlib.XDefaultDepth)(**xconn.display, screen); + return vec![default_depth]; + } + + assert!(num_depths != 0); + + let ret: Vec<_> = (0..num_depths) + .into_iter() + .map(|offset| *depths.offset(offset as isize)) + .collect(); + + (xlib.XFree)(depths as *mut _); + ret + } +} + +impl XConnection { + pub fn query_monitor_list_xinerama(&self) -> Vec { + let (xlib, xinerama) = syms!(XLIB, XINERAMA); + // Alright, so we got the list of screens, however, Xinerama also + // exposes a list of monitors and how they were stitched together to + // make the screens. + // + // Well, or at least that's how it should work. My AMD GPU's new shiny + // modern driver, amdgpu, did not support Xinerama at all, it would just + // SEGV. Xinerama is, after all, very much in the category of legacy shit. + // + // Meanwhile, my GPU's older driver, radeon, successfully enabled + // Xinerama and merged all my monitors into a single screen, however, + // despite how hard I tried, refused to expose the XINERAMA extension + // to X11 clients. + // + // We add these to the list in case applications want to borderless + // fullscreen onto just _one_ monitor, not the whole screen. + // + // FIXME: As it currently stands, if a user passes in one of these + // monitors for fullscreening, it will just fullscreen the whole screen. + // To fix this, we need to use the `_NET_WM_FULLSCREEN_MONITORS` atom, + // an atom specifically made for use with Xinerama. + // + // The issue is that I don't know of any WM that supports this atom + // because _it is specifically made for use with Xinerama,_ and what + // modern WM supports Xinerama? + // + // I (Freya) could barely get Xinerama working, so I'm not even going to + // bother supported this silly atom. + let mut monitors = self.query_monitor_list_none(); + let num_screens = unsafe { (xlib.XScreenCount)(**self.display) }; + + unsafe { + let mut number_of_screens = 0; + // Should return NULL and set number_of_screens to 0 only if + // Xinerama is not active. + // + // Now, in xdisplay.rs we check to make sure that Xinerama _is_ + // active, so this should never happen, however, we check this stuff + // here again because I really **really** don't trust the people who + // implemented these legacy X11 extensions, especially given how + // buggy it was to setup on my rig. + let xinerama_screens = + (xinerama.XineramaQueryScreens)(**self.display, &mut number_of_screens); + + if xinerama_screens.is_null() { + return monitors; + } + + for xinerama_screen_index in 0..number_of_screens { + let xinerama_screen = *xinerama_screens.offset(xinerama_screen_index as isize); + + // Just a sanity check + assert!(xinerama_screen.screen_number < num_screens); + + // These should be ordered, so this should be alright. + let mut new_monitor = monitors[xinerama_screen.screen_number as usize].clone(); + // Just a sanity check + assert_eq!(xinerama_screen.screen_number, new_monitor.screen.unwrap()); + + // Our new monitor is never the primary. + new_monitor.primary = false; + + // And of course it's position & size, which will overlap with the screen's. + let dimensions = (xinerama_screen.width as u32, xinerama_screen.height as u32); + new_monitor.dimensions = dimensions; + new_monitor.position = (xinerama_screen.x_org as i32, xinerama_screen.y_org as i32); + new_monitor.rect = AaRect::new(new_monitor.position, new_monitor.dimensions); + new_monitor + .video_modes + .iter_mut() + .for_each(|video_mode| video_mode.size = dimensions); + + monitors.push(new_monitor); + } + + (xlib.XFree)(xinerama_screens as *mut _); + } + + monitors + } + + pub fn query_monitor_list_none(&self) -> Vec { + let xlib = syms!(XLIB); + let num_screens = unsafe { (xlib.XScreenCount)(**self.display) }; + let default_screen = unsafe { (xlib.XDefaultScreen)(**self.display) }; + + (0..num_screens) + .into_iter() + .map(|screen| { + // Every screen starts at the start, relative to itself... + let position = (0, 0); + let (dimensions, _) = self.get_xlib_dims(screen); + let rect = AaRect::new(position, dimensions); + + let depths = get_depths(self, screen); + let video_modes: Vec<_> = depths + .into_iter() + .map(|bit_depth| VideoMode { + size: dimensions, + bit_depth: bit_depth as u16, + // Does it matter? Not like they can change the video mode + // easily, especially if they are using Xinerama which makes + // it literally impossible. + // + // The only alternative to RandR for getting the refresh + // rate is using something like XF86VMODE, which is + // undocumented and glitchy as hell. + refresh_rate: 60, + // This is populated in `MonitorHandle::video_modes` as the + // video mode is returned to the user + monitor: None, + // RandR only. + native_mode: None, + }) + .collect(); + + MonitorHandle { + position, + id: None, + screen: Some(screen), + // We will treat the default screen as the primary. + primary: screen == default_screen, + name: format!("Unknown - Screen {}", screen), + scale_factor: self.acquire_scale_factor(None, screen).unwrap(), + dimensions, + rect, + video_modes, + } + }) + .collect() + } +} diff --git a/src/platform_impl/linux/x11/util/randr.rs b/src/platform_impl/linux/x11/util/randr.rs index b9258c68f1..25cbc73cb6 100644 --- a/src/platform_impl/linux/x11/util/randr.rs +++ b/src/platform_impl/linux/x11/util/randr.rs @@ -1,71 +1,135 @@ -use std::{env, slice, str::FromStr}; +use std::os::raw::*; +use std::slice; use super::{ - ffi::{CurrentTime, RRCrtc, RRMode, Success, XRRCrtcInfo, XRRScreenResources}, + ffi::{ + CurrentTime, RRCrtc, RRCrtcChangeNotifyMask, RRMode, RROutputPropertyNotifyMask, + RRScreenChangeNotifyMask, Success, True, Window, XRRCrtcInfo, XRRScreenResources, + }, *, }; -use crate::{dpi::validate_scale_factor, platform_impl::platform::x11::VideoMode}; +use crate::platform_impl::platform::x11::{ + monitor::{MonitorHandle, MonitorInfoSource}, + VideoMode, +}; + +use winit_types::error::Error; /// Represents values of `WINIT_HIDPI_FACTOR`. -pub enum EnvVarDPI { - Randr, - Scale(f64), - NotSet, -} -pub fn calc_dpi_factor( - (width_px, height_px): (u32, u32), - (width_mm, height_mm): (u64, u64), -) -> f64 { - // See http://xpra.org/trac/ticket/728 for more information. - if width_mm == 0 || height_mm == 0 { - warn!("XRandR reported that the display's 0mm in size, which is certifiably insane"); - return 1.0; - } +impl XConnection { + pub fn query_monitor_list_xrandr(&self) -> Vec { + assert_eq!(self.monitor_info_source, MonitorInfoSource::XRandR); + let (xlib, xrandr) = syms!(XLIB, XRANDR_2_2_0); + unsafe { + let mut major = 0; + let mut minor = 0; + (xrandr.XRRQueryVersion)(**self.display, &mut major, &mut minor); - let ppmm = ((width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)).sqrt(); - // Quantize 1/12 step size - let dpi_factor = ((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0); - assert!(validate_scale_factor(dpi_factor)); - dpi_factor -} + // With RandR, there will only ever be one screen, so using default + // here is OK. + let screen = (xlib.XDefaultScreen)(**self.display); + let root = (xlib.XRootWindow)(**self.display, screen); + let resources = if (major == 1 && minor >= 3) || major > 1 { + (xrandr.XRRGetScreenResourcesCurrent)(**self.display, root) + } else { + // WARNING: this function is supposedly very slow, on the order of hundreds of ms. + // Upon failure, `resources` will be null. + (xrandr.XRRGetScreenResources)(**self.display, root) + }; -impl XConnection { - // Retrieve DPI from Xft.dpi property - pub unsafe fn get_xft_dpi(&self) -> Option { - (self.xlib.XrmInitialize)(); - let resource_manager_str = (self.xlib.XResourceManagerString)(self.display); - if resource_manager_str == ptr::null_mut() { - return None; - } - if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() { - let name: &str = "Xft.dpi:\t"; - for pair in res.split("\n") { - if pair.starts_with(&name) { - let res = &pair[name.len()..]; - return f64::from_str(&res).ok(); + if resources.is_null() { + panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist."); + } + + let mut available; + let mut has_primary = false; + + let primary = (xrandr.XRRGetOutputPrimary)(**self.display, root); + available = Vec::with_capacity((*resources).ncrtc as usize); + for crtc_index in 0..(*resources).ncrtc { + let crtc_id = *((*resources).crtcs.offset(crtc_index as isize)); + let crtc = (xrandr.XRRGetCrtcInfo)(**self.display, resources, crtc_id); + let is_active = (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0; + if is_active { + let primary = *(*crtc).outputs.offset(0) == primary; + has_primary |= primary; + + let (name, scale_factor, video_modes) = + self.get_output_info(resources, crtc).unwrap(); + let dimensions = ((*crtc).width as u32, (*crtc).height as u32); + let position = ((*crtc).x as i32, (*crtc).y as i32); + let rect = AaRect::new(position, dimensions); + available.push(MonitorHandle { + id: Some(crtc_id), + name, + scale_factor, + dimensions, + position, + primary, + rect, + video_modes, + screen: Some(screen), + }); } + (xrandr.XRRFreeCrtcInfo)(crtc); } + + // If no monitors were detected as being primary, we just pick one ourselves! + if !has_primary { + if let Some(ref mut fallback) = available.first_mut() { + // Setting this here will come in handy if we ever add an `is_primary` method. + fallback.primary = true; + } + } + + (xrandr.XRRFreeScreenResources)(resources); + available } - None } + + pub fn select_xrandr_input(&self, root: Window) -> Result { + assert_eq!(self.monitor_info_source, MonitorInfoSource::XRandR); + let xrandr = syms!(XRANDR_2_2_0); + + let mut event_offset = 0; + let mut error_offset = 0; + let status = unsafe { + (xrandr.XRRQueryExtension)(**self.display, &mut event_offset, &mut error_offset) + }; + + if status != True { + self.display.check_errors()?; + unreachable!("[winit] `XRRQueryExtension` failed but no error was received."); + } + + let mask = RRCrtcChangeNotifyMask | RROutputPropertyNotifyMask | RRScreenChangeNotifyMask; + unsafe { (xrandr.XRRSelectInput)(**self.display, root, mask) }; + + Ok(event_offset) + } + pub unsafe fn get_output_info( &self, resources: *mut XRRScreenResources, crtc: *mut XRRCrtcInfo, ) -> Option<(String, f64, Vec)> { + assert_eq!(self.monitor_info_source, MonitorInfoSource::XRandR); + let (xlib, xrandr) = syms!(XLIB, XRANDR_2_2_0); let output_info = - (self.xrandr.XRRGetOutputInfo)(self.display, resources, *(*crtc).outputs.offset(0)); + (xrandr.XRRGetOutputInfo)(**self.display, resources, *(*crtc).outputs.offset(0)); if output_info.is_null() { // When calling `XRRGetOutputInfo` on a virtual monitor (versus a physical display) // it's possible for it to return null. // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816596 - let _ = self.check_errors(); // discard `BadRROutput` error + let _ = self.display.check_errors(); // discard `BadRROutput` error return None; } - let screen = (self.xlib.XDefaultScreen)(self.display); - let bit_depth = (self.xlib.XDefaultDepth)(self.display, screen); + // With RandR, there will only ever be one screen, so using default + // here is OK. + let screen = (xlib.XDefaultScreen)(**self.display); + let bit_depth = (xlib.XDefaultDepth)(**self.display, screen); let output_modes = slice::from_raw_parts((*output_info).modes, (*output_info).nmode as usize); @@ -87,7 +151,7 @@ impl XConnection { size: (x.width, x.height), refresh_rate: (refresh_rate as f32 / 1000.0).round() as u16, bit_depth: bit_depth as u16, - native_mode: x.id, + native_mode: Some(x.id), // This is populated in `MonitorHandle::video_modes` as the // video mode is returned to the user monitor: None, @@ -100,82 +164,34 @@ impl XConnection { (*output_info).nameLen as usize, ); let name = String::from_utf8_lossy(name_slice).into(); - // Override DPI if `WINIT_X11_SCALE_FACTOR` variable is set - let deprecated_dpi_override = env::var("WINIT_HIDPI_FACTOR").ok(); - if deprecated_dpi_override.is_some() { - warn!( - "The WINIT_HIDPI_FACTOR environment variable is deprecated; use WINIT_X11_SCALE_FACTOR" - ) - } - let dpi_env = env::var("WINIT_X11_SCALE_FACTOR").ok().map_or_else( - || EnvVarDPI::NotSet, - |var| { - if var.to_lowercase() == "randr" { - EnvVarDPI::Randr - } else if let Ok(dpi) = f64::from_str(&var) { - EnvVarDPI::Scale(dpi) - } else if var.is_empty() { - EnvVarDPI::NotSet - } else { - panic!( - "`WINIT_X11_SCALE_FACTOR` invalid; DPI factors must be either normal floats greater than 0, or `randr`. Got `{}`", - var - ); - } - }, - ); - - let scale_factor = match dpi_env { - EnvVarDPI::Randr => calc_dpi_factor( - ((*crtc).width as u32, (*crtc).height as u32), - ( - (*output_info).mm_width as u64, - (*output_info).mm_height as u64, - ), - ), - EnvVarDPI::Scale(dpi_override) => { - if !validate_scale_factor(dpi_override) { - panic!( - "`WINIT_X11_SCALE_FACTOR` invalid; DPI factors must be either normal floats greater than 0, or `randr`. Got `{}`", - dpi_override, - ); - } - dpi_override - } - EnvVarDPI::NotSet => { - if let Some(dpi) = self.get_xft_dpi() { - dpi / 96. - } else { - calc_dpi_factor( - ((*crtc).width as u32, (*crtc).height as u32), - ( - (*output_info).mm_width as u64, - (*output_info).mm_height as u64, - ), - ) - } - } - }; + let scale_factor = self + .acquire_scale_factor(Some((output_info, crtc)), screen) + .unwrap(); - (self.xrandr.XRRFreeOutputInfo)(output_info); + (xrandr.XRRFreeOutputInfo)(output_info); Some((name, scale_factor, modes)) } + pub fn set_crtc_config(&self, crtc_id: RRCrtc, mode_id: RRMode) -> Result<(), ()> { + assert_eq!(self.monitor_info_source, MonitorInfoSource::XRandR); + let (xlib, xrandr) = syms!(XLIB, XRANDR_2_2_0); unsafe { let mut major = 0; let mut minor = 0; - (self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor); + (xrandr.XRRQueryVersion)(**self.display, &mut major, &mut minor); - let root = (self.xlib.XDefaultRootWindow)(self.display); + // With RandR, there will only ever be one screen, so using default + // here is OK. + let root = (xlib.XDefaultRootWindow)(**self.display); let resources = if (major == 1 && minor >= 3) || major > 1 { - (self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root) + (xrandr.XRRGetScreenResourcesCurrent)(**self.display, root) } else { - (self.xrandr.XRRGetScreenResources)(self.display, root) + (xrandr.XRRGetScreenResources)(**self.display, root) }; - let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id); - let status = (self.xrandr.XRRSetCrtcConfig)( - self.display, + let crtc = (xrandr.XRRGetCrtcInfo)(**self.display, resources, crtc_id); + let status = (xrandr.XRRSetCrtcConfig)( + **self.display, resources, crtc_id, CurrentTime, @@ -187,8 +203,8 @@ impl XConnection { 1, ); - (self.xrandr.XRRFreeCrtcInfo)(crtc); - (self.xrandr.XRRFreeScreenResources)(resources); + (xrandr.XRRFreeCrtcInfo)(crtc); + (xrandr.XRRFreeScreenResources)(resources); if status == Success as i32 { Ok(()) @@ -197,23 +213,28 @@ impl XConnection { } } } + pub fn get_crtc_mode(&self, crtc_id: RRCrtc) -> RRMode { + assert_eq!(self.monitor_info_source, MonitorInfoSource::XRandR); + let (xlib, xrandr) = syms!(XLIB, XRANDR_2_2_0); unsafe { let mut major = 0; let mut minor = 0; - (self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor); + (xrandr.XRRQueryVersion)(**self.display, &mut major, &mut minor); - let root = (self.xlib.XDefaultRootWindow)(self.display); + // With RandR, there will only ever be one screen, so using default + // here is OK. + let root = (xlib.XDefaultRootWindow)(**self.display); let resources = if (major == 1 && minor >= 3) || major > 1 { - (self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root) + (xrandr.XRRGetScreenResourcesCurrent)(**self.display, root) } else { - (self.xrandr.XRRGetScreenResources)(self.display, root) + (xrandr.XRRGetScreenResources)(**self.display, root) }; - let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id); + let crtc = (xrandr.XRRGetCrtcInfo)(**self.display, resources, crtc_id); let mode = (*crtc).mode; - (self.xrandr.XRRFreeCrtcInfo)(crtc); - (self.xrandr.XRRFreeScreenResources)(resources); + (xrandr.XRRFreeCrtcInfo)(crtc); + (xrandr.XRRFreeScreenResources)(resources); mode } } diff --git a/src/platform_impl/linux/x11/util/window_property.rs b/src/platform_impl/linux/x11/util/window_property.rs index 34977f36d2..4b75d03b63 100644 --- a/src/platform_impl/linux/x11/util/window_property.rs +++ b/src/platform_impl/linux/x11/util/window_property.rs @@ -1,11 +1,13 @@ use super::*; +use winit_types::error::Error; + pub type Cardinal = c_long; pub const CARDINAL_SIZE: usize = mem::size_of::(); #[derive(Debug, Clone)] pub enum GetPropertyError { - XError(XError), + Error(Error), TypeMismatch(ffi::Atom), FormatMismatch(c_int), NothingAllocated, @@ -40,6 +42,7 @@ impl XConnection { property: ffi::Atom, property_type: ffi::Atom, ) -> Result, GetPropertyError> { + let xlib = syms!(XLIB); let mut data = Vec::new(); let mut offset = 0; @@ -52,8 +55,8 @@ impl XConnection { while !done { unsafe { - (self.xlib.XGetWindowProperty)( - self.display, + (xlib.XGetWindowProperty)( + **self.display, window, property, // This offset is in terms of 32-bit chunks. @@ -71,9 +74,9 @@ impl XConnection { &mut buf, ); - if let Err(e) = self.check_errors() { - return Err(GetPropertyError::XError(e)); - } + self.display + .check_errors() + .map_err(GetPropertyError::Error)?; if actual_type != property_type { return Err(GetPropertyError::TypeMismatch(actual_type)); @@ -99,7 +102,7 @@ impl XConnection { );*/ data.extend_from_slice(&new_data); // Fun fact: XGetWindowProperty allocates one extra byte at the end. - (self.xlib.XFree)(buf as _); // Don't try to access new_data after this. + (xlib.XFree)(buf as _); // Don't try to access new_data after this. } else { return Err(GetPropertyError::NothingAllocated); } @@ -119,10 +122,11 @@ impl XConnection { mode: PropMode, new_value: &[T], ) -> Flusher<'a> { + let xlib = syms!(XLIB); debug_assert_eq!(mem::size_of::(), T::FORMAT.get_actual_size()); unsafe { - (self.xlib.XChangeProperty)( - self.display, + (xlib.XChangeProperty)( + **self.display, window, property, property_type, diff --git a/src/platform_impl/linux/x11/util/wm.rs b/src/platform_impl/linux/x11/util/wm.rs index 693bc5c02d..71e1f1ff32 100644 --- a/src/platform_impl/linux/x11/util/wm.rs +++ b/src/platform_impl/linux/x11/util/wm.rs @@ -1,19 +1,31 @@ +use std::os::raw; + use parking_lot::Mutex; use super::*; // This info is global to the window manager. lazy_static! { - static ref SUPPORTED_HINTS: Mutex> = Mutex::new(Vec::with_capacity(0)); - static ref WM_NAME: Mutex> = Mutex::new(None); + static ref SUPPORTED_HINTS: Mutex>> = Mutex::new(Vec::with_capacity(0)); + static ref WM_NAMES: Mutex>> = Mutex::new(Vec::with_capacity(0)); } -pub fn hint_is_supported(hint: ffi::Atom) -> bool { - (*SUPPORTED_HINTS.lock()).contains(&hint) +pub fn hint_is_supported(hint: ffi::Atom, screen: raw::c_int) -> bool { + let supported_hints = SUPPORTED_HINTS.lock(); + if supported_hints.len() <= screen as usize { + return false; + } + + supported_hints[screen as usize].contains(&hint) } -pub fn wm_name_is_one_of(names: &[&str]) -> bool { - if let Some(ref name) = *WM_NAME.lock() { +pub fn wm_name_is_one_of(names: &[&str], screen: raw::c_int) -> bool { + let vm_names = WM_NAMES.lock(); + if vm_names.len() <= screen as usize { + return false; + } + + if let Some(ref name) = vm_names[screen as usize] { names.contains(&name.as_str()) } else { false @@ -21,9 +33,19 @@ pub fn wm_name_is_one_of(names: &[&str]) -> bool { } impl XConnection { - pub fn update_cached_wm_info(&self, root: ffi::Window) { - *SUPPORTED_HINTS.lock() = self.get_supported_hints(root); - *WM_NAME.lock() = self.get_wm_name(root); + pub fn update_cached_wm_info(&self) { + let xlib = syms!(XLIB); + let num_screens = unsafe { (xlib.XScreenCount)(**self.display) }; + + let mut supported_hints = SUPPORTED_HINTS.lock(); + let mut wm_names = WM_NAMES.lock(); + supported_hints.clear(); + wm_names.clear(); + for screen in 0..num_screens { + let root = unsafe { (xlib.XRootWindow)(**self.display, screen) }; + supported_hints.push(self.get_supported_hints(root)); + wm_names.push(self.get_wm_name(root)); + } } fn get_supported_hints(&self, root: ffi::Window) -> Vec { diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index d8bb99e83f..f8b8dc0b9a 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -15,18 +15,24 @@ use libc; use parking_lot::Mutex; use crate::{ - dpi::{PhysicalPosition, PhysicalSize, Position, Size}, - error::{ExternalError, NotSupportedError, OsError as RootOsError}, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, + platform::unix::MonitorHandleExtUnix, platform_impl::{ - x11::{ime::ImeContextCreationError, MonitorHandle as X11MonitorHandle}, - MonitorHandle as PlatformMonitorHandle, OsError, PlatformSpecificWindowBuilderAttributes, + x11::{ + ime::ImeContextCreationError, monitor::MonitorInfoSource, + MonitorHandle as X11MonitorHandle, + }, + MonitorHandle as PlatformMonitorHandle, PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode, }, window::{CursorIcon, Fullscreen, Icon, WindowAttributes}, }; -use super::{ffi, util, EventLoopWindowTarget, ImeSender, WindowId, XConnection, XError}; +use super::{ffi, util, EventLoopWindowTarget, ImeSender, WindowId, XConnection}; + +use winit_types::dpi::{PhysicalPosition, PhysicalSize, Position, Size}; +use winit_types::error::{Error, ErrorType}; +use winit_types::platform::OsError; #[derive(Debug)] pub struct SharedState { @@ -36,7 +42,7 @@ pub struct SharedState { pub inner_position: Option<(i32, i32)>, pub inner_position_rel_parent: Option<(i32, i32)>, pub last_monitor: X11MonitorHandle, - pub dpi_adjusted: Option<(u32, u32)>, + pub scale_factor_adjusted: Option<(u32, u32)>, pub fullscreen: Option, // Set when application calls `set_fullscreen` when window is not visible pub desired_fullscreen: Option>, @@ -77,7 +83,7 @@ impl SharedState { position: None, inner_position: None, inner_position_rel_parent: None, - dpi_adjusted: None, + scale_factor_adjusted: None, fullscreen: None, desired_fullscreen: None, restore_position: None, @@ -97,8 +103,8 @@ unsafe impl Sync for UnownedWindow {} pub struct UnownedWindow { pub xconn: Arc, // never changes xwindow: ffi::Window, // never changes - root: ffi::Window, // never changes - screen_id: i32, // never changes + pub root: ffi::Window, // never changes + pub screen: c_int, // never changes cursor: Mutex, cursor_grabbed: Mutex, cursor_visible: Mutex, @@ -112,9 +118,15 @@ impl UnownedWindow { event_loop: &EventLoopWindowTarget, window_attrs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, - ) -> Result { + ) -> Result { let xconn = &event_loop.xconn; - let root = event_loop.root; + let xlib = syms!(XLIB); + + let screen = match pl_attribs.screen { + Some(id) => id, + None => unsafe { (xlib.XDefaultScreen)(**xconn.display) }, + }; + let root = unsafe { (xlib.XRootWindow)(**xconn.display, screen) }; let mut monitors = xconn.available_monitors(); let guessed_monitor = if monitors.is_empty() { @@ -127,7 +139,9 @@ impl UnownedWindow { let (x, y) = (pointer_state.root_x as i64, pointer_state.root_y as i64); for i in 0..monitors.len() { - if monitors[i].rect.contains_point(x, y) { + if monitors[i].screen == Some(screen) + && monitors[i].rect.contains_point(x, y) + { return Some(monitors.swap_remove(i)); } } @@ -136,23 +150,23 @@ impl UnownedWindow { }) .unwrap_or_else(|| monitors.swap_remove(0)) }; - let dpi_factor = guessed_monitor.scale_factor(); + let scale_factor = guessed_monitor.scale_factor(); - info!("Guessed window scale factor: {}", dpi_factor); + info!("[winit] Guessed window scale factor: {}", scale_factor); let max_inner_size: Option<(u32, u32)> = window_attrs .max_inner_size - .map(|size| size.to_physical::(dpi_factor).into()); + .map(|size| size.to_physical::(scale_factor).into()); let min_inner_size: Option<(u32, u32)> = window_attrs .min_inner_size - .map(|size| size.to_physical::(dpi_factor).into()); + .map(|size| size.to_physical::(scale_factor).into()); let dimensions = { // x11 only applies constraints when the window is actively resized // by the user, so we have to manually apply the initial constraints let mut dimensions: (u32, u32) = window_attrs .inner_size - .map(|size| size.to_physical::(dpi_factor)) + .map(|size| size.to_physical::(scale_factor)) .or_else(|| Some((800, 600).into())) .map(Into::into) .unwrap(); @@ -165,24 +179,19 @@ impl UnownedWindow { dimensions.1 = cmp::max(dimensions.1, min.1); } debug!( - "Calculated physical dimensions: {}x{}", + "[winit] Calculated physical dimensions: {}x{}", dimensions.0, dimensions.1 ); dimensions }; - let screen_id = match pl_attribs.screen_id { - Some(id) => id, - None => unsafe { (xconn.xlib.XDefaultScreen)(xconn.display) }, - }; - // creating let mut set_win_attr = { let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() }; swa.colormap = if let Some(vi) = pl_attribs.visual_infos { unsafe { let visual = vi.visual; - (xconn.xlib.XCreateColormap)(xconn.display, root, visual, ffi::AllocNone) + (xlib.XCreateColormap)(**xconn.display, root, visual, ffi::AllocNone) } } else { 0 @@ -209,8 +218,8 @@ impl UnownedWindow { // finally creating the window let xwindow = unsafe { - (xconn.xlib.XCreateWindow)( - xconn.display, + (xlib.XCreateWindow)( + **xconn.display, root, 0, 0, @@ -243,7 +252,7 @@ impl UnownedWindow { xconn: Arc::clone(xconn), xwindow, root, - screen_id, + screen, cursor: Default::default(), cursor_grabbed: Mutex::new(false), cursor_visible: Mutex::new(true), @@ -279,9 +288,9 @@ impl UnownedWindow { { let (class, instance) = if let Some((instance, class)) = pl_attribs.class { let instance = CString::new(instance.as_str()) - .expect("`WM_CLASS` instance contained null byte"); - let class = - CString::new(class.as_str()).expect("`WM_CLASS` class contained null byte"); + .expect("[winit] `WM_CLASS` instance contained null byte"); + let class = CString::new(class.as_str()) + .expect("[winit] `WM_CLASS` class contained null byte"); (instance, class) } else { let class = env::args() @@ -293,13 +302,13 @@ impl UnownedWindow { .map(|bin_name| bin_name.to_owned()) .or_else(|| Some(window_attrs.title.clone())) .and_then(|string| CString::new(string.as_str()).ok()) - .expect("Default `WM_CLASS` class contained null byte"); + .expect("[winit] Default `WM_CLASS` class contained null byte"); // This environment variable is extraordinarily unlikely to actually be used... let instance = env::var("RESOURCE_NAME") .ok() .and_then(|instance| CString::new(instance.as_str()).ok()) .or_else(|| Some(class.clone())) - .expect("Default `WM_CLASS` instance contained null byte"); + .expect("[winit] Default `WM_CLASS` instance contained null byte"); (instance, class) }; @@ -308,7 +317,7 @@ impl UnownedWindow { (*class_hint).res_class = instance.as_ptr() as *mut c_char; unsafe { - (xconn.xlib.XSetClassHint)(xconn.display, window.xwindow, class_hint.ptr); + (xlib.XSetClassHint)(**xconn.display, window.xwindow, class_hint.ptr); } //.queue(); } @@ -324,14 +333,16 @@ impl UnownedWindow { { let mut min_inner_size = window_attrs .min_inner_size - .map(|size| size.to_physical::(dpi_factor)); + .map(|size| size.to_physical::(scale_factor)); let mut max_inner_size = window_attrs .max_inner_size - .map(|size| size.to_physical::(dpi_factor)); + .map(|size| size.to_physical::(scale_factor)); if !window_attrs.resizable { - if util::wm_name_is_one_of(&["Xfwm4"]) { - warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4"); + if util::wm_name_is_one_of(&["Xfwm4"], window.screen) { + warn!( + "[winit] To avoid a WM bug, disabling resizing has no effect on Xfwm4" + ); } else { max_inner_size = Some(dimensions.into()); min_inner_size = Some(dimensions.into()); @@ -351,12 +362,12 @@ impl UnownedWindow { normal_hints.set_resize_increments( pl_attribs .resize_increments - .map(|size| size.to_physical::(dpi_factor).into()), + .map(|size| size.to_physical::(scale_factor).into()), ); normal_hints.set_base_size( pl_attribs .base_size - .map(|size| size.to_physical::(dpi_factor).into()), + .map(|size| size.to_physical::(scale_factor).into()), ); xconn.set_normal_hints(window.xwindow, normal_hints).queue(); } @@ -368,8 +379,8 @@ impl UnownedWindow { // Opt into handling window close unsafe { - (xconn.xlib.XSetWMProtocols)( - xconn.display, + (xlib.XSetWMProtocols)( + **xconn.display, window.xwindow, &[event_loop.wm_delete_window, event_loop.net_wm_ping] as *const ffi::Atom as *mut ffi::Atom, @@ -380,21 +391,17 @@ impl UnownedWindow { // Set visibility (map window) if window_attrs.visible { unsafe { - (xconn.xlib.XMapRaised)(xconn.display, window.xwindow); + (xlib.XMapRaised)(**xconn.display, window.xwindow); } //.queue(); } // Attempt to make keyboard input repeat detectable unsafe { let mut supported_ptr = ffi::False; - (xconn.xlib.XkbSetDetectableAutoRepeat)( - xconn.display, - ffi::True, - &mut supported_ptr, - ); + (xlib.XkbSetDetectableAutoRepeat)(**xconn.display, ffi::True, &mut supported_ptr); if supported_ptr == ffi::False { - return Err(os_error!(OsError::XMisc( - "`XkbSetDetectableAutoRepeat` failed" + return Err(make_oserror!(OsError::Misc( + "`XkbSetDetectableAutoRepeat` failed".to_string() ))); } } @@ -422,13 +429,12 @@ impl UnownedWindow { { let result = event_loop.ime.borrow_mut().create_context(window.xwindow); if let Err(err) = result { - let e = match err { - ImeContextCreationError::XError(err) => OsError::XError(err), + return Err(match err { + ImeContextCreationError::Error(err) => err, ImeContextCreationError::Null => { - OsError::XMisc("IME Context creation failed") + make_oserror!(OsError::Misc("IME Context creation failed".to_string())) } - }; - return Err(os_error!(e)); + }); } } @@ -438,7 +444,7 @@ impl UnownedWindow { } if window_attrs.fullscreen.is_some() { window - .set_fullscreen_inner(window_attrs.fullscreen.clone()) + .set_fullscreen_inner(window_attrs.fullscreen.clone())? .map(|flusher| flusher.queue()); } if window_attrs.always_on_top { @@ -449,10 +455,7 @@ impl UnownedWindow { } // We never want to give the user a broken window, since by then, it's too late to handle. - xconn - .sync_with_server() - .map(|_| window) - .map_err(|x_err| os_error!(OsError::XError(x_err))) + xconn.sync_with_server().map(|_| window) } fn set_pid(&self) -> Option> { @@ -514,7 +517,8 @@ impl UnownedWindow { fn set_gtk_theme_variant(&self, variant: String) -> util::Flusher<'_> { let hint_atom = unsafe { self.xconn.get_atom_unchecked(b"_GTK_THEME_VARIANT\0") }; let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") }; - let variant = CString::new(variant).expect("`_GTK_THEME_VARIANT` contained null byte"); + let variant = + CString::new(variant).expect("[winit] `_GTK_THEME_VARIANT` contained null byte"); self.xconn.change_property( self.xwindow, hint_atom, @@ -529,7 +533,7 @@ impl UnownedWindow { let mut wm_hints = self .xconn .get_wm_hints(self.xwindow) - .expect("`XGetWMHints` failed"); + .expect("[winit] `XGetWMHints` failed"); if is_urgent { (*wm_hints).flags |= ffi::XUrgencyHint; } else { @@ -538,7 +542,7 @@ impl UnownedWindow { self.xconn .set_wm_hints(self.xwindow, wm_hints) .flush() - .expect("Failed to set urgency hint"); + .expect("[winit] Failed to set urgency hint"); } fn set_netwm( @@ -563,6 +567,7 @@ impl UnownedWindow { } fn set_fullscreen_hint(&self, fullscreen: bool) -> util::Flusher<'_> { + let xlib = syms!(XLIB); let fullscreen_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_FULLSCREEN\0") }; let flusher = self.set_netwm(fullscreen.into(), (fullscreen_atom as c_long, 0, 0, 0)); @@ -571,8 +576,8 @@ impl UnownedWindow { // Ensure that the fullscreen window receives input focus to prevent // locking up the user's display. unsafe { - (self.xconn.xlib.XSetInputFocus)( - self.xconn.display, + (xlib.XSetInputFocus)( + **self.xconn.display, self.xwindow, ffi::RevertToParent, ffi::CurrentTime, @@ -583,22 +588,56 @@ impl UnownedWindow { flusher } - fn set_fullscreen_inner(&self, fullscreen: Option) -> Option> { + fn set_fullscreen_inner( + &self, + fullscreen: Option, + ) -> Result>, Error> { + match &fullscreen { + &Some(Fullscreen::Exclusive(_)) => { + if self.xconn.monitor_info_source != MonitorInfoSource::XRandR { + return Err(make_error!(ErrorType::NotSupported( + "X Servers that don't support the RandR extention cannot have exclusive fullscreen windows".to_string() + ))); + } + } + _ => (), + } + + let on_wrong_screen = match &fullscreen { + &Some(Fullscreen::Exclusive(ref video_mode)) => { + video_mode.monitor().x11_screen() != Some(self.screen) + } + &Some(Fullscreen::Borderless(ref monitor)) => monitor.x11_screen() != Some(self.screen), + _ => false, + }; + + if on_wrong_screen { + return Err(make_error!(ErrorType::BadApiUsage( + "Cannot fullscreen window onto monitor that is on a different X11 screen" + .to_string() + ))); + } + let mut shared_state_lock = self.shared_state.lock(); match shared_state_lock.visibility { // Setting fullscreen on a window that is not visible will generate an error. Visibility::No | Visibility::YesWait => { shared_state_lock.desired_fullscreen = Some(fullscreen); - return None; + return Ok(None); } Visibility::Yes => (), } let old_fullscreen = shared_state_lock.fullscreen.clone(); - if old_fullscreen == fullscreen { - return None; + match &old_fullscreen { + &Some(Fullscreen::Exclusive(_)) => { + assert_eq!(self.xconn.monitor_info_source, MonitorInfoSource::XRandR, "[winit] Somehow we got into exclusive fullscreen mode while not using XRandR, but this should be unreachable."); + } + _ if old_fullscreen == fullscreen => return Ok(None), + _ => (), } + shared_state_lock.fullscreen = fullscreen.clone(); match (&old_fullscreen, &fullscreen) { @@ -619,8 +658,10 @@ impl UnownedWindow { })), ) => { let monitor = video_mode.monitor.as_ref().unwrap(); - shared_state_lock.desktop_video_mode = - Some((monitor.id, self.xconn.get_crtc_mode(monitor.id))); + shared_state_lock.desktop_video_mode = Some(( + monitor.id.unwrap(), + self.xconn.get_crtc_mode(monitor.id.unwrap()), + )); } // Restore desktop video mode upon exiting exclusive fullscreen (&Some(Fullscreen::Exclusive(_)), &None) @@ -628,7 +669,7 @@ impl UnownedWindow { let (monitor_id, mode_id) = shared_state_lock.desktop_video_mode.take().unwrap(); self.xconn .set_crtc_config(monitor_id, mode_id) - .expect("failed to restore desktop video mode"); + .expect("[winit] failed to restore desktop video mode"); } _ => (), } @@ -642,7 +683,7 @@ impl UnownedWindow { if let Some(position) = shared_state_lock.restore_position.take() { self.set_position_inner(position.0, position.1).queue(); } - Some(flusher) + Ok(Some(flusher)) } Some(fullscreen) => { let (video_mode, monitor) = match fullscreen { @@ -657,7 +698,7 @@ impl UnownedWindow { // Don't set fullscreen on an invalid dummy monitor handle if monitor.is_dummy() { - return None; + return Ok(None); } if let Some(video_mode) = video_mode { @@ -686,9 +727,11 @@ impl UnownedWindow { // mode higher than the current desktop video mode (I'm sure // this will make someone unhappy, but it's very unusual for // games to want to do this anyway). - self.xconn - .set_crtc_config(monitor.id, video_mode.native_mode) - .expect("failed to set video mode"); + if let (Some(id), Some(native_mode)) = (monitor.id, video_mode.native_mode) { + self.xconn + .set_crtc_config(id, native_mode) + .expect("[winit] failed to set video mode"); + } } let window_position = self.outer_position_physical(); @@ -696,7 +739,7 @@ impl UnownedWindow { let monitor_origin: (i32, i32) = monitor.position().into(); self.set_position_inner(monitor_origin.0, monitor_origin.1) .queue(); - Some(self.set_fullscreen_hint(true)) + Ok(Some(self.set_fullscreen_hint(true))) } } } @@ -712,22 +755,25 @@ impl UnownedWindow { } #[inline] - pub fn set_fullscreen(&self, fullscreen: Option) { - if let Some(flusher) = self.set_fullscreen_inner(fullscreen) { + pub fn set_fullscreen(&self, fullscreen: Option) -> Result<(), Error> { + if let Some(flusher) = self.set_fullscreen_inner(fullscreen)? { flusher .sync() - .expect("Failed to change window fullscreen state"); + .expect("[winit] Failed to change window fullscreen state"); self.invalidate_cached_frame_extents(); } + + Ok(()) } // Called by EventProcessor when a VisibilityNotify event is received pub(crate) fn visibility_notify(&self) { + let xlib = syms!(XLIB); let mut shared_state = self.shared_state.lock(); match shared_state.visibility { Visibility::No => unsafe { - (self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow); + (xlib.XUnmapWindow)(**self.xconn.display, self.xwindow); }, Visibility::Yes => (), Visibility::YesWait => { @@ -735,7 +781,7 @@ impl UnownedWindow { if let Some(fullscreen) = shared_state.desired_fullscreen.take() { drop(shared_state); - self.set_fullscreen(fullscreen); + self.set_fullscreen(fullscreen).unwrap(); } } } @@ -755,11 +801,12 @@ impl UnownedWindow { } fn set_minimized_inner(&self, minimized: bool) -> util::Flusher<'_> { + let xlib = syms!(XLIB); unsafe { if minimized { - let screen = (self.xconn.xlib.XDefaultScreen)(self.xconn.display); - - (self.xconn.xlib.XIconifyWindow)(self.xconn.display, self.xwindow, screen); + // We must iconify to the same screen the window is on, not the + // default, else the x server will just do nothing. + (xlib.XIconifyWindow)(**self.xconn.display, self.xwindow, self.screen); util::Flusher::new(&self.xconn) } else { @@ -780,7 +827,7 @@ impl UnownedWindow { pub fn set_minimized(&self, minimized: bool) { self.set_minimized_inner(minimized) .flush() - .expect("Failed to change window minimization"); + .expect("[winit] Failed to change window minimization"); } fn set_maximized_inner(&self, maximized: bool) -> util::Flusher<'_> { @@ -802,17 +849,18 @@ impl UnownedWindow { pub fn set_maximized(&self, maximized: bool) { self.set_maximized_inner(maximized) .flush() - .expect("Failed to change window maximization"); + .expect("[winit] Failed to change window maximization"); self.invalidate_cached_frame_extents(); } fn set_title_inner(&self, title: &str) -> util::Flusher<'_> { + let xlib = syms!(XLIB); let wm_name_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_NAME\0") }; let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") }; - let title = CString::new(title).expect("Window title contained null byte"); + let title = CString::new(title).expect("[winit] Window title contained null byte"); unsafe { - (self.xconn.xlib.XStoreName)( - self.xconn.display, + (xlib.XStoreName)( + **self.xconn.display, self.xwindow, title.as_ptr() as *const c_char, ); @@ -830,7 +878,7 @@ impl UnownedWindow { pub fn set_title(&self, title: &str) { self.set_title_inner(title) .flush() - .expect("Failed to set window title"); + .expect("[winit] Failed to set window title"); } fn set_decorations_inner(&self, decorations: bool) -> util::Flusher<'_> { @@ -845,7 +893,7 @@ impl UnownedWindow { pub fn set_decorations(&self, decorations: bool) { self.set_decorations_inner(decorations) .flush() - .expect("Failed to set decoration state"); + .expect("[winit] Failed to set decoration state"); self.invalidate_cached_frame_extents(); } @@ -866,7 +914,7 @@ impl UnownedWindow { pub fn set_always_on_top(&self, always_on_top: bool) { self.set_always_on_top_inner(always_on_top) .flush() - .expect("Failed to set always-on-top state"); + .expect("[winit] Failed to set always-on-top state"); } fn set_icon_inner(&self, icon: Icon) -> util::Flusher<'_> { @@ -900,11 +948,12 @@ impl UnownedWindow { None => self.unset_icon_inner(), } .flush() - .expect("Failed to set icons"); + .expect("[winit] Failed to set icons"); } #[inline] pub fn set_visible(&self, visible: bool) { + let xlib = syms!(XLIB); let mut shared_state = self.shared_state.lock(); match (visible, shared_state.visibility) { @@ -916,19 +965,19 @@ impl UnownedWindow { if visible { unsafe { - (self.xconn.xlib.XMapRaised)(self.xconn.display, self.xwindow); + (xlib.XMapRaised)(**self.xconn.display, self.xwindow); } self.xconn .flush_requests() - .expect("Failed to call XMapRaised"); + .expect("[winit] Failed to call XMapRaised"); shared_state.visibility = Visibility::YesWait; } else { unsafe { - (self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow); + (xlib.XUnmapWindow)(**self.xconn.display, self.xwindow); } self.xconn .flush_requests() - .expect("Failed to call XUnmapWindow"); + .expect("[winit] Failed to call XUnmapWindow"); shared_state.visibility = Visibility::No; } } @@ -936,7 +985,7 @@ impl UnownedWindow { fn update_cached_frame_extents(&self) { let extents = self .xconn - .get_frame_extents_heuristic(self.xwindow, self.root); + .get_frame_extents_heuristic(self.xwindow, self.screen); (*self.shared_state.lock()).frame_extents = Some(extents); } @@ -956,7 +1005,7 @@ impl UnownedWindow { } #[inline] - pub fn outer_position(&self) -> Result, NotSupportedError> { + pub fn outer_position(&self) -> Result, Error> { let extents = (*self.shared_state.lock()).frame_extents.clone(); if let Some(extents) = extents { let (x, y) = self.inner_position_physical(); @@ -977,14 +1026,15 @@ impl UnownedWindow { } #[inline] - pub fn inner_position(&self) -> Result, NotSupportedError> { + pub fn inner_position(&self) -> Result, Error> { Ok(self.inner_position_physical().into()) } pub(crate) fn set_position_inner(&self, mut x: i32, mut y: i32) -> util::Flusher<'_> { + let xlib = syms!(XLIB); // There are a few WMs that set client area position rather than window position, so // we'll translate for consistency. - if util::wm_name_is_one_of(&["Enlightenment", "FVWM"]) { + if util::wm_name_is_one_of(&["Enlightenment", "FVWM"], self.screen) { let extents = (*self.shared_state.lock()).frame_extents.clone(); if let Some(extents) = extents { x += extents.frame_extents.left as i32; @@ -995,7 +1045,7 @@ impl UnownedWindow { } } unsafe { - (self.xconn.xlib.XMoveWindow)(self.xconn.display, self.xwindow, x as c_int, y as c_int); + (xlib.XMoveWindow)(**self.xconn.display, self.xwindow, x as c_int, y as c_int); } util::Flusher::new(&self.xconn) } @@ -1003,7 +1053,7 @@ impl UnownedWindow { pub(crate) fn set_position_physical(&self, x: i32, y: i32) { self.set_position_inner(x, y) .flush() - .expect("Failed to call `XMoveWindow`"); + .expect("[winit] Failed to call `XMoveWindow`"); } #[inline] @@ -1039,28 +1089,29 @@ impl UnownedWindow { } pub(crate) fn set_inner_size_physical(&self, width: u32, height: u32) { + let xlib = syms!(XLIB); unsafe { - (self.xconn.xlib.XResizeWindow)( - self.xconn.display, + (xlib.XResizeWindow)( + **self.xconn.display, self.xwindow, width as c_uint, height as c_uint, ); self.xconn.flush_requests() } - .expect("Failed to call `XResizeWindow`"); + .expect("[winit] Failed to call `XResizeWindow`"); } #[inline] pub fn set_inner_size(&self, size: Size) { - let dpi_factor = self.scale_factor(); - let (width, height) = size.to_physical::(dpi_factor).into(); + let scale_factor = self.scale_factor(); + let (width, height) = size.to_physical::(scale_factor).into(); self.set_inner_size_physical(width, height); } - fn update_normal_hints(&self, callback: F) -> Result<(), XError> + fn update_normal_hints(&self, callback: F) -> Result<(), Error> where - F: FnOnce(&mut util::NormalHints<'_>) -> (), + F: FnOnce(&mut util::NormalHints) -> (), { let mut normal_hints = self.xconn.get_normal_hints(self.xwindow)?; callback(&mut normal_hints); @@ -1071,7 +1122,7 @@ impl UnownedWindow { pub(crate) fn set_min_inner_size_physical(&self, dimensions: Option<(u32, u32)>) { self.update_normal_hints(|normal_hints| normal_hints.set_min_size(dimensions)) - .expect("Failed to call `XSetWMNormalHints`"); + .expect("[winit] Failed to call `XSetWMNormalHints`"); } #[inline] @@ -1084,7 +1135,7 @@ impl UnownedWindow { pub(crate) fn set_max_inner_size_physical(&self, dimensions: Option<(u32, u32)>) { self.update_normal_hints(|normal_hints| normal_hints.set_max_size(dimensions)) - .expect("Failed to call `XSetWMNormalHints`"); + .expect("[winit] Failed to call `XSetWMNormalHints`"); } #[inline] @@ -1095,28 +1146,28 @@ impl UnownedWindow { self.set_max_inner_size_physical(physical_dimensions); } - pub(crate) fn adjust_for_dpi( + pub(crate) fn adjust_for_scale_factor( &self, - old_dpi_factor: f64, - new_dpi_factor: f64, + old_scale_factor: f64, + new_scale_factor: f64, width: u32, height: u32, shared_state: &SharedState, ) -> (u32, u32) { - let scale_factor = new_dpi_factor / old_dpi_factor; + let scale_factor = new_scale_factor / old_scale_factor; self.update_normal_hints(|normal_hints| { - let dpi_adjuster = - |size: Size| -> (u32, u32) { size.to_physical::(new_dpi_factor).into() }; - let max_size = shared_state.max_inner_size.map(&dpi_adjuster); - let min_size = shared_state.min_inner_size.map(&dpi_adjuster); - let resize_increments = shared_state.resize_increments.map(&dpi_adjuster); - let base_size = shared_state.base_size.map(&dpi_adjuster); + let scale_factor_adjuster = + |size: Size| -> (u32, u32) { size.to_physical::(new_scale_factor).into() }; + let max_size = shared_state.max_inner_size.map(&scale_factor_adjuster); + let min_size = shared_state.min_inner_size.map(&scale_factor_adjuster); + let resize_increments = shared_state.resize_increments.map(&scale_factor_adjuster); + let base_size = shared_state.base_size.map(&scale_factor_adjuster); normal_hints.set_max_size(max_size); normal_hints.set_min_size(min_size); normal_hints.set_resize_increments(resize_increments); normal_hints.set_base_size(base_size); }) - .expect("Failed to update normal hints"); + .expect("[winit] Failed to update normal hints"); let new_width = (width as f64 * scale_factor).round() as u32; let new_height = (height as f64 * scale_factor).round() as u32; @@ -1125,11 +1176,11 @@ impl UnownedWindow { } pub fn set_resizable(&self, resizable: bool) { - if util::wm_name_is_one_of(&["Xfwm4"]) { + if util::wm_name_is_one_of(&["Xfwm4"], self.screen) { // Making the window unresizable on Xfwm prevents further changes to `WM_NORMAL_HINTS` from being detected. // This makes it impossible for resizing to be re-enabled, and also breaks DPI scaling. As such, we choose // the lesser of two evils and do nothing. - warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4"); + warn!("[winit] To avoid a WM bug, disabling resizing has no effect on Xfwm4"); return; } @@ -1146,33 +1197,28 @@ impl UnownedWindow { self.set_maximizable_inner(resizable).queue(); - let dpi_factor = self.scale_factor(); + let scale_factor = self.scale_factor(); let min_inner_size = min_size - .map(|size| size.to_physical::(dpi_factor)) + .map(|size| size.to_physical::(scale_factor)) .map(Into::into); let max_inner_size = max_size - .map(|size| size.to_physical::(dpi_factor)) + .map(|size| size.to_physical::(scale_factor)) .map(Into::into); self.update_normal_hints(|normal_hints| { normal_hints.set_min_size(min_inner_size); normal_hints.set_max_size(max_inner_size); }) - .expect("Failed to call `XSetWMNormalHints`"); + .expect("[winit] Failed to call `XSetWMNormalHints`"); } #[inline] pub fn xlib_display(&self) -> *mut c_void { - self.xconn.display as _ + **self.xconn.display as _ } #[inline] - pub fn xlib_screen_id(&self) -> c_int { - self.screen_id - } - - #[inline] - pub fn xlib_xconnection(&self) -> Arc { - Arc::clone(&self.xconn) + pub fn xlib_screen(&self) -> c_int { + self.screen } #[inline] @@ -1182,19 +1228,22 @@ impl UnownedWindow { #[inline] pub fn xcb_connection(&self) -> *mut c_void { - unsafe { (self.xconn.xlib_xcb.XGetXCBConnection)(self.xconn.display) as *mut _ } + let xlib_xcb = syms!(XLIB_XCB); + unsafe { (xlib_xcb.XGetXCBConnection)(**self.xconn.display) as *mut _ } } #[inline] pub fn set_cursor_icon(&self, cursor: CursorIcon) { let old_cursor = replace(&mut *self.cursor.lock(), cursor); if cursor != old_cursor && *self.cursor_visible.lock() { - self.xconn.set_cursor_icon(self.xwindow, Some(cursor)); + self.xconn + .set_cursor_icon(self.xwindow, self.root, Some(cursor)); } } #[inline] - pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { + pub fn set_cursor_grab(&self, grab: bool) -> Result<(), Error> { + let xlib = syms!(XLIB); let mut grabbed_lock = self.cursor_grabbed.lock(); if grab == *grabbed_lock { return Ok(()); @@ -1202,12 +1251,12 @@ impl UnownedWindow { unsafe { // We ungrab before grabbing to prevent passive grabs from causing `AlreadyGrabbed`. // Therefore, this is common to both codepaths. - (self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime); + (xlib.XUngrabPointer)(**self.xconn.display, ffi::CurrentTime); } let result = if grab { let result = unsafe { - (self.xconn.xlib.XGrabPointer)( - self.xconn.display, + (xlib.XGrabPointer)( + **self.xconn.display, self.xwindow, ffi::True, (ffi::ButtonPressMask @@ -1243,11 +1292,9 @@ impl UnownedWindow { ffi::GrabFrozen => Err("Cursor could not be grabbed: frozen by another client"), _ => unreachable!(), } - .map_err(|err| ExternalError::Os(os_error!(OsError::XMisc(err)))) + .map_err(|err| make_oserror!(OsError::Misc(err.to_string()))) } else { - self.xconn - .flush_requests() - .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err)))) + self.xconn.flush_requests() }; if result.is_ok() { *grabbed_lock = grab; @@ -1268,7 +1315,7 @@ impl UnownedWindow { }; *visible_lock = visible; drop(visible_lock); - self.xconn.set_cursor_icon(self.xwindow, cursor); + self.xconn.set_cursor_icon(self.xwindow, self.root, cursor); } #[inline] @@ -1276,17 +1323,16 @@ impl UnownedWindow { self.current_monitor().scale_factor } - pub fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ExternalError> { + pub fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), Error> { + let xlib = syms!(XLIB); unsafe { - (self.xconn.xlib.XWarpPointer)(self.xconn.display, 0, self.xwindow, 0, 0, 0, 0, x, y); - self.xconn - .flush_requests() - .map_err(|e| ExternalError::Os(os_error!(OsError::XError(e)))) + (xlib.XWarpPointer)(**self.xconn.display, 0, self.xwindow, 0, 0, 0, 0, x, y); + self.xconn.flush_requests() } } #[inline] - pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> { + pub fn set_cursor_position(&self, position: Position) -> Result<(), Error> { let (x, y) = position.to_physical::(self.scale_factor()).into(); self.set_cursor_position_physical(x, y) } @@ -1321,7 +1367,7 @@ impl UnownedWindow { pub fn raw_window_handle(&self) -> XlibHandle { XlibHandle { window: self.xwindow, - display: self.xconn.display as _, + display: **self.xconn.display as _, ..XlibHandle::empty() } } diff --git a/src/platform_impl/linux/x11/xdisplay.rs b/src/platform_impl/linux/x11/xdisplay.rs index 25065f045d..f34b6220da 100644 --- a/src/platform_impl/linux/x11/xdisplay.rs +++ b/src/platform_impl/linux/x11/xdisplay.rs @@ -1,92 +1,101 @@ -use std::{collections::HashMap, error::Error, fmt, os::raw::c_int, ptr}; +use std::{collections::HashMap, fmt, os::raw::c_int, sync::Arc}; -use libc; +use glutin_x11_sym::{Display, X11_DISPLAY}; use parking_lot::Mutex; +use winit_types::error::Error; use crate::window::CursorIcon; use super::ffi; +use super::monitor::MonitorInfoSource; /// A connection to an X server. pub struct XConnection { - pub xlib: ffi::Xlib, - /// Exposes XRandR functions from version < 1.5 - pub xrandr: ffi::Xrandr_2_2_0, - /// Exposes XRandR functions from version = 1.5 - pub xrandr_1_5: Option, - pub xcursor: ffi::Xcursor, - pub xinput2: ffi::XInput2, - pub xlib_xcb: ffi::Xlib_xcb, - pub xrender: ffi::Xrender, - pub display: *mut ffi::Display, + pub display: Arc, pub x11_fd: c_int, - pub latest_error: Mutex>, pub cursor_cache: Mutex, ffi::Cursor>>, + pub monitor_info_source: MonitorInfoSource, } -unsafe impl Send for XConnection {} -unsafe impl Sync for XConnection {} - -pub type XErrorHandler = - Option libc::c_int>; - impl XConnection { - pub fn new(error_handler: XErrorHandler) -> Result { + pub fn new() -> Result { // opening the libraries - let xlib = ffi::Xlib::open()?; - let xcursor = ffi::Xcursor::open()?; - let xrandr = ffi::Xrandr_2_2_0::open()?; - let xrandr_1_5 = ffi::Xrandr::open().ok(); - let xinput2 = ffi::XInput2::open()?; - let xlib_xcb = ffi::Xlib_xcb::open()?; - let xrender = ffi::Xrender::open()?; - - unsafe { (xlib.XInitThreads)() }; - unsafe { (xlib.XSetErrorHandler)(error_handler) }; + (*ffi::XLIB) + .as_ref() + .map_err(|err| make_oserror!(err.clone().into()))?; + (*ffi::XCURSOR) + .as_ref() + .map_err(|err| make_oserror!(err.clone().into()))?; + (*ffi::XINPUT2) + .as_ref() + .map_err(|err| make_oserror!(err.clone().into()))?; + (*ffi::XLIB_XCB) + .as_ref() + .map_err(|err| make_oserror!(err.clone().into()))?; + + let display = X11_DISPLAY + .lock() + .as_ref() + .map(Arc::clone) + .map_err(|err| err.clone())?; - // calling XOpenDisplay - let display = unsafe { - let display = (xlib.XOpenDisplay)(ptr::null()); - if display.is_null() { - return Err(XNotSupported::XOpenDisplayFailed); + // Get X11 socket file descriptor + let x11_fd = unsafe { (syms!(XLIB).XConnectionNumber)(**display) }; + + let mut monitor_info_source = MonitorInfoSource::Xlib; + + match (*ffi::XRANDR_2_2_0).as_ref() { + Ok(_) => { + let xrandr = syms!(XRANDR_2_2_0); + let has_xrandr = unsafe { + let mut major = 0; + let mut minor = 0; + (xrandr.XRRQueryVersion)(**display, &mut major, &mut minor) + }; + + match has_xrandr { + 0 => debug!("[winit] Queried for RANDR version but failed with {:?}, falling back to Xinerama.", has_xrandr), + _ => monitor_info_source = MonitorInfoSource::XRandR, + } + } + Err(err) => { + debug!("[winit] Tried to load RANDR ext symbols but failed with {:?}, falling back to Xinerama.", err); } - display - }; + } - // Get X11 socket file descriptor - let fd = unsafe { (xlib.XConnectionNumber)(display) }; + if monitor_info_source == MonitorInfoSource::Xlib { + match (*ffi::XINERAMA).as_ref() { + Ok(_) => { + let xinerama = syms!(XINERAMA); + let has_xinerama = unsafe { + let mut major = 0; + let mut minor = 0; + (xinerama.XineramaQueryVersion)(**display, &mut major, &mut minor) + }; + + match has_xinerama { + 0 => debug!("[winit] Queried for Xinerama version but failed with {:?}, falling back to nothing.", has_xinerama), + _ => unsafe { + match (xinerama.XineramaIsActive)(**display) { + ffi::True => monitor_info_source = MonitorInfoSource::Xinerama, + is_active => debug!("[winit] Queried Xinerama if it was active and it said {:?}, falling back to nothing.", is_active), + } + } + } + } + Err(err) => { + debug!("[winit] Tried to load Xinerama ext symbols but failed with {:?}, falling back to nothing.", err); + } + } + } Ok(XConnection { - xlib, - xrandr, - xrandr_1_5, - xcursor, - xinput2, - xlib_xcb, - xrender, display, - x11_fd: fd, - latest_error: Mutex::new(None), + x11_fd, cursor_cache: Default::default(), + monitor_info_source, }) } - - /// Checks whether an error has been triggered by the previous function calls. - #[inline] - pub fn check_errors(&self) -> Result<(), XError> { - let error = self.latest_error.lock().take(); - if let Some(error) = error { - Err(error) - } else { - Ok(()) - } - } - - /// Ignores any previous error. - #[inline] - pub fn ignore_error(&self) { - *self.latest_error.lock() = None; - } } impl fmt::Debug for XConnection { @@ -94,72 +103,3 @@ impl fmt::Debug for XConnection { self.display.fmt(f) } } - -impl Drop for XConnection { - #[inline] - fn drop(&mut self) { - unsafe { (self.xlib.XCloseDisplay)(self.display) }; - } -} - -/// Error triggered by xlib. -#[derive(Debug, Clone)] -pub struct XError { - pub description: String, - pub error_code: u8, - pub request_code: u8, - pub minor_code: u8, -} - -impl Error for XError {} - -impl fmt::Display for XError { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!( - formatter, - "X error: {} (code: {}, request code: {}, minor code: {})", - self.description, self.error_code, self.request_code, self.minor_code - ) - } -} - -/// Error returned if this system doesn't have XLib or can't create an X connection. -#[derive(Clone, Debug)] -pub enum XNotSupported { - /// Failed to load one or several shared libraries. - LibraryOpenError(ffi::OpenError), - /// Connecting to the X server with `XOpenDisplay` failed. - XOpenDisplayFailed, // TODO: add better message -} - -impl From for XNotSupported { - #[inline] - fn from(err: ffi::OpenError) -> XNotSupported { - XNotSupported::LibraryOpenError(err) - } -} - -impl XNotSupported { - fn description(&self) -> &'static str { - match self { - XNotSupported::LibraryOpenError(_) => "Failed to load one of xlib's shared libraries", - XNotSupported::XOpenDisplayFailed => "Failed to open connection to X server", - } - } -} - -impl Error for XNotSupported { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match *self { - XNotSupported::LibraryOpenError(ref err) => Some(err), - _ => None, - } - } -} - -impl fmt::Display for XNotSupported { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - formatter.write_str(self.description()) - } -} diff --git a/src/platform_impl/macos/activation_hack.rs b/src/platform_impl/macos/activation_hack.rs index 6cf1960cfc..b414f41d9c 100644 --- a/src/platform_impl/macos/activation_hack.rs +++ b/src/platform_impl/macos/activation_hack.rs @@ -92,7 +92,10 @@ impl State { pub unsafe fn get_ptr(obj: &Object) -> *mut Self { let this: *mut c_void = *(*obj).get_ivar(Self::name()); - assert!(!this.is_null(), "`activationHackState` pointer was null"); + assert!( + !this.is_null(), + "[winit] `activationHackState` pointer was null" + ); this as *mut Self } @@ -132,20 +135,20 @@ impl State { // mouse movements prior to activation are the cause of this quirk, they should // be a reliable way to determine if the hack needs to be performed. pub extern "C" fn mouse_moved(this: &Object, _: Sel, _: id) { - trace!("Triggered `activationHackMouseMoved`"); + trace!("[winit] Triggered `activationHackMouseMoved`"); unsafe { if !State::get_activated(this) { // We check if `CFBundleName` is undefined to determine if the // app is unbundled. if let None = util::app_name() { - info!("App detected as unbundled"); + info!("[winit] App detected as unbundled"); unfocus(this); } else { - info!("App detected as bundled"); + info!("[winit] App detected as bundled"); } } } - trace!("Completed `activationHackMouseMoved`"); + trace!("[winit] Completed `activationHackMouseMoved`"); } // Switch focus to the dock. @@ -163,9 +166,9 @@ unsafe fn unfocus(this: &Object) { // would almost surely be a cleaner approach. let active: BOOL = msg_send![NSApp(), isActive]; if active == YES { - error!("Unbundled app activation hack triggered on an app that's already active; this shouldn't happen!"); + error!("[winit] Unbundled app activation hack triggered on an app that's already active; this shouldn't happen!"); } else { - info!("Performing unbundled app activation hack"); + info!("[winit] Performing unbundled app activation hack"); let dock_bundle_id = util::ns_string_id_ref("com.apple.dock"); let dock_array: id = msg_send![ class!(NSRunningApplication), @@ -173,7 +176,7 @@ unsafe fn unfocus(this: &Object) { ]; let dock_array_len: NSUInteger = msg_send![dock_array, count]; if dock_array_len == 0 { - error!("The Dock doesn't seem to be running, so switching focus to it is impossible"); + error!("[winit] The Dock doesn't seem to be running, so switching focus to it is impossible"); } else { State::set_needs_refocus(this, true); let dock: id = msg_send![dock_array, objectAtIndex: 0]; @@ -184,7 +187,7 @@ unsafe fn unfocus(this: &Object) { activateWithOptions: NSApplicationActivateIgnoringOtherApps ]; if status == NO { - error!("Failed to switch focus to Dock"); + error!("[winit] Failed to switch focus to Dock"); } } } @@ -202,7 +205,7 @@ pub unsafe fn refocus(this: &Object) { activateWithOptions: NSApplicationActivateIgnoringOtherApps ]; if success == NO { - error!("Failed to refocus app"); + error!("[winit] Failed to refocus app"); } } } diff --git a/src/platform_impl/macos/app_delegate.rs b/src/platform_impl/macos/app_delegate.rs index 9263cc121f..b08977456a 100644 --- a/src/platform_impl/macos/app_delegate.rs +++ b/src/platform_impl/macos/app_delegate.rs @@ -59,23 +59,23 @@ extern "C" fn dealloc(this: &Object, _: Sel) { } extern "C" fn did_finish_launching(_: &Object, _: Sel, _: id) { - trace!("Triggered `applicationDidFinishLaunching`"); + trace!("[winit] Triggered `applicationDidFinishLaunching`"); AppState::launched(); - trace!("Completed `applicationDidFinishLaunching`"); + trace!("[winit] Completed `applicationDidFinishLaunching`"); } extern "C" fn did_become_active(this: &Object, _: Sel, _: id) { - trace!("Triggered `applicationDidBecomeActive`"); + trace!("[winit] Triggered `applicationDidBecomeActive`"); unsafe { activation_hack::State::set_activated(this, true); } - trace!("Completed `applicationDidBecomeActive`"); + trace!("[winit] Completed `applicationDidBecomeActive`"); } extern "C" fn did_resign_active(this: &Object, _: Sel, _: id) { - trace!("Triggered `applicationDidResignActive`"); + trace!("[winit] Triggered `applicationDidResignActive`"); unsafe { activation_hack::refocus(this); } - trace!("Completed `applicationDidResignActive`"); + trace!("[winit] Completed `applicationDidResignActive`"); } diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 135a3d2180..4531b86c46 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -16,11 +16,11 @@ use cocoa::{ base::{id, nil}, foundation::{NSAutoreleasePool, NSPoint, NSSize}, }; +use winit_types::dpi::LogicalSize; use objc::runtime::YES; use crate::{ - dpi::LogicalSize, event::{Event, StartCause, WindowEvent}, event_loop::{ControlFlow, EventLoopWindowTarget as RootWindowTarget}, platform_impl::platform::{ @@ -303,14 +303,17 @@ impl AppState { pub fn queue_event(wrapper: EventWrapper) { if !unsafe { msg_send![class!(NSThread), isMainThread] } { - panic!("Event queued from different thread: {:#?}", wrapper); + panic!("[winit] Event queued from different thread: {:#?}", wrapper); } HANDLER.events().push_back(wrapper); } pub fn queue_events(mut wrappers: VecDeque) { if !unsafe { msg_send![class!(NSThread), isMainThread] } { - panic!("Events queued from different thread: {:#?}", wrappers); + panic!( + "[winit] Events queued from different thread: {:#?}", + wrappers + ); } HANDLER.events().append(&mut wrappers); } diff --git a/src/platform_impl/macos/event.rs b/src/platform_impl/macos/event.rs index 6e231940f2..205d03fcfe 100644 --- a/src/platform_impl/macos/event.rs +++ b/src/platform_impl/macos/event.rs @@ -4,9 +4,9 @@ use cocoa::{ appkit::{NSEvent, NSEventModifierFlags}, base::id, }; +use winit_types::dpi::LogicalSize; use crate::{ - dpi::LogicalSize, event::{ElementState, Event, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent}, platform_impl::platform::{ util::{IdRef, Never}, diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index cc3d392840..b36ec0dc0b 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -43,7 +43,7 @@ impl EventLoop { pub fn new() -> Self { let delegate = unsafe { if !msg_send![class!(NSThread), isMainThread] { - panic!("On macOS, `EventLoop` must be created on the main thread!"); + panic!("[winit] On macOS, `EventLoop` must be created on the main thread!"); } // This must be done before `NSApp()` (equivalent to sending diff --git a/src/platform_impl/macos/mod.rs b/src/platform_impl/macos/mod.rs index 8b5ee6a59a..9af0e197a6 100644 --- a/src/platform_impl/macos/mod.rs +++ b/src/platform_impl/macos/mod.rs @@ -14,16 +14,16 @@ mod view; mod window; mod window_delegate; -use std::{fmt, ops::Deref, sync::Arc}; +use std::{ops::Deref, sync::Arc}; pub use self::{ event_loop::{EventLoop, EventLoopWindowTarget, Proxy as EventLoopProxy}, monitor::{MonitorHandle, VideoMode}, window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, UnownedWindow}, }; -use crate::{ - error::OsError as RootOsError, event::DeviceId as RootDeviceId, window::WindowAttributes, -}; +use crate::{event::DeviceId as RootDeviceId, window::WindowAttributes}; + +use winit_types::error::Error; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DeviceId; @@ -43,12 +43,6 @@ pub struct Window { _delegate: util::IdRef, } -#[derive(Debug)] -pub enum OsError { - CGError(core_graphics::base::CGError), - CreationError(&'static str), -} - unsafe impl Send for Window {} unsafe impl Sync for Window {} @@ -65,17 +59,8 @@ impl Window { _window_target: &EventLoopWindowTarget, attributes: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, - ) -> Result { + ) -> Result { let (window, _delegate) = UnownedWindow::new(attributes, pl_attribs)?; Ok(Window { window, _delegate }) } } - -impl fmt::Display for OsError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - OsError::CGError(e) => f.pad(&format!("CGError {}", e)), - OsError::CreationError(e) => f.pad(e), - } - } -} diff --git a/src/platform_impl/macos/monitor.rs b/src/platform_impl/macos/monitor.rs index 817d38ee57..47de2b730f 100644 --- a/src/platform_impl/macos/monitor.rs +++ b/src/platform_impl/macos/monitor.rs @@ -1,10 +1,8 @@ use std::{collections::VecDeque, fmt}; use super::{ffi, util}; -use crate::{ - dpi::{PhysicalPosition, PhysicalSize}, - monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, -}; +use crate::monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}; + use cocoa::{ appkit::NSScreen, base::{id, nil}, @@ -20,6 +18,7 @@ use core_video_sys::{ kCVReturnSuccess, kCVTimeIsIndefinite, CVDisplayLinkCreateWithCGDisplay, CVDisplayLinkGetNominalOutputVideoRefreshPeriod, CVDisplayLinkRelease, }; +use winit_types::dpi::{PhysicalPosition, PhysicalSize}; #[derive(Clone)] pub struct VideoMode { @@ -244,7 +243,10 @@ impl MonitorHandle { unsafe { let modes = { let array = ffi::CGDisplayCopyAllDisplayModes(self.0, std::ptr::null()); - assert!(!array.is_null(), "failed to get list of display modes"); + assert!( + !array.is_null(), + "[winit] failed to get list of display modes" + ); let array_count = CFArrayGetCount(array); let modes: Vec<_> = (0..array_count) .map(move |i| { diff --git a/src/platform_impl/macos/observer.rs b/src/platform_impl/macos/observer.rs index aa7f5362c8..ab0f33aa9b 100644 --- a/src/platform_impl/macos/observer.rs +++ b/src/platform_impl/macos/observer.rs @@ -112,9 +112,9 @@ extern "C" fn control_flow_begin_handler( #[allow(non_upper_case_globals)] match activity { kCFRunLoopAfterWaiting => { - //trace!("Triggered `CFRunLoopAfterWaiting`"); + //trace!("[winit] Triggered `CFRunLoopAfterWaiting`"); AppState::wakeup(); - //trace!("Completed `CFRunLoopAfterWaiting`"); + //trace!("[winit] Completed `CFRunLoopAfterWaiting`"); } kCFRunLoopEntry => unimplemented!(), // not expected to ever happen _ => unreachable!(), @@ -131,9 +131,9 @@ extern "C" fn control_flow_end_handler( #[allow(non_upper_case_globals)] match activity { kCFRunLoopBeforeWaiting => { - //trace!("Triggered `CFRunLoopBeforeWaiting`"); + //trace!("[winit] Triggered `CFRunLoopBeforeWaiting`"); AppState::cleared(); - //trace!("Completed `CFRunLoopBeforeWaiting`"); + //trace!("[winit] Completed `CFRunLoopBeforeWaiting`"); } kCFRunLoopExit => (), //unimplemented!(), // not expected to ever happen _ => unreachable!(), diff --git a/src/platform_impl/macos/util/async.rs b/src/platform_impl/macos/util/async.rs index 977ba32807..e52135eac6 100644 --- a/src/platform_impl/macos/util/async.rs +++ b/src/platform_impl/macos/util/async.rs @@ -10,11 +10,9 @@ use cocoa::{ }; use dispatch::Queue; use objc::rc::autoreleasepool; +use winit_types::dpi::LogicalSize; -use crate::{ - dpi::LogicalSize, - platform_impl::platform::{ffi, util::IdRef, window::SharedState}, -}; +use crate::platform_impl::platform::{ffi, util::IdRef, window::SharedState}; // Unsafe wrapper type that allows us to dispatch things that aren't Send. // This should *only* be used to dispatch to the main queue. @@ -109,10 +107,10 @@ pub unsafe fn toggle_full_screen_async( if !curr_mask.contains(required) { set_style_mask(*ns_window, *ns_view, required); if let Some(shared_state) = shared_state.upgrade() { - trace!("Locked shared state in `toggle_full_screen_callback`"); + trace!("[winit] Locked shared state in `toggle_full_screen_callback`"); let mut shared_state_lock = shared_state.lock().unwrap(); (*shared_state_lock).saved_style = Some(curr_mask); - trace!("Unlocked shared state in `toggle_full_screen_callback`"); + trace!("[winit] Unlocked shared state in `toggle_full_screen_callback`"); } } } @@ -142,7 +140,7 @@ pub unsafe fn set_maximized_async( let shared_state = MainThreadSafe(shared_state); Queue::main().exec_async(move || { if let Some(shared_state) = shared_state.upgrade() { - trace!("Locked shared state in `set_maximized`"); + trace!("[winit] Locked shared state in `set_maximized`"); let mut shared_state_lock = shared_state.lock().unwrap(); // Save the standard frame sized if it is not zoomed @@ -170,7 +168,7 @@ pub unsafe fn set_maximized_async( ns_window.setFrame_display_(new_rect, 0); } - trace!("Unlocked shared state in `set_maximized`"); + trace!("[winit] Unlocked shared state in `set_maximized`"); } }); } diff --git a/src/platform_impl/macos/util/cursor.rs b/src/platform_impl/macos/util/cursor.rs index 5c4d1537a7..8b0fc1d621 100644 --- a/src/platform_impl/macos/util/cursor.rs +++ b/src/platform_impl/macos/util/cursor.rs @@ -91,7 +91,7 @@ impl Cursor { let sel = if msg_send![class, respondsToSelector: sel] { sel } else { - warn!("Cursor `{}` appears to be invalid", cursor_name); + warn!("[winit] Cursor `{}` appears to be invalid", cursor_name); sel!(arrowCursor) }; msg_send![class, performSelector: sel] diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index 741ecb4819..068ba13145 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -15,9 +15,9 @@ use objc::{ declare::ClassDecl, runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES}, }; +use winit_types::dpi::LogicalPosition; use crate::{ - dpi::LogicalPosition, event::{ DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode, WindowEvent, @@ -299,7 +299,7 @@ extern "C" fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> i } extern "C" fn view_did_move_to_window(this: &Object, _sel: Sel) { - trace!("Triggered `viewDidMoveToWindow`"); + trace!("[winit] Triggered `viewDidMoveToWindow`"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -317,7 +317,7 @@ extern "C" fn view_did_move_to_window(this: &Object, _sel: Sel) { ]; state.tracking_rect = Some(tracking_rect); } - trace!("Completed `viewDidMoveToWindow`"); + trace!("[winit] Completed `viewDidMoveToWindow`"); } extern "C" fn frame_did_change(this: &Object, _sel: Sel, _event: id) { @@ -389,10 +389,10 @@ extern "C" fn has_marked_text(_this: &Object, _sel: Sel) -> BOOL { extern "C" fn marked_range(this: &Object, _sel: Sel) -> NSRange { unsafe { - trace!("Triggered `markedRange`"); + trace!("[winit] Triggered `markedRange`"); let marked_text: id = *this.get_ivar("markedText"); let length = marked_text.length(); - trace!("Completed `markedRange`"); + trace!("[winit] Completed `markedRange`"); if length > 0 { NSRange::new(0, length - 1) } else { @@ -402,8 +402,8 @@ extern "C" fn marked_range(this: &Object, _sel: Sel) -> NSRange { } extern "C" fn selected_range(_this: &Object, _sel: Sel) -> NSRange { - trace!("Triggered `selectedRange`"); - trace!("Completed `selectedRange`"); + trace!("[winit] Triggered `selectedRange`"); + trace!("[winit] Completed `selectedRange`"); util::EMPTY_RANGE } @@ -414,7 +414,7 @@ extern "C" fn set_marked_text( _selected_range: NSRange, _replacement_range: NSRange, ) { - trace!("Triggered `setMarkedText`"); + trace!("[winit] Triggered `setMarkedText`"); unsafe { let marked_text_ref: &mut id = this.get_mut_ivar("markedText"); let _: () = msg_send![(*marked_text_ref), release]; @@ -427,11 +427,11 @@ extern "C" fn set_marked_text( }; *marked_text_ref = marked_text; } - trace!("Completed `setMarkedText`"); + trace!("[winit] Completed `setMarkedText`"); } extern "C" fn unmark_text(this: &Object, _sel: Sel) { - trace!("Triggered `unmarkText`"); + trace!("[winit] Triggered `unmarkText`"); unsafe { let marked_text: id = *this.get_ivar("markedText"); let mutable_string = marked_text.mutableString(); @@ -439,12 +439,12 @@ extern "C" fn unmark_text(this: &Object, _sel: Sel) { let input_context: id = msg_send![this, inputContext]; let _: () = msg_send![input_context, discardMarkedText]; } - trace!("Completed `unmarkText`"); + trace!("[winit] Completed `unmarkText`"); } extern "C" fn valid_attributes_for_marked_text(_this: &Object, _sel: Sel) -> id { - trace!("Triggered `validAttributesForMarkedText`"); - trace!("Completed `validAttributesForMarkedText`"); + trace!("[winit] Triggered `validAttributesForMarkedText`"); + trace!("[winit] Completed `validAttributesForMarkedText`"); unsafe { msg_send![class!(NSArray), array] } } @@ -454,14 +454,14 @@ extern "C" fn attributed_substring_for_proposed_range( _range: NSRange, _actual_range: *mut c_void, // *mut NSRange ) -> id { - trace!("Triggered `attributedSubstringForProposedRange`"); - trace!("Completed `attributedSubstringForProposedRange`"); + trace!("[winit] Triggered `attributedSubstringForProposedRange`"); + trace!("[winit] Completed `attributedSubstringForProposedRange`"); nil } extern "C" fn character_index_for_point(_this: &Object, _sel: Sel, _point: NSPoint) -> NSUInteger { - trace!("Triggered `characterIndexForPoint`"); - trace!("Completed `characterIndexForPoint`"); + trace!("[winit] Triggered `characterIndexForPoint`"); + trace!("[winit] Completed `characterIndexForPoint`"); 0 } @@ -472,7 +472,7 @@ extern "C" fn first_rect_for_character_range( _actual_range: *mut c_void, // *mut NSRange ) -> NSRect { unsafe { - trace!("Triggered `firstRectForCharacterRange`"); + trace!("[winit] Triggered `firstRectForCharacterRange`"); let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); let (x, y) = state.ime_spot.unwrap_or_else(|| { @@ -484,13 +484,13 @@ extern "C" fn first_rect_for_character_range( let y = util::bottom_left_to_top_left(content_rect); (x, y) }); - trace!("Completed `firstRectForCharacterRange`"); + trace!("[winit] Completed `firstRectForCharacterRange`"); NSRect::new(NSPoint::new(x as _, y as _), NSSize::new(0.0, 0.0)) } } extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange) { - trace!("Triggered `insertText`"); + trace!("[winit] Triggered `insertText`"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -522,11 +522,11 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran AppState::queue_events(events); } - trace!("Completed `insertText`"); + trace!("[winit] Completed `insertText`"); } extern "C" fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) { - trace!("Triggered `doCommandBySelector`"); + trace!("[winit] Triggered `doCommandBySelector`"); // Basically, we're sent this message whenever a keyboard event that doesn't generate a "human readable" character // happens, i.e. newlines, tabs, and Ctrl+C. unsafe { @@ -559,7 +559,7 @@ extern "C" fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) { AppState::queue_events(events); } - trace!("Completed `doCommandBySelector`"); + trace!("[winit] Completed `doCommandBySelector`"); } fn get_characters(event: id, ignore_modifiers: bool) -> String { @@ -619,7 +619,7 @@ fn retrieve_keycode(event: id) -> Option { } extern "C" fn key_down(this: &Object, _sel: Sel, event: id) { - trace!("Triggered `keyDown`"); + trace!("[winit] Triggered `keyDown`"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -672,11 +672,11 @@ extern "C" fn key_down(this: &Object, _sel: Sel, event: id) { let _: () = msg_send![this, interpretKeyEvents: array]; } } - trace!("Completed `keyDown`"); + trace!("[winit] Completed `keyDown`"); } extern "C" fn key_up(this: &Object, _sel: Sel, event: id) { - trace!("Triggered `keyUp`"); + trace!("[winit] Triggered `keyUp`"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -703,11 +703,11 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) { AppState::queue_event(EventWrapper::StaticEvent(window_event)); } - trace!("Completed `keyUp`"); + trace!("[winit] Completed `keyUp`"); } extern "C" fn flags_changed(this: &Object, _sel: Sel, event: id) { - trace!("Triggered `flagsChanged`"); + trace!("[winit] Triggered `flagsChanged`"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -762,7 +762,7 @@ extern "C" fn flags_changed(this: &Object, _sel: Sel, event: id) { event: DeviceEvent::ModifiersChanged(state.modifiers), })); } - trace!("Completed `flagsChanged`"); + trace!("[winit] Completed `flagsChanged`"); } extern "C" fn insert_tab(this: &Object, _sel: Sel, _sender: id) { @@ -790,7 +790,7 @@ extern "C" fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) { // Allows us to receive Cmd-. (the shortcut for closing a dialog) // https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6 extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) { - trace!("Triggered `cancelOperation`"); + trace!("[winit] Triggered `cancelOperation`"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -818,7 +818,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) { AppState::queue_event(EventWrapper::StaticEvent(window_event)); } - trace!("Completed `cancelOperation`"); + trace!("[winit] Completed `cancelOperation`"); } fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: ElementState) { @@ -919,7 +919,7 @@ extern "C" fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) { } extern "C" fn mouse_entered(this: &Object, _sel: Sel, event: id) { - trace!("Triggered `mouseEntered`"); + trace!("[winit] Triggered `mouseEntered`"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -934,11 +934,11 @@ extern "C" fn mouse_entered(this: &Object, _sel: Sel, event: id) { AppState::queue_event(EventWrapper::StaticEvent(enter_event)); mouse_motion(this, event); } - trace!("Completed `mouseEntered`"); + trace!("[winit] Completed `mouseEntered`"); } extern "C" fn mouse_exited(this: &Object, _sel: Sel, _event: id) { - trace!("Triggered `mouseExited`"); + trace!("[winit] Triggered `mouseExited`"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -952,11 +952,11 @@ extern "C" fn mouse_exited(this: &Object, _sel: Sel, _event: id) { AppState::queue_event(EventWrapper::StaticEvent(window_event)); } - trace!("Completed `mouseExited`"); + trace!("[winit] Completed `mouseExited`"); } extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) { - trace!("Triggered `scrollWheel`"); + trace!("[winit] Triggered `scrollWheel`"); unsafe { let delta = { let (x, y) = (event.scrollingDeltaX(), event.scrollingDeltaY()); @@ -995,11 +995,11 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) { AppState::queue_event(EventWrapper::StaticEvent(device_event)); AppState::queue_event(EventWrapper::StaticEvent(window_event)); } - trace!("Completed `scrollWheel`"); + trace!("[winit] Completed `scrollWheel`"); } extern "C" fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) { - trace!("Triggered `pressureChangeWithEvent`"); + trace!("[winit] Triggered `pressureChangeWithEvent`"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -1018,7 +1018,7 @@ extern "C" fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) { AppState::queue_event(EventWrapper::StaticEvent(window_event)); } - trace!("Completed `pressureChangeWithEvent`"); + trace!("[winit] Completed `pressureChangeWithEvent`"); } // Allows us to receive Ctrl-Tab and Ctrl-Esc. diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 355fdecc27..aefb0e5975 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -10,10 +10,6 @@ use std::{ }; use crate::{ - dpi::{ - LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, Size::Logical, - }, - error::{ExternalError, NotSupportedError, OsError as RootOsError}, icon::Icon, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, platform::macos::{ActivationPolicy, RequestUserAttentionType, WindowExtMacOS}, @@ -25,10 +21,10 @@ use crate::{ view::CursorState, view::{self, new_view}, window_delegate::new_delegate, - OsError, }, window::{CursorIcon, Fullscreen, WindowAttributes, WindowId as RootWindowId}, }; + use cocoa::{ appkit::{ self, CGFloat, NSApp, NSApplication, NSApplicationActivationPolicy, @@ -43,6 +39,11 @@ use objc::{ declare::ClassDecl, runtime::{Class, Object, Sel, BOOL, NO, YES}, }; +use winit_types::dpi::{ + LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, Size::Logical, +}; +use winit_types::error::Error; +use winit_types::platform::OsError; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Id(pub usize); @@ -313,10 +314,10 @@ impl UnownedWindow { pub fn new( mut win_attribs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, - ) -> Result<(Arc, IdRef), RootOsError> { + ) -> Result<(Arc, IdRef), Error> { unsafe { if !msg_send![class!(NSThread), isMainThread] { - panic!("Windows can only be created on the main thread on macOS"); + panic!("[winit] Windows can only be created on the main thread on macOS"); } } @@ -324,18 +325,18 @@ impl UnownedWindow { let ns_app = create_app(pl_attribs.activation_policy).ok_or_else(|| { unsafe { pool.drain() }; - os_error!(OsError::CreationError("Couldn't create `NSApplication`")) + make_oserror!(OsError::CreationError("Couldn't create `NSApplication`")) })?; let ns_window = create_window(&win_attribs, &pl_attribs).ok_or_else(|| { unsafe { pool.drain() }; - os_error!(OsError::CreationError("Couldn't create `NSWindow`")) + make_oserror!(OsError::CreationError("Couldn't create `NSWindow`")) })?; let (ns_view, cursor_state) = unsafe { create_view(*ns_window, &pl_attribs) }.ok_or_else(|| { unsafe { pool.drain() }; - os_error!(OsError::CreationError("Couldn't create `NSView`")) + make_oserror!(OsError::CreationError("Couldn't create `NSView`")) })?; let input_context = unsafe { util::create_input_context(*ns_view) }; @@ -393,7 +394,7 @@ impl UnownedWindow { let delegate = new_delegate(&window, fullscreen.is_some()); // Set fullscreen mode after we setup everything - window.set_fullscreen(fullscreen); + window.set_fullscreen(fullscreen)?; // Setting the window as key has to happen *after* we set the fullscreen // state, since otherwise we'll briefly see the window at normal size @@ -444,7 +445,7 @@ impl UnownedWindow { AppState::queue_redraw(RootWindowId(self.id())); } - pub fn outer_position(&self) -> Result, NotSupportedError> { + pub fn outer_position(&self) -> Result, Error> { let frame_rect = unsafe { NSWindow::frame(*self.ns_window) }; let position = LogicalPosition::new( frame_rect.origin.x as f64, @@ -454,7 +455,7 @@ impl UnownedWindow { Ok(position.to_physical(dpi_factor)) } - pub fn inner_position(&self) -> Result, NotSupportedError> { + pub fn inner_position(&self) -> Result, Error> { let content_rect = unsafe { NSWindow::contentRectForFrameRect_(*self.ns_window, NSWindow::frame(*self.ns_window)) }; @@ -534,10 +535,10 @@ impl UnownedWindow { #[inline] pub fn set_resizable(&self, resizable: bool) { let fullscreen = { - trace!("Locked shared state in `set_resizable`"); + trace!("[winit] Locked shared state in `set_resizable`"); let mut shared_state_lock = self.shared_state.lock().unwrap(); shared_state_lock.resizable = resizable; - trace!("Unlocked shared state in `set_resizable`"); + trace!("[winit] Unlocked shared state in `set_resizable`"); shared_state_lock.fullscreen.is_some() }; if !fullscreen { @@ -564,10 +565,10 @@ impl UnownedWindow { } #[inline] - pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { + pub fn set_cursor_grab(&self, grab: bool) -> Result<(), Error> { // TODO: Do this for real https://stackoverflow.com/a/40922095/5435443 CGDisplay::associate_mouse_and_mouse_cursor_position(!grab) - .map_err(|status| ExternalError::Os(os_error!(OsError::CGError(status)))) + .map_err(|status| make_oserror!(OsError::CGError(status))) } #[inline] @@ -592,7 +593,7 @@ impl UnownedWindow { } #[inline] - pub fn set_cursor_position(&self, cursor_position: Position) -> Result<(), ExternalError> { + pub fn set_cursor_position(&self, cursor_position: Position) -> Result<(), Error> { let physical_window_position = self.inner_position().unwrap(); let dpi_factor = self.scale_factor(); let window_position = physical_window_position.to_logical::(dpi_factor); @@ -602,9 +603,9 @@ impl UnownedWindow { y: logical_cursor_position.y + window_position.y, }; CGDisplay::warp_mouse_cursor_position(point) - .map_err(|e| ExternalError::Os(os_error!(OsError::CGError(e))))?; + .map_err(|e| make_oserror!(OsError::CGError(e)))?; CGDisplay::associate_mouse_and_mouse_cursor_position(true) - .map_err(|e| ExternalError::Os(os_error!(OsError::CGError(e))))?; + .map_err(|e| make_oserror!(OsError::CGError(e)))?; Ok(()) } @@ -647,7 +648,7 @@ impl UnownedWindow { /// user clicking on the green fullscreen button or programmatically by /// `toggleFullScreen:` pub(crate) fn restore_state_from_fullscreen(&self) { - trace!("Locked shared state in `restore_state_from_fullscreen`"); + trace!("[winit] Locked shared state in `restore_state_from_fullscreen`"); let mut shared_state_lock = self.shared_state.lock().unwrap(); shared_state_lock.fullscreen = None; @@ -656,7 +657,7 @@ impl UnownedWindow { let mask = self.saved_style(&mut *shared_state_lock); drop(shared_state_lock); - trace!("Unocked shared state in `restore_state_from_fullscreen`"); + trace!("[winit] Unocked shared state in `restore_state_from_fullscreen`"); self.set_style_mask_async(mask); self.set_maximized(maximized); @@ -704,26 +705,26 @@ impl UnownedWindow { } #[inline] - pub fn set_fullscreen(&self, fullscreen: Option) { - trace!("Locked shared state in `set_fullscreen`"); + pub fn set_fullscreen(&self, fullscreen: Option) -> Result<(), Error> { + trace!("[winit] Locked shared state in `set_fullscreen`"); let mut shared_state_lock = self.shared_state.lock().unwrap(); if shared_state_lock.is_simple_fullscreen { - trace!("Unlocked shared state in `set_fullscreen`"); - return; + trace!("[winit] Unlocked shared state in `set_fullscreen`"); + return Ok(()); } if shared_state_lock.in_fullscreen_transition { // We can't set fullscreen here. // Set fullscreen after transition. shared_state_lock.target_fullscreen = Some(fullscreen); - trace!("Unlocked shared state in `set_fullscreen`"); - return; + trace!("[winit] Unlocked shared state in `set_fullscreen`"); + return Ok(()); } let old_fullscreen = shared_state_lock.fullscreen.clone(); if fullscreen == old_fullscreen { - trace!("Unlocked shared state in `set_fullscreen`"); - return; + trace!("[winit] Unlocked shared state in `set_fullscreen`"); + return Ok(()); } - trace!("Unlocked shared state in `set_fullscreen`"); + trace!("[winit] Unlocked shared state in `set_fullscreen`"); drop(shared_state_lock); // If the fullscreen is on a different monitor, we must move the window @@ -795,7 +796,10 @@ impl UnownedWindow { video_mode.video_mode.native_mode.0, std::ptr::null(), ); - assert!(result == ffi::kCGErrorSuccess, "failed to set video mode"); + assert!( + result == ffi::kCGErrorSuccess, + "[winit] failed to set video mode" + ); // After the display has been configured, fade back in // asynchronously @@ -815,10 +819,10 @@ impl UnownedWindow { } } - trace!("Locked shared state in `set_fullscreen`"); + trace!("[winit] Locked shared state in `set_fullscreen`"); let mut shared_state_lock = self.shared_state.lock().unwrap(); shared_state_lock.fullscreen = fullscreen.clone(); - trace!("Unlocked shared state in `set_fullscreen`"); + trace!("[winit] Unlocked shared state in `set_fullscreen`"); match (&old_fullscreen, &fullscreen) { (&None, &Some(_)) => unsafe { @@ -867,6 +871,8 @@ impl UnownedWindow { }, _ => (), } + + Ok(()) } #[inline] @@ -875,9 +881,9 @@ impl UnownedWindow { self.decorations.store(decorations, Ordering::Release); let (fullscreen, resizable) = { - trace!("Locked shared state in `set_decorations`"); + trace!("[winit] Locked shared state in `set_decorations`"); let shared_state_lock = self.shared_state.lock().unwrap(); - trace!("Unlocked shared state in `set_decorations`"); + trace!("[winit] Unlocked shared state in `set_decorations`"); ( shared_state_lock.fullscreen.is_some(), shared_state_lock.resizable, @@ -1091,7 +1097,7 @@ impl WindowExtMacOS for UnownedWindow { impl Drop for UnownedWindow { fn drop(&mut self) { - trace!("Dropping `UnownedWindow` ({:?})", self as *mut _); + trace!("[winit] Dropping `UnownedWindow` ({:?})", self as *mut _); // Close the window if it has not yet been closed. if *self.ns_window != nil { unsafe { util::close_async(*self.ns_window) }; diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index 7766893782..38b47456f2 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -13,9 +13,9 @@ use objc::{ declare::ClassDecl, runtime::{Class, Object, Sel, BOOL, NO, YES}, }; +use winit_types::dpi::LogicalSize; use crate::{ - dpi::LogicalSize, event::{Event, WindowEvent}, platform_impl::platform::{ app_state::AppState, @@ -260,14 +260,14 @@ extern "C" fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> i } extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL { - trace!("Triggered `windowShouldClose:`"); + trace!("[winit] Triggered `windowShouldClose:`"); with_state(this, |state| state.emit_event(WindowEvent::CloseRequested)); - trace!("Completed `windowShouldClose:`"); + trace!("[winit] Completed `windowShouldClose:`"); NO } extern "C" fn window_will_close(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowWillClose:`"); + trace!("[winit] Triggered `windowWillClose:`"); with_state(this, |state| unsafe { // `setDelegate:` retains the previous value and then autoreleases it let pool = NSAutoreleasePool::new(nil); @@ -277,56 +277,56 @@ extern "C" fn window_will_close(this: &Object, _: Sel, _: id) { pool.drain(); state.emit_event(WindowEvent::Destroyed); }); - trace!("Completed `windowWillClose:`"); + trace!("[winit] Completed `windowWillClose:`"); } extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowDidResize:`"); + trace!("[winit] Triggered `windowDidResize:`"); with_state(this, |state| { state.emit_resize_event(); state.emit_move_event(); }); - trace!("Completed `windowDidResize:`"); + trace!("[winit] Completed `windowDidResize:`"); } // This won't be triggered if the move was part of a resize. extern "C" fn window_did_move(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowDidMove:`"); + trace!("[winit] Triggered `windowDidMove:`"); with_state(this, |state| { state.emit_move_event(); }); - trace!("Completed `windowDidMove:`"); + trace!("[winit] Completed `windowDidMove:`"); } extern "C" fn window_did_change_backing_properties(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowDidChangeBackingProperties:`"); + trace!("[winit] Triggered `windowDidChangeBackingProperties:`"); with_state(this, |state| { state.emit_static_scale_factor_changed_event(); }); - trace!("Completed `windowDidChangeBackingProperties:`"); + trace!("[winit] Completed `windowDidChangeBackingProperties:`"); } extern "C" fn window_did_become_key(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowDidBecomeKey:`"); + trace!("[winit] Triggered `windowDidBecomeKey:`"); with_state(this, |state| { // TODO: center the cursor if the window had mouse grab when it // lost focus state.emit_event(WindowEvent::Focused(true)); }); - trace!("Completed `windowDidBecomeKey:`"); + trace!("[winit] Completed `windowDidBecomeKey:`"); } extern "C" fn window_did_resign_key(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowDidResignKey:`"); + trace!("[winit] Triggered `windowDidResignKey:`"); with_state(this, |state| { state.emit_event(WindowEvent::Focused(false)); }); - trace!("Completed `windowDidResignKey:`"); + trace!("[winit] Completed `windowDidResignKey:`"); } /// Invoked when the dragged image enters destination bounds or frame extern "C" fn dragging_entered(this: &Object, _: Sel, sender: id) -> BOOL { - trace!("Triggered `draggingEntered:`"); + trace!("[winit] Triggered `draggingEntered:`"); use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration}; use std::path::PathBuf; @@ -348,20 +348,20 @@ extern "C" fn dragging_entered(this: &Object, _: Sel, sender: id) -> BOOL { } } - trace!("Completed `draggingEntered:`"); + trace!("[winit] Completed `draggingEntered:`"); YES } /// Invoked when the image is released extern "C" fn prepare_for_drag_operation(_: &Object, _: Sel, _: id) -> BOOL { - trace!("Triggered `prepareForDragOperation:`"); - trace!("Completed `prepareForDragOperation:`"); + trace!("[winit] Triggered `prepareForDragOperation:`"); + trace!("[winit] Completed `prepareForDragOperation:`"); YES } /// Invoked after the released image has been removed from the screen extern "C" fn perform_drag_operation(this: &Object, _: Sel, sender: id) -> BOOL { - trace!("Triggered `performDragOperation:`"); + trace!("[winit] Triggered `performDragOperation:`"); use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration}; use std::path::PathBuf; @@ -383,31 +383,31 @@ extern "C" fn perform_drag_operation(this: &Object, _: Sel, sender: id) -> BOOL } } - trace!("Completed `performDragOperation:`"); + trace!("[winit] Completed `performDragOperation:`"); YES } /// Invoked when the dragging operation is complete extern "C" fn conclude_drag_operation(_: &Object, _: Sel, _: id) { - trace!("Triggered `concludeDragOperation:`"); - trace!("Completed `concludeDragOperation:`"); + trace!("[winit] Triggered `concludeDragOperation:`"); + trace!("[winit] Completed `concludeDragOperation:`"); } /// Invoked when the dragging operation is cancelled extern "C" fn dragging_exited(this: &Object, _: Sel, _: id) { - trace!("Triggered `draggingExited:`"); + trace!("[winit] Triggered `draggingExited:`"); with_state(this, |state| { state.emit_event(WindowEvent::HoveredFileCancelled) }); - trace!("Completed `draggingExited:`"); + trace!("[winit] Completed `draggingExited:`"); } /// Invoked when before enter fullscreen extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowWillEnterFullscreen:`"); + trace!("[winit] Triggered `windowWillEnterFullscreen:`"); with_state(this, |state| { state.with_window(|window| { - trace!("Locked shared state in `window_will_enter_fullscreen`"); + trace!("[winit] Locked shared state in `window_will_enter_fullscreen`"); let mut shared_state = window.shared_state.lock().unwrap(); shared_state.maximized = window.is_zoomed(); match shared_state.fullscreen { @@ -426,24 +426,24 @@ extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) { } } shared_state.in_fullscreen_transition = true; - trace!("Unlocked shared state in `window_will_enter_fullscreen`"); + trace!("[winit] Unlocked shared state in `window_will_enter_fullscreen`"); }) }); - trace!("Completed `windowWillEnterFullscreen:`"); + trace!("[winit] Completed `windowWillEnterFullscreen:`"); } /// Invoked when before exit fullscreen extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowWillExitFullScreen:`"); + trace!("[winit] Triggered `windowWillExitFullScreen:`"); with_state(this, |state| { state.with_window(|window| { - trace!("Locked shared state in `window_will_exit_fullscreen`"); + trace!("[winit] Locked shared state in `window_will_exit_fullscreen`"); let mut shared_state = window.shared_state.lock().unwrap(); shared_state.in_fullscreen_transition = true; - trace!("Unlocked shared state in `window_will_exit_fullscreen`"); + trace!("[winit] Unlocked shared state in `window_will_exit_fullscreen`"); }); }); - trace!("Completed `windowWillExitFullScreen:`"); + trace!("[winit] Completed `windowWillExitFullScreen:`"); } extern "C" fn window_will_use_fullscreen_presentation_options( @@ -468,42 +468,42 @@ extern "C" fn window_will_use_fullscreen_presentation_options( /// Invoked when entered fullscreen extern "C" fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowDidEnterFullscreen:`"); + trace!("[winit] Triggered `windowDidEnterFullscreen:`"); with_state(this, |state| { state.initial_fullscreen = false; state.with_window(|window| { - trace!("Locked shared state in `window_did_enter_fullscreen`"); + trace!("[winit] Locked shared state in `window_did_enter_fullscreen`"); let mut shared_state = window.shared_state.lock().unwrap(); shared_state.in_fullscreen_transition = false; let target_fullscreen = shared_state.target_fullscreen.take(); - trace!("Unlocked shared state in `window_did_enter_fullscreen`"); + trace!("[winit] Unlocked shared state in `window_did_enter_fullscreen`"); drop(shared_state); if let Some(target_fullscreen) = target_fullscreen { - window.set_fullscreen(target_fullscreen); + window.set_fullscreen(target_fullscreen).unwrap(); } }); }); - trace!("Completed `windowDidEnterFullscreen:`"); + trace!("[winit] Completed `windowDidEnterFullscreen:`"); } /// Invoked when exited fullscreen extern "C" fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowDidExitFullscreen:`"); + trace!("[winit] Triggered `windowDidExitFullscreen:`"); with_state(this, |state| { state.with_window(|window| { window.restore_state_from_fullscreen(); - trace!("Locked shared state in `window_did_exit_fullscreen`"); + trace!("[winit] Locked shared state in `window_did_exit_fullscreen`"); let mut shared_state = window.shared_state.lock().unwrap(); shared_state.in_fullscreen_transition = false; let target_fullscreen = shared_state.target_fullscreen.take(); - trace!("Unlocked shared state in `window_did_exit_fullscreen`"); + trace!("[winit] Unlocked shared state in `window_did_exit_fullscreen`"); drop(shared_state); if let Some(target_fullscreen) = target_fullscreen { - window.set_fullscreen(target_fullscreen); + window.set_fullscreen(target_fullscreen).unwrap(); } }) }); - trace!("Completed `windowDidExitFullscreen:`"); + trace!("[winit] Completed `windowDidExitFullscreen:`"); } /// Invoked when fail to enter fullscreen @@ -523,14 +523,14 @@ extern "C" fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) { /// This method indicates that there was an error, and you should clean up any /// work you may have done to prepare to enter full-screen mode. extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowDidFailToEnterFullscreen:`"); + trace!("[winit] Triggered `windowDidFailToEnterFullscreen:`"); with_state(this, |state| { state.with_window(|window| { - trace!("Locked shared state in `window_did_fail_to_enter_fullscreen`"); + trace!("[winit] Locked shared state in `window_did_fail_to_enter_fullscreen`"); let mut shared_state = window.shared_state.lock().unwrap(); shared_state.in_fullscreen_transition = false; shared_state.target_fullscreen = None; - trace!("Unlocked shared state in `window_did_fail_to_enter_fullscreen`"); + trace!("[winit] Unlocked shared state in `window_did_fail_to_enter_fullscreen`"); }); if state.initial_fullscreen { let _: () = unsafe { @@ -544,5 +544,5 @@ extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) state.with_window(|window| window.restore_state_from_fullscreen()); } }); - trace!("Completed `windowDidFailToEnterFullscreen:`"); + trace!("[winit] Completed `windowDidFailToEnterFullscreen:`"); } diff --git a/src/platform_impl/mod.rs b/src/platform_impl/mod.rs index 152065d80e..5f201bbfb0 100644 --- a/src/platform_impl/mod.rs +++ b/src/platform_impl/mod.rs @@ -37,4 +37,4 @@ mod platform; not(target_os = "openbsd"), not(target_arch = "wasm32"), ))] -compile_error!("The platform you're compiling for is not supported by winit"); +compile_error!("[winit] The platform you're compiling for is not supported by winit"); diff --git a/src/platform_impl/web/error.rs b/src/platform_impl/web/error.rs deleted file mode 100644 index 6995f2bc52..0000000000 --- a/src/platform_impl/web/error.rs +++ /dev/null @@ -1,10 +0,0 @@ -use std::fmt; - -#[derive(Debug)] -pub struct OsError(pub String); - -impl fmt::Display for OsError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 2395f34678..9ac2c46b81 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -1,10 +1,12 @@ use super::{backend, device, proxy::Proxy, runner, window}; -use crate::dpi::{PhysicalSize, Size}; use crate::event::{DeviceId, ElementState, Event, KeyboardInput, TouchPhase, WindowEvent}; use crate::event_loop::ControlFlow; use crate::window::WindowId; + use std::clone::Clone; +use winit_types::dpi::{PhysicalSize, Size}; + pub struct WindowTarget { pub(crate) runner: runner::Shared, } diff --git a/src/platform_impl/web/mod.rs b/src/platform_impl/web/mod.rs index ba022db6e7..84a16e1469 100644 --- a/src/platform_impl/web/mod.rs +++ b/src/platform_impl/web/mod.rs @@ -18,7 +18,6 @@ // compliant way. mod device; -mod error; mod event_loop; mod monitor; mod window; @@ -32,10 +31,9 @@ mod backend; mod backend; #[cfg(not(any(feature = "web-sys", feature = "stdweb")))] -compile_error!("Please select a feature to build for web: `web-sys`, `stdweb`"); +compile_error!("[winit] Please select a feature to build for web: `web-sys`, `stdweb`"); pub use self::device::Id as DeviceId; -pub use self::error::OsError; pub use self::event_loop::{ EventLoop, Proxy as EventLoopProxy, WindowTarget as EventLoopWindowTarget, }; diff --git a/src/platform_impl/web/monitor.rs b/src/platform_impl/web/monitor.rs index d2b3c364ae..d0ad03dc41 100644 --- a/src/platform_impl/web/monitor.rs +++ b/src/platform_impl/web/monitor.rs @@ -1,6 +1,7 @@ -use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::monitor::{MonitorHandle, VideoMode}; +use winit_types::dpi::{PhysicalPosition, PhysicalSize}; + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Handle; diff --git a/src/platform_impl/web/stdweb/canvas.rs b/src/platform_impl/web/stdweb/canvas.rs index a3581917dc..17a2b93da0 100644 --- a/src/platform_impl/web/stdweb/canvas.rs +++ b/src/platform_impl/web/stdweb/canvas.rs @@ -1,8 +1,10 @@ use super::event; -use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; -use crate::error::OsError as RootOE; use crate::event::{ModifiersState, MouseButton, MouseScrollDelta, ScanCode, VirtualKeyCode}; -use crate::platform_impl::{OsError, PlatformSpecificWindowBuilderAttributes}; +use crate::platform_impl::PlatformSpecificWindowBuilderAttributes; + +use winit_types::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; +use winit_types::error::Error; +use winit_types::platform::OsError; use std::cell::RefCell; use std::rc::Rc; @@ -43,14 +45,16 @@ impl Drop for Canvas { } impl Canvas { - pub fn create(attr: PlatformSpecificWindowBuilderAttributes) -> Result { + pub fn create(attr: PlatformSpecificWindowBuilderAttributes) -> Result { let canvas = match attr.canvas { Some(canvas) => canvas, None => document() .create_element("canvas") - .map_err(|_| os_error!(OsError("Failed to create canvas element".to_owned())))? + .map_err(|_| make_oserror!(OsError("Failed to create canvas element".to_owned())))? .try_into() - .map_err(|_| os_error!(OsError("Failed to create canvas element".to_owned())))?, + .map_err(|_| { + make_oserror!(OsError("Failed to create canvas element".to_owned())) + })?, }; // A tabindex is needed in order to capture local keyboard events. @@ -60,7 +64,7 @@ impl Canvas { // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex canvas .set_attribute("tabindex", "0") - .map_err(|_| os_error!(OsError("Failed to set a tabindex".to_owned())))?; + .map_err(|_| make_oserror!(OsError("Failed to set a tabindex".to_owned())))?; Ok(Canvas { raw: canvas, @@ -83,7 +87,7 @@ impl Canvas { pub fn set_attribute(&self, attribute: &str, value: &str) { self.raw .set_attribute(attribute, value) - .expect(&format!("Set attribute: {}", attribute)); + .expect(&format!("[winit] Set attribute: {}", attribute)); } pub fn position(&self) -> LogicalPosition { diff --git a/src/platform_impl/web/stdweb/event.rs b/src/platform_impl/web/stdweb/event.rs index 8c534dc053..6ff4176486 100644 --- a/src/platform_impl/web/stdweb/event.rs +++ b/src/platform_impl/web/stdweb/event.rs @@ -1,6 +1,7 @@ -use crate::dpi::LogicalPosition; use crate::event::{ModifiersState, MouseButton, MouseScrollDelta, ScanCode, VirtualKeyCode}; +use winit_types::dpi::LogicalPosition; + use stdweb::web::event::{IKeyboardEvent, IMouseEvent, MouseWheelDeltaMode, MouseWheelEvent}; use stdweb::{js, unstable::TryInto, JsSerialize}; @@ -46,7 +47,7 @@ pub fn scan_code(event: &T) -> ScanCode { key_code .try_into() - .expect("The which value should be a number") + .expect("[winit] The which value should be a number") } pub fn virtual_key_code(event: &impl IKeyboardEvent) -> Option { diff --git a/src/platform_impl/web/stdweb/mod.rs b/src/platform_impl/web/stdweb/mod.rs index 9274ab9916..bb32155f8f 100644 --- a/src/platform_impl/web/stdweb/mod.rs +++ b/src/platform_impl/web/stdweb/mod.rs @@ -5,10 +5,11 @@ mod timeout; pub use self::canvas::Canvas; pub use self::timeout::Timeout; -use crate::dpi::{LogicalSize, Size}; use crate::platform::web::WindowExtStdweb; use crate::window::Window; +use winit_types::dpi::{LogicalSize, Size}; + use stdweb::js; use stdweb::web::event::BeforeUnloadEvent; use stdweb::web::window; diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 970f87dadb..2a424b1cf1 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -1,8 +1,10 @@ use super::event; -use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; -use crate::error::OsError as RootOE; use crate::event::{ModifiersState, MouseButton, MouseScrollDelta, ScanCode, VirtualKeyCode}; -use crate::platform_impl::{OsError, PlatformSpecificWindowBuilderAttributes}; +use crate::platform_impl::PlatformSpecificWindowBuilderAttributes; + +use winit_types::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; +use winit_types::error::Error; +use winit_types::platform::OsError; use std::cell::RefCell; use std::rc::Rc; @@ -35,20 +37,22 @@ impl Drop for Canvas { } impl Canvas { - pub fn create(attr: PlatformSpecificWindowBuilderAttributes) -> Result { + pub fn create(attr: PlatformSpecificWindowBuilderAttributes) -> Result { let canvas = match attr.canvas { Some(canvas) => canvas, None => { let window = web_sys::window() - .ok_or(os_error!(OsError("Failed to obtain window".to_owned())))?; + .ok_or(make_oserror!(OsError("Failed to obtain window".to_owned())))?; - let document = window - .document() - .ok_or(os_error!(OsError("Failed to obtain document".to_owned())))?; + let document = window.document().ok_or(make_oserror!(OsError( + "Failed to obtain document".to_owned() + )))?; document .create_element("canvas") - .map_err(|_| os_error!(OsError("Failed to create canvas element".to_owned())))? + .map_err(|_| { + make_oserror!(OsError("Failed to create canvas element".to_owned())) + })? .unchecked_into() } }; @@ -60,7 +64,7 @@ impl Canvas { // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex canvas .set_attribute("tabindex", "0") - .map_err(|_| os_error!(OsError("Failed to set a tabindex".to_owned())))?; + .map_err(|_| make_oserror!(OsError("Failed to set a tabindex".to_owned())))?; Ok(Canvas { raw: canvas, @@ -83,7 +87,7 @@ impl Canvas { pub fn set_attribute(&self, attribute: &str, value: &str) { self.raw .set_attribute(attribute, value) - .expect(&format!("Set attribute: {}", attribute)); + .expect(&format!("[winit] Set attribute: {}", attribute)); } pub fn position(&self) -> LogicalPosition { @@ -268,7 +272,7 @@ impl Canvas { self.raw .add_event_listener_with_callback(event_name, &closure.as_ref().unchecked_ref()) - .expect("Failed to add event listener with callback"); + .expect("[winit] Failed to add event listener with callback"); closure } @@ -290,7 +294,7 @@ impl Canvas { if *wants_fullscreen.borrow() { canvas .request_fullscreen() - .expect("Failed to enter fullscreen"); + .expect("[winit] Failed to enter fullscreen"); *wants_fullscreen.borrow_mut() = false; } }) diff --git a/src/platform_impl/web/web_sys/event.rs b/src/platform_impl/web/web_sys/event.rs index 1c1bdba41c..ff70e46b5b 100644 --- a/src/platform_impl/web/web_sys/event.rs +++ b/src/platform_impl/web/web_sys/event.rs @@ -1,6 +1,7 @@ -use crate::dpi::LogicalPosition; use crate::event::{ModifiersState, MouseButton, MouseScrollDelta, ScanCode, VirtualKeyCode}; +use winit_types::dpi::LogicalPosition; + use std::convert::TryInto; use web_sys::{KeyboardEvent, MouseEvent, WheelEvent}; @@ -9,7 +10,11 @@ pub fn mouse_button(event: &MouseEvent) -> MouseButton { 0 => MouseButton::Left, 1 => MouseButton::Middle, 2 => MouseButton::Right, - i => MouseButton::Other((i - 3).try_into().expect("very large mouse button value")), + i => MouseButton::Other( + (i - 3) + .try_into() + .expect("[winit] very large mouse button value"), + ), } } diff --git a/src/platform_impl/web/web_sys/mod.rs b/src/platform_impl/web/web_sys/mod.rs index cafbae1fb1..2a7c8d9269 100644 --- a/src/platform_impl/web/web_sys/mod.rs +++ b/src/platform_impl/web/web_sys/mod.rs @@ -5,25 +5,28 @@ mod timeout; pub use self::canvas::Canvas; pub use self::timeout::Timeout; -use crate::dpi::{LogicalSize, Size}; use crate::platform::web::WindowExtWebSys; use crate::window::Window; + use wasm_bindgen::{closure::Closure, JsCast}; use web_sys::{window, BeforeUnloadEvent, Element, HtmlCanvasElement}; +use winit_types::dpi::{LogicalSize, Size}; pub fn throw(msg: &str) { wasm_bindgen::throw_str(msg); } pub fn exit_fullscreen() { - let window = web_sys::window().expect("Failed to obtain window"); - let document = window.document().expect("Failed to obtain document"); + let window = web_sys::window().expect("[winit] Failed to obtain window"); + let document = window + .document() + .expect("[winit] Failed to obtain document"); document.exit_fullscreen(); } pub fn on_unload(mut handler: impl FnMut() + 'static) { - let window = web_sys::window().expect("Failed to obtain window"); + let window = web_sys::window().expect("[winit] Failed to obtain window"); let closure = Closure::wrap( Box::new(move |_: BeforeUnloadEvent| handler()) as Box @@ -31,7 +34,7 @@ pub fn on_unload(mut handler: impl FnMut() + 'static) { window .add_event_listener_with_callback("beforeunload", &closure.as_ref().unchecked_ref()) - .expect("Failed to add close listener"); + .expect("[winit] Failed to add close listener"); } impl WindowExtWebSys for Window { @@ -41,23 +44,23 @@ impl WindowExtWebSys for Window { } pub fn window_size() -> LogicalSize { - let window = web_sys::window().expect("Failed to obtain window"); + let window = web_sys::window().expect("[winit] Failed to obtain window"); let width = window .inner_width() - .expect("Failed to get width") + .expect("[winit] Failed to get width") .as_f64() - .expect("Failed to get width as f64"); + .expect("[winit] Failed to get width as f64"); let height = window .inner_height() - .expect("Failed to get height") + .expect("[winit] Failed to get height") .as_f64() - .expect("Failed to get height as f64"); + .expect("[winit] Failed to get height as f64"); LogicalSize { width, height } } pub fn scale_factor() -> f64 { - let window = web_sys::window().expect("Failed to obtain window"); + let window = web_sys::window().expect("[winit] Failed to obtain window"); window.device_pixel_ratio() } @@ -73,15 +76,17 @@ pub fn set_canvas_size(raw: &HtmlCanvasElement, size: Size) { let style = raw.style(); style .set_property("width", &format!("{}px", logical_size.width)) - .expect("Failed to set canvas width"); + .expect("[winit] Failed to set canvas width"); style .set_property("height", &format!("{}px", logical_size.height)) - .expect("Failed to set canvas height"); + .expect("[winit] Failed to set canvas height"); } pub fn is_fullscreen(canvas: &HtmlCanvasElement) -> bool { - let window = window().expect("Failed to obtain window"); - let document = window.document().expect("Failed to obtain document"); + let window = window().expect("[winit] Failed to obtain window"); + let document = window + .document() + .expect("[winit] Failed to obtain document"); match document.fullscreen_element() { Some(elem) => { diff --git a/src/platform_impl/web/web_sys/timeout.rs b/src/platform_impl/web/web_sys/timeout.rs index e7ce69a083..17da5fc879 100644 --- a/src/platform_impl/web/web_sys/timeout.rs +++ b/src/platform_impl/web/web_sys/timeout.rs @@ -13,7 +13,7 @@ impl Timeout { where F: 'static + FnMut(), { - let window = web_sys::window().expect("Failed to obtain window"); + let window = web_sys::window().expect("[winit] Failed to obtain window"); let closure = Closure::wrap(Box::new(f) as Box); @@ -22,7 +22,7 @@ impl Timeout { &closure.as_ref().unchecked_ref(), duration.as_millis() as i32, ) - .expect("Failed to set timeout"); + .expect("[winit] Failed to set timeout"); Timeout { handle, @@ -33,7 +33,7 @@ impl Timeout { impl Drop for Timeout { fn drop(&mut self) { - let window = web_sys::window().expect("Failed to obtain window"); + let window = web_sys::window().expect("[winit] Failed to obtain window"); window.clear_timeout_with_handle(self.handle); } diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index bd525056fa..143cda488f 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -1,11 +1,12 @@ -use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}; -use crate::error::{ExternalError, NotSupportedError, OsError as RootOE}; use crate::icon::Icon; use crate::monitor::MonitorHandle as RootMH; use crate::window::{CursorIcon, Fullscreen, WindowAttributes, WindowId as RootWI}; use raw_window_handle::web::WebHandle; +use winit_types::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}; +use winit_types::error::Error; + use super::{backend, monitor, EventLoopWindowTarget}; use std::cell::RefCell; @@ -24,7 +25,7 @@ impl Window { target: &EventLoopWindowTarget, attr: WindowAttributes, platform_attr: PlatformSpecificBuilderAttributes, - ) -> Result { + ) -> Result { let runner = target.runner.clone(); let id = target.generate_id(); @@ -70,11 +71,11 @@ impl Window { (self.register_redraw_request)(); } - pub fn outer_position(&self) -> Result, NotSupportedError> { + pub fn outer_position(&self) -> Result, Error> { Ok(self.canvas.position().to_physical(self.scale_factor())) } - pub fn inner_position(&self) -> Result, NotSupportedError> { + pub fn inner_position(&self) -> Result, Error> { // Note: the canvas element has no window decorations, so this is equal to `outer_position`. self.outer_position() } @@ -170,13 +171,13 @@ impl Window { } #[inline] - pub fn set_cursor_position(&self, _position: Position) -> Result<(), ExternalError> { + pub fn set_cursor_position(&self, _position: Position) -> Result<(), Error> { // Intentionally a no-op, as the web does not support setting cursor positions Ok(()) } #[inline] - pub fn set_cursor_grab(&self, _grab: bool) -> Result<(), ExternalError> { + pub fn set_cursor_grab(&self, _grab: bool) -> Result<(), Error> { // Intentionally a no-op, as the web does not (properly) support grabbing the cursor Ok(()) } @@ -211,12 +212,13 @@ impl Window { } #[inline] - pub fn set_fullscreen(&self, monitor: Option) { + pub fn set_fullscreen(&self, monitor: Option) -> Result<(), Error> { if monitor.is_some() { self.canvas.request_fullscreen(); } else if self.canvas.is_fullscreen() { backend::exit_fullscreen(); } + Ok(()) } #[inline] diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index f6c7a044d4..ddba761e69 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -217,10 +217,12 @@ impl FileDropHandler { } else if get_data_result == DV_E_FORMATETC { // If the dropped item is not a file this error will occur. // In this case it is OK to return without taking further action. - debug!("Error occured while processing dropped/hovered item: item is not a file."); + debug!( + "[winit] Error occured while processing dropped/hovered item: item is not a file." + ); return None; } else { - debug!("Unexpected error occured while processing dropped/hovered item."); + debug!("[winit] Unexpected error occured while processing dropped/hovered item."); return None; } } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 0049b09c89..616c297c5b 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -27,6 +27,7 @@ use std::{ time::{Duration, Instant}, }; use winapi::shared::basetsd::{DWORD_PTR, UINT_PTR}; +use winit_types::dpi::{PhysicalPosition, PhysicalSize}; use winapi::{ shared::{ @@ -43,7 +44,6 @@ use winapi::{ use self::runner::{ELRShared, EventLoopRunnerShared}; use crate::{ - dpi::{PhysicalPosition, PhysicalSize}, event::{DeviceEvent, Event, Force, KeyboardInput, Touch, TouchPhase, WindowEvent}, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, platform_impl::platform::{ @@ -133,7 +133,7 @@ macro_rules! main_thread_check { let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() }; if thread_id != main_thread_id() { panic!(concat!( - "Initializing the event loop outside of the main thread is a significant \ + "[winit] 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 `EventLoopExtWindows::", $fn_name, @@ -431,7 +431,10 @@ impl EventLoopThreadExecutor { raw as *mut () as usize as WPARAM, 0, ); - assert!(res != 0, "PostMessage failed ; is the messages queue full?"); + assert!( + res != 0, + "[winit] PostMessage failed ; is the messages queue full?" + ); } } } diff --git a/src/platform_impl/windows/event_loop/runner.rs b/src/platform_impl/windows/event_loop/runner.rs index 44a044d43a..79a22365d6 100644 --- a/src/platform_impl/windows/event_loop/runner.rs +++ b/src/platform_impl/windows/event_loop/runner.rs @@ -1,9 +1,9 @@ use std::{any::Any, cell::RefCell, collections::VecDeque, mem, panic, ptr, rc::Rc, time::Instant}; use winapi::{shared::windef::HWND, um::winuser}; +use winit_types::dpi::PhysicalSize; use crate::{ - dpi::PhysicalSize, event::{Event, StartCause, WindowEvent}, event_loop::ControlFlow, platform_impl::platform::{event_loop::EventLoop, util}, @@ -424,11 +424,11 @@ impl EventLoopRunner { self.call_event_handler(event); } (_, Event::RedrawRequested(_)) => { - panic!("redraw event in non-redraw phase"); + panic!("[winit] redraw event in non-redraw phase"); } (RunnerState::HandlingRedraw, _) => { panic!( - "Non-redraw event dispatched durning redraw phase: {:?}", + "[winit] Non-redraw event dispatched durning redraw phase: {:?}", event.map_nonuser_event::<()>().ok() ); } diff --git a/src/platform_impl/windows/icon.rs b/src/platform_impl/windows/icon.rs index c865053dbc..878ee4fd69 100644 --- a/src/platform_impl/windows/icon.rs +++ b/src/platform_impl/windows/icon.rs @@ -1,4 +1,4 @@ -use std::{io, mem, os::windows::ffi::OsStrExt, path::Path, ptr}; +use std::{io, mem, os::windows::ffi::OsStrExt, path::Path, ptr, sync::Arc}; use winapi::{ ctypes::{c_int, wchar_t}, @@ -11,6 +11,8 @@ use winapi::{ use crate::icon::{Icon, Pixel, PIXEL_SIZE}; +use winit_types::error::Error; + impl Pixel { fn to_bgra(&mut self) { mem::swap(&mut self.r, &mut self.b); @@ -32,7 +34,7 @@ unsafe impl Send for WinIcon {} impl WinIcon { #[allow(dead_code)] - pub fn from_path>(path: P) -> Result { + pub fn from_path>(path: P) -> Result { let wide_path: Vec = path.as_ref().as_os_str().encode_wide().collect(); let handle = unsafe { winuser::LoadImageW( @@ -47,15 +49,15 @@ impl WinIcon { if !handle.is_null() { Ok(WinIcon { handle }) } else { - Err(io::Error::last_os_error()) + Err(make_oserror!(Arc::new(io::Error::last_os_error()))) } } - pub fn from_icon(icon: Icon) -> Result { + pub fn from_icon(icon: Icon) -> Result { Self::from_rgba(icon.rgba, icon.width, icon.height) } - pub fn from_rgba(mut rgba: Vec, width: u32, height: u32) -> Result { + pub fn from_rgba(mut rgba: Vec, width: u32, height: u32) -> Result { assert_eq!(rgba.len() % PIXEL_SIZE, 0); let pixel_count = rgba.len() / PIXEL_SIZE; assert_eq!(pixel_count, (width * height) as usize); @@ -81,7 +83,7 @@ impl WinIcon { if !handle.is_null() { Ok(WinIcon { handle }) } else { - Err(io::Error::last_os_error()) + Err(make_oserror!(Arc::new(io::Error::last_os_error()))) } } diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 47bf7fb53f..c51d14c4e9 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -52,8 +52,6 @@ fn wrap_device_id(id: u32) -> RootDeviceId { RootDeviceId(DeviceId(id)) } -pub type OsError = std::io::Error; - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct WindowId(HWND); unsafe impl Send for WindowId {} diff --git a/src/platform_impl/windows/monitor.rs b/src/platform_impl/windows/monitor.rs index 7136f35dfe..c2c60c01ef 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -9,11 +9,14 @@ use winapi::{ use std::{ collections::{BTreeSet, VecDeque}, io, mem, ptr, + sync::Arc, }; +use winit_types::dpi::{PhysicalPosition, PhysicalSize}; +use winit_types::error::Error; + use super::{util, EventLoop}; use crate::{ - dpi::{PhysicalPosition, PhysicalSize}, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, platform_impl::platform::{ dpi::{dpi_to_scale_factor, get_monitor_dpi}, @@ -147,7 +150,7 @@ impl Window { } } -pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result { +pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result { let mut monitor_info: winuser::MONITORINFOEXW = unsafe { mem::zeroed() }; monitor_info.cbSize = mem::size_of::() as DWORD; let status = unsafe { @@ -157,7 +160,7 @@ pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result(bitset: T, flag: T) -> bool where T: Copy + PartialEq + BitAnd, @@ -48,11 +52,11 @@ pub unsafe fn status_map BOOL>(mut fun: F) -> Option { } } -fn win_to_err BOOL>(f: F) -> Result<(), io::Error> { +fn win_to_err BOOL>(f: F) -> Result<(), Error> { if f() != 0 { Ok(()) } else { - Err(io::Error::last_os_error()) + Err(make_oserror!(Arc::new(io::Error::last_os_error()))) } } @@ -60,7 +64,7 @@ pub fn get_window_rect(hwnd: HWND) -> Option { unsafe { status_map(|rect| winuser::GetWindowRect(hwnd, rect)) } } -pub fn get_client_rect(hwnd: HWND) -> Result { +pub fn get_client_rect(hwnd: HWND) -> Result { unsafe { let mut rect = mem::zeroed(); let mut top_left = mem::zeroed(); @@ -99,7 +103,7 @@ pub(crate) fn set_inner_size_physical(window: HWND, x: u32, y: u32) { right: x as LONG, }, ) - .expect("adjust_window_rect failed"); + .expect("[winit] adjust_window_rect failed"); let outer_x = (rect.right - rect.left).abs() as _; let outer_y = (rect.top - rect.bottom).abs() as _; @@ -159,7 +163,7 @@ pub fn set_cursor_hidden(hidden: bool) { } } -pub fn get_cursor_clip() -> Result { +pub fn get_cursor_clip() -> Result { unsafe { let mut rect: RECT = mem::zeroed(); win_to_err(|| winuser::GetClipCursor(&mut rect)).map(|_| rect) @@ -169,7 +173,7 @@ pub fn get_cursor_clip() -> Result { /// Sets the cursor's clip rect. /// /// Note that calling this will automatically dispatch a `WM_MOUSEMOVE` event. -pub fn set_cursor_clip(rect: Option) -> Result<(), io::Error> { +pub fn set_cursor_clip(rect: Option) -> Result<(), Error> { unsafe { let rect_ptr = rect .as_ref() diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index f3e7851a12..6acc3ec82a 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -2,6 +2,9 @@ use parking_lot::Mutex; use raw_window_handle::{windows::WindowsHandle, RawWindowHandle}; +use winit_types::dpi::{PhysicalPosition, PhysicalSize, Position, Size}; +use winit_types::error::Error; + use std::{ cell::Cell, ffi::OsStr, @@ -30,8 +33,6 @@ use winapi::{ }; use crate::{ - dpi::{PhysicalPosition, PhysicalSize, Position, Size}, - error::{ExternalError, NotSupportedError, OsError as RootOsError}, monitor::MonitorHandle as RootMonitorHandle, platform_impl::platform::{ dark_mode::try_dark_mode, @@ -63,7 +64,7 @@ impl Window { event_loop: &EventLoopWindowTarget, w_attr: WindowAttributes, pl_attr: PlatformSpecificWindowBuilderAttributes, - ) -> Result { + ) -> Result { // We dispatch an `init` function because of code style. // First person to remove the need for cloning here gets a cookie! // @@ -77,9 +78,9 @@ impl Window { // It is ok if the initialize result is `S_FALSE` because it might happen that // multiple windows are created on the same thread. if ole_init_result == OLE_E_WRONGCOMPOBJ { - panic!("OleInitialize failed! Result was: `OLE_E_WRONGCOMPOBJ`"); + panic!("[winit] OleInitialize failed! Result was: `OLE_E_WRONGCOMPOBJ`"); } else if ole_init_result == RPC_E_CHANGED_MODE { - panic!("OleInitialize failed! Result was: `RPC_E_CHANGED_MODE`"); + panic!("[winit] OleInitialize failed! Result was: `RPC_E_CHANGED_MODE`"); } let file_drop_runner = event_loop.runner_shared.clone(); @@ -147,17 +148,17 @@ impl Window { } #[inline] - pub fn outer_position(&self) -> Result, NotSupportedError> { + pub fn outer_position(&self) -> Result, Error> { util::get_window_rect(self.window.0) .map(|rect| Ok(PhysicalPosition::new(rect.left as i32, rect.top as i32))) - .expect("Unexpected GetWindowRect failure; please report this error to https://github.com/rust-windowing/winit") + .expect("[winit] Unexpected GetWindowRect failure; please report this error to https://github.com/rust-windowing/winit") } #[inline] - pub fn inner_position(&self) -> Result, NotSupportedError> { + pub fn inner_position(&self) -> Result, Error> { let mut position: POINT = unsafe { mem::zeroed() }; if unsafe { winuser::ClientToScreen(self.window.0, &mut position) } == 0 { - panic!("Unexpected ClientToScreen failure: please report this error to https://github.com/rust-windowing/winit") + panic!("[winit] Unexpected ClientToScreen failure: please report this error to https://github.com/rust-windowing/winit") } Ok(PhysicalPosition::new(position.x as i32, position.y as i32)) } @@ -195,7 +196,7 @@ impl Window { pub fn inner_size(&self) -> PhysicalSize { let mut rect: RECT = unsafe { mem::zeroed() }; if unsafe { winuser::GetClientRect(self.window.0, &mut rect) } == 0 { - panic!("Unexpected GetClientRect failure: please report this error to https://github.com/rust-windowing/winit") + panic!("[winit] Unexpected GetClientRect failure: please report this error to https://github.com/rust-windowing/winit") } PhysicalSize::new( (rect.right - rect.left) as u32, @@ -290,7 +291,7 @@ impl Window { } #[inline] - pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { + pub fn set_cursor_grab(&self, grab: bool) -> Result<(), Error> { let window = self.window.clone(); let window_state = Arc::clone(&self.window_state); let (tx, rx) = channel(); @@ -299,8 +300,7 @@ impl Window { let result = window_state .lock() .mouse - .set_cursor_flags(window.0, |f| f.set(CursorFlags::GRABBED, grab)) - .map_err(|e| ExternalError::Os(os_error!(e))); + .set_cursor_flags(window.0, |f| f.set(CursorFlags::GRABBED, grab)); let _ = tx.send(result); }); rx.recv().unwrap() @@ -329,17 +329,17 @@ impl Window { } #[inline] - pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> { + pub fn set_cursor_position(&self, position: Position) -> Result<(), Error> { let dpi_factor = self.scale_factor(); let (x, y) = position.to_physical::(dpi_factor).into(); let mut point = POINT { x, y }; unsafe { if winuser::ClientToScreen(self.window.0, &mut point) == 0 { - return Err(ExternalError::Os(os_error!(io::Error::last_os_error()))); + return Err(make_oserror!(Arc::new(io::Error::last_os_error()))); } if winuser::SetCursorPos(point.x, point.y) == 0 { - return Err(ExternalError::Os(os_error!(io::Error::last_os_error()))); + return Err(make_oserror!(Arc::new(io::Error::last_os_error()))); } } Ok(()) @@ -381,14 +381,14 @@ impl Window { } #[inline] - pub fn set_fullscreen(&self, fullscreen: Option) { + pub fn set_fullscreen(&self, fullscreen: Option) -> Result<(), Error> { let window = self.window.clone(); let window_state = Arc::clone(&self.window_state); let mut window_state_lock = window_state.lock(); let old_fullscreen = window_state_lock.fullscreen.clone(); if window_state_lock.fullscreen == fullscreen { - return; + return Ok(()); } window_state_lock.fullscreen = fullscreen.clone(); drop(window_state_lock); @@ -542,6 +542,8 @@ impl Window { taskbar_mark_fullscreen(window.0, fullscreen.is_some()); } }); + + Ok(()) } #[inline] @@ -579,7 +581,7 @@ impl Window { pub fn set_window_icon(&self, mut window_icon: Option) { let window_icon = window_icon .take() - .map(|icon| WinIcon::from_icon(icon).expect("Failed to create `ICON_SMALL`")); + .map(|icon| WinIcon::from_icon(icon).expect("[winit] Failed to create `ICON_SMALL`")); if let Some(ref window_icon) = window_icon { window_icon.set_for_window(self.window.0, IconType::Small); } else { @@ -592,7 +594,7 @@ impl Window { pub fn set_taskbar_icon(&self, mut taskbar_icon: Option) { let taskbar_icon = taskbar_icon .take() - .map(|icon| WinIcon::from_icon(icon).expect("Failed to create `ICON_BIG`")); + .map(|icon| WinIcon::from_icon(icon).expect("[winit] Failed to create `ICON_BIG`")); if let Some(ref taskbar_icon) = taskbar_icon { taskbar_icon.set_for_window(self.window.0, IconType::Big); } else { @@ -639,7 +641,7 @@ unsafe fn init( mut attributes: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, event_loop: &EventLoopWindowTarget, -) -> Result { +) -> Result { let title = OsStr::new(&attributes.title) .encode_wide() .chain(Some(0).into_iter()) @@ -648,7 +650,7 @@ unsafe fn init( let window_icon = { let icon = attributes.window_icon.take().map(WinIcon::from_icon); if let Some(icon) = icon { - Some(icon.map_err(|e| os_error!(e))?) + Some(icon?) } else { None } @@ -656,7 +658,7 @@ unsafe fn init( let taskbar_icon = { let icon = attributes.window_icon.take().map(WinIcon::from_icon); if let Some(icon) = icon { - Some(icon.map_err(|e| os_error!(e))?) + Some(icon?) } else { None } @@ -697,7 +699,7 @@ unsafe fn init( ); if handle.is_null() { - return Err(os_error!(io::Error::last_os_error())); + return Err(make_oserror!(Arc::new(io::Error::last_os_error()))); } WindowWrapper(handle) @@ -779,7 +781,7 @@ unsafe fn init( win.set_visible(attributes.visible); if let Some(_) = attributes.fullscreen { - win.set_fullscreen(attributes.fullscreen); + win.set_fullscreen(attributes.fullscreen)?; force_window_active(win.window.0); } diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 8150d1199a..601bbb73b8 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -1,10 +1,9 @@ use crate::{ - dpi::Size, platform_impl::platform::{event_loop, icon::WinIcon, util}, window::{CursorIcon, Fullscreen, WindowAttributes}, }; use parking_lot::MutexGuard; -use std::{io, ptr}; +use std::ptr; use winapi::{ shared::{ minwindef::DWORD, @@ -13,6 +12,9 @@ use winapi::{ um::winuser, }; +use winit_types::dpi::Size; +use winit_types::error::Error; + /// Contains information about states and the window that the callback is going to use. #[derive(Clone)] pub struct WindowState { @@ -154,7 +156,7 @@ impl MouseProperties { self.cursor_flags } - pub fn set_cursor_flags(&mut self, window: HWND, f: F) -> Result<(), io::Error> + pub fn set_cursor_flags(&mut self, window: HWND, f: F) -> Result<(), Error> where F: FnOnce(&mut CursorFlags), { @@ -330,7 +332,7 @@ impl WindowFlags { } impl CursorFlags { - fn refresh_os_cursor(self, window: HWND) -> Result<(), io::Error> { + fn refresh_os_cursor(self, window: HWND) -> Result<(), Error> { let client_rect = util::get_client_rect(window)?; if util::is_focused(window) { diff --git a/src/window.rs b/src/window.rs index 99644fee4b..ce3cdf142b 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,9 +1,9 @@ //! The `Window` struct and associated types. use std::fmt; +use winit_types::dpi::{PhysicalPosition, PhysicalSize, Position, Size}; +use winit_types::error::Error; use crate::{ - dpi::{PhysicalPosition, PhysicalSize, Position, Size}, - error::{ExternalError, NotSupportedError, OsError}, event_loop::EventLoopWindowTarget, monitor::{MonitorHandle, VideoMode}, platform_impl, @@ -54,7 +54,7 @@ impl Drop for Window { // closing the window doesn't necessarily always mean application exit, // such as when there are multiple windows) if let Some(Fullscreen::Exclusive(_)) = self.fullscreen() { - self.set_fullscreen(None); + self.set_fullscreen(None).unwrap(); } } } @@ -329,7 +329,7 @@ impl WindowBuilder { pub fn build( self, window_target: &EventLoopWindowTarget, - ) -> Result { + ) -> Result { platform_impl::Window::new(&window_target.p, self.window, self.platform_specific).map( |window| { window.request_redraw(); @@ -354,7 +354,7 @@ impl Window { /// /// [`WindowBuilder::new().build(event_loop)`]: crate::window::WindowBuilder::build #[inline] - pub fn new(event_loop: &EventLoopWindowTarget) -> Result { + pub fn new(event_loop: &EventLoopWindowTarget) -> Result { let builder = WindowBuilder::new(); builder.build(event_loop) } @@ -367,7 +367,7 @@ impl Window { /// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa. /// - /// See the [`dpi`](crate::dpi) module for more information. + /// See the `dpi` module in the `winit_types` crate for more information. /// /// Note that this value can change depending on user action (for example if the window is /// moved to another screen); as such, tracking `WindowEvent::ScaleFactorChanged` events is @@ -423,7 +423,7 @@ impl Window { /// /// [safe area]: https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc #[inline] - pub fn inner_position(&self) -> Result, NotSupportedError> { + pub fn inner_position(&self) -> Result, Error> { self.window.inner_position() } @@ -443,7 +443,7 @@ impl Window { /// window in the screen space coordinate system. /// - **Web:** Returns the top-left coordinates relative to the viewport. #[inline] - pub fn outer_position(&self) -> Result, NotSupportedError> { + pub fn outer_position(&self) -> Result, Error> { self.window.outer_position() } @@ -617,8 +617,10 @@ impl Window { /// - **iOS:** Can only be called on the main thread. /// - **Wayland:** Does not support exclusive fullscreen mode. /// - **Windows:** Screen saver is disabled in fullscreen mode. + /// - **X11:** Exclusive mode is only supported if the X Server supports + /// RandR. #[inline] - pub fn set_fullscreen(&self, fullscreen: Option) { + pub fn set_fullscreen(&self, fullscreen: Option) -> Result<(), Error> { self.window.set_fullscreen(fullscreen) } @@ -705,7 +707,7 @@ impl Window { /// - **iOS:** Always returns an `Err`. /// - **Web:** Has no effect. #[inline] - pub fn set_cursor_position>(&self, position: P) -> Result<(), ExternalError> { + pub fn set_cursor_position>(&self, position: P) -> Result<(), Error> { self.window.set_cursor_position(position.into()) } @@ -721,7 +723,7 @@ impl Window { /// - **iOS:** Always returns an Err. /// - **Web:** Has no effect. #[inline] - pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { + pub fn set_cursor_grab(&self, grab: bool) -> Result<(), Error> { self.window.set_cursor_grab(grab) } @@ -794,7 +796,7 @@ unsafe impl raw_window_handle::HasRawWindowHandle for Window { /// Describes the appearance of the mouse cursor. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde_feature", derive(Serialize, Deserialize))] pub enum CursorIcon { /// The platform-dependent default cursor. Default, diff --git a/tests/serde_objects.rs b/tests/serde_objects.rs index ad729dcd1b..7533d23bb1 100644 --- a/tests/serde_objects.rs +++ b/tests/serde_objects.rs @@ -1,8 +1,7 @@ -#![cfg(feature = "serde")] +#![cfg(feature = "serde_feature")] use serde::{Deserialize, Serialize}; use winit::{ - dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}, event::{ ElementState, KeyboardInput, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode, @@ -28,12 +27,3 @@ fn events_serde() { needs_serde::(); needs_serde::(); } - -#[test] -fn dpi_serde() { - needs_serde::>(); - needs_serde::>(); - needs_serde::>(); - needs_serde::>(); - needs_serde::>(); -}