Compare commits

..

1 Commits

Author SHA1 Message Date
LGUG2Z
b575cba9f4 feat(wm): add stackbar for multi-window containers
This commit introduces the stackbar feature through careful extracting
and refactoring of code from the Komorebi-UI hard-fork.

Unfortunately on the fork, this feature was not implemented using atomic
commits, which resulted in the implementation here being more of a
"reinterpretation" than a lift-and-shit of the referenced code.

Nevertheless, this commit represents a working version of the stackbar
feature.

resolve #681
2024-03-25 14:25:25 -07:00
38 changed files with 529 additions and 973 deletions

485
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,16 +11,15 @@ members = [
]
[workspace.dependencies]
windows-interface = { version = "0.56" }
windows-implement = { version = "0.56" }
windows-core = { version = "0.56.0" }
windows-interface = { version = "0.53" }
windows-implement = { version = "0.53" }
dunce = "1"
dirs = "5"
color-eyre = "0.6"
serde_json = { package = "serde_json_lenient", version = "0.1" }
[workspace.dependencies.windows]
version = "0.56"
version = "0.54"
features = [
"implement",
"Win32_System_Com",

View File

@@ -324,7 +324,7 @@ every `WindowManagerEvent` and `SocketMessage` handled by `komorebi` in a Rust c
Below is a simple example of how to use `komorebi-client` in a basic Rust application.
```rust
// komorebi-client = { git = "https://github.com/LGUG2Z/komorebi", tag = "v0.1.24"}
// komorebi-client = { git = "https://github.com/LGUG2Z/komorebi", tag = "v0.1.22"}
use anyhow::Result;
use komorebi_client::Notification;

View File

@@ -0,0 +1,16 @@
# alt-focus-hack
```
DEPRECATED since v0.1.22
Usage: komorebic.exe alt-focus-hack <BOOLEAN_STATE>
Arguments:
<BOOLEAN_STATE>
[possible values: enable, disable]
Options:
-h, --help
Print help
```

View File

@@ -7,7 +7,7 @@ Usage: komorebic.exe float-rule <IDENTIFIER> <ID>
Arguments:
<IDENTIFIER>
[possible values: exe, class, title, path]
[possible values: exe, class, title]
<ID>
Identifier as a string

View File

@@ -0,0 +1,19 @@
# identify-border-overflow-application
```
Identify an application that has overflowing borders
Usage: komorebic.exe identify-border-overflow-application <IDENTIFIER> <ID>
Arguments:
<IDENTIFIER>
[possible values: exe, class, title]
<ID>
Identifier as a string
Options:
-h, --help
Print help
```

View File

@@ -7,7 +7,7 @@ Usage: komorebic.exe identify-layered-application <IDENTIFIER> <ID>
Arguments:
<IDENTIFIER>
[possible values: exe, class, title, path]
[possible values: exe, class, title]
<ID>
Identifier as a string

View File

@@ -7,7 +7,7 @@ Usage: komorebic.exe identify-object-name-change-application <IDENTIFIER> <ID>
Arguments:
<IDENTIFIER>
[possible values: exe, class, title, path]
[possible values: exe, class, title]
<ID>
Identifier as a string

View File

@@ -7,7 +7,7 @@ Usage: komorebic.exe identify-tray-application <IDENTIFIER> <ID>
Arguments:
<IDENTIFIER>
[possible values: exe, class, title, path]
[possible values: exe, class, title]
<ID>
Identifier as a string

View File

@@ -7,7 +7,7 @@ Usage: komorebic.exe initial-named-workspace-rule <IDENTIFIER> <ID> <WORKSPACE>
Arguments:
<IDENTIFIER>
[possible values: exe, class, title, path]
[possible values: exe, class, title]
<ID>
Identifier as a string

View File

@@ -7,7 +7,7 @@ Usage: komorebic.exe initial-workspace-rule <IDENTIFIER> <ID> <MONITOR> <WORKSPA
Arguments:
<IDENTIFIER>
[possible values: exe, class, title, path]
[possible values: exe, class, title]
<ID>
Identifier as a string

View File

@@ -7,7 +7,7 @@ Usage: komorebic.exe manage-rule <IDENTIFIER> <ID>
Arguments:
<IDENTIFIER>
[possible values: exe, class, title, path]
[possible values: exe, class, title]
<ID>
Identifier as a string

View File

@@ -7,7 +7,7 @@ Usage: komorebic.exe named-workspace-rule <IDENTIFIER> <ID> <WORKSPACE>
Arguments:
<IDENTIFIER>
[possible values: exe, class, title, path]
[possible values: exe, class, title]
<ID>
Identifier as a string

View File

@@ -7,7 +7,7 @@ Usage: komorebic.exe remove-title-bar <IDENTIFIER> <ID>
Arguments:
<IDENTIFIER>
[possible values: exe, class, title, path]
[possible values: exe, class, title]
<ID>
Identifier as a string

View File

@@ -7,7 +7,7 @@ Usage: komorebic.exe workspace-rule <IDENTIFIER> <ID> <MONITOR> <WORKSPACE>
Arguments:
<IDENTIFIER>
[possible values: exe, class, title, path]
[possible values: exe, class, title]
<ID>
Identifier as a string

View File

@@ -7,7 +7,6 @@ file.
```json
{
"active_window_border": true,
"active_window_border_style": "Rounded",
"active_window_border_colours": {
"single": {
"r": 66,
@@ -24,7 +23,7 @@ file.
"g": 51,
"b": 153
}
},
}
}
```
@@ -32,7 +31,7 @@ file.
It is important to note that the active window border will only apply to
windows managed by `komorebi`.
This feature is not considered stable, and you may encounter visual artifacts
This feature is not considered stable and you may encounter visual artifacts
from time to time.
[![Watch the tutorial

View File

@@ -1,23 +0,0 @@
# Stackbar
If you would like to add a visual stackbar to show which windows are in a container
stack ensure the following options are defined in the `komorebi.json` configuration
file.
```json
{
"stackbar": {
"height": 40,
"mode": "OnStack",
"tabs": {
"width": 300,
"focused_text": "#00a542",
"unfocused_text": "#b3b3b3",
"background": "#141414"
}
}
}
```
This feature is not considered stable, and you may encounter visual artifacts
from time to time.

View File

@@ -162,8 +162,8 @@ If you like the `grid` layout in [LeftWM](https://github.com/leftwm/leftwm-layou
## whkdrc
`whkd` is a fairly basic piece of software with a simple configuration format:
key bindings go to the left of the colon, and shell commands go to the right of the
colon. By default, the `whkdrc` file should be located in the `$Env:USERPROFILE/.config/` directory.
key bindings go to the left of the, and shell commands go to the right of the
colon.
Please remember that `whkd` does not support overriding Microsoft's limitations
on hotkey bindings that include the `Windows` key. If this is important to you,

View File

@@ -33,7 +33,6 @@ it from [source](https://github.com/LGUG2Z/komorebi) if you would prefer.
- [Scoop](#scoop)
- [WinGet](#winget)
- [Building from source](#building-from-source)
- [Offline](#offline)
## Long path support
@@ -112,12 +111,3 @@ cargo +stable install --path komorebic-no-console --locked
If the binaries have been built and added to your `$PATH` correctly, you should
see some output when running `komorebi --help` and `komorebic --help`
### Offline
Download the latest [komorebi](https://github.com/LGUG2Z/komorebi/releases)
and [whkd](https://github.com/LGUG2Z/whkd/releases) MSI installers on an internet-connected computer, then copy them to
an offline machine to install.
Once installed, proceed to get the [example configurations](example-configurations.md) (none of the commands for
first-time set up and running komorebi require an internet connection).

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.24/schema.json",
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.22/schema.json",
"app_specific_configuration_path": "$Env:USERPROFILE/applications.yaml",
"window_hiding_behaviour": "Cloak",
"cross_monitor_move_behaviour": "Insert",
@@ -16,26 +16,11 @@
"monitors": [
{
"workspaces": [
{
"name": "I",
"layout": "BSP"
},
{
"name": "II",
"layout": "VerticalStack"
},
{
"name": "III",
"layout": "HorizontalStack"
},
{
"name": "IV",
"layout": "UltrawideVerticalStack"
},
{
"name": "V",
"layout": "Rows"
}
{ "name": "I", "layout": "BSP" },
{ "name": "II", "layout": "VerticalStack" },
{ "name": "III", "layout": "HorizontalStack" },
{ "name": "IV", "layout": "UltrawideVerticalStack" },
{ "name": "V", "layout": "Rows" }
]
}
]

View File

@@ -51,6 +51,10 @@ docgen:
komorebic docgen
Get-ChildItem -Path "docs/cli" -Recurse -File | ForEach-Object { (Get-Content $_.FullName) -replace 'Usage: ', 'Usage: komorebic.exe ' | Set-Content $_.FullName }
exampledocs:
cp whkdrc.sample docs/whkdrc.sample
cp komorebi.example.json docs/komorebi.example.json
schemagen:
komorebic static-config-schema > schema.json
generate-schema-doc .\schema.json --config template_name=js_offline --config minify=false .\static-config-docs\

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi-client"
version = "0.1.25-dev.0"
version = "0.1.23-dev.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi-core"
version = "0.1.25-dev.0"
version = "0.1.23-dev.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -16,9 +16,9 @@ use crate::ApplicationIdentifier;
pub enum ApplicationOptions {
ObjectNameChange,
Layered,
BorderOverflow,
TrayAndMultiWindow,
Force,
BorderOverflow,
}
impl ApplicationOptions {
@@ -31,15 +31,15 @@ impl ApplicationOptions {
ApplicationOptions::Layered => {
format!("komorebic.exe identify-layered-application {kind} \"{id}\"",)
}
ApplicationOptions::BorderOverflow => {
format!("komorebic.exe identify-border-overflow-application {kind} \"{id}\"",)
}
ApplicationOptions::TrayAndMultiWindow => {
format!("komorebic.exe identify-tray-application {kind} \"{id}\"",)
}
ApplicationOptions::Force => {
format!("komorebic.exe manage-rule {kind} \"{id}\"")
}
ApplicationOptions::BorderOverflow => {
unreachable!("deprecated");
}
}
}

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi"
version = "0.1.25-dev.0"
version = "0.1.23-dev.0"
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
description = "A tiling window manager for Windows"
categories = ["tiling-window-manager", "windows"]
@@ -43,7 +43,6 @@ uds_windows = "1"
which = "6"
widestring = "1"
windows = { workspace = true }
windows-core = { workspace = true }
windows-implement = { workspace = true }
windows-interface = { workspace = true }
winput = "0.2"

View File

@@ -149,6 +149,7 @@ lazy_static! {
static ref PERMAIGNORE_CLASSES: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
"Chrome_RenderWidgetHostHWND".to_string(),
]));
static ref BORDER_OVERFLOW_IDENTIFIERS: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(vec![]));
static ref WSL2_UI_PROCESSES: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
"X410.exe".to_string(),
"vcxsrv.exe".to_string(),
@@ -193,9 +194,6 @@ 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()));

View File

@@ -34,7 +34,6 @@ use komorebi::window_manager::WindowManager;
use komorebi::windows_api::WindowsApi;
use komorebi::winevent_listener;
use komorebi::CUSTOM_FFM;
use komorebi::DATA_DIR;
use komorebi::HOME_DIR;
use komorebi::INITIAL_CONFIGURATION_LOADED;
use komorebi::SESSION_ID;
@@ -200,8 +199,6 @@ fn main() -> Result<()> {
Option::from,
);
std::fs::create_dir_all(&*DATA_DIR)?;
let wm = if let Some(config) = &static_config {
tracing::info!(
"creating window manager from static configuration file: {}",

View File

@@ -57,6 +57,7 @@ use crate::BORDER_ENABLED;
use crate::BORDER_HIDDEN;
use crate::BORDER_HWND;
use crate::BORDER_OFFSET;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::BORDER_WIDTH;
use crate::CUSTOM_FFM;
use crate::DATA_DIR;
@@ -1025,6 +1026,26 @@ impl WindowManager {
SocketMessage::WatchConfiguration(enable) => {
self.watch_configuration(enable)?;
}
SocketMessage::IdentifyBorderOverflowApplication(identifier, ref id) => {
let mut identifiers = BORDER_OVERFLOW_IDENTIFIERS.lock();
let mut should_push = true;
for i in &*identifiers {
if let MatchingRule::Simple(i) = i {
if i.id.eq(id) {
should_push = false;
}
}
}
if should_push {
identifiers.push(MatchingRule::Simple(IdWithIdentifier {
kind: identifier,
id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy),
}));
}
}
SocketMessage::IdentifyObjectNameChangeApplication(identifier, ref id) => {
let mut identifiers = OBJECT_NAME_CHANGE_ON_LAUNCH.lock();
@@ -1249,6 +1270,9 @@ impl WindowManager {
BORDER_OFFSET.store(offset, Ordering::SeqCst);
WindowsApi::invalidate_border_rect()?;
}
SocketMessage::AltFocusHack(_) => {
tracing::info!("this action is deprecated");
}
SocketMessage::ApplicationSpecificConfigurationSchema => {
let asc = schema_for!(Vec<ApplicationConfiguration>);
let schema = serde_json::to_string_pretty(&asc)?;
@@ -1296,9 +1320,6 @@ impl WindowManager {
REMOVE_TITLEBARS.store(!current, Ordering::SeqCst);
self.update_focused_workspace(false)?;
}
// Deprecated commands
SocketMessage::AltFocusHack(_)
| SocketMessage::IdentifyBorderOverflowApplication(_, _) => {}
};
match message {
@@ -1426,7 +1447,7 @@ impl WindowManager {
Ok(())
}
#[tracing::instrument(skip(self), level = "debug")]
#[tracing::instrument(skip(self))]
fn handle_initial_workspace_rules(
&mut self,
id: &String,
@@ -1438,7 +1459,7 @@ impl WindowManager {
Ok(())
}
#[tracing::instrument(skip(self), level = "debug")]
#[tracing::instrument(skip(self))]
fn handle_definitive_workspace_rules(
&mut self,
id: &String,
@@ -1450,7 +1471,7 @@ impl WindowManager {
Ok(())
}
#[tracing::instrument(skip(self), level = "debug")]
#[tracing::instrument(skip(self))]
pub fn handle_workspace_rules(
&mut self,
id: &String,

View File

@@ -339,53 +339,38 @@ impl WindowManager {
let new_window_behaviour = self.window_container_behaviour;
let workspace = self.focused_workspace_mut()?;
if !workspace
.floating_windows()
.iter()
.any(|w| w.hwnd == window.hwnd)
{
let focused_container_idx = workspace.focused_container_idx();
let focused_container_idx = workspace.focused_container_idx();
let new_position = WindowsApi::window_rect(window.hwnd())?;
let old_position = *workspace
.latest_layout()
.get(focused_container_idx)
// If the move was to another monitor with an empty workspace, the
// workspace here will refer to that empty workspace, which won't
// have any latest layout set. We fall back to a Default for Rect
// which allows us to make a reasonable guess that the drag has taken
// place across a monitor boundary to an empty workspace
.unwrap_or(&Rect::default());
let new_position = WindowsApi::window_rect(window.hwnd())?;
// This will be true if we have moved to an empty workspace on another monitor
let mut moved_across_monitors = old_position == Rect::default();
if let Some((origin_monitor_idx, origin_workspace_idx, _)) = pending {
// If we didn't move to another monitor with an empty workspace, it is
// still possible that we moved to another monitor with a populated workspace
if !moved_across_monitors {
// So we'll check if the origin monitor index and the target monitor index
// are different, if they are, we can set the override
moved_across_monitors = origin_monitor_idx != target_monitor_idx;
let old_position = *workspace
.latest_layout()
.get(focused_container_idx)
// If the move was to another monitor with an empty workspace, the
// workspace here will refer to that empty workspace, which won't
// have any latest layout set. We fall back to a Default for Rect
// which allows us to make a reasonable guess that the drag has taken
// place across a monitor boundary to an empty workspace
.unwrap_or(&Rect::default());
if moved_across_monitors {
// Want to make sure that we exclude unmanaged windows from cross-monitor
// moves with a mouse, otherwise the currently focused idx container will
// be moved when we just want to drag an unmanaged window
let origin_workspace = self
.monitors()
.get(origin_monitor_idx)
.ok_or_else(|| anyhow!("cannot get monitor idx"))?
.workspaces()
.get(origin_workspace_idx)
.ok_or_else(|| anyhow!("cannot get workspace idx"))?;
// This will be true if we have moved to an empty workspace on another monitor
let mut moved_across_monitors = old_position == Rect::default();
let managed_window =
origin_workspace.contains_managed_window(window.hwnd);
if !managed_window {
moved_across_monitors = false;
}
if let Some((origin_monitor_idx, _, _)) = pending {
// If we didn't move to another monitor with an empty workspace, it is
// still possible that we moved to another monitor with a populated workspace
if !moved_across_monitors {
// So we'll check if the origin monitor index and the target monitor index
// are different, if they are, we can set the override
moved_across_monitors = origin_monitor_idx != target_monitor_idx;
}
}
}
let workspace = self.focused_workspace_mut()?;
if workspace.contains_managed_window(window.hwnd) || moved_across_monitors {
let resize = Rect {
left: new_position.left - old_position.left,
top: new_position.top - old_position.top,

View File

@@ -7,7 +7,6 @@ use crate::window_manager::WindowManager;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::workspace::Workspace;
use crate::ACTIVE_WINDOW_BORDER_STYLE;
use crate::BORDER_COLOUR_CURRENT;
use crate::BORDER_COLOUR_MONOCLE;
use crate::BORDER_COLOUR_SINGLE;
@@ -15,6 +14,7 @@ use crate::BORDER_COLOUR_STACK;
use crate::BORDER_ENABLED;
use crate::BORDER_HWND;
use crate::BORDER_OFFSET;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::BORDER_WIDTH;
use crate::DATA_DIR;
use crate::DEFAULT_CONTAINER_PADDING;
@@ -82,17 +82,6 @@ pub struct ActiveWindowBorderColours {
pub monocle: Colour,
}
#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub enum ActiveWindowBorderStyle {
#[default]
/// Use the system border style
System,
/// Use the Windows 11-style rounded borders
Rounded,
/// Use the Windows 10-style square borders
Square,
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct WorkspaceConfig {
/// Name
@@ -231,7 +220,7 @@ impl From<&Monitor> for MonitorConfig {
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
/// The `komorebi.json` static configuration file reference for `v0.1.24`
/// The `komorebi.json` static configuration file reference for `v0.1.20`
pub struct StaticConfig {
/// DEPRECATED from v0.1.22: no longer required
#[serde(skip_serializing_if = "Option::is_none")]
@@ -271,9 +260,6 @@ pub struct StaticConfig {
/// Active window border colours for different container types
#[serde(skip_serializing_if = "Option::is_none")]
pub active_window_border_colours: Option<ActiveWindowBorderColours>,
/// Active window border style (default: System)
#[serde(skip_serializing_if = "Option::is_none")]
pub active_window_border_style: Option<ActiveWindowBorderStyle>,
/// Global default workspace padding (default: 10)
#[serde(skip_serializing_if = "Option::is_none")]
pub default_workspace_padding: Option<i32>,
@@ -425,7 +411,6 @@ impl From<&WindowManager> for StaticConfig {
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(*ACTIVE_WINDOW_BORDER_STYLE.lock()),
default_workspace_padding: Option::from(
DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst),
),
@@ -492,13 +477,11 @@ impl StaticConfig {
BORDER_COLOUR_MONOCLE.store(u32::from(colours.monocle), Ordering::SeqCst);
}
let active_window_border_style = self.active_window_border_style.unwrap_or_default();
*ACTIVE_WINDOW_BORDER_STYLE.lock() = active_window_border_style;
let mut float_identifiers = FLOAT_IDENTIFIERS.lock();
let mut regex_identifiers = REGEX_IDENTIFIERS.lock();
let mut manage_identifiers = MANAGE_IDENTIFIERS.lock();
let mut tray_and_multi_window_identifiers = TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock();
let mut border_overflow_identifiers = BORDER_OVERFLOW_IDENTIFIERS.lock();
let mut object_name_change_identifiers = OBJECT_NAME_CHANGE_ON_LAUNCH.lock();
let mut layered_identifiers = LAYERED_WHITELIST.lock();
@@ -522,6 +505,14 @@ impl StaticConfig {
populate_rules(rules, &mut layered_identifiers, &mut regex_identifiers)?;
}
if let Some(rules) = &mut self.border_overflow_applications {
populate_rules(
rules,
&mut border_overflow_identifiers,
&mut regex_identifiers,
)?;
}
if let Some(rules) = &mut self.tray_and_multi_window_applications {
populate_rules(
rules,
@@ -585,6 +576,13 @@ impl StaticConfig {
&mut regex_identifiers,
)?;
}
ApplicationOptions::BorderOverflow => {
populate_option(
&mut entry,
&mut border_overflow_identifiers,
&mut regex_identifiers,
)?;
}
ApplicationOptions::TrayAndMultiWindow => {
populate_option(
&mut entry,
@@ -599,7 +597,6 @@ impl StaticConfig {
&mut regex_identifiers,
)?;
}
ApplicationOptions::BorderOverflow => {} // deprecated
}
}
}

View File

@@ -49,6 +49,7 @@ use crate::windows_api::WindowsApi;
use crate::winevent_listener;
use crate::workspace::Workspace;
use crate::BORDER_HWND;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::DATA_DIR;
use crate::DISPLAY_INDEX_PREFERENCES;
use crate::FLOAT_IDENTIFIERS;
@@ -100,6 +101,7 @@ pub struct State {
pub manage_identifiers: Vec<MatchingRule>,
pub layered_whitelist: Vec<MatchingRule>,
pub tray_and_multi_window_identifiers: Vec<MatchingRule>,
pub border_overflow_identifiers: Vec<MatchingRule>,
pub name_change_on_launch_identifiers: Vec<MatchingRule>,
pub monitor_index_preferences: HashMap<usize, Rect>,
pub display_index_preferences: HashMap<usize, String>,
@@ -128,6 +130,7 @@ impl From<&WindowManager> for State {
manage_identifiers: MANAGE_IDENTIFIERS.lock().clone(),
layered_whitelist: LAYERED_WHITELIST.lock().clone(),
tray_and_multi_window_identifiers: TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock().clone(),
border_overflow_identifiers: BORDER_OVERFLOW_IDENTIFIERS.lock().clone(),
name_change_on_launch_identifiers: OBJECT_NAME_CHANGE_ON_LAUNCH.lock().clone(),
monitor_index_preferences: MONITOR_INDEX_PREFERENCES.lock().clone(),
display_index_preferences: DISPLAY_INDEX_PREFERENCES.lock().clone(),
@@ -476,7 +479,7 @@ impl WindowManager {
}
#[allow(clippy::too_many_arguments)]
#[tracing::instrument(skip(self), level = "debug")]
#[tracing::instrument(skip(self))]
fn add_window_handle_to_move_based_on_workspace_rule(
&self,
window_title: &String,
@@ -504,7 +507,7 @@ impl WindowManager {
});
}
#[tracing::instrument(skip(self), level = "debug")]
#[tracing::instrument(skip(self))]
pub fn enforce_workspace_rules(&mut self) -> Result<()> {
let mut to_move = vec![];

View File

@@ -41,8 +41,6 @@ use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::winevent::WinEvent;
use crate::winevent_listener;
use crate::ActiveWindowBorderStyle;
use crate::ACTIVE_WINDOW_BORDER_STYLE;
use crate::BORDER_COLOUR_CURRENT;
use crate::BORDER_RECT;
use crate::BORDER_WIDTH;
@@ -234,20 +232,10 @@ pub extern "system" fn border_window(
// 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);
}
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);
}
EndPaint(window, &ps);
ValidateRect(window, None);

View File

@@ -843,10 +843,6 @@ impl Workspace {
if container.windows().is_empty() {
self.containers_mut().remove(focused_idx);
self.resize_dimensions_mut().remove(focused_idx);
if focused_idx == self.containers().len() {
self.focus_container(focused_idx - 1);
}
} else {
container.load_focused_window();
}

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebic-no-console"
version = "0.1.25-dev.0"
version = "0.1.23-dev.0"
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
description = "The command-line interface (without a console) for Komorebi, a tiling window manager for Windows"
categories = ["cli", "tiling-window-manager", "windows"]

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebic"
version = "0.1.25-dev.0"
version = "0.1.23-dev.0"
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
description = "The command-line interface for Komorebi, a tiling window manager for Windows"
categories = ["cli", "tiling-window-manager", "windows"]
@@ -19,12 +19,12 @@ color-eyre = { workspace = true }
dirs = { workspace = true }
dunce = { workspace = true }
fs-tail = "0.1"
heck = "0.5"
heck = "0.4"
lazy_static = "1"
miette = { version = "7", features = ["fancy"] }
paste = "1"
powershell_script = "1.0"
reqwest = { version = "0.12", features = ["blocking"] }
reqwest = { version = "0.11", features = ["blocking"] }
serde = { version = "1", features = ["derive"] }
serde_json = { workspace = true }
serde_yaml = "0.9"
@@ -35,4 +35,4 @@ which = "6"
windows = { workspace = true }
[build-dependencies]
reqwest = { version = "0.12", features = ["blocking"] }
reqwest = { version = "0.11", features = ["blocking"] }

View File

@@ -1125,7 +1125,7 @@ enum SubCommand {
/// Toggle title bars for whitelisted applications
ToggleTitleBars,
/// Identify an application that has overflowing borders
#[clap(hide = true)]
#[clap(arg_required_else_help = true)]
#[clap(alias = "identify-border-overflow")]
IdentifyBorderOverflowApplication(IdentifyBorderOverflowApplication),
/// Enable or disable the active window border
@@ -1871,13 +1871,6 @@ if (!(Get-Process whkd -ErrorAction SilentlyContinue))
}
}
}
println!("\nThank you for using komorebi!\n");
println!("* Become a sponsor https://github.com/sponsors/LGUG2Z - Even $1/month makes a big difference");
println!(
"* Subscribe to https://youtube.com/@LGUG2Z - Live dev videos and feature previews"
);
println!("* Join the Discord https://discord.gg/mGkn66PHkx - Chat, ask questions, share your desktops");
}
SubCommand::Stop(arg) => {
if arg.whkd {
@@ -2063,6 +2056,9 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
SubCommand::CompleteConfiguration => {
send_message(&SocketMessage::CompleteConfiguration.as_bytes()?)?;
}
SubCommand::AltFocusHack(arg) => {
send_message(&SocketMessage::AltFocusHack(arg.boolean_state.into()).as_bytes()?)?;
}
SubCommand::IdentifyObjectNameChangeApplication(target) => {
send_message(
&SocketMessage::IdentifyObjectNameChangeApplication(target.identifier, target.id)
@@ -2080,6 +2076,12 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
.as_bytes()?,
)?;
}
SubCommand::IdentifyBorderOverflowApplication(target) => {
send_message(
&SocketMessage::IdentifyBorderOverflowApplication(target.identifier, target.id)
.as_bytes()?,
)?;
}
SubCommand::RemoveTitleBar(target) => {
match target.identifier {
ApplicationIdentifier::Exe => {}
@@ -2271,10 +2273,6 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
SubCommand::GenerateStaticConfig => {
print_query(&SocketMessage::GenerateStaticConfig.as_bytes()?);
}
// Deprecated
SubCommand::AltFocusHack(_) | SubCommand::IdentifyBorderOverflowApplication(_) => {
println!("Command deprecated - this is now automatically handled by komorebi! 🎉");
}
}
Ok(())

View File

@@ -53,7 +53,6 @@ nav:
- Common workflows:
- common-workflows/komorebi-config-home.md
- common-workflows/active-window-border.md
- common-workflows/stackbar.md
- common-workflows/remove-gaps.md
- common-workflows/ignore-windows.md
- common-workflows/force-manage-windows.md
@@ -182,6 +181,7 @@ nav:
- cli/identify-layered-application.md
- cli/remove-title-bar.md
- cli/toggle-title-bars.md
- cli/identify-border-overflow-application.md
- cli/active-window-border.md
- cli/active-window-border-colour.md
- cli/active-window-border-width.md

View File

@@ -133,32 +133,6 @@
}
}
},
"active_window_border_style": {
"description": "Active window border style (default: System)",
"oneOf": [
{
"description": "Use the system border style",
"type": "string",
"enum": [
"System"
]
},
{
"description": "Use the Windows 11-style rounded borders",
"type": "string",
"enum": [
"Rounded"
]
},
{
"description": "Use the Windows 10-style square borders",
"type": "string",
"enum": [
"Square"
]
}
]
},
"app_specific_configuration_path": {
"description": "Path to applications.yaml from komorebi-application-specific-configurations (default: None)",
"type": "string"
@@ -172,83 +146,36 @@
"description": "Identify border overflow applications",
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex",
"DoesNotEndWith",
"DoesNotStartWith",
"DoesNotEqual",
"DoesNotContain"
]
}
}
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
{
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex",
"DoesNotEndWith",
"DoesNotStartWith",
"DoesNotEqual",
"DoesNotContain"
]
}
}
}
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex"
]
}
]
}
}
},
"border_width": {
@@ -296,83 +223,36 @@
"description": "Individual window floating rules",
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex",
"DoesNotEndWith",
"DoesNotStartWith",
"DoesNotEqual",
"DoesNotContain"
]
}
}
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
{
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex",
"DoesNotEndWith",
"DoesNotStartWith",
"DoesNotEqual",
"DoesNotContain"
]
}
}
}
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex"
]
}
]
}
}
},
"focus_follows_mouse": {
@@ -462,166 +342,72 @@
"description": "Identify applications that have the WS_EX_LAYERED extended window style",
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex",
"DoesNotEndWith",
"DoesNotStartWith",
"DoesNotEqual",
"DoesNotContain"
]
}
}
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
{
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex",
"DoesNotEndWith",
"DoesNotStartWith",
"DoesNotEqual",
"DoesNotContain"
]
}
}
}
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex"
]
}
]
}
}
},
"manage_rules": {
"description": "Individual window force-manage rules",
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex",
"DoesNotEndWith",
"DoesNotStartWith",
"DoesNotEqual",
"DoesNotContain"
]
}
}
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
{
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex",
"DoesNotEndWith",
"DoesNotStartWith",
"DoesNotEqual",
"DoesNotContain"
]
}
}
}
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex"
]
}
]
}
}
},
"monitor_index_preferences": {
@@ -755,11 +541,7 @@
"StartsWith",
"EndsWith",
"Contains",
"Regex",
"DoesNotEndWith",
"DoesNotStartWith",
"DoesNotEqual",
"DoesNotContain"
"Regex"
]
}
}
@@ -833,11 +615,7 @@
"StartsWith",
"EndsWith",
"Contains",
"Regex",
"DoesNotEndWith",
"DoesNotStartWith",
"DoesNotEqual",
"DoesNotContain"
"Regex"
]
}
}
@@ -857,83 +635,36 @@
"description": "Identify applications that send EVENT_OBJECT_NAMECHANGE on launch (very rare)",
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex",
"DoesNotEndWith",
"DoesNotStartWith",
"DoesNotEqual",
"DoesNotContain"
]
}
}
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
{
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex",
"DoesNotEndWith",
"DoesNotStartWith",
"DoesNotEqual",
"DoesNotContain"
]
}
}
}
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex"
]
}
]
}
}
},
"resize_delta": {
@@ -1082,83 +813,36 @@
"description": "Identify tray and multi-window applications",
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex",
"DoesNotEndWith",
"DoesNotStartWith",
"DoesNotEqual",
"DoesNotContain"
]
}
}
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
{
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"kind"
],
"properties": {
"id": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex",
"DoesNotEndWith",
"DoesNotStartWith",
"DoesNotEqual",
"DoesNotContain"
]
}
}
}
"kind": {
"type": "string",
"enum": [
"Exe",
"Class",
"Title",
"Path"
]
},
"matching_strategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex"
]
}
]
}
}
},
"unmanaged_window_operation_behaviour": {