mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-03-22 01:19:21 +01:00
feat(wm): reintroduce remove-title-bar cmds
This commit reintroduces some old code from the feature/remove-titlebars branch. This feature is very unstable and it is strongly advised that nobody actually uses it. Wherever possible, please use the "remove titlebar" functionality provided directly within an application.
This commit is contained in:
@@ -143,6 +143,8 @@ pub enum SocketMessage {
|
||||
ToggleFocusFollowsMouse(FocusFollowsMouseImplementation),
|
||||
MouseFollowsFocus(bool),
|
||||
ToggleMouseFollowsFocus,
|
||||
RemoveTitleBar(ApplicationIdentifier, String),
|
||||
ToggleTitleBars,
|
||||
AddSubscriber(String),
|
||||
RemoveSubscriber(String),
|
||||
NotificationSchema,
|
||||
|
||||
@@ -152,6 +152,10 @@ lazy_static! {
|
||||
|
||||
static ref BORDER_OFFSET: Arc<Mutex<Option<Rect>>> =
|
||||
Arc::new(Mutex::new(None));
|
||||
|
||||
// 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![]));
|
||||
}
|
||||
|
||||
pub static INITIAL_CONFIGURATION_LOADED: AtomicBool = AtomicBool::new(false);
|
||||
@@ -168,6 +172,7 @@ pub static BORDER_COLOUR_CURRENT: AtomicU32 = AtomicU32::new(0);
|
||||
pub static BORDER_WIDTH: AtomicI32 = AtomicI32::new(20);
|
||||
// 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);
|
||||
|
||||
@@ -511,7 +516,7 @@ fn main() -> Result<()> {
|
||||
|
||||
tracing::error!("received ctrl-c, restoring all hidden windows and terminating process");
|
||||
|
||||
wm.lock().restore_all_windows();
|
||||
wm.lock().restore_all_windows()?;
|
||||
|
||||
if WindowsApi::focus_follows_mouse()? {
|
||||
WindowsApi::disable_focus_follows_mouse()?;
|
||||
|
||||
@@ -61,7 +61,9 @@ use crate::INITIAL_CONFIGURATION_LOADED;
|
||||
use crate::LAYERED_WHITELIST;
|
||||
use crate::MANAGE_IDENTIFIERS;
|
||||
use crate::MONITOR_INDEX_PREFERENCES;
|
||||
use crate::NO_TITLEBAR;
|
||||
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
|
||||
use crate::REMOVE_TITLEBARS;
|
||||
use crate::SUBSCRIPTION_PIPES;
|
||||
use crate::TCP_CONNECTIONS;
|
||||
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
||||
@@ -575,7 +577,7 @@ impl WindowManager {
|
||||
tracing::info!(
|
||||
"received stop command, restoring all hidden windows and terminating process"
|
||||
);
|
||||
self.restore_all_windows();
|
||||
self.restore_all_windows()?;
|
||||
|
||||
if WindowsApi::focus_follows_mouse()? {
|
||||
WindowsApi::disable_focus_follows_mouse()?;
|
||||
@@ -1076,6 +1078,17 @@ impl WindowManager {
|
||||
let mut stream = UnixStream::connect(socket)?;
|
||||
stream.write_all(schema.as_bytes())?;
|
||||
}
|
||||
SocketMessage::RemoveTitleBar(_, ref id) => {
|
||||
let mut identifiers = NO_TITLEBAR.lock();
|
||||
if !identifiers.contains(id) {
|
||||
identifiers.push(id.clone());
|
||||
}
|
||||
}
|
||||
SocketMessage::ToggleTitleBars => {
|
||||
let current = REMOVE_TITLEBARS.load(Ordering::SeqCst);
|
||||
REMOVE_TITLEBARS.store(!current, Ordering::SeqCst);
|
||||
self.update_focused_workspace(false)?;
|
||||
}
|
||||
};
|
||||
|
||||
match message {
|
||||
|
||||
@@ -32,6 +32,7 @@ use crate::HIDDEN_HWNDS;
|
||||
use crate::HIDING_BEHAVIOUR;
|
||||
use crate::LAYERED_WHITELIST;
|
||||
use crate::MANAGE_IDENTIFIERS;
|
||||
use crate::NO_TITLEBAR;
|
||||
use crate::WSL2_UI_PROCESSES;
|
||||
|
||||
#[derive(Debug, Clone, Copy, JsonSchema)]
|
||||
@@ -346,7 +347,7 @@ impl Window {
|
||||
pub fn transparent(self) -> Result<()> {
|
||||
let mut ex_style = self.ex_style()?;
|
||||
ex_style.insert(ExtendedWindowStyle::LAYERED);
|
||||
self.update_ex_style(ex_style)?;
|
||||
self.update_ex_style(&ex_style)?;
|
||||
WindowsApi::set_transparent(self.hwnd());
|
||||
Ok(())
|
||||
}
|
||||
@@ -354,15 +355,15 @@ impl Window {
|
||||
pub fn opaque(self) -> Result<()> {
|
||||
let mut ex_style = self.ex_style()?;
|
||||
ex_style.remove(ExtendedWindowStyle::LAYERED);
|
||||
self.update_ex_style(ex_style)
|
||||
self.update_ex_style(&ex_style)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn update_style(self, style: WindowStyle) -> Result<()> {
|
||||
pub fn update_style(self, style: &WindowStyle) -> Result<()> {
|
||||
WindowsApi::update_style(self.hwnd(), isize::try_from(style.bits())?)
|
||||
}
|
||||
|
||||
pub fn update_ex_style(self, style: ExtendedWindowStyle) -> Result<()> {
|
||||
pub fn update_ex_style(self, style: &ExtendedWindowStyle) -> Result<()> {
|
||||
WindowsApi::update_ex_style(self.hwnd(), isize::try_from(style.bits())?)
|
||||
}
|
||||
|
||||
@@ -397,6 +398,20 @@ impl Window {
|
||||
WindowsApi::is_window(self.hwnd())
|
||||
}
|
||||
|
||||
pub fn remove_title_bar(self) -> Result<()> {
|
||||
let mut style = self.style()?;
|
||||
style.remove(WindowStyle::CAPTION);
|
||||
style.remove(WindowStyle::THICKFRAME);
|
||||
self.update_style(&style)
|
||||
}
|
||||
|
||||
pub fn add_title_bar(self) -> Result<()> {
|
||||
let mut style = self.style()?;
|
||||
style.insert(WindowStyle::CAPTION);
|
||||
style.insert(WindowStyle::THICKFRAME);
|
||||
self.update_style(&style)
|
||||
}
|
||||
|
||||
#[tracing::instrument(fields(exe, title))]
|
||||
pub fn should_manage(self, event: Option<WindowManagerEvent>) -> Result<bool> {
|
||||
if let Some(WindowManagerEvent::DisplayChange(_)) = event {
|
||||
@@ -427,7 +442,7 @@ impl Window {
|
||||
// If not allowing cloaked windows, we need to ensure the window is not cloaked
|
||||
(false, false) => {
|
||||
if let (Ok(title), Ok(exe_name), Ok(class)) = (self.title(), self.exe(), self.class()) {
|
||||
return Ok(window_is_eligible(&title, &exe_name, &class, self.style()?, self.ex_style()?, event));
|
||||
return Ok(window_is_eligible(&title, &exe_name, &class, &self.style()?, &self.ex_style()?, event));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -441,8 +456,8 @@ fn window_is_eligible(
|
||||
title: &String,
|
||||
exe_name: &String,
|
||||
class: &String,
|
||||
style: WindowStyle,
|
||||
ex_style: ExtendedWindowStyle,
|
||||
style: &WindowStyle,
|
||||
ex_style: &ExtendedWindowStyle,
|
||||
event: Option<WindowManagerEvent>,
|
||||
) -> bool {
|
||||
let mut should_float = false;
|
||||
@@ -503,7 +518,12 @@ fn window_is_eligible(
|
||||
wsl2_ui_processes.contains(exe_name)
|
||||
};
|
||||
|
||||
if (allow_wsl2_gui || style.contains(WindowStyle::CAPTION) && ex_style.contains(ExtendedWindowStyle::WINDOWEDGE))
|
||||
let allow_titlebar_removed = {
|
||||
let titlebars_removed = NO_TITLEBAR.lock();
|
||||
titlebars_removed.contains(exe_name)
|
||||
};
|
||||
|
||||
if (allow_wsl2_gui || allow_titlebar_removed || style.contains(WindowStyle::CAPTION) && ex_style.contains(ExtendedWindowStyle::WINDOWEDGE))
|
||||
&& !ex_style.contains(ExtendedWindowStyle::DLGMODALFRAME)
|
||||
// Get a lot of dupe events coming through that make the redrawing go crazy
|
||||
// on FocusChange events if I don't filter out this one. But, if we are
|
||||
|
||||
@@ -49,7 +49,9 @@ use crate::FLOAT_IDENTIFIERS;
|
||||
use crate::HOME_DIR;
|
||||
use crate::LAYERED_WHITELIST;
|
||||
use crate::MANAGE_IDENTIFIERS;
|
||||
use crate::NO_TITLEBAR;
|
||||
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
|
||||
use crate::REMOVE_TITLEBARS;
|
||||
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
||||
use crate::WORKSPACE_RULES;
|
||||
|
||||
@@ -75,6 +77,7 @@ pub struct WindowManager {
|
||||
pub already_moved_window_handles: Arc<Mutex<HashSet<isize>>>,
|
||||
}
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Debug, Serialize, JsonSchema)]
|
||||
pub struct State {
|
||||
pub monitors: Ring<Monitor>,
|
||||
@@ -87,6 +90,7 @@ pub struct State {
|
||||
pub focus_follows_mouse: Option<FocusFollowsMouseImplementation>,
|
||||
pub mouse_follows_focus: bool,
|
||||
pub has_pending_raise_op: bool,
|
||||
pub remove_titlebars: bool,
|
||||
pub float_identifiers: Vec<String>,
|
||||
pub manage_identifiers: Vec<String>,
|
||||
pub layered_whitelist: Vec<String>,
|
||||
@@ -114,6 +118,7 @@ impl From<&WindowManager> for State {
|
||||
focus_follows_mouse: wm.focus_follows_mouse,
|
||||
mouse_follows_focus: wm.mouse_follows_focus,
|
||||
has_pending_raise_op: wm.has_pending_raise_op,
|
||||
remove_titlebars: REMOVE_TITLEBARS.load(Ordering::SeqCst),
|
||||
float_identifiers: FLOAT_IDENTIFIERS.lock().clone(),
|
||||
manage_identifiers: MANAGE_IDENTIFIERS.lock().clone(),
|
||||
layered_whitelist: LAYERED_WHITELIST.lock().clone(),
|
||||
@@ -480,6 +485,7 @@ impl WindowManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[tracing::instrument(skip(self))]
|
||||
fn add_window_handle_to_move_based_on_workspace_rule(
|
||||
&self,
|
||||
@@ -954,18 +960,26 @@ impl WindowManager {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn restore_all_windows(&mut self) {
|
||||
pub fn restore_all_windows(&mut self) -> Result<()> {
|
||||
tracing::info!("restoring all hidden windows");
|
||||
|
||||
let no_titlebar = NO_TITLEBAR.lock();
|
||||
|
||||
for monitor in self.monitors_mut() {
|
||||
for workspace in monitor.workspaces_mut() {
|
||||
for containers in workspace.containers_mut() {
|
||||
for window in containers.windows_mut() {
|
||||
if no_titlebar.contains(&window.exe()?) {
|
||||
window.add_title_bar()?;
|
||||
}
|
||||
|
||||
window.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
|
||||
@@ -524,6 +524,8 @@ impl WindowsApi {
|
||||
}
|
||||
|
||||
fn window_long_ptr_w(hwnd: HWND, index: WINDOW_LONG_PTR_INDEX) -> Result<isize> {
|
||||
// Can return 0, which does not always mean that an error has occurred
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw
|
||||
Result::from(WindowsResult::from(unsafe {
|
||||
GetWindowLongPtrW(hwnd, index)
|
||||
}))
|
||||
|
||||
@@ -23,6 +23,8 @@ use crate::ring::Ring;
|
||||
use crate::window::Window;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::INITIAL_CONFIGURATION_LOADED;
|
||||
use crate::NO_TITLEBAR;
|
||||
use crate::REMOVE_TITLEBARS;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Getters, CopyGetters, MutGetters, Setters, JsonSchema)]
|
||||
pub struct Workspace {
|
||||
@@ -212,9 +214,18 @@ impl Workspace {
|
||||
self.resize_dimensions(),
|
||||
);
|
||||
|
||||
let should_remove_titlebars = REMOVE_TITLEBARS.load(Ordering::SeqCst);
|
||||
let no_titlebar = NO_TITLEBAR.lock().clone();
|
||||
|
||||
let windows = self.visible_windows_mut();
|
||||
for (i, window) in windows.into_iter().enumerate() {
|
||||
if let (Some(window), Some(layout)) = (window, layouts.get(i)) {
|
||||
if should_remove_titlebars && no_titlebar.contains(&window.exe()?) {
|
||||
window.remove_title_bar()?;
|
||||
} else if no_titlebar.contains(&window.exe()?) {
|
||||
window.add_title_bar()?;
|
||||
}
|
||||
|
||||
window.set_position(layout, invisible_borders, false)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,6 +513,7 @@ gen_application_target_subcommand_args! {
|
||||
IdentifyLayeredApplication,
|
||||
IdentifyObjectNameChangeApplication,
|
||||
IdentifyBorderOverflowApplication,
|
||||
RemoveTitleBar,
|
||||
}
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
@@ -964,6 +965,11 @@ enum SubCommand {
|
||||
/// Identify an application that has WS_EX_LAYERED, but should still be managed
|
||||
#[clap(arg_required_else_help = true)]
|
||||
IdentifyLayeredApplication(IdentifyLayeredApplication),
|
||||
/// Whitelist an application for title bar removal
|
||||
#[clap(arg_required_else_help = true)]
|
||||
RemoveTitleBar(RemoveTitleBar),
|
||||
/// Toggle title bars for whitelisted applications
|
||||
ToggleTitleBars,
|
||||
/// Identify an application that has overflowing borders
|
||||
#[clap(arg_required_else_help = true)]
|
||||
#[clap(alias = "identify-border-overflow")]
|
||||
@@ -1710,6 +1716,21 @@ fn main() -> Result<()> {
|
||||
.as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::RemoveTitleBar(target) => {
|
||||
match target.identifier {
|
||||
ApplicationIdentifier::Exe => {}
|
||||
_ => {
|
||||
return Err(anyhow!(
|
||||
"this command requires applications to be identified by their exe"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
send_message(&SocketMessage::RemoveTitleBar(target.identifier, target.id).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::ToggleTitleBars => {
|
||||
send_message(&SocketMessage::ToggleTitleBars.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::Manage => {
|
||||
send_message(&SocketMessage::ManageFocusedWindow.as_bytes()?)?;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user