mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-01-19 22:13:41 +01:00
Compare commits
1 Commits
feature/bo
...
feature/ko
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f36926cdb1 |
28
Cargo.lock
generated
28
Cargo.lock
generated
@@ -796,6 +796,33 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "komoborders"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"color-eyre",
|
||||
"dirs",
|
||||
"komoborders-client",
|
||||
"komorebi",
|
||||
"komorebi-client",
|
||||
"lazy_static",
|
||||
"parking_lot",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uds_windows",
|
||||
"windows 0.54.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "komoborders-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"serde",
|
||||
"serde_json_lenient",
|
||||
"uds_windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "komorebi"
|
||||
version = "0.1.26-dev.0"
|
||||
@@ -810,6 +837,7 @@ dependencies = [
|
||||
"getset",
|
||||
"hex_color",
|
||||
"hotwatch",
|
||||
"komoborders-client",
|
||||
"komorebi-core",
|
||||
"lazy_static",
|
||||
"miow",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
resolver = "2"
|
||||
members = [
|
||||
"derive-ahk",
|
||||
"derive-ahk", "komoborders", "komoborders-client",
|
||||
"komorebi",
|
||||
"komorebi-client",
|
||||
"komorebi-core",
|
||||
|
||||
12
komoborders-client/Cargo.toml
Normal file
12
komoborders-client/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "komoborders-client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
uds_windows = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
74
komoborders-client/src/lib.rs
Normal file
74
komoborders-client/src/lib.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::io::Write;
|
||||
use std::str::FromStr;
|
||||
use uds_windows::UnixStream;
|
||||
|
||||
const KOMOBORDERS: &str = "komoborders.sock";
|
||||
|
||||
pub fn send_message(message: &SocketMessage) -> std::io::Result<()> {
|
||||
let socket = dirs::data_local_dir()
|
||||
.expect("there is no local data directory")
|
||||
.join("komorebi")
|
||||
.join(KOMOBORDERS);
|
||||
|
||||
let mut connected = false;
|
||||
while !connected {
|
||||
if let Ok(mut stream) = UnixStream::connect(&socket) {
|
||||
connected = true;
|
||||
stream.write_all(serde_json::to_string(message)?.as_bytes())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Serialize, Deserialize)]
|
||||
pub enum ZOrder {
|
||||
Top,
|
||||
NoTopMost,
|
||||
Bottom,
|
||||
TopMost,
|
||||
}
|
||||
|
||||
// impl From<isize> for ZOrder {
|
||||
// fn from(value: isize) -> Self {
|
||||
// match value {
|
||||
// -2 => Self::NoTopMost,
|
||||
// -1 => Self::TopMost,
|
||||
// 0 => Self::Top,
|
||||
// 1 => Self::Bottom,
|
||||
// _ => unimplemented!(),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Into<isize> for ZOrder {
|
||||
fn into(self) -> isize {
|
||||
match self {
|
||||
ZOrder::Top => 0,
|
||||
ZOrder::NoTopMost => -2,
|
||||
ZOrder::Bottom => 1,
|
||||
ZOrder::TopMost => -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum SocketMessage {
|
||||
FocusedColour(u32, u32, u32),
|
||||
UnfocusedColour(u32, u32, u32),
|
||||
MonocleColour(u32, u32, u32),
|
||||
StackColour(u32, u32, u32),
|
||||
Width(i32),
|
||||
Offset(i32),
|
||||
ZOrder(ZOrder),
|
||||
}
|
||||
|
||||
impl FromStr for SocketMessage {
|
||||
type Err = serde_json::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
serde_json::from_str(s)
|
||||
}
|
||||
}
|
||||
19
komoborders/Cargo.toml
Normal file
19
komoborders/Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "komoborders"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
komorebi-client = { path = "../komorebi-client" }
|
||||
komoborders-client = { path = "../komoborders-client" }
|
||||
komorebi = { path = "../komorebi" }
|
||||
serde_json = "1"
|
||||
color-eyre = "0.6"
|
||||
windows = { workspace = true }
|
||||
lazy_static = "1"
|
||||
parking_lot = "0.12"
|
||||
uds_windows = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
@@ -1,22 +1,11 @@
|
||||
use crate::border_manager::WindowKind;
|
||||
use crate::border_manager::BORDER_OFFSET;
|
||||
use crate::border_manager::BORDER_WIDTH;
|
||||
use crate::border_manager::FOCUSED;
|
||||
use crate::border_manager::FOCUS_STATE;
|
||||
use crate::border_manager::MONOCLE;
|
||||
use crate::border_manager::RECT_STATE;
|
||||
use crate::border_manager::STACK;
|
||||
use crate::border_manager::STYLE;
|
||||
use crate::border_manager::UNFOCUSED;
|
||||
use crate::border_manager::Z_ORDER;
|
||||
use crate::WindowsApi;
|
||||
use crate::WINDOWS_11;
|
||||
|
||||
use komorebi_core::ActiveWindowBorderStyle;
|
||||
use komorebi_core::Rect;
|
||||
|
||||
use komoborders_client::ZOrder;
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::atomic::AtomicI32;
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use windows::core::PCWSTR;
|
||||
use windows::Win32::Foundation::COLORREF;
|
||||
@@ -29,7 +18,6 @@ use windows::Win32::Graphics::Gdi::CreatePen;
|
||||
use windows::Win32::Graphics::Gdi::EndPaint;
|
||||
use windows::Win32::Graphics::Gdi::InvalidateRect;
|
||||
use windows::Win32::Graphics::Gdi::Rectangle;
|
||||
use windows::Win32::Graphics::Gdi::RoundRect;
|
||||
use windows::Win32::Graphics::Gdi::SelectObject;
|
||||
use windows::Win32::Graphics::Gdi::ValidateRect;
|
||||
use windows::Win32::Graphics::Gdi::PAINTSTRUCT;
|
||||
@@ -47,6 +35,33 @@ use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WM_PAINT;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
|
||||
|
||||
use crate::FocusKind;
|
||||
use crate::FOCUSED_STATE;
|
||||
use crate::RECT_STATE;
|
||||
use komorebi::Rgb;
|
||||
use komorebi::WindowsApi;
|
||||
use komorebi_client::Rect;
|
||||
|
||||
pub static TRANSPARENCY: u32 = 0;
|
||||
pub static BORDER_WIDTH: AtomicI32 = AtomicI32::new(8);
|
||||
pub static BORDER_OFFSET: AtomicI32 = AtomicI32::new(-1);
|
||||
|
||||
lazy_static! {
|
||||
pub static ref Z_ORDER: Arc<Mutex<ZOrder>> = Arc::new(Mutex::new(ZOrder::Bottom));
|
||||
pub static ref FOCUSED: AtomicU32 = AtomicU32::new(u32::from(komorebi_client::Colour::Rgb(
|
||||
Rgb::new(66, 165, 245)
|
||||
)));
|
||||
pub static ref UNFOCUSED: AtomicU32 = AtomicU32::new(u32::from(komorebi_client::Colour::Rgb(
|
||||
Rgb::new(128, 128, 128)
|
||||
)));
|
||||
pub static ref MONOCLE: AtomicU32 = AtomicU32::new(u32::from(komorebi_client::Colour::Rgb(
|
||||
Rgb::new(255, 51, 153)
|
||||
)));
|
||||
pub static ref STACK: AtomicU32 = AtomicU32::new(u32::from(komorebi_client::Colour::Rgb(
|
||||
Rgb::new(0, 165, 66)
|
||||
)));
|
||||
}
|
||||
|
||||
pub struct Border {
|
||||
pub hwnd: isize,
|
||||
}
|
||||
@@ -67,7 +82,7 @@ impl Border {
|
||||
lpszClassName: class_name,
|
||||
style: CS_HREDRAW | CS_VREDRAW,
|
||||
lpfnWndProc: Some(Self::callback),
|
||||
hbrBackground: WindowsApi::create_solid_brush(0),
|
||||
hbrBackground: WindowsApi::create_solid_brush(TRANSPARENCY),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -140,11 +155,11 @@ impl Border {
|
||||
if let Some(rect) = rects.get(&window.0).copied() {
|
||||
// Grab the focus kind for this border
|
||||
let focus_kind = {
|
||||
FOCUS_STATE
|
||||
FOCUSED_STATE
|
||||
.lock()
|
||||
.get(&window.0)
|
||||
.copied()
|
||||
.unwrap_or(WindowKind::Unfocused)
|
||||
.unwrap_or(FocusKind::Unfocused)
|
||||
};
|
||||
|
||||
// Set up the brush to draw the border
|
||||
@@ -154,39 +169,19 @@ impl Border {
|
||||
PS_SOLID | PS_INSIDEFRAME,
|
||||
BORDER_WIDTH.load(Ordering::SeqCst),
|
||||
COLORREF(match focus_kind {
|
||||
WindowKind::Unfocused => UNFOCUSED.load(Ordering::SeqCst),
|
||||
WindowKind::Single => FOCUSED.load(Ordering::SeqCst),
|
||||
WindowKind::Stack => STACK.load(Ordering::SeqCst),
|
||||
WindowKind::Monocle => MONOCLE.load(Ordering::SeqCst),
|
||||
FocusKind::Unfocused => UNFOCUSED.load(Ordering::SeqCst),
|
||||
FocusKind::Single => FOCUSED.load(Ordering::SeqCst),
|
||||
FocusKind::Stack => STACK.load(Ordering::SeqCst),
|
||||
FocusKind::Monocle => MONOCLE.load(Ordering::SeqCst),
|
||||
}),
|
||||
);
|
||||
|
||||
let hbrush = WindowsApi::create_solid_brush(0);
|
||||
let hbrush = WindowsApi::create_solid_brush(TRANSPARENCY);
|
||||
|
||||
// Draw the border
|
||||
SelectObject(hdc, hpen);
|
||||
SelectObject(hdc, hbrush);
|
||||
// TODO(raggi): this is approximately the correct curvature for
|
||||
// the top left of a Windows 11 window (DWMWCP_DEFAULT), but
|
||||
// often the bottom right has a different shape. Furthermore if
|
||||
// the window was made with DWMWCP_ROUNDSMALL then this is the
|
||||
// wrong size. In the future we should read the DWM properties
|
||||
// of windows and attempt to match appropriately.
|
||||
match *STYLE.lock() {
|
||||
ActiveWindowBorderStyle::System => {
|
||||
if *WINDOWS_11 {
|
||||
RoundRect(hdc, 0, 0, rect.right, rect.bottom, 20, 20);
|
||||
} else {
|
||||
Rectangle(hdc, 0, 0, rect.right, rect.bottom);
|
||||
}
|
||||
}
|
||||
ActiveWindowBorderStyle::Rounded => {
|
||||
RoundRect(hdc, 0, 0, rect.right, rect.bottom, 20, 20);
|
||||
}
|
||||
ActiveWindowBorderStyle::Square => {
|
||||
Rectangle(hdc, 0, 0, rect.right, rect.bottom);
|
||||
}
|
||||
}
|
||||
Rectangle(hdc, 0, 0, rect.right, rect.bottom);
|
||||
EndPaint(window, &ps);
|
||||
ValidateRect(window, None);
|
||||
}
|
||||
259
komoborders/src/main.rs
Normal file
259
komoborders/src/main.rs
Normal file
@@ -0,0 +1,259 @@
|
||||
#![warn(clippy::all, clippy::nursery, clippy::pedantic)]
|
||||
#![allow(
|
||||
clippy::missing_errors_doc,
|
||||
clippy::redundant_pub_crate,
|
||||
clippy::significant_drop_tightening,
|
||||
clippy::significant_drop_in_scrutinee
|
||||
)]
|
||||
|
||||
mod border;
|
||||
|
||||
use komorebi_client::Rect;
|
||||
use komorebi_client::UnixListener;
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::HashMap;
|
||||
use std::io::BufRead;
|
||||
use std::io::BufReader;
|
||||
use std::io::ErrorKind;
|
||||
use std::str::FromStr;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
use uds_windows::UnixStream;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
|
||||
use crate::border::Border;
|
||||
use crate::border::BORDER_WIDTH;
|
||||
use crate::border::FOCUSED;
|
||||
use crate::border::MONOCLE;
|
||||
use crate::border::STACK;
|
||||
use crate::border::UNFOCUSED;
|
||||
use crate::border::Z_ORDER;
|
||||
use komorebi::WindowsApi;
|
||||
use komorebi_client::Rgb;
|
||||
|
||||
lazy_static! {
|
||||
static ref BORDER_STATE: Mutex<HashMap<String, Border>> = Mutex::new(HashMap::new());
|
||||
static ref RECT_STATE: Mutex<HashMap<isize, Rect>> = Mutex::new(HashMap::new());
|
||||
static ref FOCUSED_STATE: Mutex<HashMap<isize, FocusKind>> = Mutex::new(HashMap::new());
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum FocusKind {
|
||||
Unfocused,
|
||||
Single,
|
||||
Stack,
|
||||
Monocle,
|
||||
}
|
||||
|
||||
pub fn read_commands_uds(stream: UnixStream) -> color_eyre::Result<()> {
|
||||
let reader = BufReader::new(stream.try_clone()?);
|
||||
for line in reader.lines() {
|
||||
let message = komoborders_client::SocketMessage::from_str(&line?)?;
|
||||
|
||||
match message {
|
||||
komoborders_client::SocketMessage::FocusedColour(r, g, b) => FOCUSED.store(
|
||||
komorebi::Colour::Rgb(Rgb::new(r, g, b)).into(),
|
||||
Ordering::SeqCst,
|
||||
),
|
||||
komoborders_client::SocketMessage::UnfocusedColour(r, g, b) => UNFOCUSED.store(
|
||||
komorebi::Colour::Rgb(Rgb::new(r, g, b)).into(),
|
||||
Ordering::SeqCst,
|
||||
),
|
||||
komoborders_client::SocketMessage::MonocleColour(r, g, b) => MONOCLE.store(
|
||||
komorebi::Colour::Rgb(Rgb::new(r, g, b)).into(),
|
||||
Ordering::SeqCst,
|
||||
),
|
||||
komoborders_client::SocketMessage::StackColour(r, g, b) => STACK.store(
|
||||
komorebi::Colour::Rgb(Rgb::new(r, g, b)).into(),
|
||||
Ordering::SeqCst,
|
||||
),
|
||||
komoborders_client::SocketMessage::Width(width) => {
|
||||
BORDER_WIDTH.store(width, Ordering::SeqCst)
|
||||
}
|
||||
komoborders_client::SocketMessage::Offset(offset) => {
|
||||
BORDER_WIDTH.store(offset, Ordering::SeqCst)
|
||||
}
|
||||
komoborders_client::SocketMessage::ZOrder(z_order) => {
|
||||
let mut z = Z_ORDER.lock();
|
||||
*z = z_order;
|
||||
}
|
||||
}
|
||||
|
||||
let borders = BORDER_STATE.lock();
|
||||
for (_, border) in borders.iter() {
|
||||
border.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> color_eyre::Result<()> {
|
||||
WindowsApi::set_process_dpi_awareness_context()?;
|
||||
let socket = dirs::data_local_dir()
|
||||
.expect("there is no local data directory")
|
||||
.join("komorebi")
|
||||
.join("komoborders.sock");
|
||||
|
||||
match std::fs::remove_file(&socket) {
|
||||
Ok(()) => {}
|
||||
Err(error) => match error.kind() {
|
||||
// Doing this because ::exists() doesn't work reliably on Windows via IntelliJ
|
||||
ErrorKind::NotFound => {}
|
||||
_ => {
|
||||
return Err(error.into());
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let listener = UnixListener::bind(&socket)?;
|
||||
std::thread::spawn(move || {
|
||||
for client in listener.incoming() {
|
||||
match client {
|
||||
Ok(stream) => match read_commands_uds(stream) {
|
||||
Ok(()) => {
|
||||
println!("processed message");
|
||||
}
|
||||
Err(error) => {
|
||||
println!("{error}");
|
||||
}
|
||||
},
|
||||
Err(error) => {
|
||||
println!("{error}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let komorebi = komorebi_client::subscribe("komoborders")?;
|
||||
|
||||
for client in komorebi.incoming() {
|
||||
match client {
|
||||
Ok(subscription) => {
|
||||
let reader = BufReader::new(subscription);
|
||||
|
||||
#[allow(clippy::lines_filter_map_ok)]
|
||||
for line in reader.lines().flatten() {
|
||||
if let Ok(notification) =
|
||||
serde_json::from_str::<komorebi_client::Notification>(&line)
|
||||
{
|
||||
let mut borders = BORDER_STATE.lock();
|
||||
// Check the state every time we receive a notification
|
||||
let state = notification.state;
|
||||
|
||||
for m in state.monitors.elements() {
|
||||
// Only operate on the focused workspace of each monitor
|
||||
if let Some(ws) = m.focused_workspace() {
|
||||
let mut should_proceed = true;
|
||||
|
||||
// Handle the monocle container separately
|
||||
if let Some(monocle) = ws.monocle_container() {
|
||||
for (_, border) in borders.iter() {
|
||||
border.destroy()?;
|
||||
}
|
||||
|
||||
borders.clear();
|
||||
let border = borders
|
||||
.entry(monocle.id().clone())
|
||||
.or_insert_with(|| Border::create(monocle.id()).unwrap());
|
||||
|
||||
{
|
||||
let mut focused = FOCUSED_STATE.lock();
|
||||
focused.insert(border.hwnd, FocusKind::Monocle);
|
||||
}
|
||||
|
||||
let rect = WindowsApi::window_rect(
|
||||
monocle.focused_window().unwrap().hwnd(),
|
||||
)?;
|
||||
|
||||
border.update(&rect)?;
|
||||
should_proceed = false;
|
||||
}
|
||||
|
||||
if should_proceed {
|
||||
let is_maximized = WindowsApi::is_zoomed(HWND(
|
||||
WindowsApi::foreground_window().unwrap_or_default(),
|
||||
));
|
||||
|
||||
if is_maximized {
|
||||
for (_, border) in borders.iter() {
|
||||
border.destroy()?;
|
||||
}
|
||||
|
||||
borders.clear();
|
||||
should_proceed = false;
|
||||
}
|
||||
}
|
||||
|
||||
if should_proceed {
|
||||
// Destroy any borders not associated with the focused workspace
|
||||
let container_ids = ws
|
||||
.containers()
|
||||
.iter()
|
||||
.map(|c| c.id().clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (id, border) in borders.iter() {
|
||||
if !container_ids.contains(id) {
|
||||
border.destroy()?;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove them from the border map
|
||||
borders.retain(|k, _| container_ids.contains(k));
|
||||
|
||||
for (idx, c) in ws.containers().iter().enumerate() {
|
||||
// Get the border entry for this container from the map or create one
|
||||
let border = borders
|
||||
.entry(c.id().clone())
|
||||
.or_insert_with(|| Border::create(c.id()).unwrap());
|
||||
|
||||
// Update the focused state for all containers on this workspace
|
||||
{
|
||||
let mut focused = FOCUSED_STATE.lock();
|
||||
focused.insert(
|
||||
border.hwnd,
|
||||
if idx != ws.focused_container_idx() {
|
||||
FocusKind::Unfocused
|
||||
} else {
|
||||
if c.windows().len() > 1 {
|
||||
FocusKind::Stack
|
||||
} else {
|
||||
FocusKind::Single
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let rect = WindowsApi::window_rect(
|
||||
c.focused_window().unwrap().hwnd(),
|
||||
)?;
|
||||
|
||||
border.update(&rect)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
if error.raw_os_error().expect("could not get raw os error") == 109 {
|
||||
while komorebi_client::send_message(
|
||||
&komorebi_client::SocketMessage::AddSubscriberSocket(String::from(
|
||||
"komoborders",
|
||||
)),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
std::thread::sleep(Duration::from_secs(5));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -218,7 +218,6 @@ pub enum WindowKind {
|
||||
Single,
|
||||
Stack,
|
||||
Monocle,
|
||||
Unfocused,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
|
||||
@@ -12,6 +12,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
komorebi-core = { path = "../komorebi-core" }
|
||||
komoborders-client = { path = "../komoborders-client" }
|
||||
|
||||
bitflags = { version = "2", features = ["serde"] }
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
|
||||
108
komorebi/src/border.rs
Normal file
108
komorebi/src/border.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use color_eyre::Result;
|
||||
use windows::core::PCWSTR;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::FindWindowW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
|
||||
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::WNDCLASSW;
|
||||
|
||||
use crate::window::Window;
|
||||
use crate::windows_callbacks;
|
||||
use crate::WindowsApi;
|
||||
use crate::BORDER_HWND;
|
||||
use crate::BORDER_OFFSET;
|
||||
use crate::BORDER_RECT;
|
||||
use crate::BORDER_WIDTH;
|
||||
use crate::TRANSPARENCY_COLOUR;
|
||||
|
||||
#[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: Vec<u16> = format!("{name}\0").encode_utf16().collect();
|
||||
let instance = WindowsApi::module_handle_w()?;
|
||||
let class_name = PCWSTR(name.as_ptr());
|
||||
let brush = WindowsApi::create_solid_brush(TRANSPARENCY_COLOUR);
|
||||
let window_class = WNDCLASSW {
|
||||
hInstance: instance.into(),
|
||||
lpszClassName: class_name,
|
||||
style: CS_HREDRAW | CS_VREDRAW,
|
||||
lpfnWndProc: Some(windows_callbacks::border_window),
|
||||
hbrBackground: brush,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let _atom = WindowsApi::register_class_w(&window_class)?;
|
||||
|
||||
let name_cl = name.clone();
|
||||
std::thread::spawn(move || -> Result<()> {
|
||||
let hwnd = WindowsApi::create_border_window(PCWSTR(name_cl.as_ptr()), instance)?;
|
||||
let border = Self::from(hwnd);
|
||||
|
||||
let mut message = MSG::default();
|
||||
|
||||
unsafe {
|
||||
while GetMessageW(&mut message, border.hwnd(), 0, 0).into() {
|
||||
DispatchMessageW(&message);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
let mut hwnd = HWND(0);
|
||||
while hwnd == HWND(0) {
|
||||
hwnd = unsafe { FindWindowW(PCWSTR(name.as_ptr()), PCWSTR::null()) };
|
||||
}
|
||||
|
||||
BORDER_HWND.store(hwnd.0, Ordering::SeqCst);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn hide(self) -> Result<()> {
|
||||
if self.hwnd == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
WindowsApi::hide_border_window(self.hwnd())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_position(self, window: Window, activate: bool) -> Result<()> {
|
||||
if self.hwnd == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
if !WindowsApi::is_window(self.hwnd()) {
|
||||
Self::create("komorebi-border-window")?;
|
||||
}
|
||||
|
||||
let mut rect = WindowsApi::window_rect(window.hwnd())?;
|
||||
rect.add_padding(-BORDER_OFFSET.load(Ordering::SeqCst));
|
||||
|
||||
let border_width = BORDER_WIDTH.load(Ordering::SeqCst);
|
||||
rect.add_margin(border_width);
|
||||
|
||||
*BORDER_RECT.lock() = rect;
|
||||
|
||||
WindowsApi::position_border_window(self.hwnd(), &rect, activate)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,288 +0,0 @@
|
||||
mod border;
|
||||
|
||||
use crossbeam_channel::Receiver;
|
||||
use crossbeam_channel::Sender;
|
||||
use crossbeam_utils::atomic::AtomicConsume;
|
||||
use komorebi_core::ActiveWindowBorderStyle;
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::Mutex;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::AtomicI32;
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::sync::Arc;
|
||||
use std::sync::OnceLock;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
|
||||
use crate::Colour;
|
||||
use crate::Rect;
|
||||
use crate::Rgb;
|
||||
use crate::WindowManager;
|
||||
use crate::WindowsApi;
|
||||
use border::Border;
|
||||
use komorebi_core::WindowKind;
|
||||
|
||||
pub static BORDER_WIDTH: AtomicI32 = AtomicI32::new(8);
|
||||
pub static BORDER_OFFSET: AtomicI32 = AtomicI32::new(-1);
|
||||
|
||||
pub static BORDER_ENABLED: AtomicBool = AtomicBool::new(true);
|
||||
|
||||
lazy_static! {
|
||||
pub static ref Z_ORDER: Arc<Mutex<ZOrder>> = Arc::new(Mutex::new(ZOrder::Bottom));
|
||||
pub static ref STYLE: Arc<Mutex<ActiveWindowBorderStyle>> =
|
||||
Arc::new(Mutex::new(ActiveWindowBorderStyle::System));
|
||||
pub static ref FOCUSED: AtomicU32 =
|
||||
AtomicU32::new(u32::from(Colour::Rgb(Rgb::new(66, 165, 245))));
|
||||
pub static ref UNFOCUSED: AtomicU32 =
|
||||
AtomicU32::new(u32::from(Colour::Rgb(Rgb::new(128, 128, 128))));
|
||||
pub static ref MONOCLE: AtomicU32 =
|
||||
AtomicU32::new(u32::from(Colour::Rgb(Rgb::new(255, 51, 153))));
|
||||
pub static ref STACK: AtomicU32 = AtomicU32::new(u32::from(Colour::Rgb(Rgb::new(0, 165, 66))));
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref BORDERS_MONITORS: Mutex<HashMap<String, usize>> = Mutex::new(HashMap::new());
|
||||
static ref BORDER_STATE: Mutex<HashMap<String, Border>> = Mutex::new(HashMap::new());
|
||||
static ref RECT_STATE: Mutex<HashMap<isize, Rect>> = Mutex::new(HashMap::new());
|
||||
static ref FOCUS_STATE: Mutex<HashMap<isize, WindowKind>> = Mutex::new(HashMap::new());
|
||||
}
|
||||
|
||||
pub struct Notification;
|
||||
|
||||
static CHANNEL: OnceLock<(Sender<Notification>, Receiver<Notification>)> = OnceLock::new();
|
||||
|
||||
pub fn channel() -> &'static (Sender<Notification>, Receiver<Notification>) {
|
||||
CHANNEL.get_or_init(crossbeam_channel::unbounded)
|
||||
}
|
||||
|
||||
pub fn event_tx() -> Sender<Notification> {
|
||||
channel().0.clone()
|
||||
}
|
||||
|
||||
pub fn event_rx() -> Receiver<Notification> {
|
||||
channel().1.clone()
|
||||
}
|
||||
|
||||
pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
|
||||
tracing::info!("listening");
|
||||
let receiver = event_rx();
|
||||
|
||||
std::thread::spawn(move || -> color_eyre::Result<()> {
|
||||
'receiver: for _ in receiver {
|
||||
let mut borders = BORDER_STATE.lock();
|
||||
let mut borders_monitors = BORDERS_MONITORS.lock();
|
||||
|
||||
// Check the wm state every time we receive a notification
|
||||
let state = wm.lock();
|
||||
|
||||
if !BORDER_ENABLED.load_consume() || state.is_paused {
|
||||
if !borders.is_empty() {
|
||||
for (_, border) in borders.iter() {
|
||||
border.destroy()?;
|
||||
}
|
||||
|
||||
borders.clear();
|
||||
}
|
||||
|
||||
continue 'receiver;
|
||||
}
|
||||
|
||||
let focused_monitor_idx = state.focused_monitor_idx();
|
||||
|
||||
for (monitor_idx, m) in state.monitors.elements().iter().enumerate() {
|
||||
// Only operate on the focused workspace of each monitor
|
||||
if let Some(ws) = m.focused_workspace() {
|
||||
// Workspaces with tiling disabled don't have borders
|
||||
if !ws.tile() {
|
||||
let mut to_remove = vec![];
|
||||
for (id, border) in borders.iter() {
|
||||
if borders_monitors.get(id).copied().unwrap_or_default() == monitor_idx
|
||||
{
|
||||
border.destroy()?;
|
||||
to_remove.push(id.clone());
|
||||
}
|
||||
}
|
||||
|
||||
for id in &to_remove {
|
||||
borders.remove(id);
|
||||
}
|
||||
|
||||
continue 'receiver;
|
||||
}
|
||||
|
||||
// Handle the monocle container separately
|
||||
if let Some(monocle) = ws.monocle_container() {
|
||||
let mut to_remove = vec![];
|
||||
for (id, border) in borders.iter() {
|
||||
if borders_monitors.get(id).copied().unwrap_or_default() == monitor_idx
|
||||
{
|
||||
border.destroy()?;
|
||||
to_remove.push(id.clone());
|
||||
}
|
||||
}
|
||||
|
||||
for id in &to_remove {
|
||||
borders.remove(id);
|
||||
}
|
||||
|
||||
let border = borders.entry(monocle.id().clone()).or_insert_with(|| {
|
||||
Border::create(monocle.id()).expect("border creation failed")
|
||||
});
|
||||
|
||||
borders_monitors.insert(monocle.id().clone(), monitor_idx);
|
||||
|
||||
{
|
||||
let mut focus_state = FOCUS_STATE.lock();
|
||||
focus_state.insert(border.hwnd, WindowKind::Monocle);
|
||||
}
|
||||
|
||||
let rect = WindowsApi::window_rect(
|
||||
monocle
|
||||
.focused_window()
|
||||
.expect("monocle container has no focused window")
|
||||
.hwnd(),
|
||||
)?;
|
||||
|
||||
border.update(&rect)?;
|
||||
continue 'receiver;
|
||||
}
|
||||
|
||||
let is_maximized = WindowsApi::is_zoomed(HWND(
|
||||
WindowsApi::foreground_window().unwrap_or_default(),
|
||||
));
|
||||
|
||||
if is_maximized {
|
||||
let mut to_remove = vec![];
|
||||
for (id, border) in borders.iter() {
|
||||
if borders_monitors.get(id).copied().unwrap_or_default() == monitor_idx
|
||||
{
|
||||
border.destroy()?;
|
||||
to_remove.push(id.clone());
|
||||
}
|
||||
}
|
||||
|
||||
for id in &to_remove {
|
||||
borders.remove(id);
|
||||
}
|
||||
|
||||
continue 'receiver;
|
||||
}
|
||||
|
||||
// Destroy any borders not associated with the focused workspace
|
||||
let container_ids = ws
|
||||
.containers()
|
||||
.iter()
|
||||
.map(|c| c.id().clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut to_remove = vec![];
|
||||
for (id, border) in borders.iter() {
|
||||
if borders_monitors.get(id).copied().unwrap_or_default() == monitor_idx {
|
||||
if !container_ids.contains(id) {
|
||||
border.destroy()?;
|
||||
to_remove.push(id.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for id in &to_remove {
|
||||
borders.remove(id);
|
||||
}
|
||||
|
||||
for (idx, c) in ws.containers().iter().enumerate() {
|
||||
// Update border when moving or resizing with mouse
|
||||
if state.pending_move_op.is_some() && idx == ws.focused_container_idx() {
|
||||
let restore_z_order = *Z_ORDER.lock();
|
||||
*Z_ORDER.lock() = ZOrder::TopMost;
|
||||
|
||||
let mut rect = WindowsApi::window_rect(
|
||||
c.focused_window()
|
||||
.expect("container has no focused window")
|
||||
.hwnd(),
|
||||
)?;
|
||||
|
||||
while WindowsApi::lbutton_is_pressed() {
|
||||
let border = borders.entry(c.id().clone()).or_insert_with(|| {
|
||||
Border::create(c.id()).expect("border creation failed")
|
||||
});
|
||||
|
||||
let new_rect = WindowsApi::window_rect(
|
||||
c.focused_window()
|
||||
.expect("container has no focused window")
|
||||
.hwnd(),
|
||||
)?;
|
||||
|
||||
if rect != new_rect {
|
||||
rect = new_rect;
|
||||
border.update(&rect)?;
|
||||
}
|
||||
}
|
||||
|
||||
*Z_ORDER.lock() = restore_z_order;
|
||||
|
||||
continue 'receiver;
|
||||
}
|
||||
|
||||
// Get the border entry for this container from the map or create one
|
||||
let border = borders.entry(c.id().clone()).or_insert_with(|| {
|
||||
Border::create(c.id()).expect("border creation failed")
|
||||
});
|
||||
|
||||
borders_monitors.insert(c.id().clone(), monitor_idx);
|
||||
|
||||
// Update the focused state for all containers on this workspace
|
||||
{
|
||||
let mut focus_state = FOCUS_STATE.lock();
|
||||
focus_state.insert(
|
||||
border.hwnd,
|
||||
if idx != ws.focused_container_idx()
|
||||
|| monitor_idx != focused_monitor_idx
|
||||
{
|
||||
WindowKind::Unfocused
|
||||
} else {
|
||||
if c.windows().len() > 1 {
|
||||
WindowKind::Stack
|
||||
} else {
|
||||
WindowKind::Single
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let rect = WindowsApi::window_rect(
|
||||
c.focused_window()
|
||||
.expect("container has no focused window")
|
||||
.hwnd(),
|
||||
)?;
|
||||
|
||||
border.update(&rect)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub enum ZOrder {
|
||||
Top,
|
||||
NoTopMost,
|
||||
Bottom,
|
||||
TopMost,
|
||||
}
|
||||
|
||||
impl Into<isize> for ZOrder {
|
||||
fn into(self) -> isize {
|
||||
match self {
|
||||
ZOrder::Top => 0,
|
||||
ZOrder::NoTopMost => -2,
|
||||
ZOrder::Bottom => 1,
|
||||
ZOrder::TopMost => -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
|
||||
use crate::windows_callbacks;
|
||||
use crate::WindowsApi;
|
||||
use crate::HIDDEN_HWND;
|
||||
use crate::TRANSPARENCY_COLOUR;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Hidden {
|
||||
@@ -35,7 +36,7 @@ impl Hidden {
|
||||
let name: Vec<u16> = format!("{name}\0").encode_utf16().collect();
|
||||
let instance = WindowsApi::module_handle_w()?;
|
||||
let class_name = PCWSTR(name.as_ptr());
|
||||
let brush = WindowsApi::create_solid_brush(0);
|
||||
let brush = WindowsApi::create_solid_brush(TRANSPARENCY_COLOUR);
|
||||
let window_class = WNDCLASSW {
|
||||
hInstance: instance.into(),
|
||||
lpszClassName: class_name,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub mod border_manager;
|
||||
pub mod border;
|
||||
pub mod com;
|
||||
#[macro_use]
|
||||
pub mod ring;
|
||||
@@ -53,6 +53,7 @@ use color_eyre::Result;
|
||||
use komorebi_core::config_generation::IdWithIdentifier;
|
||||
use komorebi_core::config_generation::MatchingRule;
|
||||
use komorebi_core::config_generation::MatchingStrategy;
|
||||
use komorebi_core::ActiveWindowBorderStyle;
|
||||
use komorebi_core::ApplicationIdentifier;
|
||||
use komorebi_core::HidingBehaviour;
|
||||
use komorebi_core::Rect;
|
||||
@@ -196,6 +197,13 @@ lazy_static! {
|
||||
)
|
||||
};
|
||||
|
||||
static ref ACTIVE_WINDOW_BORDER_STYLE: Arc<Mutex<ActiveWindowBorderStyle>> =
|
||||
Arc::new(Mutex::new(ActiveWindowBorderStyle::System));
|
||||
|
||||
static ref BORDER_RECT: Arc<Mutex<Rect>> =
|
||||
Arc::new(Mutex::new(Rect::default()));
|
||||
|
||||
|
||||
// Use app-specific titlebar removal options where possible
|
||||
// eg. Windows Terminal, IntelliJ IDEA, Firefox
|
||||
static ref NO_TITLEBAR: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
|
||||
@@ -212,7 +220,18 @@ pub static DEFAULT_CONTAINER_PADDING: AtomicI32 = AtomicI32::new(10);
|
||||
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_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||
pub static BORDER_HWND: AtomicIsize = AtomicIsize::new(0);
|
||||
pub static BORDER_HIDDEN: AtomicBool = AtomicBool::new(false);
|
||||
pub static BORDER_COLOUR_SINGLE: AtomicU32 = AtomicU32::new(0);
|
||||
pub static BORDER_COLOUR_STACK: AtomicU32 = AtomicU32::new(0);
|
||||
pub static BORDER_COLOUR_MONOCLE: AtomicU32 = AtomicU32::new(0);
|
||||
pub static BORDER_COLOUR_CURRENT: AtomicU32 = AtomicU32::new(0);
|
||||
pub static BORDER_WIDTH: AtomicI32 = AtomicI32::new(8);
|
||||
pub static BORDER_OFFSET: AtomicI32 = AtomicI32::new(-1);
|
||||
|
||||
// 0 0 0 aka pure black, I doubt anyone will want this as a border colour
|
||||
pub const TRANSPARENCY_COLOUR: u32 = 0;
|
||||
pub static REMOVE_TITLEBARS: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
pub static HIDDEN_HWND: AtomicIsize = AtomicIsize::new(0);
|
||||
|
||||
@@ -23,7 +23,6 @@ use tracing_appender::non_blocking::WorkerGuard;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use komorebi::border_manager;
|
||||
use komorebi::hidden::Hidden;
|
||||
use komorebi::load_configuration;
|
||||
use komorebi::process_command::listen_for_commands;
|
||||
@@ -254,8 +253,6 @@ fn main() -> Result<()> {
|
||||
listen_for_movements(wm.clone());
|
||||
}
|
||||
|
||||
border_manager::listen_for_notifications(wm.clone());
|
||||
|
||||
let (ctrlc_sender, ctrlc_receiver) = crossbeam_channel::bounded(1);
|
||||
ctrlc::set_handler(move || {
|
||||
ctrlc_sender
|
||||
|
||||
@@ -13,6 +13,7 @@ use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::eyre::bail;
|
||||
use color_eyre::Result;
|
||||
use miow::pipe::connect;
|
||||
use net2::TcpStreamExt;
|
||||
@@ -38,8 +39,7 @@ use komorebi_core::StateQuery;
|
||||
use komorebi_core::WindowContainerBehaviour;
|
||||
use komorebi_core::WindowKind;
|
||||
|
||||
use crate::border_manager;
|
||||
use crate::border_manager::STYLE;
|
||||
use crate::border::Border;
|
||||
use crate::colour::Rgb;
|
||||
use crate::current_virtual_desktop;
|
||||
use crate::notify_subscribers;
|
||||
@@ -52,6 +52,16 @@ use crate::windows_api::WindowsApi;
|
||||
use crate::GlobalState;
|
||||
use crate::Notification;
|
||||
use crate::NotificationEvent;
|
||||
use crate::ACTIVE_WINDOW_BORDER_STYLE;
|
||||
use crate::BORDER_COLOUR_CURRENT;
|
||||
use crate::BORDER_COLOUR_MONOCLE;
|
||||
use crate::BORDER_COLOUR_SINGLE;
|
||||
use crate::BORDER_COLOUR_STACK;
|
||||
use crate::BORDER_ENABLED;
|
||||
use crate::BORDER_HIDDEN;
|
||||
use crate::BORDER_HWND;
|
||||
use crate::BORDER_OFFSET;
|
||||
use crate::BORDER_WIDTH;
|
||||
use crate::CUSTOM_FFM;
|
||||
use crate::DATA_DIR;
|
||||
use crate::DISPLAY_INDEX_PREFERENCES;
|
||||
@@ -165,6 +175,21 @@ impl WindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
match message {
|
||||
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()?;
|
||||
BORDER_HIDDEN.store(true, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
match message {
|
||||
SocketMessage::CycleFocusWorkspace(_) | SocketMessage::FocusWorkspaceNumber(_) => {
|
||||
if let Some(monitor) = self.focused_monitor_mut() {
|
||||
@@ -615,6 +640,10 @@ impl WindowManager {
|
||||
);
|
||||
|
||||
self.focus_workspace(workspace_idx)?;
|
||||
|
||||
if BORDER_ENABLED.load(Ordering::SeqCst) {
|
||||
self.show_border()?;
|
||||
};
|
||||
}
|
||||
SocketMessage::FocusLastWorkspace => {
|
||||
// This is to ensure that even on an empty workspace on a secondary monitor, the
|
||||
@@ -638,6 +667,10 @@ impl WindowManager {
|
||||
self.focused_monitor_mut()
|
||||
.ok_or_else(|| anyhow!("there is no monitor"))?
|
||||
.set_last_focused_workspace(Option::from(idx));
|
||||
|
||||
if BORDER_ENABLED.load(Ordering::SeqCst) {
|
||||
self.show_border()?;
|
||||
};
|
||||
}
|
||||
SocketMessage::FocusWorkspaceNumber(workspace_idx) => {
|
||||
// This is to ensure that even on an empty workspace on a secondary monitor, the
|
||||
@@ -648,6 +681,10 @@ impl WindowManager {
|
||||
}
|
||||
|
||||
self.focus_workspace(workspace_idx)?;
|
||||
|
||||
if BORDER_ENABLED.load(Ordering::SeqCst) {
|
||||
self.show_border()?;
|
||||
};
|
||||
}
|
||||
SocketMessage::FocusWorkspaceNumbers(workspace_idx) => {
|
||||
// This is to ensure that even on an empty workspace on a secondary monitor, the
|
||||
@@ -667,6 +704,10 @@ impl WindowManager {
|
||||
}
|
||||
|
||||
self.focus_workspace(workspace_idx)?;
|
||||
|
||||
if BORDER_ENABLED.load(Ordering::SeqCst) {
|
||||
self.show_border()?;
|
||||
};
|
||||
}
|
||||
SocketMessage::FocusMonitorWorkspaceNumber(monitor_idx, workspace_idx) => {
|
||||
self.focus_monitor(monitor_idx)?;
|
||||
@@ -679,6 +720,10 @@ impl WindowManager {
|
||||
self.focus_monitor(monitor_idx)?;
|
||||
self.focus_workspace(workspace_idx)?;
|
||||
}
|
||||
|
||||
if BORDER_ENABLED.load(Ordering::SeqCst) {
|
||||
self.show_border()?;
|
||||
};
|
||||
}
|
||||
SocketMessage::Stop => {
|
||||
tracing::info!(
|
||||
@@ -1193,31 +1238,60 @@ impl WindowManager {
|
||||
self.unmanaged_window_operation_behaviour = behaviour;
|
||||
}
|
||||
SocketMessage::ActiveWindowBorder(enable) => {
|
||||
border_manager::BORDER_ENABLED.store(enable, Ordering::SeqCst);
|
||||
if enable {
|
||||
if BORDER_HWND.load(Ordering::SeqCst) == 0 {
|
||||
Border::create("komorebi-border-window")?;
|
||||
}
|
||||
|
||||
BORDER_ENABLED.store(true, Ordering::SeqCst);
|
||||
self.show_border()?;
|
||||
} else {
|
||||
BORDER_ENABLED.store(false, Ordering::SeqCst);
|
||||
self.hide_border()?;
|
||||
}
|
||||
}
|
||||
SocketMessage::ActiveWindowBorderColour(kind, r, g, b) => match kind {
|
||||
WindowKind::Single => {
|
||||
border_manager::FOCUSED.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
SocketMessage::ActiveWindowBorderColour(kind, r, g, b) => {
|
||||
match kind {
|
||||
WindowKind::Single => {
|
||||
BORDER_COLOUR_SINGLE.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
BORDER_COLOUR_CURRENT.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
komoborders_client::send_message(
|
||||
&komoborders_client::SocketMessage::FocusedColour(r, g, b),
|
||||
)?;
|
||||
}
|
||||
WindowKind::Stack => {
|
||||
BORDER_COLOUR_STACK.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
komoborders_client::send_message(
|
||||
&komoborders_client::SocketMessage::StackColour(r, g, b),
|
||||
)?;
|
||||
}
|
||||
WindowKind::Monocle => {
|
||||
BORDER_COLOUR_MONOCLE.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
komoborders_client::send_message(
|
||||
&komoborders_client::SocketMessage::MonocleColour(r, g, b),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
WindowKind::Stack => {
|
||||
border_manager::STACK.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
}
|
||||
WindowKind::Monocle => {
|
||||
border_manager::MONOCLE.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
}
|
||||
WindowKind::Unfocused => {
|
||||
border_manager::UNFOCUSED.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
}
|
||||
},
|
||||
|
||||
WindowsApi::invalidate_border_rect()?;
|
||||
}
|
||||
SocketMessage::ActiveWindowBorderStyle(style) => {
|
||||
let mut active_window_border_style = STYLE.lock();
|
||||
let mut active_window_border_style = ACTIVE_WINDOW_BORDER_STYLE.lock();
|
||||
*active_window_border_style = style;
|
||||
|
||||
WindowsApi::invalidate_border_rect()?;
|
||||
}
|
||||
SocketMessage::BorderWidth(width) => {
|
||||
border_manager::BORDER_WIDTH.store(width, Ordering::SeqCst);
|
||||
BORDER_WIDTH.store(width, Ordering::SeqCst);
|
||||
WindowsApi::invalidate_border_rect()?;
|
||||
komoborders_client::send_message(&komoborders_client::SocketMessage::Width(width))?;
|
||||
}
|
||||
SocketMessage::BorderOffset(offset) => {
|
||||
border_manager::BORDER_OFFSET.store(offset, Ordering::SeqCst);
|
||||
BORDER_OFFSET.store(offset, Ordering::SeqCst);
|
||||
WindowsApi::invalidate_border_rect()?;
|
||||
komoborders_client::send_message(&komoborders_client::SocketMessage::Offset(
|
||||
offset,
|
||||
))?;
|
||||
}
|
||||
SocketMessage::StackbarMode(mode) => {
|
||||
let mut stackbar_mode = STACKBAR_MODE.lock();
|
||||
@@ -1309,13 +1383,154 @@ impl WindowManager {
|
||||
| SocketMessage::IdentifyBorderOverflowApplication(_, _) => {}
|
||||
};
|
||||
|
||||
let notification = Notification {
|
||||
event: NotificationEvent::Socket(message.clone()),
|
||||
state: self.as_ref().into(),
|
||||
};
|
||||
match message {
|
||||
SocketMessage::ToggleMonocle => {
|
||||
let current = BORDER_COLOUR_CURRENT.load(Ordering::SeqCst);
|
||||
let monocle = BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst);
|
||||
|
||||
notify_subscribers(&serde_json::to_string(¬ification)?)?;
|
||||
border_manager::event_tx().send(border_manager::Notification)?;
|
||||
if monocle != 0 {
|
||||
if current == monocle {
|
||||
BORDER_COLOUR_CURRENT.store(
|
||||
BORDER_COLOUR_SINGLE.load(Ordering::SeqCst),
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
} else {
|
||||
BORDER_COLOUR_CURRENT.store(
|
||||
BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst),
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
SocketMessage::StackWindow(_) => {
|
||||
let stack = BORDER_COLOUR_STACK.load(Ordering::SeqCst);
|
||||
if stack != 0 {
|
||||
BORDER_COLOUR_CURRENT
|
||||
.store(BORDER_COLOUR_STACK.load(Ordering::SeqCst), Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
SocketMessage::UnstackWindow => {
|
||||
BORDER_COLOUR_CURRENT.store(
|
||||
BORDER_COLOUR_SINGLE.load(Ordering::SeqCst),
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match message {
|
||||
SocketMessage::ChangeLayout(_)
|
||||
| SocketMessage::CycleLayout(_)
|
||||
| SocketMessage::ChangeLayoutCustom(_)
|
||||
| SocketMessage::FlipLayout(_)
|
||||
| SocketMessage::ManageFocusedWindow
|
||||
| SocketMessage::MoveWorkspaceToMonitorNumber(_)
|
||||
| SocketMessage::MoveContainerToMonitorNumber(_)
|
||||
| SocketMessage::MoveContainerToWorkspaceNumber(_)
|
||||
| SocketMessage::MoveContainerToMonitorWorkspaceNumber(_, _)
|
||||
| SocketMessage::MoveContainerToNamedWorkspace(_)
|
||||
| SocketMessage::ResizeWindowEdge(_, _)
|
||||
| SocketMessage::ResizeWindowAxis(_, _)
|
||||
| SocketMessage::ToggleFloat
|
||||
| SocketMessage::ToggleMonocle
|
||||
| SocketMessage::ToggleMaximize
|
||||
| SocketMessage::Promote
|
||||
| SocketMessage::PromoteFocus
|
||||
| SocketMessage::StackWindow(_)
|
||||
| SocketMessage::UnstackWindow
|
||||
| SocketMessage::Retile
|
||||
// Adding this one so that changes can be seen instantly after
|
||||
// modifying the active window border offset
|
||||
| SocketMessage::BorderOffset(_)
|
||||
// Adding this one because sometimes EVENT_SYSTEM_FOREGROUND isn't
|
||||
// getting sent on FocusWindow, meaning the border won't be set
|
||||
// when processing events
|
||||
| SocketMessage::FocusWindow(_)
|
||||
| SocketMessage::InvisibleBorders(_)
|
||||
| SocketMessage::WorkAreaOffset(_)
|
||||
| SocketMessage::CycleMoveWindow(_)
|
||||
| SocketMessage::MoveWindow(_)
|
||||
| SocketMessage::CycleFocusMonitor(_)
|
||||
| SocketMessage::CycleFocusWorkspace(_)
|
||||
| SocketMessage::FocusMonitorNumber(_)
|
||||
| SocketMessage::FocusMonitorWorkspaceNumber(_, _)
|
||||
| SocketMessage::FocusWorkspaceNumber(_) => {
|
||||
// The foreground window might be de-activating if we've just
|
||||
// set it as a result of our own actions, so wait until the new
|
||||
// one returns. This particularly happens when switching monitors.
|
||||
//
|
||||
// TODO(raggi): re-evaluate this branch. I checked the
|
||||
// suggestion from the comment above, that we don't get
|
||||
// EVENT_SYSTEM_FOREGROUND, but if I print out trace events I
|
||||
// see that we do.
|
||||
// XXX(raggi) We drop FocusChange events though for windows that
|
||||
// we're not managing, so that's one of the ways that the border
|
||||
// window gets stuck. We should stop overloading `should_manage`
|
||||
// as an event filter, and separately filter events that we want
|
||||
// to handle, and windows that we want to handle, as some events
|
||||
// must be handled even if we're not managing the target window.
|
||||
let mut attempts = 0;
|
||||
let foreground = loop {
|
||||
match WindowsApi::foreground_window() {
|
||||
Ok(foreground) => break foreground,
|
||||
Err(_) => {
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
attempts+=1;
|
||||
if attempts == 10 {
|
||||
bail!("failed to get foreground window after 100ms")
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
let foreground_window = Window { hwnd: foreground };
|
||||
|
||||
let monocle = BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst);
|
||||
if monocle != 0 && self.focused_workspace()?.monocle_container().is_some() {
|
||||
BORDER_COLOUR_CURRENT.store(
|
||||
monocle,
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
}
|
||||
|
||||
// it is not acceptable to fail here; we need to be able to send the event to
|
||||
// subscribers
|
||||
if self.focused_container().is_ok() {
|
||||
let stack = BORDER_COLOUR_STACK.load(Ordering::SeqCst);
|
||||
if stack != 0 && self.focused_container()?.windows().len() > 1 {
|
||||
BORDER_COLOUR_CURRENT
|
||||
.store(stack, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
border.set_position(foreground_window, false)?;
|
||||
}
|
||||
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, true)?;
|
||||
focused.focus(false)?;
|
||||
}
|
||||
}
|
||||
SocketMessage::ToggleTiling | SocketMessage::WorkspaceTiling(..) => {
|
||||
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, true)?;
|
||||
focused.focus(false)?;
|
||||
} else {
|
||||
border.hide()?;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
tracing::info!("processed");
|
||||
Ok(())
|
||||
@@ -1392,6 +1607,10 @@ pub fn read_commands_uds(wm: &Arc<Mutex<WindowManager>>, mut stream: UnixStream)
|
||||
}
|
||||
|
||||
wm.process_command(message.clone(), &mut stream)?;
|
||||
notify_subscribers(&serde_json::to_string(&Notification {
|
||||
event: NotificationEvent::Socket(message.clone()),
|
||||
state: wm.as_ref().into(),
|
||||
})?)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -1438,6 +1657,10 @@ pub fn read_commands_tcp(
|
||||
}
|
||||
|
||||
wm.process_command(message.clone(), &mut *stream)?;
|
||||
notify_subscribers(&serde_json::to_string(&Notification {
|
||||
event: NotificationEvent::Socket(message.clone()),
|
||||
state: wm.as_ref().into(),
|
||||
})?)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,7 @@ use komorebi_core::Rect;
|
||||
use komorebi_core::Sizing;
|
||||
use komorebi_core::WindowContainerBehaviour;
|
||||
|
||||
use crate::border_manager;
|
||||
use crate::border_manager::BORDER_OFFSET;
|
||||
use crate::border_manager::BORDER_WIDTH;
|
||||
use crate::border::Border;
|
||||
use crate::current_virtual_desktop;
|
||||
use crate::notify_subscribers;
|
||||
use crate::window::should_act;
|
||||
@@ -21,9 +19,17 @@ use crate::window::RuleDebug;
|
||||
use crate::window_manager::WindowManager;
|
||||
use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::winevent::WinEvent;
|
||||
use crate::Notification;
|
||||
use crate::NotificationEvent;
|
||||
use crate::BORDER_COLOUR_CURRENT;
|
||||
use crate::BORDER_COLOUR_MONOCLE;
|
||||
use crate::BORDER_COLOUR_SINGLE;
|
||||
use crate::BORDER_COLOUR_STACK;
|
||||
use crate::BORDER_ENABLED;
|
||||
use crate::BORDER_HIDDEN;
|
||||
use crate::BORDER_HWND;
|
||||
use crate::BORDER_OFFSET;
|
||||
use crate::BORDER_WIDTH;
|
||||
use crate::DATA_DIR;
|
||||
use crate::HIDDEN_HWNDS;
|
||||
use crate::REGEX_IDENTIFIERS;
|
||||
@@ -65,6 +71,28 @@ impl WindowManager {
|
||||
|
||||
let should_manage = event.window().should_manage(Some(event), &mut rule_debug)?;
|
||||
|
||||
// Hide or reposition the window based on whether the target is managed.
|
||||
if BORDER_ENABLED.load(Ordering::SeqCst) {
|
||||
if let WindowManagerEvent::FocusChange(_, window) = event {
|
||||
let border_window = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
|
||||
if should_manage {
|
||||
border_window.set_position(window, true)?;
|
||||
} else {
|
||||
let mut stackbar = false;
|
||||
if let Ok(class) = window.class() {
|
||||
if class == "komorebi_stackbar" {
|
||||
stackbar = true;
|
||||
}
|
||||
}
|
||||
|
||||
if !stackbar {
|
||||
border_window.hide()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All event handlers below this point should only be processed if the event is
|
||||
// related to a window that should be managed by the WindowManager.
|
||||
if !should_manage && !matches!(event, WindowManagerEvent::DisplayChange(_)) {
|
||||
@@ -570,11 +598,106 @@ impl WindowManager {
|
||||
| WindowManagerEvent::Cloak(..) => {}
|
||||
};
|
||||
|
||||
if !self.focused_workspace()?.tile() {
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
border.hide()?;
|
||||
BORDER_HIDDEN.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
if *self.focused_workspace()?.tile() && BORDER_ENABLED.load(Ordering::SeqCst) {
|
||||
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)
|
||||
| WindowManagerEvent::Hide(_, window)
|
||||
| WindowManagerEvent::Uncloak(_, window)
|
||||
| WindowManagerEvent::Minimize(_, window) => {
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
let mut target_window = None;
|
||||
let mut target_window_is_monocle = false;
|
||||
if self
|
||||
.focused_workspace()?
|
||||
.floating_windows()
|
||||
.iter()
|
||||
.any(|w| w.hwnd == window.hwnd)
|
||||
{
|
||||
target_window = Option::from(window);
|
||||
WindowsApi::raise_window(border.hwnd())?;
|
||||
};
|
||||
|
||||
if let Some(monocle_container) = self.focused_workspace()?.monocle_container() {
|
||||
if let Some(window) = monocle_container.focused_window() {
|
||||
target_window = Option::from(*window);
|
||||
target_window_is_monocle = true;
|
||||
}
|
||||
}
|
||||
|
||||
if target_window.is_none() {
|
||||
match self.focused_container() {
|
||||
// if there is no focused container, the desktop is empty
|
||||
Err(..) => {
|
||||
WindowsApi::hide_border_window(border.hwnd())?;
|
||||
}
|
||||
Ok(container) => {
|
||||
if !(matches!(event, WindowManagerEvent::Minimize(_, _))
|
||||
&& container.windows().len() == 1)
|
||||
{
|
||||
let container_size = self.focused_container()?.windows().len();
|
||||
target_window = Option::from(*self.focused_window()?);
|
||||
|
||||
if target_window_is_monocle {
|
||||
BORDER_COLOUR_CURRENT.store(
|
||||
BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst),
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
} else if container_size > 1 {
|
||||
BORDER_COLOUR_CURRENT.store(
|
||||
BORDER_COLOUR_STACK.load(Ordering::SeqCst),
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
} else {
|
||||
BORDER_COLOUR_CURRENT.store(
|
||||
BORDER_COLOUR_SINGLE.load(Ordering::SeqCst),
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(target_window) = target_window {
|
||||
let activate = BORDER_HIDDEN.load(Ordering::SeqCst);
|
||||
|
||||
WindowsApi::invalidate_border_rect()?;
|
||||
border.set_position(target_window, 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()?)?;
|
||||
}
|
||||
|
||||
// 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() {
|
||||
@@ -595,21 +718,10 @@ impl WindowManager {
|
||||
.open(hwnd_json)?;
|
||||
|
||||
serde_json::to_writer_pretty(&file, &known_hwnds)?;
|
||||
|
||||
let notification = Notification {
|
||||
notify_subscribers(&serde_json::to_string(&Notification {
|
||||
event: NotificationEvent::WindowManager(event),
|
||||
state: self.as_ref().into(),
|
||||
};
|
||||
|
||||
// Avoid unnecessary updates, this fires every single time you interact
|
||||
// with something on JetBrains IDEs
|
||||
if !matches!(
|
||||
event,
|
||||
WindowManagerEvent::Show(WinEvent::ObjectNameChange, _)
|
||||
) {
|
||||
notify_subscribers(&serde_json::to_string(¬ification)?)?;
|
||||
border_manager::event_tx().send(border_manager::Notification)?;
|
||||
}
|
||||
})?)?;
|
||||
|
||||
tracing::info!("processed: {}", event.window().to_string());
|
||||
Ok(())
|
||||
|
||||
@@ -61,6 +61,7 @@ use crate::STACKBAR_TAB_BACKGROUND_COLOUR;
|
||||
use crate::STACKBAR_TAB_HEIGHT;
|
||||
use crate::STACKBAR_TAB_WIDTH;
|
||||
use crate::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
||||
use crate::TRANSPARENCY_COLOUR;
|
||||
use crate::WINDOWS_BY_BAR_HWNDS;
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
@@ -128,7 +129,7 @@ impl Stackbar {
|
||||
lpfnWndProc: Some(Self::window_proc),
|
||||
hInstance: h_module.into(),
|
||||
lpszClassName: class_name,
|
||||
hbrBackground: WindowsApi::create_solid_brush(0),
|
||||
hbrBackground: WindowsApi::create_solid_brush(TRANSPARENCY_COLOUR),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -156,7 +157,7 @@ impl Stackbar {
|
||||
None,
|
||||
);
|
||||
|
||||
SetLayeredWindowAttributes(hwnd, COLORREF(0), 0, LWA_COLORKEY)?;
|
||||
SetLayeredWindowAttributes(hwnd, COLORREF(TRANSPARENCY_COLOUR), 0, LWA_COLORKEY)?;
|
||||
hwnd_sender.send(hwnd)?;
|
||||
|
||||
let mut msg = MSG::default();
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use crate::border_manager;
|
||||
use crate::border_manager::ZOrder;
|
||||
use crate::border_manager::STYLE;
|
||||
use crate::border_manager::Z_ORDER;
|
||||
use crate::border::Border;
|
||||
use crate::colour::Colour;
|
||||
use crate::current_virtual_desktop;
|
||||
use crate::monitor::Monitor;
|
||||
@@ -10,6 +7,16 @@ use crate::window_manager::WindowManager;
|
||||
use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::workspace::Workspace;
|
||||
use crate::Rgb;
|
||||
use crate::ACTIVE_WINDOW_BORDER_STYLE;
|
||||
use crate::BORDER_COLOUR_CURRENT;
|
||||
use crate::BORDER_COLOUR_MONOCLE;
|
||||
use crate::BORDER_COLOUR_SINGLE;
|
||||
use crate::BORDER_COLOUR_STACK;
|
||||
use crate::BORDER_ENABLED;
|
||||
use crate::BORDER_HWND;
|
||||
use crate::BORDER_OFFSET;
|
||||
use crate::BORDER_WIDTH;
|
||||
use crate::DATA_DIR;
|
||||
use crate::DEFAULT_CONTAINER_PADDING;
|
||||
use crate::DEFAULT_WORKSPACE_PADDING;
|
||||
@@ -71,13 +78,11 @@ use uds_windows::UnixStream;
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct ActiveWindowBorderColours {
|
||||
/// Border colour when the container contains a single window
|
||||
pub single: Option<Colour>,
|
||||
pub single: Colour,
|
||||
/// Border colour when the container contains multiple windows
|
||||
pub stack: Option<Colour>,
|
||||
pub stack: Colour,
|
||||
/// Border colour when the container is in monocle mode
|
||||
pub monocle: Option<Colour>,
|
||||
/// Border colour when the container is unfocused
|
||||
pub unfocused: Option<Colour>,
|
||||
pub monocle: Colour,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
@@ -261,9 +266,6 @@ pub struct StaticConfig {
|
||||
/// Active window border style (default: System)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub active_window_border_style: Option<ActiveWindowBorderStyle>,
|
||||
/// Active window border z-order (default: System)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub active_window_border_z_order: Option<ZOrder>,
|
||||
/// Global default workspace padding (default: 10)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub default_workspace_padding: Option<i32>,
|
||||
@@ -383,16 +385,21 @@ impl From<&WindowManager> for StaticConfig {
|
||||
}
|
||||
}
|
||||
|
||||
let border_colours = if border_manager::FOCUSED.load(Ordering::SeqCst) == 0 {
|
||||
let border_colours = if BORDER_COLOUR_SINGLE.load(Ordering::SeqCst) == 0 {
|
||||
None
|
||||
} else {
|
||||
Option::from(ActiveWindowBorderColours {
|
||||
single: Option::from(Colour::from(border_manager::FOCUSED.load(Ordering::SeqCst))),
|
||||
stack: Option::from(Colour::from(border_manager::STACK.load(Ordering::SeqCst))),
|
||||
monocle: Option::from(Colour::from(border_manager::MONOCLE.load(Ordering::SeqCst))),
|
||||
unfocused: Option::from(Colour::from(
|
||||
border_manager::UNFOCUSED.load(Ordering::SeqCst),
|
||||
)),
|
||||
single: Colour::from(BORDER_COLOUR_SINGLE.load(Ordering::SeqCst)),
|
||||
stack: Colour::from(if BORDER_COLOUR_STACK.load(Ordering::SeqCst) == 0 {
|
||||
BORDER_COLOUR_SINGLE.load(Ordering::SeqCst)
|
||||
} else {
|
||||
BORDER_COLOUR_STACK.load(Ordering::SeqCst)
|
||||
}),
|
||||
monocle: Colour::from(if BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst) == 0 {
|
||||
BORDER_COLOUR_SINGLE.load(Ordering::SeqCst)
|
||||
} else {
|
||||
BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst)
|
||||
}),
|
||||
})
|
||||
};
|
||||
|
||||
@@ -407,14 +414,11 @@ impl From<&WindowManager> for StaticConfig {
|
||||
focus_follows_mouse: value.focus_follows_mouse,
|
||||
mouse_follows_focus: Option::from(value.mouse_follows_focus),
|
||||
app_specific_configuration_path: None,
|
||||
border_width: Option::from(border_manager::BORDER_WIDTH.load(Ordering::SeqCst)),
|
||||
border_offset: Option::from(border_manager::BORDER_OFFSET.load(Ordering::SeqCst)),
|
||||
active_window_border: Option::from(
|
||||
border_manager::BORDER_ENABLED.load(Ordering::SeqCst),
|
||||
),
|
||||
border_width: Option::from(BORDER_WIDTH.load(Ordering::SeqCst)),
|
||||
border_offset: Option::from(BORDER_OFFSET.load(Ordering::SeqCst)),
|
||||
active_window_border: Option::from(BORDER_ENABLED.load(Ordering::SeqCst)),
|
||||
active_window_border_colours: border_colours,
|
||||
active_window_border_style: Option::from(*STYLE.lock()),
|
||||
active_window_border_z_order: Option::from(*Z_ORDER.lock()),
|
||||
active_window_border_style: Option::from(*ACTIVE_WINDOW_BORDER_STYLE.lock()),
|
||||
default_workspace_padding: Option::from(
|
||||
DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst),
|
||||
),
|
||||
@@ -463,33 +467,49 @@ impl StaticConfig {
|
||||
DEFAULT_WORKSPACE_PADDING.store(workspace, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
border_manager::BORDER_WIDTH.store(self.border_width.unwrap_or(8), Ordering::SeqCst);
|
||||
border_manager::BORDER_OFFSET.store(self.border_offset.unwrap_or(-1), Ordering::SeqCst);
|
||||
self.border_width.map_or_else(
|
||||
|| {
|
||||
BORDER_WIDTH.store(8, Ordering::SeqCst);
|
||||
},
|
||||
|width| {
|
||||
BORDER_WIDTH.store(width, Ordering::SeqCst);
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(enabled) = &self.active_window_border {
|
||||
border_manager::BORDER_ENABLED.store(*enabled, Ordering::SeqCst);
|
||||
}
|
||||
komoborders_client::send_message(&komoborders_client::SocketMessage::Width(
|
||||
self.border_width.unwrap_or(8),
|
||||
))?;
|
||||
|
||||
BORDER_OFFSET.store(self.border_offset.unwrap_or(-1), Ordering::SeqCst);
|
||||
|
||||
komoborders_client::send_message(&komoborders_client::SocketMessage::Width(
|
||||
self.border_offset.unwrap_or(-1),
|
||||
))?;
|
||||
|
||||
if let Some(colours) = &self.active_window_border_colours {
|
||||
if let Some(single) = colours.single {
|
||||
border_manager::FOCUSED.store(u32::from(single), Ordering::SeqCst);
|
||||
}
|
||||
BORDER_COLOUR_SINGLE.store(u32::from(colours.single), Ordering::SeqCst);
|
||||
BORDER_COLOUR_CURRENT.store(u32::from(colours.single), Ordering::SeqCst);
|
||||
BORDER_COLOUR_STACK.store(u32::from(colours.stack), Ordering::SeqCst);
|
||||
BORDER_COLOUR_MONOCLE.store(u32::from(colours.monocle), Ordering::SeqCst);
|
||||
|
||||
if let Some(stack) = colours.stack {
|
||||
border_manager::STACK.store(u32::from(stack), Ordering::SeqCst);
|
||||
}
|
||||
let single: Rgb = u32::from(colours.single).into();
|
||||
komoborders_client::send_message(&komoborders_client::SocketMessage::FocusedColour(
|
||||
single.r, single.g, single.b,
|
||||
))?;
|
||||
|
||||
if let Some(monocle) = colours.monocle {
|
||||
border_manager::MONOCLE.store(u32::from(monocle), Ordering::SeqCst);
|
||||
}
|
||||
let stack: Rgb = u32::from(colours.stack).into();
|
||||
komoborders_client::send_message(&komoborders_client::SocketMessage::StackColour(
|
||||
stack.r, stack.g, stack.b,
|
||||
))?;
|
||||
|
||||
if let Some(unfocused) = colours.unfocused {
|
||||
border_manager::UNFOCUSED.store(u32::from(unfocused), Ordering::SeqCst);
|
||||
}
|
||||
let monocle: Rgb = u32::from(colours.monocle).into();
|
||||
komoborders_client::send_message(&komoborders_client::SocketMessage::MonocleColour(
|
||||
monocle.r, monocle.g, monocle.b,
|
||||
))?;
|
||||
}
|
||||
|
||||
let active_window_border_style = self.active_window_border_style.unwrap_or_default();
|
||||
*STYLE.lock() = active_window_border_style;
|
||||
*ACTIVE_WINDOW_BORDER_STYLE.lock() = active_window_border_style;
|
||||
|
||||
let mut float_identifiers = FLOAT_IDENTIFIERS.lock();
|
||||
let mut regex_identifiers = REGEX_IDENTIFIERS.lock();
|
||||
@@ -722,7 +742,12 @@ impl StaticConfig {
|
||||
}
|
||||
|
||||
if value.active_window_border == Some(true) {
|
||||
border_manager::BORDER_ENABLED.store(true, Ordering::SeqCst);
|
||||
if BORDER_HWND.load(Ordering::SeqCst) == 0 {
|
||||
Border::create("komorebi-border-window")?;
|
||||
}
|
||||
|
||||
BORDER_ENABLED.store(true, Ordering::SeqCst);
|
||||
wm.show_border()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -776,8 +801,16 @@ impl StaticConfig {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(enabled) = value.active_window_border {
|
||||
border_manager::BORDER_ENABLED.store(enabled, Ordering::SeqCst);
|
||||
if value.active_window_border == Some(true) {
|
||||
if BORDER_HWND.load(Ordering::SeqCst) == 0 {
|
||||
Border::create("komorebi-border-window")?;
|
||||
}
|
||||
|
||||
BORDER_ENABLED.store(true, Ordering::SeqCst);
|
||||
wm.show_border()?;
|
||||
} else {
|
||||
BORDER_ENABLED.store(false, Ordering::SeqCst);
|
||||
wm.hide_border()?;
|
||||
}
|
||||
|
||||
if let Some(val) = value.window_container_behaviour {
|
||||
|
||||
@@ -39,8 +39,7 @@ use komorebi_core::Rect;
|
||||
use komorebi_core::Sizing;
|
||||
use komorebi_core::WindowContainerBehaviour;
|
||||
|
||||
use crate::border_manager;
|
||||
use crate::border_manager::STYLE;
|
||||
use crate::border::Border;
|
||||
use crate::container::Container;
|
||||
use crate::current_virtual_desktop;
|
||||
use crate::load_configuration;
|
||||
@@ -56,6 +55,14 @@ use crate::ActiveWindowBorderColours;
|
||||
use crate::Colour;
|
||||
use crate::Rgb;
|
||||
use crate::WorkspaceRule;
|
||||
use crate::ACTIVE_WINDOW_BORDER_STYLE;
|
||||
use crate::BORDER_COLOUR_MONOCLE;
|
||||
use crate::BORDER_COLOUR_SINGLE;
|
||||
use crate::BORDER_COLOUR_STACK;
|
||||
use crate::BORDER_ENABLED;
|
||||
use crate::BORDER_HWND;
|
||||
use crate::BORDER_OFFSET;
|
||||
use crate::BORDER_WIDTH;
|
||||
use crate::CUSTOM_FFM;
|
||||
use crate::DATA_DIR;
|
||||
use crate::DISPLAY_INDEX_PREFERENCES;
|
||||
@@ -146,24 +153,15 @@ pub struct GlobalState {
|
||||
impl Default for GlobalState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
active_window_border_enabled: border_manager::BORDER_ENABLED.load(Ordering::SeqCst),
|
||||
active_window_border_enabled: BORDER_ENABLED.load(Ordering::SeqCst),
|
||||
active_window_border_colours: ActiveWindowBorderColours {
|
||||
single: Option::from(Colour::Rgb(Rgb::from(
|
||||
border_manager::FOCUSED.load(Ordering::SeqCst),
|
||||
))),
|
||||
stack: Option::from(Colour::Rgb(Rgb::from(
|
||||
border_manager::STACK.load(Ordering::SeqCst),
|
||||
))),
|
||||
monocle: Option::from(Colour::Rgb(Rgb::from(
|
||||
border_manager::MONOCLE.load(Ordering::SeqCst),
|
||||
))),
|
||||
unfocused: Option::from(Colour::Rgb(Rgb::from(
|
||||
border_manager::UNFOCUSED.load(Ordering::SeqCst),
|
||||
))),
|
||||
single: Colour::Rgb(Rgb::from(BORDER_COLOUR_SINGLE.load(Ordering::SeqCst))),
|
||||
stack: Colour::Rgb(Rgb::from(BORDER_COLOUR_STACK.load(Ordering::SeqCst))),
|
||||
monocle: Colour::Rgb(Rgb::from(BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst))),
|
||||
},
|
||||
active_window_border_style: *STYLE.lock(),
|
||||
border_offset: border_manager::BORDER_OFFSET.load(Ordering::SeqCst),
|
||||
border_width: border_manager::BORDER_WIDTH.load(Ordering::SeqCst),
|
||||
active_window_border_style: *ACTIVE_WINDOW_BORDER_STYLE.lock(),
|
||||
border_offset: BORDER_OFFSET.load(Ordering::SeqCst),
|
||||
border_width: BORDER_WIDTH.load(Ordering::SeqCst),
|
||||
stackbar_mode: *STACKBAR_MODE.lock(),
|
||||
stackbar_focused_text_colour: Colour::Rgb(Rgb::from(
|
||||
STACKBAR_FOCUSED_TEXT_COLOUR.load(Ordering::SeqCst),
|
||||
@@ -288,6 +286,28 @@ impl WindowManager {
|
||||
WindowsApi::load_workspace_information(&mut self.monitors)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn show_border(&self) -> Result<()> {
|
||||
if self.focused_container().is_ok() {
|
||||
let foreground = WindowsApi::foreground_window()?;
|
||||
let foreground_window = Window { hwnd: foreground };
|
||||
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
border.set_position(foreground_window, true)?;
|
||||
WindowsApi::invalidate_border_rect()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[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");
|
||||
@@ -1346,8 +1366,6 @@ impl WindowManager {
|
||||
anyhow!("could not remove container at given origin index")
|
||||
})?;
|
||||
|
||||
self.focused_workspace_mut()?.focus_previous_container();
|
||||
|
||||
// focus the target monitor
|
||||
self.focus_monitor(target_monitor_idx)?;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::collections::VecDeque;
|
||||
use std::convert::TryFrom;
|
||||
use std::ffi::c_void;
|
||||
use std::mem::size_of;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::eyre::Error;
|
||||
@@ -33,6 +34,7 @@ use windows::Win32::Graphics::Gdi::CreateSolidBrush;
|
||||
use windows::Win32::Graphics::Gdi::EnumDisplayDevicesW;
|
||||
use windows::Win32::Graphics::Gdi::EnumDisplayMonitors;
|
||||
use windows::Win32::Graphics::Gdi::GetMonitorInfoW;
|
||||
use windows::Win32::Graphics::Gdi::InvalidateRect;
|
||||
use windows::Win32::Graphics::Gdi::MonitorFromPoint;
|
||||
use windows::Win32::Graphics::Gdi::MonitorFromWindow;
|
||||
use windows::Win32::Graphics::Gdi::Rectangle;
|
||||
@@ -99,6 +101,7 @@ use windows::Win32::UI::WindowsAndMessaging::EDD_GET_DEVICE_INTERFACE_NAME;
|
||||
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_TOP;
|
||||
use windows::Win32::UI::WindowsAndMessaging::LWA_ALPHA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::LWA_COLORKEY;
|
||||
@@ -140,6 +143,8 @@ use crate::monitor::Monitor;
|
||||
use crate::ring::Ring;
|
||||
use crate::set_window_position::SetWindowPosition;
|
||||
use crate::windows_callbacks;
|
||||
use crate::BORDER_HWND;
|
||||
use crate::TRANSPARENCY_COLOUR;
|
||||
|
||||
pub enum WindowsResult<T, E> {
|
||||
Err(E),
|
||||
@@ -391,6 +396,36 @@ impl WindowsApi {
|
||||
Self::set_window_pos(hwnd, &Rect::default(), 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
|
||||
};
|
||||
|
||||
// TODO(raggi): This leaves the window behind the active window, which
|
||||
// can result e.g. single pixel window borders being invisible in the
|
||||
// case of opaque window borders (e.g. EPIC Games Launcher). Ideally
|
||||
// we'd be able to pass a parent window to place ourselves just in front
|
||||
// of, however the SetWindowPos API explicitly ignores that parameter
|
||||
// unless the window being positioned is being activated - and we don't
|
||||
// want to activate the border window here. We can hopefully find a
|
||||
// better workaround in the future.
|
||||
// The trade-off chosen prevents the border window from sitting over the
|
||||
// top of other pop-up dialogs such as a file picker dialog from
|
||||
// Firefox. When adjusting this in the future, it's important to check
|
||||
// those dialog cases.
|
||||
// TODO: Make the HWND_X flag configurable
|
||||
Self::set_window_pos(hwnd, layout, HWND_BOTTOM, 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_border_pos(hwnd: HWND, layout: &Rect, position: HWND) -> Result<()> {
|
||||
let flags = { SetWindowPosition::SHOW_WINDOW | SetWindowPosition::NO_ACTIVATE };
|
||||
Self::set_window_pos(hwnd, layout, position, flags.bits())
|
||||
@@ -942,7 +977,7 @@ impl WindowsApi {
|
||||
None,
|
||||
);
|
||||
|
||||
SetLayeredWindowAttributes(hwnd, COLORREF(0), 0, LWA_COLORKEY)?;
|
||||
SetLayeredWindowAttributes(hwnd, COLORREF(TRANSPARENCY_COLOUR), 0, LWA_COLORKEY)?;
|
||||
|
||||
hwnd
|
||||
}
|
||||
@@ -979,6 +1014,12 @@ impl WindowsApi {
|
||||
.process()
|
||||
}
|
||||
|
||||
pub fn invalidate_border_rect() -> Result<()> {
|
||||
unsafe { InvalidateRect(HWND(BORDER_HWND.load(Ordering::SeqCst)), None, false) }
|
||||
.ok()
|
||||
.process()
|
||||
}
|
||||
|
||||
pub fn alt_is_pressed() -> bool {
|
||||
let state = unsafe { GetKeyState(i32::from(VK_MENU.0)) };
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
|
||||
@@ -1,21 +1,36 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::atomic::Ordering;
|
||||
use widestring::U16CStr;
|
||||
|
||||
use windows::Win32::Foundation::BOOL;
|
||||
use windows::Win32::Foundation::COLORREF;
|
||||
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::BeginPaint;
|
||||
use windows::Win32::Graphics::Gdi::CreatePen;
|
||||
use windows::Win32::Graphics::Gdi::EndPaint;
|
||||
use windows::Win32::Graphics::Gdi::Rectangle;
|
||||
use windows::Win32::Graphics::Gdi::RoundRect;
|
||||
use windows::Win32::Graphics::Gdi::SelectObject;
|
||||
use windows::Win32::Graphics::Gdi::ValidateRect;
|
||||
use windows::Win32::Graphics::Gdi::HDC;
|
||||
use windows::Win32::Graphics::Gdi::HMONITOR;
|
||||
use windows::Win32::Graphics::Gdi::PAINTSTRUCT;
|
||||
use windows::Win32::Graphics::Gdi::PS_INSIDEFRAME;
|
||||
use windows::Win32::Graphics::Gdi::PS_SOLID;
|
||||
use windows::Win32::UI::Accessibility::HWINEVENTHOOK;
|
||||
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage;
|
||||
use windows::Win32::UI::WindowsAndMessaging::DBT_DEVNODES_CHANGED;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SPI_ICONVERTICALSPACING;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SPI_SETWORKAREA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WM_DEVICECHANGE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WM_DISPLAYCHANGE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WM_PAINT;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WM_SETTINGCHANGE;
|
||||
|
||||
use crate::container::Container;
|
||||
@@ -27,8 +42,15 @@ use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::winevent::WinEvent;
|
||||
use crate::winevent_listener;
|
||||
use crate::ACTIVE_WINDOW_BORDER_STYLE;
|
||||
use crate::BORDER_COLOUR_CURRENT;
|
||||
use crate::BORDER_RECT;
|
||||
use crate::BORDER_WIDTH;
|
||||
use crate::DISPLAY_INDEX_PREFERENCES;
|
||||
use crate::MONITOR_INDEX_PREFERENCES;
|
||||
use crate::TRANSPARENCY_COLOUR;
|
||||
use crate::WINDOWS_11;
|
||||
use komorebi_core::ActiveWindowBorderStyle;
|
||||
|
||||
pub extern "system" fn valid_display_monitors(
|
||||
hmonitor: HMONITOR,
|
||||
@@ -182,6 +204,62 @@ pub extern "system" fn win_event_hook(
|
||||
.expect("could not send message on winevent_listener::event_tx");
|
||||
}
|
||||
|
||||
pub extern "system" fn border_window(
|
||||
window: HWND,
|
||||
message: u32,
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
) -> LRESULT {
|
||||
unsafe {
|
||||
match message {
|
||||
WM_PAINT => {
|
||||
let border_rect = *BORDER_RECT.lock();
|
||||
let mut ps = PAINTSTRUCT::default();
|
||||
let hdc = BeginPaint(window, &mut ps);
|
||||
let hpen = CreatePen(
|
||||
PS_SOLID | PS_INSIDEFRAME,
|
||||
BORDER_WIDTH.load(Ordering::SeqCst),
|
||||
COLORREF(BORDER_COLOUR_CURRENT.load(Ordering::SeqCst)),
|
||||
);
|
||||
let hbrush = WindowsApi::create_solid_brush(TRANSPARENCY_COLOUR);
|
||||
|
||||
SelectObject(hdc, hpen);
|
||||
SelectObject(hdc, hbrush);
|
||||
// TODO(raggi): this is approximately the correct curvature for
|
||||
// the top left of a Windows 11 window (DWMWCP_DEFAULT), but
|
||||
// often the bottom right has a different shape. Furthermore if
|
||||
// the window was made with DWMWCP_ROUNDSMALL then this is the
|
||||
// wrong size. In the future we should read the DWM properties
|
||||
// of windows and attempt to match appropriately.
|
||||
match *ACTIVE_WINDOW_BORDER_STYLE.lock() {
|
||||
ActiveWindowBorderStyle::System => {
|
||||
if *WINDOWS_11 {
|
||||
RoundRect(hdc, 0, 0, border_rect.right, border_rect.bottom, 20, 20);
|
||||
} else {
|
||||
Rectangle(hdc, 0, 0, border_rect.right, border_rect.bottom);
|
||||
}
|
||||
}
|
||||
ActiveWindowBorderStyle::Rounded => {
|
||||
RoundRect(hdc, 0, 0, border_rect.right, border_rect.bottom, 20, 20);
|
||||
}
|
||||
ActiveWindowBorderStyle::Square => {
|
||||
Rectangle(hdc, 0, 0, border_rect.right, border_rect.bottom);
|
||||
}
|
||||
}
|
||||
EndPaint(window, &ps);
|
||||
ValidateRect(window, None);
|
||||
|
||||
LRESULT(0)
|
||||
}
|
||||
WM_DESTROY => {
|
||||
PostQuitMessage(0);
|
||||
LRESULT(0)
|
||||
}
|
||||
_ => DefWindowProcW(window, message, wparam, lparam),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub extern "system" fn hidden_window(
|
||||
window: HWND,
|
||||
message: u32,
|
||||
|
||||
@@ -20,14 +20,14 @@ use komorebi_core::Layout;
|
||||
use komorebi_core::OperationDirection;
|
||||
use komorebi_core::Rect;
|
||||
|
||||
use crate::border_manager::BORDER_OFFSET;
|
||||
use crate::border_manager::BORDER_WIDTH;
|
||||
use crate::container::Container;
|
||||
use crate::ring::Ring;
|
||||
use crate::static_config::WorkspaceConfig;
|
||||
use crate::window::Window;
|
||||
use crate::window::WindowDetails;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::BORDER_OFFSET;
|
||||
use crate::BORDER_WIDTH;
|
||||
use crate::DEFAULT_CONTAINER_PADDING;
|
||||
use crate::DEFAULT_WORKSPACE_PADDING;
|
||||
use crate::INITIAL_CONFIGURATION_LOADED;
|
||||
@@ -1376,7 +1376,7 @@ impl Workspace {
|
||||
vec
|
||||
}
|
||||
|
||||
pub fn focus_previous_container(&mut self) {
|
||||
fn focus_previous_container(&mut self) {
|
||||
let focused_idx = self.focused_container_idx();
|
||||
|
||||
if focused_idx != 0 {
|
||||
|
||||
Reference in New Issue
Block a user