mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-04-25 01:58:51 +02: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",
|
"lazy_static",
|
||||||
"miow 0.4.0",
|
"miow 0.4.0",
|
||||||
"nanoid",
|
"nanoid",
|
||||||
|
"os_info",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"paste",
|
"paste",
|
||||||
"schemars",
|
"schemars",
|
||||||
@@ -760,6 +761,17 @@ version = "1.13.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
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]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
version = "6.2.0"
|
version = "6.2.0"
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ members = [
|
|||||||
"derive-ahk",
|
"derive-ahk",
|
||||||
"komorebi",
|
"komorebi",
|
||||||
"komorebi-core",
|
"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
|
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.
|
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
|
#### 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:
|
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
|
unmanage Unmanage a window that was forcibly managed
|
||||||
reload-configuration Reload ~/komorebi.ahk (if it exists)
|
reload-configuration Reload ~/komorebi.ahk (if it exists)
|
||||||
watch-configuration Enable or disable watching of ~/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
|
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
|
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
|
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-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-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
|
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
|
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
|
toggle-focus-follows-mouse Toggle focus follows mouse for the operating system
|
||||||
mouse-follows-focus Enable or disable mouse follows focus on all workspaces
|
mouse-follows-focus Enable or disable mouse follows focus on all workspaces
|
||||||
|
|||||||
@@ -97,6 +97,8 @@ pub enum SocketMessage {
|
|||||||
ReloadConfiguration,
|
ReloadConfiguration,
|
||||||
WatchConfiguration(bool),
|
WatchConfiguration(bool),
|
||||||
CompleteConfiguration,
|
CompleteConfiguration,
|
||||||
|
ActiveWindowBorder(bool),
|
||||||
|
ActiveWindowBorderColour(u32, u32, u32),
|
||||||
InvisibleBorders(Rect),
|
InvisibleBorders(Rect),
|
||||||
WorkAreaOffset(Rect),
|
WorkAreaOffset(Rect),
|
||||||
ResizeDelta(i32),
|
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")]
|
#[strum(serialize_all = "snake_case")]
|
||||||
pub enum StateQuery {
|
pub enum StateQuery {
|
||||||
FocusedMonitorIndex,
|
FocusedMonitorIndex,
|
||||||
@@ -141,7 +143,7 @@ pub enum StateQuery {
|
|||||||
FocusedWindowIndex,
|
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")]
|
#[strum(serialize_all = "snake_case")]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum ApplicationIdentifier {
|
pub enum ApplicationIdentifier {
|
||||||
@@ -150,7 +152,7 @@ pub enum ApplicationIdentifier {
|
|||||||
Title,
|
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")]
|
#[strum(serialize_all = "snake_case")]
|
||||||
pub enum FocusFollowsMouseImplementation {
|
pub enum FocusFollowsMouseImplementation {
|
||||||
Komorebi,
|
Komorebi,
|
||||||
@@ -171,7 +173,7 @@ pub enum MoveBehaviour {
|
|||||||
Insert,
|
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")]
|
#[strum(serialize_all = "snake_case")]
|
||||||
pub enum HidingBehaviour {
|
pub enum HidingBehaviour {
|
||||||
Hide,
|
Hide,
|
||||||
|
|||||||
@@ -23,9 +23,12 @@ dirs = "4"
|
|||||||
getset = "0.1"
|
getset = "0.1"
|
||||||
hotwatch = "0.4"
|
hotwatch = "0.4"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
|
miow = "0.4"
|
||||||
nanoid = "0.4"
|
nanoid = "0.4"
|
||||||
|
os_info = "3.4"
|
||||||
parking_lot = { version = "0.12", features = ["deadlock_detection"] }
|
parking_lot = { version = "0.12", features = ["deadlock_detection"] }
|
||||||
paste = "1"
|
paste = "1"
|
||||||
|
schemars = "0.8"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
strum = { version = "0.24", features = ["derive"] }
|
strum = { version = "0.24", features = ["derive"] }
|
||||||
@@ -36,9 +39,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|||||||
uds_windows = "1"
|
uds_windows = "1"
|
||||||
which = "4"
|
which = "4"
|
||||||
winput = "0.2"
|
winput = "0.2"
|
||||||
miow = "0.4"
|
|
||||||
winreg = "0.10"
|
winreg = "0.10"
|
||||||
schemars = "0.8"
|
|
||||||
|
|
||||||
[dependencies.windows]
|
[dependencies.windows]
|
||||||
version = "0.39"
|
version = "0.39"
|
||||||
@@ -46,12 +47,15 @@ features = [
|
|||||||
"Win32_Foundation",
|
"Win32_Foundation",
|
||||||
"Win32_Graphics_Dwm",
|
"Win32_Graphics_Dwm",
|
||||||
"Win32_Graphics_Gdi",
|
"Win32_Graphics_Gdi",
|
||||||
"Win32_System_Threading",
|
"Win32_System_LibraryLoader",
|
||||||
"Win32_System_RemoteDesktop",
|
"Win32_System_RemoteDesktop",
|
||||||
|
"Win32_System_Threading",
|
||||||
"Win32_UI_Accessibility",
|
"Win32_UI_Accessibility",
|
||||||
"Win32_UI_HiDpi",
|
"Win32_UI_HiDpi",
|
||||||
"Win32_UI_Input_KeyboardAndMouse",
|
"Win32_UI_Input_KeyboardAndMouse",
|
||||||
"Win32_UI_WindowsAndMessaging",
|
"Win32_UI_Shell",
|
||||||
|
"Win32_UI_Shell_Common",
|
||||||
|
"Win32_UI_WindowsAndMessaging"
|
||||||
]
|
]
|
||||||
|
|
||||||
[features]
|
[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::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::atomic::AtomicIsize;
|
||||||
use std::sync::atomic::AtomicU32;
|
use std::sync::atomic::AtomicU32;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -20,6 +21,7 @@ use crossbeam_channel::Receiver;
|
|||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
use crossbeam_utils::Backoff;
|
use crossbeam_utils::Backoff;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use os_info::Version;
|
||||||
#[cfg(feature = "deadlock_detection")]
|
#[cfg(feature = "deadlock_detection")]
|
||||||
use parking_lot::deadlock;
|
use parking_lot::deadlock;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
@@ -35,6 +37,7 @@ use which::which;
|
|||||||
use winreg::enums::HKEY_CURRENT_USER;
|
use winreg::enums::HKEY_CURRENT_USER;
|
||||||
use winreg::RegKey;
|
use winreg::RegKey;
|
||||||
|
|
||||||
|
use crate::border::Border;
|
||||||
use komorebi_core::HidingBehaviour;
|
use komorebi_core::HidingBehaviour;
|
||||||
use komorebi_core::SocketMessage;
|
use komorebi_core::SocketMessage;
|
||||||
|
|
||||||
@@ -49,6 +52,7 @@ use crate::windows_api::WindowsApi;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod ring;
|
mod ring;
|
||||||
|
|
||||||
|
mod border;
|
||||||
mod container;
|
mod container;
|
||||||
mod monitor;
|
mod monitor;
|
||||||
mod process_command;
|
mod process_command;
|
||||||
@@ -140,11 +144,20 @@ lazy_static! {
|
|||||||
|
|
||||||
ahk_v2
|
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 INITIAL_CONFIGURATION_LOADED: AtomicBool = AtomicBool::new(false);
|
||||||
pub static CUSTOM_FFM: AtomicBool = AtomicBool::new(false);
|
pub static CUSTOM_FFM: AtomicBool = AtomicBool::new(false);
|
||||||
pub static SESSION_ID: AtomicU32 = AtomicU32::new(0);
|
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)> {
|
fn setup() -> Result<(WorkerGuard, WorkerGuard)> {
|
||||||
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
|
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
|
||||||
@@ -415,6 +428,7 @@ fn main() -> Result<()> {
|
|||||||
let process_id = WindowsApi::current_process_id();
|
let process_id = WindowsApi::current_process_id();
|
||||||
WindowsApi::allow_set_foreground_window(process_id)?;
|
WindowsApi::allow_set_foreground_window(process_id)?;
|
||||||
WindowsApi::set_process_dpi_awareness_context()?;
|
WindowsApi::set_process_dpi_awareness_context()?;
|
||||||
|
Border::create("komorebi-border-window")?;
|
||||||
|
|
||||||
let (outgoing, incoming): (Sender<WindowManagerEvent>, Receiver<WindowManagerEvent>) =
|
let (outgoing, incoming): (Sender<WindowManagerEvent>, Receiver<WindowManagerEvent>) =
|
||||||
crossbeam_channel::unbounded();
|
crossbeam_channel::unbounded();
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use parking_lot::Mutex;
|
|||||||
use schemars::schema_for;
|
use schemars::schema_for;
|
||||||
use uds_windows::UnixStream;
|
use uds_windows::UnixStream;
|
||||||
|
|
||||||
|
use crate::border::Border;
|
||||||
use komorebi_core::ApplicationIdentifier;
|
use komorebi_core::ApplicationIdentifier;
|
||||||
use komorebi_core::Axis;
|
use komorebi_core::Axis;
|
||||||
use komorebi_core::FocusFollowsMouseImplementation;
|
use komorebi_core::FocusFollowsMouseImplementation;
|
||||||
@@ -29,11 +30,13 @@ use komorebi_core::WindowContainerBehaviour;
|
|||||||
|
|
||||||
use crate::current_virtual_desktop;
|
use crate::current_virtual_desktop;
|
||||||
use crate::notify_subscribers;
|
use crate::notify_subscribers;
|
||||||
|
use crate::window::Window;
|
||||||
use crate::window_manager;
|
use crate::window_manager;
|
||||||
use crate::window_manager::WindowManager;
|
use crate::window_manager::WindowManager;
|
||||||
use crate::windows_api::WindowsApi;
|
use crate::windows_api::WindowsApi;
|
||||||
use crate::Notification;
|
use crate::Notification;
|
||||||
use crate::NotificationEvent;
|
use crate::NotificationEvent;
|
||||||
|
use crate::BORDER_HWND;
|
||||||
use crate::BORDER_OVERFLOW_IDENTIFIERS;
|
use crate::BORDER_OVERFLOW_IDENTIFIERS;
|
||||||
use crate::CUSTOM_FFM;
|
use crate::CUSTOM_FFM;
|
||||||
use crate::FLOAT_IDENTIFIERS;
|
use crate::FLOAT_IDENTIFIERS;
|
||||||
@@ -115,24 +118,24 @@ impl WindowManager {
|
|||||||
SocketMessage::WorkspacePadding(monitor_idx, workspace_idx, size) => {
|
SocketMessage::WorkspacePadding(monitor_idx, workspace_idx, size) => {
|
||||||
self.set_workspace_padding(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();
|
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()?;
|
self.enforce_workspace_rules()?;
|
||||||
}
|
}
|
||||||
SocketMessage::ManageRule(_, id) => {
|
SocketMessage::ManageRule(_, ref id) => {
|
||||||
let mut manage_identifiers = MANAGE_IDENTIFIERS.lock();
|
let mut manage_identifiers = MANAGE_IDENTIFIERS.lock();
|
||||||
if !manage_identifiers.contains(&id) {
|
if !manage_identifiers.contains(id) {
|
||||||
manage_identifiers.push(id);
|
manage_identifiers.push(id.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SocketMessage::FloatRule(identifier, id) => {
|
SocketMessage::FloatRule(identifier, ref id) => {
|
||||||
let mut float_identifiers = FLOAT_IDENTIFIERS.lock();
|
let mut float_identifiers = FLOAT_IDENTIFIERS.lock();
|
||||||
if !float_identifiers.contains(&id) {
|
if !float_identifiers.contains(id) {
|
||||||
float_identifiers.push(id.clone());
|
float_identifiers.push(id.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
let invisible_borders = self.invisible_borders;
|
let invisible_borders = self.invisible_borders;
|
||||||
@@ -149,17 +152,17 @@ impl WindowManager {
|
|||||||
for window in container.windows().iter() {
|
for window in container.windows().iter() {
|
||||||
match identifier {
|
match identifier {
|
||||||
ApplicationIdentifier::Exe => {
|
ApplicationIdentifier::Exe => {
|
||||||
if window.exe()? == id {
|
if window.exe()? == *id {
|
||||||
hwnds_to_purge.push((i, window.hwnd));
|
hwnds_to_purge.push((i, window.hwnd));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ApplicationIdentifier::Class => {
|
ApplicationIdentifier::Class => {
|
||||||
if window.class()? == id {
|
if window.class()? == *id {
|
||||||
hwnds_to_purge.push((i, window.hwnd));
|
hwnds_to_purge.push((i, window.hwnd));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ApplicationIdentifier::Title => {
|
ApplicationIdentifier::Title => {
|
||||||
if window.title()? == id {
|
if window.title()? == *id {
|
||||||
hwnds_to_purge.push((i, window.hwnd));
|
hwnds_to_purge.push((i, window.hwnd));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,9 +239,11 @@ impl WindowManager {
|
|||||||
SocketMessage::Retile => self.retile_all(false)?,
|
SocketMessage::Retile => self.retile_all(false)?,
|
||||||
SocketMessage::FlipLayout(layout_flip) => self.flip_layout(layout_flip)?,
|
SocketMessage::FlipLayout(layout_flip) => self.flip_layout(layout_flip)?,
|
||||||
SocketMessage::ChangeLayout(layout) => self.change_workspace_layout_default(layout)?,
|
SocketMessage::ChangeLayout(layout) => self.change_workspace_layout_default(layout)?,
|
||||||
SocketMessage::ChangeLayoutCustom(path) => self.change_workspace_custom_layout(path)?,
|
SocketMessage::ChangeLayoutCustom(ref path) => {
|
||||||
SocketMessage::WorkspaceLayoutCustom(monitor_idx, workspace_idx, path) => {
|
self.change_workspace_custom_layout(path.clone())?;
|
||||||
self.set_workspace_layout_custom(monitor_idx, workspace_idx, path)?;
|
}
|
||||||
|
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) => {
|
SocketMessage::WorkspaceTiling(monitor_idx, workspace_idx, tile) => {
|
||||||
self.set_workspace_tiling(monitor_idx, workspace_idx, tile)?;
|
self.set_workspace_tiling(monitor_idx, workspace_idx, tile)?;
|
||||||
@@ -263,13 +268,13 @@ impl WindowManager {
|
|||||||
monitor_idx,
|
monitor_idx,
|
||||||
workspace_idx,
|
workspace_idx,
|
||||||
at_container_count,
|
at_container_count,
|
||||||
path,
|
ref path,
|
||||||
) => {
|
) => {
|
||||||
self.add_workspace_layout_custom_rule(
|
self.add_workspace_layout_custom_rule(
|
||||||
monitor_idx,
|
monitor_idx,
|
||||||
workspace_idx,
|
workspace_idx,
|
||||||
at_container_count,
|
at_container_count,
|
||||||
path,
|
path.clone(),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
SocketMessage::ClearWorkspaceLayoutRules(monitor_idx, workspace_idx) => {
|
SocketMessage::ClearWorkspaceLayoutRules(monitor_idx, workspace_idx) => {
|
||||||
@@ -333,8 +338,8 @@ impl WindowManager {
|
|||||||
SocketMessage::NewWorkspace => {
|
SocketMessage::NewWorkspace => {
|
||||||
self.new_workspace()?;
|
self.new_workspace()?;
|
||||||
}
|
}
|
||||||
SocketMessage::WorkspaceName(monitor_idx, workspace_idx, name) => {
|
SocketMessage::WorkspaceName(monitor_idx, workspace_idx, ref name) => {
|
||||||
self.set_workspace_name(monitor_idx, workspace_idx, name)?;
|
self.set_workspace_name(monitor_idx, workspace_idx, name.to_string())?;
|
||||||
}
|
}
|
||||||
SocketMessage::State => {
|
SocketMessage::State => {
|
||||||
let state = match serde_json::to_string_pretty(&window_manager::State::from(&*self))
|
let state = match serde_json::to_string_pretty(&window_manager::State::from(&*self))
|
||||||
@@ -583,28 +588,28 @@ impl WindowManager {
|
|||||||
SocketMessage::WatchConfiguration(enable) => {
|
SocketMessage::WatchConfiguration(enable) => {
|
||||||
self.watch_configuration(enable)?;
|
self.watch_configuration(enable)?;
|
||||||
}
|
}
|
||||||
SocketMessage::IdentifyBorderOverflowApplication(_, id) => {
|
SocketMessage::IdentifyBorderOverflowApplication(_, ref id) => {
|
||||||
let mut identifiers = BORDER_OVERFLOW_IDENTIFIERS.lock();
|
let mut identifiers = BORDER_OVERFLOW_IDENTIFIERS.lock();
|
||||||
if !identifiers.contains(&id) {
|
if !identifiers.contains(id) {
|
||||||
identifiers.push(id);
|
identifiers.push(id.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SocketMessage::IdentifyObjectNameChangeApplication(_, id) => {
|
SocketMessage::IdentifyObjectNameChangeApplication(_, ref id) => {
|
||||||
let mut identifiers = OBJECT_NAME_CHANGE_ON_LAUNCH.lock();
|
let mut identifiers = OBJECT_NAME_CHANGE_ON_LAUNCH.lock();
|
||||||
if !identifiers.contains(&id) {
|
if !identifiers.contains(id) {
|
||||||
identifiers.push(id);
|
identifiers.push(id.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SocketMessage::IdentifyTrayApplication(_, id) => {
|
SocketMessage::IdentifyTrayApplication(_, ref id) => {
|
||||||
let mut identifiers = TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock();
|
let mut identifiers = TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock();
|
||||||
if !identifiers.contains(&id) {
|
if !identifiers.contains(id) {
|
||||||
identifiers.push(id);
|
identifiers.push(id.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SocketMessage::IdentifyLayeredApplication(_, id) => {
|
SocketMessage::IdentifyLayeredApplication(_, ref id) => {
|
||||||
let mut identifiers = LAYERED_WHITELIST.lock();
|
let mut identifiers = LAYERED_WHITELIST.lock();
|
||||||
if !identifiers.contains(&id) {
|
if !identifiers.contains(id) {
|
||||||
identifiers.push(id);
|
identifiers.push(id.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SocketMessage::ManageFocusedWindow => {
|
SocketMessage::ManageFocusedWindow => {
|
||||||
@@ -654,7 +659,7 @@ impl WindowManager {
|
|||||||
workspace.set_resize_dimensions(resize);
|
workspace.set_resize_dimensions(resize);
|
||||||
self.update_focused_workspace(false)?;
|
self.update_focused_workspace(false)?;
|
||||||
}
|
}
|
||||||
SocketMessage::Save(path) => {
|
SocketMessage::Save(ref path) => {
|
||||||
let workspace = self.focused_workspace_mut()?;
|
let workspace = self.focused_workspace_mut()?;
|
||||||
let resize = workspace.resize_dimensions();
|
let resize = workspace.resize_dimensions();
|
||||||
|
|
||||||
@@ -662,14 +667,14 @@ impl WindowManager {
|
|||||||
.write(true)
|
.write(true)
|
||||||
.truncate(true)
|
.truncate(true)
|
||||||
.create(true)
|
.create(true)
|
||||||
.open(path)?;
|
.open(path.clone())?;
|
||||||
|
|
||||||
serde_json::to_writer_pretty(&file, &resize)?;
|
serde_json::to_writer_pretty(&file, &resize)?;
|
||||||
}
|
}
|
||||||
SocketMessage::Load(path) => {
|
SocketMessage::Load(ref path) => {
|
||||||
let workspace = self.focused_workspace_mut()?;
|
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()))?;
|
.map_err(|_| anyhow!("no file found at {}", path.display().to_string()))?;
|
||||||
|
|
||||||
let resize: Vec<Option<Rect>> = serde_json::from_reader(file)?;
|
let resize: Vec<Option<Rect>> = serde_json::from_reader(file)?;
|
||||||
@@ -677,18 +682,18 @@ impl WindowManager {
|
|||||||
workspace.set_resize_dimensions(resize);
|
workspace.set_resize_dimensions(resize);
|
||||||
self.update_focused_workspace(false)?;
|
self.update_focused_workspace(false)?;
|
||||||
}
|
}
|
||||||
SocketMessage::AddSubscriber(subscriber) => {
|
SocketMessage::AddSubscriber(ref subscriber) => {
|
||||||
let mut pipes = SUBSCRIPTION_PIPES.lock();
|
let mut pipes = SUBSCRIPTION_PIPES.lock();
|
||||||
let pipe_path = format!(r"\\.\pipe\{}", subscriber);
|
let pipe_path = format!(r"\\.\pipe\{}", subscriber);
|
||||||
let pipe = connect(&pipe_path).map_err(|_| {
|
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)
|
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();
|
let mut pipes = SUBSCRIPTION_PIPES.lock();
|
||||||
pipes.remove(&subscriber);
|
pipes.remove(subscriber);
|
||||||
}
|
}
|
||||||
SocketMessage::MouseFollowsFocus(enable) => {
|
SocketMessage::MouseFollowsFocus(enable) => {
|
||||||
self.mouse_follows_focus = enable;
|
self.mouse_follows_focus = enable;
|
||||||
@@ -729,6 +734,17 @@ impl WindowManager {
|
|||||||
SocketMessage::UnmanagedWindowOperationBehaviour(behaviour) => {
|
SocketMessage::UnmanagedWindowOperationBehaviour(behaviour) => {
|
||||||
self.unmanaged_window_operation_behaviour = 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 => {
|
SocketMessage::NotificationSchema => {
|
||||||
let notification = schema_for!(Notification);
|
let notification = schema_for!(Notification);
|
||||||
let schema = serde_json::to_string_pretty(¬ification)?;
|
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");
|
tracing::info!("processed");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use color_eyre::eyre::anyhow;
|
use color_eyre::eyre::anyhow;
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use crossbeam_channel::select;
|
use crossbeam_channel::select;
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
use crate::border::Border;
|
||||||
use komorebi_core::OperationDirection;
|
use komorebi_core::OperationDirection;
|
||||||
use komorebi_core::Rect;
|
use komorebi_core::Rect;
|
||||||
use komorebi_core::Sizing;
|
use komorebi_core::Sizing;
|
||||||
@@ -18,6 +21,9 @@ use crate::window_manager_event::WindowManagerEvent;
|
|||||||
use crate::windows_api::WindowsApi;
|
use crate::windows_api::WindowsApi;
|
||||||
use crate::Notification;
|
use crate::Notification;
|
||||||
use crate::NotificationEvent;
|
use crate::NotificationEvent;
|
||||||
|
use crate::BORDER_HIDDEN;
|
||||||
|
use crate::BORDER_HWND;
|
||||||
|
|
||||||
use crate::DATA_DIR;
|
use crate::DATA_DIR;
|
||||||
use crate::HIDDEN_HWNDS;
|
use crate::HIDDEN_HWNDS;
|
||||||
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
||||||
@@ -469,11 +475,42 @@ impl WindowManager {
|
|||||||
WindowManagerEvent::MonitorPoll(..) | WindowManagerEvent::MouseCapture(..) => {}
|
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 we unmanaged a window, it shouldn't be immediately hidden behind managed windows
|
||||||
if let WindowManagerEvent::Unmanage(window) = event {
|
if let WindowManagerEvent::Unmanage(window) = event {
|
||||||
window.center(&self.focused_monitor_work_area()?, &invisible_borders)?;
|
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");
|
tracing::trace!("updating list of known hwnds");
|
||||||
let mut known_hwnds = vec![];
|
let mut known_hwnds = vec![];
|
||||||
for monitor in self.monitors() {
|
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");
|
let receiver = message_loop::start().expect("could not start winput message loop");
|
||||||
|
|
||||||
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 {
|
if let Some(FocusFollowsMouseImplementation::Komorebi) = focus_follows_mouse {
|
||||||
match receiver.next_event() {
|
match receiver.next_event() {
|
||||||
// Don't want to send any raise events while we are dragging or resizing
|
// 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::io::ErrorKind;
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use color_eyre::eyre::anyhow;
|
use color_eyre::eyre::anyhow;
|
||||||
@@ -14,6 +15,7 @@ use schemars::JsonSchema;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use uds_windows::UnixListener;
|
use uds_windows::UnixListener;
|
||||||
|
|
||||||
|
use crate::border::Border;
|
||||||
use komorebi_core::custom_layout::CustomLayout;
|
use komorebi_core::custom_layout::CustomLayout;
|
||||||
use komorebi_core::Arrangement;
|
use komorebi_core::Arrangement;
|
||||||
use komorebi_core::Axis;
|
use komorebi_core::Axis;
|
||||||
@@ -38,6 +40,7 @@ use crate::window_manager_event::WindowManagerEvent;
|
|||||||
use crate::windows_api::WindowsApi;
|
use crate::windows_api::WindowsApi;
|
||||||
use crate::winevent_listener::WINEVENT_CALLBACK_CHANNEL;
|
use crate::winevent_listener::WINEVENT_CALLBACK_CHANNEL;
|
||||||
use crate::workspace::Workspace;
|
use crate::workspace::Workspace;
|
||||||
|
use crate::BORDER_HWND;
|
||||||
use crate::BORDER_OVERFLOW_IDENTIFIERS;
|
use crate::BORDER_OVERFLOW_IDENTIFIERS;
|
||||||
use crate::DATA_DIR;
|
use crate::DATA_DIR;
|
||||||
use crate::FLOAT_IDENTIFIERS;
|
use crate::FLOAT_IDENTIFIERS;
|
||||||
@@ -104,7 +107,7 @@ impl From<&WindowManager> for State {
|
|||||||
resize_delta: wm.resize_delta,
|
resize_delta: wm.resize_delta,
|
||||||
new_window_behaviour: wm.window_container_behaviour,
|
new_window_behaviour: wm.window_container_behaviour,
|
||||||
cross_monitor_move_behaviour: wm.cross_monitor_move_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,
|
mouse_follows_focus: wm.mouse_follows_focus,
|
||||||
has_pending_raise_op: wm.has_pending_raise_op,
|
has_pending_raise_op: wm.has_pending_raise_op,
|
||||||
float_identifiers: FLOAT_IDENTIFIERS.lock().clone(),
|
float_identifiers: FLOAT_IDENTIFIERS.lock().clone(),
|
||||||
@@ -193,6 +196,26 @@ impl WindowManager {
|
|||||||
WindowsApi::load_workspace_information(&mut self.monitors)
|
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]
|
#[tracing::instrument]
|
||||||
pub fn reload_configuration() {
|
pub fn reload_configuration() {
|
||||||
tracing::info!("reloading configuration");
|
tracing::info!("reloading configuration");
|
||||||
@@ -267,7 +290,7 @@ impl WindowManager {
|
|||||||
Ok(())
|
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()?;
|
let current_monitor_size = self.focused_monitor_size().ok()?;
|
||||||
|
|
||||||
for (idx, monitor) in self.monitors.elements().iter().enumerate() {
|
for (idx, monitor) in self.monitors.elements().iter().enumerate() {
|
||||||
@@ -953,7 +976,7 @@ impl WindowManager {
|
|||||||
match new_idx {
|
match new_idx {
|
||||||
None => {
|
None => {
|
||||||
let monitor_idx = self
|
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"))?;
|
.ok_or_else(|| anyhow!("there is no container or monitor in this direction"))?;
|
||||||
|
|
||||||
self.focus_monitor(monitor_idx)?;
|
self.focus_monitor(monitor_idx)?;
|
||||||
@@ -991,7 +1014,7 @@ impl WindowManager {
|
|||||||
// in that direction if there is one
|
// in that direction if there is one
|
||||||
None => {
|
None => {
|
||||||
let target_monitor_idx = self
|
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"))?;
|
.ok_or_else(|| anyhow!("there is no container or monitor in this direction"))?;
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -1069,6 +1092,20 @@ impl WindowManager {
|
|||||||
.get_mut(origin_monitor_idx)
|
.get_mut(origin_monitor_idx)
|
||||||
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
|
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
|
||||||
.update_focused_workspace(offset, &invisible_borders)?;
|
.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) => {
|
Some(new_idx) => {
|
||||||
let workspace = self.focused_workspace_mut()?;
|
let workspace = self.focused_workspace_mut()?;
|
||||||
|
|||||||
@@ -6,29 +6,37 @@ use color_eyre::eyre::anyhow;
|
|||||||
use color_eyre::eyre::Error;
|
use color_eyre::eyre::Error;
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use windows::core::Result as WindowsCrateResult;
|
use windows::core::Result as WindowsCrateResult;
|
||||||
|
use windows::core::PCSTR;
|
||||||
use windows::core::PWSTR;
|
use windows::core::PWSTR;
|
||||||
use windows::Win32::Foundation::BOOL;
|
use windows::Win32::Foundation::BOOL;
|
||||||
use windows::Win32::Foundation::HANDLE;
|
use windows::Win32::Foundation::HANDLE;
|
||||||
|
use windows::Win32::Foundation::HINSTANCE;
|
||||||
use windows::Win32::Foundation::HWND;
|
use windows::Win32::Foundation::HWND;
|
||||||
use windows::Win32::Foundation::LPARAM;
|
use windows::Win32::Foundation::LPARAM;
|
||||||
use windows::Win32::Foundation::POINT;
|
use windows::Win32::Foundation::POINT;
|
||||||
use windows::Win32::Foundation::RECT;
|
use windows::Win32::Foundation::RECT;
|
||||||
use windows::Win32::Graphics::Dwm::DwmGetWindowAttribute;
|
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_CLOAKED;
|
||||||
use windows::Win32::Graphics::Dwm::DWMWA_EXTENDED_FRAME_BOUNDS;
|
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::DWMWINDOWATTRIBUTE;
|
||||||
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_APP;
|
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_APP;
|
||||||
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_INHERITED;
|
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_INHERITED;
|
||||||
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_SHELL;
|
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::EnumDisplayMonitors;
|
||||||
use windows::Win32::Graphics::Gdi::GetMonitorInfoW;
|
use windows::Win32::Graphics::Gdi::GetMonitorInfoW;
|
||||||
use windows::Win32::Graphics::Gdi::MonitorFromPoint;
|
use windows::Win32::Graphics::Gdi::MonitorFromPoint;
|
||||||
use windows::Win32::Graphics::Gdi::MonitorFromWindow;
|
use windows::Win32::Graphics::Gdi::MonitorFromWindow;
|
||||||
|
use windows::Win32::Graphics::Gdi::HBRUSH;
|
||||||
use windows::Win32::Graphics::Gdi::HDC;
|
use windows::Win32::Graphics::Gdi::HDC;
|
||||||
use windows::Win32::Graphics::Gdi::HMONITOR;
|
use windows::Win32::Graphics::Gdi::HMONITOR;
|
||||||
use windows::Win32::Graphics::Gdi::MONITORENUMPROC;
|
use windows::Win32::Graphics::Gdi::MONITORENUMPROC;
|
||||||
use windows::Win32::Graphics::Gdi::MONITORINFO;
|
use windows::Win32::Graphics::Gdi::MONITORINFO;
|
||||||
use windows::Win32::Graphics::Gdi::MONITOR_DEFAULTTONEAREST;
|
use windows::Win32::Graphics::Gdi::MONITOR_DEFAULTTONEAREST;
|
||||||
|
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
|
||||||
use windows::Win32::System::RemoteDesktop::ProcessIdToSessionId;
|
use windows::Win32::System::RemoteDesktop::ProcessIdToSessionId;
|
||||||
use windows::Win32::System::Threading::AttachThreadInput;
|
use windows::Win32::System::Threading::AttachThreadInput;
|
||||||
use windows::Win32::System::Threading::GetCurrentProcessId;
|
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::SetProcessDpiAwarenessContext;
|
||||||
use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
|
use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::SetFocus;
|
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::AllowSetForegroundWindow;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::CreateWindowExA;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::EnumWindows;
|
use windows::Win32::UI::WindowsAndMessaging::EnumWindows;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::GetCursorPos;
|
use windows::Win32::UI::WindowsAndMessaging::GetCursorPos;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::GetDesktopWindow;
|
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::IsWindow;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::IsWindowVisible;
|
use windows::Win32::UI::WindowsAndMessaging::IsWindowVisible;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::RealGetWindowClassW;
|
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::SetCursorPos;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::SetForegroundWindow;
|
use windows::Win32::UI::WindowsAndMessaging::SetForegroundWindow;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrW;
|
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::ShowWindow;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::SystemParametersInfoW;
|
use windows::Win32::UI::WindowsAndMessaging::SystemParametersInfoW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WindowFromPoint;
|
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_EXSTYLE;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::GWL_STYLE;
|
use windows::Win32::UI::WindowsAndMessaging::GWL_STYLE;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::GW_HWNDNEXT;
|
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_NOTOPMOST;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::HWND_TOPMOST;
|
use windows::Win32::UI::WindowsAndMessaging::HWND_TOPMOST;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::SET_WINDOW_POS_FLAGS;
|
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_ACTION;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS;
|
use windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WINDOW_LONG_PTR_INDEX;
|
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::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;
|
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> {
|
impl<T, E> From<WindowsResult<T, E>> for Result<T, E> {
|
||||||
fn from(result: WindowsResult<T, E>) -> Self {
|
fn from(result: WindowsResult<T, E>) -> Self {
|
||||||
@@ -261,6 +283,24 @@ impl WindowsApi {
|
|||||||
Self::set_window_pos(hwnd, layout, position, flags.bits())
|
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<()> {
|
pub fn set_window_pos(hwnd: HWND, layout: &Rect, position: HWND, flags: u32) -> Result<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
SetWindowPos(
|
SetWindowPos(
|
||||||
@@ -443,6 +483,11 @@ impl WindowsApi {
|
|||||||
Self::set_window_long_ptr_w(hwnd, GWL_STYLE, new_value)
|
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> {
|
pub fn window_text_w(hwnd: HWND) -> Result<String> {
|
||||||
let mut text: [u16; 512] = [0; 512];
|
let mut text: [u16; 512] = [0; 512];
|
||||||
match WindowsResult::from(unsafe { GetWindowTextW(hwnd, &mut text) }) {
|
match WindowsResult::from(unsafe { GetWindowTextW(hwnd, &mut text) }) {
|
||||||
@@ -623,4 +668,71 @@ impl WindowsApi {
|
|||||||
SPIF_SENDCHANGE,
|
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::BOOL;
|
||||||
use windows::Win32::Foundation::HWND;
|
use windows::Win32::Foundation::HWND;
|
||||||
use windows::Win32::Foundation::LPARAM;
|
use windows::Win32::Foundation::LPARAM;
|
||||||
|
use windows::Win32::Foundation::LRESULT;
|
||||||
use windows::Win32::Foundation::RECT;
|
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::HDC;
|
||||||
use windows::Win32::Graphics::Gdi::HMONITOR;
|
use windows::Win32::Graphics::Gdi::HMONITOR;
|
||||||
use windows::Win32::UI::Accessibility::HWINEVENTHOOK;
|
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::container::Container;
|
||||||
use crate::monitor::Monitor;
|
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 {
|
std::thread::spawn(move || unsafe {
|
||||||
let hook_ref = SetWinEventHook(
|
let hook_ref = SetWinEventHook(
|
||||||
EVENT_MIN as u32,
|
EVENT_MIN,
|
||||||
EVENT_MAX as u32,
|
EVENT_MAX,
|
||||||
None,
|
None,
|
||||||
Some(windows_callbacks::win_event_hook),
|
Some(windows_callbacks::win_event_hook),
|
||||||
0,
|
0,
|
||||||
|
|||||||
@@ -300,6 +300,14 @@ IdentifyBorderOverflowApplication(identifier, id) {
|
|||||||
Run, komorebic.exe identify-border-overflow-application %identifier% "%id%", , Hide
|
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) {
|
FocusFollowsMouse(boolean_state, implementation) {
|
||||||
Run, komorebic.exe focus-follows-mouse %boolean_state% --implementation %implementation%, , Hide
|
Run, komorebic.exe focus-follows-mouse %boolean_state% --implementation %implementation%, , Hide
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -393,6 +393,22 @@ struct FocusFollowsMouse {
|
|||||||
boolean_state: BooleanState,
|
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)]
|
#[derive(Parser, AhkFunction)]
|
||||||
struct Start {
|
struct Start {
|
||||||
/// Allow the use of komorebi's custom focus-follows-mouse implementation
|
/// Allow the use of komorebi's custom focus-follows-mouse implementation
|
||||||
@@ -665,6 +681,12 @@ enum SubCommand {
|
|||||||
#[clap(arg_required_else_help = true)]
|
#[clap(arg_required_else_help = true)]
|
||||||
#[clap(alias = "identify-border-overflow")]
|
#[clap(alias = "identify-border-overflow")]
|
||||||
IdentifyBorderOverflowApplication(IdentifyBorderOverflowApplication),
|
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
|
/// Enable or disable focus follows mouse for the operating system
|
||||||
#[clap(arg_required_else_help = true)]
|
#[clap(arg_required_else_help = true)]
|
||||||
FocusFollowsMouse(FocusFollowsMouse),
|
FocusFollowsMouse(FocusFollowsMouse),
|
||||||
@@ -1182,6 +1204,14 @@ fn main() -> Result<()> {
|
|||||||
SubCommand::MouseFollowsFocus(arg) => {
|
SubCommand::MouseFollowsFocus(arg) => {
|
||||||
send_message(&SocketMessage::MouseFollowsFocus(arg.boolean_state.into()).as_bytes()?)?;
|
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) => {
|
SubCommand::ResizeDelta(arg) => {
|
||||||
send_message(&SocketMessage::ResizeDelta(arg.pixels).as_bytes()?)?;
|
send_message(&SocketMessage::ResizeDelta(arg.pixels).as_bytes()?)?;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user