mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-01-11 22:12:53 +01:00
feat(wm): add active window border
This commit adds an optional active window border with a user-defined colour. This is achieved by spawning a dedicated "border window" and constantly placing it behind the focused window, or hiding it whenever necessary. Some constraints to note: - The border will only be applied to windows managed by komorebi - This means that if you temporarily float a window, it will lose the active window border - There are some issues where parts of the border will be broken by applications like Zoom, even if Zoom is behind the currently focused window - You probably want to turn off window shadows globally in Advanced System Settings -> Performance for the borders to have a consistent colour all the way around the window - There is some inevitable jank due to trying to reposition both the focused window and the "border window" behind it simultaneously - There are no borders for unfocused windows resolve #182
This commit is contained in:
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -494,6 +494,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"miow 0.4.0",
|
||||
"nanoid",
|
||||
"os_info",
|
||||
"parking_lot",
|
||||
"paste",
|
||||
"schemars",
|
||||
@@ -760,6 +761,17 @@ version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
||||
|
||||
[[package]]
|
||||
name = "os_info"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eca3ecae1481e12c3d9379ec541b238a16f0b75c9a409942daa8ec20dbfdb62"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.2.0"
|
||||
|
||||
@@ -4,5 +4,5 @@ members = [
|
||||
"derive-ahk",
|
||||
"komorebi",
|
||||
"komorebi-core",
|
||||
"komorebic"
|
||||
"komorebic",
|
||||
]
|
||||
|
||||
14
README.md
14
README.md
@@ -258,6 +258,17 @@ If you already have configuration files that you wish to keep, move them to the
|
||||
The next time you run `komorebic start`, any files created by or loaded by _komorebi_ will be placed or expected to
|
||||
exist in this folder.
|
||||
|
||||
#### Adding an Active Window Border
|
||||
|
||||
If you would like to add a visual border around the currently focused window, two commands are available:
|
||||
|
||||
```powershell
|
||||
komorebic.exe active-window-border [enable|disable]
|
||||
komorebic.exe active-window-border-colour [R G B]
|
||||
```
|
||||
|
||||
It is important to note that the active window border will only apply to windows managed by `komorebi`.
|
||||
|
||||
#### Removing Gaps
|
||||
|
||||
If you would like to remove all gaps from a given workspace, both between windows themselves, and between the monitor edges and the windows, you can set the following two configuration options to `0` for the desired monitors and workspaces:
|
||||
@@ -532,6 +543,7 @@ manage Force komorebi to manage the focused
|
||||
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
|
||||
@@ -543,6 +555,8 @@ identify-object-name-change-application Identify an application that sends EV
|
||||
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
|
||||
|
||||
@@ -97,6 +97,8 @@ pub enum SocketMessage {
|
||||
ReloadConfiguration,
|
||||
WatchConfiguration(bool),
|
||||
CompleteConfiguration,
|
||||
ActiveWindowBorder(bool),
|
||||
ActiveWindowBorderColour(u32, u32, u32),
|
||||
InvisibleBorders(Rect),
|
||||
WorkAreaOffset(Rect),
|
||||
ResizeDelta(i32),
|
||||
@@ -132,7 +134,7 @@ impl FromStr for SocketMessage {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum StateQuery {
|
||||
FocusedMonitorIndex,
|
||||
@@ -141,7 +143,7 @@ pub enum StateQuery {
|
||||
FocusedWindowIndex,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ApplicationIdentifier {
|
||||
@@ -150,7 +152,7 @@ pub enum ApplicationIdentifier {
|
||||
Title,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum FocusFollowsMouseImplementation {
|
||||
Komorebi,
|
||||
@@ -171,7 +173,7 @@ pub enum MoveBehaviour {
|
||||
Insert,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum HidingBehaviour {
|
||||
Hide,
|
||||
|
||||
@@ -23,9 +23,12 @@ dirs = "4"
|
||||
getset = "0.1"
|
||||
hotwatch = "0.4"
|
||||
lazy_static = "1"
|
||||
miow = "0.4"
|
||||
nanoid = "0.4"
|
||||
os_info = "3.4"
|
||||
parking_lot = { version = "0.12", features = ["deadlock_detection"] }
|
||||
paste = "1"
|
||||
schemars = "0.8"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
strum = { version = "0.24", features = ["derive"] }
|
||||
@@ -36,9 +39,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
uds_windows = "1"
|
||||
which = "4"
|
||||
winput = "0.2"
|
||||
miow = "0.4"
|
||||
winreg = "0.10"
|
||||
schemars = "0.8"
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.39"
|
||||
@@ -46,12 +47,15 @@ features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Graphics_Dwm",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_System_Threading",
|
||||
"Win32_System_LibraryLoader",
|
||||
"Win32_System_RemoteDesktop",
|
||||
"Win32_System_Threading",
|
||||
"Win32_UI_Accessibility",
|
||||
"Win32_UI_HiDpi",
|
||||
"Win32_UI_Input_KeyboardAndMouse",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
"Win32_UI_Shell",
|
||||
"Win32_UI_Shell_Common",
|
||||
"Win32_UI_WindowsAndMessaging"
|
||||
]
|
||||
|
||||
[features]
|
||||
|
||||
119
komorebi/src/border.rs
Normal file
119
komorebi/src/border.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
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;
|
||||
use windows::Win32::UI::WindowsAndMessaging::FindWindowA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetMessageA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::MSG;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSA;
|
||||
|
||||
use crate::window::Window;
|
||||
use crate::windows_callbacks;
|
||||
use crate::WindowsApi;
|
||||
use crate::BORDER_HWND;
|
||||
use crate::BORDER_OVERFLOW_IDENTIFIERS;
|
||||
use crate::WINDOWS_11;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Border {
|
||||
pub(crate) hwnd: isize,
|
||||
}
|
||||
|
||||
impl From<isize> for Border {
|
||||
fn from(hwnd: isize) -> Self {
|
||||
Self { hwnd }
|
||||
}
|
||||
}
|
||||
|
||||
impl Border {
|
||||
pub const fn hwnd(self) -> HWND {
|
||||
HWND(self.hwnd)
|
||||
}
|
||||
|
||||
pub fn create(name: &str) -> Result<()> {
|
||||
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 window_class = WNDCLASSA {
|
||||
hInstance: instance,
|
||||
lpszClassName: class_name,
|
||||
style: CS_HREDRAW | CS_VREDRAW,
|
||||
lpfnWndProc: Some(windows_callbacks::border_window),
|
||||
hbrBackground: brush,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let _atom = WindowsApi::register_class_a(&window_class)?;
|
||||
|
||||
let name_cl = name.clone();
|
||||
std::thread::spawn(move || -> Result<()> {
|
||||
let hwnd = WindowsApi::create_border_window(PCSTR(name_cl.as_ptr()), instance)?;
|
||||
let border = Self::from(hwnd);
|
||||
|
||||
let mut message = MSG::default();
|
||||
|
||||
unsafe {
|
||||
while GetMessageA(&mut message, border.hwnd(), 0, 0).into() {
|
||||
DispatchMessageA(&message);
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
let mut hwnd = HWND(0);
|
||||
while hwnd == HWND(0) {
|
||||
hwnd = unsafe { FindWindowA(PCSTR(name.as_ptr()), PCSTR::null()) };
|
||||
}
|
||||
|
||||
BORDER_HWND.store(hwnd.0, Ordering::SeqCst);
|
||||
|
||||
if *WINDOWS_11 {
|
||||
WindowsApi::round_corners(hwnd.0)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn hide(self) -> Result<()> {
|
||||
WindowsApi::hide_border_window(self.hwnd())
|
||||
}
|
||||
|
||||
pub fn set_position(
|
||||
self,
|
||||
window: Window,
|
||||
invisible_borders: &Rect,
|
||||
activate: bool,
|
||||
) -> Result<()> {
|
||||
let mut should_expand_border = false;
|
||||
|
||||
let mut rect = WindowsApi::window_rect(window.hwnd())?;
|
||||
rect.top -= invisible_borders.bottom;
|
||||
rect.bottom += invisible_borders.bottom;
|
||||
|
||||
let border_overflows = BORDER_OVERFLOW_IDENTIFIERS.lock();
|
||||
if border_overflows.contains(&window.title()?)
|
||||
|| border_overflows.contains(&window.exe()?)
|
||||
|| border_overflows.contains(&window.class()?)
|
||||
{
|
||||
should_expand_border = true;
|
||||
}
|
||||
|
||||
if should_expand_border {
|
||||
rect.left -= invisible_borders.left;
|
||||
rect.top -= invisible_borders.top;
|
||||
rect.right += invisible_borders.right;
|
||||
rect.bottom += invisible_borders.bottom;
|
||||
}
|
||||
|
||||
WindowsApi::position_border_window(self.hwnd(), &rect, activate)
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::AtomicIsize;
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
@@ -20,6 +21,7 @@ use crossbeam_channel::Receiver;
|
||||
use crossbeam_channel::Sender;
|
||||
use crossbeam_utils::Backoff;
|
||||
use lazy_static::lazy_static;
|
||||
use os_info::Version;
|
||||
#[cfg(feature = "deadlock_detection")]
|
||||
use parking_lot::deadlock;
|
||||
use parking_lot::Mutex;
|
||||
@@ -35,6 +37,7 @@ use which::which;
|
||||
use winreg::enums::HKEY_CURRENT_USER;
|
||||
use winreg::RegKey;
|
||||
|
||||
use crate::border::Border;
|
||||
use komorebi_core::HidingBehaviour;
|
||||
use komorebi_core::SocketMessage;
|
||||
|
||||
@@ -49,6 +52,7 @@ use crate::windows_api::WindowsApi;
|
||||
#[macro_use]
|
||||
mod ring;
|
||||
|
||||
mod border;
|
||||
mod container;
|
||||
mod monitor;
|
||||
mod process_command;
|
||||
@@ -140,11 +144,20 @@ lazy_static! {
|
||||
|
||||
ahk_v2
|
||||
};
|
||||
|
||||
static ref WINDOWS_11: bool = {
|
||||
matches!(
|
||||
os_info::get().version(),
|
||||
Version::Semantic(_, _, x) if x >= &22000
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
pub static INITIAL_CONFIGURATION_LOADED: AtomicBool = AtomicBool::new(false);
|
||||
pub static CUSTOM_FFM: AtomicBool = AtomicBool::new(false);
|
||||
pub static SESSION_ID: AtomicU32 = AtomicU32::new(0);
|
||||
pub static BORDER_HWND: AtomicIsize = AtomicIsize::new(0);
|
||||
pub static BORDER_HIDDEN: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
fn setup() -> Result<(WorkerGuard, WorkerGuard)> {
|
||||
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
|
||||
@@ -415,6 +428,7 @@ fn main() -> Result<()> {
|
||||
let process_id = WindowsApi::current_process_id();
|
||||
WindowsApi::allow_set_foreground_window(process_id)?;
|
||||
WindowsApi::set_process_dpi_awareness_context()?;
|
||||
Border::create("komorebi-border-window")?;
|
||||
|
||||
let (outgoing, incoming): (Sender<WindowManagerEvent>, Receiver<WindowManagerEvent>) =
|
||||
crossbeam_channel::unbounded();
|
||||
|
||||
@@ -15,6 +15,7 @@ use parking_lot::Mutex;
|
||||
use schemars::schema_for;
|
||||
use uds_windows::UnixStream;
|
||||
|
||||
use crate::border::Border;
|
||||
use komorebi_core::ApplicationIdentifier;
|
||||
use komorebi_core::Axis;
|
||||
use komorebi_core::FocusFollowsMouseImplementation;
|
||||
@@ -29,11 +30,13 @@ use komorebi_core::WindowContainerBehaviour;
|
||||
|
||||
use crate::current_virtual_desktop;
|
||||
use crate::notify_subscribers;
|
||||
use crate::window::Window;
|
||||
use crate::window_manager;
|
||||
use crate::window_manager::WindowManager;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::Notification;
|
||||
use crate::NotificationEvent;
|
||||
use crate::BORDER_HWND;
|
||||
use crate::BORDER_OVERFLOW_IDENTIFIERS;
|
||||
use crate::CUSTOM_FFM;
|
||||
use crate::FLOAT_IDENTIFIERS;
|
||||
@@ -115,24 +118,24 @@ impl WindowManager {
|
||||
SocketMessage::WorkspacePadding(monitor_idx, workspace_idx, size) => {
|
||||
self.set_workspace_padding(monitor_idx, workspace_idx, size)?;
|
||||
}
|
||||
SocketMessage::WorkspaceRule(_, id, monitor_idx, workspace_idx) => {
|
||||
SocketMessage::WorkspaceRule(_, ref id, monitor_idx, workspace_idx) => {
|
||||
{
|
||||
let mut workspace_rules = WORKSPACE_RULES.lock();
|
||||
workspace_rules.insert(id, (monitor_idx, workspace_idx));
|
||||
workspace_rules.insert(id.to_string(), (monitor_idx, workspace_idx));
|
||||
}
|
||||
|
||||
self.enforce_workspace_rules()?;
|
||||
}
|
||||
SocketMessage::ManageRule(_, id) => {
|
||||
SocketMessage::ManageRule(_, ref id) => {
|
||||
let mut manage_identifiers = MANAGE_IDENTIFIERS.lock();
|
||||
if !manage_identifiers.contains(&id) {
|
||||
manage_identifiers.push(id);
|
||||
if !manage_identifiers.contains(id) {
|
||||
manage_identifiers.push(id.to_string());
|
||||
}
|
||||
}
|
||||
SocketMessage::FloatRule(identifier, id) => {
|
||||
SocketMessage::FloatRule(identifier, ref id) => {
|
||||
let mut float_identifiers = FLOAT_IDENTIFIERS.lock();
|
||||
if !float_identifiers.contains(&id) {
|
||||
float_identifiers.push(id.clone());
|
||||
if !float_identifiers.contains(id) {
|
||||
float_identifiers.push(id.to_string());
|
||||
}
|
||||
|
||||
let invisible_borders = self.invisible_borders;
|
||||
@@ -149,17 +152,17 @@ impl WindowManager {
|
||||
for window in container.windows().iter() {
|
||||
match identifier {
|
||||
ApplicationIdentifier::Exe => {
|
||||
if window.exe()? == id {
|
||||
if window.exe()? == *id {
|
||||
hwnds_to_purge.push((i, window.hwnd));
|
||||
}
|
||||
}
|
||||
ApplicationIdentifier::Class => {
|
||||
if window.class()? == id {
|
||||
if window.class()? == *id {
|
||||
hwnds_to_purge.push((i, window.hwnd));
|
||||
}
|
||||
}
|
||||
ApplicationIdentifier::Title => {
|
||||
if window.title()? == id {
|
||||
if window.title()? == *id {
|
||||
hwnds_to_purge.push((i, window.hwnd));
|
||||
}
|
||||
}
|
||||
@@ -236,9 +239,11 @@ impl WindowManager {
|
||||
SocketMessage::Retile => self.retile_all(false)?,
|
||||
SocketMessage::FlipLayout(layout_flip) => self.flip_layout(layout_flip)?,
|
||||
SocketMessage::ChangeLayout(layout) => self.change_workspace_layout_default(layout)?,
|
||||
SocketMessage::ChangeLayoutCustom(path) => self.change_workspace_custom_layout(path)?,
|
||||
SocketMessage::WorkspaceLayoutCustom(monitor_idx, workspace_idx, path) => {
|
||||
self.set_workspace_layout_custom(monitor_idx, workspace_idx, path)?;
|
||||
SocketMessage::ChangeLayoutCustom(ref path) => {
|
||||
self.change_workspace_custom_layout(path.clone())?;
|
||||
}
|
||||
SocketMessage::WorkspaceLayoutCustom(monitor_idx, workspace_idx, ref path) => {
|
||||
self.set_workspace_layout_custom(monitor_idx, workspace_idx, path.clone())?;
|
||||
}
|
||||
SocketMessage::WorkspaceTiling(monitor_idx, workspace_idx, tile) => {
|
||||
self.set_workspace_tiling(monitor_idx, workspace_idx, tile)?;
|
||||
@@ -263,13 +268,13 @@ impl WindowManager {
|
||||
monitor_idx,
|
||||
workspace_idx,
|
||||
at_container_count,
|
||||
path,
|
||||
ref path,
|
||||
) => {
|
||||
self.add_workspace_layout_custom_rule(
|
||||
monitor_idx,
|
||||
workspace_idx,
|
||||
at_container_count,
|
||||
path,
|
||||
path.clone(),
|
||||
)?;
|
||||
}
|
||||
SocketMessage::ClearWorkspaceLayoutRules(monitor_idx, workspace_idx) => {
|
||||
@@ -333,8 +338,8 @@ impl WindowManager {
|
||||
SocketMessage::NewWorkspace => {
|
||||
self.new_workspace()?;
|
||||
}
|
||||
SocketMessage::WorkspaceName(monitor_idx, workspace_idx, name) => {
|
||||
self.set_workspace_name(monitor_idx, workspace_idx, name)?;
|
||||
SocketMessage::WorkspaceName(monitor_idx, workspace_idx, ref name) => {
|
||||
self.set_workspace_name(monitor_idx, workspace_idx, name.to_string())?;
|
||||
}
|
||||
SocketMessage::State => {
|
||||
let state = match serde_json::to_string_pretty(&window_manager::State::from(&*self))
|
||||
@@ -583,28 +588,28 @@ impl WindowManager {
|
||||
SocketMessage::WatchConfiguration(enable) => {
|
||||
self.watch_configuration(enable)?;
|
||||
}
|
||||
SocketMessage::IdentifyBorderOverflowApplication(_, id) => {
|
||||
SocketMessage::IdentifyBorderOverflowApplication(_, ref id) => {
|
||||
let mut identifiers = BORDER_OVERFLOW_IDENTIFIERS.lock();
|
||||
if !identifiers.contains(&id) {
|
||||
identifiers.push(id);
|
||||
if !identifiers.contains(id) {
|
||||
identifiers.push(id.to_string());
|
||||
}
|
||||
}
|
||||
SocketMessage::IdentifyObjectNameChangeApplication(_, id) => {
|
||||
SocketMessage::IdentifyObjectNameChangeApplication(_, ref id) => {
|
||||
let mut identifiers = OBJECT_NAME_CHANGE_ON_LAUNCH.lock();
|
||||
if !identifiers.contains(&id) {
|
||||
identifiers.push(id);
|
||||
if !identifiers.contains(id) {
|
||||
identifiers.push(id.to_string());
|
||||
}
|
||||
}
|
||||
SocketMessage::IdentifyTrayApplication(_, id) => {
|
||||
SocketMessage::IdentifyTrayApplication(_, ref id) => {
|
||||
let mut identifiers = TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock();
|
||||
if !identifiers.contains(&id) {
|
||||
identifiers.push(id);
|
||||
if !identifiers.contains(id) {
|
||||
identifiers.push(id.to_string());
|
||||
}
|
||||
}
|
||||
SocketMessage::IdentifyLayeredApplication(_, id) => {
|
||||
SocketMessage::IdentifyLayeredApplication(_, ref id) => {
|
||||
let mut identifiers = LAYERED_WHITELIST.lock();
|
||||
if !identifiers.contains(&id) {
|
||||
identifiers.push(id);
|
||||
if !identifiers.contains(id) {
|
||||
identifiers.push(id.to_string());
|
||||
}
|
||||
}
|
||||
SocketMessage::ManageFocusedWindow => {
|
||||
@@ -654,7 +659,7 @@ impl WindowManager {
|
||||
workspace.set_resize_dimensions(resize);
|
||||
self.update_focused_workspace(false)?;
|
||||
}
|
||||
SocketMessage::Save(path) => {
|
||||
SocketMessage::Save(ref path) => {
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
let resize = workspace.resize_dimensions();
|
||||
|
||||
@@ -662,14 +667,14 @@ impl WindowManager {
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.create(true)
|
||||
.open(path)?;
|
||||
.open(path.clone())?;
|
||||
|
||||
serde_json::to_writer_pretty(&file, &resize)?;
|
||||
}
|
||||
SocketMessage::Load(path) => {
|
||||
SocketMessage::Load(ref path) => {
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
|
||||
let file = File::open(&path)
|
||||
let file = File::open(path)
|
||||
.map_err(|_| anyhow!("no file found at {}", path.display().to_string()))?;
|
||||
|
||||
let resize: Vec<Option<Rect>> = serde_json::from_reader(file)?;
|
||||
@@ -677,18 +682,18 @@ impl WindowManager {
|
||||
workspace.set_resize_dimensions(resize);
|
||||
self.update_focused_workspace(false)?;
|
||||
}
|
||||
SocketMessage::AddSubscriber(subscriber) => {
|
||||
SocketMessage::AddSubscriber(ref subscriber) => {
|
||||
let mut pipes = SUBSCRIPTION_PIPES.lock();
|
||||
let pipe_path = format!(r"\\.\pipe\{}", subscriber);
|
||||
let pipe = connect(&pipe_path).map_err(|_| {
|
||||
anyhow!("the named pipe '{}' has not yet been created; please create it before running this command", pipe_path)
|
||||
})?;
|
||||
|
||||
pipes.insert(subscriber, pipe);
|
||||
pipes.insert(subscriber.clone(), pipe);
|
||||
}
|
||||
SocketMessage::RemoveSubscriber(subscriber) => {
|
||||
SocketMessage::RemoveSubscriber(ref subscriber) => {
|
||||
let mut pipes = SUBSCRIPTION_PIPES.lock();
|
||||
pipes.remove(&subscriber);
|
||||
pipes.remove(subscriber);
|
||||
}
|
||||
SocketMessage::MouseFollowsFocus(enable) => {
|
||||
self.mouse_follows_focus = enable;
|
||||
@@ -729,6 +734,17 @@ impl WindowManager {
|
||||
SocketMessage::UnmanagedWindowOperationBehaviour(behaviour) => {
|
||||
self.unmanaged_window_operation_behaviour = behaviour;
|
||||
}
|
||||
SocketMessage::ActiveWindowBorder(enable) => {
|
||||
if enable {
|
||||
self.show_border()?;
|
||||
} else {
|
||||
self.hide_border()?;
|
||||
}
|
||||
}
|
||||
SocketMessage::ActiveWindowBorderColour(r, g, b) => {
|
||||
let hwnd = BORDER_HWND.load(Ordering::SeqCst);
|
||||
WindowsApi::change_border_colour(hwnd, r, g, b)?;
|
||||
}
|
||||
SocketMessage::NotificationSchema => {
|
||||
let notification = schema_for!(Notification);
|
||||
let schema = serde_json::to_string_pretty(¬ification)?;
|
||||
@@ -741,6 +757,68 @@ impl WindowManager {
|
||||
}
|
||||
};
|
||||
|
||||
match message {
|
||||
SocketMessage::ChangeLayout(_)
|
||||
| SocketMessage::ChangeLayoutCustom(_)
|
||||
| SocketMessage::FlipLayout(_)
|
||||
| SocketMessage::ManageFocusedWindow
|
||||
| SocketMessage::MoveWorkspaceToMonitorNumber(_)
|
||||
| SocketMessage::MoveContainerToMonitorNumber(_)
|
||||
| SocketMessage::MoveContainerToWorkspaceNumber(_)
|
||||
| SocketMessage::ResizeWindowEdge(_, _)
|
||||
| SocketMessage::ResizeWindowAxis(_, _)
|
||||
| SocketMessage::ToggleFloat
|
||||
| SocketMessage::ToggleMonocle
|
||||
| SocketMessage::ToggleMaximize
|
||||
| SocketMessage::Promote
|
||||
| SocketMessage::Retile
|
||||
| SocketMessage::MoveWindow(_) => {
|
||||
let foreground = WindowsApi::foreground_window()?;
|
||||
let foreground_window = Window { hwnd: foreground };
|
||||
let mut rect = WindowsApi::window_rect(foreground_window.hwnd())?;
|
||||
rect.top -= self.invisible_borders.bottom;
|
||||
rect.bottom += self.invisible_borders.bottom;
|
||||
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
border.set_position(foreground_window, &self.invisible_borders, false)?;
|
||||
}
|
||||
SocketMessage::CycleFocusMonitor(_)
|
||||
| SocketMessage::CycleFocusWorkspace(_)
|
||||
| SocketMessage::FocusMonitorNumber(_)
|
||||
| SocketMessage::FocusMonitorWorkspaceNumber(_, _)
|
||||
| SocketMessage::FocusWorkspaceNumber(_) => {
|
||||
if self.focused_workspace()?.visible_windows().is_empty() {
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
border.hide()?;
|
||||
}
|
||||
}
|
||||
SocketMessage::TogglePause => {
|
||||
let is_paused = self.is_paused;
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
|
||||
if is_paused {
|
||||
border.hide()?;
|
||||
} else {
|
||||
let focused = self.focused_window()?;
|
||||
border.set_position(*focused, &self.invisible_borders, true)?;
|
||||
focused.focus(false)?;
|
||||
}
|
||||
}
|
||||
SocketMessage::ToggleTiling => {
|
||||
let tiling_enabled = *self.focused_workspace_mut()?.tile();
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
|
||||
if tiling_enabled {
|
||||
let focused = self.focused_window()?;
|
||||
border.set_position(*focused, &self.invisible_borders, true)?;
|
||||
focused.focus(false)?;
|
||||
} else {
|
||||
border.hide()?;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
tracing::info!("processed");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
use std::fs::OpenOptions;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::Result;
|
||||
use crossbeam_channel::select;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use crate::border::Border;
|
||||
use komorebi_core::OperationDirection;
|
||||
use komorebi_core::Rect;
|
||||
use komorebi_core::Sizing;
|
||||
@@ -18,6 +21,9 @@ use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::Notification;
|
||||
use crate::NotificationEvent;
|
||||
use crate::BORDER_HIDDEN;
|
||||
use crate::BORDER_HWND;
|
||||
|
||||
use crate::DATA_DIR;
|
||||
use crate::HIDDEN_HWNDS;
|
||||
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
||||
@@ -469,11 +475,42 @@ impl WindowManager {
|
||||
WindowManagerEvent::MonitorPoll(..) | WindowManagerEvent::MouseCapture(..) => {}
|
||||
};
|
||||
|
||||
match event {
|
||||
WindowManagerEvent::MoveResizeStart(_, _) => {
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
border.hide()?;
|
||||
BORDER_HIDDEN.store(true, Ordering::SeqCst);
|
||||
}
|
||||
WindowManagerEvent::MoveResizeEnd(_, window)
|
||||
| WindowManagerEvent::Show(_, window)
|
||||
| WindowManagerEvent::FocusChange(_, 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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// If we unmanaged a window, it shouldn't be immediately hidden behind managed windows
|
||||
if let WindowManagerEvent::Unmanage(window) = event {
|
||||
window.center(&self.focused_monitor_work_area()?, &invisible_borders)?;
|
||||
}
|
||||
|
||||
// If there are no more windows on the workspace, we shouldn't show the border window
|
||||
if self.focused_workspace()?.containers().is_empty() {
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
border.hide()?;
|
||||
BORDER_HIDDEN.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
tracing::trace!("updating list of known hwnds");
|
||||
let mut known_hwnds = vec![];
|
||||
for monitor in self.monitors() {
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn listen_for_movements(wm: Arc<Mutex<WindowManager>>) {
|
||||
let receiver = message_loop::start().expect("could not start winput message loop");
|
||||
|
||||
loop {
|
||||
let focus_follows_mouse = wm.lock().focus_follows_mouse.clone();
|
||||
let focus_follows_mouse = wm.lock().focus_follows_mouse;
|
||||
if let Some(FocusFollowsMouseImplementation::Komorebi) = focus_follows_mouse {
|
||||
match receiver.next_event() {
|
||||
// Don't want to send any raise events while we are dragging or resizing
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::collections::VecDeque;
|
||||
use std::io::ErrorKind;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
use color_eyre::eyre::anyhow;
|
||||
@@ -14,6 +15,7 @@ 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;
|
||||
@@ -38,6 +40,7 @@ use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::winevent_listener::WINEVENT_CALLBACK_CHANNEL;
|
||||
use crate::workspace::Workspace;
|
||||
use crate::BORDER_HWND;
|
||||
use crate::BORDER_OVERFLOW_IDENTIFIERS;
|
||||
use crate::DATA_DIR;
|
||||
use crate::FLOAT_IDENTIFIERS;
|
||||
@@ -104,7 +107,7 @@ impl From<&WindowManager> for State {
|
||||
resize_delta: wm.resize_delta,
|
||||
new_window_behaviour: wm.window_container_behaviour,
|
||||
cross_monitor_move_behaviour: wm.cross_monitor_move_behaviour,
|
||||
focus_follows_mouse: wm.focus_follows_mouse.clone(),
|
||||
focus_follows_mouse: wm.focus_follows_mouse,
|
||||
mouse_follows_focus: wm.mouse_follows_focus,
|
||||
has_pending_raise_op: wm.has_pending_raise_op,
|
||||
float_identifiers: FLOAT_IDENTIFIERS.lock().clone(),
|
||||
@@ -193,6 +196,26 @@ impl WindowManager {
|
||||
WindowsApi::load_workspace_information(&mut self.monitors)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn show_border(&self) -> Result<()> {
|
||||
let foreground = WindowsApi::foreground_window()?;
|
||||
let foreground_window = Window { hwnd: foreground };
|
||||
let mut rect = WindowsApi::window_rect(foreground_window.hwnd())?;
|
||||
rect.top -= self.invisible_borders.bottom;
|
||||
rect.bottom += self.invisible_borders.bottom;
|
||||
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
border.set_position(foreground_window, &self.invisible_borders, true)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn hide_border(&self) -> Result<()> {
|
||||
let focused = self.focused_window()?;
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
border.hide()?;
|
||||
focused.focus(false)
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub fn reload_configuration() {
|
||||
tracing::info!("reloading configuration");
|
||||
@@ -267,7 +290,7 @@ impl WindowManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn monitor_index_in_direction(&self, direction: OperationDirection) -> Option<usize> {
|
||||
pub fn monitor_idx_in_direction(&self, direction: OperationDirection) -> Option<usize> {
|
||||
let current_monitor_size = self.focused_monitor_size().ok()?;
|
||||
|
||||
for (idx, monitor) in self.monitors.elements().iter().enumerate() {
|
||||
@@ -953,7 +976,7 @@ impl WindowManager {
|
||||
match new_idx {
|
||||
None => {
|
||||
let monitor_idx = self
|
||||
.monitor_index_in_direction(direction)
|
||||
.monitor_idx_in_direction(direction)
|
||||
.ok_or_else(|| anyhow!("there is no container or monitor in this direction"))?;
|
||||
|
||||
self.focus_monitor(monitor_idx)?;
|
||||
@@ -991,7 +1014,7 @@ impl WindowManager {
|
||||
// in that direction if there is one
|
||||
None => {
|
||||
let target_monitor_idx = self
|
||||
.monitor_index_in_direction(direction)
|
||||
.monitor_idx_in_direction(direction)
|
||||
.ok_or_else(|| anyhow!("there is no container or monitor in this direction"))?;
|
||||
|
||||
{
|
||||
@@ -1069,6 +1092,20 @@ impl WindowManager {
|
||||
.get_mut(origin_monitor_idx)
|
||||
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
|
||||
.update_focused_workspace(offset, &invisible_borders)?;
|
||||
|
||||
let a = self
|
||||
.focused_monitor()
|
||||
.ok_or_else(|| anyhow!("there is no monitor focused monitor"))?
|
||||
.id();
|
||||
let b = self
|
||||
.monitors_mut()
|
||||
.get_mut(origin_monitor_idx)
|
||||
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
|
||||
.id();
|
||||
|
||||
if !WindowsApi::monitors_have_same_scale_factor(a, b)? {
|
||||
self.update_focused_workspace(self.mouse_follows_focus)?;
|
||||
}
|
||||
}
|
||||
Some(new_idx) => {
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
|
||||
@@ -6,29 +6,37 @@ use color_eyre::eyre::anyhow;
|
||||
use color_eyre::eyre::Error;
|
||||
use color_eyre::Result;
|
||||
use windows::core::Result as WindowsCrateResult;
|
||||
use windows::core::PCSTR;
|
||||
use windows::core::PWSTR;
|
||||
use windows::Win32::Foundation::BOOL;
|
||||
use windows::Win32::Foundation::HANDLE;
|
||||
use windows::Win32::Foundation::HINSTANCE;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
use windows::Win32::Foundation::LPARAM;
|
||||
use windows::Win32::Foundation::POINT;
|
||||
use windows::Win32::Foundation::RECT;
|
||||
use windows::Win32::Graphics::Dwm::DwmGetWindowAttribute;
|
||||
use windows::Win32::Graphics::Dwm::DwmSetWindowAttribute;
|
||||
use windows::Win32::Graphics::Dwm::DWMWA_CLOAKED;
|
||||
use windows::Win32::Graphics::Dwm::DWMWA_EXTENDED_FRAME_BOUNDS;
|
||||
use windows::Win32::Graphics::Dwm::DWMWA_WINDOW_CORNER_PREFERENCE;
|
||||
use windows::Win32::Graphics::Dwm::DWMWCP_ROUND;
|
||||
use windows::Win32::Graphics::Dwm::DWMWINDOWATTRIBUTE;
|
||||
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_APP;
|
||||
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_INHERITED;
|
||||
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::MonitorFromPoint;
|
||||
use windows::Win32::Graphics::Gdi::MonitorFromWindow;
|
||||
use windows::Win32::Graphics::Gdi::HBRUSH;
|
||||
use windows::Win32::Graphics::Gdi::HDC;
|
||||
use windows::Win32::Graphics::Gdi::HMONITOR;
|
||||
use windows::Win32::Graphics::Gdi::MONITORENUMPROC;
|
||||
use windows::Win32::Graphics::Gdi::MONITORINFO;
|
||||
use windows::Win32::Graphics::Gdi::MONITOR_DEFAULTTONEAREST;
|
||||
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
|
||||
use windows::Win32::System::RemoteDesktop::ProcessIdToSessionId;
|
||||
use windows::Win32::System::Threading::AttachThreadInput;
|
||||
use windows::Win32::System::Threading::GetCurrentProcessId;
|
||||
@@ -41,7 +49,10 @@ use windows::Win32::System::Threading::PROCESS_QUERY_INFORMATION;
|
||||
use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext;
|
||||
use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
|
||||
use windows::Win32::UI::Input::KeyboardAndMouse::SetFocus;
|
||||
use windows::Win32::UI::Shell::Common::DEVICE_SCALE_FACTOR;
|
||||
use windows::Win32::UI::Shell::GetScaleFactorForMonitor;
|
||||
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
|
||||
use windows::Win32::UI::WindowsAndMessaging::CreateWindowExA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::EnumWindows;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetCursorPos;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetDesktopWindow;
|
||||
@@ -56,6 +67,8 @@ use windows::Win32::UI::WindowsAndMessaging::IsIconic;
|
||||
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::SetWindowLongPtrW;
|
||||
@@ -63,9 +76,12 @@ 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::SET_WINDOW_POS_FLAGS;
|
||||
@@ -81,7 +97,13 @@ use windows::Win32::UI::WindowsAndMessaging::SW_RESTORE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_ACTION;
|
||||
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_TOOLWINDOW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_MAXIMIZEBOX;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_MINIMIZEBOX;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_SYSMENU;
|
||||
|
||||
use komorebi_core::Rect;
|
||||
|
||||
@@ -112,7 +134,7 @@ macro_rules! impl_from_integer_for_windows_result {
|
||||
};
|
||||
}
|
||||
|
||||
impl_from_integer_for_windows_result!(isize, u32, i32);
|
||||
impl_from_integer_for_windows_result!(usize, isize, u16, u32, i32);
|
||||
|
||||
impl<T, E> From<WindowsResult<T, E>> for Result<T, E> {
|
||||
fn from(result: WindowsResult<T, E>) -> Self {
|
||||
@@ -261,6 +283,24 @@ impl WindowsApi {
|
||||
Self::set_window_pos(hwnd, layout, position, flags.bits())
|
||||
}
|
||||
|
||||
pub fn position_border_window(hwnd: HWND, layout: &Rect, activate: bool) -> Result<()> {
|
||||
let flags = if activate {
|
||||
SetWindowPosition::SHOW_WINDOW | SetWindowPosition::NO_ACTIVATE
|
||||
} else {
|
||||
SetWindowPosition::NO_ACTIVATE
|
||||
};
|
||||
|
||||
let position = HWND_BOTTOM;
|
||||
Self::set_window_pos(hwnd, layout, position, flags.bits())
|
||||
}
|
||||
|
||||
pub fn hide_border_window(hwnd: HWND) -> Result<()> {
|
||||
let flags = SetWindowPosition::HIDE_WINDOW;
|
||||
|
||||
let position = HWND_BOTTOM;
|
||||
Self::set_window_pos(hwnd, &Rect::default(), position, flags.bits())
|
||||
}
|
||||
|
||||
pub fn set_window_pos(hwnd: HWND, layout: &Rect, position: HWND, flags: u32) -> Result<()> {
|
||||
unsafe {
|
||||
SetWindowPos(
|
||||
@@ -443,6 +483,11 @@ impl WindowsApi {
|
||||
Self::set_window_long_ptr_w(hwnd, GWL_STYLE, new_value)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn update_ex_style(hwnd: HWND, new_value: isize) -> Result<()> {
|
||||
Self::set_window_long_ptr_w(hwnd, GWL_EXSTYLE, new_value)
|
||||
}
|
||||
|
||||
pub fn window_text_w(hwnd: HWND) -> Result<String> {
|
||||
let mut text: [u16; 512] = [0; 512];
|
||||
match WindowsResult::from(unsafe { GetWindowTextW(hwnd, &mut text) }) {
|
||||
@@ -623,4 +668,71 @@ impl WindowsApi {
|
||||
SPIF_SENDCHANGE,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn module_handle_w() -> Result<HINSTANCE> {
|
||||
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 register_class_a(window_class: &WNDCLASSA) -> Result<u16> {
|
||||
Result::from(WindowsResult::from(unsafe { RegisterClassA(window_class) }))
|
||||
}
|
||||
|
||||
pub fn scale_factor_for_monitor(hmonitor: isize) -> Result<DEVICE_SCALE_FACTOR> {
|
||||
unsafe { GetScaleFactorForMonitor(HMONITOR(hmonitor)) }.process()
|
||||
}
|
||||
|
||||
pub fn monitors_have_same_scale_factor(a: isize, b: isize) -> Result<bool> {
|
||||
let a = Self::scale_factor_for_monitor(a)?;
|
||||
let b = Self::scale_factor_for_monitor(b)?;
|
||||
|
||||
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;
|
||||
|
||||
unsafe {
|
||||
DwmSetWindowAttribute(
|
||||
HWND(hwnd),
|
||||
DWMWA_WINDOW_CORNER_PREFERENCE,
|
||||
std::ptr::addr_of!(round).cast(),
|
||||
4,
|
||||
)
|
||||
}
|
||||
.process()
|
||||
}
|
||||
|
||||
pub fn create_border_window(name: PCSTR, instance: HINSTANCE) -> Result<isize> {
|
||||
unsafe {
|
||||
CreateWindowExA(
|
||||
WS_EX_TOOLWINDOW,
|
||||
name,
|
||||
name,
|
||||
WS_POPUP | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
None,
|
||||
None,
|
||||
instance,
|
||||
std::ptr::null(),
|
||||
)
|
||||
}
|
||||
.process()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,17 @@ use std::collections::VecDeque;
|
||||
use windows::Win32::Foundation::BOOL;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
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::HDC;
|
||||
use windows::Win32::Graphics::Gdi::HMONITOR;
|
||||
use windows::Win32::UI::Accessibility::HWINEVENTHOOK;
|
||||
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WM_PAINT;
|
||||
|
||||
use crate::container::Container;
|
||||
use crate::monitor::Monitor;
|
||||
@@ -103,3 +110,24 @@ pub extern "system" fn win_event_hook(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub extern "system" fn border_window(
|
||||
window: HWND,
|
||||
message: u32,
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
) -> LRESULT {
|
||||
unsafe {
|
||||
match message as u32 {
|
||||
WM_PAINT => {
|
||||
InvalidateRect(window, std::ptr::null(), true);
|
||||
LRESULT(0)
|
||||
}
|
||||
WM_DESTROY => {
|
||||
PostQuitMessage(0);
|
||||
LRESULT(0)
|
||||
}
|
||||
_ => DefWindowProcW(window, message, wparam, lparam),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@ impl WinEventListener {
|
||||
|
||||
std::thread::spawn(move || unsafe {
|
||||
let hook_ref = SetWinEventHook(
|
||||
EVENT_MIN as u32,
|
||||
EVENT_MAX as u32,
|
||||
EVENT_MIN,
|
||||
EVENT_MAX,
|
||||
None,
|
||||
Some(windows_callbacks::win_event_hook),
|
||||
0,
|
||||
|
||||
@@ -300,6 +300,14 @@ IdentifyBorderOverflowApplication(identifier, id) {
|
||||
Run, komorebic.exe identify-border-overflow-application %identifier% "%id%", , Hide
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
FocusFollowsMouse(boolean_state, implementation) {
|
||||
Run, komorebic.exe focus-follows-mouse %boolean_state% --implementation %implementation%, , Hide
|
||||
}
|
||||
|
||||
@@ -393,6 +393,22 @@ struct FocusFollowsMouse {
|
||||
boolean_state: BooleanState,
|
||||
}
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
struct ActiveWindowBorder {
|
||||
#[clap(arg_enum)]
|
||||
boolean_state: BooleanState,
|
||||
}
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
struct ActiveWindowBorderColour {
|
||||
/// Red
|
||||
r: u32,
|
||||
/// Green
|
||||
g: u32,
|
||||
/// Blue
|
||||
b: u32,
|
||||
}
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
struct Start {
|
||||
/// Allow the use of komorebi's custom focus-follows-mouse implementation
|
||||
@@ -665,6 +681,12 @@ enum SubCommand {
|
||||
#[clap(arg_required_else_help = true)]
|
||||
#[clap(alias = "identify-border-overflow")]
|
||||
IdentifyBorderOverflowApplication(IdentifyBorderOverflowApplication),
|
||||
/// Enable or disable the active window border
|
||||
#[clap(arg_required_else_help = true)]
|
||||
ActiveWindowBorder(ActiveWindowBorder),
|
||||
/// Set the colour for the active window border
|
||||
#[clap(arg_required_else_help = true)]
|
||||
ActiveWindowBorderColour(ActiveWindowBorderColour),
|
||||
/// Enable or disable focus follows mouse for the operating system
|
||||
#[clap(arg_required_else_help = true)]
|
||||
FocusFollowsMouse(FocusFollowsMouse),
|
||||
@@ -1182,6 +1204,14 @@ fn main() -> Result<()> {
|
||||
SubCommand::MouseFollowsFocus(arg) => {
|
||||
send_message(&SocketMessage::MouseFollowsFocus(arg.boolean_state.into()).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::ActiveWindowBorder(arg) => {
|
||||
send_message(&SocketMessage::ActiveWindowBorder(arg.boolean_state.into()).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::ActiveWindowBorderColour(arg) => {
|
||||
send_message(
|
||||
&SocketMessage::ActiveWindowBorderColour(arg.r, arg.g, arg.b).as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::ResizeDelta(arg) => {
|
||||
send_message(&SocketMessage::ResizeDelta(arg.pixels).as_bytes()?)?;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user