diff --git a/.gitignore b/.gitignore index 9fdb6f0115..bf5cea56b6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ Cargo.lock target/ rls/ +.vscode/ *~ #*# diff --git a/Cargo.toml b/Cargo.toml index d7257077e0..15fe3a24f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,9 @@ cocoa = "0.15" core-foundation = "0.6" core-graphics = "0.14" +[target.'cfg(target_os = "windows")'.dependencies] +libloading = "0.5.0" + [target.'cfg(target_os = "windows")'.dependencies.winapi] version = "0.3.5" features = [ diff --git a/examples/blur.rs b/examples/blur.rs new file mode 100644 index 0000000000..00cd464475 --- /dev/null +++ b/examples/blur.rs @@ -0,0 +1,65 @@ +extern crate winit; +extern crate winapi; + +fn main() { + let mut events_loop = winit::EventsLoop::new(); + + let window = winit::WindowBuilder::new() + .with_title("A blurry window!") + .with_blur(true) + .build(&events_loop) + .unwrap(); + + #[cfg(target_os = "macos")] { + // On macOS the blur material is 'light' by default. + // Let's change it to a dark theme! + use winit::os::macos::{BlurMaterial, WindowExt}; + unsafe { window.set_blur_material(BlurMaterial::Dark) }; + } + + events_loop.run_forever(|event| match event { + winit::Event::WindowEvent { + event: winit::WindowEvent::CloseRequested, + .. + } => winit::ControlFlow::Break, + winit::Event::WindowEvent { + event: + winit::WindowEvent::KeyboardInput { + input: + winit::KeyboardInput { + virtual_keycode: Some(winit::VirtualKeyCode::Escape), + .. + }, + .. + }, + .. + } => winit::ControlFlow::Break, + winit::Event::WindowEvent { + event: winit::WindowEvent::Refresh, + window_id, + } if window_id == window.id() => { + paint_window(&window); // Important! + winit::ControlFlow::Continue + }, + _ => winit::ControlFlow::Continue, + }); +} + +fn paint_window(window: &winit::Window) { + // On Windows we need to paint the color black onto the window. + // The black color is made transparent by the compositor. + #[cfg(target_os = "windows")] { + use winapi::um::winuser; + use winapi::shared::windef; + use winit::os::windows::WindowExt; + + let window = window.get_hwnd() as windef::HWND; + + unsafe { + let mut ps: winuser::PAINTSTRUCT = std::mem::zeroed(); + let hdc = winuser::BeginPaint(window, &mut ps as *mut _); + let _ = winuser::FillRect(hdc, &ps.rcPaint as *const _, (winuser::COLOR_WINDOWTEXT + 1) as _); + let _ = winuser::EndPaint(window, &mut ps as *mut _); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 44a1cb4ad9..c4dca9d47e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,10 +87,12 @@ #[allow(unused_imports)] #[macro_use] extern crate lazy_static; -extern crate libc; #[cfg(feature = "icon_loading")] extern crate image; +extern crate libc; +#[cfg(target_os = "windows")] +extern crate libloading; #[cfg(target_os = "windows")] extern crate winapi; #[cfg(any(target_os = "macos", target_os = "ios"))] @@ -102,19 +104,27 @@ extern crate cocoa; extern crate core_foundation; #[cfg(target_os = "macos")] extern crate core_graphics; -#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))] -extern crate x11_dl; -#[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") +)] extern crate parking_lot; -#[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") +)] 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; +#[cfg( + any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd") +)] +extern crate x11_dl; pub(crate) use dpi::*; // TODO: Actually change the imports throughout the codebase. pub use events::*; -pub use window::{AvailableMonitorsIter, MonitorId}; pub use icon::*; +pub use window::{AvailableMonitorsIter, MonitorId}; pub mod dpi; mod events; @@ -179,7 +189,7 @@ pub struct DeviceId(platform::DeviceId); /// `EventsLoopProxy` allows you to wakeup an `EventsLoop` from an other thread. pub struct EventsLoop { events_loop: platform::EventsLoop, - _marker: ::std::marker::PhantomData<*mut ()> // Not Send nor Sync + _marker: ::std::marker::PhantomData<*mut ()>, // Not Send nor Sync } /// Returned by the user callback given to the `EventsLoop::run_forever` method. @@ -213,20 +223,25 @@ impl EventsLoop { #[inline] pub fn get_available_monitors(&self) -> AvailableMonitorsIter { let data = self.events_loop.get_available_monitors(); - AvailableMonitorsIter{ data: data.into_iter() } + AvailableMonitorsIter { + data: data.into_iter(), + } } /// Returns the primary monitor of the system. #[inline] pub fn get_primary_monitor(&self) -> MonitorId { - MonitorId { inner: self.events_loop.get_primary_monitor() } + MonitorId { + inner: self.events_loop.get_primary_monitor(), + } } /// Fetches all the events that are pending, calls the callback function for each of them, /// and returns. #[inline] pub fn poll_events(&mut self, callback: F) - where F: FnMut(Event) + where + F: FnMut(Event), { self.events_loop.poll_events(callback) } @@ -241,7 +256,8 @@ impl EventsLoop { /// at a sufficient rate. Rendering in the callback with vsync enabled **will** cause significant lag. #[inline] pub fn run_forever(&mut self, callback: F) - where F: FnMut(Event) -> ControlFlow + where + F: FnMut(Event) -> ControlFlow, { self.events_loop.run_forever(callback) } @@ -482,6 +498,16 @@ pub struct WindowAttributes { /// [iOS only] Enable multitouch, /// see [multipleTouchEnabled](https://developer.apple.com/documentation/uikit/uiview/1622519-multipletouchenabled) pub multitouch: bool, + + /// Whether the window should have a blur effect. + /// + /// Blur is similar to transparency in that both allow you to "look though" the window, + /// except that with blur the seethrough content has an effect applied that gives it a + /// milky or smeared look. + /// The exact look can be controlled per platform via traits like `WindowExt`. + /// + /// The default is `false`. + pub blur: bool, } impl Default for WindowAttributes { @@ -501,6 +527,7 @@ impl Default for WindowAttributes { always_on_top: false, window_icon: None, multitouch: false, + blur: false, } } } diff --git a/src/os/macos.rs b/src/os/macos.rs index 776c10ffb8..0460b3bd03 100644 --- a/src/os/macos.rs +++ b/src/os/macos.rs @@ -16,6 +16,13 @@ pub trait WindowExt { /// /// The pointer will become invalid when the `Window` is destroyed. fn get_nsview(&self) -> *mut c_void; + + /// For windows created with the [blurred](WindowBuilder::with_blur) option, + /// this controls the appearance of the blur effect. + /// + /// Marked as unsafe because depending on the version of macOS and the `BlurMaterial` variant passed, + /// this might cause a crash. + unsafe fn set_blur_material(&self, material: BlurMaterial); } impl WindowExt for Window { @@ -28,6 +35,11 @@ impl WindowExt for Window { fn get_nsview(&self) -> *mut c_void { self.window.get_nsview() } + + #[inline] + unsafe fn set_blur_material(&self, material: BlurMaterial) { + self.window.set_blur_material(material); + } } /// Corresponds to `NSApplicationActivationPolicy`. @@ -157,3 +169,53 @@ impl MonitorIdExt for MonitorId { self.inner.get_nsscreen().map(|s| s as *mut c_void) } } + +/// Enumeration of all possible blur materials for macOS. Applies to macOS SDK 10.10+. +/// +/// Not all versions of macOS support all the variants listed here. +/// Check [Apple's documentation](https://developer.apple.com/documentation/appkit/nsvisualeffectview/material) +/// to find out what your target version supports. +/// The behaviour for using a material which is not supported depends how it is implemented in cocoa, +/// but will most likely cause a crash. +#[repr(i64)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum BlurMaterial { + /// A default material for the view’s effective appearance. + AppearanceBased = 0, + /// A material with a light effect. + Light = 1, + /// A material with a dark effect. + Dark = 2, + /// The material for a window’s titlebar. + Titlebar = 3, + /// The material used to indicate a selection. + Selection = 4, + /// The material for menus. + Menu = 5, + /// The material for the background of popover windows. + Popover = 6, + /// The material for the background of window sidebars. + Sidebar = 7, + /// A material with a medium-light effect. + MediumLight = 8, + /// A material with an ultra-dark effect. + UltraDark = 9, + /// The material for in-line header or footer views. + HeaderView = 10, + /// The material for the background of sheet windows. + Sheet = 11, + /// The material for the background of opaque windows. + WindowBackground = 12, + /// The material for the background of heads-up display (HUD) windows. + HudWindow = 13, + /// The material for the background of a full-screen modal interface. + FullScreenUi = 15, + /// The material for the background of a tool tip. + ToolTip = 17, + /// The material for the background of opaque content. + ContentBackground = 18, + /// The material for under a window's background. + UnderWindowBackground = 21, + /// The material for the area behind the pages of a document. + UnderPageBackground = 22, +} \ No newline at end of file diff --git a/src/platform/macos/view.rs b/src/platform/macos/view.rs index c5a4b41a15..82be876dd4 100644 --- a/src/platform/macos/view.rs +++ b/src/platform/macos/view.rs @@ -13,7 +13,8 @@ use cocoa::foundation::{NSPoint, NSRect, NSSize, NSString, NSUInteger}; use objc::declare::ClassDecl; use objc::runtime::{Class, Object, Protocol, Sel, BOOL}; -use {ElementState, Event, KeyboardInput, MouseButton, WindowEvent, WindowId}; +use os::macos::BlurMaterial; +use {ElementState, Event, KeyboardInput, MouseButton, WindowAttributes, WindowEvent, WindowId}; use platform::platform::events_loop::{DEVICE_ID, event_mods, Shared, to_virtual_key_code}; use platform::platform::util; use platform::platform::ffi::*; @@ -27,7 +28,7 @@ struct ViewState { last_insert: Option, } -pub fn new_view(window: id, shared: Weak) -> IdRef { +pub fn new_view(window: id, shared: Weak, win_attribs: &WindowAttributes) -> IdRef { let state = ViewState { window, shared, @@ -38,8 +39,14 @@ pub fn new_view(window: id, shared: Weak) -> IdRef { unsafe { // This is free'd in `dealloc` let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void; - let view: id = msg_send![VIEW_CLASS.0, alloc]; - IdRef::new(msg_send![view, initWithWinit:state_ptr]) + let view_class = if win_attribs.blur { VISUAL_EFFECT_VIEW_CLASS.0 } else { VIEW_CLASS.0 }; + let view: id = msg_send![view_class, alloc]; + let view: id = msg_send![view, initWithWinit:state_ptr]; + if win_attribs.blur { + let _: () = msg_send![view, setMaterial:BlurMaterial::Light as i64]; + let _: () = msg_send![view, setBlendingMode:0i64]; + } + IdRef::new(view) } } @@ -65,6 +72,18 @@ unsafe impl Sync for ViewClass {} lazy_static! { static ref VIEW_CLASS: ViewClass = unsafe { let superclass = Class::get("NSView").unwrap(); + let decl = common_view_decl(superclass); + ViewClass(decl.register()) + }; + + static ref VISUAL_EFFECT_VIEW_CLASS: ViewClass = unsafe { + let superclass = Class::get("NSVisualEffectView").unwrap(); + let decl = common_view_decl(superclass); + ViewClass(decl.register()) + }; +} + +unsafe fn common_view_decl(superclass: &'static Class) -> ClassDecl { let mut decl = ClassDecl::new("WinitView", superclass).unwrap(); decl.add_method(sel!(dealloc), dealloc as extern fn(&Object, Sel)); decl.add_method( @@ -126,8 +145,7 @@ lazy_static! { decl.add_ivar::("markedText"); let protocol = Protocol::get("NSTextInputClient").unwrap(); decl.add_protocol(&protocol); - ViewClass(decl.register()) - }; + decl } extern fn dealloc(this: &Object, _sel: Sel) { diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index 06a8f978a8..fd445b7528 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -37,7 +37,7 @@ use { WindowId, }; use CreationError::OsError; -use os::macos::{ActivationPolicy, WindowExt}; +use os::macos::{ActivationPolicy, BlurMaterial, WindowExt}; use platform::platform::{ffi, util}; use platform::platform::events_loop::{EventsLoop, Shared}; use platform::platform::view::{new_view, set_ime_spot}; @@ -582,6 +582,11 @@ impl WindowExt for Window2 { fn get_nsview(&self) -> *mut c_void { *self.view as *mut c_void } + + #[inline] + unsafe fn set_blur_material(&self, material: BlurMaterial) { + let _: () = msg_send![*self.view, setMaterial:material as i64]; + } } impl Window2 { @@ -618,7 +623,7 @@ impl Window2 { return Err(OsError(format!("Couldn't create NSWindow"))); }, }; - let view = match Window2::create_view(*window, Weak::clone(&shared)) { + let view = match Window2::create_view(*window, Weak::clone(&shared), &win_attribs) { Some(view) => view, None => { let _: () = unsafe { msg_send![autoreleasepool, drain] }; @@ -843,9 +848,9 @@ impl Window2 { } } - fn create_view(window: id, shared: Weak) -> Option { + fn create_view(window: id, shared: Weak, win_attribs: &WindowAttributes) -> Option { unsafe { - let view = new_view(window, shared); + let view = new_view(window, shared, win_attribs); view.non_nil().map(|view| { view.setWantsBestResolutionOpenGLSurface_(YES); window.setContentView_(*view); diff --git a/src/platform/windows/blur.rs b/src/platform/windows/blur.rs new file mode 100644 index 0000000000..0f756d6f50 --- /dev/null +++ b/src/platform/windows/blur.rs @@ -0,0 +1,183 @@ +//! This module contains the multiple implementations for controlling +//! both transparency and blur for windows. +//! +//! On `Windows` there are, depending on the version of the OS, different +//! APIs to use for enabling transparency and blur. +//! This modules abstracts over these different APIs and provides a single, +//! simple interface to use the correct API automatically. + +use std::os::raw; +use std::ptr; + +use libloading; +use winapi::shared::minwindef::ULONG; +use winapi::shared::windef::HWND; +use winapi::um::{dwmapi, errhandlingapi, winuser}; + +use platform::platform::window::WindowWrapper; + +lazy_static! { + static ref USER32_DLL: Option = + { libloading::Library::new("user32.dll").ok() }; + static ref POINTERS: Option = { + Some(AcrylicBlurPointers { + swca: unsafe { + USER32_DLL + .as_ref()? + .get(b"SetWindowCompositionAttribute") + .ok()? + }, + }) + }; +} + +static DWM: Dwm = Dwm; +static ACRYLIC_BLUR: AcrylicBlur = AcrylicBlur; + +pub fn implementation() -> &'static Implementation { + // unsafe { + // let ver = sysinfoapi::GetVersion(); + // let (major, minor) = (ver & 0xFF, (ver & 0xFF00) >> 8); + // println!("Major: {}, minor: {}", major, minor); + // } + + if let Some(_) = *POINTERS { + &ACRYLIC_BLUR + } else { + &DWM + } +} + +pub trait Implementation { + fn set_opaque(&self, window: &mut WindowWrapper); + fn set_transparent(&self, window: &mut WindowWrapper); + fn set_blur(&self, window: &mut WindowWrapper); +} + +struct Dwm; + +impl Implementation for Dwm { + fn set_opaque(&self, window: &mut WindowWrapper) { + let bb = dwmapi::DWM_BLURBEHIND { + dwFlags: 0x1, // FIXME: DWM_BB_ENABLE; + fEnable: 0, + hRgnBlur: ptr::null_mut(), + fTransitionOnMaximized: 0, + }; + unsafe { + dwmapi::DwmEnableBlurBehindWindow(window.0, &bb); + } + } + + fn set_transparent(&self, window: &mut WindowWrapper) { + self.set_blur(window); // TODO: Can you even do actual transparency with DWM? + } + + fn set_blur(&self, window: &mut WindowWrapper) { + let bb = dwmapi::DWM_BLURBEHIND { + dwFlags: 0x1, // FIXME: DWM_BB_ENABLE; + fEnable: 1, + hRgnBlur: ptr::null_mut(), + fTransitionOnMaximized: 0, + }; + unsafe { + dwmapi::DwmEnableBlurBehindWindow(window.0, &bb); + } + } +} + +struct AcrylicBlur; + +/// SetWindowCompositionAttribute function pointer +type SwcaFn = unsafe extern "system" fn( + hwnd: HWND, + attribute: *const WindowCompositionAttributeData +) -> raw::c_int; + +struct AcrylicBlurPointers { + swca: libloading::Symbol<'static, SwcaFn>, +} + +impl AcrylicBlurPointers { + fn set_window_composite_attribute( + &self, + window: &mut WindowWrapper, + attribute: &WindowCompositionAttributeData, + ) -> raw::c_int { + unsafe { (self.swca)(window.0, attribute as *const _) } + } +} + +#[repr(u32)] +enum AccentState { + Disable = 0, + EnableGradient = 1, + EnableTransparentGradient = 2, + EnableBlurBehind = 3, + EnableAcrylicBlurBehind = 4, + InvalidState = 5, +} + +#[repr(C)] +struct AccentPolicy { + state: AccentState, + flags: raw::c_int, + gradient_color: raw::c_uint, + animation_id: raw::c_int, +} + +#[repr(u32)] +enum WindowCompositionAttribute { + AccentPolicy = 19, +} + +#[repr(C)] +struct WindowCompositionAttributeData { + attribute: WindowCompositionAttribute, + policy: *const AccentPolicy, + size: ULONG, +} + +impl AcrylicBlur {} + +impl Implementation for AcrylicBlur { + fn set_opaque(&self, window: &mut WindowWrapper) { + POINTERS.as_ref().unwrap().set_window_composite_attribute( + window, + &WindowCompositionAttributeData { + attribute: WindowCompositionAttribute::AccentPolicy, + policy: &AccentPolicy { + state: AccentState::Disable, + flags: 0, + gradient_color: 0x00000000, + animation_id: 0, + } as *const _, + size: ::std::mem::size_of::() as _, + }, + ); + } + + fn set_transparent(&self, window: &mut WindowWrapper) { + self.set_blur(window); // Also, apparently no 'true' transparency support based on WCA. + } + + fn set_blur(&self, window: &mut WindowWrapper) { + POINTERS.as_ref().unwrap().set_window_composite_attribute( + window, + &WindowCompositionAttributeData { + attribute: WindowCompositionAttribute::AccentPolicy, + policy: &AccentPolicy { + // TODO: Decide between EnableBlurBehind and EnableAcrylicBlurBehind (since build 17063) + // based on the Windows version. + // When choosing EnableAcrylicBlurBehind, set gradient_color + // to something non-zero (apparently mandatory). + state: AccentState::EnableBlurBehind, + flags: 0, + gradient_color: 0x00000000, + animation_id: 0, + } as *const _, + size: ::std::mem::size_of::() as _, + }, + ); + } +} diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index 1f51ac1dd7..40c6ed41dc 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -473,7 +473,8 @@ pub unsafe extern "system" fn callback( window_id: SuperWindowId(WindowId(window)), event: Refresh, }); - winuser::DefWindowProcW(window, msg, wparam, lparam) + //winuser::DefWindowProcW(window, msg, wparam, lparam) // TODO: Why was this here? + 0 }, // WM_MOVE supplies client area positions, so we send Moved here instead. diff --git a/src/platform/windows/mod.rs b/src/platform/windows/mod.rs index 69c96d68f8..d3259aba3b 100644 --- a/src/platform/windows/mod.rs +++ b/src/platform/windows/mod.rs @@ -47,6 +47,7 @@ pub struct WindowId(HWND); unsafe impl Send for WindowId {} unsafe impl Sync for WindowId {} +mod blur; mod dpi; mod event; mod events_loop; diff --git a/src/platform/windows/window.rs b/src/platform/windows/window.rs index 516ec93322..aaacbfdf9a 100644 --- a/src/platform/windows/window.rs +++ b/src/platform/windows/window.rs @@ -2,36 +2,31 @@ use std::cell::{Cell, RefCell}; use std::ffi::OsStr; -use std::{io, mem, ptr}; use std::os::windows::ffi::OsStrExt; -use std::sync::{Arc, Mutex}; use std::sync::mpsc::channel; +use std::sync::{Arc, Mutex}; +use std::{io, mem, ptr}; 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::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::winnt::{LONG, LPCWSTR}; +use winapi::um::{combaseapi, dwmapi, libloaderapi, winuser}; -use { - CreationError, - CursorState, - Icon, - LogicalPosition, - LogicalSize, - MonitorId as RootMonitorId, - MouseCursor, - PhysicalSize, - WindowAttributes, +use platform::platform::dpi::{ + dpi_to_scale_factor, get_window_dpi, get_window_scale_factor, BASE_DPI, }; -use platform::platform::{Cursor, EventsLoop, PlatformSpecificWindowBuilderAttributes, WindowId}; -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::raw_input::register_all_mice_and_keyboards_for_raw_input; use platform::platform::util; +use platform::platform::{Cursor, EventsLoop, PlatformSpecificWindowBuilderAttributes, WindowId}; +use { + CreationError, CursorState, Icon, LogicalPosition, LogicalSize, MonitorId as RootMonitorId, + MouseCursor, PhysicalSize, WindowAttributes, +}; /// The Win32 implementation of the main `Window` object. pub struct Window { @@ -121,17 +116,15 @@ impl Window { } pub(crate) fn get_position_physical(&self) -> Option<(i32, i32)> { - util::get_window_rect(self.window.0) - .map(|rect| (rect.left as i32, rect.top as i32)) + util::get_window_rect(self.window.0).map(|rect| (rect.left as i32, rect.top as i32)) } #[inline] pub fn get_position(&self) -> Option { - self.get_position_physical() - .map(|physical_position| { - let dpi_factor = self.get_hidpi_factor(); - LogicalPosition::from_physical(physical_position, dpi_factor) - }) + 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)> { @@ -144,11 +137,10 @@ impl Window { #[inline] pub fn get_inner_position(&self) -> Option { - self.get_inner_position_physical() - .map(|physical_position| { - let dpi_factor = self.get_hidpi_factor(); - LogicalPosition::from_physical(physical_position, dpi_factor) - }) + 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) { @@ -186,28 +178,27 @@ impl Window { #[inline] pub fn get_inner_size(&self) -> Option { - self.get_inner_size_physical() - .map(|physical_size| { - let dpi_factor = self.get_hidpi_factor(); - LogicalSize::from_physical(physical_size, dpi_factor) - }) + self.get_inner_size_physical().map(|physical_size| { + let dpi_factor = self.get_hidpi_factor(); + LogicalSize::from_physical(physical_size, dpi_factor) + }) } pub(crate) fn get_outer_size_physical(&self) -> Option<(u32, u32)> { - util::get_window_rect(self.window.0) - .map(|rect| ( + util::get_window_rect(self.window.0).map(|rect| { + ( (rect.right - rect.left) as u32, (rect.bottom - rect.top) as u32, - )) + ) + }) } #[inline] pub fn get_outer_size(&self) -> Option { - self.get_outer_size_physical() - .map(|physical_size| { - let dpi_factor = self.get_hidpi_factor(); - LogicalSize::from_physical(physical_size, dpi_factor) - }) + 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) { @@ -232,9 +223,9 @@ impl Window { outer_x, outer_y, winuser::SWP_ASYNCWINDOWPOS - | winuser::SWP_NOZORDER - | winuser::SWP_NOREPOSITION - | winuser::SWP_NOMOVE, + | winuser::SWP_NOZORDER + | winuser::SWP_NOREPOSITION + | winuser::SWP_NOMOVE, ); winuser::UpdateWindow(self.window.0); } @@ -290,9 +281,7 @@ impl Window { return; } - let mut style = unsafe { - winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE) - }; + let mut style = unsafe { winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE) }; if resizable { style |= winuser::WS_SIZEBOX as LONG; } else { @@ -300,11 +289,7 @@ impl Window { } unsafe { - winuser::SetWindowLongW( - self.window.0, - winuser::GWL_STYLE, - style as _, - ); + winuser::SetWindowLongW(self.window.0, winuser::GWL_STYLE, style as _); }; self.resizable.replace(resizable); } @@ -323,16 +308,24 @@ impl Window { MouseCursor::Crosshair => winuser::IDC_CROSS, MouseCursor::Text | MouseCursor::VerticalText => winuser::IDC_IBEAM, MouseCursor::NotAllowed | MouseCursor::NoDrop => winuser::IDC_NO, - MouseCursor::Grab | MouseCursor::Grabbing | - MouseCursor::Move | MouseCursor::AllScroll => winuser::IDC_SIZEALL, - MouseCursor::EResize | MouseCursor::WResize | - MouseCursor::EwResize | MouseCursor::ColResize => winuser::IDC_SIZEWE, - MouseCursor::NResize | MouseCursor::SResize | - MouseCursor::NsResize | MouseCursor::RowResize => winuser::IDC_SIZENS, - MouseCursor::NeResize | MouseCursor::SwResize | - MouseCursor::NeswResize => winuser::IDC_SIZENESW, - MouseCursor::NwResize | MouseCursor::SeResize | - MouseCursor::NwseResize => winuser::IDC_SIZENWSE, + MouseCursor::Grab + | MouseCursor::Grabbing + | MouseCursor::Move + | MouseCursor::AllScroll => winuser::IDC_SIZEALL, + MouseCursor::EResize + | MouseCursor::WResize + | MouseCursor::EwResize + | MouseCursor::ColResize => winuser::IDC_SIZEWE, + MouseCursor::NResize + | MouseCursor::SResize + | MouseCursor::NsResize + | MouseCursor::RowResize => winuser::IDC_SIZENS, + MouseCursor::NeResize | MouseCursor::SwResize | MouseCursor::NeswResize => { + winuser::IDC_SIZENESW + } + MouseCursor::NwResize | MouseCursor::SeResize | MouseCursor::NwseResize => { + winuser::IDC_SIZENWSE + } MouseCursor::Wait => winuser::IDC_WAIT, MouseCursor::Progress => winuser::IDC_APPSTARTING, MouseCursor::Help => winuser::IDC_HELP, @@ -354,7 +347,8 @@ impl Window { if winuser::ClientToScreen(self.window.0, &mut client_rect.left as *mut _ as LPPOINT) == 0 { return Err("`ClientToScreen` (left, top) failed".to_owned()); } - if winuser::ClientToScreen(self.window.0, &mut client_rect.right as *mut _ as LPPOINT) == 0 { + if winuser::ClientToScreen(self.window.0, &mut client_rect.right as *mut _ as LPPOINT) == 0 + { return Err("`ClientToScreen` (right, bottom) failed".to_owned()); } if winuser::GetClipCursor(&mut clip_rect) == 0 { @@ -387,8 +381,7 @@ impl Window { winuser::ShowCursor(TRUE); }, - (CursorState::Normal, CursorState::Grab) - | (CursorState::Hide, CursorState::Grab) => unsafe { + (CursorState::Normal, CursorState::Grab) | (CursorState::Hide, CursorState::Grab) => unsafe { let mut rect = mem::uninitialized(); if winuser::GetClientRect(window.0, &mut rect) == 0 { return Err("`GetClientRect` failed".to_owned()); @@ -431,10 +424,9 @@ impl Window { CursorState::Grab if !is_grabbed => CursorState::Normal, current_state => current_state, }; - let result = Self::change_cursor_state(&window, current_state, state) - .map(|_| { - window_state_lock.cursor_state = state; - }); + let result = Self::change_cursor_state(&window, current_state, state).map(|_| { + window_state_lock.cursor_state = state; + }); let _ = tx.send(result); }); rx.recv().unwrap() @@ -474,7 +466,9 @@ impl Window { pub fn set_maximized(&self, maximized: bool) { self.maximized.replace(maximized); // We only maximize if we're not in fullscreen. - if self.fullscreen.borrow().is_some() { return; } + if self.fullscreen.borrow().is_some() { + return; + } let window = self.window.clone(); unsafe { @@ -511,7 +505,8 @@ impl Window { let mut placement: winuser::WINDOWPLACEMENT = mem::zeroed(); placement.length = mem::size_of::() as u32; winuser::GetWindowPlacement(self.window.0, &mut placement); - self.maximized.replace(placement.showCmd == (winuser::SW_SHOWMAXIMIZED as u32)); + self.maximized + .replace(placement.showCmd == (winuser::SW_SHOWMAXIMIZED as u32)); let saved_window_info = window_state.saved_window_info.as_ref().unwrap(); (saved_window_info.style, saved_window_info.ex_style) @@ -561,9 +556,9 @@ impl Window { rect.right - rect.left, rect.bottom - rect.top, winuser::SWP_ASYNCWINDOWPOS - | winuser::SWP_NOZORDER - | winuser::SWP_NOACTIVATE - | winuser::SWP_FRAMECHANGED, + | winuser::SWP_NOZORDER + | winuser::SWP_NOACTIVATE + | winuser::SWP_FRAMECHANGED, ); // We apply any requested changes to maximization state that occurred while we were in fullscreen. @@ -603,7 +598,8 @@ impl Window { window.0, winuser::GWL_EXSTYLE, ((ex_style as DWORD) - & !(winuser::WS_EX_DLGMODALFRAME | winuser::WS_EX_WINDOWEDGE + & !(winuser::WS_EX_DLGMODALFRAME + | winuser::WS_EX_WINDOWEDGE | winuser::WS_EX_CLIENTEDGE | winuser::WS_EX_STATICEDGE)) as LONG, @@ -616,7 +612,8 @@ impl Window { y as c_int, width as c_int, height as c_int, - winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER + winuser::SWP_ASYNCWINDOWPOS + | winuser::SWP_NOZORDER | winuser::SWP_NOACTIVATE | winuser::SWP_FRAMECHANGED, ); @@ -705,9 +702,9 @@ impl Window { rect.right - rect.left, rect.bottom - rect.top, winuser::SWP_ASYNCWINDOWPOS - | winuser::SWP_NOZORDER - | winuser::SWP_NOACTIVATE - | winuser::SWP_FRAMECHANGED, + | winuser::SWP_NOZORDER + | winuser::SWP_NOACTIVATE + | winuser::SWP_FRAMECHANGED, ); }); } @@ -798,7 +795,7 @@ impl Drop for Window { /// A simple non-owning wrapper around a window. #[doc(hidden)] #[derive(Clone)] -pub struct WindowWrapper(HWND, HDC); +pub struct WindowWrapper(pub HWND, pub HDC); // Send is not implemented for HWND and HDC, we have to wrap it and implement it manually. // For more info see: @@ -806,9 +803,18 @@ pub struct WindowWrapper(HWND, HDC); // https://github.com/retep998/winapi-rs/issues/396 unsafe impl Send for WindowWrapper {} -pub unsafe fn adjust_size(physical_size: PhysicalSize, style: DWORD, ex_style: DWORD) -> (LONG, LONG) { +pub unsafe fn adjust_size( + physical_size: PhysicalSize, + style: DWORD, + ex_style: DWORD, +) -> (LONG, LONG) { let (width, height): (u32, u32) = physical_size.into(); - let mut rect = RECT { left: 0, right: width as LONG, top: 0, bottom: height as LONG }; + let mut rect = RECT { + left: 0, + right: width as LONG, + top: 0, + bottom: height as LONG, + }; winuser::AdjustWindowRectEx(&mut rect, style, 0, ex_style); (rect.right - rect.left, rect.bottom - rect.top) } @@ -825,9 +831,7 @@ unsafe fn init( .collect::>(); let window_icon = { - let icon = attributes.window_icon - .take() - .map(WinIcon::from_icon); + let icon = attributes.window_icon.take().map(WinIcon::from_icon); if icon.is_some() { Some(icon.unwrap().map_err(|err| { CreationError::OsError(format!("Failed to create `ICON_SMALL`: {:?}", err)) @@ -837,9 +841,7 @@ unsafe fn init( } }; let taskbar_icon = { - let icon = pl_attribs.taskbar_icon - .take() - .map(WinIcon::from_icon); + let icon = pl_attribs.taskbar_icon.take().map(WinIcon::from_icon); if icon.is_some() { Some(icon.unwrap().map_err(|err| { CreationError::OsError(format!("Failed to create `ICON_BIG`: {:?}", err)) @@ -852,9 +854,7 @@ unsafe fn init( // registering the window class let class_name = register_window_class(&window_icon, &taskbar_icon); - let (width, height) = attributes.dimensions - .map(Into::into) - .unwrap_or((1024, 768)); + let (width, height) = attributes.dimensions.map(Into::into).unwrap_or((1024, 768)); // building a RECT object with coordinates let mut rect = RECT { left: 0, @@ -865,18 +865,20 @@ unsafe fn init( // computing the style and extended style of the window 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 if pl_attribs.parent.is_some() { winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN - } - else { + } else { winuser::WS_POPUP | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN - } + }, ) } else { - (winuser::WS_EX_APPWINDOW | winuser::WS_EX_WINDOWEDGE, - winuser::WS_OVERLAPPEDWINDOW | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN) + ( + winuser::WS_EX_APPWINDOW | winuser::WS_EX_WINDOWEDGE, + winuser::WS_OVERLAPPEDWINDOW | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN, + ) }; if attributes.always_on_top { @@ -887,19 +889,29 @@ unsafe fn init( winuser::AdjustWindowRectEx(&mut rect, style, 0, ex_style); // creating the real window this time, by using the functions in `extra_functions` - let real_window = { + let mut real_window = { let (adjusted_width, adjusted_height) = if attributes.dimensions.is_some() { - let min_dimensions = attributes.min_dimensions + let min_dimensions = attributes + .min_dimensions .map(|logical_size| PhysicalSize::from_logical(logical_size, 1.0)) .map(|physical_size| adjust_size(physical_size, style, ex_style)) .unwrap_or((0, 0)); - let max_dimensions = attributes.max_dimensions + let max_dimensions = attributes + .max_dimensions .map(|logical_size| PhysicalSize::from_logical(logical_size, 1.0)) .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.bottom - rect.top).min(max_dimensions.1).max(min_dimensions.1)) + 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), + ), ) } else { (None, None) @@ -919,11 +931,13 @@ unsafe fn init( style |= winuser::WS_CHILD; } - let handle = winuser::CreateWindowExW(ex_style | winuser::WS_EX_ACCEPTFILES, + let handle = winuser::CreateWindowExW( + ex_style | winuser::WS_EX_ACCEPTFILES, class_name.as_ptr(), title.as_ptr() as LPCWSTR, style | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN, - winuser::CW_USEDEFAULT, winuser::CW_USEDEFAULT, + winuser::CW_USEDEFAULT, + 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()), @@ -933,14 +947,18 @@ unsafe fn init( ); if handle.is_null() { - return Err(CreationError::OsError(format!("CreateWindowEx function failed: {}", - format!("{}", io::Error::last_os_error())))); + return Err(CreationError::OsError(format!( + "CreateWindowEx function failed: {}", + format!("{}", io::Error::last_os_error()) + ))); } let hdc = winuser::GetDC(handle); if hdc.is_null() { - return Err(CreationError::OsError(format!("GetDC function failed: {}", - format!("{}", io::Error::last_os_error())))); + return Err(CreationError::OsError(format!( + "GetDC function failed: {}", + format!("{}", io::Error::last_os_error()) + ))); } WindowWrapper(handle, hdc) @@ -951,9 +969,9 @@ unsafe fn init( // Register for touch events if applicable { - let digitizer = winuser::GetSystemMetrics( winuser::SM_DIGITIZER ) as u32; + let digitizer = winuser::GetSystemMetrics(winuser::SM_DIGITIZER) as u32; if digitizer & winuser::NID_READY != 0 { - winuser::RegisterTouchWindow( real_window.0, winuser::TWF_WANTPALM ); + winuser::RegisterTouchWindow(real_window.0, winuser::TWF_WANTPALM); } } @@ -974,9 +992,11 @@ unsafe fn init( } let window_state = { - let max_size = attributes.max_dimensions + let max_size = attributes + .max_dimensions .map(|logical_size| PhysicalSize::from_logical(logical_size, dpi_factor)); - let min_size = attributes.min_dimensions + 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 @@ -993,14 +1013,18 @@ unsafe fn init( // making the window transparent if attributes.transparent { - let bb = dwmapi::DWM_BLURBEHIND { - dwFlags: 0x1, // FIXME: DWM_BB_ENABLE; - fEnable: 1, - hRgnBlur: ptr::null_mut(), - fTransitionOnMaximized: 0, - }; - - dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb); + // let bb = dwmapi::DWM_BLURBEHIND { + // dwFlags: 0x1, // FIXME: DWM_BB_ENABLE; + // fEnable: 1, + // hRgnBlur: ptr::null_mut(), + // fTransitionOnMaximized: 0, + // }; + + // dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb); + + super::blur::implementation().set_transparent(&mut real_window); + } else if attributes.blur { + super::blur::implementation().set_blur(&mut real_window); } let win = Window { @@ -1069,7 +1093,6 @@ unsafe fn register_window_class( class_name } - struct ComInitialized(*mut ()); impl Drop for ComInitialized { fn drop(&mut self) { diff --git a/src/window.rs b/src/window.rs index 0fb2c4d246..2e8fbcc55e 100644 --- a/src/window.rs +++ b/src/window.rs @@ -96,6 +96,20 @@ impl WindowBuilder { #[inline] pub fn with_transparency(mut self, transparent: bool) -> WindowBuilder { self.window.transparent = transparent; + if transparent { + self.window.blur = false; + } + self + } + + /// Sets whether the background of the window should be blurred. + /// See the blur member on [WindowAttributes](::WindowAttributes) for more info. + #[inline] + pub fn with_blur(mut self, blur: bool) -> WindowBuilder { + self.window.blur = blur; + if blur { + self.window.transparent = false; + } self }