mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-26 03:36:32 +11:00
DPI for everyone (#548)
This commit is contained in:
parent
f083dae328
commit
1b74822cfc
41 changed files with 3096 additions and 1663 deletions
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -1,6 +1,17 @@
|
||||||
# Unreleased
|
# Unreleased
|
||||||
|
|
||||||
- **Breaking:** Removed `VirtualKeyCode::LMenu` and `VirtualKeyCode::RMenu`; Windows now generates `VirtualKeyCode::LAlt` and `VirtualKeyCode::RAlt` instead.
|
- **Breaking:** Removed `VirtualKeyCode::LMenu` and `VirtualKeyCode::RMenu`; Windows now generates `VirtualKeyCode::LAlt` and `VirtualKeyCode::RAlt` instead.
|
||||||
|
- On X11, exiting fullscreen no longer leaves the window in the monitor's top left corner.
|
||||||
|
- **Breaking:** `Window::hidpi_factor` has been renamed to `Window::get_hidpi_factor` for better consistency. `WindowEvent::HiDPIFactorChanged` has been renamed to `WindowEvent::HiDpiFactorChanged`. DPI factors are always represented as `f64` instead of `f32` now.
|
||||||
|
- The Windows backend is now DPI aware. `WindowEvent::HiDpiFactorChanged` is implemented, and `MonitorId::get_hidpi_factor` and `Window::hidpi_factor` return accurate values.
|
||||||
|
- Implemented `WindowEvent::HiDpiFactorChanged` on X11.
|
||||||
|
- On macOS, `Window::set_cursor_position` is now relative to the client area.
|
||||||
|
- On macOS, setting the maximum and minimum dimensions now applies to the client area dimensions rather than to the window dimensions.
|
||||||
|
- On iOS, `MonitorId::get_dimensions` has been implemented and both `MonitorId::get_hidpi_factor` and `Window::get_hidpi_factor` return accurate values.
|
||||||
|
- On Emscripten, `MonitorId::get_hidpi_factor` now returns the same value as `Window::get_hidpi_factor` (it previously would always return 1.0).
|
||||||
|
- **Breaking:** The entire API for sizes, positions, etc. has changed. In the majority of cases, winit produces and consumes positions and sizes as `LogicalPosition` and `LogicalSize`, respectively. The notable exception is `MonitorId` methods, which deal in `PhysicalPosition` and `PhysicalSize`. See the documentation for specifics and explanations of the types. Additionally, winit automatically conserves logical size when the DPI factor changes.
|
||||||
|
- **Breaking:** All deprecated methods have been removed. For `Window::platform_display` and `Window::platform_window`, switch to the appropriate platform-specific `WindowExt` methods. For `Window::get_inner_size_points` and `Window::get_inner_size_pixels`, use the `LogicalSize` returned by `Window::get_inner_size` and convert as needed.
|
||||||
|
- HiDPI support for Wayland.
|
||||||
|
|
||||||
# Version 0.15.1 (2018-06-13)
|
# Version 0.15.1 (2018-06-13)
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ features = [
|
||||||
"objbase",
|
"objbase",
|
||||||
"processthreadsapi",
|
"processthreadsapi",
|
||||||
"shellapi",
|
"shellapi",
|
||||||
|
"shellscalingapi",
|
||||||
"shobjidl_core",
|
"shobjidl_core",
|
||||||
"unknwnbase",
|
"unknwnbase",
|
||||||
"windowsx",
|
"windowsx",
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
extern crate winit;
|
extern crate winit;
|
||||||
|
|
||||||
|
use winit::dpi::LogicalSize;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut events_loop = winit::EventsLoop::new();
|
let mut events_loop = winit::EventsLoop::new();
|
||||||
|
|
||||||
|
@ -7,8 +9,8 @@ fn main() {
|
||||||
.build(&events_loop)
|
.build(&events_loop)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
window.set_min_dimensions(Some((400, 200)));
|
window.set_min_dimensions(Some(LogicalSize::new(400.0, 200.0)));
|
||||||
window.set_max_dimensions(Some((800, 400)));
|
window.set_max_dimensions(Some(LogicalSize::new(800.0, 400.0)));
|
||||||
|
|
||||||
events_loop.run_forever(|event| {
|
events_loop.run_forever(|event| {
|
||||||
println!("{:?}", event);
|
println!("{:?}", event);
|
||||||
|
|
|
@ -7,7 +7,7 @@ fn main() {
|
||||||
|
|
||||||
let window = winit::WindowBuilder::new()
|
let window = winit::WindowBuilder::new()
|
||||||
.with_title("Hit space to toggle resizability.")
|
.with_title("Hit space to toggle resizability.")
|
||||||
.with_dimensions(400, 200)
|
.with_dimensions((400, 200).into())
|
||||||
.with_resizable(resizable)
|
.with_resizable(resizable)
|
||||||
.build(&events_loop)
|
.build(&events_loop)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
307
src/dpi.rs
Normal file
307
src/dpi.rs
Normal file
|
@ -0,0 +1,307 @@
|
||||||
|
|
||||||
|
//! DPI is important, so read the docs for this module if you don't want to be confused.
|
||||||
|
//!
|
||||||
|
//! Originally, `winit` dealt entirely in physical pixels (excluding unintentional inconsistencies), but now all
|
||||||
|
//! window-related functions both produce and consume logical pixels. Monitor-related functions still use physical
|
||||||
|
//! pixels, as do any context-related functions in `glutin`.
|
||||||
|
//!
|
||||||
|
//! If you've never heard of these terms before, then you're not alone, and this documentation will explain the
|
||||||
|
//! concepts.
|
||||||
|
//!
|
||||||
|
//! Modern screens have a defined physical resolution, most commonly 1920x1080. Indepedent of that is the amount of
|
||||||
|
//! space the screen occupies, which is to say, the height and width in millimeters. The relationship between these two
|
||||||
|
//! measurements is the *pixel density*. Mobile screens require a high pixel density, as they're held close to the
|
||||||
|
//! eyes. Larger displays also require a higher pixel density, hence the growing presence of 1440p and 4K displays.
|
||||||
|
//!
|
||||||
|
//! So, this presents a problem. Let's say we want to render a square 100px button. It will occupy 100x100 of the
|
||||||
|
//! screen's pixels, which in many cases, seems perfectly fine. However, because this size doesn't account for the
|
||||||
|
//! screen's dimensions or pixel density, the button's size can vary quite a bit. On a 4K display, it would be unusably
|
||||||
|
//! small.
|
||||||
|
//!
|
||||||
|
//! That's a description of what happens when the button is 100x100 *physical* pixels. Instead, let's try using 100x100
|
||||||
|
//! *logical* pixels. To map logical pixels to physical pixels, we simply multiply by the DPI factor. On a "typical"
|
||||||
|
//! desktop display, the DPI factor will be 1.0, so 100x100 logical pixels equates to 100x100 physical pixels. However,
|
||||||
|
//! a 1440p display may have a DPI factor of 1.25, so the button is rendered as 125x125 physical pixels. Ideally, the
|
||||||
|
//! button now has approximately the same perceived size across varying displays.
|
||||||
|
//!
|
||||||
|
//! Failure to account for the DPI factor can create a badly 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 entering an existential panic. Once users enter that state, they will no longer be focused on your application.
|
||||||
|
//!
|
||||||
|
//! There are two ways to get the DPI factor: either by calling
|
||||||
|
//! [`MonitorId::get_hidpi_factor`](../struct.MonitorId.html#method.get_hidpi_factor), or
|
||||||
|
//! [`Window::get_hidpi_factor`](../struct.Window.html#method.get_hidpi_factor). You'll almost always use the latter,
|
||||||
|
//! which is basically equivalent to `window.get_current_monitor().get_hidpi_factor()` anyway.
|
||||||
|
//!
|
||||||
|
//! Here's an overview of what sort of DPI factors you can expect, and where they come from:
|
||||||
|
//! - **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" DPI factors, i.e.
|
||||||
|
//! 1.0, 1.25, 1.5... on Windows 7, the DPI factor is global and changing it requires logging out.
|
||||||
|
//! - **macOS:** The buzzword is "retina displays", which have a DPI factor of 2.0. Otherwise, the DPI factor is 1.0.
|
||||||
|
//! Intermediate DPI factors are never used, thus 1440p displays/etc. aren't properly supported. It's possible for any
|
||||||
|
//! display to use that 2.0 DPI factor, given the use of the command line.
|
||||||
|
//! - **X11:** On X11, we calcuate the DPI factor based on the millimeter dimensions provided by XRandR. This can
|
||||||
|
//! result in a wide range of possible values, including some interesting ones like 1.0833333333333333. This can be
|
||||||
|
//! overridden using the `WINIT_HIDPI_FACTOR` environment variable, though that's not recommended.
|
||||||
|
//! - **Wayland:** On Wayland, DPI factors are very much at the discretion of the user.
|
||||||
|
//! - **iOS:** DPI factors are both constant and device-specific on iOS.
|
||||||
|
//! - **Android:** This feature isn't yet implemented on Android, so the DPI factor will always be returned as 1.0.
|
||||||
|
//!
|
||||||
|
//! The window's logical size is conserved across DPI changes, resulting in the physical size changing instead. This
|
||||||
|
//! may be surprising on X11, but is quite standard elsewhere. Physical size changes produce a
|
||||||
|
//! [`Resized`](../enum.WindowEvent.html#variant.Resized) event, even on platforms where no resize actually occurs,
|
||||||
|
//! such as macOS and Wayland. As a result, it's not necessary to separately handle
|
||||||
|
//! [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) if you're only listening for size.
|
||||||
|
//!
|
||||||
|
//! Your GPU has no awareness of the concept of logical pixels, and unless you like wasting pixel density, your
|
||||||
|
//! framebuffer's size should be in physical pixels.
|
||||||
|
|
||||||
|
/// Checks that the DPI factor is a normal positive `f64`.
|
||||||
|
///
|
||||||
|
/// All functions that take a DPI factor assert that this will return `true`. If you're sourcing DPI 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_hidpi_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)]
|
||||||
|
pub struct LogicalPosition {
|
||||||
|
pub x: f64,
|
||||||
|
pub y: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LogicalPosition {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(x: f64, y: f64) -> Self {
|
||||||
|
LogicalPosition { x, y }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn from_physical<T: Into<PhysicalPosition>>(physical: T, dpi_factor: f64) -> Self {
|
||||||
|
physical.into().to_logical(dpi_factor)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn to_physical(&self, dpi_factor: f64) -> PhysicalPosition {
|
||||||
|
assert!(validate_hidpi_factor(dpi_factor));
|
||||||
|
let x = self.x * dpi_factor;
|
||||||
|
let y = self.y * dpi_factor;
|
||||||
|
PhysicalPosition::new(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(f64, f64)> for LogicalPosition {
|
||||||
|
#[inline]
|
||||||
|
fn from((x, y): (f64, f64)) -> Self {
|
||||||
|
Self::new(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(i32, i32)> for LogicalPosition {
|
||||||
|
#[inline]
|
||||||
|
fn from((x, y): (i32, i32)) -> Self {
|
||||||
|
Self::new(x as f64, y as f64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<(f64, f64)> for LogicalPosition {
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> (f64, f64) {
|
||||||
|
(self.x, self.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<(i32, i32)> for LogicalPosition {
|
||||||
|
/// Note that this rounds instead of truncating.
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> (i32, i32) {
|
||||||
|
(self.x.round() as _, self.y.round() as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A position represented in physical 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)]
|
||||||
|
pub struct PhysicalPosition {
|
||||||
|
pub x: f64,
|
||||||
|
pub y: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PhysicalPosition {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(x: f64, y: f64) -> Self {
|
||||||
|
PhysicalPosition { x, y }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn from_logical<T: Into<LogicalPosition>>(logical: T, dpi_factor: f64) -> Self {
|
||||||
|
logical.into().to_physical(dpi_factor)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn to_logical(&self, dpi_factor: f64) -> LogicalPosition {
|
||||||
|
assert!(validate_hidpi_factor(dpi_factor));
|
||||||
|
let x = self.x / dpi_factor;
|
||||||
|
let y = self.y / dpi_factor;
|
||||||
|
LogicalPosition::new(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(f64, f64)> for PhysicalPosition {
|
||||||
|
#[inline]
|
||||||
|
fn from((x, y): (f64, f64)) -> Self {
|
||||||
|
Self::new(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(i32, i32)> for PhysicalPosition {
|
||||||
|
#[inline]
|
||||||
|
fn from((x, y): (i32, i32)) -> Self {
|
||||||
|
Self::new(x as f64, y as f64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<(f64, f64)> for PhysicalPosition {
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> (f64, f64) {
|
||||||
|
(self.x, self.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<(i32, i32)> for PhysicalPosition {
|
||||||
|
/// Note that this rounds instead of truncating.
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> (i32, i32) {
|
||||||
|
(self.x.round() as _, self.y.round() as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A size represented in logical pixels.
|
||||||
|
///
|
||||||
|
/// The size 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<(u32, u32)>` implementation is provided which
|
||||||
|
/// does the rounding for you.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub struct LogicalSize {
|
||||||
|
pub width: f64,
|
||||||
|
pub height: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LogicalSize {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(width: f64, height: f64) -> Self {
|
||||||
|
LogicalSize { width, height }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn from_physical<T: Into<PhysicalSize>>(physical: T, dpi_factor: f64) -> Self {
|
||||||
|
physical.into().to_logical(dpi_factor)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn to_physical(&self, dpi_factor: f64) -> PhysicalSize {
|
||||||
|
assert!(validate_hidpi_factor(dpi_factor));
|
||||||
|
let width = self.width * dpi_factor;
|
||||||
|
let height = self.height * dpi_factor;
|
||||||
|
PhysicalSize::new(width, height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(f64, f64)> for LogicalSize {
|
||||||
|
#[inline]
|
||||||
|
fn from((width, height): (f64, f64)) -> Self {
|
||||||
|
Self::new(width, height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(u32, u32)> for LogicalSize {
|
||||||
|
#[inline]
|
||||||
|
fn from((width, height): (u32, u32)) -> Self {
|
||||||
|
Self::new(width as f64, height as f64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<(f64, f64)> for LogicalSize {
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> (f64, f64) {
|
||||||
|
(self.width, self.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<(u32, u32)> for LogicalSize {
|
||||||
|
/// Note that this rounds instead of truncating.
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> (u32, u32) {
|
||||||
|
(self.width.round() as _, self.height.round() as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A size represented in physical pixels.
|
||||||
|
///
|
||||||
|
/// The size 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<(u32, u32)>` implementation is provided which
|
||||||
|
/// does the rounding for you.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub struct PhysicalSize {
|
||||||
|
pub width: f64,
|
||||||
|
pub height: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PhysicalSize {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(width: f64, height: f64) -> Self {
|
||||||
|
PhysicalSize { width, height }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn from_logical<T: Into<LogicalSize>>(logical: T, dpi_factor: f64) -> Self {
|
||||||
|
logical.into().to_physical(dpi_factor)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn to_logical(&self, dpi_factor: f64) -> LogicalSize {
|
||||||
|
assert!(validate_hidpi_factor(dpi_factor));
|
||||||
|
let width = self.width / dpi_factor;
|
||||||
|
let height = self.height / dpi_factor;
|
||||||
|
LogicalSize::new(width, height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(f64, f64)> for PhysicalSize {
|
||||||
|
#[inline]
|
||||||
|
fn from((width, height): (f64, f64)) -> Self {
|
||||||
|
Self::new(width, height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(u32, u32)> for PhysicalSize {
|
||||||
|
#[inline]
|
||||||
|
fn from((width, height): (u32, u32)) -> Self {
|
||||||
|
Self::new(width as f64, height as f64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<(f64, f64)> for PhysicalSize {
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> (f64, f64) {
|
||||||
|
(self.width, self.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<(u32, u32)> for PhysicalSize {
|
||||||
|
/// Note that this rounds instead of truncating.
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> (u32, u32) {
|
||||||
|
(self.width.round() as _, self.height.round() as _)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use {WindowId, DeviceId};
|
|
||||||
|
use {DeviceId, LogicalPosition, LogicalSize, WindowId};
|
||||||
|
|
||||||
/// Describes a generic event.
|
/// Describes a generic event.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -23,12 +24,11 @@ pub enum Event {
|
||||||
/// Describes an event from a `Window`.
|
/// Describes an event from a `Window`.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum WindowEvent {
|
pub enum WindowEvent {
|
||||||
|
|
||||||
/// The size of the window has changed. Contains the client area's new dimensions.
|
/// The size of the window has changed. Contains the client area's new dimensions.
|
||||||
Resized(u32, u32),
|
Resized(LogicalSize),
|
||||||
|
|
||||||
/// The position of the window has changed. Contains the window's new position.
|
/// The position of the window has changed. Contains the window's new position.
|
||||||
Moved(i32, i32),
|
Moved(LogicalPosition),
|
||||||
|
|
||||||
/// The window has been requested to close.
|
/// The window has been requested to close.
|
||||||
CloseRequested,
|
CloseRequested,
|
||||||
|
@ -63,7 +63,7 @@ pub enum WindowEvent {
|
||||||
/// (x,y) coords in pixels relative to the top-left corner of the window. Because the range of this data is
|
/// (x,y) coords in pixels relative to the top-left corner of the window. Because the range of this data is
|
||||||
/// limited by the display area and it may have been transformed by the OS to implement effects such as cursor
|
/// limited by the display area and it may have been transformed by the OS to implement effects such as cursor
|
||||||
/// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control.
|
/// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control.
|
||||||
position: (f64, f64),
|
position: LogicalPosition,
|
||||||
modifiers: ModifiersState
|
modifiers: ModifiersState
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -96,14 +96,16 @@ pub enum WindowEvent {
|
||||||
/// Touch event has been received
|
/// Touch event has been received
|
||||||
Touch(Touch),
|
Touch(Touch),
|
||||||
|
|
||||||
/// DPI scaling factor of the window has changed.
|
/// The DPI factor of the window has changed.
|
||||||
///
|
///
|
||||||
/// The following actions cause DPI changes:
|
/// The following user actions can cause DPI changes:
|
||||||
///
|
///
|
||||||
/// * A user changes the resolution.
|
/// * Changing the display's resolution.
|
||||||
/// * A user changes the desktop scaling value (e.g. in Control Panel on Windows).
|
/// * Changing the display's DPI factor (e.g. in Control Panel on Windows).
|
||||||
/// * A user moves the application window to a display with a different DPI.
|
/// * Moving the window to a display with a different DPI factor.
|
||||||
HiDPIFactorChanged(f32),
|
///
|
||||||
|
/// For more information about DPI in general, see the [`dpi`](dpi/index.html) module.
|
||||||
|
HiDpiFactorChanged(f64),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents raw hardware events that are not associated with any particular window.
|
/// Represents raw hardware events that are not associated with any particular window.
|
||||||
|
@ -197,7 +199,7 @@ pub enum TouchPhase {
|
||||||
pub struct Touch {
|
pub struct Touch {
|
||||||
pub device_id: DeviceId,
|
pub device_id: DeviceId,
|
||||||
pub phase: TouchPhase,
|
pub phase: TouchPhase,
|
||||||
pub location: (f64,f64),
|
pub location: LogicalPosition,
|
||||||
/// unique identifier of a finger.
|
/// unique identifier of a finger.
|
||||||
pub id: u64
|
pub id: u64
|
||||||
}
|
}
|
||||||
|
@ -242,7 +244,7 @@ pub enum MouseScrollDelta {
|
||||||
/// Scroll events are expressed as a PixelDelta if
|
/// Scroll events are expressed as a PixelDelta if
|
||||||
/// supported by the device (eg. a touchpad) and
|
/// supported by the device (eg. a touchpad) and
|
||||||
/// platform.
|
/// platform.
|
||||||
PixelDelta(f32, f32)
|
PixelDelta(LogicalPosition),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Symbolic name for a keyboard key.
|
/// Symbolic name for a keyboard key.
|
||||||
|
|
28
src/lib.rs
28
src/lib.rs
|
@ -35,14 +35,18 @@
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use winit::{Event, WindowEvent};
|
//! use winit::{Event, WindowEvent};
|
||||||
|
//! use winit::dpi::LogicalSize;
|
||||||
//! # use winit::EventsLoop;
|
//! # use winit::EventsLoop;
|
||||||
//! # let mut events_loop = EventsLoop::new();
|
//! # let mut events_loop = EventsLoop::new();
|
||||||
//!
|
//!
|
||||||
//! loop {
|
//! loop {
|
||||||
//! events_loop.poll_events(|event| {
|
//! events_loop.poll_events(|event| {
|
||||||
//! match event {
|
//! match event {
|
||||||
//! Event::WindowEvent { event: WindowEvent::Resized(w, h), .. } => {
|
//! Event::WindowEvent {
|
||||||
//! println!("The window was resized to {}x{}", w, h);
|
//! event: WindowEvent::Resized(LogicalSize { width, height }),
|
||||||
|
//! ..
|
||||||
|
//! } => {
|
||||||
|
//! println!("The window was resized to {}x{}", width, height);
|
||||||
//! },
|
//! },
|
||||||
//! _ => ()
|
//! _ => ()
|
||||||
//! }
|
//! }
|
||||||
|
@ -80,6 +84,7 @@
|
||||||
//! to create an OpenGL/Vulkan/DirectX/Metal/etc. context that will draw on the window.
|
//! to create an OpenGL/Vulkan/DirectX/Metal/etc. context that will draw on the window.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
@ -106,14 +111,16 @@ extern crate percent_encoding;
|
||||||
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
|
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
|
||||||
extern crate smithay_client_toolkit as sctk;
|
extern crate smithay_client_toolkit as sctk;
|
||||||
|
|
||||||
|
pub(crate) use dpi::*; // TODO: Actually change the imports throughout the codebase.
|
||||||
pub use events::*;
|
pub use events::*;
|
||||||
pub use window::{AvailableMonitorsIter, MonitorId};
|
pub use window::{AvailableMonitorsIter, MonitorId};
|
||||||
pub use icon::*;
|
pub use icon::*;
|
||||||
|
|
||||||
mod platform;
|
pub mod dpi;
|
||||||
mod events;
|
mod events;
|
||||||
mod window;
|
|
||||||
mod icon;
|
mod icon;
|
||||||
|
mod platform;
|
||||||
|
mod window;
|
||||||
|
|
||||||
pub mod os;
|
pub mod os;
|
||||||
|
|
||||||
|
@ -227,6 +234,11 @@ impl EventsLoop {
|
||||||
/// Calls `callback` every time an event is received. If no event is available, sleeps the
|
/// Calls `callback` every time an event is received. If no event is available, sleeps the
|
||||||
/// current thread and waits for an event. If the callback returns `ControlFlow::Break` then
|
/// current thread and waits for an event. If the callback returns `ControlFlow::Break` then
|
||||||
/// `run_forever` will immediately return.
|
/// `run_forever` will immediately return.
|
||||||
|
///
|
||||||
|
/// # Danger!
|
||||||
|
///
|
||||||
|
/// The callback is run after *every* event, so if its execution time is non-trivial the event queue may not empty
|
||||||
|
/// at a sufficient rate. Rendering in the callback with vsync enabled **will** cause significant lag.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn run_forever<F>(&mut self, callback: F)
|
pub fn run_forever<F>(&mut self, callback: F)
|
||||||
where F: FnMut(Event) -> ControlFlow
|
where F: FnMut(Event) -> ControlFlow
|
||||||
|
@ -403,23 +415,23 @@ impl Default for CursorState {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attributes to use when creating a window.
|
/// Attributes to use when creating a window.
|
||||||
#[derive(Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct WindowAttributes {
|
pub struct WindowAttributes {
|
||||||
/// The dimensions of the window. If this is `None`, some platform-specific dimensions will be
|
/// The dimensions of the window. If this is `None`, some platform-specific dimensions will be
|
||||||
/// used.
|
/// used.
|
||||||
///
|
///
|
||||||
/// The default is `None`.
|
/// The default is `None`.
|
||||||
pub dimensions: Option<(u32, u32)>,
|
pub dimensions: Option<LogicalSize>,
|
||||||
|
|
||||||
/// The minimum dimensions a window can be, If this is `None`, the window will have no minimum dimensions (aside from reserved).
|
/// The minimum dimensions a window can be, If this is `None`, the window will have no minimum dimensions (aside from reserved).
|
||||||
///
|
///
|
||||||
/// The default is `None`.
|
/// The default is `None`.
|
||||||
pub min_dimensions: Option<(u32, u32)>,
|
pub min_dimensions: Option<LogicalSize>,
|
||||||
|
|
||||||
/// The maximum dimensions a window can be, If this is `None`, the maximum will have no maximum or will be set to the primary monitor's dimensions by the platform.
|
/// The maximum dimensions a window can be, If this is `None`, the maximum will have no maximum or will be set to the primary monitor's dimensions by the platform.
|
||||||
///
|
///
|
||||||
/// The default is `None`.
|
/// The default is `None`.
|
||||||
pub max_dimensions: Option<(u32, u32)>,
|
pub max_dimensions: Option<LogicalSize>,
|
||||||
|
|
||||||
/// Whether the window is resizable or not.
|
/// Whether the window is resizable or not.
|
||||||
///
|
///
|
||||||
|
|
43
src/os/ios.rs
Normal file
43
src/os/ios.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#![cfg(target_os = "ios")]
|
||||||
|
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
|
use {MonitorId, Window};
|
||||||
|
|
||||||
|
/// Additional methods on `Window` that are specific to iOS.
|
||||||
|
pub trait WindowExt {
|
||||||
|
/// Returns a pointer to the `UIWindow` that is used by this window.
|
||||||
|
///
|
||||||
|
/// The pointer will become invalid when the `Window` is destroyed.
|
||||||
|
fn get_uiwindow(&self) -> *mut c_void;
|
||||||
|
|
||||||
|
/// Returns a pointer to the `UIView` that is used by this window.
|
||||||
|
///
|
||||||
|
/// The pointer will become invalid when the `Window` is destroyed.
|
||||||
|
fn get_uiview(&self) -> *mut c_void;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowExt for Window {
|
||||||
|
#[inline]
|
||||||
|
fn get_uiwindow(&self) -> *mut c_void {
|
||||||
|
self.window.get_uiwindow() as _
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_uiview(&self) -> *mut c_void {
|
||||||
|
self.window.get_uiview() as _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Additional methods on `MonitorId` that are specific to iOS.
|
||||||
|
pub trait MonitorIdExt {
|
||||||
|
/// Returns a pointer to the `UIScreen` that is used by this monitor.
|
||||||
|
fn get_uiscreen(&self) -> *mut c_void;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MonitorIdExt for MonitorId {
|
||||||
|
#[inline]
|
||||||
|
fn get_uiscreen(&self) -> *mut c_void {
|
||||||
|
self.inner.get_uiscreen() as _
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use cocoa::appkit::NSApplicationActivationPolicy;
|
use cocoa::appkit::NSApplicationActivationPolicy;
|
||||||
use {MonitorId, Window, WindowBuilder};
|
use {LogicalSize, MonitorId, Window, WindowBuilder};
|
||||||
|
|
||||||
/// Additional methods on `Window` that are specific to MacOS.
|
/// Additional methods on `Window` that are specific to MacOS.
|
||||||
pub trait WindowExt {
|
pub trait WindowExt {
|
||||||
|
@ -86,7 +86,7 @@ pub trait WindowBuilderExt {
|
||||||
/// Makes the window content appear behind the titlebar.
|
/// Makes the window content appear behind the titlebar.
|
||||||
fn with_fullsize_content_view(self, fullsize_content_view: bool) -> WindowBuilder;
|
fn with_fullsize_content_view(self, fullsize_content_view: bool) -> WindowBuilder;
|
||||||
/// Build window with `resizeIncrements` property. Values must not be 0.
|
/// Build window with `resizeIncrements` property. Values must not be 0.
|
||||||
fn with_resize_increments(self, width_inc: u32, height_inc: u32) -> WindowBuilder;
|
fn with_resize_increments(self, increments: LogicalSize) -> WindowBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowBuilderExt for WindowBuilder {
|
impl WindowBuilderExt for WindowBuilder {
|
||||||
|
@ -133,8 +133,8 @@ impl WindowBuilderExt for WindowBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_resize_increments(mut self, width_inc: u32, height_inc: u32) -> WindowBuilder {
|
fn with_resize_increments(mut self, increments: LogicalSize) -> WindowBuilder {
|
||||||
self.platform_specific.resize_increments = Some((width_inc, height_inc));
|
self.platform_specific.resize_increments = Some(increments.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
//! Contains the follow modules:
|
//! Contains the follow modules:
|
||||||
//!
|
//!
|
||||||
//! - `android`
|
//! - `android`
|
||||||
|
//! - `ios`
|
||||||
//! - `macos`
|
//! - `macos`
|
||||||
//! - `unix`
|
//! - `unix`
|
||||||
//! - `windows`
|
//! - `windows`
|
||||||
|
@ -10,6 +11,7 @@
|
||||||
//! However only the module corresponding to the platform you're compiling to will be available.
|
//! However only the module corresponding to the platform you're compiling to will be available.
|
||||||
//!
|
//!
|
||||||
pub mod android;
|
pub mod android;
|
||||||
|
pub mod ios;
|
||||||
pub mod macos;
|
pub mod macos;
|
||||||
pub mod unix;
|
pub mod unix;
|
||||||
pub mod windows;
|
pub mod windows;
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||||
|
|
||||||
use std::os::raw;
|
use std::os::raw;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use EventsLoop;
|
use std::sync::Arc;
|
||||||
use MonitorId;
|
|
||||||
use Window;
|
use {
|
||||||
use platform::EventsLoop as LinuxEventsLoop;
|
EventsLoop,
|
||||||
use platform::Window as LinuxWindow;
|
LogicalSize,
|
||||||
use WindowBuilder;
|
MonitorId,
|
||||||
|
Window,
|
||||||
|
WindowBuilder,
|
||||||
|
};
|
||||||
|
use platform::{
|
||||||
|
EventsLoop as LinuxEventsLoop,
|
||||||
|
Window as LinuxWindow,
|
||||||
|
};
|
||||||
use platform::x11::XConnection;
|
use platform::x11::XConnection;
|
||||||
use platform::x11::ffi::XVisualInfo;
|
use platform::x11::ffi::XVisualInfo;
|
||||||
|
|
||||||
|
@ -72,6 +78,7 @@ impl EventsLoopExt for EventsLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[doc(hidden)]
|
||||||
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> {
|
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> {
|
||||||
self.events_loop.x_connection().cloned()
|
self.events_loop.x_connection().cloned()
|
||||||
}
|
}
|
||||||
|
@ -93,6 +100,7 @@ pub trait WindowExt {
|
||||||
|
|
||||||
fn get_xlib_screen_id(&self) -> Option<raw::c_int>;
|
fn get_xlib_screen_id(&self) -> Option<raw::c_int>;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>>;
|
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>>;
|
||||||
|
|
||||||
/// Set window urgency hint (`XUrgencyHint`). Only relevant on X.
|
/// Set window urgency hint (`XUrgencyHint`). Only relevant on X.
|
||||||
|
@ -155,6 +163,7 @@ impl WindowExt for Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[doc(hidden)]
|
||||||
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> {
|
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> {
|
||||||
match self.window {
|
match self.window {
|
||||||
LinuxWindow::X(ref w) => Some(w.get_xlib_xconnection()),
|
LinuxWindow::X(ref w) => Some(w.get_xlib_xconnection()),
|
||||||
|
@ -211,9 +220,9 @@ pub trait WindowBuilderExt {
|
||||||
/// Build window with `_NET_WM_WINDOW_TYPE` hint; defaults to `Normal`. Only relevant on X11.
|
/// Build window with `_NET_WM_WINDOW_TYPE` hint; defaults to `Normal`. Only relevant on X11.
|
||||||
fn with_x11_window_type(self, x11_window_type: XWindowType) -> WindowBuilder;
|
fn with_x11_window_type(self, x11_window_type: XWindowType) -> WindowBuilder;
|
||||||
/// Build window with resize increment hint. Only implemented on X11.
|
/// Build window with resize increment hint. Only implemented on X11.
|
||||||
fn with_resize_increments(self, width_inc: u32, height_inc: u32) -> WindowBuilder;
|
fn with_resize_increments(self, increments: LogicalSize) -> WindowBuilder;
|
||||||
/// Build window with base size hint. Only implemented on X11.
|
/// Build window with base size hint. Only implemented on X11.
|
||||||
fn with_base_size(self, base_width: u32, base_height: u32) -> WindowBuilder;
|
fn with_base_size(self, base_size: LogicalSize) -> WindowBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowBuilderExt for WindowBuilder {
|
impl WindowBuilderExt for WindowBuilder {
|
||||||
|
@ -250,14 +259,14 @@ impl WindowBuilderExt for WindowBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_resize_increments(mut self, width_inc: u32, height_inc: u32) -> WindowBuilder {
|
fn with_resize_increments(mut self, increments: LogicalSize) -> WindowBuilder {
|
||||||
self.platform_specific.resize_increments = Some((width_inc, height_inc));
|
self.platform_specific.resize_increments = Some(increments.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_base_size(mut self, base_width: u32, base_height: u32) -> WindowBuilder {
|
fn with_base_size(mut self, base_size: LogicalSize) -> WindowBuilder {
|
||||||
self.platform_specific.base_size = Some((base_width, base_height));
|
self.platform_specific.base_size = Some(base_size.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,25 @@ use std::os::raw::c_void;
|
||||||
use libc;
|
use libc;
|
||||||
use winapi::shared::windef::HWND;
|
use winapi::shared::windef::HWND;
|
||||||
|
|
||||||
use {DeviceId, Icon, MonitorId, Window, WindowBuilder};
|
use {DeviceId, EventsLoop, Icon, MonitorId, Window, WindowBuilder};
|
||||||
|
use platform::EventsLoop as WindowsEventsLoop;
|
||||||
|
|
||||||
|
/// Additional methods on `EventsLoop` that are specific to Windows.
|
||||||
|
pub trait EventsLoopExt {
|
||||||
|
/// By default, winit on Windows will attempt to enable process-wide DPI awareness. If that's
|
||||||
|
/// undesirable, you can create an `EventsLoop` using this function instead.
|
||||||
|
fn new_dpi_unaware() -> Self where Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventsLoopExt for EventsLoop {
|
||||||
|
#[inline]
|
||||||
|
fn new_dpi_unaware() -> Self {
|
||||||
|
EventsLoop {
|
||||||
|
events_loop: WindowsEventsLoop::with_dpi_awareness(false),
|
||||||
|
_marker: ::std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Additional methods on `Window` that are specific to Windows.
|
/// Additional methods on `Window` that are specific to Windows.
|
||||||
pub trait WindowExt {
|
pub trait WindowExt {
|
||||||
|
|
|
@ -2,24 +2,34 @@
|
||||||
|
|
||||||
extern crate android_glue;
|
extern crate android_glue;
|
||||||
|
|
||||||
use libc;
|
mod ffi;
|
||||||
use std::sync::mpsc::{Receiver, channel};
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::fmt;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use {CreationError, Event, WindowEvent, MouseCursor};
|
use std::sync::mpsc::{Receiver, channel};
|
||||||
|
|
||||||
|
use {
|
||||||
|
CreationError,
|
||||||
|
CursorState,
|
||||||
|
Event,
|
||||||
|
LogicalPosition,
|
||||||
|
LogicalSize,
|
||||||
|
MouseCursor,
|
||||||
|
PhysicalPosition,
|
||||||
|
PhysicalSize,
|
||||||
|
WindowAttributes,
|
||||||
|
WindowEvent,
|
||||||
|
WindowId as RootWindowId,
|
||||||
|
};
|
||||||
use CreationError::OsError;
|
use CreationError::OsError;
|
||||||
use WindowId as RootWindowId;
|
|
||||||
use events::{Touch, TouchPhase};
|
use events::{Touch, TouchPhase};
|
||||||
use window::MonitorId as RootMonitorId;
|
use window::MonitorId as RootMonitorId;
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
|
|
||||||
use CursorState;
|
|
||||||
use WindowAttributes;
|
|
||||||
|
|
||||||
pub struct EventsLoop {
|
pub struct EventsLoop {
|
||||||
event_rx: Receiver<android_glue::Event>,
|
event_rx: Receiver<android_glue::Event>,
|
||||||
suspend_callback: RefCell<Option<Box<Fn(bool) -> ()>>>
|
suspend_callback: RefCell<Option<Box<Fn(bool) -> ()>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -31,13 +41,13 @@ impl EventsLoop {
|
||||||
android_glue::add_sender(tx);
|
android_glue::add_sender(tx);
|
||||||
EventsLoop {
|
EventsLoop {
|
||||||
event_rx: rx,
|
event_rx: rx,
|
||||||
suspend_callback: RefCell::new(None),
|
suspend_callback: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||||
let mut rb = VecDeque::new();
|
let mut rb = VecDeque::with_capacity(1);
|
||||||
rb.push_back(MonitorId);
|
rb.push_back(MonitorId);
|
||||||
rb
|
rb
|
||||||
}
|
}
|
||||||
|
@ -47,14 +57,17 @@ impl EventsLoop {
|
||||||
MonitorId
|
MonitorId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn poll_events<F>(&mut self, mut callback: F)
|
pub fn poll_events<F>(&mut self, mut callback: F)
|
||||||
where F: FnMut(::Event)
|
where F: FnMut(::Event)
|
||||||
{
|
{
|
||||||
while let Ok(event) = self.event_rx.try_recv() {
|
while let Ok(event) = self.event_rx.try_recv() {
|
||||||
|
|
||||||
let e = match event{
|
let e = match event{
|
||||||
android_glue::Event::EventMotion(motion) => {
|
android_glue::Event::EventMotion(motion) => {
|
||||||
|
let dpi_factor = MonitorId.get_hidpi_factor();
|
||||||
|
let location = LogicalPosition::from_physical(
|
||||||
|
(motion.x as f64, motion.y as f64),
|
||||||
|
dpi_factor,
|
||||||
|
);
|
||||||
Some(Event::WindowEvent {
|
Some(Event::WindowEvent {
|
||||||
window_id: RootWindowId(WindowId),
|
window_id: RootWindowId(WindowId),
|
||||||
event: WindowEvent::Touch(Touch {
|
event: WindowEvent::Touch(Touch {
|
||||||
|
@ -64,7 +77,7 @@ impl EventsLoop {
|
||||||
android_glue::MotionAction::Up => TouchPhase::Ended,
|
android_glue::MotionAction::Up => TouchPhase::Ended,
|
||||||
android_glue::MotionAction::Cancel => TouchPhase::Cancelled,
|
android_glue::MotionAction::Cancel => TouchPhase::Cancelled,
|
||||||
},
|
},
|
||||||
location: (motion.x as f64, motion.y as f64),
|
location,
|
||||||
id: motion.pointer_id as u64,
|
id: motion.pointer_id as u64,
|
||||||
device_id: DEVICE_ID,
|
device_id: DEVICE_ID,
|
||||||
}),
|
}),
|
||||||
|
@ -91,11 +104,12 @@ impl EventsLoop {
|
||||||
if native_window.is_null() {
|
if native_window.is_null() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let w = unsafe { ffi::ANativeWindow_getWidth(native_window as *const _) } as u32;
|
let dpi_factor = MonitorId.get_hidpi_factor();
|
||||||
let h = unsafe { ffi::ANativeWindow_getHeight(native_window as *const _) } as u32;
|
let physical_size = MonitorId.get_dimensions();
|
||||||
|
let size = LogicalSize::from_physical(physical_size, dpi_factor);
|
||||||
Some(Event::WindowEvent {
|
Some(Event::WindowEvent {
|
||||||
window_id: RootWindowId(WindowId),
|
window_id: RootWindowId(WindowId),
|
||||||
event: WindowEvent::Resized(w, h),
|
event: WindowEvent::Resized(size),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -164,10 +178,29 @@ pub struct Window {
|
||||||
native_window: *const c_void,
|
native_window: *const c_void,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MonitorId;
|
pub struct MonitorId;
|
||||||
|
|
||||||
mod ffi;
|
impl fmt::Debug for MonitorId {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct MonitorId {
|
||||||
|
name: Option<String>,
|
||||||
|
dimensions: PhysicalSize,
|
||||||
|
position: PhysicalPosition,
|
||||||
|
hidpi_factor: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
let monitor_id_proxy = MonitorId {
|
||||||
|
name: self.get_name(),
|
||||||
|
dimensions: self.get_dimensions(),
|
||||||
|
position: self.get_position(),
|
||||||
|
hidpi_factor: self.get_hidpi_factor(),
|
||||||
|
};
|
||||||
|
|
||||||
|
monitor_id_proxy.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl MonitorId {
|
impl MonitorId {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -176,21 +209,24 @@ impl MonitorId {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||||
unsafe {
|
unsafe {
|
||||||
let window = android_glue::get_native_window();
|
let window = android_glue::get_native_window();
|
||||||
(ffi::ANativeWindow_getWidth(window) as u32, ffi::ANativeWindow_getHeight(window) as u32)
|
(
|
||||||
|
ffi::ANativeWindow_getWidth(window) as f64,
|
||||||
|
ffi::ANativeWindow_getHeight(window) as f64,
|
||||||
|
).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_position(&self) -> (i32, i32) {
|
pub fn get_position(&self) -> PhysicalPosition {
|
||||||
// Android assumes single screen
|
// Android assumes single screen
|
||||||
(0, 0)
|
(0, 0).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_hidpi_factor(&self) -> f32 {
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
1.0
|
1.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,10 +241,6 @@ impl Window {
|
||||||
_: PlatformSpecificWindowBuilderAttributes)
|
_: PlatformSpecificWindowBuilderAttributes)
|
||||||
-> Result<Window, CreationError>
|
-> Result<Window, CreationError>
|
||||||
{
|
{
|
||||||
// not implemented
|
|
||||||
assert!(win_attribs.min_dimensions.is_none());
|
|
||||||
assert!(win_attribs.max_dimensions.is_none());
|
|
||||||
|
|
||||||
let native_window = unsafe { android_glue::get_native_window() };
|
let native_window = unsafe { android_glue::get_native_window() };
|
||||||
if native_window.is_null() {
|
if native_window.is_null() {
|
||||||
return Err(OsError(format!("Android's native window is null")));
|
return Err(OsError(format!("Android's native window is null")));
|
||||||
|
@ -228,35 +260,45 @@ impl Window {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_title(&self, _: &str) {
|
pub fn set_title(&self, _: &str) {
|
||||||
|
// N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn show(&self) {
|
pub fn show(&self) {
|
||||||
|
// N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hide(&self) {
|
pub fn hide(&self) {
|
||||||
|
// N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||||
|
// N/A
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||||
|
// N/A
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_position(&self, _x: i32, _y: i32) {
|
pub fn set_position(&self, _position: LogicalPosition) {
|
||||||
|
// N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_min_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
pub fn set_min_dimensions(&self, _dimensions: Option<LogicalSize>) {
|
||||||
|
// N/A
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
pub fn set_max_dimensions(&self, _dimensions: Option<LogicalSize>) {
|
||||||
|
// N/A
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_resizable(&self, _resizable: bool) {
|
pub fn set_resizable(&self, _resizable: bool) {
|
||||||
|
@ -264,62 +306,57 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||||
if self.native_window.is_null() {
|
if self.native_window.is_null() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some((
|
let dpi_factor = self.get_hidpi_factor();
|
||||||
unsafe { ffi::ANativeWindow_getWidth(self.native_window as *const _) } as u32,
|
let physical_size = self.get_current_monitor().get_dimensions();
|
||||||
unsafe { ffi::ANativeWindow_getHeight(self.native_window as *const _) } as u32
|
Some(LogicalSize::from_physical(physical_size, dpi_factor))
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||||
self.get_inner_size()
|
self.get_inner_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_inner_size(&self, _x: u32, _y: u32) {
|
pub fn set_inner_size(&self, _size: LogicalSize) {
|
||||||
|
// N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
unimplemented!();
|
self.get_current_monitor().get_hidpi_factor()
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor(&self, _: MouseCursor) {
|
pub fn set_cursor(&self, _: MouseCursor) {
|
||||||
|
// N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_state(&self, _state: CursorState) -> Result<(), String> {
|
pub fn set_cursor_state(&self, _state: CursorState) -> Result<(), String> {
|
||||||
|
// N/A
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hidpi_factor(&self) -> f32 {
|
pub fn set_cursor_position(&self, _position: LogicalPosition) -> Result<(), ()> {
|
||||||
1.0
|
// N/A
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_maximized(&self, _maximized: bool) {
|
pub fn set_maximized(&self, _maximized: bool) {
|
||||||
|
// N/A
|
||||||
// Android has single screen maximized apps so nothing to do
|
// Android has single screen maximized apps so nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) {
|
pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) {
|
||||||
|
// N/A
|
||||||
// Android has single screen maximized apps so nothing to do
|
// Android has single screen maximized apps so nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,15 +376,16 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_ime_spot(&self, _x: i32, _y: i32) {
|
pub fn set_ime_spot(&self, _spot: LogicalPosition) {
|
||||||
// N/A
|
// N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||||
RootMonitorId{inner: MonitorId}
|
RootMonitorId { inner: MonitorId }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn id(&self) -> WindowId {
|
pub fn id(&self) -> WindowId {
|
||||||
WindowId
|
WindowId
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code, non_camel_case_types, non_snake_case)]
|
||||||
#![allow(non_snake_case)]
|
|
||||||
#![allow(non_camel_case_types)]
|
|
||||||
|
|
||||||
use std::os::raw::{c_int, c_char, c_void, c_ulong, c_double, c_long, c_ushort};
|
use std::os::raw::{c_int, c_char, c_void, c_ulong, c_double, c_long, c_ushort};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -2,16 +2,21 @@
|
||||||
|
|
||||||
mod ffi;
|
mod ffi;
|
||||||
|
|
||||||
use std::mem;
|
use std::{mem, ptr, str};
|
||||||
use std::os::raw::{c_char, c_void, c_double, c_ulong, c_int};
|
|
||||||
use std::ptr;
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use std::sync::{Mutex, Arc};
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
use std::os::raw::{c_char, c_void, c_double, c_ulong, c_int};
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::{Mutex, Arc};
|
||||||
|
|
||||||
|
use {LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
|
||||||
|
|
||||||
const DOCUMENT_NAME: &'static str = "#document\0";
|
const DOCUMENT_NAME: &'static str = "#document\0";
|
||||||
|
|
||||||
|
fn get_hidpi_factor() -> f64 {
|
||||||
|
unsafe { ffi::emscripten_get_device_pixel_ratio() as f64 }
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct PlatformSpecificWindowBuilderAttributes;
|
pub struct PlatformSpecificWindowBuilderAttributes;
|
||||||
|
|
||||||
|
@ -34,18 +39,18 @@ impl MonitorId {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_position(&self) -> (i32, i32) {
|
pub fn get_position(&self) -> PhysicalPosition {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||||
(0, 0)
|
(0, 0).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_hidpi_factor(&self) -> f32 {
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
1.0
|
get_hidpi_factor()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,17 +95,19 @@ impl EventsLoop {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn interrupt(&self) {
|
pub fn interrupt(&self) {
|
||||||
self.interrupted.store(true, Ordering::Relaxed);
|
self.interrupted.store(true, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn create_proxy(&self) -> EventsLoopProxy {
|
pub fn create_proxy(&self) -> EventsLoopProxy {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||||
let mut list = VecDeque::new();
|
let mut list = VecDeque::with_capacity(1);
|
||||||
list.push_back(MonitorId);
|
list.push_back(MonitorId);
|
||||||
list
|
list
|
||||||
}
|
}
|
||||||
|
@ -115,7 +122,7 @@ impl EventsLoop {
|
||||||
{
|
{
|
||||||
let ref mut window = *self.window.lock().unwrap();
|
let ref mut window = *self.window.lock().unwrap();
|
||||||
if let &mut Some(ref mut window) = window {
|
if let &mut Some(ref mut window) = window {
|
||||||
while let Some(event) = window.events.borrow_mut().pop_front() {
|
while let Some(event) = window.events.lock().unwrap().pop_front() {
|
||||||
callback(event)
|
callback(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,7 +151,7 @@ pub struct WindowId(usize);
|
||||||
pub struct Window2 {
|
pub struct Window2 {
|
||||||
cursor_state: Mutex<::CursorState>,
|
cursor_state: Mutex<::CursorState>,
|
||||||
is_fullscreen: bool,
|
is_fullscreen: bool,
|
||||||
events: Box<RefCell<VecDeque<::Event>>>,
|
events: Box<Mutex<VecDeque<::Event>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
|
@ -176,7 +183,7 @@ extern "C" fn mouse_callback(
|
||||||
event_queue: *mut c_void) -> ffi::EM_BOOL
|
event_queue: *mut c_void) -> ffi::EM_BOOL
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let queue: &RefCell<VecDeque<::Event>> = mem::transmute(event_queue);
|
let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue);
|
||||||
|
|
||||||
let modifiers = ::ModifiersState {
|
let modifiers = ::ModifiersState {
|
||||||
shift: (*event).shiftKey == ffi::EM_TRUE,
|
shift: (*event).shiftKey == ffi::EM_TRUE,
|
||||||
|
@ -187,15 +194,20 @@ extern "C" fn mouse_callback(
|
||||||
|
|
||||||
match event_type {
|
match event_type {
|
||||||
ffi::EMSCRIPTEN_EVENT_MOUSEMOVE => {
|
ffi::EMSCRIPTEN_EVENT_MOUSEMOVE => {
|
||||||
queue.borrow_mut().push_back(::Event::WindowEvent {
|
let dpi_factor = get_hidpi_factor();
|
||||||
|
let position = LogicalPosition::from_physical(
|
||||||
|
((*event).canvasX as f64, (*event).canvasY as f64),
|
||||||
|
dpi_factor,
|
||||||
|
);
|
||||||
|
queue.lock().unwrap().push_back(::Event::WindowEvent {
|
||||||
window_id: ::WindowId(WindowId(0)),
|
window_id: ::WindowId(WindowId(0)),
|
||||||
event: ::WindowEvent::CursorMoved {
|
event: ::WindowEvent::CursorMoved {
|
||||||
device_id: ::DeviceId(DeviceId),
|
device_id: ::DeviceId(DeviceId),
|
||||||
position: ((*event).canvasX as f64, (*event).canvasY as f64),
|
position,
|
||||||
modifiers: modifiers,
|
modifiers: modifiers,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
queue.borrow_mut().push_back(::Event::DeviceEvent {
|
queue.lock().unwrap().push_back(::Event::DeviceEvent {
|
||||||
device_id: ::DeviceId(DeviceId),
|
device_id: ::DeviceId(DeviceId),
|
||||||
event: ::DeviceEvent::MouseMotion {
|
event: ::DeviceEvent::MouseMotion {
|
||||||
delta: ((*event).movementX as f64, (*event).movementY as f64),
|
delta: ((*event).movementX as f64, (*event).movementY as f64),
|
||||||
|
@ -215,7 +227,7 @@ extern "C" fn mouse_callback(
|
||||||
ffi::EMSCRIPTEN_EVENT_MOUSEUP => ::ElementState::Released,
|
ffi::EMSCRIPTEN_EVENT_MOUSEUP => ::ElementState::Released,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
queue.borrow_mut().push_back(::Event::WindowEvent {
|
queue.lock().unwrap().push_back(::Event::WindowEvent {
|
||||||
window_id: ::WindowId(WindowId(0)),
|
window_id: ::WindowId(WindowId(0)),
|
||||||
event: ::WindowEvent::MouseInput {
|
event: ::WindowEvent::MouseInput {
|
||||||
device_id: ::DeviceId(DeviceId),
|
device_id: ::DeviceId(DeviceId),
|
||||||
|
@ -238,7 +250,7 @@ extern "C" fn keyboard_callback(
|
||||||
event_queue: *mut c_void) -> ffi::EM_BOOL
|
event_queue: *mut c_void) -> ffi::EM_BOOL
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let queue: &RefCell<VecDeque<::Event>> = mem::transmute(event_queue);
|
let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue);
|
||||||
|
|
||||||
let modifiers = ::ModifiersState {
|
let modifiers = ::ModifiersState {
|
||||||
shift: (*event).shiftKey == ffi::EM_TRUE,
|
shift: (*event).shiftKey == ffi::EM_TRUE,
|
||||||
|
@ -249,7 +261,7 @@ extern "C" fn keyboard_callback(
|
||||||
|
|
||||||
match event_type {
|
match event_type {
|
||||||
ffi::EMSCRIPTEN_EVENT_KEYDOWN => {
|
ffi::EMSCRIPTEN_EVENT_KEYDOWN => {
|
||||||
queue.borrow_mut().push_back(::Event::WindowEvent {
|
queue.lock().unwrap().push_back(::Event::WindowEvent {
|
||||||
window_id: ::WindowId(WindowId(0)),
|
window_id: ::WindowId(WindowId(0)),
|
||||||
event: ::WindowEvent::KeyboardInput {
|
event: ::WindowEvent::KeyboardInput {
|
||||||
device_id: ::DeviceId(DeviceId),
|
device_id: ::DeviceId(DeviceId),
|
||||||
|
@ -263,7 +275,7 @@ extern "C" fn keyboard_callback(
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
ffi::EMSCRIPTEN_EVENT_KEYUP => {
|
ffi::EMSCRIPTEN_EVENT_KEYUP => {
|
||||||
queue.borrow_mut().push_back(::Event::WindowEvent {
|
queue.lock().unwrap().push_back(::Event::WindowEvent {
|
||||||
window_id: ::WindowId(WindowId(0)),
|
window_id: ::WindowId(WindowId(0)),
|
||||||
event: ::WindowEvent::KeyboardInput {
|
event: ::WindowEvent::KeyboardInput {
|
||||||
device_id: ::DeviceId(DeviceId),
|
device_id: ::DeviceId(DeviceId),
|
||||||
|
@ -289,7 +301,7 @@ extern fn touch_callback(
|
||||||
event_queue: *mut c_void) -> ffi::EM_BOOL
|
event_queue: *mut c_void) -> ffi::EM_BOOL
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let queue: &RefCell<VecDeque<::Event>> = mem::transmute(event_queue);
|
let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue);
|
||||||
|
|
||||||
let phase = match event_type {
|
let phase = match event_type {
|
||||||
ffi::EMSCRIPTEN_EVENT_TOUCHSTART => ::TouchPhase::Started,
|
ffi::EMSCRIPTEN_EVENT_TOUCHSTART => ::TouchPhase::Started,
|
||||||
|
@ -302,13 +314,18 @@ extern fn touch_callback(
|
||||||
for touch in 0..(*event).numTouches as usize {
|
for touch in 0..(*event).numTouches as usize {
|
||||||
let touch = (*event).touches[touch];
|
let touch = (*event).touches[touch];
|
||||||
if touch.isChanged == ffi::EM_TRUE {
|
if touch.isChanged == ffi::EM_TRUE {
|
||||||
queue.borrow_mut().push_back(::Event::WindowEvent {
|
let dpi_factor = get_hidpi_factor();
|
||||||
|
let location = LogicalPosition::from_physical(
|
||||||
|
(touch.canvasX as f64, touch.canvasY as f64),
|
||||||
|
dpi_factor,
|
||||||
|
);
|
||||||
|
queue.lock().unwrap().push_back(::Event::WindowEvent {
|
||||||
window_id: ::WindowId(WindowId(0)),
|
window_id: ::WindowId(WindowId(0)),
|
||||||
event: ::WindowEvent::Touch(::Touch {
|
event: ::WindowEvent::Touch(::Touch {
|
||||||
device_id: ::DeviceId(DeviceId),
|
device_id: ::DeviceId(DeviceId),
|
||||||
phase,
|
phase,
|
||||||
id: touch.identifier as u64,
|
id: touch.identifier as u64,
|
||||||
location: (touch.canvasX as f64, touch.canvasY as f64),
|
location,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -356,8 +373,8 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
let w = Window2 {
|
let w = Window2 {
|
||||||
cursor_state: Mutex::new(::CursorState::Normal),
|
cursor_state: Default::default(),
|
||||||
events: Box::new(RefCell::new(VecDeque::new())),
|
events: Default::default(),
|
||||||
is_fullscreen: attribs.fullscreen.is_some(),
|
is_fullscreen: attribs.fullscreen.is_some(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -395,8 +412,8 @@ impl Window {
|
||||||
em_try(ffi::emscripten_set_fullscreenchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, Some(fullscreen_callback)))
|
em_try(ffi::emscripten_set_fullscreenchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, Some(fullscreen_callback)))
|
||||||
.map_err(|e| ::CreationError::OsError(e))?;
|
.map_err(|e| ::CreationError::OsError(e))?;
|
||||||
}
|
}
|
||||||
} else if let Some((w, h)) = attribs.dimensions {
|
} else if let Some(size) = attribs.dimensions {
|
||||||
window.set_inner_size(w, h);
|
window.set_inner_size(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
*events_loop.window.lock().unwrap() = Some(window.window.clone());
|
*events_loop.window.lock().unwrap() = Some(window.window.clone());
|
||||||
|
@ -413,22 +430,22 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||||
Some((0, 0))
|
Some((0, 0).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||||
Some((0, 0))
|
Some((0, 0).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_position(&self, _: i32, _: i32) {
|
pub fn set_position(&self, _: LogicalPosition) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
#[inline]
|
||||||
|
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||||
unsafe {
|
unsafe {
|
||||||
use std::{mem, ptr};
|
|
||||||
let mut width = 0;
|
let mut width = 0;
|
||||||
let mut height = 0;
|
let mut height = 0;
|
||||||
let mut fullscreen = 0;
|
let mut fullscreen = 0;
|
||||||
|
@ -438,30 +455,41 @@ impl Window {
|
||||||
{
|
{
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some((width as u32, height as u32))
|
let dpi_factor = self.get_hidpi_factor();
|
||||||
|
let logical = LogicalSize::from_physical((width as u32, height as u32), dpi_factor);
|
||||||
|
Some(logical)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||||
self.get_inner_size()
|
self.get_inner_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_inner_size(&self, width: u32, height: u32) {
|
pub fn set_inner_size(&self, size: LogicalSize) {
|
||||||
unsafe {
|
unsafe {
|
||||||
use std::ptr;
|
let dpi_factor = self.get_hidpi_factor();
|
||||||
ffi::emscripten_set_element_css_size(ptr::null(), width as c_double, height
|
let physical = PhysicalSize::from_logical(size, dpi_factor);
|
||||||
as c_double);
|
let (width, height): (u32, u32) = physical.into();
|
||||||
|
ffi::emscripten_set_element_css_size(
|
||||||
|
ptr::null(),
|
||||||
|
width as c_double,
|
||||||
|
height as c_double,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_min_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
pub fn set_min_dimensions(&self, _dimensions: Option<LogicalSize>) {
|
||||||
|
// N/A
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
pub fn set_max_dimensions(&self, _dimensions: Option<LogicalSize>) {
|
||||||
|
// N/A
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_resizable(&self, _resizable: bool) {
|
pub fn set_resizable(&self, _resizable: bool) {
|
||||||
|
@ -473,16 +501,6 @@ impl Window {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hide(&self) {}
|
pub fn hide(&self) {}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn platform_display(&self) -> *mut ::libc::c_void {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn platform_window(&self) -> *mut ::libc::c_void {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor(&self, _cursor: ::MouseCursor) {}
|
pub fn set_cursor(&self, _cursor: ::MouseCursor) {}
|
||||||
|
|
||||||
|
@ -524,12 +542,12 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hidpi_factor(&self) -> f32 {
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
unsafe { ffi::emscripten_get_device_pixel_ratio() as f32 }
|
get_hidpi_factor()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
|
pub fn set_cursor_position(&self, _position: LogicalPosition) -> Result<(), ()> {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,7 +577,7 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_ime_spot(&self, _x: i32, _y: i32) {
|
pub fn set_ime_spot(&self, _logical_spot: LogicalPosition) {
|
||||||
// N/A
|
// N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -612,7 +630,6 @@ fn error_to_str(code: ffi::EMSCRIPTEN_RESULT) -> &'static str {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_translate(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES]) -> u8 {
|
fn key_translate(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES]) -> u8 {
|
||||||
use std::str;
|
|
||||||
let slice = &input[0..input.iter().take_while(|x| **x != 0).count()];
|
let slice = &input[0..input.iter().take_while(|x| **x != 0).count()];
|
||||||
let maybe_key = unsafe { str::from_utf8(mem::transmute::<_, &[u8]>(slice)) };
|
let maybe_key = unsafe { str::from_utf8(mem::transmute::<_, &[u8]>(slice)) };
|
||||||
let key = match maybe_key {
|
let key = match maybe_key {
|
||||||
|
@ -629,7 +646,6 @@ fn key_translate(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES]) ->
|
||||||
fn key_translate_virt(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES],
|
fn key_translate_virt(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES],
|
||||||
location: c_ulong) -> Option<::VirtualKeyCode>
|
location: c_ulong) -> Option<::VirtualKeyCode>
|
||||||
{
|
{
|
||||||
use std::str;
|
|
||||||
let slice = &input[0..input.iter().take_while(|x| **x != 0).count()];
|
let slice = &input[0..input.iter().take_while(|x| **x != 0).count()];
|
||||||
let maybe_key = unsafe { str::from_utf8(mem::transmute::<_, &[u8]>(slice)) };
|
let maybe_key = unsafe { str::from_utf8(mem::transmute::<_, &[u8]>(slice)) };
|
||||||
let key = match maybe_key {
|
let key = match maybe_key {
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
|
#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
|
||||||
|
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
|
use std::mem;
|
||||||
|
use std::os::raw::*;
|
||||||
|
|
||||||
use libc;
|
use objc::runtime::{Class, Object};
|
||||||
use objc::runtime::{ Object, Class };
|
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
pub type id = *mut Object;
|
pub type id = *mut Object;
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#[allow(non_upper_case_globals)]
|
|
||||||
pub const nil: id = 0 as id;
|
pub const nil: id = 0 as id;
|
||||||
|
|
||||||
pub type CFStringRef = *const libc::c_void;
|
pub type CFStringRef = *const c_void;
|
||||||
pub type CFTimeInterval = f64;
|
pub type CFTimeInterval = f64;
|
||||||
pub type Boolean = u32;
|
pub type Boolean = u32;
|
||||||
|
|
||||||
#[allow(non_upper_case_globals)]
|
|
||||||
pub const kCFRunLoopRunHandledSource: i32 = 4;
|
pub const kCFRunLoopRunHandledSource: i32 = 4;
|
||||||
|
|
||||||
|
pub const UIViewAutoresizingFlexibleWidth: NSUInteger = 1 << 1;
|
||||||
|
pub const UIViewAutoresizingFlexibleHeight: NSUInteger = 1 << 4;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
pub type CGFloat = f32;
|
pub type CGFloat = f32;
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
@ -38,14 +39,14 @@ pub struct CGPoint {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CGRect {
|
pub struct CGRect {
|
||||||
pub origin: CGPoint,
|
pub origin: CGPoint,
|
||||||
pub size: CGSize
|
pub size: CGSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CGSize {
|
pub struct CGSize {
|
||||||
pub width: CGFloat,
|
pub width: CGFloat,
|
||||||
pub height: CGFloat
|
pub height: CGFloat,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[link(name = "UIKit", kind = "framework")]
|
#[link(name = "UIKit", kind = "framework")]
|
||||||
|
@ -55,15 +56,24 @@ extern {
|
||||||
pub static kCFRunLoopDefaultMode: CFStringRef;
|
pub static kCFRunLoopDefaultMode: CFStringRef;
|
||||||
|
|
||||||
// int UIApplicationMain ( int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName );
|
// int UIApplicationMain ( int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName );
|
||||||
pub fn UIApplicationMain(argc: libc::c_int, argv: *const libc::c_char, principalClassName: id, delegateClassName: id) -> libc::c_int;
|
pub fn UIApplicationMain(
|
||||||
|
argc: c_int,
|
||||||
|
argv: *const c_char,
|
||||||
|
principalClassName: id,
|
||||||
|
delegateClassName: id,
|
||||||
|
) -> c_int;
|
||||||
|
|
||||||
// SInt32 CFRunLoopRunInMode ( CFStringRef mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled );
|
// SInt32 CFRunLoopRunInMode ( CFStringRef mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled );
|
||||||
pub fn CFRunLoopRunInMode(mode: CFStringRef, seconds: CFTimeInterval, returnAfterSourceHandled: Boolean) -> i32;
|
pub fn CFRunLoopRunInMode(
|
||||||
|
mode: CFStringRef,
|
||||||
|
seconds: CFTimeInterval,
|
||||||
|
returnAfterSourceHandled: Boolean,
|
||||||
|
) -> i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern {
|
extern {
|
||||||
pub fn setjmp(env: *mut libc::c_void) -> libc::c_int;
|
pub fn setjmp(env: *mut c_void) -> c_int;
|
||||||
pub fn longjmp(env: *mut libc::c_void, val: libc::c_int);
|
pub fn longjmp(env: *mut c_void, val: c_int);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait NSString: Sized {
|
pub trait NSString: Sized {
|
||||||
|
@ -71,17 +81,14 @@ pub trait NSString: Sized {
|
||||||
msg_send![class("NSString"), alloc]
|
msg_send![class("NSString"), alloc]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id;
|
||||||
unsafe fn initWithUTF8String_(self, c_string: *const i8) -> id;
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
unsafe fn stringByAppendingString_(self, other: id) -> id;
|
unsafe fn stringByAppendingString_(self, other: id) -> id;
|
||||||
unsafe fn init_str(self, string: &str) -> Self;
|
unsafe fn init_str(self, string: &str) -> Self;
|
||||||
#[allow(non_snake_case)]
|
unsafe fn UTF8String(self) -> *const c_char;
|
||||||
unsafe fn UTF8String(self) -> *const libc::c_char;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NSString for id {
|
impl NSString for id {
|
||||||
unsafe fn initWithUTF8String_(self, c_string: *const i8) -> id {
|
unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id {
|
||||||
msg_send![self, initWithUTF8String:c_string as id]
|
msg_send![self, initWithUTF8String:c_string as id]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +101,7 @@ impl NSString for id {
|
||||||
self.initWithUTF8String_(cstring.as_ptr())
|
self.initWithUTF8String_(cstring.as_ptr())
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn UTF8String(self) -> *const libc::c_char {
|
unsafe fn UTF8String(self) -> *const c_char {
|
||||||
msg_send![self, UTF8String]
|
msg_send![self, UTF8String]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,6 +109,6 @@ impl NSString for id {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn class(name: &str) -> *mut Class {
|
pub fn class(name: &str) -> *mut Class {
|
||||||
unsafe {
|
unsafe {
|
||||||
::std::mem::transmute(Class::get(name))
|
mem::transmute(Class::get(name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
//!
|
//!
|
||||||
//! ```rust, ignore
|
//! ```rust, ignore
|
||||||
//! #[no_mangle]
|
//! #[no_mangle]
|
||||||
//! pub extern fn start_glutin_app() {
|
//! pub extern fn start_winit_app() {
|
||||||
//! start_inner()
|
//! start_inner()
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
|
@ -29,13 +29,13 @@
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Compile project and then drag resulting .a into Xcode project. Add glutin.h to xcode.
|
//! Compile project and then drag resulting .a into Xcode project. Add winit.h to xcode.
|
||||||
//!
|
//!
|
||||||
//! ```ignore
|
//! ```ignore
|
||||||
//! void start_glutin_app();
|
//! void start_winit_app();
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Use start_glutin_app inside your xcode's main function.
|
//! Use start_winit_app inside your xcode's main function.
|
||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
//! # App lifecycle and events
|
//! # App lifecycle and events
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
//! [app lifecycle](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/).
|
//! [app lifecycle](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/).
|
||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
//! This is how those event are represented in glutin:
|
//! This is how those event are represented in winit:
|
||||||
//!
|
//!
|
||||||
//! - applicationDidBecomeActive is Focused(true)
|
//! - applicationDidBecomeActive is Focused(true)
|
||||||
//! - applicationWillResignActive is Focused(false)
|
//! - applicationWillResignActive is Focused(false)
|
||||||
|
@ -60,100 +60,147 @@
|
||||||
|
|
||||||
#![cfg(target_os = "ios")]
|
#![cfg(target_os = "ios")]
|
||||||
|
|
||||||
|
use std::{fmt, mem, ptr};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::ptr;
|
use std::os::raw::*;
|
||||||
use std::mem;
|
|
||||||
use std::os::raw::c_void;
|
|
||||||
|
|
||||||
use libc;
|
use objc::declare::ClassDecl;
|
||||||
use libc::c_int;
|
use objc::runtime::{BOOL, Class, Object, Sel, YES};
|
||||||
use objc::runtime::{Class, Object, Sel, BOOL, YES };
|
|
||||||
use objc::declare::{ ClassDecl };
|
|
||||||
|
|
||||||
use { CreationError, CursorState, MouseCursor, WindowAttributes };
|
use {
|
||||||
use WindowId as RootEventId;
|
CreationError,
|
||||||
use WindowEvent;
|
CursorState,
|
||||||
use Event;
|
Event,
|
||||||
use events::{ Touch, TouchPhase };
|
LogicalPosition,
|
||||||
|
LogicalSize,
|
||||||
|
MouseCursor,
|
||||||
|
PhysicalPosition,
|
||||||
|
PhysicalSize,
|
||||||
|
WindowAttributes,
|
||||||
|
WindowEvent,
|
||||||
|
WindowId as RootEventId,
|
||||||
|
};
|
||||||
|
use events::{Touch, TouchPhase};
|
||||||
use window::MonitorId as RootMonitorId;
|
use window::MonitorId as RootMonitorId;
|
||||||
|
|
||||||
mod ffi;
|
mod ffi;
|
||||||
use self::ffi::{
|
use self::ffi::{
|
||||||
setjmp,
|
|
||||||
UIApplicationMain,
|
|
||||||
CFTimeInterval,
|
CFTimeInterval,
|
||||||
CFRunLoopRunInMode,
|
CFRunLoopRunInMode,
|
||||||
|
CGFloat,
|
||||||
|
CGPoint,
|
||||||
|
CGRect,
|
||||||
|
id,
|
||||||
kCFRunLoopDefaultMode,
|
kCFRunLoopDefaultMode,
|
||||||
kCFRunLoopRunHandledSource,
|
kCFRunLoopRunHandledSource,
|
||||||
id,
|
longjmp,
|
||||||
nil,
|
nil,
|
||||||
NSString,
|
NSString,
|
||||||
CGFloat,
|
setjmp,
|
||||||
longjmp,
|
UIApplicationMain,
|
||||||
CGRect,
|
UIViewAutoresizingFlexibleWidth,
|
||||||
CGPoint
|
UIViewAutoresizingFlexibleHeight,
|
||||||
};
|
};
|
||||||
|
|
||||||
static mut jmpbuf: [c_int;27] = [0;27];
|
static mut JMPBUF: [c_int; 27] = [0; 27];
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct MonitorId;
|
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
delegate_state: *mut DelegateState
|
delegate_state: *mut DelegateState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
unsafe impl Send for Window {}
|
||||||
pub struct WindowProxy;
|
unsafe impl Sync for Window {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct DelegateState {
|
struct DelegateState {
|
||||||
events_queue: VecDeque<Event>,
|
events_queue: VecDeque<Event>,
|
||||||
window: id,
|
window: id,
|
||||||
controller: id,
|
controller: id,
|
||||||
size: (u32,u32),
|
view: id,
|
||||||
scale: f32
|
size: LogicalSize,
|
||||||
|
scale: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl DelegateState {
|
impl DelegateState {
|
||||||
#[inline]
|
fn new(window: id, controller: id, view: id, size: LogicalSize, scale: f64) -> DelegateState {
|
||||||
fn new(window: id, controller:id, size: (u32,u32), scale: f32) -> DelegateState {
|
|
||||||
DelegateState {
|
DelegateState {
|
||||||
events_queue: VecDeque::new(),
|
events_queue: VecDeque::new(),
|
||||||
window: window,
|
window,
|
||||||
controller: controller,
|
controller,
|
||||||
size: size,
|
view,
|
||||||
scale: scale
|
size,
|
||||||
|
scale,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for DelegateState {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![self.window, release];
|
||||||
|
let _: () = msg_send![self.controller, release];
|
||||||
|
let _: () = msg_send![self.view, release];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MonitorId;
|
||||||
|
|
||||||
|
impl fmt::Debug for MonitorId {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct MonitorId {
|
||||||
|
name: Option<String>,
|
||||||
|
dimensions: PhysicalSize,
|
||||||
|
position: PhysicalPosition,
|
||||||
|
hidpi_factor: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
let monitor_id_proxy = MonitorId {
|
||||||
|
name: self.get_name(),
|
||||||
|
dimensions: self.get_dimensions(),
|
||||||
|
position: self.get_position(),
|
||||||
|
hidpi_factor: self.get_hidpi_factor(),
|
||||||
|
};
|
||||||
|
|
||||||
|
monitor_id_proxy.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl MonitorId {
|
impl MonitorId {
|
||||||
|
#[inline]
|
||||||
|
pub fn get_uiscreen(&self) -> id {
|
||||||
|
let class = Class::get("UIScreen").expect("Failed to get class `UIScreen`");
|
||||||
|
unsafe { msg_send![class, mainScreen] }
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_name(&self) -> Option<String> {
|
pub fn get_name(&self) -> Option<String> {
|
||||||
Some("Primary".to_string())
|
Some("Primary".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||||
unimplemented!()
|
let bounds: CGRect = unsafe { msg_send![self.get_uiscreen(), nativeBounds] };
|
||||||
|
(bounds.size.width as f64, bounds.size.height as f64).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_position(&self) -> (i32, i32) {
|
pub fn get_position(&self) -> PhysicalPosition {
|
||||||
// iOS assumes single screen
|
// iOS assumes single screen
|
||||||
(0, 0)
|
(0, 0).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_hidpi_factor(&self) -> f32 {
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
1.0
|
let scale: CGFloat = unsafe { msg_send![self.get_uiscreen(), nativeScale] };
|
||||||
|
scale as f64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EventsLoop {
|
pub struct EventsLoop {
|
||||||
delegate_state: *mut DelegateState
|
delegate_state: *mut DelegateState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -162,30 +209,26 @@ pub struct EventsLoopProxy;
|
||||||
impl EventsLoop {
|
impl EventsLoop {
|
||||||
pub fn new() -> EventsLoop {
|
pub fn new() -> EventsLoop {
|
||||||
unsafe {
|
unsafe {
|
||||||
if setjmp(mem::transmute(&mut jmpbuf)) != 0 {
|
if setjmp(mem::transmute(&mut JMPBUF)) != 0 {
|
||||||
let app: id = msg_send![Class::get("UIApplication").unwrap(), sharedApplication];
|
let app_class = Class::get("UIApplication").expect("Failed to get class `UIApplication`");
|
||||||
|
let app: id = msg_send![app_class, sharedApplication];
|
||||||
let delegate: id = msg_send![app, delegate];
|
let delegate: id = msg_send![app, delegate];
|
||||||
let state: *mut c_void = *(&*delegate).get_ivar("glutinState");
|
let state: *mut c_void = *(&*delegate).get_ivar("winitState");
|
||||||
let state = state as *mut DelegateState;
|
let delegate_state = state as *mut DelegateState;
|
||||||
|
return EventsLoop { delegate_state };
|
||||||
let events_loop = EventsLoop {
|
|
||||||
delegate_state: state
|
|
||||||
};
|
|
||||||
|
|
||||||
return events_loop;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
create_delegate_class();
|
|
||||||
create_view_class();
|
create_view_class();
|
||||||
|
create_delegate_class();
|
||||||
start_app();
|
start_app();
|
||||||
|
|
||||||
panic!("Couldn't create UIApplication")
|
panic!("Couldn't create `UIApplication`!")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||||
let mut rb = VecDeque::new();
|
let mut rb = VecDeque::with_capacity(1);
|
||||||
rb.push_back(MonitorId);
|
rb.push_back(MonitorId);
|
||||||
rb
|
rb
|
||||||
}
|
}
|
||||||
|
@ -207,7 +250,7 @@ impl EventsLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
// jump hack, so we won't quit on willTerminate event before processing it
|
// jump hack, so we won't quit on willTerminate event before processing it
|
||||||
if setjmp(mem::transmute(&mut jmpbuf)) != 0 {
|
if setjmp(mem::transmute(&mut JMPBUF)) != 0 {
|
||||||
if let Some(event) = state.events_queue.pop_front() {
|
if let Some(event) = state.events_queue.pop_front() {
|
||||||
callback(event);
|
callback(event);
|
||||||
return;
|
return;
|
||||||
|
@ -262,60 +305,83 @@ pub struct DeviceId;
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct PlatformSpecificWindowBuilderAttributes;
|
pub struct PlatformSpecificWindowBuilderAttributes;
|
||||||
|
|
||||||
|
// TODO: AFAIK transparency is enabled by default on iOS,
|
||||||
|
// so to be consistent with other platforms we have to change that.
|
||||||
impl Window {
|
impl Window {
|
||||||
pub fn new(ev: &EventsLoop, _: WindowAttributes, _: PlatformSpecificWindowBuilderAttributes)
|
pub fn new(
|
||||||
-> Result<Window, CreationError>
|
ev: &EventsLoop,
|
||||||
{
|
_attributes: WindowAttributes,
|
||||||
Ok(Window {
|
_pl_alltributes: PlatformSpecificWindowBuilderAttributes,
|
||||||
delegate_state: ev.delegate_state,
|
) -> Result<Window, CreationError> {
|
||||||
})
|
Ok(Window { delegate_state: ev.delegate_state })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_title(&self, _: &str) {
|
pub fn get_uiwindow(&self) -> id {
|
||||||
|
unsafe { (*self.delegate_state).window }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_uiview(&self) -> id {
|
||||||
|
unsafe { (*self.delegate_state).view }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_title(&self, _title: &str) {
|
||||||
|
// N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn show(&self) {
|
pub fn show(&self) {
|
||||||
|
// N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hide(&self) {
|
pub fn hide(&self) {
|
||||||
|
// N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||||
|
// N/A
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||||
|
// N/A
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_position(&self, _x: i32, _y: i32) {
|
pub fn set_position(&self, _position: LogicalPosition) {
|
||||||
|
// N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||||
unsafe { Some((&*self.delegate_state).size) }
|
unsafe { Some((&*self.delegate_state).size) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||||
self.get_inner_size()
|
self.get_inner_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_inner_size(&self, _x: u32, _y: u32) {
|
pub fn set_inner_size(&self, _size: LogicalSize) {
|
||||||
|
// N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_min_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
pub fn set_min_dimensions(&self, _dimensions: Option<LogicalSize>) {
|
||||||
|
// N/A
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
pub fn set_max_dimensions(&self, _dimensions: Option<LogicalSize>) {
|
||||||
|
// N/A
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_resizable(&self, _resizable: bool) {
|
pub fn set_resizable(&self, _resizable: bool) {
|
||||||
|
@ -323,50 +389,36 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
pub fn set_cursor(&self, _cursor: MouseCursor) {
|
||||||
unimplemented!();
|
// N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
pub fn set_cursor_state(&self, _cursor_state: CursorState) -> Result<(), String> {
|
||||||
unimplemented!()
|
// N/A
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_cursor(&self, _: MouseCursor) {
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_cursor_state(&self, _: CursorState) -> Result<(), String> {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hidpi_factor(&self) -> f32 {
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
unsafe { (&*self.delegate_state) }.scale
|
unsafe { (&*self.delegate_state) }.scale
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
|
pub fn set_cursor_position(&self, _position: LogicalPosition) -> Result<(), ()> {
|
||||||
unimplemented!();
|
// N/A
|
||||||
}
|
Ok(())
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn create_window_proxy(&self) -> WindowProxy {
|
|
||||||
WindowProxy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_maximized(&self, _maximized: bool) {
|
pub fn set_maximized(&self, _maximized: bool) {
|
||||||
|
// N/A
|
||||||
// iOS has single screen maximized apps so nothing to do
|
// iOS has single screen maximized apps so nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) {
|
pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) {
|
||||||
|
// N/A
|
||||||
// iOS has single screen maximized apps so nothing to do
|
// iOS has single screen maximized apps so nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,13 +438,13 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_ime_spot(&self, _x: i32, _y: i32) {
|
pub fn set_ime_spot(&self, _logical_spot: LogicalPosition) {
|
||||||
// N/A
|
// N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||||
RootMonitorId{inner: MonitorId}
|
RootMonitorId { inner: MonitorId }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -403,26 +455,32 @@ impl Window {
|
||||||
|
|
||||||
fn create_delegate_class() {
|
fn create_delegate_class() {
|
||||||
extern fn did_finish_launching(this: &mut Object, _: Sel, _: id, _: id) -> BOOL {
|
extern fn did_finish_launching(this: &mut Object, _: Sel, _: id, _: id) -> BOOL {
|
||||||
|
let screen_class = Class::get("UIScreen").expect("Failed to get class `UIScreen`");
|
||||||
|
let window_class = Class::get("UIWindow").expect("Failed to get class `UIWindow`");
|
||||||
|
let controller_class = Class::get("MainViewController").expect("Failed to get class `MainViewController`");
|
||||||
|
let view_class = Class::get("MainView").expect("Failed to get class `MainView`");
|
||||||
unsafe {
|
unsafe {
|
||||||
let main_screen: id = msg_send![Class::get("UIScreen").unwrap(), mainScreen];
|
let main_screen: id = msg_send![screen_class, mainScreen];
|
||||||
let bounds: CGRect = msg_send![main_screen, bounds];
|
let bounds: CGRect = msg_send![main_screen, bounds];
|
||||||
let scale: CGFloat = msg_send![main_screen, nativeScale];
|
let scale: CGFloat = msg_send![main_screen, nativeScale];
|
||||||
|
|
||||||
let window: id = msg_send![Class::get("UIWindow").unwrap(), alloc];
|
let window: id = msg_send![window_class, alloc];
|
||||||
let window: id = msg_send![window, initWithFrame:bounds.clone()];
|
let window: id = msg_send![window, initWithFrame:bounds.clone()];
|
||||||
|
|
||||||
let size = (bounds.size.width as u32, bounds.size.height as u32);
|
let size = (bounds.size.width as f64, bounds.size.height as f64).into();
|
||||||
|
|
||||||
let view_controller: id = msg_send![Class::get("MainViewController").unwrap(), alloc];
|
let view_controller: id = msg_send![controller_class, alloc];
|
||||||
let view_controller: id = msg_send![view_controller, init];
|
let view_controller: id = msg_send![view_controller, init];
|
||||||
|
|
||||||
|
let view: id = msg_send![view_class, alloc];
|
||||||
|
let view: id = msg_send![view, initForGl:&bounds];
|
||||||
|
|
||||||
let _: () = msg_send![window, setRootViewController:view_controller];
|
let _: () = msg_send![window, setRootViewController:view_controller];
|
||||||
let _: () = msg_send![window, makeKeyAndVisible];
|
let _: () = msg_send![window, makeKeyAndVisible];
|
||||||
|
|
||||||
let state = Box::new(DelegateState::new(window, view_controller, size, scale as f32));
|
let state = Box::new(DelegateState::new(window, view_controller, view, size, scale as f64));
|
||||||
let state_ptr: *mut DelegateState = mem::transmute(state);
|
let state_ptr: *mut DelegateState = mem::transmute(state);
|
||||||
this.set_ivar("glutinState", state_ptr as *mut c_void);
|
this.set_ivar("winitState", state_ptr as *mut c_void);
|
||||||
|
|
||||||
|
|
||||||
let _: () = msg_send![this, performSelector:sel!(postLaunch:) withObject:nil afterDelay:0.0];
|
let _: () = msg_send![this, performSelector:sel!(postLaunch:) withObject:nil afterDelay:0.0];
|
||||||
}
|
}
|
||||||
|
@ -430,12 +488,12 @@ fn create_delegate_class() {
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn post_launch(_: &Object, _: Sel, _: id) {
|
extern fn post_launch(_: &Object, _: Sel, _: id) {
|
||||||
unsafe { longjmp(mem::transmute(&mut jmpbuf),1); }
|
unsafe { longjmp(mem::transmute(&mut JMPBUF),1); }
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn did_become_active(this: &Object, _: Sel, _: id) {
|
extern fn did_become_active(this: &Object, _: Sel, _: id) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state: *mut c_void = *this.get_ivar("glutinState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
state.events_queue.push_back(Event::WindowEvent {
|
state.events_queue.push_back(Event::WindowEvent {
|
||||||
window_id: RootEventId(WindowId),
|
window_id: RootEventId(WindowId),
|
||||||
|
@ -446,7 +504,7 @@ fn create_delegate_class() {
|
||||||
|
|
||||||
extern fn will_resign_active(this: &Object, _: Sel, _: id) {
|
extern fn will_resign_active(this: &Object, _: Sel, _: id) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state: *mut c_void = *this.get_ivar("glutinState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
state.events_queue.push_back(Event::WindowEvent {
|
state.events_queue.push_back(Event::WindowEvent {
|
||||||
window_id: RootEventId(WindowId),
|
window_id: RootEventId(WindowId),
|
||||||
|
@ -457,7 +515,7 @@ fn create_delegate_class() {
|
||||||
|
|
||||||
extern fn will_enter_foreground(this: &Object, _: Sel, _: id) {
|
extern fn will_enter_foreground(this: &Object, _: Sel, _: id) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state: *mut c_void = *this.get_ivar("glutinState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
state.events_queue.push_back(Event::Suspended(false));
|
state.events_queue.push_back(Event::Suspended(false));
|
||||||
}
|
}
|
||||||
|
@ -465,7 +523,7 @@ fn create_delegate_class() {
|
||||||
|
|
||||||
extern fn did_enter_background(this: &Object, _: Sel, _: id) {
|
extern fn did_enter_background(this: &Object, _: Sel, _: id) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state: *mut c_void = *this.get_ivar("glutinState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
state.events_queue.push_back(Event::Suspended(true));
|
state.events_queue.push_back(Event::Suspended(true));
|
||||||
}
|
}
|
||||||
|
@ -473,7 +531,7 @@ fn create_delegate_class() {
|
||||||
|
|
||||||
extern fn will_terminate(this: &Object, _: Sel, _: id) {
|
extern fn will_terminate(this: &Object, _: Sel, _: id) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state: *mut c_void = *this.get_ivar("glutinState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
// push event to the front to garantee that we'll process it
|
// push event to the front to garantee that we'll process it
|
||||||
// immidiatly after jump
|
// immidiatly after jump
|
||||||
|
@ -481,13 +539,13 @@ fn create_delegate_class() {
|
||||||
window_id: RootEventId(WindowId),
|
window_id: RootEventId(WindowId),
|
||||||
event: WindowEvent::Destroyed,
|
event: WindowEvent::Destroyed,
|
||||||
});
|
});
|
||||||
longjmp(mem::transmute(&mut jmpbuf),1);
|
longjmp(mem::transmute(&mut JMPBUF),1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn handle_touches(this: &Object, _: Sel, touches: id, _:id) {
|
extern fn handle_touches(this: &Object, _: Sel, touches: id, _:id) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state: *mut c_void = *this.get_ivar("glutinState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
|
|
||||||
let touches_enum: id = msg_send![touches, objectEnumerator];
|
let touches_enum: id = msg_send![touches, objectEnumerator];
|
||||||
|
@ -506,7 +564,7 @@ fn create_delegate_class() {
|
||||||
event: WindowEvent::Touch(Touch {
|
event: WindowEvent::Touch(Touch {
|
||||||
device_id: DEVICE_ID,
|
device_id: DEVICE_ID,
|
||||||
id: touch_id,
|
id: touch_id,
|
||||||
location: (location.x as f64, location.y as f64),
|
location: (location.x as f64, location.y as f64).into(),
|
||||||
phase: match phase {
|
phase: match phase {
|
||||||
0 => TouchPhase::Started,
|
0 => TouchPhase::Started,
|
||||||
1 => TouchPhase::Moved,
|
1 => TouchPhase::Moved,
|
||||||
|
@ -521,8 +579,8 @@ fn create_delegate_class() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ui_responder = Class::get("UIResponder").unwrap();
|
let ui_responder = Class::get("UIResponder").expect("Failed to get class `UIResponder`");
|
||||||
let mut decl = ClassDecl::new("AppDelegate", ui_responder).unwrap();
|
let mut decl = ClassDecl::new("AppDelegate", ui_responder).expect("Failed to declare class `AppDelegate`");
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
decl.add_method(sel!(application:didFinishLaunchingWithOptions:),
|
decl.add_method(sel!(application:didFinishLaunchingWithOptions:),
|
||||||
|
@ -560,17 +618,45 @@ fn create_delegate_class() {
|
||||||
decl.add_method(sel!(postLaunch:),
|
decl.add_method(sel!(postLaunch:),
|
||||||
post_launch as extern fn(&Object, Sel, id));
|
post_launch as extern fn(&Object, Sel, id));
|
||||||
|
|
||||||
decl.add_ivar::<*mut c_void>("glutinState");
|
decl.add_ivar::<*mut c_void>("winitState");
|
||||||
|
|
||||||
decl.register();
|
decl.register();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_view_class() {
|
// TODO: winit shouldn't contain GL-specfiic code
|
||||||
let ui_view_controller = Class::get("UIViewController").unwrap();
|
pub fn create_view_class() {
|
||||||
let decl = ClassDecl::new("MainViewController", ui_view_controller).unwrap();
|
let superclass = Class::get("UIViewController").expect("Failed to get class `UIViewController`");
|
||||||
|
let decl = ClassDecl::new("MainViewController", superclass).expect("Failed to declare class `MainViewController`");
|
||||||
decl.register();
|
decl.register();
|
||||||
|
|
||||||
|
extern fn init_for_gl(this: &Object, _: Sel, frame: *const c_void) -> id {
|
||||||
|
unsafe {
|
||||||
|
let bounds = frame as *const CGRect;
|
||||||
|
let view: id = msg_send![this, initWithFrame:(*bounds).clone()];
|
||||||
|
|
||||||
|
let mask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||||
|
let _: () = msg_send![view, setAutoresizingMask:mask];
|
||||||
|
let _: () = msg_send![view, setAutoresizesSubviews:YES];
|
||||||
|
|
||||||
|
let layer: id = msg_send![view, layer];
|
||||||
|
let _ : () = msg_send![layer, setOpaque:YES];
|
||||||
|
|
||||||
|
view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn layer_class(_: &Class, _: Sel) -> *const Class {
|
||||||
|
unsafe { mem::transmute(Class::get("CAEAGLLayer").expect("Failed to get class `CAEAGLLayer`")) }
|
||||||
|
}
|
||||||
|
|
||||||
|
let superclass = Class::get("GLKView").expect("Failed to get class `GLKView`");
|
||||||
|
let mut decl = ClassDecl::new("MainView", superclass).expect("Failed to declare class `MainView`");
|
||||||
|
unsafe {
|
||||||
|
decl.add_method(sel!(initForGl:), init_for_gl as extern fn(&Object, Sel, *const c_void) -> id);
|
||||||
|
decl.add_class_method(sel!(layerClass), layer_class as extern fn(&Class, Sel) -> *const Class);
|
||||||
|
decl.register();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -8,15 +8,16 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use sctk::reexports::client::ConnectError;
|
use sctk::reexports::client::ConnectError;
|
||||||
|
|
||||||
// `std::os::raw::c_void` and `libc::c_void` are NOT interchangeable!
|
|
||||||
use libc;
|
|
||||||
|
|
||||||
use {
|
use {
|
||||||
CreationError,
|
CreationError,
|
||||||
CursorState,
|
CursorState,
|
||||||
EventsLoopClosed,
|
EventsLoopClosed,
|
||||||
Icon,
|
Icon,
|
||||||
|
LogicalPosition,
|
||||||
|
LogicalSize,
|
||||||
MouseCursor,
|
MouseCursor,
|
||||||
|
PhysicalPosition,
|
||||||
|
PhysicalSize,
|
||||||
ControlFlow,
|
ControlFlow,
|
||||||
WindowAttributes,
|
WindowAttributes,
|
||||||
};
|
};
|
||||||
|
@ -57,19 +58,19 @@ thread_local!(
|
||||||
|
|
||||||
pub enum Window {
|
pub enum Window {
|
||||||
X(x11::Window),
|
X(x11::Window),
|
||||||
Wayland(wayland::Window)
|
Wayland(wayland::Window),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum WindowId {
|
pub enum WindowId {
|
||||||
X(x11::WindowId),
|
X(x11::WindowId),
|
||||||
Wayland(wayland::WindowId)
|
Wayland(wayland::WindowId),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum DeviceId {
|
pub enum DeviceId {
|
||||||
X(x11::DeviceId),
|
X(x11::DeviceId),
|
||||||
Wayland(wayland::DeviceId)
|
Wayland(wayland::DeviceId),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -96,7 +97,7 @@ impl MonitorId {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||||
match self {
|
match self {
|
||||||
&MonitorId::X(ref m) => m.get_dimensions(),
|
&MonitorId::X(ref m) => m.get_dimensions(),
|
||||||
&MonitorId::Wayland(ref m) => m.get_dimensions(),
|
&MonitorId::Wayland(ref m) => m.get_dimensions(),
|
||||||
|
@ -104,7 +105,7 @@ impl MonitorId {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_position(&self) -> (i32, i32) {
|
pub fn get_position(&self) -> PhysicalPosition {
|
||||||
match self {
|
match self {
|
||||||
&MonitorId::X(ref m) => m.get_position(),
|
&MonitorId::X(ref m) => m.get_position(),
|
||||||
&MonitorId::Wayland(ref m) => m.get_position(),
|
&MonitorId::Wayland(ref m) => m.get_position(),
|
||||||
|
@ -112,10 +113,10 @@ impl MonitorId {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_hidpi_factor(&self) -> f32 {
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
match self {
|
match self {
|
||||||
&MonitorId::X(ref m) => m.get_hidpi_factor(),
|
&MonitorId::X(ref m) => m.get_hidpi_factor(),
|
||||||
&MonitorId::Wayland(ref m) => m.get_hidpi_factor(),
|
&MonitorId::Wayland(ref m) => m.get_hidpi_factor() as f64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,7 +142,7 @@ impl Window {
|
||||||
pub fn id(&self) -> WindowId {
|
pub fn id(&self) -> WindowId {
|
||||||
match self {
|
match self {
|
||||||
&Window::X(ref w) => WindowId::X(w.id()),
|
&Window::X(ref w) => WindowId::X(w.id()),
|
||||||
&Window::Wayland(ref w) => WindowId::Wayland(w.id())
|
&Window::Wayland(ref w) => WindowId::Wayland(w.id()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +150,7 @@ impl Window {
|
||||||
pub fn set_title(&self, title: &str) {
|
pub fn set_title(&self, title: &str) {
|
||||||
match self {
|
match self {
|
||||||
&Window::X(ref w) => w.set_title(title),
|
&Window::X(ref w) => w.set_title(title),
|
||||||
&Window::Wayland(ref w) => w.set_title(title)
|
&Window::Wayland(ref w) => w.set_title(title),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +158,7 @@ impl Window {
|
||||||
pub fn show(&self) {
|
pub fn show(&self) {
|
||||||
match self {
|
match self {
|
||||||
&Window::X(ref w) => w.show(),
|
&Window::X(ref w) => w.show(),
|
||||||
&Window::Wayland(ref w) => w.show()
|
&Window::Wayland(ref w) => w.show(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,20 +166,20 @@ impl Window {
|
||||||
pub fn hide(&self) {
|
pub fn hide(&self) {
|
||||||
match self {
|
match self {
|
||||||
&Window::X(ref w) => w.hide(),
|
&Window::X(ref w) => w.hide(),
|
||||||
&Window::Wayland(ref w) => w.hide()
|
&Window::Wayland(ref w) => w.hide(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||||
match self {
|
match self {
|
||||||
&Window::X(ref w) => w.get_position(),
|
&Window::X(ref w) => w.get_position(),
|
||||||
&Window::Wayland(ref w) => w.get_position()
|
&Window::Wayland(ref w) => w.get_position(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||||
match self {
|
match self {
|
||||||
&Window::X(ref m) => m.get_inner_position(),
|
&Window::X(ref m) => m.get_inner_position(),
|
||||||
&Window::Wayland(ref m) => m.get_inner_position(),
|
&Window::Wayland(ref m) => m.get_inner_position(),
|
||||||
|
@ -186,50 +187,50 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_position(&self, x: i32, y: i32) {
|
pub fn set_position(&self, position: LogicalPosition) {
|
||||||
match self {
|
match self {
|
||||||
&Window::X(ref w) => w.set_position(x, y),
|
&Window::X(ref w) => w.set_position(position),
|
||||||
&Window::Wayland(ref w) => w.set_position(x, y)
|
&Window::Wayland(ref w) => w.set_position(position),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||||
match self {
|
match self {
|
||||||
&Window::X(ref w) => w.get_inner_size(),
|
&Window::X(ref w) => w.get_inner_size(),
|
||||||
&Window::Wayland(ref w) => w.get_inner_size()
|
&Window::Wayland(ref w) => w.get_inner_size(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||||
match self {
|
match self {
|
||||||
&Window::X(ref w) => w.get_outer_size(),
|
&Window::X(ref w) => w.get_outer_size(),
|
||||||
&Window::Wayland(ref w) => w.get_outer_size()
|
&Window::Wayland(ref w) => w.get_outer_size(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_inner_size(&self, x: u32, y: u32) {
|
pub fn set_inner_size(&self, size: LogicalSize) {
|
||||||
match self {
|
match self {
|
||||||
&Window::X(ref w) => w.set_inner_size(x, y),
|
&Window::X(ref w) => w.set_inner_size(size),
|
||||||
&Window::Wayland(ref w) => w.set_inner_size(x, y)
|
&Window::Wayland(ref w) => w.set_inner_size(size),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
pub fn set_min_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||||
match self {
|
match self {
|
||||||
&Window::X(ref w) => w.set_min_dimensions(dimensions),
|
&Window::X(ref w) => w.set_min_dimensions(dimensions),
|
||||||
&Window::Wayland(ref w) => w.set_min_dimensions(dimensions)
|
&Window::Wayland(ref w) => w.set_min_dimensions(dimensions),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
pub fn set_max_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||||
match self {
|
match self {
|
||||||
&Window::X(ref w) => w.set_max_dimensions(dimensions),
|
&Window::X(ref w) => w.set_max_dimensions(dimensions),
|
||||||
&Window::Wayland(ref w) => w.set_max_dimensions(dimensions)
|
&Window::Wayland(ref w) => w.set_max_dimensions(dimensions),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,34 +259,18 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hidpi_factor(&self) -> f32 {
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
match self {
|
match self {
|
||||||
&Window::X(ref w) => w.hidpi_factor(),
|
&Window::X(ref w) => w.get_hidpi_factor(),
|
||||||
&Window::Wayland(ref w) => w.hidpi_factor()
|
&Window::Wayland(ref w) => w.hidpi_factor() as f64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), ()> {
|
||||||
match self {
|
match self {
|
||||||
&Window::X(ref w) => w.set_cursor_position(x, y),
|
&Window::X(ref w) => w.set_cursor_position(position),
|
||||||
&Window::Wayland(ref w) => w.set_cursor_position(x, y)
|
&Window::Wayland(ref w) => w.set_cursor_position(position),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
|
||||||
match self {
|
|
||||||
&Window::X(ref w) => w.platform_display(),
|
|
||||||
&Window::Wayland(ref w) => w.get_display().c_ptr() as *mut _
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
|
||||||
match self {
|
|
||||||
&Window::X(ref w) => w.platform_window(),
|
|
||||||
&Window::Wayland(ref w) => w.get_surface().c_ptr() as *mut _
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,9 +315,9 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_ime_spot(&self, x: i32, y: i32) {
|
pub fn set_ime_spot(&self, position: LogicalPosition) {
|
||||||
match self {
|
match self {
|
||||||
&Window::X(ref w) => w.send_xim_spot(x as i16, y as i16),
|
&Window::X(ref w) => w.set_ime_spot(position),
|
||||||
&Window::Wayland(_) => (),
|
&Window::Wayland(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -447,11 +432,14 @@ r#"Failed to initialize any backend!
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||||
match *self {
|
match *self {
|
||||||
EventsLoop::Wayland(ref evlp) => evlp.get_available_monitors()
|
EventsLoop::Wayland(ref evlp) => evlp
|
||||||
|
.get_available_monitors()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(MonitorId::Wayland)
|
.map(MonitorId::Wayland)
|
||||||
.collect(),
|
.collect(),
|
||||||
EventsLoop::X(ref evlp) => x11::get_available_monitors(evlp.x_connection())
|
EventsLoop::X(ref evlp) => evlp
|
||||||
|
.x_connection()
|
||||||
|
.get_available_monitors()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(MonitorId::X)
|
.map(MonitorId::X)
|
||||||
.collect(),
|
.collect(),
|
||||||
|
@ -462,7 +450,7 @@ r#"Failed to initialize any backend!
|
||||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||||
match *self {
|
match *self {
|
||||||
EventsLoop::Wayland(ref evlp) => MonitorId::Wayland(evlp.get_primary_monitor()),
|
EventsLoop::Wayland(ref evlp) => MonitorId::Wayland(evlp.get_primary_monitor()),
|
||||||
EventsLoop::X(ref evlp) => MonitorId::X(x11::get_primary_monitor(evlp.x_connection())),
|
EventsLoop::X(ref evlp) => MonitorId::X(evlp.x_connection().get_primary_monitor()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::fmt;
|
||||||
use std::sync::{Arc, Mutex, Weak};
|
use std::sync::{Arc, Mutex, Weak};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
use {ControlFlow, EventsLoopClosed};
|
use {ControlFlow, EventsLoopClosed, PhysicalPosition, PhysicalSize};
|
||||||
|
|
||||||
use super::WindowId;
|
use super::WindowId;
|
||||||
use super::window::WindowStore;
|
use super::window::WindowStore;
|
||||||
|
@ -248,16 +248,21 @@ impl EventsLoop {
|
||||||
}
|
}
|
||||||
// process pending resize/refresh
|
// process pending resize/refresh
|
||||||
self.store.lock().unwrap().for_each(
|
self.store.lock().unwrap().for_each(
|
||||||
|newsize, refresh, frame_refresh, closed, wid, frame| {
|
|newsize, size, new_dpi, refresh, frame_refresh, closed, wid, frame| {
|
||||||
if let Some(frame) = frame {
|
if let Some(frame) = frame {
|
||||||
if let Some((w, h)) = newsize {
|
if let Some((w, h)) = newsize {
|
||||||
frame.resize(w as u32, h as u32);
|
frame.resize(w, h);
|
||||||
frame.refresh();
|
frame.refresh();
|
||||||
sink.send_event(::WindowEvent::Resized(w as u32, h as u32), wid);
|
let logical_size = ::LogicalSize::new(w as f64, h as f64);
|
||||||
|
sink.send_event(::WindowEvent::Resized(logical_size), wid);
|
||||||
|
*size = (w, h);
|
||||||
} else if frame_refresh {
|
} else if frame_refresh {
|
||||||
frame.refresh();
|
frame.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(dpi) = new_dpi {
|
||||||
|
sink.send_event(::WindowEvent::HiDpiFactorChanged(dpi as f64), wid);
|
||||||
|
}
|
||||||
if refresh {
|
if refresh {
|
||||||
sink.send_event(::WindowEvent::Refresh, wid);
|
sink.send_event(::WindowEvent::Refresh, wid);
|
||||||
}
|
}
|
||||||
|
@ -434,9 +439,9 @@ impl fmt::Debug for MonitorId {
|
||||||
struct MonitorId {
|
struct MonitorId {
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
native_identifier: u32,
|
native_identifier: u32,
|
||||||
dimensions: (u32, u32),
|
dimensions: PhysicalSize,
|
||||||
position: (i32, i32),
|
position: PhysicalPosition,
|
||||||
hidpi_factor: f32,
|
hidpi_factor: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
let monitor_id_proxy = MonitorId {
|
let monitor_id_proxy = MonitorId {
|
||||||
|
@ -463,7 +468,7 @@ impl MonitorId {
|
||||||
self.mgr.with_info(&self.proxy, |id, _| id).unwrap_or(0)
|
self.mgr.with_info(&self.proxy, |id, _| id).unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||||
match self.mgr.with_info(&self.proxy, |_, info| {
|
match self.mgr.with_info(&self.proxy, |_, info| {
|
||||||
info.modes
|
info.modes
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -472,19 +477,20 @@ impl MonitorId {
|
||||||
}) {
|
}) {
|
||||||
Some(Some((w, h))) => (w as u32, h as u32),
|
Some(Some((w, h))) => (w as u32, h as u32),
|
||||||
_ => (0, 0),
|
_ => (0, 0),
|
||||||
}
|
}.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_position(&self) -> (i32, i32) {
|
pub fn get_position(&self) -> PhysicalPosition {
|
||||||
self.mgr
|
self.mgr
|
||||||
.with_info(&self.proxy, |_, info| info.location)
|
.with_info(&self.proxy, |_, info| info.location)
|
||||||
.unwrap_or((0, 0))
|
.unwrap_or((0, 0))
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_hidpi_factor(&self) -> f32 {
|
pub fn get_hidpi_factor(&self) -> i32 {
|
||||||
self.mgr
|
self.mgr
|
||||||
.with_info(&self.proxy, |_, info| info.scale_factor as f32)
|
.with_info(&self.proxy, |_, info| info.scale_factor)
|
||||||
.unwrap_or(1.0)
|
.unwrap_or(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub fn implement_pointer(
|
||||||
sink.send_event(
|
sink.send_event(
|
||||||
WindowEvent::CursorMoved {
|
WindowEvent::CursorMoved {
|
||||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||||
position: (surface_x, surface_y),
|
position: (surface_x, surface_y).into(),
|
||||||
// TODO: replace dummy value with actual modifier state
|
// TODO: replace dummy value with actual modifier state
|
||||||
modifiers: ModifiersState::default(),
|
modifiers: ModifiersState::default(),
|
||||||
},
|
},
|
||||||
|
@ -71,7 +71,7 @@ pub fn implement_pointer(
|
||||||
sink.send_event(
|
sink.send_event(
|
||||||
WindowEvent::CursorMoved {
|
WindowEvent::CursorMoved {
|
||||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||||
position: (surface_x, surface_y),
|
position: (surface_x, surface_y).into(),
|
||||||
// TODO: replace dummy value with actual modifier state
|
// TODO: replace dummy value with actual modifier state
|
||||||
modifiers: ModifiersState::default(),
|
modifiers: ModifiersState::default(),
|
||||||
},
|
},
|
||||||
|
@ -117,7 +117,7 @@ pub fn implement_pointer(
|
||||||
sink.send_event(
|
sink.send_event(
|
||||||
WindowEvent::MouseWheel {
|
WindowEvent::MouseWheel {
|
||||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
|
||||||
phase: TouchPhase::Moved,
|
phase: TouchPhase::Moved,
|
||||||
// TODO: replace dummy value with actual modifier state
|
// TODO: replace dummy value with actual modifier state
|
||||||
modifiers: ModifiersState::default(),
|
modifiers: ModifiersState::default(),
|
||||||
|
@ -158,7 +158,7 @@ pub fn implement_pointer(
|
||||||
sink.send_event(
|
sink.send_event(
|
||||||
WindowEvent::MouseWheel {
|
WindowEvent::MouseWheel {
|
||||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
|
||||||
phase: axis_state,
|
phase: axis_state,
|
||||||
// TODO: replace dummy value with actual modifier state
|
// TODO: replace dummy value with actual modifier state
|
||||||
modifiers: ModifiersState::default(),
|
modifiers: ModifiersState::default(),
|
||||||
|
|
|
@ -34,7 +34,7 @@ pub(crate) fn implement_touch(
|
||||||
WindowEvent::Touch(::Touch {
|
WindowEvent::Touch(::Touch {
|
||||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||||
phase: TouchPhase::Started,
|
phase: TouchPhase::Started,
|
||||||
location: (x, y),
|
location: (x, y).into(),
|
||||||
id: id as u64,
|
id: id as u64,
|
||||||
}),
|
}),
|
||||||
wid,
|
wid,
|
||||||
|
@ -54,7 +54,7 @@ pub(crate) fn implement_touch(
|
||||||
WindowEvent::Touch(::Touch {
|
WindowEvent::Touch(::Touch {
|
||||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||||
phase: TouchPhase::Ended,
|
phase: TouchPhase::Ended,
|
||||||
location: pt.location,
|
location: pt.location.into(),
|
||||||
id: id as u64,
|
id: id as u64,
|
||||||
}),
|
}),
|
||||||
pt.wid,
|
pt.wid,
|
||||||
|
@ -69,7 +69,7 @@ pub(crate) fn implement_touch(
|
||||||
WindowEvent::Touch(::Touch {
|
WindowEvent::Touch(::Touch {
|
||||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||||
phase: TouchPhase::Moved,
|
phase: TouchPhase::Moved,
|
||||||
location: (x, y),
|
location: (x, y).into(),
|
||||||
id: id as u64,
|
id: id as u64,
|
||||||
}),
|
}),
|
||||||
pt.wid,
|
pt.wid,
|
||||||
|
@ -82,7 +82,7 @@ pub(crate) fn implement_touch(
|
||||||
WindowEvent::Touch(::Touch {
|
WindowEvent::Touch(::Touch {
|
||||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||||
phase: TouchPhase::Cancelled,
|
phase: TouchPhase::Cancelled,
|
||||||
location: pt.location,
|
location: pt.location.into(),
|
||||||
id: pt.id as u64,
|
id: pt.id as u64,
|
||||||
}),
|
}),
|
||||||
pt.wid,
|
pt.wid,
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use std::sync::{Arc, Mutex, Weak};
|
use std::sync::{Arc, Mutex, Weak};
|
||||||
|
|
||||||
use {CreationError, CursorState, MouseCursor, WindowAttributes};
|
use {CreationError, CursorState, MouseCursor, WindowAttributes, LogicalPosition, LogicalSize};
|
||||||
use platform::MonitorId as PlatformMonitorId;
|
use platform::MonitorId as PlatformMonitorId;
|
||||||
use window::MonitorId as RootMonitorId;
|
use window::MonitorId as RootMonitorId;
|
||||||
|
|
||||||
use sctk::window::{BasicFrame, Event as WEvent, Window as SWindow};
|
use sctk::window::{BasicFrame, Event as WEvent, Window as SWindow};
|
||||||
use sctk::reexports::client::{Display, Proxy};
|
use sctk::reexports::client::{Display, Proxy};
|
||||||
use sctk::reexports::client::protocol::{wl_seat, wl_surface};
|
use sctk::reexports::client::protocol::{wl_seat, wl_surface, wl_output};
|
||||||
use sctk::reexports::client::protocol::wl_compositor::RequestsTrait as CompositorRequests;
|
use sctk::reexports::client::protocol::wl_compositor::RequestsTrait as CompositorRequests;
|
||||||
use sctk::reexports::client::protocol::wl_surface::RequestsTrait as SurfaceRequests;
|
use sctk::reexports::client::protocol::wl_surface::RequestsTrait as SurfaceRequests;
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ use super::{make_wid, EventsLoop, MonitorId, WindowId};
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
surface: Proxy<wl_surface::WlSurface>,
|
surface: Proxy<wl_surface::WlSurface>,
|
||||||
frame: Arc<Mutex<SWindow<BasicFrame>>>,
|
frame: Arc<Mutex<SWindow<BasicFrame>>>,
|
||||||
monitors: Arc<Mutex<Vec<MonitorId>>>,
|
monitors: Arc<Mutex<MonitorList>>,
|
||||||
size: Arc<Mutex<(u32, u32)>>,
|
size: Arc<Mutex<(u32, u32)>>,
|
||||||
kill_switch: (Arc<Mutex<bool>>, Arc<Mutex<bool>>),
|
kill_switch: (Arc<Mutex<bool>>, Arc<Mutex<bool>>),
|
||||||
display: Arc<Display>,
|
display: Arc<Display>,
|
||||||
|
@ -24,23 +24,42 @@ pub struct Window {
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
pub fn new(evlp: &EventsLoop, attributes: WindowAttributes) -> Result<Window, CreationError> {
|
pub fn new(evlp: &EventsLoop, attributes: WindowAttributes) -> Result<Window, CreationError> {
|
||||||
let (width, height) = attributes.dimensions.unwrap_or((800, 600));
|
// TODO: Update for new DPI API
|
||||||
|
//let (width, height) = attributes.dimensions.unwrap_or((800, 600));
|
||||||
|
let (width, height) = (64, 64);
|
||||||
// Create the window
|
// Create the window
|
||||||
let size = Arc::new(Mutex::new((width, height)));
|
let size = Arc::new(Mutex::new((width, height)));
|
||||||
|
|
||||||
// monitor tracking
|
// monitor tracking
|
||||||
let monitor_list = Arc::new(Mutex::new(Vec::new()));
|
let monitor_list = Arc::new(Mutex::new(MonitorList::new()));
|
||||||
|
|
||||||
let surface = evlp.env.compositor.create_surface().unwrap().implement({
|
let surface = evlp.env.compositor.create_surface().unwrap().implement({
|
||||||
let list = monitor_list.clone();
|
let list = monitor_list.clone();
|
||||||
let omgr = evlp.env.outputs.clone();
|
let omgr = evlp.env.outputs.clone();
|
||||||
move |event, _| match event {
|
let window_store = evlp.store.clone();
|
||||||
wl_surface::Event::Enter { output } => list.lock().unwrap().push(MonitorId {
|
move |event, surface: Proxy<wl_surface::WlSurface>| match event {
|
||||||
|
wl_surface::Event::Enter { output } => {
|
||||||
|
let dpi_change = list.lock().unwrap().add_output(MonitorId {
|
||||||
proxy: output,
|
proxy: output,
|
||||||
mgr: omgr.clone(),
|
mgr: omgr.clone(),
|
||||||
}),
|
});
|
||||||
|
if let Some(dpi) = dpi_change {
|
||||||
|
if surface.version() >= 3 {
|
||||||
|
// without version 3 we can't be dpi aware
|
||||||
|
window_store.lock().unwrap().dpi_change(&surface, dpi);
|
||||||
|
surface.set_buffer_scale(dpi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
wl_surface::Event::Leave { output } => {
|
wl_surface::Event::Leave { output } => {
|
||||||
list.lock().unwrap().retain(|m| !m.proxy.equals(&output));
|
let dpi_change = list.lock().unwrap().del_output(&output);
|
||||||
|
if let Some(dpi) = dpi_change {
|
||||||
|
if surface.version() >= 3 {
|
||||||
|
// without version 3 we can't be dpi aware
|
||||||
|
window_store.lock().unwrap().dpi_change(&surface, dpi);
|
||||||
|
surface.set_buffer_scale(dpi);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -59,7 +78,7 @@ impl Window {
|
||||||
let mut store = window_store.lock().unwrap();
|
let mut store = window_store.lock().unwrap();
|
||||||
for window in &mut store.windows {
|
for window in &mut store.windows {
|
||||||
if window.surface.equals(&my_surface) {
|
if window.surface.equals(&my_surface) {
|
||||||
window.newsize = new_size.map(|(w, h)| (w as i32, h as i32));
|
window.newsize = new_size;
|
||||||
window.need_refresh = true;
|
window.need_refresh = true;
|
||||||
*(window.need_frame_refresh.lock().unwrap()) = true;
|
*(window.need_frame_refresh.lock().unwrap()) = true;
|
||||||
return;
|
return;
|
||||||
|
@ -107,8 +126,9 @@ impl Window {
|
||||||
frame.set_decorate(attributes.decorations);
|
frame.set_decorate(attributes.decorations);
|
||||||
|
|
||||||
// min-max dimensions
|
// min-max dimensions
|
||||||
frame.set_min_size(attributes.min_dimensions);
|
// TODO: Update for new DPI API
|
||||||
frame.set_max_size(attributes.max_dimensions);
|
//frame.set_min_size(attributes.min_dimensions);
|
||||||
|
//frame.set_max_size(attributes.max_dimensions);
|
||||||
|
|
||||||
let kill_switch = Arc::new(Mutex::new(false));
|
let kill_switch = Arc::new(Mutex::new(false));
|
||||||
let need_frame_refresh = Arc::new(Mutex::new(true));
|
let need_frame_refresh = Arc::new(Mutex::new(true));
|
||||||
|
@ -117,11 +137,14 @@ impl Window {
|
||||||
evlp.store.lock().unwrap().windows.push(InternalWindow {
|
evlp.store.lock().unwrap().windows.push(InternalWindow {
|
||||||
closed: false,
|
closed: false,
|
||||||
newsize: None,
|
newsize: None,
|
||||||
|
size: size.clone(),
|
||||||
need_refresh: false,
|
need_refresh: false,
|
||||||
need_frame_refresh: need_frame_refresh.clone(),
|
need_frame_refresh: need_frame_refresh.clone(),
|
||||||
surface: surface.clone(),
|
surface: surface.clone(),
|
||||||
kill_switch: kill_switch.clone(),
|
kill_switch: kill_switch.clone(),
|
||||||
frame: Arc::downgrade(&frame),
|
frame: Arc::downgrade(&frame),
|
||||||
|
current_dpi: 1,
|
||||||
|
new_dpi: None,
|
||||||
});
|
});
|
||||||
evlp.evq.borrow_mut().sync_roundtrip().unwrap();
|
evlp.evq.borrow_mut().sync_roundtrip().unwrap();
|
||||||
|
|
||||||
|
@ -156,48 +179,49 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||||
// Not possible with wayland
|
// Not possible with wayland
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||||
// Not possible with wayland
|
// Not possible with wayland
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_position(&self, _x: i32, _y: i32) {
|
pub fn set_position(&self, _pos: LogicalPosition) {
|
||||||
// Not possible with wayland
|
// Not possible with wayland
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||||
Some(self.size.lock().unwrap().clone())
|
Some(self.size.lock().unwrap().clone().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||||
let (w, h) = self.size.lock().unwrap().clone();
|
let (w, h) = self.size.lock().unwrap().clone();
|
||||||
// let (w, h) = super::wayland_window::add_borders(w as i32, h as i32);
|
// let (w, h) = super::wayland_window::add_borders(w as i32, h as i32);
|
||||||
Some((w as u32, h as u32))
|
Some((w, h).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
// NOTE: This will only resize the borders, the contents must be updated by the user
|
// NOTE: This will only resize the borders, the contents must be updated by the user
|
||||||
pub fn set_inner_size(&self, x: u32, y: u32) {
|
pub fn set_inner_size(&self, size: LogicalSize) {
|
||||||
self.frame.lock().unwrap().resize(x, y);
|
let (w, h) = size.into();
|
||||||
*(self.size.lock().unwrap()) = (x, y);
|
self.frame.lock().unwrap().resize(w, h);
|
||||||
|
*(self.size.lock().unwrap()) = (w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
pub fn set_min_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||||
self.frame.lock().unwrap().set_min_size(dimensions);
|
self.frame.lock().unwrap().set_min_size(dimensions.map(Into::into));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
pub fn set_max_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||||
self.frame.lock().unwrap().set_max_size(dimensions);
|
self.frame.lock().unwrap().set_max_size(dimensions.map(Into::into));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -222,14 +246,8 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hidpi_factor(&self) -> f32 {
|
pub fn hidpi_factor(&self) -> i32 {
|
||||||
let mut factor: f32 = 1.0;
|
self.monitors.lock().unwrap().compute_hidpi_factor()
|
||||||
let guard = self.monitors.lock().unwrap();
|
|
||||||
for monitor_id in guard.iter() {
|
|
||||||
let hidpif = monitor_id.get_hidpi_factor();
|
|
||||||
factor = factor.max(hidpif);
|
|
||||||
}
|
|
||||||
factor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_decorations(&self, decorate: bool) {
|
pub fn set_decorations(&self, decorate: bool) {
|
||||||
|
@ -260,7 +278,7 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
|
pub fn set_cursor_position(&self, _pos: LogicalPosition) -> Result<(), ()> {
|
||||||
// TODO: not yet possible on wayland
|
// TODO: not yet possible on wayland
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
|
@ -277,7 +295,7 @@ impl Window {
|
||||||
// we don't know how much each monitor sees us so...
|
// we don't know how much each monitor sees us so...
|
||||||
// just return the most recent one ?
|
// just return the most recent one ?
|
||||||
let guard = self.monitors.lock().unwrap();
|
let guard = self.monitors.lock().unwrap();
|
||||||
guard.last().unwrap().clone()
|
guard.monitors.last().unwrap().clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,12 +312,15 @@ impl Drop for Window {
|
||||||
|
|
||||||
struct InternalWindow {
|
struct InternalWindow {
|
||||||
surface: Proxy<wl_surface::WlSurface>,
|
surface: Proxy<wl_surface::WlSurface>,
|
||||||
newsize: Option<(i32, i32)>,
|
newsize: Option<(u32, u32)>,
|
||||||
|
size: Arc<Mutex<(u32, u32)>>,
|
||||||
need_refresh: bool,
|
need_refresh: bool,
|
||||||
need_frame_refresh: Arc<Mutex<bool>>,
|
need_frame_refresh: Arc<Mutex<bool>>,
|
||||||
closed: bool,
|
closed: bool,
|
||||||
kill_switch: Arc<Mutex<bool>>,
|
kill_switch: Arc<Mutex<bool>>,
|
||||||
frame: Weak<Mutex<SWindow<BasicFrame>>>,
|
frame: Weak<Mutex<SWindow<BasicFrame>>>,
|
||||||
|
current_dpi: i32,
|
||||||
|
new_dpi: Option<i32>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WindowStore {
|
pub struct WindowStore {
|
||||||
|
@ -345,24 +366,84 @@ impl WindowStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dpi_change(&mut self, surface: &Proxy<wl_surface::WlSurface>, new: i32) {
|
||||||
|
for window in &mut self.windows {
|
||||||
|
if surface.equals(&window.surface) {
|
||||||
|
window.new_dpi = Some(new);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn for_each<F>(&mut self, mut f: F)
|
pub fn for_each<F>(&mut self, mut f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(Option<(i32, i32)>, bool, bool, bool, WindowId, Option<&mut SWindow<BasicFrame>>),
|
F: FnMut(Option<(u32, u32)>, &mut (u32, u32), Option<i32>, bool, bool, bool, WindowId, Option<&mut SWindow<BasicFrame>>),
|
||||||
{
|
{
|
||||||
for window in &mut self.windows {
|
for window in &mut self.windows {
|
||||||
let opt_arc = window.frame.upgrade();
|
let opt_arc = window.frame.upgrade();
|
||||||
let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap());
|
let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap());
|
||||||
f(
|
f(
|
||||||
window.newsize.take(),
|
window.newsize.take(),
|
||||||
|
&mut *(window.size.lock().unwrap()),
|
||||||
|
window.new_dpi,
|
||||||
window.need_refresh,
|
window.need_refresh,
|
||||||
::std::mem::replace(&mut *window.need_frame_refresh.lock().unwrap(), false),
|
::std::mem::replace(&mut *window.need_frame_refresh.lock().unwrap(), false),
|
||||||
window.closed,
|
window.closed,
|
||||||
make_wid(&window.surface),
|
make_wid(&window.surface),
|
||||||
opt_mutex_lock.as_mut().map(|m| &mut **m),
|
opt_mutex_lock.as_mut().map(|m| &mut **m),
|
||||||
);
|
);
|
||||||
|
if let Some(dpi) = window.new_dpi.take() {
|
||||||
|
window.current_dpi = dpi;
|
||||||
|
}
|
||||||
window.need_refresh = false;
|
window.need_refresh = false;
|
||||||
// avoid re-spamming the event
|
// avoid re-spamming the event
|
||||||
window.closed = false;
|
window.closed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Monitor list with some covenience method to compute DPI
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct MonitorList {
|
||||||
|
monitors: Vec<MonitorId>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MonitorList {
|
||||||
|
fn new() -> MonitorList {
|
||||||
|
MonitorList {
|
||||||
|
monitors: Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_hidpi_factor(&self) -> i32 {
|
||||||
|
let mut factor = 1;
|
||||||
|
for monitor_id in &self.monitors {
|
||||||
|
let monitor_dpi = monitor_id.get_hidpi_factor();
|
||||||
|
if monitor_dpi > factor { factor = monitor_dpi; }
|
||||||
|
}
|
||||||
|
factor
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_output(&mut self, monitor: MonitorId) -> Option<i32> {
|
||||||
|
let old_dpi = self.compute_hidpi_factor();
|
||||||
|
let monitor_dpi = monitor.get_hidpi_factor();
|
||||||
|
self.monitors.push(monitor);
|
||||||
|
if monitor_dpi > old_dpi {
|
||||||
|
Some(monitor_dpi)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn del_output(&mut self, output: &Proxy<wl_output::WlOutput>) -> Option<i32> {
|
||||||
|
let old_dpi = self.compute_hidpi_factor();
|
||||||
|
self.monitors.retain(|m| !m.proxy.equals(output));
|
||||||
|
let new_dpi = self.compute_hidpi_factor();
|
||||||
|
if new_dpi != old_dpi {
|
||||||
|
Some(new_dpi)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,13 +9,7 @@ mod dnd;
|
||||||
mod ime;
|
mod ime;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
pub use self::monitor::{
|
pub use self::monitor::MonitorId;
|
||||||
MonitorId,
|
|
||||||
get_available_monitors,
|
|
||||||
get_monitor_for_window,
|
|
||||||
get_primary_monitor,
|
|
||||||
invalidate_cached_monitor_list,
|
|
||||||
};
|
|
||||||
pub use self::window::UnownedWindow;
|
pub use self::window::UnownedWindow;
|
||||||
pub use self::xdisplay::{XConnection, XNotSupported, XError};
|
pub use self::xdisplay::{XConnection, XNotSupported, XError};
|
||||||
|
|
||||||
|
@ -29,7 +23,6 @@ use std::sync::{Arc, mpsc, Weak};
|
||||||
use std::sync::atomic::{self, AtomicBool};
|
use std::sync::atomic::{self, AtomicBool};
|
||||||
|
|
||||||
use libc::{self, setlocale, LC_CTYPE};
|
use libc::{self, setlocale, LC_CTYPE};
|
||||||
use parking_lot::Mutex;
|
|
||||||
|
|
||||||
use {
|
use {
|
||||||
ControlFlow,
|
ControlFlow,
|
||||||
|
@ -38,6 +31,8 @@ use {
|
||||||
Event,
|
Event,
|
||||||
EventsLoopClosed,
|
EventsLoopClosed,
|
||||||
KeyboardInput,
|
KeyboardInput,
|
||||||
|
LogicalPosition,
|
||||||
|
LogicalSize,
|
||||||
WindowAttributes,
|
WindowAttributes,
|
||||||
WindowEvent,
|
WindowEvent,
|
||||||
};
|
};
|
||||||
|
@ -92,7 +87,7 @@ impl EventsLoop {
|
||||||
result.expect("Failed to set input method destruction callback")
|
result.expect("Failed to set input method destruction callback")
|
||||||
});
|
});
|
||||||
|
|
||||||
let randr_event_offset = monitor::select_input(&xconn, root)
|
let randr_event_offset = xconn.select_xrandr_input(root)
|
||||||
.expect("Failed to query XRandR extension");
|
.expect("Failed to query XRandR extension");
|
||||||
|
|
||||||
let xi2ext = unsafe {
|
let xi2ext = unsafe {
|
||||||
|
@ -394,6 +389,13 @@ impl EventsLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi::ConfigureNotify => {
|
ffi::ConfigureNotify => {
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct Events {
|
||||||
|
resized: Option<WindowEvent>,
|
||||||
|
moved: Option<WindowEvent>,
|
||||||
|
dpi_changed: Option<WindowEvent>,
|
||||||
|
}
|
||||||
|
|
||||||
let xev: &ffi::XConfigureEvent = xev.as_ref();
|
let xev: &ffi::XConfigureEvent = xev.as_ref();
|
||||||
let xwindow = xev.window;
|
let xwindow = xev.window;
|
||||||
let events = self.with_window(xwindow, |window| {
|
let events = self.with_window(xwindow, |window| {
|
||||||
|
@ -406,9 +408,11 @@ impl EventsLoop {
|
||||||
// that has a position relative to the parent window.
|
// that has a position relative to the parent window.
|
||||||
let is_synthetic = xev.send_event == ffi::True;
|
let is_synthetic = xev.send_event == ffi::True;
|
||||||
|
|
||||||
|
// These are both in physical space.
|
||||||
let new_inner_size = (xev.width as u32, xev.height as u32);
|
let new_inner_size = (xev.width as u32, xev.height as u32);
|
||||||
let new_inner_position = (xev.x as i32, xev.y as i32);
|
let new_inner_position = (xev.x as i32, xev.y as i32);
|
||||||
|
|
||||||
|
let monitor = window.get_current_monitor(); // This must be done *before* locking!
|
||||||
let mut shared_state_lock = window.shared_state.lock();
|
let mut shared_state_lock = window.shared_state.lock();
|
||||||
|
|
||||||
let (resized, moved) = {
|
let (resized, moved) = {
|
||||||
|
@ -431,14 +435,33 @@ impl EventsLoop {
|
||||||
(resized, moved)
|
(resized, moved)
|
||||||
};
|
};
|
||||||
|
|
||||||
let capacity = resized as usize + moved as usize;
|
// This is a hack to ensure that the DPI adjusted resize is actually applied on all WMs. KWin
|
||||||
let mut events = Vec::with_capacity(capacity);
|
// doesn't need this, but Xfwm does.
|
||||||
|
if let Some(adjusted_size) = shared_state_lock.dpi_adjusted {
|
||||||
if resized {
|
let rounded_size = (adjusted_size.0.round() as u32, adjusted_size.1.round() as u32);
|
||||||
events.push(WindowEvent::Resized(new_inner_size.0, new_inner_size.1));
|
if new_inner_size == rounded_size {
|
||||||
|
// When this finally happens, the event will not be synthetic.
|
||||||
|
shared_state_lock.dpi_adjusted = None;
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
(self.xconn.xlib.XResizeWindow)(
|
||||||
|
self.xconn.display,
|
||||||
|
xwindow,
|
||||||
|
rounded_size.0 as c_uint,
|
||||||
|
rounded_size.1 as c_uint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if moved || shared_state_lock.position.is_none() {
|
let mut events = Events::default();
|
||||||
|
|
||||||
|
if resized {
|
||||||
|
let logical_size = LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor);
|
||||||
|
events.resized = Some(WindowEvent::Resized(logical_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_outer_position = if moved || shared_state_lock.position.is_none() {
|
||||||
// We need to convert client area position to window position.
|
// We need to convert client area position to window position.
|
||||||
let frame_extents = shared_state_lock.frame_extents
|
let frame_extents = shared_state_lock.frame_extents
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -451,19 +474,59 @@ impl EventsLoop {
|
||||||
let outer = frame_extents.inner_pos_to_outer(new_inner_position.0, new_inner_position.1);
|
let outer = frame_extents.inner_pos_to_outer(new_inner_position.0, new_inner_position.1);
|
||||||
shared_state_lock.position = Some(outer);
|
shared_state_lock.position = Some(outer);
|
||||||
if moved {
|
if moved {
|
||||||
events.push(WindowEvent::Moved(outer.0, outer.1));
|
let logical_position = LogicalPosition::from_physical(outer, monitor.hidpi_factor);
|
||||||
|
events.moved = Some(WindowEvent::Moved(logical_position));
|
||||||
}
|
}
|
||||||
|
outer
|
||||||
|
} else {
|
||||||
|
shared_state_lock.position.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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
|
||||||
|
.unwrap_or_else(|| (xev.width as f64, xev.height as f64));
|
||||||
|
let last_hidpi_factor = if shared_state_lock.is_new_window {
|
||||||
|
shared_state_lock.is_new_window = false;
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
shared_state_lock.last_monitor
|
||||||
|
.as_ref()
|
||||||
|
.map(|last_monitor| last_monitor.hidpi_factor)
|
||||||
|
.unwrap_or(1.0)
|
||||||
|
};
|
||||||
|
let new_hidpi_factor = {
|
||||||
|
let window_rect = util::Rect::new(new_outer_position, new_inner_size);
|
||||||
|
let monitor = self.xconn.get_monitor_for_window(Some(window_rect));
|
||||||
|
let new_hidpi_factor = monitor.hidpi_factor;
|
||||||
|
shared_state_lock.last_monitor = Some(monitor);
|
||||||
|
new_hidpi_factor
|
||||||
|
};
|
||||||
|
if last_hidpi_factor != new_hidpi_factor {
|
||||||
|
events.dpi_changed = Some(WindowEvent::HiDpiFactorChanged(new_hidpi_factor));
|
||||||
|
let (new_width, new_height, flusher) = window.adjust_for_dpi(
|
||||||
|
last_hidpi_factor,
|
||||||
|
new_hidpi_factor,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
);
|
||||||
|
flusher.queue();
|
||||||
|
shared_state_lock.dpi_adjusted = Some((new_width, new_height));
|
||||||
}
|
}
|
||||||
|
|
||||||
events
|
events
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(events) = events {
|
if let Some(events) = events {
|
||||||
for event in events {
|
let window_id = mkwid(xwindow);
|
||||||
callback(Event::WindowEvent {
|
if let Some(event) = events.resized {
|
||||||
window_id: mkwid(xwindow),
|
callback(Event::WindowEvent { window_id, event });
|
||||||
event,
|
}
|
||||||
});
|
if let Some(event) = events.moved {
|
||||||
|
callback(Event::WindowEvent { window_id, event });
|
||||||
|
}
|
||||||
|
if let Some(event) = events.dpi_changed {
|
||||||
|
callback(Event::WindowEvent { window_id, event });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -694,14 +757,25 @@ impl EventsLoop {
|
||||||
util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
|
util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
|
||||||
});
|
});
|
||||||
if cursor_moved == Some(true) {
|
if cursor_moved == Some(true) {
|
||||||
|
let dpi_factor = self.with_window(xev.event, |window| {
|
||||||
|
window.get_hidpi_factor()
|
||||||
|
});
|
||||||
|
if let Some(dpi_factor) = dpi_factor {
|
||||||
|
let position = LogicalPosition::from_physical(
|
||||||
|
(xev.event_x as f64, xev.event_y as f64),
|
||||||
|
dpi_factor,
|
||||||
|
);
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
event: CursorMoved {
|
event: CursorMoved {
|
||||||
device_id,
|
device_id,
|
||||||
position: new_cursor_pos,
|
position,
|
||||||
modifiers,
|
modifiers,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else if cursor_moved.is_none() {
|
} else if cursor_moved.is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -782,19 +856,29 @@ impl EventsLoop {
|
||||||
event: CursorEntered { device_id },
|
event: CursorEntered { device_id },
|
||||||
});
|
});
|
||||||
|
|
||||||
let new_cursor_pos = (xev.event_x, xev.event_y);
|
|
||||||
|
|
||||||
// The mods field on this event isn't actually populated, so query the
|
// The mods field on this event isn't actually populated, so query the
|
||||||
// pointer device. In the future, we can likely remove this round-trip by
|
// pointer device. In the future, we can likely remove this round-trip by
|
||||||
// relying on Xkb for modifier values.
|
// relying on Xkb for modifier values.
|
||||||
let modifiers = self.xconn.query_pointer(xev.event, xev.deviceid)
|
let modifiers = self.xconn.query_pointer(xev.event, xev.deviceid)
|
||||||
.expect("Failed to query pointer device").get_modifier_state();
|
.expect("Failed to query pointer device").get_modifier_state();
|
||||||
|
|
||||||
callback(Event::WindowEvent { window_id, event: CursorMoved {
|
let dpi_factor = self.with_window(xev.event, |window| {
|
||||||
|
window.get_hidpi_factor()
|
||||||
|
});
|
||||||
|
if let Some(dpi_factor) = dpi_factor {
|
||||||
|
let position = LogicalPosition::from_physical(
|
||||||
|
(xev.event_x as f64, xev.event_y as f64),
|
||||||
|
dpi_factor,
|
||||||
|
);
|
||||||
|
callback(Event::WindowEvent {
|
||||||
|
window_id,
|
||||||
|
event: CursorMoved {
|
||||||
device_id,
|
device_id,
|
||||||
position: new_cursor_pos,
|
position,
|
||||||
modifiers,
|
modifiers,
|
||||||
}})
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ffi::XI_Leave => {
|
ffi::XI_Leave => {
|
||||||
let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) };
|
||||||
|
@ -812,7 +896,12 @@ impl EventsLoop {
|
||||||
ffi::XI_FocusIn => {
|
ffi::XI_FocusIn => {
|
||||||
let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
|
||||||
|
|
||||||
if !self.window_exists(xev.event) { return; }
|
let dpi_factor = match self.with_window(xev.event, |window| {
|
||||||
|
window.get_hidpi_factor()
|
||||||
|
}) {
|
||||||
|
Some(dpi_factor) => dpi_factor,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
let window_id = mkwid(xev.event);
|
let window_id = mkwid(xev.event);
|
||||||
|
|
||||||
self.ime
|
self.ime
|
||||||
|
@ -830,11 +919,15 @@ impl EventsLoop {
|
||||||
.map(|device| device.attachment)
|
.map(|device| device.attachment)
|
||||||
.unwrap_or(2);
|
.unwrap_or(2);
|
||||||
|
|
||||||
|
let position = LogicalPosition::from_physical(
|
||||||
|
(xev.event_x as f64, xev.event_y as f64),
|
||||||
|
dpi_factor,
|
||||||
|
);
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
event: CursorMoved {
|
event: CursorMoved {
|
||||||
device_id: mkdid(pointer_id),
|
device_id: mkdid(pointer_id),
|
||||||
position: (xev.event_x, xev.event_y),
|
position,
|
||||||
modifiers: ModifiersState::from(xev.mods),
|
modifiers: ModifiersState::from(xev.mods),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -861,15 +954,24 @@ impl EventsLoop {
|
||||||
ffi::XI_TouchEnd => TouchPhase::Ended,
|
ffi::XI_TouchEnd => TouchPhase::Ended,
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
};
|
};
|
||||||
|
let dpi_factor = self.with_window(xev.event, |window| {
|
||||||
|
window.get_hidpi_factor()
|
||||||
|
});
|
||||||
|
if let Some(dpi_factor) = dpi_factor {
|
||||||
|
let location = LogicalPosition::from_physical(
|
||||||
|
(xev.event_x as f64, xev.event_y as f64),
|
||||||
|
dpi_factor,
|
||||||
|
);
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
event: WindowEvent::Touch(Touch {
|
event: WindowEvent::Touch(Touch {
|
||||||
device_id: mkdid(xev.deviceid),
|
device_id: mkdid(xev.deviceid),
|
||||||
phase,
|
phase,
|
||||||
location: (xev.event_x, xev.event_y),
|
location,
|
||||||
id: xev.detail as u64,
|
id: xev.detail as u64,
|
||||||
},
|
}),
|
||||||
)})
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => {
|
ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => {
|
||||||
|
@ -986,7 +1088,44 @@ impl EventsLoop {
|
||||||
_ => {
|
_ => {
|
||||||
if event_type == self.randr_event_offset {
|
if event_type == self.randr_event_offset {
|
||||||
// In the future, it would be quite easy to emit monitor hotplug events.
|
// In the future, it would be quite easy to emit monitor hotplug events.
|
||||||
monitor::invalidate_cached_monitor_list();
|
let prev_list = monitor::invalidate_cached_monitor_list();
|
||||||
|
if let Some(prev_list) = prev_list {
|
||||||
|
let new_list = self.xconn.get_available_monitors();
|
||||||
|
for new_monitor in new_list {
|
||||||
|
prev_list
|
||||||
|
.iter()
|
||||||
|
.find(|prev_monitor| prev_monitor.name == new_monitor.name)
|
||||||
|
.map(|prev_monitor| {
|
||||||
|
if new_monitor.hidpi_factor != prev_monitor.hidpi_factor {
|
||||||
|
for (window_id, window) in self.windows.borrow().iter() {
|
||||||
|
if let Some(window) = window.upgrade() {
|
||||||
|
// Check if the window is on this monitor
|
||||||
|
let monitor = window.get_current_monitor();
|
||||||
|
if monitor.name == new_monitor.name {
|
||||||
|
callback(Event::WindowEvent {
|
||||||
|
window_id: mkwid(window_id.0),
|
||||||
|
event: WindowEvent::HiDpiFactorChanged(
|
||||||
|
new_monitor.hidpi_factor
|
||||||
|
),
|
||||||
|
});
|
||||||
|
let (width, height) = match window.get_inner_size_physical() {
|
||||||
|
Some(result) => result,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let (_, _, flusher) = window.adjust_for_dpi(
|
||||||
|
prev_monitor.hidpi_factor,
|
||||||
|
new_monitor.hidpi_factor,
|
||||||
|
width as f64,
|
||||||
|
height as f64,
|
||||||
|
);
|
||||||
|
flusher.queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1110,16 +1249,13 @@ pub struct WindowId(ffi::Window);
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct DeviceId(c_int);
|
pub struct DeviceId(c_int);
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window(Arc<UnownedWindow>);
|
||||||
pub window: Arc<UnownedWindow>,
|
|
||||||
ime_sender: Mutex<ImeSender>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Window {
|
impl Deref for Window {
|
||||||
type Target = UnownedWindow;
|
type Target = UnownedWindow;
|
||||||
#[inline]
|
#[inline]
|
||||||
fn deref(&self) -> &UnownedWindow {
|
fn deref(&self) -> &UnownedWindow {
|
||||||
&*self.window
|
&*self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1130,35 +1266,19 @@ impl Window {
|
||||||
pl_attribs: PlatformSpecificWindowBuilderAttributes
|
pl_attribs: PlatformSpecificWindowBuilderAttributes
|
||||||
) -> Result<Self, CreationError> {
|
) -> Result<Self, CreationError> {
|
||||||
let window = Arc::new(UnownedWindow::new(&event_loop, attribs, pl_attribs)?);
|
let window = Arc::new(UnownedWindow::new(&event_loop, attribs, pl_attribs)?);
|
||||||
|
|
||||||
event_loop.windows
|
event_loop.windows
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(window.id(), Arc::downgrade(&window));
|
.insert(window.id(), Arc::downgrade(&window));
|
||||||
|
Ok(Window(window))
|
||||||
event_loop.ime
|
|
||||||
.borrow_mut()
|
|
||||||
.create_context(window.id().0)
|
|
||||||
.expect("Failed to create input context");
|
|
||||||
|
|
||||||
Ok(Window {
|
|
||||||
window,
|
|
||||||
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn send_xim_spot(&self, x: i16, y: i16) {
|
|
||||||
let _ = self.ime_sender
|
|
||||||
.lock()
|
|
||||||
.send((self.window.id().0, x, y));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Window {
|
impl Drop for Window {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let xconn = &self.window.xconn;
|
let window = self.deref();
|
||||||
|
let xconn = &window.xconn;
|
||||||
unsafe {
|
unsafe {
|
||||||
(xconn.xlib.XDestroyWindow)(xconn.display, self.window.id().0);
|
(xconn.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.
|
// 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.check_errors();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use std::os::raw::*;
|
use std::os::raw::*;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
use {PhysicalPosition, PhysicalSize};
|
||||||
|
use super::{util, XConnection, XError};
|
||||||
use super::ffi::{
|
use super::ffi::{
|
||||||
RRCrtcChangeNotifyMask,
|
RRCrtcChangeNotifyMask,
|
||||||
RROutputPropertyNotifyMask,
|
RROutputPropertyNotifyMask,
|
||||||
|
@ -11,7 +12,6 @@ use super::ffi::{
|
||||||
Window,
|
Window,
|
||||||
XRRScreenResources,
|
XRRScreenResources,
|
||||||
};
|
};
|
||||||
use super::{util, XConnection, XError};
|
|
||||||
|
|
||||||
// Used to test XRandR < 1.5 code path. This should always be committed as false.
|
// Used to test XRandR < 1.5 code path. This should always be committed as false.
|
||||||
const FORCE_RANDR_COMPAT: bool = false;
|
const FORCE_RANDR_COMPAT: bool = false;
|
||||||
|
@ -45,7 +45,7 @@ pub struct MonitorId {
|
||||||
/// The actual id
|
/// The actual id
|
||||||
id: u32,
|
id: u32,
|
||||||
/// The name of the monitor
|
/// The name of the monitor
|
||||||
name: String,
|
pub(crate) name: String,
|
||||||
/// The size of the monitor
|
/// The size of the monitor
|
||||||
dimensions: (u32, u32),
|
dimensions: (u32, u32),
|
||||||
/// The position of the monitor in the X screen
|
/// The position of the monitor in the X screen
|
||||||
|
@ -53,14 +53,14 @@ pub struct MonitorId {
|
||||||
/// If the monitor is the primary one
|
/// If the monitor is the primary one
|
||||||
primary: bool,
|
primary: bool,
|
||||||
/// The DPI scale factor
|
/// The DPI scale factor
|
||||||
pub(crate) hidpi_factor: f32,
|
pub(crate) hidpi_factor: f64,
|
||||||
/// Used to determine which windows are on this monitor
|
/// Used to determine which windows are on this monitor
|
||||||
pub(crate) rect: util::Rect,
|
pub(crate) rect: util::Rect,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MonitorId {
|
impl MonitorId {
|
||||||
fn from_repr(
|
fn from_repr(
|
||||||
xconn: &Arc<XConnection>,
|
xconn: &XConnection,
|
||||||
resources: *mut XRRScreenResources,
|
resources: *mut XRRScreenResources,
|
||||||
id: u32,
|
id: u32,
|
||||||
repr: util::MonitorRepr,
|
repr: util::MonitorRepr,
|
||||||
|
@ -89,25 +89,23 @@ impl MonitorId {
|
||||||
self.id as u32
|
self.id as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||||
self.dimensions
|
self.dimensions.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_position(&self) -> (i32, i32) {
|
pub fn get_position(&self) -> PhysicalPosition {
|
||||||
self.position
|
self.position.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_hidpi_factor(&self) -> f32 {
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
self.hidpi_factor
|
self.hidpi_factor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_monitor_for_window(
|
impl XConnection {
|
||||||
xconn: &Arc<XConnection>,
|
pub fn get_monitor_for_window(&self, window_rect: Option<util::Rect>) -> MonitorId {
|
||||||
window_rect: Option<util::Rect>,
|
let monitors = self.get_available_monitors();
|
||||||
) -> MonitorId {
|
|
||||||
let monitors = get_available_monitors(xconn);
|
|
||||||
let default = monitors
|
let default = monitors
|
||||||
.get(0)
|
.get(0)
|
||||||
.expect("[winit] Failed to find any monitors using XRandR.");
|
.expect("[winit] Failed to find any monitors using XRandR.");
|
||||||
|
@ -128,14 +126,14 @@ pub fn get_monitor_for_window(
|
||||||
}
|
}
|
||||||
|
|
||||||
matched_monitor.to_owned()
|
matched_monitor.to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_monitor_list(xconn: &Arc<XConnection>) -> Vec<MonitorId> {
|
fn query_monitor_list(&self) -> Vec<MonitorId> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let root = (xconn.xlib.XDefaultRootWindow)(xconn.display);
|
let root = (self.xlib.XDefaultRootWindow)(self.display);
|
||||||
// WARNING: this function is supposedly very slow, on the order of hundreds of ms.
|
// WARNING: this function is supposedly very slow, on the order of hundreds of ms.
|
||||||
// Upon failure, `resources` will be null.
|
// Upon failure, `resources` will be null.
|
||||||
let resources = (xconn.xrandr.XRRGetScreenResources)(xconn.display, root);
|
let resources = (self.xrandr.XRRGetScreenResources)(self.display, root);
|
||||||
if resources.is_null() {
|
if resources.is_null() {
|
||||||
panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist.");
|
panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist.");
|
||||||
}
|
}
|
||||||
|
@ -143,12 +141,12 @@ fn query_monitor_list(xconn: &Arc<XConnection>) -> Vec<MonitorId> {
|
||||||
let mut available;
|
let mut available;
|
||||||
let mut has_primary = false;
|
let mut has_primary = false;
|
||||||
|
|
||||||
if xconn.xrandr_1_5.is_some() && version_is_at_least(1, 5) && !FORCE_RANDR_COMPAT {
|
if self.xrandr_1_5.is_some() && version_is_at_least(1, 5) && !FORCE_RANDR_COMPAT {
|
||||||
// We're in XRandR >= 1.5, enumerate monitors. This supports things like MST and
|
// We're in XRandR >= 1.5, enumerate monitors. This supports things like MST and
|
||||||
// videowalls.
|
// videowalls.
|
||||||
let xrandr_1_5 = xconn.xrandr_1_5.as_ref().unwrap();
|
let xrandr_1_5 = self.xrandr_1_5.as_ref().unwrap();
|
||||||
let mut monitor_count = 0;
|
let mut monitor_count = 0;
|
||||||
let monitors = (xrandr_1_5.XRRGetMonitors)(xconn.display, root, 1, &mut monitor_count);
|
let monitors = (xrandr_1_5.XRRGetMonitors)(self.display, root, 1, &mut monitor_count);
|
||||||
assert!(monitor_count >= 0);
|
assert!(monitor_count >= 0);
|
||||||
available = Vec::with_capacity(monitor_count as usize);
|
available = Vec::with_capacity(monitor_count as usize);
|
||||||
for monitor_index in 0..monitor_count {
|
for monitor_index in 0..monitor_count {
|
||||||
|
@ -156,7 +154,7 @@ fn query_monitor_list(xconn: &Arc<XConnection>) -> Vec<MonitorId> {
|
||||||
let is_primary = (*monitor).primary != 0;
|
let is_primary = (*monitor).primary != 0;
|
||||||
has_primary |= is_primary;
|
has_primary |= is_primary;
|
||||||
available.push(MonitorId::from_repr(
|
available.push(MonitorId::from_repr(
|
||||||
xconn,
|
self,
|
||||||
resources,
|
resources,
|
||||||
monitor_index as u32,
|
monitor_index as u32,
|
||||||
monitor.into(),
|
monitor.into(),
|
||||||
|
@ -168,25 +166,25 @@ fn query_monitor_list(xconn: &Arc<XConnection>) -> Vec<MonitorId> {
|
||||||
// We're in XRandR < 1.5, enumerate CRTCs. Everything will work except MST and
|
// We're in XRandR < 1.5, enumerate CRTCs. Everything will work except MST and
|
||||||
// videowall setups will also show monitors that aren't in the logical groups the user
|
// videowall setups will also show monitors that aren't in the logical groups the user
|
||||||
// cares about.
|
// cares about.
|
||||||
let primary = (xconn.xrandr.XRRGetOutputPrimary)(xconn.display, root);
|
let primary = (self.xrandr.XRRGetOutputPrimary)(self.display, root);
|
||||||
available = Vec::with_capacity((*resources).ncrtc as usize);
|
available = Vec::with_capacity((*resources).ncrtc as usize);
|
||||||
for crtc_index in 0..(*resources).ncrtc {
|
for crtc_index in 0..(*resources).ncrtc {
|
||||||
let crtc_id = *((*resources).crtcs.offset(crtc_index as isize));
|
let crtc_id = *((*resources).crtcs.offset(crtc_index as isize));
|
||||||
let crtc = (xconn.xrandr.XRRGetCrtcInfo)(xconn.display, resources, crtc_id);
|
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
|
||||||
let is_active = (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0;
|
let is_active = (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0;
|
||||||
if is_active {
|
if is_active {
|
||||||
let crtc = util::MonitorRepr::from(crtc);
|
let crtc = util::MonitorRepr::from(crtc);
|
||||||
let is_primary = crtc.get_output() == primary;
|
let is_primary = crtc.get_output() == primary;
|
||||||
has_primary |= is_primary;
|
has_primary |= is_primary;
|
||||||
available.push(MonitorId::from_repr(
|
available.push(MonitorId::from_repr(
|
||||||
xconn,
|
self,
|
||||||
resources,
|
resources,
|
||||||
crtc_id as u32,
|
crtc_id as u32,
|
||||||
crtc,
|
crtc,
|
||||||
is_primary,
|
is_primary,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
(xconn.xrandr.XRRFreeCrtcInfo)(crtc);
|
(self.xrandr.XRRFreeCrtcInfo)(crtc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,43 +196,43 @@ fn query_monitor_list(xconn: &Arc<XConnection>) -> Vec<MonitorId> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(xconn.xrandr.XRRFreeScreenResources)(resources);
|
(self.xrandr.XRRFreeScreenResources)(resources);
|
||||||
available
|
available
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_available_monitors(xconn: &Arc<XConnection>) -> Vec<MonitorId> {
|
pub fn get_available_monitors(&self) -> Vec<MonitorId> {
|
||||||
let mut monitors_lock = MONITORS.lock();
|
let mut monitors_lock = MONITORS.lock();
|
||||||
(*monitors_lock)
|
(*monitors_lock)
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.cloned()
|
.cloned()
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
let monitors = Some(query_monitor_list(xconn));
|
let monitors = Some(self.query_monitor_list());
|
||||||
if !DISABLE_MONITOR_LIST_CACHING {
|
if !DISABLE_MONITOR_LIST_CACHING {
|
||||||
(*monitors_lock) = monitors.clone();
|
(*monitors_lock) = monitors.clone();
|
||||||
}
|
}
|
||||||
monitors
|
monitors
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_primary_monitor(xconn: &Arc<XConnection>) -> MonitorId {
|
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||||
get_available_monitors(xconn)
|
self.get_available_monitors()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.find(|monitor| monitor.primary)
|
.find(|monitor| monitor.primary)
|
||||||
.expect("[winit] Failed to find any monitors using XRandR.")
|
.expect("[winit] Failed to find any monitors using XRandR.")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_input(xconn: &Arc<XConnection>, root: Window) -> Result<c_int, XError> {
|
pub fn select_xrandr_input(&self, root: Window) -> Result<c_int, XError> {
|
||||||
{
|
{
|
||||||
let mut version_lock = XRANDR_VERSION.lock();
|
let mut version_lock = XRANDR_VERSION.lock();
|
||||||
if version_lock.is_none() {
|
if version_lock.is_none() {
|
||||||
let mut major = 0;
|
let mut major = 0;
|
||||||
let mut minor = 0;
|
let mut minor = 0;
|
||||||
let has_extension = unsafe {
|
let has_extension = unsafe {
|
||||||
(xconn.xrandr.XRRQueryVersion)(
|
(self.xrandr.XRRQueryVersion)(
|
||||||
xconn.display,
|
self.display,
|
||||||
&mut major,
|
&mut major,
|
||||||
&mut minor,
|
&mut minor,
|
||||||
)
|
)
|
||||||
|
@ -249,22 +247,23 @@ pub fn select_input(xconn: &Arc<XConnection>, root: Window) -> Result<c_int, XEr
|
||||||
let mut event_offset = 0;
|
let mut event_offset = 0;
|
||||||
let mut error_offset = 0;
|
let mut error_offset = 0;
|
||||||
let status = unsafe {
|
let status = unsafe {
|
||||||
(xconn.xrandr.XRRQueryExtension)(
|
(self.xrandr.XRRQueryExtension)(
|
||||||
xconn.display,
|
self.display,
|
||||||
&mut event_offset,
|
&mut event_offset,
|
||||||
&mut error_offset,
|
&mut error_offset,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
if status != True {
|
if status != True {
|
||||||
xconn.check_errors()?;
|
self.check_errors()?;
|
||||||
unreachable!("[winit] `XRRQueryExtension` failed but no error was received.");
|
unreachable!("[winit] `XRRQueryExtension` failed but no error was received.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mask = RRCrtcChangeNotifyMask
|
let mask = RRCrtcChangeNotifyMask
|
||||||
| RROutputPropertyNotifyMask
|
| RROutputPropertyNotifyMask
|
||||||
| RRScreenChangeNotifyMask;
|
| RRScreenChangeNotifyMask;
|
||||||
unsafe { (xconn.xrandr.XRRSelectInput)(xconn.display, root, mask) };
|
unsafe { (self.xrandr.XRRSelectInput)(self.display, root, mask) };
|
||||||
|
|
||||||
Ok(event_offset)
|
Ok(event_offset)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use {LogicalPosition, LogicalSize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Rect {
|
pub struct Rect {
|
||||||
|
@ -78,6 +79,24 @@ impl FrameExtents {
|
||||||
pub fn from_border(border: c_ulong) -> Self {
|
pub fn from_border(border: c_ulong) -> Self {
|
||||||
Self::new(border, border, border, border)
|
Self::new(border, border, border, border)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_logical(&self, factor: f64) -> LogicalFrameExtents {
|
||||||
|
let logicalize = |value: c_ulong| value as f64 / factor;
|
||||||
|
LogicalFrameExtents {
|
||||||
|
left: logicalize(self.left),
|
||||||
|
right: logicalize(self.right),
|
||||||
|
top: logicalize(self.top),
|
||||||
|
bottom: logicalize(self.bottom),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LogicalFrameExtents {
|
||||||
|
pub left: f64,
|
||||||
|
pub right: f64,
|
||||||
|
pub top: f64,
|
||||||
|
pub bottom: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -103,6 +122,16 @@ impl FrameExtentsHeuristic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn inner_pos_to_outer_logical(&self, mut logical: LogicalPosition, factor: f64) -> LogicalPosition {
|
||||||
|
use self::FrameExtentsHeuristicPath::*;
|
||||||
|
if self.heuristic_path != UnsupportedBordered {
|
||||||
|
let frame_extents = self.frame_extents.as_logical(factor);
|
||||||
|
logical.x -= frame_extents.left;
|
||||||
|
logical.y -= frame_extents.top;
|
||||||
|
}
|
||||||
|
logical
|
||||||
|
}
|
||||||
|
|
||||||
pub fn inner_size_to_outer(&self, width: u32, height: u32) -> (u32, u32) {
|
pub fn inner_size_to_outer(&self, width: u32, height: u32) -> (u32, u32) {
|
||||||
(
|
(
|
||||||
width.saturating_add(
|
width.saturating_add(
|
||||||
|
@ -113,6 +142,13 @@ impl FrameExtentsHeuristic {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn inner_size_to_outer_logical(&self, mut logical: LogicalSize, factor: f64) -> LogicalSize {
|
||||||
|
let frame_extents = self.frame_extents.as_logical(factor);
|
||||||
|
logical.width += frame_extents.left + frame_extents.right;
|
||||||
|
logical.height += frame_extents.top + frame_extents.bottom;
|
||||||
|
logical
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XConnection {
|
impl XConnection {
|
||||||
|
|
|
@ -68,6 +68,99 @@ impl WindowType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct NormalHints<'a> {
|
||||||
|
size_hints: XSmartPointer<'a, ffi::XSizeHints>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NormalHints<'a> {
|
||||||
|
pub fn new(xconn: &'a XConnection) -> Self {
|
||||||
|
NormalHints { size_hints: xconn.alloc_size_hints() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_flag(&self, flag: c_long) -> bool {
|
||||||
|
has_flag(self.size_hints.flags, flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getter(&self, flag: c_long, field1: &c_int, field2: &c_int) -> Option<(u32, u32)> {
|
||||||
|
if self.has_flag(flag) {
|
||||||
|
Some((*field1 as _, *field2 as _))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_size(&self) -> Option<(u32, u32)> {
|
||||||
|
self.getter(ffi::PSize, &self.size_hints.width, &self.size_hints.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WARNING: This hint is obsolete
|
||||||
|
pub fn set_size(&mut self, size: Option<(u32, u32)>) {
|
||||||
|
if let Some((width, height)) = size {
|
||||||
|
self.size_hints.flags |= ffi::PSize;
|
||||||
|
self.size_hints.width = width as c_int;
|
||||||
|
self.size_hints.height = height as c_int;
|
||||||
|
} else {
|
||||||
|
self.size_hints.flags &= !ffi::PSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_max_size(&self) -> Option<(u32, u32)> {
|
||||||
|
self.getter(ffi::PMaxSize, &self.size_hints.max_width, &self.size_hints.max_height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_max_size(&mut self, max_size: Option<(u32, u32)>) {
|
||||||
|
if let Some((max_width, max_height)) = max_size {
|
||||||
|
self.size_hints.flags |= ffi::PMaxSize;
|
||||||
|
self.size_hints.max_width = max_width as c_int;
|
||||||
|
self.size_hints.max_height = max_height as c_int;
|
||||||
|
} else {
|
||||||
|
self.size_hints.flags &= !ffi::PMaxSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_min_size(&self) -> Option<(u32, u32)> {
|
||||||
|
self.getter(ffi::PMinSize, &self.size_hints.min_width, &self.size_hints.min_height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_min_size(&mut self, min_size: Option<(u32, u32)>) {
|
||||||
|
if let Some((min_width, min_height)) = min_size {
|
||||||
|
self.size_hints.flags |= ffi::PMinSize;
|
||||||
|
self.size_hints.min_width = min_width as c_int;
|
||||||
|
self.size_hints.min_height = min_height as c_int;
|
||||||
|
} else {
|
||||||
|
self.size_hints.flags &= !ffi::PMinSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_resize_increments(&self) -> Option<(u32, u32)> {
|
||||||
|
self.getter(ffi::PResizeInc, &self.size_hints.width_inc, &self.size_hints.height_inc)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_resize_increments(&mut self, resize_increments: Option<(u32, u32)>) {
|
||||||
|
if let Some((width_inc, height_inc)) = resize_increments {
|
||||||
|
self.size_hints.flags |= ffi::PResizeInc;
|
||||||
|
self.size_hints.width_inc = width_inc as c_int;
|
||||||
|
self.size_hints.height_inc = height_inc as c_int;
|
||||||
|
} else {
|
||||||
|
self.size_hints.flags &= !ffi::PResizeInc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_base_size(&self) -> Option<(u32, u32)> {
|
||||||
|
self.getter(ffi::PBaseSize, &self.size_hints.base_width, &self.size_hints.base_height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_base_size(&mut self, base_size: Option<(u32, u32)>) {
|
||||||
|
if let Some((base_width, base_height)) = base_size {
|
||||||
|
self.size_hints.flags |= ffi::PBaseSize;
|
||||||
|
self.size_hints.base_width = base_width as c_int;
|
||||||
|
self.size_hints.base_height = base_height as c_int;
|
||||||
|
} else {
|
||||||
|
self.size_hints.flags &= !ffi::PBaseSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl XConnection {
|
impl XConnection {
|
||||||
pub fn get_wm_hints(&self, window: ffi::Window) -> Result<XSmartPointer<ffi::XWMHints>, XError> {
|
pub fn get_wm_hints(&self, window: ffi::Window) -> Result<XSmartPointer<ffi::XWMHints>, XError> {
|
||||||
let wm_hints = unsafe { (self.xlib.XGetWMHints)(self.display, window) };
|
let wm_hints = unsafe { (self.xlib.XGetWMHints)(self.display, window) };
|
||||||
|
@ -90,4 +183,29 @@ impl XConnection {
|
||||||
}
|
}
|
||||||
Flusher::new(self)
|
Flusher::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_normal_hints(&self, window: ffi::Window) -> Result<NormalHints, XError> {
|
||||||
|
let size_hints = self.alloc_size_hints();
|
||||||
|
let mut supplied_by_user: c_long = unsafe { mem::uninitialized() };
|
||||||
|
unsafe {
|
||||||
|
(self.xlib.XGetWMNormalHints)(
|
||||||
|
self.display,
|
||||||
|
window,
|
||||||
|
size_hints.ptr,
|
||||||
|
&mut supplied_by_user,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.check_errors().map(|_| NormalHints { size_hints })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_normal_hints(&self, window: ffi::Window, normal_hints: NormalHints) -> Flusher {
|
||||||
|
unsafe {
|
||||||
|
(self.xlib.XSetWMNormalHints)(
|
||||||
|
self.display,
|
||||||
|
window,
|
||||||
|
normal_hints.size_hints.ptr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Flusher::new(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,10 +27,16 @@ pub use self::wm::*;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
use std::ops::BitAnd;
|
||||||
use std::os::raw::*;
|
use std::os::raw::*;
|
||||||
|
|
||||||
use super::{ffi, XConnection, XError};
|
use super::{ffi, XConnection, XError};
|
||||||
|
|
||||||
|
pub fn reinterpret<'a, A, B>(a: &'a A) -> &'a B {
|
||||||
|
let b_ptr = a as *const _ as *const B;
|
||||||
|
unsafe { &*b_ptr }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn maybe_change<T: PartialEq>(field: &mut Option<T>, value: T) -> bool {
|
pub fn maybe_change<T: PartialEq>(field: &mut Option<T>, value: T) -> bool {
|
||||||
let wrapped = Some(value);
|
let wrapped = Some(value);
|
||||||
if *field != wrapped {
|
if *field != wrapped {
|
||||||
|
@ -41,6 +47,13 @@ pub fn maybe_change<T: PartialEq>(field: &mut Option<T>, value: T) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_flag<T>(bitset: T, flag: T) -> bool
|
||||||
|
where T:
|
||||||
|
Copy + PartialEq + BitAnd<T, Output = T>
|
||||||
|
{
|
||||||
|
bitset & flag == flag
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use = "This request was made asynchronously, and is still in the output buffer. You must explicitly choose to either `.flush()` (empty the output buffer, sending the request now) or `.queue()` (wait to send the request, allowing you to continue to add more requests without additional round-trips). For more information, see the documentation for `util::flush_requests`."]
|
#[must_use = "This request was made asynchronously, and is still in the output buffer. You must explicitly choose to either `.flush()` (empty the output buffer, sending the request now) or `.queue()` (wait to send the request, allowing you to continue to add more requests without additional round-trips). For more information, see the documentation for `util::flush_requests`."]
|
||||||
pub struct Flusher<'a> {
|
pub struct Flusher<'a> {
|
||||||
xconn: &'a XConnection,
|
xconn: &'a XConnection,
|
||||||
|
|
|
@ -1,21 +1,48 @@
|
||||||
use std::{env, slice};
|
use std::{env, slice};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use validate_hidpi_factor;
|
||||||
use super::*;
|
use super::*;
|
||||||
use super::ffi::{
|
|
||||||
RROutput,
|
pub fn calc_dpi_factor(
|
||||||
XRRCrtcInfo,
|
(width_px, height_px): (u32, u32),
|
||||||
XRRMonitorInfo,
|
(width_mm, height_mm): (u64, u64),
|
||||||
XRRScreenResources,
|
) -> f64 {
|
||||||
};
|
// Override DPI if `WINIT_HIDPI_FACTOR` variable is set
|
||||||
|
let dpi_override = env::var("WINIT_HIDPI_FACTOR")
|
||||||
|
.ok()
|
||||||
|
.and_then(|var| f64::from_str(&var).ok());
|
||||||
|
if let Some(dpi_override) = dpi_override {
|
||||||
|
if !validate_hidpi_factor(dpi_override) {
|
||||||
|
panic!(
|
||||||
|
"`WINIT_HIDPI_FACTOR` invalid; DPI factors must be normal floats greater than 0. Got `{}`",
|
||||||
|
dpi_override,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return dpi_override;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See http://xpra.org/trac/ticket/728 for more information.
|
||||||
|
if width_mm == 0 || width_mm == 0 {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_hidpi_factor(dpi_factor));
|
||||||
|
dpi_factor
|
||||||
|
}
|
||||||
|
|
||||||
pub enum MonitorRepr {
|
pub enum MonitorRepr {
|
||||||
Monitor(*mut XRRMonitorInfo),
|
Monitor(*mut ffi::XRRMonitorInfo),
|
||||||
Crtc(*mut XRRCrtcInfo),
|
Crtc(*mut ffi::XRRCrtcInfo),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MonitorRepr {
|
impl MonitorRepr {
|
||||||
pub unsafe fn get_output(&self) -> RROutput {
|
pub unsafe fn get_output(&self) -> ffi::RROutput {
|
||||||
match *self {
|
match *self {
|
||||||
// Same member names, but different locations within the struct...
|
// Same member names, but different locations within the struct...
|
||||||
MonitorRepr::Monitor(monitor) => *((*monitor).outputs.offset(0)),
|
MonitorRepr::Monitor(monitor) => *((*monitor).outputs.offset(0)),
|
||||||
|
@ -38,47 +65,20 @@ impl MonitorRepr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<*mut XRRMonitorInfo> for MonitorRepr {
|
impl From<*mut ffi::XRRMonitorInfo> for MonitorRepr {
|
||||||
fn from(monitor: *mut XRRMonitorInfo) -> Self {
|
fn from(monitor: *mut ffi::XRRMonitorInfo) -> Self {
|
||||||
MonitorRepr::Monitor(monitor)
|
MonitorRepr::Monitor(monitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<*mut XRRCrtcInfo> for MonitorRepr {
|
impl From<*mut ffi::XRRCrtcInfo> for MonitorRepr {
|
||||||
fn from(crtc: *mut XRRCrtcInfo) -> Self {
|
fn from(crtc: *mut ffi::XRRCrtcInfo) -> Self {
|
||||||
MonitorRepr::Crtc(crtc)
|
MonitorRepr::Crtc(crtc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calc_dpi_factor(
|
|
||||||
(width_px, height_px): (u32, u32),
|
|
||||||
(width_mm, height_mm): (u64, u64),
|
|
||||||
) -> f64 {
|
|
||||||
// Override DPI if `WINIT_HIDPI_FACTOR` variable is set
|
|
||||||
if let Ok(dpi_factor_str) = env::var("WINIT_HIDPI_FACTOR") {
|
|
||||||
if let Ok(dpi_factor) = f64::from_str(&dpi_factor_str) {
|
|
||||||
if dpi_factor <= 0. {
|
|
||||||
panic!("Expected `WINIT_HIDPI_FACTOR` to be bigger than 0, got '{}'", dpi_factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dpi_factor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See http://xpra.org/trac/ticket/728 for more information
|
|
||||||
if width_mm == 0 || width_mm == 0 {
|
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ppmm = (
|
|
||||||
(width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)
|
|
||||||
).sqrt();
|
|
||||||
// Quantize 1/12 step size
|
|
||||||
((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl XConnection {
|
impl XConnection {
|
||||||
pub unsafe fn get_output_info(&self, resources: *mut XRRScreenResources, repr: &MonitorRepr) -> (String, f32) {
|
pub unsafe fn get_output_info(&self, resources: *mut ffi::XRRScreenResources, repr: &MonitorRepr) -> (String, f64) {
|
||||||
let output_info = (self.xrandr.XRRGetOutputInfo)(
|
let output_info = (self.xrandr.XRRGetOutputInfo)(
|
||||||
self.display,
|
self.display,
|
||||||
resources,
|
resources,
|
||||||
|
@ -92,7 +92,7 @@ impl XConnection {
|
||||||
let hidpi_factor = calc_dpi_factor(
|
let hidpi_factor = calc_dpi_factor(
|
||||||
repr.get_dimensions(),
|
repr.get_dimensions(),
|
||||||
((*output_info).mm_width as u64, (*output_info).mm_height as u64),
|
((*output_info).mm_width as u64, (*output_info).mm_height as u64),
|
||||||
) as f32;
|
);
|
||||||
(self.xrandr.XRRFreeOutputInfo)(output_info);
|
(self.xrandr.XRRFreeOutputInfo)(output_info);
|
||||||
(name, hidpi_factor)
|
(name, hidpi_factor)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,15 +7,14 @@ use std::sync::Arc;
|
||||||
use libc;
|
use libc;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
use {CursorState, Icon, MouseCursor, WindowAttributes};
|
use {CursorState, Icon, LogicalPosition, LogicalSize, MouseCursor, WindowAttributes};
|
||||||
use CreationError::{self, OsError};
|
use CreationError::{self, OsError};
|
||||||
use platform::MonitorId as PlatformMonitorId;
|
use platform::MonitorId as PlatformMonitorId;
|
||||||
use platform::PlatformSpecificWindowBuilderAttributes;
|
use platform::PlatformSpecificWindowBuilderAttributes;
|
||||||
use platform::x11::MonitorId as X11MonitorId;
|
use platform::x11::MonitorId as X11MonitorId;
|
||||||
use platform::x11::monitor::get_monitor_for_window;
|
|
||||||
use window::MonitorId as RootMonitorId;
|
use window::MonitorId as RootMonitorId;
|
||||||
|
|
||||||
use super::{ffi, util, XConnection, XError, WindowId, EventsLoop};
|
use super::{ffi, util, ImeSender, XConnection, XError, WindowId, EventsLoop};
|
||||||
|
|
||||||
unsafe extern "C" fn visibility_predicate(
|
unsafe extern "C" fn visibility_predicate(
|
||||||
_display: *mut ffi::Display,
|
_display: *mut ffi::Display,
|
||||||
|
@ -29,7 +28,8 @@ unsafe extern "C" fn visibility_predicate(
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct SharedState {
|
pub struct SharedState {
|
||||||
pub multitouch: bool,
|
// Window creation assumes a DPI factor of 1.0, so we use this flag to handle that special case.
|
||||||
|
pub is_new_window: bool,
|
||||||
pub cursor_pos: Option<(f64, f64)>,
|
pub cursor_pos: Option<(f64, f64)>,
|
||||||
pub size: Option<(u32, u32)>,
|
pub size: Option<(u32, u32)>,
|
||||||
pub position: Option<(i32, i32)>,
|
pub position: Option<(i32, i32)>,
|
||||||
|
@ -37,9 +37,19 @@ pub struct SharedState {
|
||||||
pub inner_position_rel_parent: Option<(i32, i32)>,
|
pub inner_position_rel_parent: Option<(i32, i32)>,
|
||||||
pub last_monitor: Option<X11MonitorId>,
|
pub last_monitor: Option<X11MonitorId>,
|
||||||
pub dpi_adjusted: Option<(f64, f64)>,
|
pub dpi_adjusted: Option<(f64, f64)>,
|
||||||
|
// Used to restore position after exiting fullscreen.
|
||||||
|
pub restore_position: Option<(i32, i32)>,
|
||||||
pub frame_extents: Option<util::FrameExtentsHeuristic>,
|
pub frame_extents: Option<util::FrameExtentsHeuristic>,
|
||||||
pub min_dimensions: Option<(u32, u32)>,
|
pub min_dimensions: Option<LogicalSize>,
|
||||||
pub max_dimensions: Option<(u32, u32)>,
|
pub max_dimensions: Option<LogicalSize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SharedState {
|
||||||
|
fn new() -> Mutex<Self> {
|
||||||
|
let mut shared_state = SharedState::default();
|
||||||
|
shared_state.is_new_window = true;
|
||||||
|
Mutex::new(shared_state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for UnownedWindow {}
|
unsafe impl Send for UnownedWindow {}
|
||||||
|
@ -52,6 +62,7 @@ pub struct UnownedWindow {
|
||||||
screen_id: i32, // never changes
|
screen_id: i32, // never changes
|
||||||
cursor: Mutex<MouseCursor>,
|
cursor: Mutex<MouseCursor>,
|
||||||
cursor_state: Mutex<CursorState>,
|
cursor_state: Mutex<CursorState>,
|
||||||
|
ime_sender: Mutex<ImeSender>,
|
||||||
pub multitouch: bool, // never changes
|
pub multitouch: bool, // never changes
|
||||||
pub shared_state: Mutex<SharedState>,
|
pub shared_state: Mutex<SharedState>,
|
||||||
}
|
}
|
||||||
|
@ -65,15 +76,20 @@ impl UnownedWindow {
|
||||||
let xconn = &event_loop.xconn;
|
let xconn = &event_loop.xconn;
|
||||||
let root = event_loop.root;
|
let root = event_loop.root;
|
||||||
|
|
||||||
|
let max_dimensions: Option<(u32, u32)> = window_attrs.max_dimensions.map(Into::into);
|
||||||
|
let min_dimensions: Option<(u32, u32)> = window_attrs.min_dimensions.map(Into::into);
|
||||||
|
|
||||||
let dimensions = {
|
let dimensions = {
|
||||||
// x11 only applies constraints when the window is actively resized
|
// x11 only applies constraints when the window is actively resized
|
||||||
// by the user, so we have to manually apply the initial constraints
|
// by the user, so we have to manually apply the initial constraints
|
||||||
let mut dimensions = window_attrs.dimensions.unwrap_or((800, 600));
|
let mut dimensions = window_attrs.dimensions
|
||||||
if let Some(max) = window_attrs.max_dimensions {
|
.map(Into::into)
|
||||||
|
.unwrap_or((800, 600));
|
||||||
|
if let Some(max) = max_dimensions {
|
||||||
dimensions.0 = cmp::min(dimensions.0, max.0);
|
dimensions.0 = cmp::min(dimensions.0, max.0);
|
||||||
dimensions.1 = cmp::min(dimensions.1, max.1);
|
dimensions.1 = cmp::min(dimensions.1, max.1);
|
||||||
}
|
}
|
||||||
if let Some(min) = window_attrs.min_dimensions {
|
if let Some(min) = min_dimensions {
|
||||||
dimensions.0 = cmp::max(dimensions.0, min.0);
|
dimensions.0 = cmp::max(dimensions.0, min.0);
|
||||||
dimensions.1 = cmp::max(dimensions.1, min.1);
|
dimensions.1 = cmp::max(dimensions.1, min.1);
|
||||||
}
|
}
|
||||||
|
@ -145,8 +161,9 @@ impl UnownedWindow {
|
||||||
screen_id,
|
screen_id,
|
||||||
cursor: Default::default(),
|
cursor: Default::default(),
|
||||||
cursor_state: Default::default(),
|
cursor_state: Default::default(),
|
||||||
|
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
|
||||||
multitouch: window_attrs.multitouch,
|
multitouch: window_attrs.multitouch,
|
||||||
shared_state: Default::default(),
|
shared_state: SharedState::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Title must be set before mapping. Some tiling window managers (i.e. i3) use the window
|
// Title must be set before mapping. Some tiling window managers (i.e. i3) use the window
|
||||||
|
@ -218,46 +235,24 @@ impl UnownedWindow {
|
||||||
|
|
||||||
// set size hints
|
// set size hints
|
||||||
{
|
{
|
||||||
(*window.shared_state.lock()).min_dimensions = window_attrs.min_dimensions;
|
|
||||||
(*window.shared_state.lock()).max_dimensions = window_attrs.max_dimensions;
|
|
||||||
let mut min_dimensions = window_attrs.min_dimensions;
|
let mut min_dimensions = window_attrs.min_dimensions;
|
||||||
let mut max_dimensions = window_attrs.max_dimensions;
|
let mut max_dimensions = window_attrs.max_dimensions;
|
||||||
if !window_attrs.resizable && !util::wm_name_is_one_of(&["Xfwm4"]) {
|
if !window_attrs.resizable && !util::wm_name_is_one_of(&["Xfwm4"]) {
|
||||||
max_dimensions = Some(dimensions);
|
max_dimensions = Some(dimensions.into());
|
||||||
min_dimensions = Some(dimensions);
|
min_dimensions = Some(dimensions.into());
|
||||||
|
|
||||||
|
let mut shared_state_lock = window.shared_state.lock();
|
||||||
|
shared_state_lock.min_dimensions = window_attrs.min_dimensions;
|
||||||
|
shared_state_lock.max_dimensions = window_attrs.max_dimensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut size_hints = xconn.alloc_size_hints();
|
let mut normal_hints = util::NormalHints::new(xconn);
|
||||||
(*size_hints).flags = ffi::PSize;
|
normal_hints.set_size(Some(dimensions));
|
||||||
(*size_hints).width = dimensions.0 as c_int;
|
normal_hints.set_min_size(min_dimensions.map(Into::into));
|
||||||
(*size_hints).height = dimensions.1 as c_int;
|
normal_hints.set_max_size(max_dimensions.map(Into::into));
|
||||||
if let Some((min_width, min_height)) = min_dimensions {
|
normal_hints.set_resize_increments(pl_attribs.resize_increments);
|
||||||
(*size_hints).flags |= ffi::PMinSize;
|
normal_hints.set_base_size(pl_attribs.base_size);
|
||||||
(*size_hints).min_width = min_width as c_int;
|
xconn.set_normal_hints(window.xwindow, normal_hints).queue();
|
||||||
(*size_hints).min_height = min_height as c_int;
|
|
||||||
}
|
|
||||||
if let Some((max_width, max_height)) = max_dimensions {
|
|
||||||
(*size_hints).flags |= ffi::PMaxSize;
|
|
||||||
(*size_hints).max_width = max_width as c_int;
|
|
||||||
(*size_hints).max_height = max_height as c_int;
|
|
||||||
}
|
|
||||||
if let Some((width_inc, height_inc)) = pl_attribs.resize_increments {
|
|
||||||
(*size_hints).flags |= ffi::PResizeInc;
|
|
||||||
(*size_hints).width_inc = width_inc as c_int;
|
|
||||||
(*size_hints).height_inc = height_inc as c_int;
|
|
||||||
}
|
|
||||||
if let Some((base_width, base_height)) = pl_attribs.base_size {
|
|
||||||
(*size_hints).flags |= ffi::PBaseSize;
|
|
||||||
(*size_hints).base_width = base_width as c_int;
|
|
||||||
(*size_hints).base_height = base_height as c_int;
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
(xconn.xlib.XSetWMNormalHints)(
|
|
||||||
xconn.display,
|
|
||||||
window.xwindow,
|
|
||||||
size_hints.ptr,
|
|
||||||
);
|
|
||||||
}//.queue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set window icons
|
// Set window icons
|
||||||
|
@ -291,7 +286,7 @@ impl UnownedWindow {
|
||||||
&mut supported_ptr,
|
&mut supported_ptr,
|
||||||
);
|
);
|
||||||
if supported_ptr == ffi::False {
|
if supported_ptr == ffi::False {
|
||||||
return Err(OsError(format!("XkbSetDetectableAutoRepeat failed")));
|
return Err(OsError(format!("`XkbSetDetectableAutoRepeat` failed")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,6 +310,15 @@ impl UnownedWindow {
|
||||||
};
|
};
|
||||||
xconn.select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask).queue();
|
xconn.select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask).queue();
|
||||||
|
|
||||||
|
{
|
||||||
|
let result = event_loop.ime
|
||||||
|
.borrow_mut()
|
||||||
|
.create_context(window.xwindow);
|
||||||
|
if let Err(err) = result {
|
||||||
|
return Err(OsError(format!("Failed to create input context: {:?}", err)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// These properties must be set after mapping
|
// These properties must be set after mapping
|
||||||
if window_attrs.maximized {
|
if window_attrs.maximized {
|
||||||
window.set_maximized_inner(window_attrs.maximized).queue();
|
window.set_maximized_inner(window_attrs.maximized).queue();
|
||||||
|
@ -355,6 +359,16 @@ impl UnownedWindow {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn logicalize_coords(&self, (x, y): (i32, i32)) -> LogicalPosition {
|
||||||
|
let dpi = self.get_hidpi_factor();
|
||||||
|
LogicalPosition::from_physical((x, y), dpi)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn logicalize_size(&self, (width, height): (u32, u32)) -> LogicalSize {
|
||||||
|
let dpi = self.get_hidpi_factor();
|
||||||
|
LogicalSize::from_physical((width, height), dpi)
|
||||||
|
}
|
||||||
|
|
||||||
fn set_pid(&self) -> Option<util::Flusher> {
|
fn set_pid(&self) -> Option<util::Flusher> {
|
||||||
let pid_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_PID\0") };
|
let pid_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_PID\0") };
|
||||||
let client_machine_atom = unsafe { self.xconn.get_atom_unchecked(b"WM_CLIENT_MACHINE\0") };
|
let client_machine_atom = unsafe { self.xconn.get_atom_unchecked(b"WM_CLIENT_MACHINE\0") };
|
||||||
|
@ -400,6 +414,7 @@ impl UnownedWindow {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn set_urgent(&self, is_urgent: bool) {
|
pub fn set_urgent(&self, is_urgent: bool) {
|
||||||
let mut wm_hints = self.xconn.get_wm_hints(self.xwindow).expect("`XGetWMHints` failed");
|
let mut wm_hints = self.xconn.get_wm_hints(self.xwindow).expect("`XGetWMHints` failed");
|
||||||
if is_urgent {
|
if is_urgent {
|
||||||
|
@ -439,17 +454,24 @@ impl UnownedWindow {
|
||||||
fn set_fullscreen_inner(&self, monitor: Option<RootMonitorId>) -> util::Flusher {
|
fn set_fullscreen_inner(&self, monitor: Option<RootMonitorId>) -> util::Flusher {
|
||||||
match monitor {
|
match monitor {
|
||||||
None => {
|
None => {
|
||||||
self.set_fullscreen_hint(false)
|
let flusher = self.set_fullscreen_hint(false);
|
||||||
|
if let Some(position) = self.shared_state.lock().restore_position.take() {
|
||||||
|
self.set_position_inner(position.0, position.1).queue();
|
||||||
|
}
|
||||||
|
flusher
|
||||||
},
|
},
|
||||||
Some(RootMonitorId { inner: PlatformMonitorId::X(monitor) }) => {
|
Some(RootMonitorId { inner: PlatformMonitorId::X(monitor) }) => {
|
||||||
let screenpos = monitor.get_position();
|
let window_position = self.get_position_physical();
|
||||||
self.set_position(screenpos.0 as i32, screenpos.1 as i32);
|
self.shared_state.lock().restore_position = window_position;
|
||||||
|
let monitor_origin: (i32, i32) = monitor.get_position().into();
|
||||||
|
self.set_position_inner(monitor_origin.0, monitor_origin.1).queue();
|
||||||
self.set_fullscreen_hint(true)
|
self.set_fullscreen_hint(true)
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
|
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
|
||||||
self.set_fullscreen_inner(monitor)
|
self.set_fullscreen_inner(monitor)
|
||||||
.flush()
|
.flush()
|
||||||
|
@ -459,15 +481,26 @@ impl UnownedWindow {
|
||||||
|
|
||||||
fn get_rect(&self) -> Option<util::Rect> {
|
fn get_rect(&self) -> Option<util::Rect> {
|
||||||
// TODO: This might round-trip more times than needed.
|
// TODO: This might round-trip more times than needed.
|
||||||
if let (Some(position), Some(size)) = (self.get_position(), self.get_outer_size()) {
|
if let (Some(position), Some(size)) = (self.get_position_physical(), self.get_outer_size_physical()) {
|
||||||
Some(util::Rect::new(position, size))
|
Some(util::Rect::new(position, size))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn get_current_monitor(&self) -> X11MonitorId {
|
pub fn get_current_monitor(&self) -> X11MonitorId {
|
||||||
get_monitor_for_window(&self.xconn, self.get_rect()).to_owned()
|
let monitor = self.shared_state
|
||||||
|
.lock()
|
||||||
|
.last_monitor
|
||||||
|
.as_ref()
|
||||||
|
.cloned();
|
||||||
|
monitor
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
let monitor = self.xconn.get_monitor_for_window(self.get_rect()).to_owned();
|
||||||
|
self.shared_state.lock().last_monitor = Some(monitor.clone());
|
||||||
|
monitor
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_maximized_inner(&self, maximized: bool) -> util::Flusher {
|
fn set_maximized_inner(&self, maximized: bool) -> util::Flusher {
|
||||||
|
@ -476,6 +509,7 @@ impl UnownedWindow {
|
||||||
self.set_netwm(maximized.into(), (horz_atom as c_long, vert_atom as c_long, 0, 0))
|
self.set_netwm(maximized.into(), (horz_atom as c_long, vert_atom as c_long, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn set_maximized(&self, maximized: bool) {
|
pub fn set_maximized(&self, maximized: bool) {
|
||||||
self.set_maximized_inner(maximized)
|
self.set_maximized_inner(maximized)
|
||||||
.flush()
|
.flush()
|
||||||
|
@ -503,6 +537,7 @@ impl UnownedWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn set_title(&self, title: &str) {
|
pub fn set_title(&self, title: &str) {
|
||||||
self.set_title_inner(title)
|
self.set_title_inner(title)
|
||||||
.flush()
|
.flush()
|
||||||
|
@ -526,6 +561,7 @@ impl UnownedWindow {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn set_decorations(&self, decorations: bool) {
|
pub fn set_decorations(&self, decorations: bool) {
|
||||||
self.set_decorations_inner(decorations)
|
self.set_decorations_inner(decorations)
|
||||||
.flush()
|
.flush()
|
||||||
|
@ -538,6 +574,7 @@ impl UnownedWindow {
|
||||||
self.set_netwm(always_on_top.into(), (above_atom as c_long, 0, 0, 0))
|
self.set_netwm(always_on_top.into(), (above_atom as c_long, 0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn set_always_on_top(&self, always_on_top: bool) {
|
pub fn set_always_on_top(&self, always_on_top: bool) {
|
||||||
self.set_always_on_top_inner(always_on_top)
|
self.set_always_on_top_inner(always_on_top)
|
||||||
.flush()
|
.flush()
|
||||||
|
@ -568,6 +605,7 @@ impl UnownedWindow {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn set_window_icon(&self, icon: Option<Icon>) {
|
pub fn set_window_icon(&self, icon: Option<Icon>) {
|
||||||
match icon {
|
match icon {
|
||||||
Some(icon) => self.set_icon_inner(icon),
|
Some(icon) => self.set_icon_inner(icon),
|
||||||
|
@ -575,6 +613,7 @@ impl UnownedWindow {
|
||||||
}.flush().expect("Failed to set icons");
|
}.flush().expect("Failed to set icons");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn show(&self) {
|
pub fn show(&self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
(self.xconn.xlib.XMapRaised)(self.xconn.display, self.xwindow);
|
(self.xconn.xlib.XMapRaised)(self.xconn.display, self.xwindow);
|
||||||
|
@ -583,6 +622,7 @@ impl UnownedWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn hide(&self) {
|
pub fn hide(&self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
(self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow);
|
(self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow);
|
||||||
|
@ -596,31 +636,46 @@ impl UnownedWindow {
|
||||||
(*self.shared_state.lock()).frame_extents = Some(extents);
|
(*self.shared_state.lock()).frame_extents = Some(extents);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invalidate_cached_frame_extents(&self) {
|
pub(crate) fn invalidate_cached_frame_extents(&self) {
|
||||||
(*self.shared_state.lock()).frame_extents.take();
|
(*self.shared_state.lock()).frame_extents.take();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub(crate) fn get_position_physical(&self) -> Option<(i32, i32)> {
|
||||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
|
||||||
let extents = (*self.shared_state.lock()).frame_extents.clone();
|
let extents = (*self.shared_state.lock()).frame_extents.clone();
|
||||||
if let Some(extents) = extents {
|
if let Some(extents) = extents {
|
||||||
self.get_inner_position().map(|(x, y)|
|
self.get_inner_position_physical()
|
||||||
extents.inner_pos_to_outer(x, y)
|
.map(|(x, y)| extents.inner_pos_to_outer(x, y))
|
||||||
)
|
} else {
|
||||||
|
self.update_cached_frame_extents();
|
||||||
|
self.get_position_physical()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||||
|
let extents = (*self.shared_state.lock()).frame_extents.clone();
|
||||||
|
if let Some(extents) = extents {
|
||||||
|
self.get_inner_position()
|
||||||
|
.map(|logical| extents.inner_pos_to_outer_logical(logical, self.get_hidpi_factor()))
|
||||||
} else {
|
} else {
|
||||||
self.update_cached_frame_extents();
|
self.update_cached_frame_extents();
|
||||||
self.get_position()
|
self.get_position()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub(crate) fn get_inner_position_physical(&self) -> Option<(i32, i32)> {
|
||||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
self.xconn.translate_coords(self.xwindow, self.root)
|
||||||
self.xconn.translate_coords(self.xwindow, self.root )
|
|
||||||
.ok()
|
.ok()
|
||||||
.map(|coords| (coords.x_rel_root, coords.y_rel_root))
|
.map(|coords| (coords.x_rel_root, coords.y_rel_root))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_position(&self, mut x: i32, mut y: i32) {
|
#[inline]
|
||||||
|
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||||
|
self.get_inner_position_physical()
|
||||||
|
.map(|coords| self.logicalize_coords(coords))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_position_inner(&self, mut x: i32, mut y: i32) -> util::Flusher {
|
||||||
// There are a few WMs that set client area position rather than window position, so
|
// There are a few WMs that set client area position rather than window position, so
|
||||||
// we'll translate for consistency.
|
// we'll translate for consistency.
|
||||||
if util::wm_name_is_one_of(&["Enlightenment", "FVWM"]) {
|
if util::wm_name_is_one_of(&["Enlightenment", "FVWM"]) {
|
||||||
|
@ -630,7 +685,7 @@ impl UnownedWindow {
|
||||||
y += extents.frame_extents.top as i32;
|
y += extents.frame_extents.top as i32;
|
||||||
} else {
|
} else {
|
||||||
self.update_cached_frame_extents();
|
self.update_cached_frame_extents();
|
||||||
self.set_position(x, y)
|
return self.set_position_inner(x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -640,32 +695,58 @@ impl UnownedWindow {
|
||||||
x as c_int,
|
x as c_int,
|
||||||
y as c_int,
|
y as c_int,
|
||||||
);
|
);
|
||||||
self.xconn.flush_requests()
|
}
|
||||||
}.expect("Failed to call XMoveWindow");
|
util::Flusher::new(&self.xconn)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_position_physical(&self, x: i32, y: i32) {
|
||||||
|
self.set_position_inner(x, y)
|
||||||
|
.flush()
|
||||||
|
.expect("Failed to call `XMoveWindow`");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
pub fn set_position(&self, logical_position: LogicalPosition) {
|
||||||
|
let (x, y) = logical_position.to_physical(self.get_hidpi_factor()).into();
|
||||||
|
self.set_position_physical(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_inner_size_physical(&self) -> Option<(u32, u32)> {
|
||||||
self.xconn.get_geometry(self.xwindow)
|
self.xconn.get_geometry(self.xwindow)
|
||||||
.ok()
|
.ok()
|
||||||
.map(|geo| (geo.width, geo.height))
|
.map(|geo| (geo.width, geo.height))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||||
let extents = (*self.shared_state.lock()).frame_extents.clone();
|
self.get_inner_size_physical()
|
||||||
|
.map(|size| self.logicalize_size(size))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_outer_size_physical(&self) -> Option<(u32, u32)> {
|
||||||
|
let extents = self.shared_state.lock().frame_extents.clone();
|
||||||
if let Some(extents) = extents {
|
if let Some(extents) = extents {
|
||||||
self.get_inner_size().map(|(w, h)|
|
self.get_inner_size_physical()
|
||||||
extents.inner_size_to_outer(w, h)
|
.map(|(w, h)| extents.inner_size_to_outer(w, h))
|
||||||
)
|
} else {
|
||||||
|
self.update_cached_frame_extents();
|
||||||
|
self.get_outer_size_physical()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||||
|
let extents = self.shared_state.lock().frame_extents.clone();
|
||||||
|
if let Some(extents) = extents {
|
||||||
|
self.get_inner_size()
|
||||||
|
.map(|logical| extents.inner_size_to_outer_logical(logical, self.get_hidpi_factor()))
|
||||||
} else {
|
} else {
|
||||||
self.update_cached_frame_extents();
|
self.update_cached_frame_extents();
|
||||||
self.get_outer_size()
|
self.get_outer_size()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub(crate) fn set_inner_size_physical(&self, width: u32, height: u32) {
|
||||||
pub fn set_inner_size(&self, width: u32, height: u32) {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
(self.xconn.xlib.XResizeWindow)(
|
(self.xconn.xlib.XResizeWindow)(
|
||||||
self.xconn.display,
|
self.xconn.display,
|
||||||
|
@ -674,62 +755,86 @@ impl UnownedWindow {
|
||||||
height as c_uint,
|
height as c_uint,
|
||||||
);
|
);
|
||||||
self.xconn.flush_requests()
|
self.xconn.flush_requests()
|
||||||
}.expect("Failed to call XResizeWindow");
|
}.expect("Failed to call `XResizeWindow`");
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn update_normal_hints<F>(&self, callback: F) -> Result<(), XError>
|
#[inline]
|
||||||
where F: FnOnce(*mut ffi::XSizeHints) -> ()
|
pub fn set_inner_size(&self, logical_size: LogicalSize) {
|
||||||
|
let dpi_factor = self.get_hidpi_factor();
|
||||||
|
let (width, height) = logical_size.to_physical(dpi_factor).into();
|
||||||
|
self.set_inner_size_physical(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_normal_hints<F>(&self, callback: F) -> Result<(), XError>
|
||||||
|
where F: FnOnce(&mut util::NormalHints) -> ()
|
||||||
{
|
{
|
||||||
let size_hints = self.xconn.alloc_size_hints();
|
let mut normal_hints = self.xconn.get_normal_hints(self.xwindow)?;
|
||||||
let mut flags: c_long = mem::uninitialized();
|
callback(&mut normal_hints);
|
||||||
(self.xconn.xlib.XGetWMNormalHints)(
|
self.xconn.set_normal_hints(self.xwindow, normal_hints).flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_min_dimensions_physical(&self, dimensions: Option<(u32, u32)>) {
|
||||||
|
self.update_normal_hints(|normal_hints| normal_hints.set_min_size(dimensions))
|
||||||
|
.expect("Failed to call `XSetWMNormalHints`");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_min_dimensions(&self, logical_dimensions: Option<LogicalSize>) {
|
||||||
|
self.shared_state.lock().min_dimensions = logical_dimensions;
|
||||||
|
let physical_dimensions = logical_dimensions.map(|logical_dimensions| {
|
||||||
|
logical_dimensions.to_physical(self.get_hidpi_factor()).into()
|
||||||
|
});
|
||||||
|
self.set_min_dimensions_physical(physical_dimensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_max_dimensions_physical(&self, dimensions: Option<(u32, u32)>) {
|
||||||
|
self.update_normal_hints(|normal_hints| normal_hints.set_max_size(dimensions))
|
||||||
|
.expect("Failed to call `XSetWMNormalHints`");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_max_dimensions(&self, logical_dimensions: Option<LogicalSize>) {
|
||||||
|
self.shared_state.lock().max_dimensions = logical_dimensions;
|
||||||
|
let physical_dimensions = logical_dimensions.map(|logical_dimensions| {
|
||||||
|
logical_dimensions.to_physical(self.get_hidpi_factor()).into()
|
||||||
|
});
|
||||||
|
self.set_max_dimensions_physical(physical_dimensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn adjust_for_dpi(
|
||||||
|
&self,
|
||||||
|
old_dpi_factor: f64,
|
||||||
|
new_dpi_factor: f64,
|
||||||
|
width: f64,
|
||||||
|
height: f64,
|
||||||
|
) -> (f64, f64, util::Flusher) {
|
||||||
|
let scale_factor = new_dpi_factor / old_dpi_factor;
|
||||||
|
let new_width = width * scale_factor;
|
||||||
|
let new_height = height * scale_factor;
|
||||||
|
self.update_normal_hints(|normal_hints| {
|
||||||
|
let dpi_adjuster = |(width, height): (u32, u32)| -> (u32, u32) {
|
||||||
|
let new_width = width as f64 * scale_factor;
|
||||||
|
let new_height = height as f64 * scale_factor;
|
||||||
|
(new_width.round() as u32, new_height.round() as u32)
|
||||||
|
};
|
||||||
|
let max_size = normal_hints.get_max_size().map(&dpi_adjuster);
|
||||||
|
let min_size = normal_hints.get_min_size().map(&dpi_adjuster);
|
||||||
|
let resize_increments = normal_hints.get_resize_increments().map(&dpi_adjuster);
|
||||||
|
let base_size = normal_hints.get_base_size().map(&dpi_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");
|
||||||
|
unsafe {
|
||||||
|
(self.xconn.xlib.XResizeWindow)(
|
||||||
self.xconn.display,
|
self.xconn.display,
|
||||||
self.xwindow,
|
self.xwindow,
|
||||||
size_hints.ptr,
|
new_width.round() as c_uint,
|
||||||
&mut flags,
|
new_height.round() as c_uint,
|
||||||
);
|
);
|
||||||
self.xconn.check_errors()?;
|
|
||||||
|
|
||||||
callback(size_hints.ptr);
|
|
||||||
|
|
||||||
(self.xconn.xlib.XSetWMNormalHints)(
|
|
||||||
self.xconn.display,
|
|
||||||
self.xwindow,
|
|
||||||
size_hints.ptr,
|
|
||||||
);
|
|
||||||
self.xconn.flush_requests()?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
(new_width, new_height, util::Flusher::new(&self.xconn))
|
||||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
|
||||||
(*self.shared_state.lock()).min_dimensions = dimensions;
|
|
||||||
unsafe {
|
|
||||||
self.update_normal_hints(|size_hints| {
|
|
||||||
if let Some((width, height)) = dimensions {
|
|
||||||
(*size_hints).flags |= ffi::PMinSize;
|
|
||||||
(*size_hints).min_width = width as c_int;
|
|
||||||
(*size_hints).min_height = height as c_int;
|
|
||||||
} else {
|
|
||||||
(*size_hints).flags &= !ffi::PMinSize;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}.expect("Failed to call XSetWMNormalHints");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
|
||||||
(*self.shared_state.lock()).max_dimensions = dimensions;
|
|
||||||
unsafe {
|
|
||||||
self.update_normal_hints(|size_hints| {
|
|
||||||
if let Some((width, height)) = dimensions {
|
|
||||||
(*size_hints).flags |= ffi::PMaxSize;
|
|
||||||
(*size_hints).max_width = width as c_int;
|
|
||||||
(*size_hints).max_height = height as c_int;
|
|
||||||
} else {
|
|
||||||
(*size_hints).flags &= !ffi::PMaxSize;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}.expect("Failed to call XSetWMNormalHints");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_resizable(&self, resizable: bool) {
|
pub fn set_resizable(&self, resizable: bool) {
|
||||||
|
@ -739,24 +844,26 @@ impl UnownedWindow {
|
||||||
// the lesser of two evils and do nothing.
|
// the lesser of two evils and do nothing.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if resizable {
|
|
||||||
let min_dimensions = (*self.shared_state.lock()).min_dimensions;
|
let (logical_min, logical_max) = if resizable {
|
||||||
let max_dimensions = (*self.shared_state.lock()).max_dimensions;
|
let shared_state_lock = self.shared_state.lock();
|
||||||
self.set_min_dimensions(min_dimensions);
|
(shared_state_lock.min_dimensions, shared_state_lock.max_dimensions)
|
||||||
self.set_max_dimensions(max_dimensions);
|
|
||||||
} else {
|
} else {
|
||||||
unsafe {
|
let window_size = self.get_inner_size();
|
||||||
self.update_normal_hints(|size_hints| {
|
(window_size.clone(), window_size)
|
||||||
(*size_hints).flags |= ffi::PMinSize | ffi::PMaxSize;
|
};
|
||||||
if let Some((width, height)) = self.get_inner_size() {
|
|
||||||
(*size_hints).min_width = width as c_int;
|
let dpi_factor = self.get_hidpi_factor();
|
||||||
(*size_hints).min_height = height as c_int;
|
let min_dimensions = logical_min
|
||||||
(*size_hints).max_width = width as c_int;
|
.map(|logical_size| logical_size.to_physical(dpi_factor))
|
||||||
(*size_hints).max_height = height as c_int;
|
.map(Into::into);
|
||||||
}
|
let max_dimensions = logical_max
|
||||||
})
|
.map(|logical_size| logical_size.to_physical(dpi_factor))
|
||||||
}.expect("Failed to call XSetWMNormalHints");
|
.map(Into::into);
|
||||||
}
|
self.update_normal_hints(|normal_hints| {
|
||||||
|
normal_hints.set_min_size(min_dimensions);
|
||||||
|
normal_hints.set_max_size(max_dimensions);
|
||||||
|
}).expect("Failed to call `XSetWMNormalHints`");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -774,21 +881,12 @@ impl UnownedWindow {
|
||||||
Arc::clone(&self.xconn)
|
Arc::clone(&self.xconn)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
|
||||||
self.xconn.display as _
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_xlib_window(&self) -> c_ulong {
|
pub fn get_xlib_window(&self) -> c_ulong {
|
||||||
self.xwindow
|
self.xwindow
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
|
||||||
self.xwindow as _
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_xcb_connection(&self) -> *mut c_void {
|
pub fn get_xcb_connection(&self) -> *mut c_void {
|
||||||
unsafe {
|
unsafe {
|
||||||
(self.xconn.xlib_xcb.XGetXCBConnection)(self.xconn.display) as *mut _
|
(self.xconn.xlib_xcb.XGetXCBConnection)(self.xconn.display) as *mut _
|
||||||
|
@ -886,6 +984,7 @@ impl UnownedWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||||
*self.cursor.lock() = cursor;
|
*self.cursor.lock() = cursor;
|
||||||
if *self.cursor_state.lock() != CursorState::Hide {
|
if *self.cursor_state.lock() != CursorState::Hide {
|
||||||
|
@ -931,6 +1030,7 @@ impl UnownedWindow {
|
||||||
Some(cursor)
|
Some(cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||||
use CursorState::*;
|
use CursorState::*;
|
||||||
|
|
||||||
|
@ -994,11 +1094,12 @@ impl UnownedWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hidpi_factor(&self) -> f32 {
|
#[inline]
|
||||||
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
self.get_current_monitor().hidpi_factor
|
self.get_current_monitor().hidpi_factor
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
pub(crate) fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
(self.xconn.xlib.XWarpPointer)(
|
(self.xconn.xlib.XWarpPointer)(
|
||||||
self.xconn.display,
|
self.xconn.display,
|
||||||
|
@ -1015,6 +1116,24 @@ impl UnownedWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_cursor_position(&self, logical_position: LogicalPosition) -> Result<(), ()> {
|
||||||
|
let (x, y) = logical_position.to_physical(self.get_hidpi_factor()).into();
|
||||||
|
self.set_cursor_position_physical(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_ime_spot_physical(&self, x: i32, y: i32) {
|
||||||
|
let _ = self.ime_sender
|
||||||
|
.lock()
|
||||||
|
.send((self.xwindow, x as i16, y as i16));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_ime_spot(&self, logical_spot: LogicalPosition) {
|
||||||
|
let (x, y) = logical_spot.to_physical(self.get_hidpi_factor()).into();
|
||||||
|
self.set_ime_spot_physical(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn id(&self) -> WindowId { WindowId(self.xwindow) }
|
pub fn id(&self) -> WindowId { WindowId(self.xwindow) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -377,14 +377,16 @@ impl EventsLoop {
|
||||||
} else {
|
} else {
|
||||||
window.view.convertPoint_fromView_(window_point, cocoa::base::nil)
|
window.view.convertPoint_fromView_(window_point, cocoa::base::nil)
|
||||||
};
|
};
|
||||||
|
|
||||||
let view_rect = NSView::frame(*window.view);
|
let view_rect = NSView::frame(*window.view);
|
||||||
let scale_factor = window.hidpi_factor();
|
let x = view_point.x as f64;
|
||||||
|
let y = (view_rect.size.height - view_point.y) as f64;
|
||||||
let x = (scale_factor * view_point.x as f32) as f64;
|
let window_event = WindowEvent::CursorMoved {
|
||||||
let y = (scale_factor * (view_rect.size.height - view_point.y) as f32) as f64;
|
device_id: DEVICE_ID,
|
||||||
let window_event = WindowEvent::CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event_mods(ns_event) };
|
position: (x, y).into(),
|
||||||
|
modifiers: event_mods(ns_event),
|
||||||
|
};
|
||||||
let event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event };
|
let event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event };
|
||||||
|
|
||||||
self.shared.pending_events.lock().unwrap().push_back(event);
|
self.shared.pending_events.lock().unwrap().push_back(event);
|
||||||
Some(into_event(WindowEvent::CursorEntered { device_id: DEVICE_ID }))
|
Some(into_event(WindowEvent::CursorEntered { device_id: DEVICE_ID }))
|
||||||
},
|
},
|
||||||
|
@ -402,27 +404,25 @@ impl EventsLoop {
|
||||||
None => return None,
|
None => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let scale_factor = window.hidpi_factor();
|
|
||||||
|
|
||||||
let mut events = std::collections::VecDeque::with_capacity(3);
|
let mut events = std::collections::VecDeque::with_capacity(3);
|
||||||
|
|
||||||
let delta_x = (scale_factor * ns_event.deltaX() as f32) as f64;
|
let delta_x = ns_event.deltaX() as f64;
|
||||||
if delta_x != 0.0 {
|
if delta_x != 0.0 {
|
||||||
let motion_event = DeviceEvent::Motion { axis: 0, value: delta_x };
|
let motion_event = DeviceEvent::Motion { axis: 0, value: delta_x };
|
||||||
let event = Event::DeviceEvent{ device_id: DEVICE_ID, event: motion_event };
|
let event = Event::DeviceEvent { device_id: DEVICE_ID, event: motion_event };
|
||||||
events.push_back(event);
|
events.push_back(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
let delta_y = (scale_factor * ns_event.deltaY() as f32) as f64;
|
let delta_y = ns_event.deltaY() as f64;
|
||||||
if delta_y != 0.0 {
|
if delta_y != 0.0 {
|
||||||
let motion_event = DeviceEvent::Motion { axis: 1, value: delta_y };
|
let motion_event = DeviceEvent::Motion { axis: 1, value: delta_y };
|
||||||
let event = Event::DeviceEvent{ device_id: DEVICE_ID, event: motion_event };
|
let event = Event::DeviceEvent { device_id: DEVICE_ID, event: motion_event };
|
||||||
events.push_back(event);
|
events.push_back(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
if delta_x != 0.0 || delta_y != 0.0 {
|
if delta_x != 0.0 || delta_y != 0.0 {
|
||||||
let motion_event = DeviceEvent::MouseMotion { delta: (delta_x, delta_y) };
|
let motion_event = DeviceEvent::MouseMotion { delta: (delta_x, delta_y) };
|
||||||
let event = Event::DeviceEvent{ device_id: DEVICE_ID, event: motion_event };
|
let event = Event::DeviceEvent { device_id: DEVICE_ID, event: motion_event };
|
||||||
events.push_back(event);
|
events.push_back(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,19 +433,22 @@ impl EventsLoop {
|
||||||
|
|
||||||
appkit::NSScrollWheel => {
|
appkit::NSScrollWheel => {
|
||||||
// If none of the windows received the scroll, return `None`.
|
// If none of the windows received the scroll, return `None`.
|
||||||
let window = match maybe_window {
|
if maybe_window.is_none() {
|
||||||
Some(window) => window,
|
return None;
|
||||||
None => return None,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
use events::MouseScrollDelta::{LineDelta, PixelDelta};
|
use events::MouseScrollDelta::{LineDelta, PixelDelta};
|
||||||
let scale_factor = window.hidpi_factor();
|
|
||||||
let delta = if ns_event.hasPreciseScrollingDeltas() == cocoa::base::YES {
|
let delta = if ns_event.hasPreciseScrollingDeltas() == cocoa::base::YES {
|
||||||
PixelDelta(scale_factor * ns_event.scrollingDeltaX() as f32,
|
PixelDelta((
|
||||||
scale_factor * ns_event.scrollingDeltaY() as f32)
|
ns_event.scrollingDeltaX() as f64,
|
||||||
|
ns_event.scrollingDeltaY() as f64,
|
||||||
|
).into())
|
||||||
} else {
|
} else {
|
||||||
LineDelta(scale_factor * ns_event.scrollingDeltaX() as f32,
|
// TODO: This is probably wrong
|
||||||
scale_factor * ns_event.scrollingDeltaY() as f32)
|
LineDelta(
|
||||||
|
ns_event.scrollingDeltaX() as f32,
|
||||||
|
ns_event.scrollingDeltaY() as f32,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let phase = match ns_event.phase() {
|
let phase = match ns_event.phase() {
|
||||||
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => TouchPhase::Started,
|
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => TouchPhase::Started,
|
||||||
|
@ -456,11 +459,15 @@ impl EventsLoop {
|
||||||
device_id: DEVICE_ID,
|
device_id: DEVICE_ID,
|
||||||
event: DeviceEvent::MouseWheel {
|
event: DeviceEvent::MouseWheel {
|
||||||
delta: if ns_event.hasPreciseScrollingDeltas() == cocoa::base::YES {
|
delta: if ns_event.hasPreciseScrollingDeltas() == cocoa::base::YES {
|
||||||
PixelDelta(ns_event.scrollingDeltaX() as f32,
|
PixelDelta((
|
||||||
ns_event.scrollingDeltaY() as f32)
|
ns_event.scrollingDeltaX() as f64,
|
||||||
|
ns_event.scrollingDeltaY() as f64,
|
||||||
|
).into())
|
||||||
} else {
|
} else {
|
||||||
LineDelta(ns_event.scrollingDeltaX() as f32,
|
LineDelta(
|
||||||
ns_event.scrollingDeltaY() as f32)
|
ns_event.scrollingDeltaX() as f32,
|
||||||
|
ns_event.scrollingDeltaY() as f32,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
use cocoa::appkit::NSScreen;
|
use cocoa::appkit::NSScreen;
|
||||||
use cocoa::base::{id, nil};
|
use cocoa::base::{id, nil};
|
||||||
use cocoa::foundation::{NSString, NSUInteger};
|
use cocoa::foundation::{NSString, NSUInteger};
|
||||||
use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds};
|
use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds};
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::fmt;
|
use {PhysicalPosition, PhysicalSize};
|
||||||
use super::EventsLoop;
|
use super::EventsLoop;
|
||||||
use super::window::IdRef;
|
use super::window::IdRef;
|
||||||
|
|
||||||
|
@ -39,9 +42,9 @@ impl fmt::Debug for MonitorId {
|
||||||
struct MonitorId {
|
struct MonitorId {
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
native_identifier: u32,
|
native_identifier: u32,
|
||||||
dimensions: (u32, u32),
|
dimensions: PhysicalSize,
|
||||||
position: (i32, i32),
|
position: PhysicalPosition,
|
||||||
hidpi_factor: f32,
|
hidpi_factor: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
let monitor_id_proxy = MonitorId {
|
let monitor_id_proxy = MonitorId {
|
||||||
|
@ -68,30 +71,32 @@ impl MonitorId {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||||
let MonitorId(display_id) = *self;
|
let MonitorId(display_id) = *self;
|
||||||
let display = CGDisplay::new(display_id);
|
let display = CGDisplay::new(display_id);
|
||||||
let dimension = {
|
|
||||||
let height = display.pixels_high();
|
let height = display.pixels_high();
|
||||||
let width = display.pixels_wide();
|
let width = display.pixels_wide();
|
||||||
(width as u32, height as u32)
|
PhysicalSize::from_logical(
|
||||||
};
|
(width as f64, height as f64),
|
||||||
dimension
|
self.get_hidpi_factor(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_position(&self) -> (i32, i32) {
|
pub fn get_position(&self) -> PhysicalPosition {
|
||||||
let bounds = unsafe { CGDisplayBounds(self.get_native_identifier()) };
|
let bounds = unsafe { CGDisplayBounds(self.get_native_identifier()) };
|
||||||
(bounds.origin.x as i32, bounds.origin.y as i32)
|
PhysicalPosition::from_logical(
|
||||||
|
(bounds.origin.x as f64, bounds.origin.y as f64),
|
||||||
|
self.get_hidpi_factor(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_hidpi_factor(&self) -> f32 {
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
let screen = match self.get_nsscreen() {
|
let screen = match self.get_nsscreen() {
|
||||||
Some(screen) => screen,
|
Some(screen) => screen,
|
||||||
None => return 1.0, // default to 1.0 when we can't find the screen
|
None => return 1.0, // default to 1.0 when we can't find the screen
|
||||||
};
|
};
|
||||||
|
unsafe { NSScreen::backingScaleFactor(screen) as f64 }
|
||||||
unsafe { NSScreen::backingScaleFactor(screen) as f32 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_nsscreen(&self) -> Option<id> {
|
pub(crate) fn get_nsscreen(&self) -> Option<id> {
|
||||||
|
|
|
@ -14,8 +14,8 @@ pub const EMPTY_RANGE: ffi::NSRange = ffi::NSRange {
|
||||||
// For consistency with other platforms, this will...
|
// For consistency with other platforms, this will...
|
||||||
// 1. translate the bottom-left window corner into the top-left window corner
|
// 1. translate the bottom-left window corner into the top-left window corner
|
||||||
// 2. translate the coordinate from a bottom-left origin coordinate system to a top-left one
|
// 2. translate the coordinate from a bottom-left origin coordinate system to a top-left one
|
||||||
pub fn bottom_left_to_top_left(rect: NSRect) -> i32 {
|
pub fn bottom_left_to_top_left(rect: NSRect) -> f64 {
|
||||||
(CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height)) as _
|
CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn set_style_mask(window: id, view: id, mask: NSWindowStyleMask) {
|
pub unsafe fn set_style_mask(window: id, view: id, mask: NSWindowStyleMask) {
|
||||||
|
|
|
@ -22,7 +22,7 @@ use platform::platform::window::{get_window_id, IdRef};
|
||||||
struct ViewState {
|
struct ViewState {
|
||||||
window: id,
|
window: id,
|
||||||
shared: Weak<Shared>,
|
shared: Weak<Shared>,
|
||||||
ime_spot: Option<(i32, i32)>,
|
ime_spot: Option<(f64, f64)>,
|
||||||
raw_characters: Option<String>,
|
raw_characters: Option<String>,
|
||||||
last_insert: Option<String>,
|
last_insert: Option<String>,
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ pub fn new_view(window: id, shared: Weak<Shared>) -> IdRef {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_ime_spot(view: id, input_context: id, x: i32, y: i32) {
|
pub fn set_ime_spot(view: id, input_context: id, x: f64, y: f64) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state_ptr: *mut c_void = *(*view).get_mut_ivar("winitState");
|
let state_ptr: *mut c_void = *(*view).get_mut_ivar("winitState");
|
||||||
let state = &mut *(state_ptr as *mut ViewState);
|
let state = &mut *(state_ptr as *mut ViewState);
|
||||||
|
@ -51,8 +51,8 @@ pub fn set_ime_spot(view: id, input_context: id, x: i32, y: i32) {
|
||||||
state.window,
|
state.window,
|
||||||
NSWindow::frame(state.window),
|
NSWindow::frame(state.window),
|
||||||
);
|
);
|
||||||
let base_x = content_rect.origin.x as i32;
|
let base_x = content_rect.origin.x as f64;
|
||||||
let base_y = (content_rect.origin.y + content_rect.size.height) as i32;
|
let base_y = (content_rect.origin.y + content_rect.size.height) as f64;
|
||||||
state.ime_spot = Some((base_x + x, base_y - y));
|
state.ime_spot = Some((base_x + x, base_y - y));
|
||||||
let _: () = msg_send![input_context, invalidateCharacterCoordinates];
|
let _: () = msg_send![input_context, invalidateCharacterCoordinates];
|
||||||
}
|
}
|
||||||
|
@ -249,7 +249,7 @@ extern fn first_rect_for_character_range(
|
||||||
);
|
);
|
||||||
let x = content_rect.origin.x;
|
let x = content_rect.origin.x;
|
||||||
let y = util::bottom_left_to_top_left(content_rect);
|
let y = util::bottom_left_to_top_left(content_rect);
|
||||||
(x as i32, y as i32)
|
(x, y)
|
||||||
});
|
});
|
||||||
|
|
||||||
NSRect::new(
|
NSRect::new(
|
||||||
|
@ -527,15 +527,14 @@ fn mouse_motion(this: &Object, event: id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let scale_factor = NSWindow::backingScaleFactor(state.window) as f64;
|
let x = view_point.x as f64;
|
||||||
let x = scale_factor * view_point.x as f64;
|
let y = view_rect.size.height as f64 - view_point.y as f64;
|
||||||
let y = scale_factor * (view_rect.size.height as f64 - view_point.y as f64);
|
|
||||||
|
|
||||||
let window_event = Event::WindowEvent {
|
let window_event = Event::WindowEvent {
|
||||||
window_id: WindowId(get_window_id(state.window)),
|
window_id: WindowId(get_window_id(state.window)),
|
||||||
event: WindowEvent::CursorMoved {
|
event: WindowEvent::CursorMoved {
|
||||||
device_id: DEVICE_ID,
|
device_id: DEVICE_ID,
|
||||||
position: (x, y),
|
position: (x, y).into(),
|
||||||
modifiers: event_mods(event),
|
modifiers: event_mods(event),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,40 +1,52 @@
|
||||||
use {CreationError, Event, WindowEvent, WindowId, MouseCursor, CursorState};
|
|
||||||
use CreationError::OsError;
|
|
||||||
use libc;
|
|
||||||
|
|
||||||
use WindowAttributes;
|
|
||||||
use os::macos::ActivationPolicy;
|
|
||||||
use os::macos::WindowExt;
|
|
||||||
|
|
||||||
use objc;
|
|
||||||
use objc::runtime::{Class, Object, Sel, BOOL, YES, NO};
|
|
||||||
use objc::declare::ClassDecl;
|
|
||||||
|
|
||||||
use cocoa;
|
|
||||||
use cocoa::appkit::{self, NSApplication, NSColor, NSScreen, NSView, NSWindow, NSWindowButton,
|
|
||||||
NSWindowStyleMask};
|
|
||||||
use cocoa::base::{id, nil};
|
|
||||||
use cocoa::foundation::{NSDictionary, NSPoint, NSRect, NSSize, NSString, NSAutoreleasePool};
|
|
||||||
|
|
||||||
use core_graphics::display::CGDisplay;
|
|
||||||
|
|
||||||
use std;
|
use std;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::sync::Weak;
|
use std::sync::Weak;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
|
|
||||||
use super::events_loop::{EventsLoop, Shared};
|
use cocoa;
|
||||||
use platform::platform::ffi;
|
use cocoa::appkit::{
|
||||||
use platform::platform::util;
|
self,
|
||||||
use platform::platform::view::{new_view, set_ime_spot};
|
CGFloat,
|
||||||
|
NSApplication,
|
||||||
|
NSColor,
|
||||||
|
NSScreen,
|
||||||
|
NSView,
|
||||||
|
NSWindow,
|
||||||
|
NSWindowButton,
|
||||||
|
NSWindowStyleMask,
|
||||||
|
};
|
||||||
|
use cocoa::base::{id, nil};
|
||||||
|
use cocoa::foundation::{NSAutoreleasePool, NSDictionary, NSPoint, NSRect, NSSize, NSString};
|
||||||
|
|
||||||
|
use core_graphics::display::CGDisplay;
|
||||||
|
|
||||||
|
use objc;
|
||||||
|
use objc::runtime::{Class, Object, Sel, BOOL, YES, NO};
|
||||||
|
use objc::declare::ClassDecl;
|
||||||
|
|
||||||
|
use {
|
||||||
|
CreationError,
|
||||||
|
CursorState,
|
||||||
|
Event,
|
||||||
|
LogicalPosition,
|
||||||
|
LogicalSize,
|
||||||
|
MouseCursor,
|
||||||
|
WindowAttributes,
|
||||||
|
WindowEvent,
|
||||||
|
WindowId,
|
||||||
|
};
|
||||||
|
use CreationError::OsError;
|
||||||
|
use os::macos::{ActivationPolicy, WindowExt};
|
||||||
|
use platform::platform::{ffi, util};
|
||||||
|
use platform::platform::events_loop::{EventsLoop, Shared};
|
||||||
|
use platform::platform::view::{new_view, set_ime_spot};
|
||||||
use window::MonitorId as RootMonitorId;
|
use window::MonitorId as RootMonitorId;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Id(pub usize);
|
pub struct Id(pub usize);
|
||||||
|
|
||||||
struct DelegateState {
|
pub struct DelegateState {
|
||||||
view: IdRef,
|
view: IdRef,
|
||||||
window: IdRef,
|
window: IdRef,
|
||||||
shared: Weak<Shared>,
|
shared: Weak<Shared>,
|
||||||
|
@ -47,8 +59,11 @@ struct DelegateState {
|
||||||
// see comments of `window_did_fail_to_enter_fullscreen`
|
// see comments of `window_did_fail_to_enter_fullscreen`
|
||||||
handle_with_fullscreen: bool,
|
handle_with_fullscreen: bool,
|
||||||
|
|
||||||
// During windowDidResize, we use this to only send Moved if the position changed.
|
// During `windowDidResize`, we use this to only send Moved if the position changed.
|
||||||
previous_position: Option<(i32, i32)>,
|
previous_position: Option<(f64, f64)>,
|
||||||
|
|
||||||
|
// Used to prevent redundant events.
|
||||||
|
previous_dpi_factor: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DelegateState {
|
impl DelegateState {
|
||||||
|
@ -150,48 +165,44 @@ pub struct WindowDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowDelegate {
|
impl WindowDelegate {
|
||||||
/// Get the delegate class, initiailizing it neccessary
|
|
||||||
fn class() -> *const Class {
|
|
||||||
use std::os::raw::c_void;
|
|
||||||
|
|
||||||
// Emits an event via the `EventsLoop`'s callback or stores it in the pending queue.
|
// Emits an event via the `EventsLoop`'s callback or stores it in the pending queue.
|
||||||
unsafe fn emit_event(state: &mut DelegateState, window_event: WindowEvent) {
|
pub fn emit_event(state: &mut DelegateState, window_event: WindowEvent) {
|
||||||
let window_id = get_window_id(*state.window);
|
let window_id = get_window_id(*state.window);
|
||||||
let event = Event::WindowEvent {
|
let event = Event::WindowEvent {
|
||||||
window_id: WindowId(window_id),
|
window_id: WindowId(window_id),
|
||||||
event: window_event,
|
event: window_event,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(shared) = state.shared.upgrade() {
|
if let Some(shared) = state.shared.upgrade() {
|
||||||
shared.call_user_callback_with_event_or_store_in_pending(event);
|
shared.call_user_callback_with_event_or_store_in_pending(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when the window is resized or when the window was moved to a different screen.
|
pub fn emit_resize_event(state: &mut DelegateState) {
|
||||||
unsafe fn emit_resize_event(state: &mut DelegateState) {
|
let rect = unsafe { NSView::frame(*state.view) };
|
||||||
let rect = NSView::frame(*state.view);
|
let size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64);
|
||||||
let scale_factor = NSWindow::backingScaleFactor(*state.window) as f32;
|
WindowDelegate::emit_event(state, WindowEvent::Resized(size));
|
||||||
let width = (scale_factor * rect.size.width as f32) as u32;
|
|
||||||
let height = (scale_factor * rect.size.height as f32) as u32;
|
|
||||||
emit_event(state, WindowEvent::Resized(width, height));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn emit_move_event(state: &mut DelegateState) {
|
pub fn emit_move_event(state: &mut DelegateState) {
|
||||||
let frame_rect = NSWindow::frame(*state.window);
|
let rect = unsafe { NSWindow::frame(*state.window) };
|
||||||
let x = frame_rect.origin.x as _;
|
let x = rect.origin.x as f64;
|
||||||
let y = util::bottom_left_to_top_left(frame_rect);
|
let y = util::bottom_left_to_top_left(rect);
|
||||||
let moved = state.previous_position != Some((x, y));
|
let moved = state.previous_position != Some((x, y));
|
||||||
if moved {
|
if moved {
|
||||||
state.previous_position = Some((x, y));
|
state.previous_position = Some((x, y));
|
||||||
emit_event(state, WindowEvent::Moved(x, y));
|
WindowDelegate::emit_event(state, WindowEvent::Moved((x, y).into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the delegate class, initiailizing it neccessary
|
||||||
|
fn class() -> *const Class {
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
extern fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
|
extern fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state: *mut c_void = *this.get_ivar("winitState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
emit_event(state, WindowEvent::CloseRequested);
|
WindowDelegate::emit_event(state, WindowEvent::CloseRequested);
|
||||||
}
|
}
|
||||||
NO
|
NO
|
||||||
}
|
}
|
||||||
|
@ -201,7 +212,7 @@ impl WindowDelegate {
|
||||||
let state: *mut c_void = *this.get_ivar("winitState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
|
|
||||||
emit_event(state, WindowEvent::Destroyed);
|
WindowDelegate::emit_event(state, WindowEvent::Destroyed);
|
||||||
|
|
||||||
// Remove the window from the shared state.
|
// Remove the window from the shared state.
|
||||||
if let Some(shared) = state.shared.upgrade() {
|
if let Some(shared) = state.shared.upgrade() {
|
||||||
|
@ -215,8 +226,8 @@ impl WindowDelegate {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state: *mut c_void = *this.get_ivar("winitState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
emit_resize_event(state);
|
WindowDelegate::emit_resize_event(state);
|
||||||
emit_move_event(state);
|
WindowDelegate::emit_move_event(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +236,7 @@ impl WindowDelegate {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state: *mut c_void = *this.get_ivar("winitState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
emit_move_event(state);
|
WindowDelegate::emit_move_event(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,18 +244,26 @@ impl WindowDelegate {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state: *mut c_void = *this.get_ivar("winitState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
emit_resize_event(state);
|
let dpi_factor = NSWindow::backingScaleFactor(*state.window) as f64;
|
||||||
let scale_factor = NSWindow::backingScaleFactor(*state.window) as f32;
|
if state.previous_dpi_factor != dpi_factor {
|
||||||
emit_event(state, WindowEvent::HiDPIFactorChanged(scale_factor));
|
state.previous_dpi_factor = dpi_factor;
|
||||||
|
WindowDelegate::emit_event(state, WindowEvent::HiDpiFactorChanged(dpi_factor));
|
||||||
|
WindowDelegate::emit_resize_event(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This will always be called before `window_did_change_screen`.
|
||||||
extern fn window_did_change_backing_properties(this: &Object, _:Sel, _:id) {
|
extern fn window_did_change_backing_properties(this: &Object, _:Sel, _:id) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state: *mut c_void = *this.get_ivar("winitState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
let scale_factor = NSWindow::backingScaleFactor(*state.window) as f32;
|
let dpi_factor = NSWindow::backingScaleFactor(*state.window) as f64;
|
||||||
emit_event(state, WindowEvent::HiDPIFactorChanged(scale_factor));
|
if state.previous_dpi_factor != dpi_factor {
|
||||||
|
state.previous_dpi_factor = dpi_factor;
|
||||||
|
WindowDelegate::emit_event(state, WindowEvent::HiDpiFactorChanged(dpi_factor));
|
||||||
|
WindowDelegate::emit_resize_event(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +273,7 @@ impl WindowDelegate {
|
||||||
// lost focus
|
// lost focus
|
||||||
let state: *mut c_void = *this.get_ivar("winitState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
emit_event(state, WindowEvent::Focused(true));
|
WindowDelegate::emit_event(state, WindowEvent::Focused(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,7 +281,7 @@ impl WindowDelegate {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state: *mut c_void = *this.get_ivar("winitState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
emit_event(state, WindowEvent::Focused(false));
|
WindowDelegate::emit_event(state, WindowEvent::Focused(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,7 +304,7 @@ impl WindowDelegate {
|
||||||
|
|
||||||
let state: *mut c_void = *this.get_ivar("winitState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
emit_event(state, WindowEvent::HoveredFile(PathBuf::from(path)));
|
WindowDelegate::emit_event(state, WindowEvent::HoveredFile(PathBuf::from(path)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -314,7 +333,7 @@ impl WindowDelegate {
|
||||||
|
|
||||||
let state: *mut c_void = *this.get_ivar("winitState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
emit_event(state, WindowEvent::DroppedFile(PathBuf::from(path)));
|
WindowDelegate::emit_event(state, WindowEvent::DroppedFile(PathBuf::from(path)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -329,7 +348,7 @@ impl WindowDelegate {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state: *mut c_void = *this.get_ivar("winitState");
|
let state: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state as *mut DelegateState);
|
let state = &mut *(state as *mut DelegateState);
|
||||||
emit_event(state, WindowEvent::HoveredFileCancelled);
|
WindowDelegate::emit_event(state, WindowEvent::HoveredFileCancelled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,7 +519,7 @@ pub struct PlatformSpecificWindowBuilderAttributes {
|
||||||
pub titlebar_hidden: bool,
|
pub titlebar_hidden: bool,
|
||||||
pub titlebar_buttons_hidden: bool,
|
pub titlebar_buttons_hidden: bool,
|
||||||
pub fullsize_content_view: bool,
|
pub fullsize_content_view: bool,
|
||||||
pub resize_increments: Option<(u32, u32)>,
|
pub resize_increments: Option<LogicalSize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Window2 {
|
pub struct Window2 {
|
||||||
|
@ -617,12 +636,11 @@ impl Window2 {
|
||||||
|
|
||||||
app.activateIgnoringOtherApps_(YES);
|
app.activateIgnoringOtherApps_(YES);
|
||||||
|
|
||||||
if let Some((width, height)) = win_attribs.min_dimensions {
|
if let Some(dimensions) = win_attribs.min_dimensions {
|
||||||
nswindow_set_min_dimensions(window.0, width.into(), height.into());
|
nswindow_set_min_dimensions(window.0, dimensions);
|
||||||
}
|
}
|
||||||
|
if let Some(dimensions) = win_attribs.max_dimensions {
|
||||||
if let Some((width, height)) = win_attribs.max_dimensions {
|
nswindow_set_max_dimensions(window.0, dimensions);
|
||||||
nswindow_set_max_dimensions(window.0, width.into(), height.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
use cocoa::foundation::NSArray;
|
use cocoa::foundation::NSArray;
|
||||||
|
@ -631,7 +649,9 @@ impl Window2 {
|
||||||
registerForDraggedTypes:NSArray::arrayWithObject(nil, appkit::NSFilenamesPboardType)];
|
registerForDraggedTypes:NSArray::arrayWithObject(nil, appkit::NSFilenamesPboardType)];
|
||||||
}
|
}
|
||||||
|
|
||||||
let ds = DelegateState {
|
let dpi_factor = unsafe { NSWindow::backingScaleFactor(*window) as f64 };
|
||||||
|
|
||||||
|
let mut delegate_state = DelegateState {
|
||||||
view: view.clone(),
|
view: view.clone(),
|
||||||
window: window.clone(),
|
window: window.clone(),
|
||||||
shared,
|
shared,
|
||||||
|
@ -640,13 +660,19 @@ impl Window2 {
|
||||||
save_style_mask: Cell::new(None),
|
save_style_mask: Cell::new(None),
|
||||||
handle_with_fullscreen: win_attribs.fullscreen.is_some(),
|
handle_with_fullscreen: win_attribs.fullscreen.is_some(),
|
||||||
previous_position: None,
|
previous_position: None,
|
||||||
|
previous_dpi_factor: dpi_factor,
|
||||||
};
|
};
|
||||||
ds.win_attribs.borrow_mut().fullscreen = None;
|
delegate_state.win_attribs.borrow_mut().fullscreen = None;
|
||||||
|
|
||||||
|
if dpi_factor != 1.0 {
|
||||||
|
WindowDelegate::emit_event(&mut delegate_state, WindowEvent::HiDpiFactorChanged(dpi_factor));
|
||||||
|
WindowDelegate::emit_resize_event(&mut delegate_state);
|
||||||
|
}
|
||||||
|
|
||||||
let window = Window2 {
|
let window = Window2 {
|
||||||
view: view,
|
view: view,
|
||||||
window: window,
|
window: window,
|
||||||
delegate: WindowDelegate::new(ds),
|
delegate: WindowDelegate::new(delegate_state),
|
||||||
input_context,
|
input_context,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -729,8 +755,10 @@ impl Window2 {
|
||||||
let frame = match screen {
|
let frame = match screen {
|
||||||
Some(screen) => appkit::NSScreen::frame(screen),
|
Some(screen) => appkit::NSScreen::frame(screen),
|
||||||
None => {
|
None => {
|
||||||
let (width, height) = attrs.dimensions.unwrap_or((800, 600));
|
let (width, height) = attrs.dimensions
|
||||||
NSRect::new(NSPoint::new(0., 0.), NSSize::new(width as f64, height as f64))
|
.map(|logical| (logical.width, logical.height))
|
||||||
|
.unwrap_or((800.0, 600.0));
|
||||||
|
NSRect::new(NSPoint::new(0.0, 0.0), NSSize::new(width, height))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -799,9 +827,10 @@ impl Window2 {
|
||||||
let _: () = msg_send![*window, setLevel:ffi::NSWindowLevel::NSFloatingWindowLevel];
|
let _: () = msg_send![*window, setLevel:ffi::NSWindowLevel::NSFloatingWindowLevel];
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((x, y)) = pl_attrs.resize_increments {
|
if let Some(increments) = pl_attrs.resize_increments {
|
||||||
if x >= 1 && y >= 1 {
|
let (x, y) = (increments.width, increments.height);
|
||||||
let size = NSSize::new(x as _, y as _);
|
if x >= 1.0 && y >= 1.0 {
|
||||||
|
let size = NSSize::new(x as CGFloat, y as CGFloat);
|
||||||
window.setResizeIncrements_(size);
|
window.setResizeIncrements_(size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -843,15 +872,15 @@ impl Window2 {
|
||||||
unsafe { NSWindow::orderOut_(*self.window, nil); }
|
unsafe { NSWindow::orderOut_(*self.window, nil); }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||||
let frame_rect = unsafe { NSWindow::frame(*self.window) };
|
let frame_rect = unsafe { NSWindow::frame(*self.window) };
|
||||||
Some((
|
Some((
|
||||||
frame_rect.origin.x as i32,
|
frame_rect.origin.x as f64,
|
||||||
util::bottom_left_to_top_left(frame_rect),
|
util::bottom_left_to_top_left(frame_rect),
|
||||||
))
|
).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||||
let content_rect = unsafe {
|
let content_rect = unsafe {
|
||||||
NSWindow::contentRectForFrameRect_(
|
NSWindow::contentRectForFrameRect_(
|
||||||
*self.window,
|
*self.window,
|
||||||
|
@ -859,18 +888,18 @@ impl Window2 {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
Some((
|
Some((
|
||||||
content_rect.origin.x as i32,
|
content_rect.origin.x as f64,
|
||||||
util::bottom_left_to_top_left(content_rect),
|
util::bottom_left_to_top_left(content_rect),
|
||||||
))
|
).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_position(&self, x: i32, y: i32) {
|
pub fn set_position(&self, position: LogicalPosition) {
|
||||||
let dummy = NSRect::new(
|
let dummy = NSRect::new(
|
||||||
NSPoint::new(
|
NSPoint::new(
|
||||||
x as f64,
|
position.x,
|
||||||
// While it's true that we're setting the top-left position, it still needs to be
|
// While it's true that we're setting the top-left position, it still needs to be
|
||||||
// in a bottom-left coordinate system.
|
// in a bottom-left coordinate system.
|
||||||
CGDisplay::main().pixels_high() as f64 - y as f64,
|
CGDisplay::main().pixels_high() as f64 - position.y,
|
||||||
),
|
),
|
||||||
NSSize::new(0f64, 0f64),
|
NSSize::new(0f64, 0f64),
|
||||||
);
|
);
|
||||||
|
@ -880,42 +909,35 @@ impl Window2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||||
let factor = self.hidpi_factor() as f64; // API convention is that size is in physical pixels
|
let view_frame = unsafe { NSView::frame(*self.view) };
|
||||||
unsafe {
|
Some((view_frame.size.width as f64, view_frame.size.height as f64).into())
|
||||||
let view_frame = NSView::frame(*self.view);
|
|
||||||
Some(((view_frame.size.width*factor) as u32, (view_frame.size.height*factor) as u32))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||||
let factor = self.hidpi_factor() as f64; // API convention is that size is in physical pixels
|
let view_frame = unsafe { NSWindow::frame(*self.window) };
|
||||||
unsafe {
|
Some((view_frame.size.width as f64, view_frame.size.height as f64).into())
|
||||||
let window_frame = NSWindow::frame(*self.window);
|
|
||||||
Some(((window_frame.size.width*factor) as u32, (window_frame.size.height*factor) as u32))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_inner_size(&self, width: u32, height: u32) {
|
pub fn set_inner_size(&self, size: LogicalSize) {
|
||||||
let factor = self.hidpi_factor() as f64; // API convention is that size is in physical pixels
|
|
||||||
unsafe {
|
unsafe {
|
||||||
NSWindow::setContentSize_(*self.window, NSSize::new((width as f64)/factor, (height as f64)/factor));
|
NSWindow::setContentSize_(*self.window, NSSize::new(size.width as CGFloat, size.height as CGFloat));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
pub fn set_min_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let (width, height) = dimensions.unwrap_or((0, 0));
|
let dimensions = dimensions.unwrap_or_else(|| (0, 0).into());
|
||||||
nswindow_set_min_dimensions(self.window.0, width.into(), height.into());
|
nswindow_set_min_dimensions(self.window.0, dimensions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
pub fn set_max_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let (width, height) = dimensions.unwrap_or((!0, !0));
|
let dimensions = dimensions.unwrap_or_else(|| (!0, !0).into());
|
||||||
nswindow_set_max_dimensions(self.window.0, width.into(), height.into());
|
nswindow_set_max_dimensions(self.window.0, dimensions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -934,16 +956,6 @@ impl Window2 {
|
||||||
} // Otherwise, we don't change the mask until we exit fullscreen.
|
} // Otherwise, we don't change the mask until we exit fullscreen.
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
|
||||||
*self.window as *mut libc::c_void
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||||
let cursor_name = match cursor {
|
let cursor_name = match cursor {
|
||||||
MouseCursor::Arrow | MouseCursor::Default => "arrowCursor",
|
MouseCursor::Arrow | MouseCursor::Default => "arrowCursor",
|
||||||
|
@ -1005,24 +1017,24 @@ impl Window2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hidpi_factor(&self) -> f32 {
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
unsafe {
|
unsafe {
|
||||||
NSWindow::backingScaleFactor(*self.window) as f32
|
NSWindow::backingScaleFactor(*self.window) as f64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
pub fn set_cursor_position(&self, cursor_position: LogicalPosition) -> Result<(), ()> {
|
||||||
let (window_x, window_y) = self.get_position().unwrap_or((0, 0));
|
let window_position = self.get_inner_position()
|
||||||
let (cursor_x, cursor_y) = (window_x + x, window_y + y);
|
.expect("`get_inner_position` failed");
|
||||||
|
let point = appkit::CGPoint {
|
||||||
// TODO: Check for errors.
|
x: (cursor_position.x + window_position.x) as CGFloat,
|
||||||
let _ = CGDisplay::warp_mouse_cursor_position(appkit::CGPoint {
|
y: (cursor_position.y + window_position.y) as CGFloat,
|
||||||
x: cursor_x as appkit::CGFloat,
|
};
|
||||||
y: cursor_y as appkit::CGFloat,
|
CGDisplay::warp_mouse_cursor_position(point)
|
||||||
});
|
.expect("`CGWarpMouseCursorPosition` failed");
|
||||||
let _ = CGDisplay::associate_mouse_and_mouse_cursor_position(true);
|
CGDisplay::associate_mouse_and_mouse_cursor_position(true)
|
||||||
|
.expect("`CGAssociateMouseAndMouseCursorPosition` failed");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1131,8 +1143,8 @@ impl Window2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_ime_spot(&self, x: i32, y: i32) {
|
pub fn set_ime_spot(&self, logical_spot: LogicalPosition) {
|
||||||
set_ime_spot(*self.view, *self.input_context, x, y);
|
set_ime_spot(*self.view, *self.input_context, logical_spot.x, logical_spot.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1149,51 +1161,50 @@ pub fn get_window_id(window_cocoa_id: id) -> Id {
|
||||||
Id(window_cocoa_id as *const objc::runtime::Object as usize)
|
Id(window_cocoa_id as *const objc::runtime::Object as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn nswindow_set_min_dimensions<V: NSWindow + Copy>(
|
unsafe fn nswindow_set_min_dimensions<V: NSWindow + Copy>(window: V, mut min_size: LogicalSize) {
|
||||||
window: V, min_width: f64, min_height: f64)
|
let mut current_rect = NSWindow::frame(window);
|
||||||
{
|
let content_rect = NSWindow::contentRectForFrameRect_(window, NSWindow::frame(window));
|
||||||
|
// Convert from client area size to window size
|
||||||
|
min_size.width += (current_rect.size.width - content_rect.size.width) as f64; // this tends to be 0
|
||||||
|
min_size.height += (current_rect.size.height - content_rect.size.height) as f64;
|
||||||
window.setMinSize_(NSSize {
|
window.setMinSize_(NSSize {
|
||||||
width: min_width,
|
width: min_size.width as CGFloat,
|
||||||
height: min_height,
|
height: min_size.height as CGFloat,
|
||||||
});
|
});
|
||||||
// If necessary, resize the window to match constraint
|
// If necessary, resize the window to match constraint
|
||||||
let mut current_rect = NSWindow::frame(window);
|
if current_rect.size.width < min_size.width {
|
||||||
if current_rect.size.width < min_width {
|
current_rect.size.width = min_size.width;
|
||||||
current_rect.size.width = min_width;
|
|
||||||
window.setFrame_display_(current_rect, 0)
|
window.setFrame_display_(current_rect, 0)
|
||||||
}
|
}
|
||||||
if current_rect.size.height < min_height {
|
if current_rect.size.height < min_size.height {
|
||||||
// The origin point of a rectangle is at its bottom left in Cocoa. To
|
// The origin point of a rectangle is at its bottom left in Cocoa.
|
||||||
// ensure the window's top-left point remains the same:
|
// To ensure the window's top-left point remains the same:
|
||||||
current_rect.origin.y +=
|
current_rect.origin.y += current_rect.size.height - min_size.height;
|
||||||
current_rect.size.height - min_height;
|
current_rect.size.height = min_size.height;
|
||||||
|
|
||||||
current_rect.size.height = min_height;
|
|
||||||
window.setFrame_display_(current_rect, 0)
|
window.setFrame_display_(current_rect, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn nswindow_set_max_dimensions<V: NSWindow + Copy>(
|
unsafe fn nswindow_set_max_dimensions<V: NSWindow + Copy>(window: V, mut max_size: LogicalSize) {
|
||||||
window: V, max_width: f64, max_height: f64)
|
let mut current_rect = NSWindow::frame(window);
|
||||||
{
|
let content_rect = NSWindow::contentRectForFrameRect_(window, NSWindow::frame(window));
|
||||||
|
// Convert from client area size to window size
|
||||||
|
max_size.width += (current_rect.size.width - content_rect.size.width) as f64; // this tends to be 0
|
||||||
|
max_size.height += (current_rect.size.height - content_rect.size.height) as f64;
|
||||||
window.setMaxSize_(NSSize {
|
window.setMaxSize_(NSSize {
|
||||||
width: max_width,
|
width: max_size.width as CGFloat,
|
||||||
height: max_height,
|
height: max_size.height as CGFloat,
|
||||||
});
|
});
|
||||||
// If necessary, resize the window to match constraint
|
// If necessary, resize the window to match constraint
|
||||||
let mut current_rect = NSWindow::frame(window);
|
if current_rect.size.width > max_size.width {
|
||||||
if current_rect.size.width > max_width {
|
current_rect.size.width = max_size.width;
|
||||||
current_rect.size.width = max_width;
|
|
||||||
window.setFrame_display_(current_rect, 0)
|
window.setFrame_display_(current_rect, 0)
|
||||||
}
|
}
|
||||||
if current_rect.size.height > max_height {
|
if current_rect.size.height > max_size.height {
|
||||||
// The origin point of a rectangle is at its bottom left in
|
// The origin point of a rectangle is at its bottom left in Cocoa.
|
||||||
// Cocoa. To ensure the window's top-left point remains the
|
// To ensure the window's top-left point remains the same:
|
||||||
// same:
|
current_rect.origin.y += current_rect.size.height - max_size.height;
|
||||||
current_rect.origin.y +=
|
current_rect.size.height = max_size.height;
|
||||||
current_rect.size.height - max_height;
|
|
||||||
|
|
||||||
current_rect.size.height = max_height;
|
|
||||||
window.setFrame_display_(current_rect, 0)
|
window.setFrame_display_(current_rect, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
196
src/platform/windows/dpi.rs
Normal file
196
src/platform/windows/dpi.rs
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
#![allow(non_snake_case, unused_unsafe)]
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
use std::sync::{Once, ONCE_INIT};
|
||||||
|
|
||||||
|
use winapi::shared::minwindef::{BOOL, UINT, FALSE};
|
||||||
|
use winapi::shared::windef::{
|
||||||
|
DPI_AWARENESS_CONTEXT,
|
||||||
|
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE,
|
||||||
|
HDC,
|
||||||
|
HMONITOR,
|
||||||
|
HWND,
|
||||||
|
};
|
||||||
|
use winapi::shared::winerror::S_OK;
|
||||||
|
use winapi::um::libloaderapi::{GetProcAddress, LoadLibraryA};
|
||||||
|
use winapi::um::shellscalingapi::{
|
||||||
|
MDT_EFFECTIVE_DPI,
|
||||||
|
MONITOR_DPI_TYPE,
|
||||||
|
PROCESS_DPI_AWARENESS,
|
||||||
|
PROCESS_PER_MONITOR_DPI_AWARE,
|
||||||
|
};
|
||||||
|
use winapi::um::wingdi::{GetDeviceCaps, LOGPIXELSX};
|
||||||
|
use winapi::um::winnt::{HRESULT, LPCSTR};
|
||||||
|
use winapi::um::winuser::{self, MONITOR_DEFAULTTONEAREST};
|
||||||
|
|
||||||
|
const DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2: DPI_AWARENESS_CONTEXT = -4isize as _;
|
||||||
|
|
||||||
|
type SetProcessDPIAware = unsafe extern "system" fn () -> BOOL;
|
||||||
|
type SetProcessDpiAwareness = unsafe extern "system" fn (
|
||||||
|
value: PROCESS_DPI_AWARENESS,
|
||||||
|
) -> HRESULT;
|
||||||
|
type SetProcessDpiAwarenessContext = unsafe extern "system" fn (
|
||||||
|
value: DPI_AWARENESS_CONTEXT,
|
||||||
|
) -> BOOL;
|
||||||
|
type GetDpiForWindow = unsafe extern "system" fn (hwnd: HWND) -> UINT;
|
||||||
|
type GetDpiForMonitor = unsafe extern "system" fn (
|
||||||
|
hmonitor: HMONITOR,
|
||||||
|
dpi_type: MONITOR_DPI_TYPE,
|
||||||
|
dpi_x: *mut UINT,
|
||||||
|
dpi_y: *mut UINT,
|
||||||
|
) -> HRESULT;
|
||||||
|
type EnableNonClientDpiScaling = unsafe extern "system" fn (hwnd: HWND) -> BOOL;
|
||||||
|
|
||||||
|
// Helper function to dynamically load function pointer.
|
||||||
|
// `library` and `function` must be zero-terminated.
|
||||||
|
fn get_function_impl(library: &str, function: &str) -> Option<*const c_void> {
|
||||||
|
assert_eq!(library.chars().last(), Some('\0'));
|
||||||
|
assert_eq!(function.chars().last(), Some('\0'));
|
||||||
|
|
||||||
|
// Library names we will use are ASCII so we can use the A version to avoid string conversion.
|
||||||
|
let module = unsafe { LoadLibraryA(library.as_ptr() as LPCSTR) };
|
||||||
|
if module.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let function_ptr = unsafe { GetProcAddress(module, function.as_ptr() as LPCSTR) };
|
||||||
|
if function_ptr.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(function_ptr as _)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! get_function {
|
||||||
|
($lib:expr, $func:ident) => {
|
||||||
|
get_function_impl(concat!($lib, '\0'), concat!(stringify!($func), '\0'))
|
||||||
|
.map(|f| unsafe { mem::transmute::<*const _, $func>(f) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref GET_DPI_FOR_WINDOW: Option<GetDpiForWindow> = get_function!(
|
||||||
|
"user32.dll",
|
||||||
|
GetDpiForWindow
|
||||||
|
);
|
||||||
|
static ref GET_DPI_FOR_MONITOR: Option<GetDpiForMonitor> = get_function!(
|
||||||
|
"shcore.dll",
|
||||||
|
GetDpiForMonitor
|
||||||
|
);
|
||||||
|
static ref ENABLE_NON_CLIENT_DPI_SCALING: Option<EnableNonClientDpiScaling> = get_function!(
|
||||||
|
"user32.dll",
|
||||||
|
EnableNonClientDpiScaling
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn become_dpi_aware(enable: bool) {
|
||||||
|
if !enable { return; }
|
||||||
|
static ENABLE_DPI_AWARENESS: Once = ONCE_INIT;
|
||||||
|
ENABLE_DPI_AWARENESS.call_once(|| { unsafe {
|
||||||
|
if let Some(SetProcessDpiAwarenessContext) = get_function!(
|
||||||
|
"user32.dll",
|
||||||
|
SetProcessDpiAwarenessContext
|
||||||
|
) {
|
||||||
|
// We are on Windows 10 Anniversary Update (1607) or later.
|
||||||
|
if SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
|
||||||
|
== FALSE {
|
||||||
|
// V2 only works with Windows 10 Creators Update (1703). Try using the older
|
||||||
|
// V1 if we can't set V2.
|
||||||
|
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
|
||||||
|
}
|
||||||
|
} else if let Some(SetProcessDpiAwareness) = get_function!(
|
||||||
|
"shcore.dll",
|
||||||
|
SetProcessDpiAwareness
|
||||||
|
) {
|
||||||
|
// We are on Windows 8.1 or later.
|
||||||
|
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
|
||||||
|
} else if let Some(SetProcessDPIAware) = get_function!(
|
||||||
|
"user32.dll",
|
||||||
|
SetProcessDPIAware
|
||||||
|
) {
|
||||||
|
// We are on Vista or later.
|
||||||
|
SetProcessDPIAware();
|
||||||
|
}
|
||||||
|
} });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable_non_client_dpi_scaling(hwnd: HWND) {
|
||||||
|
unsafe {
|
||||||
|
if let Some(EnableNonClientDpiScaling) = *ENABLE_NON_CLIENT_DPI_SCALING {
|
||||||
|
EnableNonClientDpiScaling(hwnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_monitor_dpi(hmonitor: HMONITOR) -> Option<u32> {
|
||||||
|
unsafe {
|
||||||
|
if let Some(GetDpiForMonitor) = *GET_DPI_FOR_MONITOR {
|
||||||
|
// We are on Windows 8.1 or later.
|
||||||
|
let mut dpi_x = 0;
|
||||||
|
let mut dpi_y = 0;
|
||||||
|
if GetDpiForMonitor(hmonitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) == S_OK {
|
||||||
|
// MSDN says that "the values of *dpiX and *dpiY are identical. You only need to
|
||||||
|
// record one of the values to determine the DPI and respond appropriately".
|
||||||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx
|
||||||
|
return Some(dpi_x as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const BASE_DPI: u32 = 96;
|
||||||
|
pub fn dpi_to_scale_factor(dpi: u32) -> f64 {
|
||||||
|
dpi as f64 / BASE_DPI as f64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn get_window_dpi(hwnd: HWND, hdc: HDC) -> u32 {
|
||||||
|
if let Some(GetDpiForWindow) = *GET_DPI_FOR_WINDOW {
|
||||||
|
// We are on Windows 10 Anniversary Update (1607) or later.
|
||||||
|
match GetDpiForWindow(hwnd) {
|
||||||
|
0 => BASE_DPI, // 0 is returned if hwnd is invalid
|
||||||
|
dpi => dpi as u32,
|
||||||
|
}
|
||||||
|
} else if let Some(GetDpiForMonitor) = *GET_DPI_FOR_MONITOR {
|
||||||
|
// We are on Windows 8.1 or later.
|
||||||
|
let monitor = winuser::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||||
|
if monitor.is_null() {
|
||||||
|
return BASE_DPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut dpi_x = 0;
|
||||||
|
let mut dpi_y = 0;
|
||||||
|
if GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) == S_OK {
|
||||||
|
dpi_x as u32
|
||||||
|
} else {
|
||||||
|
BASE_DPI
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We are on Vista or later.
|
||||||
|
if winuser::IsProcessDPIAware() != FALSE {
|
||||||
|
// If the process is DPI aware, then scaling must be handled by the application using
|
||||||
|
// this DPI value.
|
||||||
|
GetDeviceCaps(hdc, LOGPIXELSX) as u32
|
||||||
|
} else {
|
||||||
|
// If the process is DPI unaware, then scaling is performed by the OS; we thus return
|
||||||
|
// 96 (scale factor 1.0) to prevent the window from being re-scaled by both the
|
||||||
|
// application and the WM.
|
||||||
|
BASE_DPI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use this when you have both the HWND and HDC on hand (i.e. window methods)
|
||||||
|
pub fn get_window_scale_factor(hwnd: HWND, hdc: HDC) -> f64 {
|
||||||
|
dpi_to_scale_factor(unsafe { get_window_dpi(hwnd, hdc) })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use this when you only have the HWND (i.e. event handling)
|
||||||
|
pub fn get_hwnd_scale_factor(hwnd: HWND) -> f64 {
|
||||||
|
let hdc = unsafe { winuser::GetDC(hwnd) };
|
||||||
|
if hdc.is_null() {
|
||||||
|
panic!("[winit] `GetDC` returned null!");
|
||||||
|
}
|
||||||
|
unsafe { get_window_scale_factor(hwnd, hdc) }
|
||||||
|
}
|
|
@ -12,42 +12,56 @@
|
||||||
//! The closure passed to the `execute_in_thread` method takes an `Inserter` that you can use to
|
//! The closure passed to the `execute_in_thread` method takes an `Inserter` that you can use to
|
||||||
//! add a `WindowState` entry to a list of window to be used by the callback.
|
//! add a `WindowState` entry to a list of window to be used by the callback.
|
||||||
|
|
||||||
|
use std::{mem, ptr, thread};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::mem;
|
|
||||||
use std::os::windows::ffi::OsStringExt;
|
use std::os::windows::ffi::OsStringExt;
|
||||||
use std::os::windows::io::AsRawHandle;
|
use std::os::windows::io::AsRawHandle;
|
||||||
use std::ptr;
|
use std::sync::{Arc, Barrier, Condvar, mpsc, Mutex};
|
||||||
use std::sync::mpsc;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::Barrier;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
use std::sync::Condvar;
|
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
use winapi::shared::minwindef::{LOWORD, HIWORD, DWORD, WPARAM, LPARAM, INT, UINT, LRESULT, MAX_PATH};
|
use winapi::ctypes::c_int;
|
||||||
|
use winapi::shared::minwindef::{
|
||||||
|
BOOL,
|
||||||
|
DWORD,
|
||||||
|
HIWORD,
|
||||||
|
INT,
|
||||||
|
LOWORD,
|
||||||
|
LPARAM,
|
||||||
|
LRESULT,
|
||||||
|
MAX_PATH,
|
||||||
|
UINT,
|
||||||
|
WPARAM,
|
||||||
|
};
|
||||||
use winapi::shared::windef::{HWND, POINT, RECT};
|
use winapi::shared::windef::{HWND, POINT, RECT};
|
||||||
use winapi::shared::windowsx;
|
use winapi::shared::windowsx;
|
||||||
use winapi::um::{winuser, shellapi, processthreadsapi};
|
use winapi::um::{winuser, shellapi, processthreadsapi};
|
||||||
use winapi::um::winnt::{LONG, SHORT};
|
use winapi::um::winnt::{LONG, LPCSTR, SHORT};
|
||||||
|
|
||||||
use events::DeviceEvent;
|
use {
|
||||||
|
ControlFlow,
|
||||||
|
CursorState,
|
||||||
|
Event,
|
||||||
|
EventsLoopClosed,
|
||||||
|
KeyboardInput,
|
||||||
|
LogicalPosition,
|
||||||
|
LogicalSize,
|
||||||
|
PhysicalSize,
|
||||||
|
WindowEvent,
|
||||||
|
WindowId as SuperWindowId,
|
||||||
|
};
|
||||||
|
use events::{DeviceEvent, Touch, TouchPhase};
|
||||||
use platform::platform::{event, Cursor, WindowId, DEVICE_ID, wrap_device_id, util};
|
use platform::platform::{event, Cursor, WindowId, DEVICE_ID, wrap_device_id, util};
|
||||||
|
use platform::platform::dpi::{
|
||||||
|
become_dpi_aware,
|
||||||
|
dpi_to_scale_factor,
|
||||||
|
enable_non_client_dpi_scaling,
|
||||||
|
get_hwnd_scale_factor,
|
||||||
|
};
|
||||||
use platform::platform::event::{handle_extended_keys, process_key_params, vkey_to_winit_vkey};
|
use platform::platform::event::{handle_extended_keys, process_key_params, vkey_to_winit_vkey};
|
||||||
use platform::platform::raw_input::*;
|
use platform::platform::raw_input::{get_raw_input_data, get_raw_mouse_button_state};
|
||||||
use platform::platform::window::adjust_size;
|
use platform::platform::window::adjust_size;
|
||||||
|
|
||||||
use ControlFlow;
|
|
||||||
use CursorState;
|
|
||||||
use Event;
|
|
||||||
use EventsLoopClosed;
|
|
||||||
use KeyboardInput;
|
|
||||||
use WindowAttributes;
|
|
||||||
use WindowEvent;
|
|
||||||
use WindowId as SuperWindowId;
|
|
||||||
use events::{Touch, TouchPhase};
|
|
||||||
|
|
||||||
/// Contains saved window info for switching between fullscreen
|
/// Contains saved window info for switching between fullscreen
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SavedWindowInfo {
|
pub struct SavedWindowInfo {
|
||||||
|
@ -57,6 +71,11 @@ pub struct SavedWindowInfo {
|
||||||
pub ex_style: LONG,
|
pub ex_style: LONG,
|
||||||
/// Window position and size
|
/// Window position and size
|
||||||
pub rect: RECT,
|
pub rect: RECT,
|
||||||
|
// Since a window can be fullscreened to a different monitor, a DPI change can be triggered. This could result in
|
||||||
|
// the window being automitcally resized to smaller/larger than it was supposed to be restored to, so we thus must
|
||||||
|
// check if the post-fullscreen DPI matches the pre-fullscreen DPI.
|
||||||
|
pub is_fullscreen: bool,
|
||||||
|
pub dpi_factor: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains information about states and the window that the callback is going to use.
|
/// Contains information about states and the window that the callback is going to use.
|
||||||
|
@ -67,11 +86,28 @@ pub struct WindowState {
|
||||||
/// Cursor state to set at the next `WM_SETCURSOR` event received.
|
/// Cursor state to set at the next `WM_SETCURSOR` event received.
|
||||||
pub cursor_state: CursorState,
|
pub cursor_state: CursorState,
|
||||||
/// Used by `WM_GETMINMAXINFO`.
|
/// Used by `WM_GETMINMAXINFO`.
|
||||||
pub attributes: WindowAttributes,
|
pub max_size: Option<PhysicalSize>,
|
||||||
|
pub min_size: Option<PhysicalSize>,
|
||||||
/// Will contain `true` if the mouse is hovering the window.
|
/// Will contain `true` if the mouse is hovering the window.
|
||||||
pub mouse_in_window: bool,
|
pub mouse_in_window: bool,
|
||||||
/// Saved window info for fullscreen restored
|
/// Saved window info for fullscreen restored
|
||||||
pub saved_window_info: Option<SavedWindowInfo>,
|
pub saved_window_info: Option<SavedWindowInfo>,
|
||||||
|
// This is different from the value in `SavedWindowInfo`! That one represents the DPI saved upon entering
|
||||||
|
// fullscreen. This will always be the most recent DPI for the window.
|
||||||
|
pub dpi_factor: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowState {
|
||||||
|
pub fn update_min_max(&mut self, old_dpi_factor: f64, new_dpi_factor: f64) {
|
||||||
|
let scale_factor = new_dpi_factor / old_dpi_factor;
|
||||||
|
let dpi_adjuster = |mut physical_size: PhysicalSize| -> PhysicalSize {
|
||||||
|
physical_size.width *= scale_factor;
|
||||||
|
physical_size.height *= scale_factor;
|
||||||
|
physical_size
|
||||||
|
};
|
||||||
|
self.max_size = self.max_size.map(&dpi_adjuster);
|
||||||
|
self.min_size = self.min_size.map(&dpi_adjuster);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dummy object that allows inserting a window's state.
|
/// Dummy object that allows inserting a window's state.
|
||||||
|
@ -98,11 +134,17 @@ pub struct EventsLoop {
|
||||||
// Variable that contains the block state of the win32 event loop thread during a WM_SIZE event.
|
// Variable that contains the block state of the win32 event loop thread during a WM_SIZE event.
|
||||||
// The mutex's value is `true` when it's blocked, and should be set to false when it's done
|
// The mutex's value is `true` when it's blocked, and should be set to false when it's done
|
||||||
// blocking. That's done by the parent thread when it receives a Resized event.
|
// blocking. That's done by the parent thread when it receives a Resized event.
|
||||||
win32_block_loop: Arc<(Mutex<bool>, Condvar)>
|
win32_block_loop: Arc<(Mutex<bool>, Condvar)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventsLoop {
|
impl EventsLoop {
|
||||||
pub fn new() -> EventsLoop {
|
pub fn new() -> EventsLoop {
|
||||||
|
Self::with_dpi_awareness(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_dpi_awareness(dpi_aware: bool) -> EventsLoop {
|
||||||
|
become_dpi_aware(dpi_aware);
|
||||||
|
|
||||||
// The main events transfer channel.
|
// The main events transfer channel.
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
let win32_block_loop = Arc::new((Mutex::new(false), Condvar::new()));
|
let win32_block_loop = Arc::new((Mutex::new(false), Condvar::new()));
|
||||||
|
@ -171,7 +213,7 @@ impl EventsLoop {
|
||||||
EventsLoop {
|
EventsLoop {
|
||||||
thread_id,
|
thread_id,
|
||||||
receiver: rx,
|
receiver: rx,
|
||||||
win32_block_loop
|
win32_block_loop,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,23 +331,22 @@ impl EventsLoopProxy {
|
||||||
where
|
where
|
||||||
F: FnMut(Inserter) + Send + 'static,
|
F: FnMut(Inserter) + Send + 'static,
|
||||||
{
|
{
|
||||||
unsafe {
|
|
||||||
// We are using double-boxing here because it make casting back much easier
|
// We are using double-boxing here because it make casting back much easier
|
||||||
let boxed = Box::new(function) as Box<FnMut(_)>;
|
let double_box = Box::new(Box::new(function) as Box<FnMut(_)>);
|
||||||
let boxed2 = Box::new(boxed);
|
let raw = Box::into_raw(double_box);
|
||||||
let raw = Box::into_raw(boxed2);
|
|
||||||
|
|
||||||
let res = winuser::PostThreadMessageA(
|
let res = unsafe {
|
||||||
|
winuser::PostThreadMessageA(
|
||||||
self.thread_id,
|
self.thread_id,
|
||||||
*EXEC_MSG_ID,
|
*EXEC_MSG_ID,
|
||||||
raw as *mut () as usize as WPARAM,
|
raw as *mut () as usize as WPARAM,
|
||||||
0,
|
0,
|
||||||
);
|
)
|
||||||
// PostThreadMessage can only fail if the thread ID is invalid (which shouldn't happen
|
};
|
||||||
// as the events loop is still alive) or if the queue is full.
|
// PostThreadMessage can only fail if the thread ID is invalid (which shouldn't happen as
|
||||||
|
// the events loop is still alive) or if the queue is full.
|
||||||
assert!(res != 0, "PostThreadMessage failed; is the messages queue full?");
|
assert!(res != 0, "PostThreadMessage failed; is the messages queue full?");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
@ -313,7 +354,7 @@ lazy_static! {
|
||||||
// WPARAM and LPARAM are unused.
|
// WPARAM and LPARAM are unused.
|
||||||
static ref WAKEUP_MSG_ID: u32 = {
|
static ref WAKEUP_MSG_ID: u32 = {
|
||||||
unsafe {
|
unsafe {
|
||||||
winuser::RegisterWindowMessageA("Winit::WakeupMsg\0".as_ptr() as *const i8)
|
winuser::RegisterWindowMessageA("Winit::WakeupMsg\0".as_ptr() as LPCSTR)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Message sent when we want to execute a closure in the thread.
|
// Message sent when we want to execute a closure in the thread.
|
||||||
|
@ -321,14 +362,21 @@ lazy_static! {
|
||||||
// and LPARAM is unused.
|
// and LPARAM is unused.
|
||||||
static ref EXEC_MSG_ID: u32 = {
|
static ref EXEC_MSG_ID: u32 = {
|
||||||
unsafe {
|
unsafe {
|
||||||
winuser::RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr() as *const i8)
|
winuser::RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr() as LPCSTR)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Message sent by a `Window` when it wants to be destroyed by the main thread.
|
// Message sent by a `Window` when it wants to be destroyed by the main thread.
|
||||||
// WPARAM and LPARAM are unused.
|
// WPARAM and LPARAM are unused.
|
||||||
pub static ref DESTROY_MSG_ID: u32 = {
|
pub static ref DESTROY_MSG_ID: u32 = {
|
||||||
unsafe {
|
unsafe {
|
||||||
winuser::RegisterWindowMessageA("Winit::DestroyMsg\0".as_ptr() as *const i8)
|
winuser::RegisterWindowMessageA("Winit::DestroyMsg\0".as_ptr() as LPCSTR)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Message sent by a `Window` after creation if it has a DPI != 96.
|
||||||
|
// WPARAM is the the DPI (u32). LOWORD of LPARAM is width, and HIWORD is height.
|
||||||
|
pub static ref INITIAL_DPI_MSG_ID: u32 = {
|
||||||
|
unsafe {
|
||||||
|
winuser::RegisterWindowMessageA("Winit::InitialDpiMsg\0".as_ptr() as LPCSTR)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -385,11 +433,18 @@ unsafe fn release_mouse() {
|
||||||
//
|
//
|
||||||
// Returning 0 tells the Win32 API that the message has been processed.
|
// Returning 0 tells the Win32 API that the message has been processed.
|
||||||
// FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary
|
// FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary
|
||||||
pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
pub unsafe extern "system" fn callback(
|
||||||
wparam: WPARAM, lparam: LPARAM)
|
window: HWND,
|
||||||
-> LRESULT
|
msg: UINT,
|
||||||
{
|
wparam: WPARAM,
|
||||||
|
lparam: LPARAM,
|
||||||
|
) -> LRESULT {
|
||||||
match msg {
|
match msg {
|
||||||
|
winuser::WM_NCCREATE => {
|
||||||
|
enable_non_client_dpi_scaling(window);
|
||||||
|
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||||
|
},
|
||||||
|
|
||||||
winuser::WM_CLOSE => {
|
winuser::WM_CLOSE => {
|
||||||
use events::WindowEvent::CloseRequested;
|
use events::WindowEvent::CloseRequested;
|
||||||
send_event(Event::WindowEvent {
|
send_event(Event::WindowEvent {
|
||||||
|
@ -427,9 +482,14 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||||
|
|
||||||
let windowpos = lparam as *const winuser::WINDOWPOS;
|
let windowpos = lparam as *const winuser::WINDOWPOS;
|
||||||
if (*windowpos).flags & winuser::SWP_NOMOVE != winuser::SWP_NOMOVE {
|
if (*windowpos).flags & winuser::SWP_NOMOVE != winuser::SWP_NOMOVE {
|
||||||
|
let dpi_factor = get_hwnd_scale_factor(window);
|
||||||
|
let logical_position = LogicalPosition::from_physical(
|
||||||
|
((*windowpos).x, (*windowpos).y),
|
||||||
|
dpi_factor,
|
||||||
|
);
|
||||||
send_event(Event::WindowEvent {
|
send_event(Event::WindowEvent {
|
||||||
window_id: SuperWindowId(WindowId(window)),
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
event: Moved((*windowpos).x, (*windowpos).y),
|
event: Moved(logical_position),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,9 +508,11 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||||
let mut context_stash = context_stash.borrow_mut();
|
let mut context_stash = context_stash.borrow_mut();
|
||||||
let cstash = context_stash.as_mut().unwrap();
|
let cstash = context_stash.as_mut().unwrap();
|
||||||
|
|
||||||
|
let dpi_factor = get_hwnd_scale_factor(window);
|
||||||
|
let logical_size = LogicalSize::from_physical((w, h), dpi_factor);
|
||||||
let event = Event::WindowEvent {
|
let event = Event::WindowEvent {
|
||||||
window_id: SuperWindowId(WindowId(window)),
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
event: Resized(w, h),
|
event: Resized(logical_size),
|
||||||
};
|
};
|
||||||
|
|
||||||
// If this window has been inserted into the window map, the resize event happened
|
// If this window has been inserted into the window map, the resize event happened
|
||||||
|
@ -528,10 +590,12 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||||
|
|
||||||
let x = windowsx::GET_X_LPARAM(lparam) as f64;
|
let x = windowsx::GET_X_LPARAM(lparam) as f64;
|
||||||
let y = windowsx::GET_Y_LPARAM(lparam) as f64;
|
let y = windowsx::GET_Y_LPARAM(lparam) as f64;
|
||||||
|
let dpi_factor = get_hwnd_scale_factor(window);
|
||||||
|
let position = LogicalPosition::from_physical((x, y), dpi_factor);
|
||||||
|
|
||||||
send_event(Event::WindowEvent {
|
send_event(Event::WindowEvent {
|
||||||
window_id: SuperWindowId(WindowId(window)),
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
event: CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event::get_key_mods() },
|
event: CursorMoved { device_id: DEVICE_ID, position, modifiers: event::get_key_mods() },
|
||||||
});
|
});
|
||||||
|
|
||||||
0
|
0
|
||||||
|
@ -869,10 +933,17 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||||
let mut inputs = Vec::with_capacity( pcount );
|
let mut inputs = Vec::with_capacity( pcount );
|
||||||
inputs.set_len( pcount );
|
inputs.set_len( pcount );
|
||||||
let htouch = lparam as winuser::HTOUCHINPUT;
|
let htouch = lparam as winuser::HTOUCHINPUT;
|
||||||
if winuser::GetTouchInputInfo( htouch, pcount as UINT,
|
if winuser::GetTouchInputInfo(
|
||||||
|
htouch,
|
||||||
|
pcount as UINT,
|
||||||
inputs.as_mut_ptr(),
|
inputs.as_mut_ptr(),
|
||||||
mem::size_of::<winuser::TOUCHINPUT>() as INT ) > 0 {
|
mem::size_of::<winuser::TOUCHINPUT>() as INT,
|
||||||
|
) > 0 {
|
||||||
|
let dpi_factor = get_hwnd_scale_factor(window);
|
||||||
for input in &inputs {
|
for input in &inputs {
|
||||||
|
let x = (input.x as f64) / 100f64;
|
||||||
|
let y = (input.y as f64) / 100f64;
|
||||||
|
let location = LogicalPosition::from_physical((x, y), dpi_factor);
|
||||||
send_event( Event::WindowEvent {
|
send_event( Event::WindowEvent {
|
||||||
window_id: SuperWindowId(WindowId(window)),
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
event: WindowEvent::Touch(Touch {
|
event: WindowEvent::Touch(Touch {
|
||||||
|
@ -886,8 +957,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
location: ((input.x as f64) / 100f64,
|
location,
|
||||||
(input.y as f64) / 100f64),
|
|
||||||
id: input.dwID as u64,
|
id: input.dwID as u64,
|
||||||
device_id: DEVICE_ID,
|
device_id: DEVICE_ID,
|
||||||
})
|
})
|
||||||
|
@ -907,11 +977,14 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||||
|
|
||||||
let x = windowsx::GET_X_LPARAM(lparam) as f64;
|
let x = windowsx::GET_X_LPARAM(lparam) as f64;
|
||||||
let y = windowsx::GET_Y_LPARAM(lparam) as f64;
|
let y = windowsx::GET_Y_LPARAM(lparam) as f64;
|
||||||
|
let dpi_factor = get_hwnd_scale_factor(window);
|
||||||
|
let position = LogicalPosition::from_physical((x, y), dpi_factor);
|
||||||
|
|
||||||
send_event(Event::WindowEvent {
|
send_event(Event::WindowEvent {
|
||||||
window_id: SuperWindowId(WindowId(window)),
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
event: CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event::get_key_mods() },
|
event: CursorMoved { device_id: DEVICE_ID, position, modifiers: event::get_key_mods() },
|
||||||
});
|
});
|
||||||
|
|
||||||
0
|
0
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -985,18 +1058,15 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||||
if let Some(wstash) = cstash.windows.get(&window) {
|
if let Some(wstash) = cstash.windows.get(&window) {
|
||||||
let window_state = wstash.lock().unwrap();
|
let window_state = wstash.lock().unwrap();
|
||||||
|
|
||||||
if window_state.attributes.min_dimensions.is_some() ||
|
if window_state.min_size.is_some() || window_state.max_size.is_some() {
|
||||||
window_state.attributes.max_dimensions.is_some() {
|
|
||||||
|
|
||||||
let style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD;
|
let style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD;
|
||||||
let ex_style = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD;
|
let ex_style = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD;
|
||||||
|
if let Some(min_size) = window_state.min_size {
|
||||||
if let Some(min_dimensions) = window_state.attributes.min_dimensions {
|
let (width, height) = adjust_size(min_size, style, ex_style);
|
||||||
let (width, height) = adjust_size(min_dimensions, style, ex_style);
|
|
||||||
(*mmi).ptMinTrackSize = POINT { x: width as i32, y: height as i32 };
|
(*mmi).ptMinTrackSize = POINT { x: width as i32, y: height as i32 };
|
||||||
}
|
}
|
||||||
if let Some(max_dimensions) = window_state.attributes.max_dimensions {
|
if let Some(max_size) = window_state.max_size {
|
||||||
let (width, height) = adjust_size(max_dimensions, style, ex_style);
|
let (width, height) = adjust_size(max_size, style, ex_style);
|
||||||
(*mmi).ptMaxTrackSize = POINT { x: width as i32, y: height as i32 };
|
(*mmi).ptMaxTrackSize = POINT { x: width as i32, y: height as i32 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1007,10 +1077,115 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||||
0
|
0
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Only sent on Windows 8.1 or newer. On Windows 7 and older user has to log out to change
|
||||||
|
// DPI, therefore all applications are closed while DPI is changing.
|
||||||
|
winuser::WM_DPICHANGED => {
|
||||||
|
use events::WindowEvent::HiDpiFactorChanged;
|
||||||
|
|
||||||
|
// This message actually provides two DPI values - x and y. However MSDN says that
|
||||||
|
// "you only need to use either the X-axis or the Y-axis value when scaling your
|
||||||
|
// application since they are the same".
|
||||||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/dn312083(v=vs.85).aspx
|
||||||
|
let new_dpi_x = u32::from(LOWORD(wparam as DWORD));
|
||||||
|
let new_dpi_factor = dpi_to_scale_factor(new_dpi_x);
|
||||||
|
|
||||||
|
let suppress_resize = CONTEXT_STASH.with(|context_stash| {
|
||||||
|
context_stash
|
||||||
|
.borrow()
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|cstash| cstash.windows.get(&window))
|
||||||
|
.map(|window_state_mutex| {
|
||||||
|
let mut window_state = window_state_mutex.lock().unwrap();
|
||||||
|
let suppress_resize = window_state.saved_window_info
|
||||||
|
.as_mut()
|
||||||
|
.map(|saved_window_info| {
|
||||||
|
let dpi_changed = if !saved_window_info.is_fullscreen {
|
||||||
|
saved_window_info.dpi_factor.take() != Some(new_dpi_factor)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
!dpi_changed || saved_window_info.is_fullscreen
|
||||||
|
})
|
||||||
|
.unwrap_or(false);
|
||||||
|
// Now we adjust the min/max dimensions for the new DPI.
|
||||||
|
if !suppress_resize {
|
||||||
|
let old_dpi_factor = window_state.dpi_factor;
|
||||||
|
window_state.update_min_max(old_dpi_factor, new_dpi_factor);
|
||||||
|
}
|
||||||
|
window_state.dpi_factor = new_dpi_factor;
|
||||||
|
suppress_resize
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
// This prevents us from re-applying DPI adjustment to the restored size after exiting
|
||||||
|
// fullscreen (the restored size is already DPI adjusted).
|
||||||
|
if !suppress_resize {
|
||||||
|
// Resize window to the size suggested by Windows.
|
||||||
|
let rect = &*(lparam as *const RECT);
|
||||||
|
winuser::SetWindowPos(
|
||||||
|
window,
|
||||||
|
ptr::null_mut(),
|
||||||
|
rect.left,
|
||||||
|
rect.top,
|
||||||
|
rect.right - rect.left,
|
||||||
|
rect.bottom - rect.top,
|
||||||
|
winuser::SWP_NOZORDER | winuser::SWP_NOACTIVATE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: HiDpiFactorChanged(new_dpi_factor),
|
||||||
|
});
|
||||||
|
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
if msg == *DESTROY_MSG_ID {
|
if msg == *DESTROY_MSG_ID {
|
||||||
winuser::DestroyWindow(window);
|
winuser::DestroyWindow(window);
|
||||||
0
|
0
|
||||||
|
} else if msg == *INITIAL_DPI_MSG_ID {
|
||||||
|
use events::WindowEvent::HiDpiFactorChanged;
|
||||||
|
let scale_factor = dpi_to_scale_factor(wparam as u32);
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: HiDpiFactorChanged(scale_factor),
|
||||||
|
});
|
||||||
|
// Automatically resize for actual DPI
|
||||||
|
let width = LOWORD(lparam as DWORD) as u32;
|
||||||
|
let height = HIWORD(lparam as DWORD) as u32;
|
||||||
|
let (adjusted_width, adjusted_height): (u32, u32) = PhysicalSize::from_logical(
|
||||||
|
(width, height),
|
||||||
|
scale_factor,
|
||||||
|
).into();
|
||||||
|
// We're not done yet! `SetWindowPos` needs the window size, not the client area size.
|
||||||
|
let mut rect = RECT {
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
bottom: adjusted_height as LONG,
|
||||||
|
right: adjusted_width as LONG,
|
||||||
|
};
|
||||||
|
let dw_style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD;
|
||||||
|
let b_menu = !winuser::GetMenu(window).is_null() as BOOL;
|
||||||
|
let dw_style_ex = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD;
|
||||||
|
winuser::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
|
||||||
|
let outer_x = (rect.right - rect.left).abs() as c_int;
|
||||||
|
let outer_y = (rect.top - rect.bottom).abs() as c_int;
|
||||||
|
winuser::SetWindowPos(
|
||||||
|
window,
|
||||||
|
ptr::null_mut(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
outer_x,
|
||||||
|
outer_y,
|
||||||
|
winuser::SWP_NOMOVE
|
||||||
|
| winuser::SWP_NOREPOSITION
|
||||||
|
| winuser::SWP_NOZORDER
|
||||||
|
| winuser::SWP_NOACTIVATE,
|
||||||
|
);
|
||||||
|
0
|
||||||
} else {
|
} else {
|
||||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ pub struct WindowId(HWND);
|
||||||
unsafe impl Send for WindowId {}
|
unsafe impl Send for WindowId {}
|
||||||
unsafe impl Sync for WindowId {}
|
unsafe impl Sync for WindowId {}
|
||||||
|
|
||||||
|
mod dpi;
|
||||||
mod event;
|
mod event;
|
||||||
mod events_loop;
|
mod events_loop;
|
||||||
mod icon;
|
mod icon;
|
||||||
|
|
|
@ -1,38 +1,31 @@
|
||||||
use winapi::ctypes::wchar_t;
|
use winapi::shared::minwindef::{BOOL, DWORD, LPARAM, TRUE};
|
||||||
use winapi::shared::minwindef::{DWORD, LPARAM, BOOL, TRUE};
|
use winapi::shared::windef::{HDC, HMONITOR, HWND, LPRECT, POINT};
|
||||||
use winapi::shared::windef::{HMONITOR, HDC, LPRECT, HWND};
|
|
||||||
use winapi::um::winuser;
|
use winapi::um::winuser;
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::{mem, ptr};
|
use std::{mem, ptr};
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use {PhysicalPosition, PhysicalSize};
|
||||||
use super::{EventsLoop, util};
|
use super::{EventsLoop, util};
|
||||||
|
use platform::platform::dpi::{dpi_to_scale_factor, get_monitor_dpi};
|
||||||
|
|
||||||
/// Win32 implementation of the main `MonitorId` object.
|
/// Win32 implementation of the main `MonitorId` object.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MonitorId {
|
pub struct MonitorId {
|
||||||
/// The system name of the adapter.
|
|
||||||
adapter_name: [wchar_t; 32],
|
|
||||||
|
|
||||||
/// Monitor handle.
|
/// Monitor handle.
|
||||||
hmonitor: HMonitor,
|
hmonitor: HMonitor,
|
||||||
|
|
||||||
/// The system name of the monitor.
|
/// The system name of the monitor.
|
||||||
monitor_name: String,
|
monitor_name: String,
|
||||||
|
|
||||||
/// True if this is the primary monitor.
|
/// True if this is the primary monitor.
|
||||||
primary: bool,
|
primary: bool,
|
||||||
|
|
||||||
/// The position of the monitor in pixels on the desktop.
|
/// The position of the monitor in pixels on the desktop.
|
||||||
///
|
///
|
||||||
/// A window that is positioned at these coordinates will overlap the monitor.
|
/// A window that is positioned at these coordinates will overlap the monitor.
|
||||||
position: (i32, i32),
|
position: (i32, i32),
|
||||||
|
|
||||||
/// The current resolution in pixels on the monitor.
|
/// The current resolution in pixels on the monitor.
|
||||||
dimensions: (u32, u32),
|
dimensions: (u32, u32),
|
||||||
|
/// DPI scale factor.
|
||||||
/// DPI scaling factor.
|
hidpi_factor: f64,
|
||||||
hidpi_factor: f32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send is not implemented for HMONITOR, we have to wrap it and implement it manually.
|
// Send is not implemented for HMONITOR, we have to wrap it and implement it manually.
|
||||||
|
@ -44,122 +37,109 @@ struct HMonitor(HMONITOR);
|
||||||
|
|
||||||
unsafe impl Send for HMonitor {}
|
unsafe impl Send for HMonitor {}
|
||||||
|
|
||||||
unsafe extern "system" fn monitor_enum_proc(hmonitor: HMONITOR, _: HDC, place: LPRECT, data: LPARAM) -> BOOL {
|
unsafe extern "system" fn monitor_enum_proc(
|
||||||
|
hmonitor: HMONITOR,
|
||||||
|
_hdc: HDC,
|
||||||
|
_place: LPRECT,
|
||||||
|
data: LPARAM,
|
||||||
|
) -> BOOL {
|
||||||
let monitors = data as *mut VecDeque<MonitorId>;
|
let monitors = data as *mut VecDeque<MonitorId>;
|
||||||
|
(*monitors).push_back(MonitorId::from_hmonitor(hmonitor));
|
||||||
let place = *place;
|
TRUE // continue enumeration
|
||||||
let position = (place.left as i32, place.top as i32);
|
|
||||||
let dimensions = ((place.right - place.left) as u32, (place.bottom - place.top) as u32);
|
|
||||||
|
|
||||||
let mut monitor_info: winuser::MONITORINFOEXW = mem::zeroed();
|
|
||||||
monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
|
|
||||||
if winuser::GetMonitorInfoW(hmonitor, &mut monitor_info as *mut winuser::MONITORINFOEXW as *mut winuser::MONITORINFO) == 0 {
|
|
||||||
// Some error occurred, just skip this monitor and go on.
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*monitors).push_back(MonitorId {
|
|
||||||
adapter_name: monitor_info.szDevice,
|
|
||||||
hmonitor: HMonitor(hmonitor),
|
|
||||||
monitor_name: util::wchar_to_string(&monitor_info.szDevice),
|
|
||||||
primary: monitor_info.dwFlags & winuser::MONITORINFOF_PRIMARY != 0,
|
|
||||||
position,
|
|
||||||
dimensions,
|
|
||||||
hidpi_factor: 1.0,
|
|
||||||
});
|
|
||||||
|
|
||||||
// TRUE means continue enumeration.
|
|
||||||
TRUE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventsLoop {
|
impl EventsLoop {
|
||||||
|
// TODO: Investigate opportunities for caching
|
||||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||||
|
let mut monitors: VecDeque<MonitorId> = VecDeque::new();
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut result: VecDeque<MonitorId> = VecDeque::new();
|
winuser::EnumDisplayMonitors(
|
||||||
winuser::EnumDisplayMonitors(ptr::null_mut(), ptr::null_mut(), Some(monitor_enum_proc), &mut result as *mut _ as LPARAM);
|
ptr::null_mut(),
|
||||||
result
|
ptr::null_mut(),
|
||||||
}
|
Some(monitor_enum_proc),
|
||||||
}
|
&mut monitors as *mut _ as LPARAM,
|
||||||
|
|
||||||
pub fn get_current_monitor(handle: HWND) -> MonitorId {
|
|
||||||
unsafe {
|
|
||||||
let mut monitor_info: winuser::MONITORINFOEXW = mem::zeroed();
|
|
||||||
monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
|
|
||||||
|
|
||||||
let hmonitor = winuser::MonitorFromWindow(handle, winuser::MONITOR_DEFAULTTONEAREST);
|
|
||||||
|
|
||||||
winuser::GetMonitorInfoW(
|
|
||||||
hmonitor,
|
|
||||||
&mut monitor_info as *mut winuser::MONITORINFOEXW as *mut winuser::MONITORINFO,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let place = monitor_info.rcMonitor;
|
|
||||||
let position = (place.left as i32, place.top as i32);
|
|
||||||
let dimensions = (
|
|
||||||
(place.right - place.left) as u32,
|
|
||||||
(place.bottom - place.top) as u32,
|
|
||||||
);
|
|
||||||
|
|
||||||
MonitorId {
|
|
||||||
adapter_name: monitor_info.szDevice,
|
|
||||||
hmonitor: super::monitor::HMonitor(hmonitor),
|
|
||||||
monitor_name: util::wchar_to_string(&monitor_info.szDevice),
|
|
||||||
primary: monitor_info.dwFlags & winuser::MONITORINFOF_PRIMARY != 0,
|
|
||||||
position,
|
|
||||||
dimensions,
|
|
||||||
hidpi_factor: 1.0,
|
|
||||||
}
|
}
|
||||||
|
monitors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_current_monitor(hwnd: HWND) -> MonitorId {
|
||||||
|
let hmonitor = unsafe {
|
||||||
|
winuser::MonitorFromWindow(hwnd, winuser::MONITOR_DEFAULTTONEAREST)
|
||||||
|
};
|
||||||
|
MonitorId::from_hmonitor(hmonitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||||
// we simply get all available monitors and return the one with the `MONITORINFOF_PRIMARY` flag
|
const ORIGIN: POINT = POINT { x: 0, y: 0 };
|
||||||
// TODO: it is possible to query the win32 API for the primary monitor, this should be done
|
let hmonitor = unsafe {
|
||||||
// instead
|
winuser::MonitorFromPoint(ORIGIN, winuser::MONITOR_DEFAULTTOPRIMARY)
|
||||||
for monitor in self.get_available_monitors().into_iter() {
|
};
|
||||||
if monitor.primary {
|
MonitorId::from_hmonitor(hmonitor)
|
||||||
return monitor;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
panic!("Failed to find the primary monitor")
|
fn get_monitor_info(hmonitor: HMONITOR) -> Result<winuser::MONITORINFOEXW, util::WinError> {
|
||||||
|
let mut monitor_info: winuser::MONITORINFOEXW = unsafe { mem::uninitialized() };
|
||||||
|
monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
|
||||||
|
let status = unsafe {
|
||||||
|
winuser::GetMonitorInfoW(
|
||||||
|
hmonitor,
|
||||||
|
&mut monitor_info as *mut winuser::MONITORINFOEXW as *mut winuser::MONITORINFO,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if status == 0 {
|
||||||
|
Err(util::WinError::from_last_error())
|
||||||
|
} else {
|
||||||
|
Ok(monitor_info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MonitorId {
|
impl MonitorId {
|
||||||
/// See the docs if the crate root file.
|
pub(crate) fn from_hmonitor(hmonitor: HMONITOR) -> Self {
|
||||||
|
let monitor_info = get_monitor_info(hmonitor).expect("`GetMonitorInfoW` failed");
|
||||||
|
let place = monitor_info.rcMonitor;
|
||||||
|
let dimensions = (
|
||||||
|
(place.right - place.left) as u32,
|
||||||
|
(place.bottom - place.top) as u32,
|
||||||
|
);
|
||||||
|
MonitorId {
|
||||||
|
hmonitor: HMonitor(hmonitor),
|
||||||
|
monitor_name: util::wchar_ptr_to_string(monitor_info.szDevice.as_ptr()),
|
||||||
|
primary: util::has_flag(monitor_info.dwFlags, winuser::MONITORINFOF_PRIMARY),
|
||||||
|
position: (place.left as i32, place.top as i32),
|
||||||
|
dimensions,
|
||||||
|
hidpi_factor: dpi_to_scale_factor(get_monitor_dpi(hmonitor).unwrap_or(96)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_name(&self) -> Option<String> {
|
pub fn get_name(&self) -> Option<String> {
|
||||||
Some(self.monitor_name.clone())
|
Some(self.monitor_name.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See the docs of the crate root file.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_native_identifier(&self) -> String {
|
pub fn get_native_identifier(&self) -> String {
|
||||||
self.monitor_name.clone()
|
self.monitor_name.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See the docs of the crate root file.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_hmonitor(&self) -> HMONITOR {
|
pub fn get_hmonitor(&self) -> HMONITOR {
|
||||||
self.hmonitor.0
|
self.hmonitor.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See the docs of the crate root file.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||||
// TODO: retrieve the dimensions every time this is called
|
self.dimensions.into()
|
||||||
self.dimensions
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A window that is positioned at these coordinates will overlap the monitor.
|
|
||||||
#[inline]
|
|
||||||
pub fn get_position(&self) -> (i32, i32) {
|
|
||||||
self.position
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_hidpi_factor(&self) -> f32 {
|
pub fn get_position(&self) -> PhysicalPosition {
|
||||||
|
self.position.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
self.hidpi_factor
|
self.hidpi_factor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::{self, mem, ptr};
|
use std::{self, mem, ptr, slice};
|
||||||
use std::ops::BitAnd;
|
use std::ops::BitAnd;
|
||||||
|
|
||||||
use winapi::ctypes::wchar_t;
|
use winapi::ctypes::wchar_t;
|
||||||
use winapi::shared::minwindef::DWORD;
|
use winapi::shared::minwindef::DWORD;
|
||||||
use winapi::shared::windef::RECT;
|
use winapi::shared::windef::{HWND, RECT};
|
||||||
use winapi::um::errhandlingapi::GetLastError;
|
use winapi::um::errhandlingapi::GetLastError;
|
||||||
use winapi::um::winbase::{
|
use winapi::um::winbase::{
|
||||||
FormatMessageW,
|
FormatMessageW,
|
||||||
|
@ -19,6 +19,7 @@ use winapi::um::winnt::{
|
||||||
LANG_NEUTRAL,
|
LANG_NEUTRAL,
|
||||||
SUBLANG_DEFAULT,
|
SUBLANG_DEFAULT,
|
||||||
};
|
};
|
||||||
|
use winapi::um::winuser;
|
||||||
|
|
||||||
pub fn has_flag<T>(bitset: T, flag: T) -> bool
|
pub fn has_flag<T>(bitset: T, flag: T) -> bool
|
||||||
where T:
|
where T:
|
||||||
|
@ -28,9 +29,22 @@ where T:
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wchar_to_string(wchar: &[wchar_t]) -> String {
|
pub fn wchar_to_string(wchar: &[wchar_t]) -> String {
|
||||||
String::from_utf16_lossy(wchar)
|
String::from_utf16_lossy(wchar).to_string()
|
||||||
.trim_right_matches(0 as char)
|
}
|
||||||
.to_string()
|
|
||||||
|
pub fn wchar_ptr_to_string(wchar: *const wchar_t) -> String {
|
||||||
|
let len = unsafe { lstrlenW(wchar) } as usize;
|
||||||
|
let wchar_slice = unsafe { slice::from_raw_parts(wchar, len) };
|
||||||
|
wchar_to_string(wchar_slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_window_rect(hwnd: HWND) -> Option<RECT> {
|
||||||
|
let mut rect: RECT = unsafe { mem::uninitialized() };
|
||||||
|
if unsafe { winuser::GetWindowRect(hwnd, &mut rect) } != 0 {
|
||||||
|
Some(rect)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This won't be needed anymore if we just add a derive to winapi.
|
// This won't be needed anymore if we just add a derive to winapi.
|
||||||
|
|
|
@ -1,29 +1,34 @@
|
||||||
#![cfg(target_os = "windows")]
|
#![cfg(target_os = "windows")]
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::{Cell, RefCell};
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::{io, mem, ptr};
|
use std::{io, mem, ptr};
|
||||||
use std::os::raw;
|
|
||||||
use std::os::windows::ffi::OsStrExt;
|
use std::os::windows::ffi::OsStrExt;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
|
|
||||||
use winapi::shared::minwindef::{BOOL, DWORD, FALSE, TRUE, UINT};
|
use winapi::ctypes::c_int;
|
||||||
|
use winapi::shared::minwindef::{BOOL, DWORD, FALSE, LPARAM, TRUE, UINT, WORD, WPARAM};
|
||||||
use winapi::shared::windef::{HDC, HWND, LPPOINT, POINT, RECT};
|
use winapi::shared::windef::{HDC, HWND, LPPOINT, POINT, RECT};
|
||||||
use winapi::um::{combaseapi, dwmapi, libloaderapi, winuser};
|
use winapi::um::{combaseapi, dwmapi, libloaderapi, winuser};
|
||||||
use winapi::um::objbase::{COINIT_MULTITHREADED};
|
use winapi::um::objbase::{COINIT_MULTITHREADED};
|
||||||
use winapi::um::shobjidl_core::{CLSID_TaskbarList, ITaskbarList2};
|
use winapi::um::shobjidl_core::{CLSID_TaskbarList, ITaskbarList2};
|
||||||
use winapi::um::winnt::{LONG, LPCWSTR};
|
use winapi::um::winnt::{LONG, LPCWSTR};
|
||||||
|
|
||||||
use CreationError;
|
use {
|
||||||
use CursorState;
|
CreationError,
|
||||||
use Icon;
|
CursorState,
|
||||||
use MonitorId as RootMonitorId;
|
Icon,
|
||||||
use MouseCursor;
|
LogicalPosition,
|
||||||
use WindowAttributes;
|
LogicalSize,
|
||||||
|
MonitorId as RootMonitorId,
|
||||||
|
MouseCursor,
|
||||||
|
PhysicalSize,
|
||||||
|
WindowAttributes,
|
||||||
|
};
|
||||||
use platform::platform::{Cursor, EventsLoop, PlatformSpecificWindowBuilderAttributes, WindowId};
|
use platform::platform::{Cursor, EventsLoop, PlatformSpecificWindowBuilderAttributes, WindowId};
|
||||||
use platform::platform::events_loop::{self, DESTROY_MSG_ID};
|
use platform::platform::dpi::{BASE_DPI, dpi_to_scale_factor, get_window_dpi, get_window_scale_factor};
|
||||||
|
use platform::platform::events_loop::{self, DESTROY_MSG_ID, INITIAL_DPI_MSG_ID};
|
||||||
use platform::platform::icon::{self, IconType, WinIcon};
|
use platform::platform::icon::{self, IconType, WinIcon};
|
||||||
use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input;
|
use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input;
|
||||||
use platform::platform::util;
|
use platform::platform::util;
|
||||||
|
@ -33,6 +38,12 @@ pub struct Window {
|
||||||
/// Main handle for the window.
|
/// Main handle for the window.
|
||||||
window: WindowWrapper,
|
window: WindowWrapper,
|
||||||
|
|
||||||
|
decorations: Cell<bool>,
|
||||||
|
maximized: Cell<bool>,
|
||||||
|
resizable: Cell<bool>,
|
||||||
|
fullscreen: RefCell<Option<::MonitorId>>,
|
||||||
|
always_on_top: Cell<bool>,
|
||||||
|
|
||||||
/// The current window state.
|
/// The current window state.
|
||||||
window_state: Arc<Mutex<events_loop::WindowState>>,
|
window_state: Arc<Mutex<events_loop::WindowState>>,
|
||||||
|
|
||||||
|
@ -56,19 +67,16 @@ unsafe impl Sync for Window {}
|
||||||
// and it added fifty pixels to the top.
|
// and it added fifty pixels to the top.
|
||||||
// From this we can perform the reverse calculation: Instead of expanding the rectangle, we shrink it.
|
// From this we can perform the reverse calculation: Instead of expanding the rectangle, we shrink it.
|
||||||
unsafe fn unjust_window_rect(prc: &mut RECT, style: DWORD, ex_style: DWORD) -> BOOL {
|
unsafe fn unjust_window_rect(prc: &mut RECT, style: DWORD, ex_style: DWORD) -> BOOL {
|
||||||
let mut rc: RECT = mem::zeroed();
|
let mut rc: RECT = mem::uninitialized();
|
||||||
|
|
||||||
winuser::SetRectEmpty(&mut rc);
|
winuser::SetRectEmpty(&mut rc);
|
||||||
|
let status = winuser::AdjustWindowRectEx(&mut rc, style, 0, ex_style);
|
||||||
let frc = winuser::AdjustWindowRectEx(&mut rc, style, 0, ex_style);
|
if status != 0 {
|
||||||
if frc != 0 {
|
|
||||||
prc.left -= rc.left;
|
prc.left -= rc.left;
|
||||||
prc.top -= rc.top;
|
prc.top -= rc.top;
|
||||||
prc.right -= rc.right;
|
prc.right -= rc.right;
|
||||||
prc.bottom -= rc.bottom;
|
prc.bottom -= rc.bottom;
|
||||||
}
|
}
|
||||||
|
status
|
||||||
frc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
|
@ -78,16 +86,13 @@ impl Window {
|
||||||
pl_attr: PlatformSpecificWindowBuilderAttributes,
|
pl_attr: PlatformSpecificWindowBuilderAttributes,
|
||||||
) -> Result<Window, CreationError> {
|
) -> Result<Window, CreationError> {
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
|
|
||||||
let proxy = events_loop.create_proxy();
|
let proxy = events_loop.create_proxy();
|
||||||
|
|
||||||
events_loop.execute_in_thread(move |inserter| {
|
events_loop.execute_in_thread(move |inserter| {
|
||||||
// We dispatch an `init` function because of code style.
|
// We dispatch an `init` function because of code style.
|
||||||
// First person to remove the need for cloning here gets a cookie!
|
// First person to remove the need for cloning here gets a cookie!
|
||||||
let win = unsafe { init(w_attr.clone(), pl_attr.clone(), inserter, proxy.clone()) };
|
let win = unsafe { init(w_attr.clone(), pl_attr.clone(), inserter, proxy.clone()) };
|
||||||
let _ = tx.send(win);
|
let _ = tx.send(win);
|
||||||
});
|
});
|
||||||
|
|
||||||
rx.recv().unwrap()
|
rx.recv().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,142 +120,176 @@ impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See the docs in the crate root file.
|
pub(crate) fn get_position_physical(&self) -> Option<(i32, i32)> {
|
||||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
util::get_window_rect(self.window.0)
|
||||||
let mut rect: RECT = unsafe { mem::uninitialized() };
|
.map(|rect| (rect.left as i32, rect.top as i32))
|
||||||
|
|
||||||
if unsafe { winuser::GetWindowRect(self.window.0, &mut rect) } != 0 {
|
|
||||||
Some((rect.left as i32, rect.top as i32))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
#[inline]
|
||||||
use std::mem;
|
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||||
|
self.get_position_physical()
|
||||||
|
.map(|physical_position| {
|
||||||
|
let dpi_factor = self.get_hidpi_factor();
|
||||||
|
LogicalPosition::from_physical(physical_position, dpi_factor)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_inner_position_physical(&self) -> Option<(i32, i32)> {
|
||||||
let mut position: POINT = unsafe { mem::zeroed() };
|
let mut position: POINT = unsafe { mem::zeroed() };
|
||||||
if unsafe { winuser::ClientToScreen(self.window.0, &mut position) } == 0 {
|
if unsafe { winuser::ClientToScreen(self.window.0, &mut position) } == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((position.x, position.y))
|
Some((position.x, position.y))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See the docs in the crate root file.
|
#[inline]
|
||||||
pub fn set_position(&self, x: i32, y: i32) {
|
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||||
|
self.get_inner_position_physical()
|
||||||
|
.map(|physical_position| {
|
||||||
|
let dpi_factor = self.get_hidpi_factor();
|
||||||
|
LogicalPosition::from_physical(physical_position, dpi_factor)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_position_physical(&self, x: i32, y: i32) {
|
||||||
unsafe {
|
unsafe {
|
||||||
winuser::SetWindowPos(self.window.0, ptr::null_mut(), x as raw::c_int, y as raw::c_int,
|
winuser::SetWindowPos(
|
||||||
0, 0, winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOSIZE);
|
self.window.0,
|
||||||
|
ptr::null_mut(),
|
||||||
|
x as c_int,
|
||||||
|
y as c_int,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOSIZE,
|
||||||
|
);
|
||||||
winuser::UpdateWindow(self.window.0);
|
winuser::UpdateWindow(self.window.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See the docs in the crate root file.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
pub fn set_position(&self, logical_position: LogicalPosition) {
|
||||||
let mut rect: RECT = unsafe { mem::uninitialized() };
|
let dpi_factor = self.get_hidpi_factor();
|
||||||
|
let (x, y) = logical_position.to_physical(dpi_factor).into();
|
||||||
|
self.set_position_physical(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_inner_size_physical(&self) -> Option<(u32, u32)> {
|
||||||
|
let mut rect: RECT = unsafe { mem::uninitialized() };
|
||||||
if unsafe { winuser::GetClientRect(self.window.0, &mut rect) } == 0 {
|
if unsafe { winuser::GetClientRect(self.window.0, &mut rect) } == 0 {
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((
|
Some((
|
||||||
(rect.right - rect.left) as u32,
|
(rect.right - rect.left) as u32,
|
||||||
(rect.bottom - rect.top) as u32
|
(rect.bottom - rect.top) as u32,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See the docs in the crate root file.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||||
let mut rect: RECT = unsafe { mem::uninitialized() };
|
self.get_inner_size_physical()
|
||||||
|
.map(|physical_size| {
|
||||||
if unsafe { winuser::GetWindowRect(self.window.0, &mut rect) } == 0 {
|
let dpi_factor = self.get_hidpi_factor();
|
||||||
return None
|
LogicalSize::from_physical(physical_size, dpi_factor)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((
|
pub(crate) fn get_outer_size_physical(&self) -> Option<(u32, u32)> {
|
||||||
|
util::get_window_rect(self.window.0)
|
||||||
|
.map(|rect| (
|
||||||
(rect.right - rect.left) as u32,
|
(rect.right - rect.left) as u32,
|
||||||
(rect.bottom - rect.top) as u32
|
(rect.bottom - rect.top) as u32,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See the docs in the crate root file.
|
#[inline]
|
||||||
pub fn set_inner_size(&self, x: u32, y: u32) {
|
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||||
|
self.get_outer_size_physical()
|
||||||
|
.map(|physical_size| {
|
||||||
|
let dpi_factor = self.get_hidpi_factor();
|
||||||
|
LogicalSize::from_physical(physical_size, dpi_factor)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_inner_size_physical(&self, x: u32, y: u32) {
|
||||||
unsafe {
|
unsafe {
|
||||||
// Calculate the outer size based upon the specified inner size
|
let mut rect = RECT {
|
||||||
let mut rect = RECT { top: 0, left: 0, bottom: y as LONG, right: x as LONG };
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
bottom: y as LONG,
|
||||||
|
right: x as LONG,
|
||||||
|
};
|
||||||
let dw_style = winuser::GetWindowLongA(self.window.0, winuser::GWL_STYLE) as DWORD;
|
let dw_style = winuser::GetWindowLongA(self.window.0, winuser::GWL_STYLE) as DWORD;
|
||||||
let b_menu = !winuser::GetMenu(self.window.0).is_null() as BOOL;
|
let b_menu = !winuser::GetMenu(self.window.0).is_null() as BOOL;
|
||||||
let dw_style_ex = winuser::GetWindowLongA(self.window.0, winuser::GWL_EXSTYLE) as DWORD;
|
let dw_style_ex = winuser::GetWindowLongA(self.window.0, winuser::GWL_EXSTYLE) as DWORD;
|
||||||
winuser::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
|
winuser::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
|
||||||
let outer_x = (rect.right - rect.left).abs() as raw::c_int;
|
let outer_x = (rect.right - rect.left).abs() as c_int;
|
||||||
let outer_y = (rect.top - rect.bottom).abs() as raw::c_int;
|
let outer_y = (rect.top - rect.bottom).abs() as c_int;
|
||||||
|
winuser::SetWindowPos(
|
||||||
winuser::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, outer_x, outer_y,
|
self.window.0,
|
||||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOREPOSITION | winuser::SWP_NOMOVE);
|
ptr::null_mut(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
outer_x,
|
||||||
|
outer_y,
|
||||||
|
winuser::SWP_ASYNCWINDOWPOS
|
||||||
|
| winuser::SWP_NOZORDER
|
||||||
|
| winuser::SWP_NOREPOSITION
|
||||||
|
| winuser::SWP_NOMOVE,
|
||||||
|
);
|
||||||
winuser::UpdateWindow(self.window.0);
|
winuser::UpdateWindow(self.window.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See the docs in the crate root file.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
pub fn set_inner_size(&self, logical_size: LogicalSize) {
|
||||||
let mut window_state = self.window_state.lock().unwrap();
|
let dpi_factor = self.get_hidpi_factor();
|
||||||
window_state.attributes.min_dimensions = dimensions;
|
let (width, height) = logical_size.to_physical(dpi_factor).into();
|
||||||
|
self.set_inner_size_physical(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_min_dimensions_physical(&self, dimensions: Option<(u32, u32)>) {
|
||||||
|
self.window_state.lock().unwrap().min_size = dimensions.map(Into::into);
|
||||||
// Make windows re-check the window size bounds.
|
// Make windows re-check the window size bounds.
|
||||||
if let Some(inner_size) = self.get_inner_size() {
|
self.get_inner_size_physical()
|
||||||
unsafe {
|
.map(|(width, height)| self.set_inner_size_physical(width, height));
|
||||||
let mut rect = RECT { top: 0, left: 0, bottom: inner_size.1 as LONG, right: inner_size.0 as LONG };
|
|
||||||
let dw_style = winuser::GetWindowLongA(self.window.0, winuser::GWL_STYLE) as DWORD;
|
|
||||||
let b_menu = !winuser::GetMenu(self.window.0).is_null() as BOOL;
|
|
||||||
let dw_style_ex = winuser::GetWindowLongA(self.window.0, winuser::GWL_EXSTYLE) as DWORD;
|
|
||||||
winuser::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
|
|
||||||
let outer_x = (rect.right - rect.left).abs() as raw::c_int;
|
|
||||||
let outer_y = (rect.top - rect.bottom).abs() as raw::c_int;
|
|
||||||
|
|
||||||
winuser::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, outer_x, outer_y,
|
|
||||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOREPOSITION | winuser::SWP_NOMOVE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See the docs in the crate root file.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
pub fn set_min_dimensions(&self, logical_size: Option<LogicalSize>) {
|
||||||
let mut window_state = self.window_state.lock().unwrap();
|
let physical_size = logical_size.map(|logical_size| {
|
||||||
window_state.attributes.max_dimensions = dimensions;
|
let dpi_factor = self.get_hidpi_factor();
|
||||||
|
logical_size.to_physical(dpi_factor).into()
|
||||||
|
});
|
||||||
|
self.set_min_dimensions_physical(physical_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_max_dimensions_physical(&self, dimensions: Option<(u32, u32)>) {
|
||||||
|
self.window_state.lock().unwrap().max_size = dimensions.map(Into::into);
|
||||||
// Make windows re-check the window size bounds.
|
// Make windows re-check the window size bounds.
|
||||||
if let Some(inner_size) = self.get_inner_size() {
|
self.get_inner_size_physical()
|
||||||
unsafe {
|
.map(|(width, height)| self.set_inner_size_physical(width, height));
|
||||||
let mut rect = RECT { top: 0, left: 0, bottom: inner_size.1 as LONG, right: inner_size.0 as LONG };
|
}
|
||||||
let dw_style = winuser::GetWindowLongA(self.window.0, winuser::GWL_STYLE) as DWORD;
|
|
||||||
let b_menu = !winuser::GetMenu(self.window.0).is_null() as BOOL;
|
|
||||||
let dw_style_ex = winuser::GetWindowLongA(self.window.0, winuser::GWL_EXSTYLE) as DWORD;
|
|
||||||
winuser::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
|
|
||||||
let outer_x = (rect.right - rect.left).abs() as raw::c_int;
|
|
||||||
let outer_y = (rect.top - rect.bottom).abs() as raw::c_int;
|
|
||||||
|
|
||||||
winuser::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, outer_x, outer_y,
|
#[inline]
|
||||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOREPOSITION | winuser::SWP_NOMOVE);
|
pub fn set_max_dimensions(&self, logical_size: Option<LogicalSize>) {
|
||||||
}
|
let physical_size = logical_size.map(|logical_size| {
|
||||||
}
|
let dpi_factor = self.get_hidpi_factor();
|
||||||
|
logical_size.to_physical(dpi_factor).into()
|
||||||
|
});
|
||||||
|
self.set_max_dimensions_physical(physical_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_resizable(&self, resizable: bool) {
|
pub fn set_resizable(&self, resizable: bool) {
|
||||||
if let Ok(mut window_state) = self.window_state.lock() {
|
if resizable == self.resizable.get() {
|
||||||
if window_state.attributes.resizable == resizable {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if window_state.attributes.fullscreen.is_some() {
|
if self.fullscreen.borrow().is_some() {
|
||||||
window_state.attributes.resizable = resizable;
|
// If we're in fullscreen, update stored configuration but don't apply anything.
|
||||||
|
self.resizable.replace(resizable);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let window = self.window.clone();
|
|
||||||
let mut style = unsafe {
|
let mut style = unsafe {
|
||||||
winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE)
|
winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE)
|
||||||
};
|
};
|
||||||
|
@ -259,24 +298,15 @@ impl Window {
|
||||||
} else {
|
} else {
|
||||||
style &= !winuser::WS_SIZEBOX as LONG;
|
style &= !winuser::WS_SIZEBOX as LONG;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
winuser::SetWindowLongW(
|
winuser::SetWindowLongW(
|
||||||
window.0,
|
self.window.0,
|
||||||
winuser::GWL_STYLE,
|
winuser::GWL_STYLE,
|
||||||
style as _,
|
style as _,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
window_state.attributes.resizable = resizable;
|
self.resizable.replace(resizable);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove
|
|
||||||
pub fn platform_display(&self) -> *mut ::libc::c_void {
|
|
||||||
panic!() // Deprecated function ; we don't care anymore
|
|
||||||
}
|
|
||||||
// TODO: remove
|
|
||||||
pub fn platform_window(&self) -> *mut ::libc::c_void {
|
|
||||||
self.window.0 as *mut ::libc::c_void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `hwnd` of this window.
|
/// Returns the `hwnd` of this window.
|
||||||
|
@ -411,29 +441,30 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hidpi_factor(&self) -> f32 {
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
1.0
|
get_window_scale_factor(self.window.0, self.window.1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||||
let mut point = POINT {
|
let mut point = POINT { x, y };
|
||||||
x: x,
|
|
||||||
y: y,
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if winuser::ClientToScreen(self.window.0, &mut point) == 0 {
|
if winuser::ClientToScreen(self.window.0, &mut point) == 0 {
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if winuser::SetCursorPos(point.x, point.y) == 0 {
|
if winuser::SetCursorPos(point.x, point.y) == 0 {
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_cursor_position(&self, logical_position: LogicalPosition) -> Result<(), ()> {
|
||||||
|
let dpi_factor = self.get_hidpi_factor();
|
||||||
|
let (x, y) = logical_position.to_physical(dpi_factor).into();
|
||||||
|
self.set_cursor_position_physical(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn id(&self) -> WindowId {
|
pub fn id(&self) -> WindowId {
|
||||||
WindowId(self.window.0)
|
WindowId(self.window.0)
|
||||||
|
@ -441,18 +472,13 @@ impl Window {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_maximized(&self, maximized: bool) {
|
pub fn set_maximized(&self, maximized: bool) {
|
||||||
let mut window_state = self.window_state.lock().unwrap();
|
self.maximized.replace(maximized);
|
||||||
|
// We only maximize if we're not in fullscreen.
|
||||||
window_state.attributes.maximized = maximized;
|
if self.fullscreen.borrow().is_some() { return; }
|
||||||
// we only maximized if we are not in fullscreen
|
|
||||||
if window_state.attributes.fullscreen.is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let window = self.window.clone();
|
let window = self.window.clone();
|
||||||
unsafe {
|
unsafe {
|
||||||
// And because ShowWindow will resize the window
|
// `ShowWindow` resizes the window, so it must be called from the main thread.
|
||||||
// We call it in the main thread
|
|
||||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||||
winuser::ShowWindow(
|
winuser::ShowWindow(
|
||||||
window.0,
|
window.0,
|
||||||
|
@ -469,15 +495,15 @@ impl Window {
|
||||||
unsafe fn set_fullscreen_style(&self) -> (LONG, LONG) {
|
unsafe fn set_fullscreen_style(&self) -> (LONG, LONG) {
|
||||||
let mut window_state = self.window_state.lock().unwrap();
|
let mut window_state = self.window_state.lock().unwrap();
|
||||||
|
|
||||||
if window_state.attributes.fullscreen.is_none() || window_state.saved_window_info.is_none() {
|
if self.fullscreen.borrow().is_none() || window_state.saved_window_info.is_none() {
|
||||||
let mut rect: RECT = mem::zeroed();
|
let rect = util::get_window_rect(self.window.0).expect("`GetWindowRect` failed");
|
||||||
|
let dpi_factor = Some(self.get_hidpi_factor());
|
||||||
winuser::GetWindowRect(self.window.0, &mut rect);
|
|
||||||
|
|
||||||
window_state.saved_window_info = Some(events_loop::SavedWindowInfo {
|
window_state.saved_window_info = Some(events_loop::SavedWindowInfo {
|
||||||
style: winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE),
|
style: winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE),
|
||||||
ex_style: winuser::GetWindowLongW(self.window.0, winuser::GWL_EXSTYLE),
|
ex_style: winuser::GetWindowLongW(self.window.0, winuser::GWL_EXSTYLE),
|
||||||
rect,
|
rect,
|
||||||
|
is_fullscreen: true,
|
||||||
|
dpi_factor,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,15 +511,14 @@ impl Window {
|
||||||
let mut placement: winuser::WINDOWPLACEMENT = mem::zeroed();
|
let mut placement: winuser::WINDOWPLACEMENT = mem::zeroed();
|
||||||
placement.length = mem::size_of::<winuser::WINDOWPLACEMENT>() as u32;
|
placement.length = mem::size_of::<winuser::WINDOWPLACEMENT>() as u32;
|
||||||
winuser::GetWindowPlacement(self.window.0, &mut placement);
|
winuser::GetWindowPlacement(self.window.0, &mut placement);
|
||||||
window_state.attributes.maximized =
|
self.maximized.replace(placement.showCmd == (winuser::SW_SHOWMAXIMIZED as u32));
|
||||||
placement.showCmd == (winuser::SW_SHOWMAXIMIZED as u32);
|
|
||||||
let saved_window_info = window_state.saved_window_info.as_ref().unwrap();
|
let saved_window_info = window_state.saved_window_info.as_ref().unwrap();
|
||||||
|
|
||||||
(saved_window_info.style, saved_window_info.ex_style)
|
(saved_window_info.style, saved_window_info.ex_style)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn restore_saved_window(&self) {
|
unsafe fn restore_saved_window(&self) {
|
||||||
let window_state = self.window_state.lock().unwrap();
|
let mut window_state = self.window_state.lock().unwrap();
|
||||||
|
|
||||||
// 'saved_window_info' can be None if the window has never been
|
// 'saved_window_info' can be None if the window has never been
|
||||||
// in fullscreen mode before this method gets called.
|
// in fullscreen mode before this method gets called.
|
||||||
|
@ -504,18 +529,21 @@ impl Window {
|
||||||
// Reset original window style and size. The multiple window size/moves
|
// Reset original window style and size. The multiple window size/moves
|
||||||
// here are ugly, but if SetWindowPos() doesn't redraw, the taskbar won't be
|
// here are ugly, but if SetWindowPos() doesn't redraw, the taskbar won't be
|
||||||
// repainted. Better-looking methods welcome.
|
// repainted. Better-looking methods welcome.
|
||||||
|
{
|
||||||
|
let saved_window_info = window_state.saved_window_info.as_mut().unwrap();
|
||||||
|
saved_window_info.is_fullscreen = false;
|
||||||
|
}
|
||||||
let saved_window_info = window_state.saved_window_info.as_ref().unwrap();
|
let saved_window_info = window_state.saved_window_info.as_ref().unwrap();
|
||||||
|
|
||||||
let rect = saved_window_info.rect.clone();
|
let rect = saved_window_info.rect.clone();
|
||||||
let window = self.window.clone();
|
let window = self.window.clone();
|
||||||
let (mut style, ex_style) = (saved_window_info.style, saved_window_info.ex_style);
|
let (mut style, ex_style) = (saved_window_info.style, saved_window_info.ex_style);
|
||||||
|
|
||||||
let maximized = window_state.attributes.maximized;
|
let maximized = self.maximized.get();
|
||||||
let resizable = window_state.attributes.resizable;
|
let resizable = self.resizable.get();
|
||||||
|
|
||||||
// On restore, resize to the previous saved rect size.
|
// We're restoring the window to its size and position from before being fullscreened.
|
||||||
// And because SetWindowPos will resize the window
|
// `ShowWindow` resizes the window, so it must be called from the main thread.
|
||||||
// We call it in the main thread
|
|
||||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||||
if resizable {
|
if resizable {
|
||||||
style |= winuser::WS_SIZEBOX as LONG;
|
style |= winuser::WS_SIZEBOX as LONG;
|
||||||
|
@ -532,11 +560,13 @@ impl Window {
|
||||||
rect.top,
|
rect.top,
|
||||||
rect.right - rect.left,
|
rect.right - rect.left,
|
||||||
rect.bottom - rect.top,
|
rect.bottom - rect.top,
|
||||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOACTIVATE
|
winuser::SWP_ASYNCWINDOWPOS
|
||||||
|
| winuser::SWP_NOZORDER
|
||||||
|
| winuser::SWP_NOACTIVATE
|
||||||
| winuser::SWP_FRAMECHANGED,
|
| winuser::SWP_FRAMECHANGED,
|
||||||
);
|
);
|
||||||
|
|
||||||
// if it was set to maximized when it were fullscreened, we restore it as well
|
// We apply any requested changes to maximization state that occurred while we were in fullscreen.
|
||||||
winuser::ShowWindow(
|
winuser::ShowWindow(
|
||||||
window.0,
|
window.0,
|
||||||
if maximized {
|
if maximized {
|
||||||
|
@ -555,8 +585,8 @@ impl Window {
|
||||||
unsafe {
|
unsafe {
|
||||||
match &monitor {
|
match &monitor {
|
||||||
&Some(RootMonitorId { ref inner }) => {
|
&Some(RootMonitorId { ref inner }) => {
|
||||||
let pos = inner.get_position();
|
let (x, y): (i32, i32) = inner.get_position().into();
|
||||||
let dim = inner.get_dimensions();
|
let (width, height): (u32, u32) = inner.get_dimensions().into();
|
||||||
let window = self.window.clone();
|
let window = self.window.clone();
|
||||||
|
|
||||||
let (style, ex_style) = self.set_fullscreen_style();
|
let (style, ex_style) = self.set_fullscreen_style();
|
||||||
|
@ -582,10 +612,10 @@ impl Window {
|
||||||
winuser::SetWindowPos(
|
winuser::SetWindowPos(
|
||||||
window.0,
|
window.0,
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
pos.0,
|
x as c_int,
|
||||||
pos.1,
|
y as c_int,
|
||||||
dim.0 as i32,
|
width as c_int,
|
||||||
dim.1 as i32,
|
height as c_int,
|
||||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER
|
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER
|
||||||
| winuser::SWP_NOACTIVATE
|
| winuser::SWP_NOACTIVATE
|
||||||
| winuser::SWP_FRAMECHANGED,
|
| winuser::SWP_FRAMECHANGED,
|
||||||
|
@ -600,14 +630,12 @@ impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut window_state = self.window_state.lock().unwrap();
|
self.fullscreen.replace(monitor);
|
||||||
window_state.attributes.fullscreen = monitor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_decorations(&self, decorations: bool) {
|
pub fn set_decorations(&self, decorations: bool) {
|
||||||
if let Ok(mut window_state) = self.window_state.lock() {
|
if self.decorations.get() == decorations {
|
||||||
if window_state.attributes.decorations == decorations {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -615,9 +643,10 @@ impl Window {
|
||||||
let ex_style_flags = (winuser::WS_EX_WINDOWEDGE) as LONG;
|
let ex_style_flags = (winuser::WS_EX_WINDOWEDGE) as LONG;
|
||||||
|
|
||||||
// if we are in fullscreen mode, we only change the saved window info
|
// if we are in fullscreen mode, we only change the saved window info
|
||||||
if window_state.attributes.fullscreen.is_some() {
|
if self.fullscreen.borrow().is_some() {
|
||||||
{
|
{
|
||||||
let mut saved = window_state.saved_window_info.as_mut().unwrap();
|
let mut window_state = self.window_state.lock().unwrap();
|
||||||
|
let saved = window_state.saved_window_info.as_mut().unwrap();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
unjust_window_rect(&mut saved.rect, saved.style as _, saved.ex_style as _);
|
unjust_window_rect(&mut saved.rect, saved.style as _, saved.ex_style as _);
|
||||||
|
@ -641,7 +670,7 @@ impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window_state.attributes.decorations = decorations;
|
self.decorations.replace(decorations);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -675,21 +704,20 @@ impl Window {
|
||||||
rect.top,
|
rect.top,
|
||||||
rect.right - rect.left,
|
rect.right - rect.left,
|
||||||
rect.bottom - rect.top,
|
rect.bottom - rect.top,
|
||||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER
|
winuser::SWP_ASYNCWINDOWPOS
|
||||||
|
| winuser::SWP_NOZORDER
|
||||||
| winuser::SWP_NOACTIVATE
|
| winuser::SWP_NOACTIVATE
|
||||||
| winuser::SWP_FRAMECHANGED,
|
| winuser::SWP_FRAMECHANGED,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window_state.attributes.decorations = decorations;
|
self.decorations.replace(decorations);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_always_on_top(&self, always_on_top: bool) {
|
pub fn set_always_on_top(&self, always_on_top: bool) {
|
||||||
if let Ok(mut window_state) = self.window_state.lock() {
|
if self.always_on_top.get() == always_on_top {
|
||||||
if window_state.attributes.always_on_top == always_on_top {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -714,8 +742,7 @@ impl Window {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window_state.attributes.always_on_top = always_on_top;
|
self.always_on_top.replace(always_on_top);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -752,7 +779,7 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_ime_spot(&self, _x: i32, _y: i32) {
|
pub fn set_ime_spot(&self, _logical_spot: LogicalPosition) {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -779,27 +806,26 @@ pub struct WindowWrapper(HWND, HDC);
|
||||||
// https://github.com/retep998/winapi-rs/issues/396
|
// https://github.com/retep998/winapi-rs/issues/396
|
||||||
unsafe impl Send for WindowWrapper {}
|
unsafe impl Send for WindowWrapper {}
|
||||||
|
|
||||||
pub unsafe fn adjust_size(
|
pub unsafe fn adjust_size(physical_size: PhysicalSize, style: DWORD, ex_style: DWORD) -> (LONG, LONG) {
|
||||||
(x, y): (u32, u32), style: DWORD, ex_style: DWORD,
|
let (width, height): (u32, u32) = physical_size.into();
|
||||||
) -> (LONG, LONG) {
|
let mut rect = RECT { left: 0, right: width as LONG, top: 0, bottom: height as LONG };
|
||||||
let mut rect = RECT { left: 0, right: x as LONG, top: 0, bottom: y as LONG };
|
|
||||||
winuser::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
|
winuser::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
|
||||||
(rect.right - rect.left, rect.bottom - rect.top)
|
(rect.right - rect.left, rect.bottom - rect.top)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn init(
|
unsafe fn init(
|
||||||
mut window: WindowAttributes,
|
mut attributes: WindowAttributes,
|
||||||
mut pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
mut pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||||
inserter: events_loop::Inserter,
|
inserter: events_loop::Inserter,
|
||||||
events_loop_proxy: events_loop::EventsLoopProxy,
|
events_loop_proxy: events_loop::EventsLoopProxy,
|
||||||
) -> Result<Window, CreationError> {
|
) -> Result<Window, CreationError> {
|
||||||
let title = OsStr::new(&window.title)
|
let title = OsStr::new(&attributes.title)
|
||||||
.encode_wide()
|
.encode_wide()
|
||||||
.chain(Some(0).into_iter())
|
.chain(Some(0).into_iter())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let window_icon = {
|
let window_icon = {
|
||||||
let icon = window.window_icon
|
let icon = attributes.window_icon
|
||||||
.take()
|
.take()
|
||||||
.map(WinIcon::from_icon);
|
.map(WinIcon::from_icon);
|
||||||
if icon.is_some() {
|
if icon.is_some() {
|
||||||
|
@ -826,14 +852,19 @@ unsafe fn init(
|
||||||
// registering the window class
|
// registering the window class
|
||||||
let class_name = register_window_class(&window_icon, &taskbar_icon);
|
let class_name = register_window_class(&window_icon, &taskbar_icon);
|
||||||
|
|
||||||
|
let (width, height) = attributes.dimensions
|
||||||
|
.map(Into::into)
|
||||||
|
.unwrap_or((1024, 768));
|
||||||
// building a RECT object with coordinates
|
// building a RECT object with coordinates
|
||||||
let mut rect = RECT {
|
let mut rect = RECT {
|
||||||
left: 0, right: window.dimensions.unwrap_or((1024, 768)).0 as LONG,
|
left: 0,
|
||||||
top: 0, bottom: window.dimensions.unwrap_or((1024, 768)).1 as LONG,
|
right: width as LONG,
|
||||||
|
top: 0,
|
||||||
|
bottom: height as LONG,
|
||||||
};
|
};
|
||||||
|
|
||||||
// computing the style and extended style of the window
|
// computing the style and extended style of the window
|
||||||
let (mut ex_style, style) = if !window.decorations {
|
let (mut ex_style, style) = if !attributes.decorations {
|
||||||
(winuser::WS_EX_APPWINDOW,
|
(winuser::WS_EX_APPWINDOW,
|
||||||
//winapi::WS_POPUP is incompatible with winapi::WS_CHILD
|
//winapi::WS_POPUP is incompatible with winapi::WS_CHILD
|
||||||
if pl_attribs.parent.is_some() {
|
if pl_attribs.parent.is_some() {
|
||||||
|
@ -848,7 +879,7 @@ unsafe fn init(
|
||||||
winuser::WS_OVERLAPPEDWINDOW | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN)
|
winuser::WS_OVERLAPPEDWINDOW | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN)
|
||||||
};
|
};
|
||||||
|
|
||||||
if window.always_on_top {
|
if attributes.always_on_top {
|
||||||
ex_style |= winuser::WS_EX_TOPMOST;
|
ex_style |= winuser::WS_EX_TOPMOST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -857,14 +888,15 @@ unsafe fn init(
|
||||||
|
|
||||||
// creating the real window this time, by using the functions in `extra_functions`
|
// creating the real window this time, by using the functions in `extra_functions`
|
||||||
let real_window = {
|
let real_window = {
|
||||||
let (width, height) = if window.dimensions.is_some() {
|
let (adjusted_width, adjusted_height) = if attributes.dimensions.is_some() {
|
||||||
let min_dimensions = window.min_dimensions
|
let min_dimensions = attributes.min_dimensions
|
||||||
.map(|d| adjust_size(d, style, ex_style))
|
.map(|logical_size| PhysicalSize::from_logical(logical_size, 1.0))
|
||||||
|
.map(|physical_size| adjust_size(physical_size, style, ex_style))
|
||||||
.unwrap_or((0, 0));
|
.unwrap_or((0, 0));
|
||||||
let max_dimensions = window.max_dimensions
|
let max_dimensions = attributes.max_dimensions
|
||||||
.map(|d| adjust_size(d, style, ex_style))
|
.map(|logical_size| PhysicalSize::from_logical(logical_size, 1.0))
|
||||||
.unwrap_or((raw::c_int::max_value(), raw::c_int::max_value()));
|
.map(|physical_size| adjust_size(physical_size, style, ex_style))
|
||||||
|
.unwrap_or((c_int::max_value(), c_int::max_value()));
|
||||||
(
|
(
|
||||||
Some((rect.right - rect.left).min(max_dimensions.0).max(min_dimensions.0)),
|
Some((rect.right - rect.left).min(max_dimensions.0).max(min_dimensions.0)),
|
||||||
Some((rect.bottom - rect.top).min(max_dimensions.1).max(min_dimensions.1))
|
Some((rect.bottom - rect.top).min(max_dimensions.1).max(min_dimensions.1))
|
||||||
|
@ -873,13 +905,13 @@ unsafe fn init(
|
||||||
(None, None)
|
(None, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut style = if !window.visible {
|
let mut style = if !attributes.visible {
|
||||||
style
|
style
|
||||||
} else {
|
} else {
|
||||||
style | winuser::WS_VISIBLE
|
style | winuser::WS_VISIBLE
|
||||||
};
|
};
|
||||||
|
|
||||||
if !window.resizable {
|
if !attributes.resizable {
|
||||||
style &= !winuser::WS_SIZEBOX;
|
style &= !winuser::WS_SIZEBOX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -892,10 +924,13 @@ unsafe fn init(
|
||||||
title.as_ptr() as LPCWSTR,
|
title.as_ptr() as LPCWSTR,
|
||||||
style | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN,
|
style | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN,
|
||||||
winuser::CW_USEDEFAULT, winuser::CW_USEDEFAULT,
|
winuser::CW_USEDEFAULT, winuser::CW_USEDEFAULT,
|
||||||
width.unwrap_or(winuser::CW_USEDEFAULT), height.unwrap_or(winuser::CW_USEDEFAULT),
|
adjusted_width.unwrap_or(winuser::CW_USEDEFAULT),
|
||||||
|
adjusted_height.unwrap_or(winuser::CW_USEDEFAULT),
|
||||||
pl_attribs.parent.unwrap_or(ptr::null_mut()),
|
pl_attribs.parent.unwrap_or(ptr::null_mut()),
|
||||||
ptr::null_mut(), libloaderapi::GetModuleHandleW(ptr::null()),
|
ptr::null_mut(),
|
||||||
ptr::null_mut());
|
libloaderapi::GetModuleHandleW(ptr::null()),
|
||||||
|
ptr::null_mut(),
|
||||||
|
);
|
||||||
|
|
||||||
if handle.is_null() {
|
if handle.is_null() {
|
||||||
return Err(CreationError::OsError(format!("CreateWindowEx function failed: {}",
|
return Err(CreationError::OsError(format!("CreateWindowEx function failed: {}",
|
||||||
|
@ -922,21 +957,42 @@ unsafe fn init(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (transparent, maximized, fullscreen) = (
|
let dpi = get_window_dpi(real_window.0, real_window.1);
|
||||||
window.transparent.clone(), window.maximized.clone(), window.fullscreen.clone()
|
let dpi_factor = dpi_to_scale_factor(dpi);
|
||||||
|
if dpi != BASE_DPI {
|
||||||
|
let mut packed_dimensions = 0;
|
||||||
|
// MAKELPARAM isn't provided by winapi yet.
|
||||||
|
let ptr = &mut packed_dimensions as *mut LPARAM as *mut WORD;
|
||||||
|
*ptr.offset(0) = width as WORD;
|
||||||
|
*ptr.offset(1) = height as WORD;
|
||||||
|
winuser::PostMessageW(
|
||||||
|
real_window.0,
|
||||||
|
*INITIAL_DPI_MSG_ID,
|
||||||
|
dpi as WPARAM,
|
||||||
|
packed_dimensions,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Creating a mutex to track the current window state
|
let window_state = {
|
||||||
let window_state = Arc::new(Mutex::new(events_loop::WindowState {
|
let max_size = attributes.max_dimensions
|
||||||
|
.map(|logical_size| PhysicalSize::from_logical(logical_size, dpi_factor));
|
||||||
|
let min_size = attributes.min_dimensions
|
||||||
|
.map(|logical_size| PhysicalSize::from_logical(logical_size, dpi_factor));
|
||||||
|
let mut window_state = events_loop::WindowState {
|
||||||
cursor: Cursor(winuser::IDC_ARROW), // use arrow by default
|
cursor: Cursor(winuser::IDC_ARROW), // use arrow by default
|
||||||
cursor_state: CursorState::Normal,
|
cursor_state: CursorState::Normal,
|
||||||
attributes: window,
|
max_size,
|
||||||
|
min_size,
|
||||||
mouse_in_window: false,
|
mouse_in_window: false,
|
||||||
saved_window_info: None,
|
saved_window_info: None,
|
||||||
}));
|
dpi_factor,
|
||||||
|
};
|
||||||
|
// Creating a mutex to track the current window state
|
||||||
|
Arc::new(Mutex::new(window_state))
|
||||||
|
};
|
||||||
|
|
||||||
// making the window transparent
|
// making the window transparent
|
||||||
if transparent {
|
if attributes.transparent {
|
||||||
let bb = dwmapi::DWM_BLURBEHIND {
|
let bb = dwmapi::DWM_BLURBEHIND {
|
||||||
dwFlags: 0x1, // FIXME: DWM_BB_ENABLE;
|
dwFlags: 0x1, // FIXME: DWM_BB_ENABLE;
|
||||||
fEnable: 1,
|
fEnable: 1,
|
||||||
|
@ -950,14 +1006,19 @@ unsafe fn init(
|
||||||
let win = Window {
|
let win = Window {
|
||||||
window: real_window,
|
window: real_window,
|
||||||
window_state: window_state,
|
window_state: window_state,
|
||||||
|
decorations: Cell::new(attributes.decorations),
|
||||||
|
maximized: Cell::new(attributes.maximized.clone()),
|
||||||
|
resizable: Cell::new(attributes.resizable.clone()),
|
||||||
|
fullscreen: RefCell::new(attributes.fullscreen.clone()),
|
||||||
|
always_on_top: Cell::new(attributes.always_on_top),
|
||||||
window_icon: Cell::new(window_icon),
|
window_icon: Cell::new(window_icon),
|
||||||
taskbar_icon: Cell::new(taskbar_icon),
|
taskbar_icon: Cell::new(taskbar_icon),
|
||||||
events_loop_proxy,
|
events_loop_proxy,
|
||||||
};
|
};
|
||||||
|
|
||||||
win.set_maximized(maximized);
|
win.set_maximized(attributes.maximized);
|
||||||
if let Some(_) = fullscreen {
|
if let Some(_) = attributes.fullscreen {
|
||||||
win.set_fullscreen(fullscreen);
|
win.set_fullscreen(attributes.fullscreen);
|
||||||
force_window_active(win.window.0);
|
force_window_active(win.window.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
178
src/window.rs
178
src/window.rs
|
@ -1,16 +1,20 @@
|
||||||
use std::collections::vec_deque::IntoIter as VecDequeIter;
|
use std::collections::vec_deque::IntoIter as VecDequeIter;
|
||||||
|
|
||||||
use CreationError;
|
use {
|
||||||
use CursorState;
|
CreationError,
|
||||||
use EventsLoop;
|
CursorState,
|
||||||
use Icon;
|
EventsLoop,
|
||||||
use MouseCursor;
|
Icon,
|
||||||
use Window;
|
LogicalPosition,
|
||||||
use WindowBuilder;
|
LogicalSize,
|
||||||
use WindowId;
|
MouseCursor,
|
||||||
|
PhysicalPosition,
|
||||||
use libc;
|
PhysicalSize,
|
||||||
use platform;
|
platform,
|
||||||
|
Window,
|
||||||
|
WindowBuilder,
|
||||||
|
WindowId,
|
||||||
|
};
|
||||||
|
|
||||||
impl WindowBuilder {
|
impl WindowBuilder {
|
||||||
/// Initializes a new `WindowBuilder` with default values.
|
/// Initializes a new `WindowBuilder` with default values.
|
||||||
|
@ -23,29 +27,23 @@ impl WindowBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Requests the window to be of specific dimensions.
|
/// Requests the window to be of specific dimensions.
|
||||||
///
|
|
||||||
/// Width and height are in pixels.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_dimensions(mut self, width: u32, height: u32) -> WindowBuilder {
|
pub fn with_dimensions(mut self, size: LogicalSize) -> WindowBuilder {
|
||||||
self.window.dimensions = Some((width, height));
|
self.window.dimensions = Some(size);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a minimum dimension size for the window
|
/// Sets a minimum dimension size for the window
|
||||||
///
|
|
||||||
/// Width and height are in pixels.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_min_dimensions(mut self, width: u32, height: u32) -> WindowBuilder {
|
pub fn with_min_dimensions(mut self, min_size: LogicalSize) -> WindowBuilder {
|
||||||
self.window.min_dimensions = Some((width, height));
|
self.window.min_dimensions = Some(min_size);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a maximum dimension size for the window
|
/// Sets a maximum dimension size for the window
|
||||||
///
|
|
||||||
/// Width and height are in pixels.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_max_dimensions(mut self, width: u32, height: u32) -> WindowBuilder {
|
pub fn with_max_dimensions(mut self, max_size: LogicalSize) -> WindowBuilder {
|
||||||
self.window.max_dimensions = Some((width, height));
|
self.window.max_dimensions = Some(max_size);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,14 +142,15 @@ impl WindowBuilder {
|
||||||
///
|
///
|
||||||
/// Error should be very rare and only occur in case of permission denied, incompatible system,
|
/// Error should be very rare and only occur in case of permission denied, incompatible system,
|
||||||
/// out of memory, etc.
|
/// out of memory, etc.
|
||||||
|
#[inline]
|
||||||
pub fn build(mut self, events_loop: &EventsLoop) -> Result<Window, CreationError> {
|
pub fn build(mut self, events_loop: &EventsLoop) -> Result<Window, CreationError> {
|
||||||
self.window.dimensions = Some(self.window.dimensions.unwrap_or_else(|| {
|
self.window.dimensions = Some(self.window.dimensions.unwrap_or_else(|| {
|
||||||
if let Some(ref monitor) = self.window.fullscreen {
|
if let Some(ref monitor) = self.window.fullscreen {
|
||||||
// resizing the window to the dimensions of the monitor when fullscreen
|
// resizing the window to the dimensions of the monitor when fullscreen
|
||||||
monitor.get_dimensions()
|
LogicalSize::from_physical(monitor.get_dimensions(), 1.0)
|
||||||
} else {
|
} else {
|
||||||
// default dimensions
|
// default dimensions
|
||||||
(1024, 768)
|
(1024, 768).into()
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -219,7 +218,7 @@ impl Window {
|
||||||
///
|
///
|
||||||
/// Returns `None` if the window no longer exists.
|
/// Returns `None` if the window no longer exists.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
pub fn get_position(&self) -> Option<LogicalPosition> {
|
||||||
self.window.get_position()
|
self.window.get_position()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +227,7 @@ impl Window {
|
||||||
///
|
///
|
||||||
/// The same conditions that apply to `get_position` apply to this method.
|
/// The same conditions that apply to `get_position` apply to this method.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
|
||||||
self.window.get_inner_position()
|
self.window.get_inner_position()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,61 +237,30 @@ impl Window {
|
||||||
///
|
///
|
||||||
/// This is a no-op if the window has already been closed.
|
/// This is a no-op if the window has already been closed.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_position(&self, x: i32, y: i32) {
|
pub fn set_position(&self, position: LogicalPosition) {
|
||||||
self.window.set_position(x, y)
|
self.window.set_position(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the size in pixels of the client area of the window.
|
/// Returns the logical size of the window's client area.
|
||||||
///
|
///
|
||||||
/// The client area is the content of the window, excluding the title bar and borders.
|
/// The client area is the content of the window, excluding the title bar and borders.
|
||||||
/// These are the dimensions that need to be supplied to `glViewport`.
|
///
|
||||||
|
/// Converting the returned `LogicalSize` to `PhysicalSize` produces the size your framebuffer should be.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the window no longer exists.
|
/// Returns `None` if the window no longer exists.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
pub fn get_inner_size(&self) -> Option<LogicalSize> {
|
||||||
self.window.get_inner_size()
|
self.window.get_inner_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the size in points of the client area of the window.
|
/// Returns the logical size of the entire window.
|
||||||
///
|
///
|
||||||
/// The client area is the content of the window, excluding the title bar and borders.
|
/// These dimensions include the title bar and borders. If you don't want that (and you usually don't),
|
||||||
/// To get the dimensions of the frame buffer when calling `glViewport`, multiply with hidpi factor.
|
|
||||||
///
|
|
||||||
/// Returns `None` if the window no longer exists.
|
|
||||||
///
|
|
||||||
/// DEPRECATED
|
|
||||||
#[inline]
|
|
||||||
#[deprecated]
|
|
||||||
pub fn get_inner_size_points(&self) -> Option<(u32, u32)> {
|
|
||||||
self.window.get_inner_size().map(|(x, y)| {
|
|
||||||
let hidpi = self.hidpi_factor();
|
|
||||||
((x as f32 / hidpi) as u32, (y as f32 / hidpi) as u32)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the size in pixels of the client area of the window.
|
|
||||||
///
|
|
||||||
/// The client area is the content of the window, excluding the title bar and borders.
|
|
||||||
/// These are the dimensions of the frame buffer, and the dimensions that you should use
|
|
||||||
/// when you call `glViewport`.
|
|
||||||
///
|
|
||||||
/// Returns `None` if the window no longer exists.
|
|
||||||
///
|
|
||||||
/// DEPRECATED
|
|
||||||
#[inline]
|
|
||||||
#[deprecated]
|
|
||||||
pub fn get_inner_size_pixels(&self) -> Option<(u32, u32)> {
|
|
||||||
self.window.get_inner_size()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the size in pixels of the window.
|
|
||||||
///
|
|
||||||
/// These dimensions include title bar and borders. If you don't want these, you should use
|
|
||||||
/// use `get_inner_size` instead.
|
/// use `get_inner_size` instead.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the window no longer exists.
|
/// Returns `None` if the window no longer exists.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
pub fn get_outer_size(&self) -> Option<LogicalSize> {
|
||||||
self.window.get_outer_size()
|
self.window.get_outer_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,23 +270,19 @@ impl Window {
|
||||||
///
|
///
|
||||||
/// This is a no-op if the window has already been closed.
|
/// This is a no-op if the window has already been closed.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_inner_size(&self, x: u32, y: u32) {
|
pub fn set_inner_size(&self, size: LogicalSize) {
|
||||||
self.window.set_inner_size(x, y)
|
self.window.set_inner_size(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a minimum dimension size for the window.
|
/// Sets a minimum dimension size for the window.
|
||||||
///
|
|
||||||
/// Width and height are in pixels.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
pub fn set_min_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||||
self.window.set_min_dimensions(dimensions)
|
self.window.set_min_dimensions(dimensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a maximum dimension size for the window.
|
/// Sets a maximum dimension size for the window.
|
||||||
///
|
|
||||||
/// Width and height are in pixels.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
pub fn set_max_dimensions(&self, dimensions: Option<LogicalSize>) {
|
||||||
self.window.set_max_dimensions(dimensions)
|
self.window.set_max_dimensions(dimensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,46 +301,30 @@ impl Window {
|
||||||
self.window.set_resizable(resizable)
|
self.window.set_resizable(resizable)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// DEPRECATED. Gets the native platform specific display for this window.
|
/// Returns the DPI factor that can be used to map logical pixels to physical pixels, and vice versa.
|
||||||
/// This is typically only required when integrating with
|
///
|
||||||
/// other libraries that need this information.
|
/// See the [`dpi`](dpi/index.html) module for more information.
|
||||||
#[deprecated]
|
///
|
||||||
|
/// ## Platform-specific
|
||||||
|
///
|
||||||
|
/// - **X11:** Can be overridden using the `WINIT_HIDPI_FACTOR` environment variable.
|
||||||
|
/// - **Android:** Always returns 1.0.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn platform_display(&self) -> *mut libc::c_void {
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
self.window.platform_display()
|
self.window.get_hidpi_factor()
|
||||||
}
|
|
||||||
|
|
||||||
/// DEPRECATED. Gets the native platform specific window handle. This is
|
|
||||||
/// typically only required when integrating with other libraries
|
|
||||||
/// that need this information.
|
|
||||||
#[deprecated]
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn platform_window(&self) -> *mut libc::c_void {
|
|
||||||
self.window.platform_window()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Modifies the mouse cursor of the window.
|
/// Modifies the mouse cursor of the window.
|
||||||
/// Has no effect on Android.
|
/// Has no effect on Android.
|
||||||
|
#[inline]
|
||||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||||
self.window.set_cursor(cursor);
|
self.window.set_cursor(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the ratio between the backing framebuffer resolution and the
|
|
||||||
/// window size in screen pixels. This is typically one for a normal display
|
|
||||||
/// and two for a retina display.
|
|
||||||
///
|
|
||||||
/// ## Platform-specific
|
|
||||||
/// On X11 the DPI factor can be overridden using the `WINIT_HIDPI_FACTOR` environment
|
|
||||||
/// variable.
|
|
||||||
#[inline]
|
|
||||||
pub fn hidpi_factor(&self) -> f32 {
|
|
||||||
self.window.hidpi_factor()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Changes the position of the cursor in window coordinates.
|
/// Changes the position of the cursor in window coordinates.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), ()> {
|
||||||
self.window.set_cursor_position(x, y)
|
self.window.set_cursor_position(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets how winit handles the cursor. See the documentation of `CursorState` for details.
|
/// Sets how winit handles the cursor. See the documentation of `CursorState` for details.
|
||||||
|
@ -426,11 +374,12 @@ impl Window {
|
||||||
|
|
||||||
/// Sets location of IME candidate box in client area coordinates relative to the top left.
|
/// Sets location of IME candidate box in client area coordinates relative to the top left.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_ime_spot(&self, x: i32, y: i32) {
|
pub fn set_ime_spot(&self, position: LogicalPosition) {
|
||||||
self.window.set_ime_spot(x, y)
|
self.window.set_ime_spot(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the monitor on which the window currently resides
|
/// Returns the monitor on which the window currently resides
|
||||||
|
#[inline]
|
||||||
pub fn get_current_monitor(&self) -> MonitorId {
|
pub fn get_current_monitor(&self) -> MonitorId {
|
||||||
self.window.get_current_monitor()
|
self.window.get_current_monitor()
|
||||||
}
|
}
|
||||||
|
@ -477,26 +426,29 @@ impl MonitorId {
|
||||||
self.inner.get_name()
|
self.inner.get_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of pixels currently displayed on the monitor.
|
/// Returns the monitor's resolution.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
pub fn get_dimensions(&self) -> PhysicalSize {
|
||||||
self.inner.get_dimensions()
|
self.inner.get_dimensions()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the top-left corner position of the monitor relative to the larger full
|
/// Returns the top-left corner position of the monitor relative to the larger full
|
||||||
/// screen area.
|
/// screen area.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_position(&self) -> (i32, i32) {
|
pub fn get_position(&self) -> PhysicalPosition {
|
||||||
self.inner.get_position()
|
self.inner.get_position()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the ratio between the monitor's physical pixels and logical pixels.
|
/// Returns the DPI factor that can be used to map logical pixels to physical pixels, and vice versa.
|
||||||
|
///
|
||||||
|
/// See the [`dpi`](dpi/index.html) module for more information.
|
||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
/// On X11 the DPI factor can be overridden using the `WINIT_HIDPI_FACTOR` environment
|
///
|
||||||
/// variable.
|
/// - **X11:** Can be overridden using the `WINIT_HIDPI_FACTOR` environment variable.
|
||||||
|
/// - **Android:** Always returns 1.0.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_hidpi_factor(&self) -> f32 {
|
pub fn get_hidpi_factor(&self) -> f64 {
|
||||||
self.inner.get_hidpi_factor()
|
self.inner.get_hidpi_factor()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue