diff --git a/README.md b/README.md index 9c9d443d..3f008291 100644 --- a/README.md +++ b/README.md @@ -268,7 +268,10 @@ If you would like to add a visual border around the currently focused window, tw ```powershell komorebic.exe active-window-border [enable|disable] -komorebic.exe active-window-border-colour [R G B] +komorebic.exe active-window-border-colour [R G B] --window-kind single + +# optionally, if you want a different colour for stacks of windows +komorebic.exe active-window-border-colour [R G B] --window-kind stack ``` It is important to note that the active window border will only apply to windows managed by `komorebi`. @@ -484,95 +487,6 @@ You can run `komorebic.exe` to get a full list of the commands that you can use keybindings with. You can run `komorebic.exe --help` to get a full explanation of the arguments required for each command. -``` -start Start komorebi.exe as a background process -stop Stop the komorebi.exe process and restore all hidden windows -state Show a JSON representation of the current window manager state -query Query the current window manager state -subscribe Subscribe to komorebi events -unsubscribe Unsubscribe from komorebi events -log Tail komorebi.exe's process logs (cancel with Ctrl-C) -quick-save-resize Quicksave the current resize layout dimensions -quick-load-resize Load the last quicksaved resize layout dimensions -save-resize Save the current resize layout dimensions to a file -load-resize Load the resize layout dimensions from a file -focus Change focus to the window in the specified direction -move Move the focused window in the specified direction -cycle-focus Change focus to the window in the specified cycle direction -cycle-move Move the focused window in the specified cycle direction -stack Stack the focused window in the specified direction -resize-edge Resize the focused window in the specified direction -resize-axis Resize the focused window or primary column along the specified axis -unstack Unstack the focused window -cycle-stack Cycle the focused stack in the specified cycle direction -move-to-monitor Move the focused window to the specified monitor -move-to-workspace Move the focused window to the specified workspace -send-to-monitor Send the focused window to the specified monitor -send-to-workspace Send the focused window to the specified workspace -send-to-monitor-workspace Send the focused window to the specified monitor workspace -focus-monitor Focus the specified monitor -focus-workspace Focus the specified workspace on the focused monitor -focus-monitor-workspace Focus the specified workspace on the target monitor -cycle-monitor Focus the monitor in the given cycle direction -cycle-workspace Focus the workspace in the given cycle direction -move-workspace-to-monitor Move the focused workspace to the specified monitor -new-workspace Create and append a new workspace on the focused monitor -resize-delta Set the resize delta (used by resize-edge and resize-axis) -invisible-borders Set the invisible border dimensions around each window -work-area-offset Set offsets to exclude parts of the work area from tiling -adjust-container-padding Adjust container padding on the focused workspace -adjust-workspace-padding Adjust workspace padding on the focused workspace -change-layout Set the layout on the focused workspace -load-custom-layout Load a custom layout from file for the focused workspace -flip-layout Flip the layout on the focused workspace (BSP only) -promote Promote the focused window to the top of the tree -retile Force the retiling of all managed windows -ensure-workspaces Create at least this many workspaces for the specified monitor -container-padding Set the container padding for the specified workspace -workspace-padding Set the workspace padding for the specified workspace -workspace-layout Set the layout for the specified workspace -workspace-custom-layout Set a custom layout for the specified workspace -workspace-layout-rule Add a dynamic layout rule for the specified workspace -workspace-custom-layout-rule Add a dynamic custom layout for the specified workspace -clear-workspace-layout-rules Clear all dynamic layout rules for the specified workspace -workspace-tiling Enable or disable window tiling for the specified workspace -workspace-name Set the workspace name for the specified workspace -toggle-window-container-behaviour Toggle the behaviour for new windows (stacking or dynamic tiling) -toggle-pause Toggle window tiling on the focused workspace -toggle-tiling Toggle window tiling on the focused workspace -toggle-float Toggle floating mode for the focused window -toggle-monocle Toggle monocle mode for the focused container -toggle-maximize Toggle native maximization for the focused window -restore-windows Restore all hidden windows (debugging command) -manage Force komorebi to manage the focused window -unmanage Unmanage a window that was forcibly managed -reload-configuration Reload ~/komorebi.ahk (if it exists) -watch-configuration Enable or disable watching of ~/komorebi.ahk (if it exists) -complete-configuration Signal that the final configuration option has been sent -window-hiding-behaviour Set the window behaviour when switching workspaces / cycling stacks -cross-monitor-move-behaviour Set the behaviour when moving windows across monitor boundaries -toggle-cross-monitor-move-behaviour Toggle the behaviour when moving windows across monitor boundaries -unmanaged-window-operation-behaviour Set the operation behaviour when the focused window is not managed -float-rule Add a rule to always float the specified application -manage-rule Add a rule to always manage the specified application -workspace-rule Add a rule to associate an application with a workspace -identify-object-name-change-application Identify an application that sends EVENT_OBJECT_NAMECHANGE on launch -identify-tray-application Identify an application that closes to the system tray -identify-layered-application Identify an application that has WS_EX_LAYERED, but should still be managed -identify-border-overflow-application Identify an application that has overflowing borders -active-window-border Enable or disable the active window border -active-window-border-colour Set the colour for the active window border -focus-follows-mouse Enable or disable focus follows mouse for the operating system -toggle-focus-follows-mouse Toggle focus follows mouse for the operating system -mouse-follows-focus Enable or disable mouse follows focus on all workspaces -toggle-mouse-follows-focus Toggle mouse follows focus on all workspaces -ahk-library Generate a library of AutoHotKey helper functions -ahk-app-specific-configuration Generate common app-specific configurations and fixes to use in komorebi.ahk -format-app-specific-configuration Format a YAML file for use with the 'ahk-app-specific-configuration' command -notification-schema Generate a JSON Schema of subscription notifications -help Print this message or the help of the given subcommand(s) -``` - ### AutoHotKey Helper Library for `komorebic` Additionally, you may run `komorebic.exe ahk-library` to @@ -602,7 +516,7 @@ used [is available here](komorebi.sample.with.lib.ahk). - [x] Resize window container in direction - [x] Resize window container on axis - [x] Set custom resize delta -- [ ] Resize child window containers by split ratio +- [x] Active window border - [x] Quicksave and quickload layouts with resize dimensions - [x] Save and load layouts with resize dimensions to/from specific files - [x] Mouse drag to swap window container position @@ -623,7 +537,7 @@ used [is available here](komorebi.sample.with.lib.ahk). - [x] Identify applications which overflow their borders by exe name and class - [x] Identify 'close/minimize to tray' applications by exe name and class - [x] Configure work area offsets to preserve space for custom taskbars -- [x] Configure and compensate for the size of Windows 10's invisible borders +- [x] Configure and compensate for the size of Windows invisible borders - [x] Toggle floating windows - [x] Toggle monocle window - [x] Toggle native maximization @@ -733,6 +647,9 @@ An example of how to create a named pipe and a subscription to `komorebi`'s hand by [@denBot](https://github.com/denBot) can be found [here](https://gist.github.com/denBot/4136279812f87819f86d99eba77c1ee0). +An example of how to create a named pipe and a subscription to `komorebi`'s handled events in Rust can also be found +in the [`komokana`](https://github.com/LGUG2Z/komokana) repository. + ### Subscription Event Notification Schema A [JSON Schema](https://json-schema.org/) of the event notifications emitted to subscribers can be generated with diff --git a/komorebi-core/src/lib.rs b/komorebi-core/src/lib.rs index 2d84eecb..28189c12 100644 --- a/komorebi-core/src/lib.rs +++ b/komorebi-core/src/lib.rs @@ -98,7 +98,7 @@ pub enum SocketMessage { WatchConfiguration(bool), CompleteConfiguration, ActiveWindowBorder(bool), - ActiveWindowBorderColour(u32, u32, u32), + ActiveWindowBorderColour(WindowKind, u32, u32, u32), InvisibleBorders(Rect), WorkAreaOffset(Rect), ResizeDelta(i32), @@ -134,6 +134,13 @@ impl FromStr for SocketMessage { } } +#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)] +#[strum(serialize_all = "snake_case")] +pub enum WindowKind { + Single, + Stack, +} + #[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)] #[strum(serialize_all = "snake_case")] pub enum StateQuery { diff --git a/komorebi.generated.ahk b/komorebi.generated.ahk index 09e2361b..d81e88b5 100644 --- a/komorebi.generated.ahk +++ b/komorebi.generated.ahk @@ -250,6 +250,9 @@ Run, komorebic.exe float-rule exe "RepoZ.exe", , Hide ; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line Run, komorebic.exe identify-tray-application exe "rbxfpsunlocker.exe", , Hide +; RoundedTB +Run, komorebic.exe float-rule exe "RoundedTB.exe", , Hide + ; RoundedTB Run, komorebic.exe identify-border-overflow-application exe "RoundedTB.exe", , Hide ; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line @@ -301,6 +304,9 @@ Run, komorebic.exe identify-tray-application exe "Telegram.exe", , Hide Run, komorebic.exe identify-tray-application exe "tcconfig.exe", , Hide Run, komorebic.exe float-rule exe "tcconfig.exe", , Hide +; TranslucentTB +Run, komorebic.exe float-rule exe "TranslucentTB.exe", , Hide + ; TranslucentTB ; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line Run, komorebic.exe identify-tray-application exe "TranslucentTB.exe", , Hide diff --git a/komorebi/src/border.rs b/komorebi/src/border.rs index 18f910ef..b190fa39 100644 --- a/komorebi/src/border.rs +++ b/komorebi/src/border.rs @@ -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) } } diff --git a/komorebi/src/main.rs b/komorebi/src/main.rs index 105b415b..101bbcae 100644 --- a/komorebi/src/main.rs +++ b/komorebi/src/main.rs @@ -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> = + 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() { diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index abb6f528..08bd06ef 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -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 }; diff --git a/komorebi/src/process_event.rs b/komorebi/src/process_event.rs index fbebe4e4..8f23f615 100644 --- a/komorebi/src/process_event.rs +++ b/komorebi/src/process_event.rs @@ -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); + } + } + } } } _ => {} diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index 706c50c4..4c7f1533 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -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))] diff --git a/komorebi/src/windows_api.rs b/komorebi/src/windows_api.rs index 28596fcd..55bca450 100644 --- a/komorebi/src/windows_api.rs +++ b/komorebi/src/windows_api.rs @@ -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 { 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 { @@ -692,16 +697,6 @@ impl WindowsApi { Ok(a == b) } - pub fn change_border_colour(hwnd: isize, r: u32, g: u32, b: u32) -> Result { - 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 { 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() + } } diff --git a/komorebi/src/windows_callbacks.rs b/komorebi/src/windows_callbacks.rs index 88805ace..9e65dc6c 100644 --- a/komorebi/src/windows_callbacks.rs +++ b/komorebi/src/windows_callbacks.rs @@ -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 => { diff --git a/komorebic.lib.ahk b/komorebic.lib.ahk index e2dd7162..044ac4d3 100644 --- a/komorebic.lib.ahk +++ b/komorebic.lib.ahk @@ -1,7 +1,7 @@ ; Generated by komorebic.exe Start(ffm, await_configuration) { - Run, komorebic.exe start %ffm% --await_configuration %await_configuration%, , Hide + Run, komorebic.exe start %ffm% --await-configuration %await_configuration%, , Hide } Stop() { @@ -304,8 +304,8 @@ ActiveWindowBorder(boolean_state) { Run, komorebic.exe active-window-border %boolean_state%, , Hide } -ActiveWindowBorderColour(r, g, b) { - Run, komorebic.exe active-window-border-colour %r% %g% %b%, , Hide +ActiveWindowBorderColour(r, g, b, window_kind) { + Run, komorebic.exe active-window-border-colour %r% %g% %b% --window-kind %window_kind%, , Hide } FocusFollowsMouse(boolean_state, implementation) { diff --git a/komorebic/src/main.rs b/komorebic/src/main.rs index 169a3d7c..a89a3888 100644 --- a/komorebic/src/main.rs +++ b/komorebic/src/main.rs @@ -43,6 +43,7 @@ use komorebi_core::Rect; use komorebi_core::Sizing; use komorebi_core::SocketMessage; use komorebi_core::StateQuery; +use komorebi_core::WindowKind; lazy_static! { static ref HOME_DIR: PathBuf = { @@ -401,6 +402,8 @@ struct ActiveWindowBorder { #[derive(Parser, AhkFunction)] struct ActiveWindowBorderColour { + #[clap(arg_enum, short, long, default_value = "single")] + window_kind: WindowKind, /// Red r: u32, /// Green @@ -1209,7 +1212,8 @@ fn main() -> Result<()> { } SubCommand::ActiveWindowBorderColour(arg) => { send_message( - &SocketMessage::ActiveWindowBorderColour(arg.r, arg.g, arg.b).as_bytes()?, + &SocketMessage::ActiveWindowBorderColour(arg.window_kind, arg.r, arg.g, arg.b) + .as_bytes()?, )?; } SubCommand::ResizeDelta(arg) => {