Skip to content

Commit

Permalink
Add winit feature flag (#1018)
Browse files Browse the repository at this point in the history
Co-authored-by: Wu Yu Wei <wusyong9104@gmail.com>
Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>
  • Loading branch information
3 people authored Sep 6, 2023
1 parent 4bdf1c3 commit b062e94
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 12 deletions.
5 changes: 5 additions & 0 deletions .changes/winit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wry": minor
---

Add `winit` and `tao` feature flag with `tao` as default.
7 changes: 7 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,10 @@ jobs:
!contains(matrix.platform.target, 'android') &&
!contains(matrix.platform.target, 'ios'))
run: cargo test --verbose --target ${{ matrix.platform.target }}

- name: build wry with winit
if: (
contains(matrix.platform.target, 'windows') ||
contains(matrix.platform.target, 'apple'))
run: cargo build --no-default-features --features winit --target ${{ matrix.platform.target }}

10 changes: 7 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ targets = [
]

[features]
default = [ "file-drop", "objc-exception", "protocol" ]
default = [ "file-drop", "objc-exception", "protocol", "tao" ]
objc-exception = [ "objc/exception" ]
file-drop = [ ]
protocol = [ ]
Expand All @@ -41,7 +41,8 @@ serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1.0"
thiserror = "1.0"
url = "2.4"
tao = { version = "0.22", default-features = false, features = [ "serde" ] }
tao = { version = "0.22", default-features = false, features = [ "serde" ], optional = true}
winit = { version = "0.28", features = [ "serde" ], optional = true }
http = "0.2.9"

[dev-dependencies]
Expand Down Expand Up @@ -77,7 +78,10 @@ dunce = "1"
"Win32_System_SystemServices",
"Win32_UI_Shell",
"Win32_UI_WindowsAndMessaging",
"Win32_Globalization"
"Win32_Globalization",
"Win32_UI_HiDpi",
"Win32_UI_Input",
"Win32_UI_Input_KeyboardAndMouse"
]

[target."cfg(any(target_os = \"ios\", target_os = \"macos\"))".dependencies]
Expand Down
4 changes: 4 additions & 0 deletions src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@
//!
//! [tao]: https://crates.io/crates/tao

#[cfg(feature = "tao")]
pub use tao::*;

#[cfg(feature = "winit")]
pub use winit::*;
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
//! Wry uses a set of feature flags to toggle several advanced features. `file-drop`, `protocol`,
//! are enabled by default.
//!
//! - `tao`: Default windowing crate used by wry. It is re-exported as `application` module.
//! - `winit`: Replace [tao] with [winit] crate. It only supports Windows and macOS.
//! - `file-drop`: Enables [`with_file_drop_handler`] to control the behaviour when there are files
//! interacting with the window. Enabled by default.
//! - `protocol`: Enables [`with_custom_protocol`] to define custom URL scheme for handling tasks like
Expand Down
28 changes: 19 additions & 9 deletions src/webview/webview2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: MIT

mod file_drop;
mod resize;

use crate::{
webview::{
Expand Down Expand Up @@ -31,9 +32,7 @@ use once_cell::unsync::OnceCell;
use windows::{
core::{ComInterface, PCSTR, PCWSTR, PWSTR},
Win32::{
Foundation::{
BOOL, E_FAIL, E_POINTER, FARPROC, HGLOBAL, HWND, LPARAM, LRESULT, POINT, RECT, WPARAM,
},
Foundation::*,
Globalization::{self, MAX_LOCALE_NAME},
System::{
Com::{IStream, StructuredStorage::CreateStreamOnHGlobal},
Expand All @@ -43,7 +42,7 @@ use windows::{
},
UI::{
Shell::{DefSubclassProc, SetWindowSubclass},
WindowsAndMessaging as win32wm,
WindowsAndMessaging::{self as win32wm},
},
},
};
Expand Down Expand Up @@ -397,11 +396,11 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_
let js = take_pwstr(js);
if js == "__WEBVIEW_LEFT_MOUSE_DOWN__" || js == "__WEBVIEW_MOUSE_MOVE__" {
if !window.is_decorated() && window.is_resizable() && !window.is_maximized() {
use crate::application::{platform::windows::hit_test, window::CursorIcon};
use crate::application::window::CursorIcon;

let mut point = POINT::default();
win32wm::GetCursorPos(&mut point);
let result = hit_test(window.hwnd(), point.x, point.y);
let result = resize::hit_test(window.hwnd(), point.x, point.y);
let cursor = match result.0 as u32 {
win32wm::HTLEFT => CursorIcon::WResize,
win32wm::HTTOP => CursorIcon::NResize,
Expand All @@ -422,7 +421,13 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_
// we ignore `HTCLIENT` variant so the webview receives the click correctly if it is not on the edges
// and prevent conflict with `tao::window::drag_window`.
if result.0 as u32 != win32wm::HTCLIENT {
window.begin_resize_drag(result.0, win32wm::WM_NCLBUTTONDOWN, point.x, point.y);
resize::begin_resize_drag(
window.hwnd(),
result.0,
win32wm::WM_NCLBUTTONDOWN,
point.x,
point.y,
);
}
}
}
Expand Down Expand Up @@ -1096,11 +1101,16 @@ fn get_function_impl(library: &str, function: &str) -> Option<FARPROC> {

macro_rules! get_function {
($lib:expr, $func:ident) => {
get_function_impl(concat!($lib, '\0'), concat!(stringify!($func), '\0'))
.map(|f| unsafe { std::mem::transmute::<windows::Win32::Foundation::FARPROC, $func>(f) })
crate::webview::webview2::get_function_impl(
concat!($lib, '\0'),
concat!(stringify!($func), '\0'),
)
.map(|f| unsafe { std::mem::transmute::<windows::Win32::Foundation::FARPROC, $func>(f) })
};
}

pub(crate) use get_function;

/// Returns a tuple of (major, minor, buildnumber)
fn get_windows_ver() -> Option<(u32, u32, u32)> {
type RtlGetVersion = unsafe extern "system" fn(*mut OSVERSIONINFOW) -> i32;
Expand Down
149 changes: 149 additions & 0 deletions src/webview/webview2/resize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#![allow(non_snake_case)]

use once_cell::sync::Lazy;
use windows::{
core::HRESULT,
Win32::{
Foundation::{HWND, LPARAM, LRESULT, RECT, WPARAM},
Graphics::Gdi::{
GetDC, GetDeviceCaps, MonitorFromWindow, HMONITOR, LOGPIXELSX, MONITOR_DEFAULTTONEAREST,
},
UI::{
HiDpi::{MDT_EFFECTIVE_DPI, MONITOR_DPI_TYPE},
Input::KeyboardAndMouse::ReleaseCapture,
WindowsAndMessaging::{
GetWindowRect, IsProcessDPIAware, PostMessageW, HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT,
HTCLIENT, HTLEFT, HTNOWHERE, HTRIGHT, HTTOP, HTTOPLEFT, HTTOPRIGHT,
},
},
},
};

use super::get_function;

#[inline]
pub fn MAKELPARAM(x: i16, y: i16) -> LPARAM {
LPARAM(((x as u16 as u32) | ((y as u16 as u32) << 16)) as usize as _)
}

#[inline]
pub fn begin_resize_drag(hwnd: isize, edge: isize, button: u32, x: i32, y: i32) {
unsafe {
let w_param = WPARAM(edge as _);
let l_param = MAKELPARAM(x as i16, y as i16);

ReleaseCapture();
PostMessageW(HWND(hwnd), button, w_param, l_param);
}
}

type GetDpiForWindow = unsafe extern "system" fn(hwnd: HWND) -> u32;
type GetDpiForMonitor = unsafe extern "system" fn(
hmonitor: HMONITOR,
dpi_type: MONITOR_DPI_TYPE,
dpi_x: *mut u32,
dpi_y: *mut u32,
) -> HRESULT;

static GET_DPI_FOR_WINDOW: Lazy<Option<GetDpiForWindow>> =
Lazy::new(|| get_function!("user32.dll", GetDpiForWindow));
static GET_DPI_FOR_MONITOR: Lazy<Option<GetDpiForMonitor>> =
Lazy::new(|| get_function!("shcore.dll", GetDpiForMonitor));

const BASE_DPI: u32 = 96;
fn dpi_to_scale_factor(dpi: u32) -> f64 {
dpi as f64 / BASE_DPI as f64
}

unsafe fn hwnd_dpi(hwnd: HWND) -> u32 {
let hdc = GetDC(hwnd);
if hdc.is_invalid() {
panic!("[tao] `GetDC` returned null!");
}
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 = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if monitor.is_invalid() {
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).is_ok() {
dpi_x as u32
} else {
BASE_DPI
}
} else {
// We are on Vista or later.
if IsProcessDPIAware().as_bool() {
// 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
}
}
}

const BORDERLESS_RESIZE_INSET: i32 = 5;

pub fn hit_test(hwnd: isize, cx: i32, cy: i32) -> LRESULT {
let hwnd = HWND(hwnd);
let mut window_rect = RECT::default();
unsafe {
if GetWindowRect(hwnd, <*mut _>::cast(&mut window_rect)).as_bool() {
const CLIENT: isize = 0b0000;
const LEFT: isize = 0b0001;
const RIGHT: isize = 0b0010;
const TOP: isize = 0b0100;
const BOTTOM: isize = 0b1000;
const TOPLEFT: isize = TOP | LEFT;
const TOPRIGHT: isize = TOP | RIGHT;
const BOTTOMLEFT: isize = BOTTOM | LEFT;
const BOTTOMRIGHT: isize = BOTTOM | RIGHT;

let RECT {
left,
right,
bottom,
top,
} = window_rect;

let dpi = hwnd_dpi(hwnd);
let scale_factor = dpi_to_scale_factor(dpi);
let inset = (BORDERLESS_RESIZE_INSET as f64 * scale_factor) as i32;

#[rustfmt::skip]
let result =
(LEFT * (if cx < (left + inset) { 1 } else { 0 }))
| (RIGHT * (if cx >= (right - inset) { 1 } else { 0 }))
| (TOP * (if cy < (top + inset) { 1 } else { 0 }))
| (BOTTOM * (if cy >= (bottom - inset) { 1 } else { 0 }));

LRESULT(match result {
CLIENT => HTCLIENT,
LEFT => HTLEFT,
RIGHT => HTRIGHT,
TOP => HTTOP,
BOTTOM => HTBOTTOM,
TOPLEFT => HTTOPLEFT,
TOPRIGHT => HTTOPRIGHT,
BOTTOMLEFT => HTBOTTOMLEFT,
BOTTOMRIGHT => HTBOTTOMRIGHT,
_ => HTNOWHERE,
} as _)
} else {
LRESULT(HTNOWHERE as _)
}
}
}

0 comments on commit b062e94

Please sign in to comment.