mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-05-19 10:16:59 +02:00
feat(borders): initial impl of direct2d border drawing
Not sure what I'm doing wrong but this is super slow to update on window dragging and an absolute resource hog.
This commit is contained in:
@@ -44,11 +44,15 @@ which = "7"
|
|||||||
version = "0.58"
|
version = "0.58"
|
||||||
features = [
|
features = [
|
||||||
"implement",
|
"implement",
|
||||||
|
"Foundation_Numerics",
|
||||||
"Win32_System_Com",
|
"Win32_System_Com",
|
||||||
"Win32_UI_Shell_Common", # for IObjectArray
|
"Win32_UI_Shell_Common", # for IObjectArray
|
||||||
"Win32_Foundation",
|
"Win32_Foundation",
|
||||||
"Win32_Graphics_Dwm",
|
"Win32_Graphics_Dwm",
|
||||||
"Win32_Graphics_Gdi",
|
"Win32_Graphics_Gdi",
|
||||||
|
"Win32_Graphics_Direct2D",
|
||||||
|
"Win32_Graphics_Direct2D_Common",
|
||||||
|
"Win32_Graphics_Dxgi_Common",
|
||||||
"Win32_System_LibraryLoader",
|
"Win32_System_LibraryLoader",
|
||||||
"Win32_System_RemoteDesktop",
|
"Win32_System_RemoteDesktop",
|
||||||
"Win32_System_Threading",
|
"Win32_System_Threading",
|
||||||
|
|||||||
@@ -5,34 +5,38 @@ use crate::border_manager::BORDER_WIDTH;
|
|||||||
use crate::border_manager::FOCUS_STATE;
|
use crate::border_manager::FOCUS_STATE;
|
||||||
use crate::border_manager::STYLE;
|
use crate::border_manager::STYLE;
|
||||||
use crate::border_manager::Z_ORDER;
|
use crate::border_manager::Z_ORDER;
|
||||||
|
use crate::core::BorderStyle;
|
||||||
|
use crate::core::Rect;
|
||||||
use crate::windows_api;
|
use crate::windows_api;
|
||||||
use crate::WindowsApi;
|
use crate::WindowsApi;
|
||||||
use crate::WINDOWS_11;
|
use crate::WINDOWS_11;
|
||||||
|
|
||||||
use crate::core::BorderStyle;
|
|
||||||
use crate::core::Rect;
|
|
||||||
|
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
use std::sync::LazyLock;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use windows::core::PCWSTR;
|
use windows::Foundation::Numerics::Matrix3x2;
|
||||||
use windows::Win32::Foundation::BOOL;
|
use windows::Win32::Foundation::BOOL;
|
||||||
use windows::Win32::Foundation::COLORREF;
|
|
||||||
use windows::Win32::Foundation::HWND;
|
use windows::Win32::Foundation::HWND;
|
||||||
use windows::Win32::Foundation::LPARAM;
|
use windows::Win32::Foundation::LPARAM;
|
||||||
use windows::Win32::Foundation::LRESULT;
|
use windows::Win32::Foundation::LRESULT;
|
||||||
use windows::Win32::Foundation::WPARAM;
|
use windows::Win32::Foundation::WPARAM;
|
||||||
use windows::Win32::Graphics::Gdi::BeginPaint;
|
use windows::Win32::Graphics::Direct2D::Common::D2D1_ALPHA_MODE_PREMULTIPLIED;
|
||||||
use windows::Win32::Graphics::Gdi::CreatePen;
|
use windows::Win32::Graphics::Direct2D::Common::D2D1_COLOR_F;
|
||||||
use windows::Win32::Graphics::Gdi::DeleteObject;
|
use windows::Win32::Graphics::Direct2D::Common::D2D1_PIXEL_FORMAT;
|
||||||
use windows::Win32::Graphics::Gdi::EndPaint;
|
use windows::Win32::Graphics::Direct2D::Common::D2D_RECT_F;
|
||||||
|
use windows::Win32::Graphics::Direct2D::Common::D2D_SIZE_U;
|
||||||
|
use windows::Win32::Graphics::Direct2D::D2D1CreateFactory;
|
||||||
|
use windows::Win32::Graphics::Direct2D::ID2D1Factory;
|
||||||
|
use windows::Win32::Graphics::Direct2D::D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
|
||||||
|
use windows::Win32::Graphics::Direct2D::D2D1_BRUSH_PROPERTIES;
|
||||||
|
use windows::Win32::Graphics::Direct2D::D2D1_FACTORY_TYPE_MULTI_THREADED;
|
||||||
|
use windows::Win32::Graphics::Direct2D::D2D1_HWND_RENDER_TARGET_PROPERTIES;
|
||||||
|
use windows::Win32::Graphics::Direct2D::D2D1_PRESENT_OPTIONS_IMMEDIATELY;
|
||||||
|
use windows::Win32::Graphics::Direct2D::D2D1_RENDER_TARGET_PROPERTIES;
|
||||||
|
use windows::Win32::Graphics::Direct2D::D2D1_RENDER_TARGET_TYPE_DEFAULT;
|
||||||
|
use windows::Win32::Graphics::Direct2D::D2D1_ROUNDED_RECT;
|
||||||
|
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_UNKNOWN;
|
||||||
use windows::Win32::Graphics::Gdi::InvalidateRect;
|
use windows::Win32::Graphics::Gdi::InvalidateRect;
|
||||||
use windows::Win32::Graphics::Gdi::Rectangle;
|
|
||||||
use windows::Win32::Graphics::Gdi::RoundRect;
|
|
||||||
use windows::Win32::Graphics::Gdi::SelectObject;
|
|
||||||
use windows::Win32::Graphics::Gdi::PAINTSTRUCT;
|
|
||||||
use windows::Win32::Graphics::Gdi::PS_INSIDEFRAME;
|
|
||||||
use windows::Win32::Graphics::Gdi::PS_SOLID;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
|
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
|
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
|
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
|
||||||
@@ -44,6 +48,15 @@ use windows::Win32::UI::WindowsAndMessaging::MSG;
|
|||||||
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
|
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WM_PAINT;
|
use windows::Win32::UI::WindowsAndMessaging::WM_PAINT;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
|
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
|
||||||
|
use windows_core::PCWSTR;
|
||||||
|
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
pub static RENDER_FACTORY: LazyLock<ID2D1Factory> = unsafe {
|
||||||
|
LazyLock::new(|| {
|
||||||
|
D2D1CreateFactory::<ID2D1Factory>(D2D1_FACTORY_TYPE_MULTI_THREADED, None)
|
||||||
|
.expect("creating RENDER_FACTORY failed")
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
pub extern "system" fn border_hwnds(hwnd: HWND, lparam: LPARAM) -> BOOL {
|
pub extern "system" fn border_hwnds(hwnd: HWND, lparam: LPARAM) -> BOOL {
|
||||||
let hwnds = unsafe { &mut *(lparam.0 as *mut Vec<isize>) };
|
let hwnds = unsafe { &mut *(lparam.0 as *mut Vec<isize>) };
|
||||||
@@ -159,71 +172,124 @@ impl Border {
|
|||||||
unsafe {
|
unsafe {
|
||||||
match message {
|
match message {
|
||||||
WM_PAINT => {
|
WM_PAINT => {
|
||||||
let mut ps = PAINTSTRUCT::default();
|
if let Ok(rect) = WindowsApi::window_rect(window.0 as isize) {
|
||||||
let hdc = BeginPaint(window, &mut ps);
|
let hwnd_render_target_properties = D2D1_HWND_RENDER_TARGET_PROPERTIES {
|
||||||
|
hwnd: window,
|
||||||
|
pixelSize: Default::default(),
|
||||||
|
presentOptions: D2D1_PRESENT_OPTIONS_IMMEDIATELY,
|
||||||
|
};
|
||||||
|
|
||||||
// With the rect that we set in Self::update
|
let render_target_properties = D2D1_RENDER_TARGET_PROPERTIES {
|
||||||
match WindowsApi::window_rect(window.0 as isize) {
|
r#type: D2D1_RENDER_TARGET_TYPE_DEFAULT,
|
||||||
Ok(rect) => {
|
pixelFormat: D2D1_PIXEL_FORMAT {
|
||||||
// Grab the focus kind for this border
|
format: DXGI_FORMAT_UNKNOWN,
|
||||||
let window_kind = {
|
alphaMode: D2D1_ALPHA_MODE_PREMULTIPLIED,
|
||||||
FOCUS_STATE
|
},
|
||||||
|
dpiX: 96.0,
|
||||||
|
dpiY: 96.0,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(render_target) = RENDER_FACTORY.CreateHwndRenderTarget(
|
||||||
|
&render_target_properties,
|
||||||
|
&hwnd_render_target_properties,
|
||||||
|
) {
|
||||||
|
render_target.SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
||||||
|
|
||||||
|
let brush_properties = D2D1_BRUSH_PROPERTIES {
|
||||||
|
opacity: 1.0,
|
||||||
|
transform: Matrix3x2::identity(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let pixel_size = D2D_SIZE_U {
|
||||||
|
width: rect.right as u32,
|
||||||
|
height: rect.bottom as u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
let border_width = BORDER_WIDTH.load(Ordering::SeqCst);
|
||||||
|
let border_offset = BORDER_OFFSET.load(Ordering::SeqCst);
|
||||||
|
|
||||||
|
let rect = D2D_RECT_F {
|
||||||
|
left: (border_width / 2 - border_offset) as f32,
|
||||||
|
top: (border_width / 2 - border_offset) as f32,
|
||||||
|
right: (rect.right - border_width / 2 + border_offset) as f32,
|
||||||
|
bottom: (rect.bottom - border_width / 2 + border_offset) as f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = render_target.Resize(&pixel_size);
|
||||||
|
|
||||||
|
// Get window kind and color
|
||||||
|
let window_kind = FOCUS_STATE
|
||||||
.lock()
|
.lock()
|
||||||
.get(&(window.0 as isize))
|
.get(&(window.0 as isize))
|
||||||
.copied()
|
.copied()
|
||||||
.unwrap_or(WindowKind::Unfocused)
|
.unwrap_or(WindowKind::Unfocused);
|
||||||
|
|
||||||
|
let color = window_kind_colour(window_kind);
|
||||||
|
let color = D2D1_COLOR_F {
|
||||||
|
r: ((color & 0xFF) as f32) / 255.0,
|
||||||
|
g: (((color >> 8) & 0xFF) as f32) / 255.0,
|
||||||
|
b: (((color >> 16) & 0xFF) as f32) / 255.0,
|
||||||
|
a: 1.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up the brush to draw the border
|
if let Ok(brush) =
|
||||||
let hpen = CreatePen(
|
render_target.CreateSolidColorBrush(&color, Some(&brush_properties))
|
||||||
PS_SOLID | PS_INSIDEFRAME,
|
{
|
||||||
BORDER_WIDTH.load(Ordering::SeqCst),
|
// Calculate border radius based on style
|
||||||
COLORREF(window_kind_colour(window_kind)),
|
let style = STYLE.load();
|
||||||
);
|
let radius = match style {
|
||||||
|
|
||||||
let hbrush = WindowsApi::create_solid_brush(0);
|
|
||||||
|
|
||||||
// Draw the border
|
|
||||||
SelectObject(hdc, hpen);
|
|
||||||
SelectObject(hdc, hbrush);
|
|
||||||
// TODO(raggi): this is approximately the correct curvature for
|
|
||||||
// the top left of a Windows 11 window (DWMWCP_DEFAULT), but
|
|
||||||
// often the bottom right has a different shape. Furthermore if
|
|
||||||
// the window was made with DWMWCP_ROUNDSMALL then this is the
|
|
||||||
// wrong size. In the future we should read the DWM properties
|
|
||||||
// of windows and attempt to match appropriately.
|
|
||||||
match STYLE.load() {
|
|
||||||
BorderStyle::System => {
|
BorderStyle::System => {
|
||||||
if *WINDOWS_11 {
|
if *WINDOWS_11 {
|
||||||
// TODO: error handling
|
10.0
|
||||||
let _ =
|
|
||||||
RoundRect(hdc, 0, 0, rect.right, rect.bottom, 20, 20);
|
|
||||||
} else {
|
} else {
|
||||||
// TODO: error handling
|
0.0
|
||||||
let _ = Rectangle(hdc, 0, 0, rect.right, rect.bottom);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BorderStyle::Rounded => {
|
BorderStyle::Rounded => 10.0,
|
||||||
// TODO: error handling
|
BorderStyle::Square => 0.0,
|
||||||
let _ = RoundRect(hdc, 0, 0, rect.right, rect.bottom, 20, 20);
|
};
|
||||||
|
|
||||||
|
render_target.BeginDraw();
|
||||||
|
render_target.Clear(None);
|
||||||
|
|
||||||
|
match radius {
|
||||||
|
0.0 => {
|
||||||
|
let rect = D2D_RECT_F {
|
||||||
|
left: rect.left,
|
||||||
|
top: rect.top,
|
||||||
|
right: rect.right,
|
||||||
|
bottom: rect.bottom,
|
||||||
|
};
|
||||||
|
|
||||||
|
render_target.DrawRectangle(
|
||||||
|
&rect,
|
||||||
|
&brush,
|
||||||
|
border_width as f32,
|
||||||
|
None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
BorderStyle::Square => {
|
10.0 => {
|
||||||
// TODO: error handling
|
let rounded_rect = D2D1_ROUNDED_RECT {
|
||||||
let _ = Rectangle(hdc, 0, 0, rect.right, rect.bottom);
|
rect,
|
||||||
}
|
radiusX: radius,
|
||||||
}
|
radiusY: radius,
|
||||||
// TODO: error handling
|
};
|
||||||
let _ = DeleteObject(hpen);
|
|
||||||
// TODO: error handling
|
render_target.DrawRoundedRectangle(
|
||||||
let _ = DeleteObject(hbrush);
|
&rounded_rect,
|
||||||
}
|
&brush,
|
||||||
Err(error) => {
|
border_width as f32,
|
||||||
tracing::error!("could not get border rect: {}", error.to_string())
|
None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: error handling
|
let _ = render_target.EndDraw(None, None);
|
||||||
let _ = EndPaint(window, &ps);
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
LRESULT(0)
|
LRESULT(0)
|
||||||
}
|
}
|
||||||
WM_DESTROY => {
|
WM_DESTROY => {
|
||||||
|
|||||||
Reference in New Issue
Block a user