Compare commits

..

1 Commits

Author SHA1 Message Date
LGUG2Z
2dbf7da249 feat(config): add default_workspace_layout opt
This commit adds a default_workspace_layout opt, which defaults to BSP
to maintain backwards compatibility. This can also be set to "None".

When set to "None" or omitted, the default behaviour for new or
undefined workspaces (i.e. on monitors without config blocks) will be
non-tiling.  Otherwise, the given value will be the default layout
applied.
2026-01-13 08:26:41 -08:00
16 changed files with 188 additions and 208 deletions

279
Cargo.lock generated
View File

@@ -615,28 +615,6 @@ dependencies = [
"arrayvec",
]
[[package]]
name = "aws-lc-rs"
version = "1.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b7b6141e96a8c160799cc2d5adecd5cbbe5054cb8c7c4af53da0f83bb7ad256"
dependencies = [
"aws-lc-sys",
"zeroize",
]
[[package]]
name = "aws-lc-sys"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c34dda4df7017c8db52132f0f8a2e0f8161649d15723ed63fc00c82d0f2081a"
dependencies = [
"cc",
"cmake",
"dunce",
"fs_extra",
]
[[package]]
name = "backtrace"
version = "0.3.76"
@@ -2173,12 +2151,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "fs_extra"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
name = "fsevent-sys"
version = "4.1.0"
@@ -2328,10 +2300,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if 1.0.4",
"js-sys",
"libc",
"wasi 0.11.1+wasi-snapshot-preview1",
"wasm-bindgen",
]
[[package]]
@@ -2341,11 +2311,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if 1.0.4",
"js-sys",
"libc",
"r-efi",
"wasip2",
"wasm-bindgen",
]
[[package]]
@@ -2724,6 +2692,22 @@ dependencies = [
"tower-service",
]
[[package]]
name = "hyper-tls"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [
"bytes",
"http-body-util",
"hyper",
"hyper-util",
"native-tls",
"tokio",
"tokio-native-tls",
"tower-service",
]
[[package]]
name = "hyper-util"
version = "0.1.19"
@@ -3498,12 +3482,6 @@ dependencies = [
"imgref",
]
[[package]]
name = "lru-slab"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
[[package]]
name = "mac-addr"
version = "0.3.0"
@@ -3746,6 +3724,23 @@ dependencies = [
"rand 0.8.5",
]
[[package]]
name = "native-tls"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
dependencies = [
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]]
name = "ndk"
version = "0.9.0"
@@ -4531,10 +4526,48 @@ dependencies = [
]
[[package]]
name = "openssl-probe"
version = "0.2.1"
name = "openssl"
version = "0.10.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
dependencies = [
"bitflags 2.10.0",
"cfg-if 1.0.4",
"foreign-types 0.3.2",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "openssl-probe"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
version = "0.9.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "option-ext"
@@ -5043,62 +5076,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "quinn"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
dependencies = [
"bytes",
"cfg_aliases",
"pin-project-lite",
"quinn-proto",
"quinn-udp",
"rustc-hash 2.1.1",
"rustls",
"socket2",
"thiserror 2.0.17",
"tokio",
"tracing",
"web-time",
]
[[package]]
name = "quinn-proto"
version = "0.11.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
dependencies = [
"aws-lc-rs",
"bytes",
"getrandom 0.3.4",
"lru-slab",
"rand 0.9.2",
"ring",
"rustc-hash 2.1.1",
"rustls",
"rustls-pki-types",
"slab",
"thiserror 2.0.17",
"tinyvec",
"tracing",
"web-time",
]
[[package]]
name = "quinn-udp"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2",
"tracing",
"windows-sys 0.60.2",
]
[[package]]
name = "quote"
version = "1.0.43"
@@ -5437,9 +5414,9 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
[[package]]
name = "reqwest"
version = "0.13.1"
version = "0.12.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04e9018c9d814e5f30cc16a0f03271aeab3571e609612d9fe78c1aa8d11c2f62"
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [
"base64",
"bytes",
@@ -5453,19 +5430,21 @@ dependencies = [
"http-body-util",
"hyper",
"hyper-rustls",
"hyper-tls",
"hyper-util",
"js-sys",
"log",
"mime",
"native-tls",
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls",
"rustls-pki-types",
"rustls-platform-verifier",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tokio-rustls",
"tokio-native-tls",
"tower",
"tower-http",
"tower-service",
@@ -5563,7 +5542,6 @@ version = "0.23.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b"
dependencies = [
"aws-lc-rs",
"once_cell",
"rustls-pki-types",
"rustls-webpki",
@@ -5571,62 +5549,21 @@ dependencies = [
"zeroize",
]
[[package]]
name = "rustls-native-certs"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63"
dependencies = [
"openssl-probe",
"rustls-pki-types",
"schannel",
"security-framework",
]
[[package]]
name = "rustls-pki-types"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282"
dependencies = [
"web-time",
"zeroize",
]
[[package]]
name = "rustls-platform-verifier"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
dependencies = [
"core-foundation 0.10.1",
"core-foundation-sys",
"jni",
"log",
"once_cell",
"rustls",
"rustls-native-certs",
"rustls-platform-verifier-android",
"rustls-webpki",
"security-framework",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.61.2",
]
[[package]]
name = "rustls-platform-verifier-android"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]]
name = "rustls-webpki"
version = "0.103.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52"
dependencies = [
"aws-lc-rs",
"ring",
"rustls-pki-types",
"untrusted",
@@ -5732,12 +5669,12 @@ dependencies = [
[[package]]
name = "security-framework"
version = "3.5.1"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags 2.10.0",
"core-foundation 0.10.1",
"core-foundation 0.9.4",
"core-foundation-sys",
"libc",
"security-framework-sys",
@@ -5837,6 +5774,18 @@ dependencies = [
"syn 2.0.114",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_variant"
version = "0.1.3"
@@ -6523,21 +6472,6 @@ dependencies = [
"zerovec",
]
[[package]]
name = "tinyvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.49.0"
@@ -6552,6 +6486,16 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
dependencies = [
"native-tls",
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.26.4"
@@ -7205,15 +7149,6 @@ dependencies = [
"web-sys",
]
[[package]]
name = "webpki-root-certs"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "weezl"
version = "0.1.12"

View File

@@ -57,7 +57,17 @@ If you need help doing this you can ask on Discord.
## Note: komorebi for Mac
komorebi for Mac lives [here](https://github.com/LGUG2Z/komorebi-for-mac) :)
If you made your way to this repo looking for [komorebi for
Mac](https://github.com/KomoCorp/komorebi-for-mac), the project is currently
being developed in private with [early access available to GitHub
Sponsors](https://github.com/sponsors/LGUG2Z).
If you want to see how far along development is before signing up for early
access (spoiler: it's very far along!) there is an overview video you can watch
[here](https://www.youtube.com/watch?v=u3eJcsa_MJk).
Sponsors with early access can install komorebi for Mac either by compiling
from source, by using Homebrew, or by using the project's Nix Flake.
## Overview

View File

@@ -28,7 +28,7 @@ num-derive = "0.4"
num-traits = "0.2"
parking_lot = { workspace = true }
random_word = { version = "0.5", features = ["en"] }
reqwest = { version = "0.13", features = ["blocking"] }
reqwest = { version = "0.12", features = ["blocking"] }
schemars = { workspace = true, optional = true }
serde = { workspace = true }
serde_json = { workspace = true }

View File

@@ -18,7 +18,7 @@ use std::path::PathBuf;
/// The `komorebi.bar.json` configuration file reference for `v0.1.40`
pub struct KomobarConfig {
/// Bar height
#[cfg_attr(feature = "schemars", schemars(extend("default" = 50)))]
#[cfg_attr(feature = "schemars", schemars(extend("default" = 50.0)))]
pub height: Option<f32>,
/// Bar padding. Use one value for all sides or use a grouped padding for horizontal and/or
/// vertical definition which can each take a single value for a symmetric padding or two

View File

@@ -30,7 +30,7 @@ parking_lot = { workspace = true }
paste = { workspace = true }
powershell_script = "1.0"
regex = "1"
reqwest = { version = "0.13", features = ["blocking"] }
reqwest = { version = "0.12", features = ["blocking"] }
schemars = { workspace = true, optional = true }
serde = { workspace = true }
serde_json = { workspace = true, features = ["preserve_order"] }
@@ -57,7 +57,7 @@ serde_with = { version = "3.12", features = ["schemars_1"] }
shadow-rs = { workspace = true }
[dev-dependencies]
reqwest = { version = "0.13", features = ["blocking"] }
reqwest = { version = "0.12", features = ["blocking"] }
uuid = { version = "1", features = ["v4"] }
[features]

View File

@@ -548,12 +548,7 @@ impl Border {
LRESULT(0)
}
WM_DESTROY => {
let border_pointer: *mut Border = GetWindowLongPtrW(window, GWLP_USERDATA) as _;
if !border_pointer.is_null() {
(*border_pointer).render_target = None;
(*border_pointer).brushes.clear();
SetWindowLongPtrW(window, GWLP_USERDATA, 0);
}
SetWindowLongPtrW(window, GWLP_USERDATA, 0);
PostQuitMessage(0);
LRESULT(0)
}

View File

@@ -767,6 +767,12 @@ fn remove_border(
fn destroy_border(border: Box<Border>) -> color_eyre::Result<()> {
let raw_pointer = Box::into_raw(border);
unsafe {
// release d2d resources **BEFORE** destroying window
// this drops render_target and brushes while HWND is still valid
// prevents EndDraw() from accessing freed HWND resources
(*raw_pointer).render_target = None;
(*raw_pointer).brushes.clear();
// Now safe to destroy window
(*raw_pointer).destroy()?;
}

View File

@@ -1,4 +1,5 @@
use clap::ValueEnum;
use core::str::FromStr;
use serde::Deserialize;
use serde::Serialize;
use strum::Display;
@@ -8,6 +9,22 @@ use super::OperationDirection;
use super::Rect;
use super::Sizing;
pub fn deserialize_option_none_default_layout<'de, D>(
deserializer: D,
) -> Result<Option<DefaultLayout>, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if s == "None" {
Ok(None)
} else {
<DefaultLayout as FromStr>::from_str(&s)
.map(Some)
.map_err(serde::de::Error::custom)
}
}
#[derive(
Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq, Display, EnumString, ValueEnum,
)]

View File

@@ -240,6 +240,8 @@ lazy_static! {
static ref CURRENT_VIRTUAL_DESKTOP: Arc<Mutex<Option<Vec<u8>>>> = Arc::new(Mutex::new(None));
}
pub static DEFAULT_WORKSPACE_LAYOUT: AtomicCell<Option<DefaultLayout>> =
AtomicCell::new(Some(DefaultLayout::BSP));
pub static DEFAULT_WORKSPACE_PADDING: AtomicI32 = AtomicI32::new(10);
pub static DEFAULT_CONTAINER_PADDING: AtomicI32 = AtomicI32::new(10);
pub static DEFAULT_RESIZE_DELTA: i32 = 50;

View File

@@ -431,24 +431,6 @@ impl WindowManager {
proceed = false;
}
// after enforce_workspace_rules() has run, check if window exists in ANY workspace
// to prevent duplication when workspace rules move windows across workspaces
if proceed {
let window_already_managed = self
.monitors()
.iter()
.flat_map(|m| m.workspaces())
.any(|ws| ws.contains_window(window.hwnd));
if window_already_managed {
tracing::debug!(
"skipping window addition, already managed after workspace rule enforcement"
);
proceed = false;
}
}
if proceed {
let behaviour = self.window_management_behaviour(
focused_monitor_idx,

View File

@@ -5,6 +5,7 @@ use crate::DATA_DIR;
use crate::DEFAULT_CONTAINER_PADDING;
use crate::DEFAULT_MOUSE_FOLLOWS_FOCUS;
use crate::DEFAULT_RESIZE_DELTA;
use crate::DEFAULT_WORKSPACE_LAYOUT;
use crate::DEFAULT_WORKSPACE_PADDING;
use crate::DISPLAY_INDEX_PREFERENCES;
use crate::FLOATING_APPLICATIONS;
@@ -557,6 +558,11 @@ pub struct StaticConfig {
/// Individual window transparency ignore rules
#[serde(skip_serializing_if = "Option::is_none")]
pub transparency_ignore_rules: Option<Vec<MatchingRule>>,
/// Global default workspace layout for new workspaces
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "schemars", schemars(extend("default" = DefaultLayout::BSP)))]
#[serde(deserialize_with = "crate::default_layout::deserialize_option_none_default_layout")]
pub default_workspace_layout: Option<DefaultLayout>,
/// Global default workspace padding
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "schemars", schemars(extend("default" = DEFAULT_WORKSPACE_PADDING)))]
@@ -907,6 +913,7 @@ impl From<&WindowManager> for StaticConfig {
remove_titlebar_applications: Option::from(NO_TITLEBAR.lock().clone()),
floating_window_aspect_ratio: Option::from(*FLOATING_WINDOW_TOGGLE_ASPECT_RATIO.lock()),
window_handling_behaviour: Option::from(WINDOW_HANDLING_BEHAVIOUR.load()),
default_workspace_layout: DEFAULT_WORKSPACE_LAYOUT.load(),
}
}
}
@@ -985,6 +992,8 @@ impl StaticConfig {
DEFAULT_CONTAINER_PADDING.store(container, Ordering::SeqCst);
}
DEFAULT_WORKSPACE_LAYOUT.store(self.default_workspace_layout);
if let Some(workspace) = self.default_workspace_padding {
DEFAULT_WORKSPACE_PADDING.store(workspace, Ordering::SeqCst);
}

View File

@@ -3876,6 +3876,7 @@ impl WindowManager {
#[cfg(test)]
mod tests {
use super::*;
use crate::DEFAULT_WORKSPACE_LAYOUT;
use crate::monitor;
use crossbeam_channel::Sender;
use crossbeam_channel::bounded;
@@ -5202,6 +5203,7 @@ mod tests {
#[test]
fn test_toggle_tiling() {
let (mut wm, _context) = setup_window_manager();
DEFAULT_WORKSPACE_LAYOUT.store(Some(DefaultLayout::BSP));
{
let mut m = monitor::new(

View File

@@ -8,6 +8,7 @@ use std::sync::atomic::Ordering;
use crate::DATA_DIR;
use crate::DEFAULT_CONTAINER_PADDING;
use crate::DEFAULT_WORKSPACE_LAYOUT;
use crate::DEFAULT_WORKSPACE_PADDING;
use crate::FloatingLayerBehaviour;
use crate::INITIAL_CONFIGURATION_LOADED;
@@ -107,6 +108,8 @@ impl_ring_elements!(Workspace, Window, "floating_window");
impl Default for Workspace {
fn default() -> Self {
let default_layout = DEFAULT_WORKSPACE_LAYOUT.load();
Self {
name: None,
containers: Ring::default(),
@@ -115,7 +118,7 @@ impl Default for Workspace {
maximized_window_restore_idx: None,
monocle_container_restore_idx: None,
floating_windows: Ring::default(),
layout: Layout::Default(DefaultLayout::BSP),
layout: Layout::Default(default_layout.unwrap_or(DefaultLayout::BSP)),
layout_options: None,
layout_rules: vec![],
layout_flip: None,
@@ -123,7 +126,7 @@ impl Default for Workspace {
container_padding: Option::from(DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst)),
latest_layout: vec![],
resize_dimensions: vec![],
tile: true,
tile: default_layout.is_some(),
work_area_offset: None,
apply_window_based_work_area_offset: true,
window_container_behaviour: None,

View File

@@ -21,7 +21,7 @@ miette = { version = "7", features = ["fancy"] }
open = "5"
paste = { workspace = true }
powershell_script = "1.0"
reqwest = { version = "0.13", features = ["blocking"] }
reqwest = { version = "0.12", features = ["blocking"] }
schemars = { workspace = true, optional = true }
serde = { workspace = true }
serde_json = { workspace = true }
@@ -33,7 +33,7 @@ win-msgbox = "0.2"
windows = { workspace = true }
[build-dependencies]
reqwest = { version = "0.13", features = ["blocking"] }
reqwest = { version = "0.12", features = ["blocking"] }
shadow-rs = { workspace = true }
[features]

View File

@@ -68,7 +68,7 @@
"null"
],
"format": "float",
"default": 1.399999976158142
"default": 1.4
},
"left_widgets": {
"description": "Left side widgets (ordered left-to-right)",
@@ -99,7 +99,15 @@
},
"monitor": {
"description": "The monitor index or the full monitor options",
"$ref": "#/$defs/MonitorConfigOrIndex"
"anyOf": [
{
"$ref": "#/$defs/MonitorConfigOrIndex"
},
{
"type": "null"
}
],
"default": 0
},
"mouse": {
"description": "Options for mouse interaction on the bar",
@@ -174,7 +182,6 @@
}
},
"required": [
"monitor",
"left_widgets",
"right_widgets"
],

View File

@@ -152,6 +152,18 @@
"format": "int32",
"default": 10
},
"default_workspace_layout": {
"description": "Global default workspace layout for new workspaces",
"anyOf": [
{
"$ref": "#/$defs/DefaultLayout"
},
{
"type": "null"
}
],
"default": "BSP"
},
"default_workspace_padding": {
"description": "Global default workspace padding",
"type": [