mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-03-31 14:33:29 +02:00
feat(transparency): add transparency manager module
This commit adds the transparency manager module, which, when enabled, will make unfocused windows transparent using a user-configurable alpha value between 0-255. The corresponding komorebic commands (transparency, transparency-alpha) have been added, as well as the corresponding static configuration values (transparency, transparency_alpha). This feature is off-by-default and must be explicitly enabled by the user. If the process is not shut down cleanly via the 'komorebic stop' command, it is possible that the user will be left with transparent windows which will not be managed by komorebi the next time it launches. This is because the WS_EX_LAYERED style is required for transparency, but is ignored by default in komorebi's window eligibility heuristics. For this reason, a separate state tracker of windows that have had this style added by the window manager is kept in the transparency manager module. For this edge case of shutdowns where the cleanup logic cannot be run, the 'komorebic restore-windows' command has been updated to remove transparency from all windows that were known to the window manager during the last session before it was killed. This must be run _before_ restarting komorebi, so that the previous session's known window data is not overwritten. In the worst case scenario that the previous session's data is overwritten, the user will have to either kill and restart the applications, or compile komorebi from source and explicitly set "allow_layered" to "true" in the window_is_eligible function, before setting the transparency alpha to 255 (fully opaque), and then resetting to the desired value.
This commit is contained in:
@@ -142,6 +142,8 @@ pub enum SocketMessage {
|
||||
BorderStyle(BorderStyle),
|
||||
BorderWidth(i32),
|
||||
BorderOffset(i32),
|
||||
Transparency(bool),
|
||||
TransparencyAlpha(u8),
|
||||
InvisibleBorders(Rect),
|
||||
StackbarMode(StackbarMode),
|
||||
StackbarLabel(StackbarLabel),
|
||||
|
||||
@@ -18,7 +18,7 @@ clap = { version = "4", features = ["derive"] }
|
||||
color-eyre = { workspace = true }
|
||||
crossbeam-channel = "0.5"
|
||||
crossbeam-utils = "0.8"
|
||||
ctrlc = "3"
|
||||
ctrlc = { version = "3", features = ["termination"] }
|
||||
dirs = { workspace = true }
|
||||
getset = "0.1"
|
||||
hex_color = { version = "3", features = ["serde"] }
|
||||
|
||||
@@ -176,7 +176,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
borders.remove(id);
|
||||
}
|
||||
|
||||
continue 'receiver;
|
||||
continue 'monitors;
|
||||
}
|
||||
|
||||
// Handle the monocle container separately
|
||||
@@ -187,7 +187,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
if let Ok(border) = Border::create(monocle.id()) {
|
||||
entry.insert(border)
|
||||
} else {
|
||||
continue 'receiver;
|
||||
continue 'monitors;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -287,7 +287,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
if let Ok(border) = Border::create(c.id()) {
|
||||
entry.insert(border)
|
||||
} else {
|
||||
continue 'receiver;
|
||||
continue 'monitors;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -304,7 +304,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
|
||||
*Z_ORDER.lock() = restore_z_order;
|
||||
|
||||
continue 'receiver;
|
||||
continue 'monitors;
|
||||
}
|
||||
|
||||
// Get the border entry for this container from the map or create one
|
||||
@@ -314,7 +314,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
if let Ok(border) = Border::create(c.id()) {
|
||||
entry.insert(border)
|
||||
} else {
|
||||
continue 'receiver;
|
||||
continue 'monitors;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ pub mod set_window_position;
|
||||
pub mod stackbar_manager;
|
||||
pub mod static_config;
|
||||
pub mod styles;
|
||||
pub mod transparency_manager;
|
||||
pub mod window;
|
||||
pub mod window_manager;
|
||||
pub mod window_manager_event;
|
||||
|
||||
@@ -34,6 +34,7 @@ use komorebi::process_movement::listen_for_movements;
|
||||
use komorebi::reaper;
|
||||
use komorebi::stackbar_manager;
|
||||
use komorebi::static_config::StaticConfig;
|
||||
use komorebi::transparency_manager;
|
||||
use komorebi::window_manager::WindowManager;
|
||||
use komorebi::windows_api::WindowsApi;
|
||||
use komorebi::winevent_listener;
|
||||
@@ -258,6 +259,7 @@ fn main() -> Result<()> {
|
||||
|
||||
border_manager::listen_for_notifications(wm.clone());
|
||||
stackbar_manager::listen_for_notifications(wm.clone());
|
||||
transparency_manager::listen_for_notifications(wm.clone());
|
||||
workspace_reconciliator::listen_for_notifications(wm.clone());
|
||||
monitor_reconciliator::listen_for_notifications(wm.clone())?;
|
||||
reaper::watch_for_orphans(wm.clone());
|
||||
|
||||
@@ -45,6 +45,7 @@ use crate::current_virtual_desktop;
|
||||
use crate::notify_subscribers;
|
||||
use crate::stackbar_manager;
|
||||
use crate::static_config::StaticConfig;
|
||||
use crate::transparency_manager;
|
||||
use crate::window::RuleDebug;
|
||||
use crate::window::Window;
|
||||
use crate::window_manager;
|
||||
@@ -1256,6 +1257,12 @@ impl WindowManager {
|
||||
SocketMessage::BorderOffset(offset) => {
|
||||
border_manager::BORDER_OFFSET.store(offset, Ordering::SeqCst);
|
||||
}
|
||||
SocketMessage::Transparency(enable) => {
|
||||
transparency_manager::TRANSPARENCY_ENABLED.store(enable, Ordering::SeqCst);
|
||||
}
|
||||
SocketMessage::TransparencyAlpha(alpha) => {
|
||||
transparency_manager::TRANSPARENCY_ALPHA.store(alpha, Ordering::SeqCst);
|
||||
}
|
||||
SocketMessage::StackbarMode(mode) => {
|
||||
STACKBAR_MODE.store(mode);
|
||||
}
|
||||
@@ -1347,6 +1354,7 @@ impl WindowManager {
|
||||
|
||||
notify_subscribers(&serde_json::to_string(¬ification)?)?;
|
||||
border_manager::event_tx().send(border_manager::Notification)?;
|
||||
transparency_manager::event_tx().send(transparency_manager::Notification)?;
|
||||
stackbar_manager::event_tx().send(stackbar_manager::Notification)?;
|
||||
|
||||
tracing::info!("processed");
|
||||
|
||||
@@ -19,6 +19,7 @@ use crate::border_manager::BORDER_WIDTH;
|
||||
use crate::current_virtual_desktop;
|
||||
use crate::notify_subscribers;
|
||||
use crate::stackbar_manager;
|
||||
use crate::transparency_manager;
|
||||
use crate::window::should_act;
|
||||
use crate::window::RuleDebug;
|
||||
use crate::window_manager::WindowManager;
|
||||
@@ -609,6 +610,7 @@ impl WindowManager {
|
||||
|
||||
notify_subscribers(&serde_json::to_string(¬ification)?)?;
|
||||
border_manager::event_tx().send(border_manager::Notification)?;
|
||||
transparency_manager::event_tx().send(transparency_manager::Notification)?;
|
||||
stackbar_manager::event_tx().send(stackbar_manager::Notification)?;
|
||||
|
||||
// Too many spammy OBJECT_NAMECHANGE events from JetBrains IDEs
|
||||
|
||||
@@ -14,6 +14,7 @@ use crate::stackbar_manager::STACKBAR_TAB_BACKGROUND_COLOUR;
|
||||
use crate::stackbar_manager::STACKBAR_TAB_HEIGHT;
|
||||
use crate::stackbar_manager::STACKBAR_TAB_WIDTH;
|
||||
use crate::stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
||||
use crate::transparency_manager;
|
||||
use crate::window_manager::WindowManager;
|
||||
use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
@@ -278,6 +279,12 @@ pub struct StaticConfig {
|
||||
/// Active window border z-order (default: System)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub border_z_order: Option<ZOrder>,
|
||||
/// Add transparency to unfocused windows (default: false)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub transparency: Option<bool>,
|
||||
/// Alpha value for unfocused window transparency [[0-255]] (default: 200)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub transparency_alpha: Option<u8>,
|
||||
/// Global default workspace padding (default: 10)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub default_workspace_padding: Option<i32>,
|
||||
@@ -468,6 +475,12 @@ impl From<&WindowManager> for StaticConfig {
|
||||
border_offset: Option::from(border_manager::BORDER_OFFSET.load(Ordering::SeqCst)),
|
||||
border: Option::from(border_manager::BORDER_ENABLED.load(Ordering::SeqCst)),
|
||||
border_colours,
|
||||
transparency: Option::from(
|
||||
transparency_manager::TRANSPARENCY_ENABLED.load(Ordering::SeqCst),
|
||||
),
|
||||
transparency_alpha: Option::from(
|
||||
transparency_manager::TRANSPARENCY_ALPHA.load(Ordering::SeqCst),
|
||||
),
|
||||
border_style: Option::from(*STYLE.lock()),
|
||||
border_z_order: Option::from(*Z_ORDER.lock()),
|
||||
default_workspace_padding: Option::from(
|
||||
@@ -546,6 +559,11 @@ impl StaticConfig {
|
||||
let border_style = self.border_style.unwrap_or_default();
|
||||
*STYLE.lock() = border_style;
|
||||
|
||||
transparency_manager::TRANSPARENCY_ENABLED
|
||||
.store(self.transparency.unwrap_or(false), Ordering::SeqCst);
|
||||
transparency_manager::TRANSPARENCY_ALPHA
|
||||
.store(self.transparency_alpha.unwrap_or(200), Ordering::SeqCst);
|
||||
|
||||
let mut float_identifiers = FLOAT_IDENTIFIERS.lock();
|
||||
let mut regex_identifiers = REGEX_IDENTIFIERS.lock();
|
||||
let mut manage_identifiers = MANAGE_IDENTIFIERS.lock();
|
||||
|
||||
155
komorebi/src/transparency_manager.rs
Normal file
155
komorebi/src/transparency_manager.rs
Normal file
@@ -0,0 +1,155 @@
|
||||
#![deny(clippy::unwrap_used, clippy::expect_used)]
|
||||
|
||||
use crossbeam_channel::Receiver;
|
||||
use crossbeam_channel::Sender;
|
||||
use crossbeam_utils::atomic::AtomicConsume;
|
||||
use parking_lot::Mutex;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::AtomicU8;
|
||||
use std::sync::Arc;
|
||||
use std::sync::OnceLock;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
|
||||
use crate::Window;
|
||||
use crate::WindowManager;
|
||||
use crate::WindowsApi;
|
||||
|
||||
pub static TRANSPARENCY_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||
pub static TRANSPARENCY_ALPHA: AtomicU8 = AtomicU8::new(200);
|
||||
|
||||
static KNOWN_HWNDS: OnceLock<Mutex<Vec<isize>>> = OnceLock::new();
|
||||
|
||||
pub struct Notification;
|
||||
|
||||
static CHANNEL: OnceLock<(Sender<Notification>, Receiver<Notification>)> = OnceLock::new();
|
||||
|
||||
pub fn known_hwnds() -> Vec<isize> {
|
||||
let known = KNOWN_HWNDS.get_or_init(|| Mutex::new(Vec::new())).lock();
|
||||
known.iter().copied().collect()
|
||||
}
|
||||
|
||||
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>>) {
|
||||
std::thread::spawn(move || loop {
|
||||
match handle_notifications(wm.clone()) {
|
||||
Ok(()) => {
|
||||
tracing::warn!("restarting finished thread");
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::warn!("restarting failed thread: {}", error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result<()> {
|
||||
tracing::info!("listening");
|
||||
|
||||
let receiver = event_rx();
|
||||
event_tx().send(Notification)?;
|
||||
|
||||
'receiver: for _ in receiver {
|
||||
let known_hwnds = KNOWN_HWNDS.get_or_init(|| Mutex::new(Vec::new()));
|
||||
if !TRANSPARENCY_ENABLED.load_consume() {
|
||||
for hwnd in known_hwnds.lock().iter() {
|
||||
Window::from(*hwnd).opaque()?;
|
||||
}
|
||||
|
||||
continue 'receiver;
|
||||
}
|
||||
|
||||
known_hwnds.lock().clear();
|
||||
|
||||
// Check the wm state every time we receive a notification
|
||||
let state = wm.lock();
|
||||
|
||||
let focused_monitor_idx = state.focused_monitor_idx();
|
||||
|
||||
'monitors: for (monitor_idx, m) in state.monitors.elements().iter().enumerate() {
|
||||
let focused_workspace_idx = m.focused_workspace_idx();
|
||||
|
||||
'workspaces: for (workspace_idx, ws) in m.workspaces().iter().enumerate() {
|
||||
// Only operate on the focused workspace of each monitor
|
||||
// Workspaces with tiling disabled don't have transparent windows
|
||||
if !ws.tile() || workspace_idx != focused_workspace_idx {
|
||||
for window in ws.visible_windows().iter().flatten() {
|
||||
window.opaque()?;
|
||||
}
|
||||
|
||||
continue 'workspaces;
|
||||
}
|
||||
|
||||
// Monocle container is never transparent
|
||||
if let Some(monocle) = ws.monocle_container() {
|
||||
if let Some(window) = monocle.focused_window() {
|
||||
window.opaque()?;
|
||||
}
|
||||
|
||||
continue 'monitors;
|
||||
}
|
||||
|
||||
let foreground_hwnd = WindowsApi::foreground_window().unwrap_or_default();
|
||||
let is_maximized = WindowsApi::is_zoomed(HWND(foreground_hwnd));
|
||||
|
||||
if is_maximized {
|
||||
Window {
|
||||
hwnd: foreground_hwnd,
|
||||
}
|
||||
.opaque()?;
|
||||
continue 'monitors;
|
||||
}
|
||||
|
||||
for (idx, c) in ws.containers().iter().enumerate() {
|
||||
// Update the transparency for all containers on this workspace
|
||||
|
||||
// If the window is not focused on the current workspace, or isn't on the focused monitor
|
||||
// make it transparent
|
||||
if idx != ws.focused_container_idx() || monitor_idx != focused_monitor_idx {
|
||||
let unfocused_window = c.focused_window().copied().unwrap_or_default();
|
||||
unfocused_window.transparent()?;
|
||||
|
||||
known_hwnds.lock().push(unfocused_window.hwnd);
|
||||
// Otherwise, make it opaque
|
||||
} else {
|
||||
c.focused_window().copied().unwrap_or_default().opaque()?;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub enum ZOrder {
|
||||
Top,
|
||||
NoTopMost,
|
||||
Bottom,
|
||||
TopMost,
|
||||
}
|
||||
|
||||
impl From<ZOrder> for isize {
|
||||
fn from(val: ZOrder) -> Self {
|
||||
match val {
|
||||
ZOrder::Top => 0,
|
||||
ZOrder::NoTopMost => -2,
|
||||
ZOrder::Bottom => 1,
|
||||
ZOrder::TopMost => -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ use std::time::Duration;
|
||||
|
||||
use color_eyre::eyre;
|
||||
use color_eyre::Result;
|
||||
use crossbeam_utils::atomic::AtomicConsume;
|
||||
use komorebi_core::config_generation::IdWithIdentifier;
|
||||
use komorebi_core::config_generation::MatchingRule;
|
||||
use komorebi_core::config_generation::MatchingStrategy;
|
||||
@@ -25,6 +26,7 @@ use komorebi_core::Rect;
|
||||
|
||||
use crate::styles::ExtendedWindowStyle;
|
||||
use crate::styles::WindowStyle;
|
||||
use crate::transparency_manager;
|
||||
use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::FLOAT_IDENTIFIERS;
|
||||
@@ -42,6 +44,12 @@ pub struct Window {
|
||||
pub hwnd: isize,
|
||||
}
|
||||
|
||||
impl From<isize> for Window {
|
||||
fn from(value: isize) -> Self {
|
||||
Self { hwnd: value }
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
#[derive(Debug, Clone, Serialize, JsonSchema)]
|
||||
pub struct WindowDetails {
|
||||
@@ -249,7 +257,10 @@ impl Window {
|
||||
let mut ex_style = self.ex_style()?;
|
||||
ex_style.insert(ExtendedWindowStyle::LAYERED);
|
||||
self.update_ex_style(&ex_style)?;
|
||||
WindowsApi::set_transparent(self.hwnd())
|
||||
WindowsApi::set_transparent(
|
||||
self.hwnd(),
|
||||
transparency_manager::TRANSPARENCY_ALPHA.load_consume(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn opaque(self) -> Result<()> {
|
||||
@@ -374,7 +385,7 @@ impl Window {
|
||||
if let (Ok(style), Ok(ex_style)) = (&self.style(), &self.ex_style()) {
|
||||
debug.window_style = Some(*style);
|
||||
debug.extended_window_style = Some(*ex_style);
|
||||
let eligible = window_is_eligible(&title, &exe_name, &class, &path, style, ex_style, event, debug);
|
||||
let eligible = window_is_eligible(self.hwnd, &title, &exe_name, &class, &path, style, ex_style, event, debug);
|
||||
debug.should_manage = eligible;
|
||||
return Ok(eligible);
|
||||
}
|
||||
@@ -394,6 +405,7 @@ pub struct RuleDebug {
|
||||
pub has_title: bool,
|
||||
pub is_cloaked: bool,
|
||||
pub allow_cloaked: bool,
|
||||
pub allow_layered_transparency: bool,
|
||||
pub window_style: Option<WindowStyle>,
|
||||
pub extended_window_style: Option<ExtendedWindowStyle>,
|
||||
pub title: Option<String>,
|
||||
@@ -410,6 +422,7 @@ pub struct RuleDebug {
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn window_is_eligible(
|
||||
hwnd: isize,
|
||||
title: &String,
|
||||
exe_name: &String,
|
||||
class: &String,
|
||||
@@ -464,7 +477,7 @@ fn window_is_eligible(
|
||||
}
|
||||
|
||||
let layered_whitelist = LAYERED_WHITELIST.lock();
|
||||
let allow_layered = if let Some(rule) = should_act(
|
||||
let mut allow_layered = if let Some(rule) = should_act(
|
||||
title,
|
||||
exe_name,
|
||||
class,
|
||||
@@ -478,8 +491,14 @@ fn window_is_eligible(
|
||||
false
|
||||
};
|
||||
|
||||
// TODO: might need this for transparency
|
||||
// let allow_layered = true;
|
||||
let known_layered_hwnds = transparency_manager::known_hwnds();
|
||||
|
||||
allow_layered = if known_layered_hwnds.contains(&hwnd) {
|
||||
debug.allow_layered_transparency = true;
|
||||
true
|
||||
} else {
|
||||
allow_layered
|
||||
};
|
||||
|
||||
let allow_wsl2_gui = {
|
||||
let wsl2_ui_processes = WSL2_UI_PROCESSES.lock();
|
||||
|
||||
@@ -55,6 +55,7 @@ use crate::stackbar_manager::STACKBAR_TAB_HEIGHT;
|
||||
use crate::stackbar_manager::STACKBAR_TAB_WIDTH;
|
||||
use crate::stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
||||
use crate::static_config::StaticConfig;
|
||||
use crate::transparency_manager;
|
||||
use crate::window::Window;
|
||||
use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
@@ -907,6 +908,7 @@ impl WindowManager {
|
||||
tracing::info!("restoring all hidden windows");
|
||||
|
||||
let no_titlebar = NO_TITLEBAR.lock();
|
||||
let known_transparent_hwnds = transparency_manager::known_hwnds();
|
||||
|
||||
for monitor in self.monitors_mut() {
|
||||
for workspace in monitor.workspaces_mut() {
|
||||
@@ -916,6 +918,10 @@ impl WindowManager {
|
||||
window.add_title_bar()?;
|
||||
}
|
||||
|
||||
if known_transparent_hwnds.contains(&window.hwnd) {
|
||||
window.opaque()?;
|
||||
}
|
||||
|
||||
window.restore();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -979,11 +979,10 @@ impl WindowsApi {
|
||||
.process()
|
||||
}
|
||||
|
||||
pub fn set_transparent(hwnd: HWND) -> Result<()> {
|
||||
pub fn set_transparent(hwnd: HWND, alpha: u8) -> Result<()> {
|
||||
unsafe {
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
// TODO: alpha should be configurable
|
||||
SetLayeredWindowAttributes(hwnd, COLORREF(-1i32 as u32), 150, LWA_ALPHA)?;
|
||||
SetLayeredWindowAttributes(hwnd, COLORREF(-1i32 as u32), alpha, LWA_ALPHA)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -649,6 +649,18 @@ struct Border {
|
||||
boolean_state: BooleanState,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Transparency {
|
||||
#[clap(value_enum)]
|
||||
boolean_state: BooleanState,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct TransparencyAlpha {
|
||||
/// Alpha
|
||||
alpha: u8,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct BorderColour {
|
||||
#[clap(value_enum, short, long, default_value = "single")]
|
||||
@@ -1161,6 +1173,12 @@ enum SubCommand {
|
||||
#[clap(arg_required_else_help = true)]
|
||||
#[clap(alias = "active-window-border-offset")]
|
||||
BorderOffset(BorderOffset),
|
||||
/// Enable or disable transparency for unfocused windows
|
||||
#[clap(arg_required_else_help = true)]
|
||||
Transparency(Transparency),
|
||||
/// Set the alpha value for unfocused window transparency
|
||||
#[clap(arg_required_else_help = true)]
|
||||
TransparencyAlpha(TransparencyAlpha),
|
||||
/// Enable or disable focus follows mouse for the operating system
|
||||
#[clap(arg_required_else_help = true)]
|
||||
FocusFollowsMouse(FocusFollowsMouse),
|
||||
@@ -2241,6 +2259,12 @@ Stop-Process -Name:komorebi -ErrorAction SilentlyContinue
|
||||
SubCommand::BorderOffset(arg) => {
|
||||
send_message(&SocketMessage::BorderOffset(arg.offset).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::Transparency(arg) => {
|
||||
send_message(&SocketMessage::Transparency(arg.boolean_state.into()).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::TransparencyAlpha(arg) => {
|
||||
send_message(&SocketMessage::TransparencyAlpha(arg.alpha).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::ResizeDelta(arg) => {
|
||||
send_message(&SocketMessage::ResizeDelta(arg.pixels).as_bytes()?)?;
|
||||
}
|
||||
@@ -2383,6 +2407,11 @@ fn show_window(hwnd: HWND, command: SHOW_WINDOW_CMD) {
|
||||
unsafe { ShowWindow(hwnd, command) };
|
||||
}
|
||||
|
||||
fn remove_transparency(hwnd: HWND) {
|
||||
let _ = komorebi_client::Window::from(hwnd.0).opaque();
|
||||
}
|
||||
|
||||
fn restore_window(hwnd: HWND) {
|
||||
show_window(hwnd, SW_RESTORE);
|
||||
remove_transparency(hwnd);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user