feat(wm): restrict border window to outline

This commit moves the border window drawing logic into the WNDPROC
callback and uses BeginPaint -> Rectangle -> Endpaint to draw a
rectangle around the outside of the window in a specific colour that is
not black, which is used as the transparency colour with
SetLayeredWindowAttributes.

All of this results in a non-filled border rect and a much nicer
experience for users who are using transparency or translucent effects
on their windows.

This commit also introduces an optional second active border colour when
the user is focused on a stack of windows. If this is not set, the
default colour for single windows will be used.

Finally, a bunch of small issues relating to the border window staying
drawn on the screen even when there are no active windows on a workspace
have been addressed.

resolve #201
This commit is contained in:
LGUG2Z
2022-08-13 08:16:27 -07:00
parent f5def84010
commit e466a17877
12 changed files with 163 additions and 134 deletions
+7 -2
View File
@@ -2,7 +2,6 @@ use std::sync::atomic::Ordering;
use std::time::Duration;
use color_eyre::Result;
use komorebi_core::Rect;
use windows::core::PCSTR;
use windows::Win32::Foundation::HWND;
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageA;
@@ -13,11 +12,15 @@ use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
use windows::Win32::UI::WindowsAndMessaging::MSG;
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSA;
use komorebi_core::Rect;
use crate::window::Window;
use crate::windows_callbacks;
use crate::WindowsApi;
use crate::BORDER_HWND;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::BORDER_RECT;
use crate::TRANSPARENCY_COLOUR;
use crate::WINDOWS_11;
#[derive(Debug, Clone, Copy)]
@@ -40,7 +43,7 @@ impl Border {
let name = format!("{name}\0");
let instance = WindowsApi::module_handle_w()?;
let class_name = PCSTR(name.as_ptr());
let brush = WindowsApi::create_solid_brush(255, 140, 0);
let brush = WindowsApi::create_solid_brush(TRANSPARENCY_COLOUR);
let window_class = WNDCLASSA {
hInstance: instance,
lpszClassName: class_name,
@@ -114,6 +117,8 @@ impl Border {
rect.bottom += invisible_borders.bottom;
}
*BORDER_RECT.lock() = rect;
WindowsApi::position_border_window(self.hwnd(), &rect, activate)
}
}
+9
View File
@@ -38,6 +38,7 @@ use winreg::enums::HKEY_CURRENT_USER;
use winreg::RegKey;
use komorebi_core::HidingBehaviour;
use komorebi_core::Rect;
use komorebi_core::SocketMessage;
use crate::border::Border;
@@ -151,6 +152,9 @@ lazy_static! {
Version::Semantic(_, _, x) if x >= &22000
)
};
static ref BORDER_RECT: Arc<Mutex<Rect>> =
Arc::new(Mutex::new(Rect::default()));
}
pub static INITIAL_CONFIGURATION_LOADED: AtomicBool = AtomicBool::new(false);
@@ -159,6 +163,11 @@ pub static SESSION_ID: AtomicU32 = AtomicU32::new(0);
pub static BORDER_ENABLED: AtomicBool = AtomicBool::new(false);
pub static BORDER_HWND: AtomicIsize = AtomicIsize::new(0);
pub static BORDER_HIDDEN: AtomicBool = AtomicBool::new(false);
pub static BORDER_COLOUR_SINGLE: AtomicU32 = AtomicU32::new(0);
pub static BORDER_COLOUR_STACK: AtomicU32 = AtomicU32::new(0);
pub static BORDER_COLOUR_CURRENT: AtomicU32 = AtomicU32::new(0);
// 0 0 0 aka pure black, I doubt anyone will want this as a border colour
pub const TRANSPARENCY_COLOUR: u32 = 0;
fn setup() -> Result<(WorkerGuard, WorkerGuard)> {
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
+18 -3
View File
@@ -26,6 +26,7 @@ use komorebi_core::Sizing;
use komorebi_core::SocketMessage;
use komorebi_core::StateQuery;
use komorebi_core::WindowContainerBehaviour;
use komorebi_core::WindowKind;
use crate::border::Border;
use crate::current_virtual_desktop;
@@ -36,6 +37,9 @@ use crate::window_manager::WindowManager;
use crate::windows_api::WindowsApi;
use crate::Notification;
use crate::NotificationEvent;
use crate::BORDER_COLOUR_CURRENT;
use crate::BORDER_COLOUR_SINGLE;
use crate::BORDER_COLOUR_STACK;
use crate::BORDER_ENABLED;
use crate::BORDER_HWND;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
@@ -758,9 +762,18 @@ impl WindowManager {
self.hide_border()?;
}
}
SocketMessage::ActiveWindowBorderColour(r, g, b) => {
let hwnd = BORDER_HWND.load(Ordering::SeqCst);
WindowsApi::change_border_colour(hwnd, r, g, b)?;
SocketMessage::ActiveWindowBorderColour(kind, r, g, b) => {
match kind {
WindowKind::Single => {
BORDER_COLOUR_SINGLE.store(r | (g << 8) | (b << 16), Ordering::SeqCst);
BORDER_COLOUR_CURRENT.store(r | (g << 8) | (b << 16), Ordering::SeqCst);
}
WindowKind::Stack => {
BORDER_COLOUR_STACK.store(r | (g << 8) | (b << 16), Ordering::SeqCst);
}
}
WindowsApi::invalidate_border_rect()?;
}
SocketMessage::NotificationSchema => {
let notification = schema_for!(Notification);
@@ -789,6 +802,8 @@ impl WindowManager {
| SocketMessage::ToggleMaximize
| SocketMessage::Promote
| SocketMessage::Retile
| SocketMessage::InvisibleBorders(_)
| SocketMessage::WorkAreaOffset(_)
| SocketMessage::MoveWindow(_) => {
let foreground = WindowsApi::foreground_window()?;
let foreground_window = Window { hwnd: foreground };
+43 -11
View File
@@ -20,6 +20,9 @@ use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::Notification;
use crate::NotificationEvent;
use crate::BORDER_COLOUR_CURRENT;
use crate::BORDER_COLOUR_SINGLE;
use crate::BORDER_COLOUR_STACK;
use crate::BORDER_ENABLED;
use crate::BORDER_HIDDEN;
use crate::BORDER_HWND;
@@ -483,19 +486,48 @@ impl WindowManager {
}
WindowManagerEvent::MoveResizeEnd(_, _)
| WindowManagerEvent::Show(_, _)
| WindowManagerEvent::FocusChange(_, _) => {
let window = self.focused_window()?;
let mut rect = WindowsApi::window_rect(window.hwnd())?;
rect.top -= self.invisible_borders.bottom;
rect.bottom += self.invisible_borders.bottom;
let activate = BORDER_HIDDEN.load(Ordering::SeqCst);
| WindowManagerEvent::FocusChange(_, _)
| WindowManagerEvent::Minimize(_, _) => {
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
border.set_position(*window, &self.invisible_borders, activate)?;
if activate {
BORDER_HIDDEN.store(false, Ordering::SeqCst);
match self.focused_container() {
// if there is no focused container, the desktop is empty
Err(..) => {
WindowsApi::hide_border_window(border.hwnd())?;
}
Ok(container) => {
if !(matches!(event, WindowManagerEvent::Minimize(_, _))
&& container.windows().len() == 1)
{
let container_size = self.focused_container()?.windows().len();
let window = self.focused_window()?;
let mut rect = WindowsApi::window_rect(window.hwnd())?;
rect.top -= self.invisible_borders.bottom;
rect.bottom += self.invisible_borders.bottom;
let activate = BORDER_HIDDEN.load(Ordering::SeqCst);
if container_size > 1 {
BORDER_COLOUR_CURRENT.store(
BORDER_COLOUR_STACK.load(Ordering::SeqCst),
Ordering::SeqCst,
);
} else {
BORDER_COLOUR_CURRENT.store(
BORDER_COLOUR_SINGLE.load(Ordering::SeqCst),
Ordering::SeqCst,
);
}
WindowsApi::invalidate_border_rect()?;
border.set_position(*window, &self.invisible_borders, activate)?;
if activate {
BORDER_HIDDEN.store(false, Ordering::SeqCst);
}
}
}
}
}
_ => {}
+3 -2
View File
@@ -15,7 +15,6 @@ use schemars::JsonSchema;
use serde::Serialize;
use uds_windows::UnixListener;
use crate::border::Border;
use komorebi_core::custom_layout::CustomLayout;
use komorebi_core::Arrangement;
use komorebi_core::Axis;
@@ -30,6 +29,7 @@ use komorebi_core::Rect;
use komorebi_core::Sizing;
use komorebi_core::WindowContainerBehaviour;
use crate::border::Border;
use crate::container::Container;
use crate::current_virtual_desktop;
use crate::load_configuration;
@@ -205,7 +205,8 @@ impl WindowManager {
rect.bottom += self.invisible_borders.bottom;
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
border.set_position(foreground_window, &self.invisible_borders, true)
border.set_position(foreground_window, &self.invisible_borders, true)?;
WindowsApi::invalidate_border_rect()
}
#[tracing::instrument(skip(self))]
+28 -17
View File
@@ -1,6 +1,7 @@
use std::collections::VecDeque;
use std::convert::TryFrom;
use std::ffi::c_void;
use std::sync::atomic::Ordering;
use color_eyre::eyre::anyhow;
use color_eyre::eyre::Error;
@@ -28,6 +29,7 @@ use windows::Win32::Graphics::Dwm::DWM_CLOAKED_SHELL;
use windows::Win32::Graphics::Gdi::CreateSolidBrush;
use windows::Win32::Graphics::Gdi::EnumDisplayMonitors;
use windows::Win32::Graphics::Gdi::GetMonitorInfoW;
use windows::Win32::Graphics::Gdi::InvalidateRect;
use windows::Win32::Graphics::Gdi::MonitorFromPoint;
use windows::Win32::Graphics::Gdi::MonitorFromWindow;
use windows::Win32::Graphics::Gdi::HBRUSH;
@@ -68,22 +70,22 @@ use windows::Win32::UI::WindowsAndMessaging::IsWindow;
use windows::Win32::UI::WindowsAndMessaging::IsWindowVisible;
use windows::Win32::UI::WindowsAndMessaging::RealGetWindowClassW;
use windows::Win32::UI::WindowsAndMessaging::RegisterClassA;
use windows::Win32::UI::WindowsAndMessaging::SetClassLongPtrW;
use windows::Win32::UI::WindowsAndMessaging::SetCursorPos;
use windows::Win32::UI::WindowsAndMessaging::SetForegroundWindow;
use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes;
use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrW;
use windows::Win32::UI::WindowsAndMessaging::SetWindowPos;
use windows::Win32::UI::WindowsAndMessaging::ShowWindow;
use windows::Win32::UI::WindowsAndMessaging::SystemParametersInfoW;
use windows::Win32::UI::WindowsAndMessaging::WindowFromPoint;
use windows::Win32::UI::WindowsAndMessaging::CW_USEDEFAULT;
use windows::Win32::UI::WindowsAndMessaging::GCLP_HBRBACKGROUND;
use windows::Win32::UI::WindowsAndMessaging::GWL_EXSTYLE;
use windows::Win32::UI::WindowsAndMessaging::GWL_STYLE;
use windows::Win32::UI::WindowsAndMessaging::GW_HWNDNEXT;
use windows::Win32::UI::WindowsAndMessaging::HWND_BOTTOM;
use windows::Win32::UI::WindowsAndMessaging::HWND_NOTOPMOST;
use windows::Win32::UI::WindowsAndMessaging::HWND_TOPMOST;
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;
@@ -99,6 +101,7 @@ use windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS
use windows::Win32::UI::WindowsAndMessaging::WINDOW_LONG_PTR_INDEX;
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSA;
use windows::Win32::UI::WindowsAndMessaging::WNDENUMPROC;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
use windows::Win32::UI::WindowsAndMessaging::WS_MAXIMIZEBOX;
use windows::Win32::UI::WindowsAndMessaging::WS_MINIMIZEBOX;
@@ -113,6 +116,8 @@ use crate::monitor::Monitor;
use crate::ring::Ring;
use crate::set_window_position::SetWindowPosition;
use crate::windows_callbacks;
use crate::BORDER_HWND;
use crate::TRANSPARENCY_COLOUR;
pub enum WindowsResult<T, E> {
Err(E),
@@ -673,8 +678,8 @@ impl WindowsApi {
unsafe { GetModuleHandleW(None) }.process()
}
pub fn create_solid_brush(r: u32, g: u32, b: u32) -> HBRUSH {
unsafe { CreateSolidBrush(r | (g << 8) | (b << 16)) }
pub fn create_solid_brush(colour: u32) -> HBRUSH {
unsafe { CreateSolidBrush(colour) }
}
pub fn register_class_a(window_class: &WNDCLASSA) -> Result<u16> {
@@ -692,16 +697,6 @@ impl WindowsApi {
Ok(a == b)
}
pub fn change_border_colour(hwnd: isize, r: u32, g: u32, b: u32) -> Result<usize> {
Result::from(WindowsResult::from(unsafe {
SetClassLongPtrW(
HWND(hwnd),
GCLP_HBRBACKGROUND,
Self::create_solid_brush(r, g, b).0,
)
}))
}
pub fn round_corners(hwnd: isize) -> Result<()> {
let round = DWMWCP_ROUND;
@@ -718,8 +713,8 @@ impl WindowsApi {
pub fn create_border_window(name: PCSTR, instance: HINSTANCE) -> Result<isize> {
unsafe {
CreateWindowExA(
WS_EX_TOOLWINDOW,
let hwnd = CreateWindowExA(
WS_EX_TOOLWINDOW | WS_EX_LAYERED,
name,
name,
WS_POPUP | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
@@ -731,8 +726,24 @@ impl WindowsApi {
None,
instance,
std::ptr::null(),
)
);
SetLayeredWindowAttributes(hwnd, TRANSPARENCY_COLOUR, 0, LWA_COLORKEY);
hwnd
}
.process()
}
pub fn invalidate_border_rect() -> Result<()> {
unsafe {
InvalidateRect(
HWND(BORDER_HWND.load(Ordering::SeqCst)),
std::ptr::null(),
false,
)
}
.ok()
.process()
}
}
+24 -2
View File
@@ -1,4 +1,5 @@
use std::collections::VecDeque;
use std::sync::atomic::Ordering;
use windows::Win32::Foundation::BOOL;
use windows::Win32::Foundation::HWND;
@@ -6,9 +7,16 @@ use windows::Win32::Foundation::LPARAM;
use windows::Win32::Foundation::LRESULT;
use windows::Win32::Foundation::RECT;
use windows::Win32::Foundation::WPARAM;
use windows::Win32::Graphics::Gdi::InvalidateRect;
use windows::Win32::Graphics::Gdi::BeginPaint;
use windows::Win32::Graphics::Gdi::CreatePen;
use windows::Win32::Graphics::Gdi::EndPaint;
use windows::Win32::Graphics::Gdi::Rectangle;
use windows::Win32::Graphics::Gdi::SelectObject;
use windows::Win32::Graphics::Gdi::ValidateRect;
use windows::Win32::Graphics::Gdi::HDC;
use windows::Win32::Graphics::Gdi::HMONITOR;
use windows::Win32::Graphics::Gdi::PAINTSTRUCT;
use windows::Win32::Graphics::Gdi::PS_SOLID;
use windows::Win32::UI::Accessibility::HWINEVENTHOOK;
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage;
@@ -22,6 +30,9 @@ use crate::window::Window;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::winevent_listener::WINEVENT_CALLBACK_CHANNEL;
use crate::BORDER_COLOUR_CURRENT;
use crate::BORDER_RECT;
use crate::TRANSPARENCY_COLOUR;
pub extern "system" fn valid_display_monitors(
hmonitor: HMONITOR,
@@ -120,7 +131,18 @@ pub extern "system" fn border_window(
unsafe {
match message as u32 {
WM_PAINT => {
InvalidateRect(window, std::ptr::null(), true);
let border_rect = *BORDER_RECT.lock();
let mut ps = PAINTSTRUCT::default();
let hdc = BeginPaint(window, std::ptr::addr_of_mut!(ps).cast());
let hpen = CreatePen(PS_SOLID, 20, BORDER_COLOUR_CURRENT.load(Ordering::SeqCst));
let hbrush = WindowsApi::create_solid_brush(TRANSPARENCY_COLOUR);
SelectObject(hdc, hpen);
SelectObject(hdc, hbrush);
Rectangle(hdc, 0, 0, border_rect.right, border_rect.bottom);
EndPaint(window, &ps);
ValidateRect(window, std::ptr::null());
LRESULT(0)
}
WM_DESTROY => {