mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-05-17 17:26:54 +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
|
},
|
||||||
.lock()
|
dpiX: 96.0,
|
||||||
.get(&(window.0 as isize))
|
dpiY: 96.0,
|
||||||
.copied()
|
..Default::default()
|
||||||
.unwrap_or(WindowKind::Unfocused)
|
};
|
||||||
|
|
||||||
|
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(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up the brush to draw the border
|
let pixel_size = D2D_SIZE_U {
|
||||||
let hpen = CreatePen(
|
width: rect.right as u32,
|
||||||
PS_SOLID | PS_INSIDEFRAME,
|
height: rect.bottom as u32,
|
||||||
BORDER_WIDTH.load(Ordering::SeqCst),
|
};
|
||||||
COLORREF(window_kind_colour(window_kind)),
|
|
||||||
);
|
|
||||||
|
|
||||||
let hbrush = WindowsApi::create_solid_brush(0);
|
let border_width = BORDER_WIDTH.load(Ordering::SeqCst);
|
||||||
|
let border_offset = BORDER_OFFSET.load(Ordering::SeqCst);
|
||||||
|
|
||||||
// Draw the border
|
let rect = D2D_RECT_F {
|
||||||
SelectObject(hdc, hpen);
|
left: (border_width / 2 - border_offset) as f32,
|
||||||
SelectObject(hdc, hbrush);
|
top: (border_width / 2 - border_offset) as f32,
|
||||||
// TODO(raggi): this is approximately the correct curvature for
|
right: (rect.right - border_width / 2 + border_offset) as f32,
|
||||||
// the top left of a Windows 11 window (DWMWCP_DEFAULT), but
|
bottom: (rect.bottom - border_width / 2 + border_offset) as f32,
|
||||||
// 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
|
let _ = render_target.Resize(&pixel_size);
|
||||||
// of windows and attempt to match appropriately.
|
|
||||||
match STYLE.load() {
|
// Get window kind and color
|
||||||
BorderStyle::System => {
|
let window_kind = FOCUS_STATE
|
||||||
if *WINDOWS_11 {
|
.lock()
|
||||||
// TODO: error handling
|
.get(&(window.0 as isize))
|
||||||
let _ =
|
.copied()
|
||||||
RoundRect(hdc, 0, 0, rect.right, rect.bottom, 20, 20);
|
.unwrap_or(WindowKind::Unfocused);
|
||||||
} else {
|
|
||||||
// TODO: error handling
|
let color = window_kind_colour(window_kind);
|
||||||
let _ = Rectangle(hdc, 0, 0, rect.right, rect.bottom);
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(brush) =
|
||||||
|
render_target.CreateSolidColorBrush(&color, Some(&brush_properties))
|
||||||
|
{
|
||||||
|
// Calculate border radius based on style
|
||||||
|
let style = STYLE.load();
|
||||||
|
let radius = match style {
|
||||||
|
BorderStyle::System => {
|
||||||
|
if *WINDOWS_11 {
|
||||||
|
10.0
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
BorderStyle::Rounded => 10.0,
|
||||||
|
BorderStyle::Square => 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
10.0 => {
|
||||||
|
let rounded_rect = D2D1_ROUNDED_RECT {
|
||||||
|
rect,
|
||||||
|
radiusX: radius,
|
||||||
|
radiusY: radius,
|
||||||
|
};
|
||||||
|
|
||||||
|
render_target.DrawRoundedRectangle(
|
||||||
|
&rounded_rect,
|
||||||
|
&brush,
|
||||||
|
border_width as f32,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
BorderStyle::Rounded => {
|
|
||||||
// TODO: error handling
|
let _ = render_target.EndDraw(None, None);
|
||||||
let _ = RoundRect(hdc, 0, 0, rect.right, rect.bottom, 20, 20);
|
|
||||||
}
|
|
||||||
BorderStyle::Square => {
|
|
||||||
// TODO: error handling
|
|
||||||
let _ = Rectangle(hdc, 0, 0, rect.right, rect.bottom);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// TODO: error handling
|
|
||||||
let _ = DeleteObject(hpen);
|
|
||||||
// TODO: error handling
|
|
||||||
let _ = DeleteObject(hbrush);
|
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
tracing::error!("could not get border rect: {}", error.to_string())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: error handling
|
|
||||||
let _ = EndPaint(window, &ps);
|
|
||||||
LRESULT(0)
|
LRESULT(0)
|
||||||
}
|
}
|
||||||
WM_DESTROY => {
|
WM_DESTROY => {
|
||||||
|
|||||||
Reference in New Issue
Block a user