mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-01-15 14:23:26 +01:00
Compare commits
1 Commits
feature/mo
...
feature/ta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67f590b5c3 |
@@ -5,16 +5,18 @@ use crate::border_manager::BORDER_WIDTH;
|
||||
use crate::border_manager::FOCUS_STATE;
|
||||
use crate::border_manager::RENDER_TARGETS;
|
||||
use crate::border_manager::STYLE;
|
||||
use crate::border_manager::Z_ORDER;
|
||||
use crate::core::BorderStyle;
|
||||
use crate::core::Rect;
|
||||
use crate::windows_api;
|
||||
use crate::WindowsApi;
|
||||
use crate::WINDOWS_11;
|
||||
use color_eyre::eyre::anyhow;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::LazyLock;
|
||||
use std::sync::OnceLock;
|
||||
use windows::Foundation::Numerics::Matrix3x2;
|
||||
use windows::Win32::Foundation::BOOL;
|
||||
use windows::Win32::Foundation::FALSE;
|
||||
@@ -30,6 +32,8 @@ 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::ID2D1HwndRenderTarget;
|
||||
use windows::Win32::Graphics::Direct2D::ID2D1SolidColorBrush;
|
||||
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;
|
||||
@@ -43,22 +47,26 @@ use windows::Win32::Graphics::Dwm::DWM_BB_BLURREGION;
|
||||
use windows::Win32::Graphics::Dwm::DWM_BB_ENABLE;
|
||||
use windows::Win32::Graphics::Dwm::DWM_BLURBEHIND;
|
||||
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_UNKNOWN;
|
||||
use windows::Win32::Graphics::Gdi::BeginPaint;
|
||||
use windows::Win32::Graphics::Gdi::CreateRectRgn;
|
||||
use windows::Win32::Graphics::Gdi::EndPaint;
|
||||
use windows::Win32::Graphics::Gdi::InvalidateRect;
|
||||
use windows::Win32::Graphics::Gdi::PAINTSTRUCT;
|
||||
use windows::Win32::Graphics::Gdi::ValidateRect;
|
||||
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetSystemMetrics;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetWindowLongPtrW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
|
||||
use windows::Win32::UI::WindowsAndMessaging::CREATESTRUCTW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DESTROY;
|
||||
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_LOCATIONCHANGE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GWLP_USERDATA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::MSG;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SM_CXVIRTUALSCREEN;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WM_CREATE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WM_PAINT;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WM_SIZE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
|
||||
use windows_core::PCWSTR;
|
||||
|
||||
@@ -89,14 +97,36 @@ pub extern "system" fn border_hwnds(hwnd: HWND, lparam: LPARAM) -> BOOL {
|
||||
true.into()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Border {
|
||||
pub hwnd: isize,
|
||||
pub render_target: OnceLock<ID2D1HwndRenderTarget>,
|
||||
pub tracking_hwnd: isize,
|
||||
pub window_rect: Rect,
|
||||
pub window_kind: WindowKind,
|
||||
pub style: BorderStyle,
|
||||
pub width: i32,
|
||||
pub offset: i32,
|
||||
pub brush_properties: D2D1_BRUSH_PROPERTIES,
|
||||
pub rounded_rect: D2D1_ROUNDED_RECT,
|
||||
pub brushes: HashMap<WindowKind, ID2D1SolidColorBrush>,
|
||||
}
|
||||
|
||||
impl From<isize> for Border {
|
||||
fn from(value: isize) -> Self {
|
||||
Self { hwnd: value }
|
||||
Self {
|
||||
hwnd: value,
|
||||
render_target: OnceLock::new(),
|
||||
tracking_hwnd: 0,
|
||||
window_rect: Rect::default(),
|
||||
window_kind: WindowKind::Unfocused,
|
||||
style: STYLE.load(),
|
||||
width: BORDER_WIDTH.load(Ordering::Relaxed),
|
||||
offset: BORDER_OFFSET.load(Ordering::Relaxed),
|
||||
brush_properties: D2D1_BRUSH_PROPERTIES::default(),
|
||||
rounded_rect: D2D1_ROUNDED_RECT::default(),
|
||||
brushes: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +135,7 @@ impl Border {
|
||||
HWND(windows_api::as_ptr!(self.hwnd))
|
||||
}
|
||||
|
||||
pub fn create(id: &str) -> color_eyre::Result<Self> {
|
||||
pub fn create(id: &str, tracking_hwnd: isize) -> color_eyre::Result<Self> {
|
||||
let name: Vec<u16> = format!("komoborder-{id}\0").encode_utf16().collect();
|
||||
let class_name = PCWSTR(name.as_ptr());
|
||||
|
||||
@@ -121,12 +151,30 @@ impl Border {
|
||||
|
||||
let _ = WindowsApi::register_class_w(&window_class);
|
||||
|
||||
let (hwnd_sender, hwnd_receiver) = mpsc::channel();
|
||||
let (border_sender, border_receiver) = mpsc::channel();
|
||||
|
||||
let instance = h_module.0 as isize;
|
||||
std::thread::spawn(move || -> color_eyre::Result<()> {
|
||||
let hwnd = WindowsApi::create_border_window(PCWSTR(name.as_ptr()), instance)?;
|
||||
hwnd_sender.send(hwnd)?;
|
||||
let mut border = Self {
|
||||
hwnd: 0,
|
||||
render_target: OnceLock::new(),
|
||||
tracking_hwnd,
|
||||
window_rect: WindowsApi::window_rect(tracking_hwnd).unwrap_or_default(),
|
||||
window_kind: WindowKind::Unfocused,
|
||||
style: STYLE.load(),
|
||||
width: BORDER_WIDTH.load(Ordering::Relaxed),
|
||||
offset: BORDER_OFFSET.load(Ordering::Relaxed),
|
||||
brush_properties: Default::default(),
|
||||
rounded_rect: Default::default(),
|
||||
brushes: HashMap::new(),
|
||||
};
|
||||
|
||||
let border_pointer = std::ptr::addr_of!(border);
|
||||
let hwnd =
|
||||
WindowsApi::create_border_window(PCWSTR(name.as_ptr()), instance, border_pointer)?;
|
||||
|
||||
border.hwnd = hwnd;
|
||||
border_sender.send(border_pointer as isize)?;
|
||||
|
||||
let mut msg: MSG = MSG::default();
|
||||
|
||||
@@ -145,8 +193,8 @@ impl Border {
|
||||
Ok(())
|
||||
});
|
||||
|
||||
let hwnd = hwnd_receiver.recv()?;
|
||||
let border = Self { hwnd };
|
||||
let border_ref = border_receiver.recv()?;
|
||||
let border = unsafe { &mut *(border_ref as *mut Border) };
|
||||
|
||||
// I have literally no idea, apparently this is to get rid of the black pixels
|
||||
// around the edges of rounded corners? @lukeyou05 borrowed this from PowerToys
|
||||
@@ -167,7 +215,7 @@ impl Border {
|
||||
}
|
||||
|
||||
let hwnd_render_target_properties = D2D1_HWND_RENDER_TARGET_PROPERTIES {
|
||||
hwnd: HWND(windows_api::as_ptr!(hwnd)),
|
||||
hwnd: HWND(windows_api::as_ptr!(border.hwnd)),
|
||||
pixelSize: Default::default(),
|
||||
presentOptions: D2D1_PRESENT_OPTIONS_IMMEDIATELY,
|
||||
};
|
||||
@@ -188,10 +236,47 @@ impl Border {
|
||||
.CreateHwndRenderTarget(&render_target_properties, &hwnd_render_target_properties)
|
||||
} {
|
||||
Ok(render_target) => unsafe {
|
||||
border.brush_properties = *BRUSH_PROPERTIES.deref();
|
||||
for window_kind in [
|
||||
WindowKind::Single,
|
||||
WindowKind::Stack,
|
||||
WindowKind::Monocle,
|
||||
WindowKind::Unfocused,
|
||||
WindowKind::Floating,
|
||||
] {
|
||||
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,
|
||||
};
|
||||
|
||||
if let Ok(brush) =
|
||||
render_target.CreateSolidColorBrush(&color, Some(&border.brush_properties))
|
||||
{
|
||||
border.brushes.insert(window_kind, brush);
|
||||
}
|
||||
}
|
||||
|
||||
render_target.SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
||||
|
||||
if border.render_target.set(render_target.clone()).is_err() {
|
||||
return Err(anyhow!("could not store border render target"));
|
||||
}
|
||||
|
||||
border.rounded_rect = {
|
||||
let radius = 8.0 + border.width as f32 / 2.0;
|
||||
D2D1_ROUNDED_RECT {
|
||||
rect: Default::default(),
|
||||
radiusX: radius,
|
||||
radiusY: radius,
|
||||
}
|
||||
};
|
||||
|
||||
let mut render_targets = RENDER_TARGETS.lock();
|
||||
render_targets.insert(hwnd, render_target);
|
||||
Ok(border)
|
||||
render_targets.insert(border.hwnd, render_target);
|
||||
Ok(border.clone())
|
||||
},
|
||||
Err(error) => Err(error.into()),
|
||||
}
|
||||
@@ -203,27 +288,17 @@ impl Border {
|
||||
WindowsApi::close_window(self.hwnd)
|
||||
}
|
||||
|
||||
pub fn update(&self, rect: &Rect, should_invalidate: bool) -> color_eyre::Result<()> {
|
||||
// Make adjustments to the border
|
||||
pub fn set_position(&self, rect: &Rect, reference_hwnd: isize) -> color_eyre::Result<()> {
|
||||
let mut rect = *rect;
|
||||
rect.add_margin(BORDER_WIDTH.load(Ordering::Relaxed));
|
||||
rect.add_padding(-BORDER_OFFSET.load(Ordering::Relaxed));
|
||||
rect.add_margin(self.width);
|
||||
rect.add_padding(-self.offset);
|
||||
|
||||
// Update the position of the border if required
|
||||
// This effectively handles WM_MOVE
|
||||
// Also if I remove this no borders render at all lol
|
||||
if !WindowsApi::window_rect(self.hwnd)?.eq(&rect) {
|
||||
WindowsApi::set_border_pos(self.hwnd, &rect, Z_ORDER.load().into())?;
|
||||
}
|
||||
|
||||
// Invalidate the rect to trigger the callback to update colours etc.
|
||||
if should_invalidate {
|
||||
self.invalidate();
|
||||
}
|
||||
WindowsApi::set_border_pos(self.hwnd, &rect, reference_hwnd)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// this triggers WM_PAINT in the callback below
|
||||
pub fn invalidate(&self) {
|
||||
let _ = unsafe { InvalidateRect(self.hwnd(), None, false) };
|
||||
}
|
||||
@@ -236,50 +311,73 @@ impl Border {
|
||||
) -> LRESULT {
|
||||
unsafe {
|
||||
match message {
|
||||
WM_SIZE | WM_PAINT => {
|
||||
if let Ok(rect) = WindowsApi::window_rect(window.0 as isize) {
|
||||
let render_targets = RENDER_TARGETS.lock();
|
||||
if let Some(render_target) = render_targets.get(&(window.0 as isize)) {
|
||||
let pixel_size = D2D_SIZE_U {
|
||||
width: rect.right as u32,
|
||||
height: rect.bottom as u32,
|
||||
};
|
||||
WM_CREATE => {
|
||||
let mut border_pointer: *mut Border =
|
||||
GetWindowLongPtrW(window, GWLP_USERDATA) as _;
|
||||
|
||||
let border_width = BORDER_WIDTH.load(Ordering::SeqCst);
|
||||
let border_offset = BORDER_OFFSET.load(Ordering::SeqCst);
|
||||
if border_pointer.is_null() {
|
||||
let create_struct: *mut CREATESTRUCTW = lparam.0 as *mut _;
|
||||
border_pointer = (*create_struct).lpCreateParams as *mut _;
|
||||
SetWindowLongPtrW(window, GWLP_USERDATA, border_pointer as _);
|
||||
}
|
||||
|
||||
let rect = D2D_RECT_F {
|
||||
LRESULT(0)
|
||||
}
|
||||
EVENT_OBJECT_DESTROY => {
|
||||
let border_pointer: *mut Border = GetWindowLongPtrW(window, GWLP_USERDATA) as _;
|
||||
|
||||
if border_pointer.is_null() {
|
||||
return LRESULT(0);
|
||||
}
|
||||
|
||||
// we don't actually want to destroy the window here, just hide it for quicker
|
||||
// visual feedback to the user; the actual destruction will be handled by the
|
||||
// core border manager loop
|
||||
WindowsApi::hide_window(window.0 as isize);
|
||||
LRESULT(0)
|
||||
}
|
||||
EVENT_OBJECT_LOCATIONCHANGE => {
|
||||
let border_pointer: *mut Border = GetWindowLongPtrW(window, GWLP_USERDATA) as _;
|
||||
|
||||
if border_pointer.is_null() {
|
||||
return LRESULT(0);
|
||||
}
|
||||
|
||||
let reference_hwnd = lparam.0;
|
||||
|
||||
let old_rect = (*border_pointer).window_rect;
|
||||
let rect = WindowsApi::window_rect(reference_hwnd).unwrap_or_default();
|
||||
|
||||
(*border_pointer).window_rect = rect;
|
||||
|
||||
if let Err(error) = (*border_pointer).set_position(&rect, reference_hwnd) {
|
||||
tracing::error!("failed to update border position {error}");
|
||||
}
|
||||
|
||||
if !rect.is_same_size_as(&old_rect) {
|
||||
if let Some(render_target) = (*border_pointer).render_target.get() {
|
||||
let border_width = (*border_pointer).width;
|
||||
let border_offset = (*border_pointer).offset;
|
||||
|
||||
(*border_pointer).rounded_rect.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);
|
||||
let _ = render_target.Resize(&D2D_SIZE_U {
|
||||
width: rect.right as u32,
|
||||
height: rect.bottom as u32,
|
||||
});
|
||||
|
||||
// Get window kind and color
|
||||
let window_kind = FOCUS_STATE
|
||||
.lock()
|
||||
.get(&(window.0 as isize))
|
||||
.copied()
|
||||
.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,
|
||||
};
|
||||
|
||||
if let Ok(brush) = render_target
|
||||
.CreateSolidColorBrush(&color, Some(BRUSH_PROPERTIES.deref()))
|
||||
{
|
||||
let window_kind = (*border_pointer).window_kind;
|
||||
if let Some(brush) = (*border_pointer).brushes.get(&window_kind) {
|
||||
render_target.BeginDraw();
|
||||
render_target.Clear(None);
|
||||
|
||||
// Calculate border radius based on style
|
||||
let style = match STYLE.load() {
|
||||
let style = match (*border_pointer).style {
|
||||
BorderStyle::System => {
|
||||
if *WINDOWS_11 {
|
||||
BorderStyle::Rounded
|
||||
@@ -293,31 +391,17 @@ impl Border {
|
||||
|
||||
match style {
|
||||
BorderStyle::Rounded => {
|
||||
let radius = 8.0 + border_width as f32 / 2.0;
|
||||
let rounded_rect = D2D1_ROUNDED_RECT {
|
||||
rect,
|
||||
radiusX: radius,
|
||||
radiusY: radius,
|
||||
};
|
||||
|
||||
render_target.DrawRoundedRectangle(
|
||||
&rounded_rect,
|
||||
&brush,
|
||||
&(*border_pointer).rounded_rect,
|
||||
brush,
|
||||
border_width as f32,
|
||||
None,
|
||||
);
|
||||
}
|
||||
BorderStyle::Square => {
|
||||
let rect = D2D_RECT_F {
|
||||
left: rect.left,
|
||||
top: rect.top,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
};
|
||||
|
||||
render_target.DrawRectangle(
|
||||
&rect,
|
||||
&brush,
|
||||
&(*border_pointer).rounded_rect.rect,
|
||||
brush,
|
||||
border_width as f32,
|
||||
None,
|
||||
);
|
||||
@@ -326,17 +410,97 @@ impl Border {
|
||||
}
|
||||
|
||||
let _ = render_target.EndDraw(None, None);
|
||||
|
||||
// If we don't do this we'll get spammed with WM_PAINT according to Raymond Chen
|
||||
// https://stackoverflow.com/questions/41783234/why-does-my-call-to-d2d1rendertargetdrawtext-result-in-a-wm-paint-being-se#comment70756781_41783234
|
||||
let _ = BeginPaint(window, &mut PAINTSTRUCT::default());
|
||||
let _ = EndPaint(window, &PAINTSTRUCT::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT(0)
|
||||
}
|
||||
WM_PAINT => {
|
||||
if let Ok(rect) = WindowsApi::window_rect(window.0 as isize) {
|
||||
let border_pointer: *mut Border =
|
||||
GetWindowLongPtrW(window, GWLP_USERDATA) as _;
|
||||
|
||||
if border_pointer.is_null() {
|
||||
return LRESULT(0);
|
||||
}
|
||||
|
||||
if let Some(render_target) = (*border_pointer).render_target.get() {
|
||||
(*border_pointer).width = BORDER_WIDTH.load(Ordering::Relaxed);
|
||||
(*border_pointer).offset = BORDER_OFFSET.load(Ordering::Relaxed);
|
||||
|
||||
let border_width = (*border_pointer).width;
|
||||
let border_offset = (*border_pointer).offset;
|
||||
|
||||
(*border_pointer).rounded_rect.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(&D2D_SIZE_U {
|
||||
width: rect.right as u32,
|
||||
height: rect.bottom as u32,
|
||||
});
|
||||
|
||||
// Get window kind and color
|
||||
|
||||
(*border_pointer).window_kind = FOCUS_STATE
|
||||
.lock()
|
||||
.get(&(window.0 as isize))
|
||||
.copied()
|
||||
.unwrap_or(WindowKind::Unfocused);
|
||||
|
||||
let window_kind = (*border_pointer).window_kind;
|
||||
if let Some(brush) = (*border_pointer).brushes.get(&window_kind) {
|
||||
render_target.BeginDraw();
|
||||
render_target.Clear(None);
|
||||
|
||||
(*border_pointer).style = STYLE.load();
|
||||
|
||||
// Calculate border radius based on style
|
||||
let style = match (*border_pointer).style {
|
||||
BorderStyle::System => {
|
||||
if *WINDOWS_11 {
|
||||
BorderStyle::Rounded
|
||||
} else {
|
||||
BorderStyle::Square
|
||||
}
|
||||
}
|
||||
BorderStyle::Rounded => BorderStyle::Rounded,
|
||||
BorderStyle::Square => BorderStyle::Square,
|
||||
};
|
||||
|
||||
match style {
|
||||
BorderStyle::Rounded => {
|
||||
render_target.DrawRoundedRectangle(
|
||||
&(*border_pointer).rounded_rect,
|
||||
brush,
|
||||
border_width as f32,
|
||||
None,
|
||||
);
|
||||
}
|
||||
BorderStyle::Square => {
|
||||
render_target.DrawRectangle(
|
||||
&(*border_pointer).rounded_rect.rect,
|
||||
brush,
|
||||
border_width as f32,
|
||||
None,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let _ = render_target.EndDraw(None, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = ValidateRect(window, None);
|
||||
LRESULT(0)
|
||||
}
|
||||
WM_DESTROY => {
|
||||
SetWindowLongPtrW(window, GWLP_USERDATA, 0);
|
||||
PostQuitMessage(0);
|
||||
LRESULT(0)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::Rgb;
|
||||
use crate::WindowManager;
|
||||
use crate::WindowsApi;
|
||||
use border::border_hwnds;
|
||||
use border::Border;
|
||||
pub use border::Border;
|
||||
use crossbeam_channel::Receiver;
|
||||
use crossbeam_channel::Sender;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
@@ -30,18 +30,13 @@ use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::sync::OnceLock;
|
||||
use windows::Win32::Graphics::Direct2D::ID2D1HwndRenderTarget;
|
||||
use windows::Win32::System::Threading::GetCurrentThread;
|
||||
use windows::Win32::System::Threading::SetThreadPriority;
|
||||
use windows::Win32::System::Threading::THREAD_PRIORITY_TIME_CRITICAL;
|
||||
|
||||
pub static BORDER_WIDTH: AtomicI32 = AtomicI32::new(8);
|
||||
pub static BORDER_OFFSET: AtomicI32 = AtomicI32::new(-1);
|
||||
|
||||
pub static BORDER_ENABLED: AtomicBool = AtomicBool::new(true);
|
||||
pub static BORDER_TEMPORARILY_DISABLED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
lazy_static! {
|
||||
pub static ref Z_ORDER: AtomicCell<ZOrder> = AtomicCell::new(ZOrder::Bottom);
|
||||
pub static ref STYLE: AtomicCell<BorderStyle> = AtomicCell::new(BorderStyle::System);
|
||||
pub static ref IMPLEMENTATION: AtomicCell<BorderImplementation> =
|
||||
AtomicCell::new(BorderImplementation::Komorebi);
|
||||
@@ -59,6 +54,7 @@ lazy_static! {
|
||||
lazy_static! {
|
||||
static ref BORDERS_MONITORS: Mutex<HashMap<String, usize>> = Mutex::new(HashMap::new());
|
||||
static ref BORDER_STATE: Mutex<HashMap<String, Border>> = Mutex::new(HashMap::new());
|
||||
static ref WINDOWS_BORDERS: Mutex<HashMap<isize, Border>> = Mutex::new(HashMap::new());
|
||||
static ref FOCUS_STATE: Mutex<HashMap<isize, WindowKind>> = Mutex::new(HashMap::new());
|
||||
static ref RENDER_TARGETS: Mutex<HashMap<isize, ID2D1HwndRenderTarget>> =
|
||||
Mutex::new(HashMap::new());
|
||||
@@ -80,6 +76,10 @@ fn event_rx() -> Receiver<Notification> {
|
||||
channel().1.clone()
|
||||
}
|
||||
|
||||
pub fn window_border(hwnd: isize) -> Option<Border> {
|
||||
WINDOWS_BORDERS.lock().get(&hwnd).cloned()
|
||||
}
|
||||
|
||||
pub fn send_notification(hwnd: Option<isize>) {
|
||||
if event_tx().try_send(Notification(hwnd)).is_err() {
|
||||
tracing::warn!("channel is full; dropping notification")
|
||||
@@ -122,31 +122,22 @@ pub fn destroy_all_borders() -> color_eyre::Result<()> {
|
||||
|
||||
fn window_kind_colour(focus_kind: WindowKind) -> u32 {
|
||||
match focus_kind {
|
||||
WindowKind::Unfocused => UNFOCUSED.load(Ordering::SeqCst),
|
||||
WindowKind::Single => FOCUSED.load(Ordering::SeqCst),
|
||||
WindowKind::Stack => STACK.load(Ordering::SeqCst),
|
||||
WindowKind::Monocle => MONOCLE.load(Ordering::SeqCst),
|
||||
WindowKind::Floating => FLOATING.load(Ordering::SeqCst),
|
||||
WindowKind::Unfocused => UNFOCUSED.load(Ordering::Relaxed),
|
||||
WindowKind::Single => FOCUSED.load(Ordering::Relaxed),
|
||||
WindowKind::Stack => STACK.load(Ordering::Relaxed),
|
||||
WindowKind::Monocle => MONOCLE.load(Ordering::Relaxed),
|
||||
WindowKind::Floating => FLOATING.load(Ordering::Relaxed),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
|
||||
std::thread::spawn(move || {
|
||||
unsafe {
|
||||
if let Err(error) = SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)
|
||||
{
|
||||
tracing::error!("{error}");
|
||||
std::thread::spawn(move || loop {
|
||||
match handle_notifications(wm.clone()) {
|
||||
Ok(()) => {
|
||||
tracing::warn!("restarting finished thread");
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
match handle_notifications(wm.clone()) {
|
||||
Ok(()) => {
|
||||
tracing::warn!("restarting finished thread");
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::warn!("restarting failed thread: {}", error);
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::warn!("restarting failed thread: {}", error);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -155,7 +146,6 @@ pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
|
||||
pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result<()> {
|
||||
tracing::info!("listening");
|
||||
|
||||
BORDER_TEMPORARILY_DISABLED.store(false, Ordering::SeqCst);
|
||||
let receiver = event_rx();
|
||||
event_tx().send(Notification(None))?;
|
||||
|
||||
@@ -172,7 +162,6 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
let focused_workspace_idx =
|
||||
state.monitors.elements()[focused_monitor_idx].focused_workspace_idx();
|
||||
let monitors = state.monitors.clone();
|
||||
let weak_pending_move_op = Arc::downgrade(&state.pending_move_op);
|
||||
let pending_move_op = *state.pending_move_op;
|
||||
let floating_window_hwnds = state.monitors.elements()[focused_monitor_idx].workspaces()
|
||||
[focused_workspace_idx]
|
||||
@@ -271,11 +260,10 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
|
||||
let mut borders = BORDER_STATE.lock();
|
||||
let mut borders_monitors = BORDERS_MONITORS.lock();
|
||||
let mut windows_borders = WINDOWS_BORDERS.lock();
|
||||
|
||||
// If borders are disabled
|
||||
if !BORDER_ENABLED.load_consume()
|
||||
// Or if they are temporarily disabled
|
||||
|| BORDER_TEMPORARILY_DISABLED.load(Ordering::SeqCst)
|
||||
// Or if the wm is paused
|
||||
|| is_paused
|
||||
// Or if we are handling an alt-tab across workspaces
|
||||
@@ -287,6 +275,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
}
|
||||
|
||||
borders.clear();
|
||||
windows_borders.clear();
|
||||
|
||||
previous_is_paused = is_paused;
|
||||
continue 'receiver;
|
||||
@@ -316,10 +305,15 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
|
||||
// Handle the monocle container separately
|
||||
if let Some(monocle) = ws.monocle_container() {
|
||||
let mut new_border = false;
|
||||
let border = match borders.entry(monocle.id().clone()) {
|
||||
Entry::Occupied(entry) => entry.into_mut(),
|
||||
Entry::Vacant(entry) => {
|
||||
if let Ok(border) = Border::create(monocle.id()) {
|
||||
if let Ok(border) = Border::create(
|
||||
monocle.id(),
|
||||
monocle.focused_window().copied().unwrap_or_default().hwnd,
|
||||
) {
|
||||
new_border = true;
|
||||
entry.insert(border)
|
||||
} else {
|
||||
continue 'monitors;
|
||||
@@ -328,6 +322,10 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
};
|
||||
|
||||
borders_monitors.insert(monocle.id().clone(), monitor_idx);
|
||||
windows_borders.insert(
|
||||
monocle.focused_window().cloned().unwrap_or_default().hwnd,
|
||||
border.clone(),
|
||||
);
|
||||
|
||||
{
|
||||
let mut focus_state = FOCUS_STATE.lock();
|
||||
@@ -341,11 +339,16 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
);
|
||||
}
|
||||
|
||||
let rect = WindowsApi::window_rect(
|
||||
monocle.focused_window().copied().unwrap_or_default().hwnd,
|
||||
)?;
|
||||
let reference_hwnd =
|
||||
monocle.focused_window().copied().unwrap_or_default().hwnd;
|
||||
|
||||
border.update(&rect, true)?;
|
||||
let rect = WindowsApi::window_rect(reference_hwnd)?;
|
||||
|
||||
if new_border {
|
||||
border.set_position(&rect, reference_hwnd)?;
|
||||
}
|
||||
|
||||
border.invalidate();
|
||||
|
||||
let border_hwnd = border.hwnd;
|
||||
let mut to_remove = vec![];
|
||||
@@ -414,65 +417,16 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
}
|
||||
|
||||
for (idx, c) in ws.containers().iter().enumerate() {
|
||||
let hwnd = c.focused_window().copied().unwrap_or_default().hwnd;
|
||||
let notification_hwnd = notification.0.unwrap_or_default();
|
||||
|
||||
// Update border when moving or resizing with mouse
|
||||
if pending_move_op.is_some()
|
||||
&& idx == ws.focused_container_idx()
|
||||
&& hwnd == notification_hwnd
|
||||
{
|
||||
let restore_z_order = Z_ORDER.load();
|
||||
Z_ORDER.store(ZOrder::TopMost);
|
||||
|
||||
let mut rect = WindowsApi::window_rect(
|
||||
c.focused_window().copied().unwrap_or_default().hwnd,
|
||||
)?;
|
||||
|
||||
// We create a new variable to track the actual pending move op so
|
||||
// that the other variable `pending_move_op` still holds the
|
||||
// pending move info so that when the move ends we know on the next
|
||||
// notification that the previous pending move and pending move are
|
||||
// different (because a move just finished) and still handle the
|
||||
// notification. If otherwise we updated the pending_move_op here
|
||||
// directly then the next pending move after finish would be the
|
||||
// same because we had already updated it here.
|
||||
let mut sync_pending_move_op =
|
||||
weak_pending_move_op.upgrade().and_then(|p| *p);
|
||||
while sync_pending_move_op.is_some() {
|
||||
sync_pending_move_op =
|
||||
weak_pending_move_op.upgrade().and_then(|p| *p);
|
||||
let border = match borders.entry(c.id().clone()) {
|
||||
Entry::Occupied(entry) => entry.into_mut(),
|
||||
Entry::Vacant(entry) => {
|
||||
if let Ok(border) = Border::create(c.id()) {
|
||||
entry.insert(border)
|
||||
} else {
|
||||
continue 'monitors;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let new_rect = WindowsApi::window_rect(
|
||||
c.focused_window().copied().unwrap_or_default().hwnd,
|
||||
)?;
|
||||
|
||||
if rect != new_rect {
|
||||
rect = new_rect;
|
||||
border.update(&rect, false)?;
|
||||
}
|
||||
}
|
||||
|
||||
Z_ORDER.store(restore_z_order);
|
||||
|
||||
continue 'monitors;
|
||||
}
|
||||
|
||||
// Get the border entry for this container from the map or create one
|
||||
let mut new_border = false;
|
||||
let border = match borders.entry(c.id().clone()) {
|
||||
Entry::Occupied(entry) => entry.into_mut(),
|
||||
Entry::Vacant(entry) => {
|
||||
if let Ok(border) = Border::create(c.id()) {
|
||||
if let Ok(border) = Border::create(
|
||||
c.id(),
|
||||
c.focused_window().copied().unwrap_or_default().hwnd,
|
||||
) {
|
||||
new_border = true;
|
||||
entry.insert(border)
|
||||
} else {
|
||||
continue 'monitors;
|
||||
@@ -481,6 +435,10 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
};
|
||||
|
||||
borders_monitors.insert(c.id().clone(), monitor_idx);
|
||||
windows_borders.insert(
|
||||
c.focused_window().cloned().unwrap_or_default().hwnd,
|
||||
border.clone(),
|
||||
);
|
||||
|
||||
#[allow(unused_assignments)]
|
||||
let mut last_focus_state = None;
|
||||
@@ -501,66 +459,35 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
last_focus_state = focus_state.insert(border.hwnd, new_focus_state);
|
||||
}
|
||||
|
||||
let rect = WindowsApi::window_rect(
|
||||
c.focused_window().copied().unwrap_or_default().hwnd,
|
||||
)?;
|
||||
let reference_hwnd =
|
||||
c.focused_window().copied().unwrap_or_default().hwnd;
|
||||
|
||||
let rect = WindowsApi::window_rect(reference_hwnd)?;
|
||||
|
||||
let should_invalidate = match last_focus_state {
|
||||
None => true,
|
||||
Some(last_focus_state) => last_focus_state != new_focus_state,
|
||||
};
|
||||
|
||||
border.update(&rect, should_invalidate)?;
|
||||
if new_border {
|
||||
border.set_position(&rect, reference_hwnd)?;
|
||||
}
|
||||
|
||||
if should_invalidate {
|
||||
border.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let restore_z_order = Z_ORDER.load();
|
||||
Z_ORDER.store(ZOrder::TopMost);
|
||||
|
||||
'windows: for window in ws.floating_windows() {
|
||||
let hwnd = window.hwnd;
|
||||
let notification_hwnd = notification.0.unwrap_or_default();
|
||||
|
||||
if pending_move_op.is_some() && hwnd == notification_hwnd {
|
||||
let mut rect = WindowsApi::window_rect(hwnd)?;
|
||||
|
||||
// Check comment above for containers move
|
||||
let mut sync_pending_move_op =
|
||||
weak_pending_move_op.upgrade().and_then(|p| *p);
|
||||
while sync_pending_move_op.is_some() {
|
||||
sync_pending_move_op =
|
||||
weak_pending_move_op.upgrade().and_then(|p| *p);
|
||||
let border = match borders.entry(hwnd.to_string()) {
|
||||
Entry::Occupied(entry) => entry.into_mut(),
|
||||
Entry::Vacant(entry) => {
|
||||
if let Ok(border) =
|
||||
Border::create(&hwnd.to_string())
|
||||
{
|
||||
entry.insert(border)
|
||||
} else {
|
||||
continue 'monitors;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let new_rect = WindowsApi::window_rect(hwnd)?;
|
||||
|
||||
if rect != new_rect {
|
||||
rect = new_rect;
|
||||
border.update(&rect, true)?;
|
||||
}
|
||||
}
|
||||
|
||||
Z_ORDER.store(restore_z_order);
|
||||
|
||||
continue 'monitors;
|
||||
}
|
||||
|
||||
let mut new_border = false;
|
||||
let border = match borders.entry(window.hwnd.to_string()) {
|
||||
Entry::Occupied(entry) => entry.into_mut(),
|
||||
Entry::Vacant(entry) => {
|
||||
if let Ok(border) = Border::create(&window.hwnd.to_string())
|
||||
if let Ok(border) =
|
||||
Border::create(&window.hwnd.to_string(), window.hwnd)
|
||||
{
|
||||
new_border = true;
|
||||
entry.insert(border)
|
||||
} else {
|
||||
continue 'monitors;
|
||||
@@ -569,6 +496,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
};
|
||||
|
||||
borders_monitors.insert(window.hwnd.to_string(), monitor_idx);
|
||||
windows_borders.insert(window.hwnd, border.clone());
|
||||
|
||||
let mut should_destroy = false;
|
||||
|
||||
@@ -607,10 +535,14 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
Some(last_focus_state) => last_focus_state != new_focus_state,
|
||||
};
|
||||
|
||||
border.update(&rect, should_invalidate)?;
|
||||
}
|
||||
if new_border {
|
||||
border.set_position(&rect, window.hwnd)?;
|
||||
}
|
||||
|
||||
Z_ORDER.store(restore_z_order);
|
||||
if should_invalidate {
|
||||
border.invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,6 +306,8 @@ pub enum BorderImplementation {
|
||||
ValueEnum,
|
||||
JsonSchema,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
)]
|
||||
pub enum WindowKind {
|
||||
Single,
|
||||
|
||||
@@ -37,6 +37,12 @@ impl From<Rect> for RECT {
|
||||
}
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
pub fn is_same_size_as(&self, rhs: &Self) -> bool {
|
||||
self.right == rhs.right && self.bottom == rhs.bottom
|
||||
}
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
/// decrease the size of self by the padding amount.
|
||||
pub fn add_padding<T>(&mut self, padding: T)
|
||||
|
||||
@@ -670,12 +670,10 @@ impl WindowManager {
|
||||
self.update_focused_workspace(self.mouse_follows_focus, true)?;
|
||||
}
|
||||
SocketMessage::Retile => {
|
||||
border_manager::BORDER_TEMPORARILY_DISABLED.store(false, Ordering::SeqCst);
|
||||
border_manager::destroy_all_borders()?;
|
||||
self.retile_all(false)?
|
||||
}
|
||||
SocketMessage::RetileWithResizeDimensions => {
|
||||
border_manager::BORDER_TEMPORARILY_DISABLED.store(false, Ordering::SeqCst);
|
||||
border_manager::destroy_all_borders()?;
|
||||
self.retile_all(true)?
|
||||
}
|
||||
@@ -1518,6 +1516,16 @@ impl WindowManager {
|
||||
}
|
||||
SocketMessage::Border(enable) => {
|
||||
border_manager::BORDER_ENABLED.store(enable, Ordering::SeqCst);
|
||||
if !enable {
|
||||
match IMPLEMENTATION.load() {
|
||||
BorderImplementation::Komorebi => {
|
||||
border_manager::destroy_all_borders()?;
|
||||
}
|
||||
BorderImplementation::Windows => {
|
||||
self.remove_all_accents()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SocketMessage::BorderImplementation(implementation) => {
|
||||
if !*WINDOWS_11 && matches!(implementation, BorderImplementation::Windows) {
|
||||
|
||||
@@ -11,7 +11,6 @@ use crate::border_manager;
|
||||
use crate::border_manager::ZOrder;
|
||||
use crate::border_manager::IMPLEMENTATION;
|
||||
use crate::border_manager::STYLE;
|
||||
use crate::border_manager::Z_ORDER;
|
||||
use crate::colour::Colour;
|
||||
use crate::core::BorderImplementation;
|
||||
use crate::core::StackbarLabel;
|
||||
@@ -297,7 +296,7 @@ pub struct StaticConfig {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(alias = "active_window_border_style")]
|
||||
pub border_style: Option<BorderStyle>,
|
||||
/// Active window border z-order (default: System)
|
||||
/// DEPRECATED from v0.1.31: no longer required
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub border_z_order: Option<ZOrder>,
|
||||
/// Active window border implementation (default: Komorebi)
|
||||
@@ -497,7 +496,7 @@ impl StaticConfig {
|
||||
}
|
||||
|
||||
pub fn deprecated(raw: &str) {
|
||||
let deprecated_options = ["invisible_borders"];
|
||||
let deprecated_options = ["invisible_borders", "border_z_order"];
|
||||
let deprecated_variants = vec![
|
||||
("Hide", "window_hiding_behaviour", "Cloak"),
|
||||
("Minimize", "window_hiding_behaviour", "Cloak"),
|
||||
@@ -600,7 +599,7 @@ impl From<&WindowManager> for StaticConfig {
|
||||
),
|
||||
transparency_ignore_rules: None,
|
||||
border_style: Option::from(STYLE.load()),
|
||||
border_z_order: Option::from(Z_ORDER.load()),
|
||||
border_z_order: None,
|
||||
border_implementation: Option::from(IMPLEMENTATION.load()),
|
||||
default_workspace_padding: Option::from(
|
||||
DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst),
|
||||
|
||||
@@ -10,7 +10,6 @@ use crate::animation::ANIMATION_ENABLED_PER_ANIMATION;
|
||||
use crate::animation::ANIMATION_MANAGER;
|
||||
use crate::animation::ANIMATION_STYLE_GLOBAL;
|
||||
use crate::animation::ANIMATION_STYLE_PER_ANIMATION;
|
||||
use crate::border_manager;
|
||||
use crate::com::SetCloak;
|
||||
use crate::focus_manager;
|
||||
use crate::stackbar_manager;
|
||||
@@ -193,9 +192,6 @@ impl RenderDispatcher for MovementRenderDispatcher {
|
||||
}
|
||||
|
||||
fn pre_render(&self) -> Result<()> {
|
||||
border_manager::BORDER_TEMPORARILY_DISABLED.store(true, Ordering::SeqCst);
|
||||
border_manager::send_notification(Some(self.hwnd));
|
||||
|
||||
stackbar_manager::STACKBAR_TEMPORARILY_DISABLED.store(true, Ordering::SeqCst);
|
||||
stackbar_manager::send_notification();
|
||||
|
||||
@@ -224,10 +220,8 @@ impl RenderDispatcher for MovementRenderDispatcher {
|
||||
focus_manager::send_notification(self.hwnd)
|
||||
}
|
||||
|
||||
border_manager::BORDER_TEMPORARILY_DISABLED.store(false, Ordering::SeqCst);
|
||||
stackbar_manager::STACKBAR_TEMPORARILY_DISABLED.store(false, Ordering::SeqCst);
|
||||
|
||||
border_manager::send_notification(Some(self.hwnd));
|
||||
stackbar_manager::send_notification();
|
||||
transparency_manager::send_notification();
|
||||
}
|
||||
|
||||
@@ -106,7 +106,6 @@ use windows::Win32::UI::WindowsAndMessaging::GWL_STYLE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GW_HWNDNEXT;
|
||||
use windows::Win32::UI::WindowsAndMessaging::HWND_TOP;
|
||||
use windows::Win32::UI::WindowsAndMessaging::LWA_ALPHA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::LWA_COLORKEY;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SET_WINDOW_POS_FLAGS;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SHOW_WINDOW_CMD;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SPIF_SENDCHANGE;
|
||||
@@ -129,7 +128,6 @@ use windows::Win32::UI::WindowsAndMessaging::WM_CLOSE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WNDENUMPROC;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_DISABLED;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_EX_NOACTIVATE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOPMOST;
|
||||
@@ -154,6 +152,7 @@ macro_rules! as_ptr {
|
||||
};
|
||||
}
|
||||
|
||||
use crate::border_manager::Border;
|
||||
pub(crate) use as_ptr;
|
||||
|
||||
pub enum WindowsResult<T, E> {
|
||||
@@ -453,7 +452,13 @@ impl WindowsApi {
|
||||
}
|
||||
|
||||
pub fn set_border_pos(hwnd: isize, layout: &Rect, position: isize) -> Result<()> {
|
||||
let flags = { SetWindowPosition::SHOW_WINDOW | SetWindowPosition::NO_ACTIVATE };
|
||||
let flags = {
|
||||
SetWindowPosition::NO_SEND_CHANGING
|
||||
| SetWindowPosition::NO_ACTIVATE
|
||||
| SetWindowPosition::NO_REDRAW
|
||||
| SetWindowPosition::SHOW_WINDOW
|
||||
};
|
||||
|
||||
Self::set_window_pos(
|
||||
HWND(as_ptr!(hwnd)),
|
||||
layout,
|
||||
@@ -1090,10 +1095,14 @@ impl WindowsApi {
|
||||
.process()
|
||||
}
|
||||
|
||||
pub fn create_border_window(name: PCWSTR, instance: isize) -> Result<isize> {
|
||||
pub fn create_border_window(
|
||||
name: PCWSTR,
|
||||
instance: isize,
|
||||
border: *const Border,
|
||||
) -> Result<isize> {
|
||||
unsafe {
|
||||
let hwnd = CreateWindowExW(
|
||||
WS_EX_TOOLWINDOW | WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_NOACTIVATE,
|
||||
CreateWindowExW(
|
||||
WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_NOACTIVATE,
|
||||
name,
|
||||
name,
|
||||
WS_POPUP | WS_SYSMENU,
|
||||
@@ -1104,12 +1113,8 @@ impl WindowsApi {
|
||||
None,
|
||||
None,
|
||||
HINSTANCE(as_ptr!(instance)),
|
||||
None,
|
||||
)?;
|
||||
|
||||
SetLayeredWindowAttributes(hwnd, COLORREF(0), 0, LWA_COLORKEY)?;
|
||||
|
||||
hwnd
|
||||
Some(border as _),
|
||||
)?
|
||||
}
|
||||
.process()
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use windows::Win32::Foundation::BOOL;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
use windows::Win32::Foundation::LPARAM;
|
||||
use windows::Win32::UI::Accessibility::HWINEVENTHOOK;
|
||||
|
||||
use crate::border_manager;
|
||||
use crate::container::Container;
|
||||
use crate::window::RuleDebug;
|
||||
use crate::window::Window;
|
||||
@@ -12,6 +8,19 @@ use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::winevent::WinEvent;
|
||||
use crate::winevent_listener;
|
||||
use windows::Win32::Foundation::BOOL;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
use windows::Win32::Foundation::LPARAM;
|
||||
use windows::Win32::Foundation::WPARAM;
|
||||
use windows::Win32::UI::Accessibility::HWINEVENTHOOK;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetWindowLongW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SendNotifyMessageW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GWL_EXSTYLE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GWL_STYLE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::OBJID_WINDOW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_CHILD;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_EX_NOACTIVATE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
|
||||
|
||||
pub extern "system" fn enum_window(hwnd: HWND, lparam: LPARAM) -> BOOL {
|
||||
let containers = unsafe { &mut *(lparam.0 as *mut VecDeque<Container>) };
|
||||
@@ -60,6 +69,15 @@ pub extern "system" fn alt_tab_windows(hwnd: HWND, lparam: LPARAM) -> BOOL {
|
||||
true.into()
|
||||
}
|
||||
|
||||
fn has_filtered_style(hwnd: HWND) -> bool {
|
||||
let style = unsafe { GetWindowLongW(hwnd, GWL_STYLE) as u32 };
|
||||
let ex_style = unsafe { GetWindowLongW(hwnd, GWL_EXSTYLE) as u32 };
|
||||
|
||||
style & WS_CHILD.0 != 0
|
||||
|| ex_style & WS_EX_TOOLWINDOW.0 != 0
|
||||
|| ex_style & WS_EX_NOACTIVATE.0 != 0
|
||||
}
|
||||
|
||||
pub extern "system" fn win_event_hook(
|
||||
_h_win_event_hook: HWINEVENTHOOK,
|
||||
event: u32,
|
||||
@@ -69,8 +87,7 @@ pub extern "system" fn win_event_hook(
|
||||
_id_event_thread: u32,
|
||||
_dwms_event_time: u32,
|
||||
) {
|
||||
// OBJID_WINDOW
|
||||
if id_object != 0 {
|
||||
if id_object != OBJID_WINDOW.0 {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -81,6 +98,23 @@ pub extern "system" fn win_event_hook(
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
// this forwards the message to the window's border when it moves or is destroyed
|
||||
// see border_manager/border.rs
|
||||
if matches!(
|
||||
winevent,
|
||||
WinEvent::ObjectLocationChange | WinEvent::ObjectDestroy
|
||||
) && !has_filtered_style(hwnd)
|
||||
{
|
||||
let border_window = border_manager::window_border(hwnd.0 as isize);
|
||||
|
||||
if let Some(border) = border_window {
|
||||
unsafe {
|
||||
let _ =
|
||||
SendNotifyMessageW(border.hwnd(), event, WPARAM(0), LPARAM(hwnd.0 as isize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let event_type = match WindowManagerEvent::from_win_event(winevent, window) {
|
||||
None => {
|
||||
tracing::trace!(
|
||||
|
||||
@@ -11,6 +11,8 @@ use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
|
||||
use windows::Win32::UI::WindowsAndMessaging::EVENT_MAX;
|
||||
use windows::Win32::UI::WindowsAndMessaging::EVENT_MIN;
|
||||
use windows::Win32::UI::WindowsAndMessaging::MSG;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WINEVENT_OUTOFCONTEXT;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WINEVENT_SKIPOWNPROCESS;
|
||||
|
||||
use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_callbacks;
|
||||
@@ -31,7 +33,7 @@ pub fn start() {
|
||||
Some(windows_callbacks::win_event_hook),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS,
|
||||
)
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user