From 49320a4c86804560f6976f7ee129e9fdcbcce43c Mon Sep 17 00:00:00 2001 From: Yocraft-2000 <304616174+Yocraft-2000@users.noreply.github.com> Date: Sat, 22 Aug 2026 02:10:58 +0200 Subject: [PATCH 1/5] feat: add default_maximize_to_edges window rule --- src/config/config.cpp | 1 + src/config/config.h | 3 +++ src/config/resolve.cpp | 5 ++++- src/view/view.cpp | 14 ++++++++++++-- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/config/config.cpp b/src/config/config.cpp index 7aa35fad..192f3804 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -1102,6 +1102,7 @@ namespace umbriel { keys.boolean("default_floating", rule.defaultFloating) .boolean("default_fullscreen", rule.defaultFullscreen) + .boolean("default_maximize_to_edges", rule.defaultMaximizeToEdges) .boolean("default_maximize", rule.defaultMaximize) .boolean("default_focused", rule.defaultFocused) .boolean("default_pinned", rule.defaultPinned) diff --git a/src/config/config.h b/src/config/config.h index 877b2168..af275141 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -178,6 +178,7 @@ namespace umbriel { std::optional defaultWidth; // column width fraction override std::optional defaultWorkspace; // 1-64 std::optional defaultFullscreen; + std::optional defaultMaximizeToEdges; std::optional defaultMaximize; std::optional defaultFocused; std::optional defaultPinned; @@ -203,6 +204,7 @@ namespace umbriel { && defaultWidth == other.defaultWidth && defaultWorkspace == other.defaultWorkspace && defaultFullscreen == other.defaultFullscreen + && defaultMaximizeToEdges == other.defaultMaximizeToEdges && defaultMaximize == other.defaultMaximize && defaultFocused == other.defaultFocused && defaultPinned == other.defaultPinned @@ -226,6 +228,7 @@ namespace umbriel { std::optional defaultWidth; std::optional defaultWorkspace; std::optional defaultFullscreen; + std::optional defaultMaximizeToEdges; std::optional defaultMaximize; std::optional defaultFocused; std::optional defaultPinned; diff --git a/src/config/resolve.cpp b/src/config/resolve.cpp index 3ab8e718..ba01db49 100644 --- a/src/config/resolve.cpp +++ b/src/config/resolve.cpp @@ -144,7 +144,10 @@ namespace umbriel { resolved.defaultWorkspace = rule.defaultWorkspace; } if (rule.defaultFullscreen) { - resolved.defaultFullscreen = rule.defaultFullscreen; + resolved.defaultFullscreen = rule.defaultFullscreen; + } + if (rule.defaultMaximizeToEdges) { + resolved.defaultMaximizeToEdges = rule.defaultMaximizeToEdges; } if (rule.defaultMaximize) { resolved.defaultMaximize = rule.defaultMaximize; diff --git a/src/view/view.cpp b/src/view/view.cpp index 188579a8..b53da32a 100644 --- a/src/view/view.cpp +++ b/src/view/view.cpp @@ -1545,7 +1545,11 @@ namespace umbriel { // Fullscreen after workspace + focus so the view lands in the right place. if (rule.defaultFullscreen && *rule.defaultFullscreen) { - setFullscreen(true); + setFullscreen(true); + } + + if (rule.defaultMaximizeToEdges && *rule.defaultMaximizeToEdges) { + setMaximizedToEdges(true); } if (Overview* overview = m_server->overview(); overview != nullptr && overview->active()) { @@ -2391,7 +2395,13 @@ namespace umbriel { if (changedInitialRule(rule.defaultFullscreen, initiallyApplied.defaultFullscreen) && *rule.defaultFullscreen && !m_toplevel->scheduled.fullscreen) { - setFullscreen(true); + setFullscreen(true); + } + + if (changedInitialRule(rule.defaultMaximizeToEdges, initiallyApplied.defaultMaximizeToEdges) + && *rule.defaultMaximizeToEdges + && !m_maximizedToEdges) { + setMaximizedToEdges(true); } if (changedInitialRule(rule.defaultMaximize, initiallyApplied.defaultMaximize) From 4331404e66fbc84d378aeeef633732ede30f3a04 Mon Sep 17 00:00:00 2001 From: Yocraft-2000 <304616174+Yocraft-2000@users.noreply.github.com> Date: Sat, 22 Aug 2026 02:28:01 +0200 Subject: [PATCH 2/5] docs: document default_maximize_to_edges window rule --- docs/user/rules.md | 1 + src/view/view.cpp | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/user/rules.md b/docs/user/rules.md index 61195bee..8491bb3d 100644 --- a/docs/user/rules.md +++ b/docs/user/rules.md @@ -46,6 +46,7 @@ opening settings do not overwrite user changes made in the meantime. | `default_width` | float | Scrolling only. Lane scroll-axis extent fraction (0.1-1.0), which is height on a vertical workspace. Gap-aware: fractions that sum to 1 tile exactly. Overrides `layout.scrolling.default_width_fraction`. Dragging the lane within or between scrolling workspaces retains its current fraction. Ignored in dwindle. | | `default_workspace` | int | Place on workspace N from 1 to 64. On dynamic outputs, values beyond the current count clamp to the last workspace. | | `default_fullscreen` | bool | Open in fullscreen. | +| `default_maximize_to_edges` | bool | Explicitly open maximized to edges, expanding the window to the usable area's edges without gaps or borders. Layer-shell exclusive zones stay visible. Takes precedence over `default_maximize`; when combined with `default_fullscreen` the window opens fullscreen and returns to maximized to edges once fullscreen is cleared. | | `default_maximize` | bool | Explicitly open maximized. Umbriel ignores maximized state restored by a client while it opens unless `general.honor_restored_maximize` is enabled, but always honors later client requests. Tiled windows expand their column to full width without changing the layout; floating windows fill the usable area. | | `default_focused` | bool | Take focus when opening, switching to the window's workspace when needed. Defaults to `true`; set to `false` to preserve the existing focus and workspace. | | `default_pinned` | bool | Open pinned above regular windows and keep the window visible across workspace changes. Pinning makes a tiled window floating. | diff --git a/src/view/view.cpp b/src/view/view.cpp index b53da32a..b18c672c 100644 --- a/src/view/view.cpp +++ b/src/view/view.cpp @@ -1543,15 +1543,17 @@ namespace umbriel { setMaximized(true); } - // Fullscreen after workspace + focus so the view lands in the right place. - if (rule.defaultFullscreen && *rule.defaultFullscreen) { - setFullscreen(true); - } - + // After default_maximize so maximize-to-edges wins the column, but before + // fullscreen: setFullscreen leaves and restores the maximize-to-edges state. if (rule.defaultMaximizeToEdges && *rule.defaultMaximizeToEdges) { setMaximizedToEdges(true); } + // Fullscreen after workspace + focus so the view lands in the right place. + if (rule.defaultFullscreen && *rule.defaultFullscreen) { + setFullscreen(true); + } + if (Overview* overview = m_server->overview(); overview != nullptr && overview->active()) { overview->onViewMapped(this); } From 142d4cdce7d9377eba9da66edac9b44e2f141fd5 Mon Sep 17 00:00:00 2001 From: Yocraft-2000 <304616174+Yocraft-2000@users.noreply.github.com> Date: Sat, 22 Aug 2026 02:30:40 +0200 Subject: [PATCH 3/5] docs: update examples/config.toml --- examples/config.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/examples/config.toml b/examples/config.toml index 694ef7c0..536a9608 100644 --- a/examples/config.toml +++ b/examples/config.toml @@ -247,6 +247,14 @@ follows_mouse = false blur = true blur_optimized = true +# [[window_rule]] +# match.app_id = "^discord$" +# default_maximize_to_edges = true +# +# [[window_rule]] +# match.title = "^Discord Updater$" +# default_maximize_to_edges = false + [[window_rule]] match.app_id = "^dev.noctalia.Noctalia$" default_floating = true From 6af879d5bf288c9fb2319c881fe94b9e6b3b077e Mon Sep 17 00:00:00 2001 From: Yocraft-2000 <304616174+Yocraft-2000@users.noreply.github.com> Date: Sat, 22 Aug 2026 02:37:40 +0200 Subject: [PATCH 4/5] fix: run just format --- src/config/resolve.cpp | 2 +- src/view/view.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/resolve.cpp b/src/config/resolve.cpp index ba01db49..01c52e28 100644 --- a/src/config/resolve.cpp +++ b/src/config/resolve.cpp @@ -144,7 +144,7 @@ namespace umbriel { resolved.defaultWorkspace = rule.defaultWorkspace; } if (rule.defaultFullscreen) { - resolved.defaultFullscreen = rule.defaultFullscreen; + resolved.defaultFullscreen = rule.defaultFullscreen; } if (rule.defaultMaximizeToEdges) { resolved.defaultMaximizeToEdges = rule.defaultMaximizeToEdges; diff --git a/src/view/view.cpp b/src/view/view.cpp index b18c672c..3abca1f9 100644 --- a/src/view/view.cpp +++ b/src/view/view.cpp @@ -2397,7 +2397,7 @@ namespace umbriel { if (changedInitialRule(rule.defaultFullscreen, initiallyApplied.defaultFullscreen) && *rule.defaultFullscreen && !m_toplevel->scheduled.fullscreen) { - setFullscreen(true); + setFullscreen(true); } if (changedInitialRule(rule.defaultMaximizeToEdges, initiallyApplied.defaultMaximizeToEdges) From 5152139b4fd2550df7b6f8084669bced5dd9aaba Mon Sep 17 00:00:00 2001 From: Lemmy Date: Wed, 26 Aug 2026 00:49:27 -0400 Subject: [PATCH 5/5] test(rules): cover default_maximize_to_edges at map, refine example A --- examples/config.toml | 5 +- .../checks/151_initial_maximize_to_edges.sh | 76 +++++++++++++++++++ 2 files changed, 77 insertions(+), 4 deletions(-) create mode 100755 tests/harness/checks/151_initial_maximize_to_edges.sh diff --git a/examples/config.toml b/examples/config.toml index 536a9608..e40bcf47 100644 --- a/examples/config.toml +++ b/examples/config.toml @@ -247,13 +247,10 @@ follows_mouse = false blur = true blur_optimized = true +# Open a specific app maximized to the usable-area edges (no gaps or borders) # [[window_rule]] # match.app_id = "^discord$" # default_maximize_to_edges = true -# -# [[window_rule]] -# match.title = "^Discord Updater$" -# default_maximize_to_edges = false [[window_rule]] match.app_id = "^dev.noctalia.Noctalia$" diff --git a/tests/harness/checks/151_initial_maximize_to_edges.sh b/tests/harness/checks/151_initial_maximize_to_edges.sh new file mode 100755 index 00000000..efe87a26 --- /dev/null +++ b/tests/harness/checks/151_initial_maximize_to_edges.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# The default_maximize_to_edges window rule must expand a matching window to the +# usable-area edges at map time. A window with the same layout width fraction but +# no matching rule stays at that fraction, so the widening is attributable to the +# rule rather than to any unrelated full-width fallback. A solid client on the +# black headless output makes the presented width measurable. +set -euo pipefail + +readonly CLIENT="${UMBRIEL_UNMAP_CLIENT:-./build-debug/unmap-client}" +readonly BASELINE="$UMBRIEL_RUNTIME_DIR/initial-maximize-to-edges-baseline.png" +readonly RULED="$UMBRIEL_RUNTIME_DIR/initial-maximize-to-edges-ruled.png" + +presented_width() { + magick "$1" -alpha off -crop 1280x1+0+360 +repage -fuzz 2% \ + -fill black +opaque '#5577AA' -fill white -opaque '#5577AA' -colorspace gray \ + -format '%[fx:round(w*mean)]' info: +} + +spawn_client() { + local title=$1 log=$2 + "$CLIENT" "$title" 1280 720 > "$log" 2>&1 & + CLIENT_PID=$! + for _ in $(seq 60); do + grep -q '^mapped$' "$log" && return 0 + sleep 0.05 + done + echo "$title client never mapped: $(cat "$log")" + return 1 +} + +stop_client() { + kill -KILL "$CLIENT_PID" 2>/dev/null || true + wait "$CLIENT_PID" 2>/dev/null || true +} + +cat >> "$UMBRIEL_CONFIG" <<'EOF' + +[appearance] +animation_ms = 1 +border_width = 0 +outer_border_width = 0 +corner_radius = 0 + +[layout.scrolling] +default_width_fraction = 0.5 + +[[window_rule]] +match.title = "^edges-ruled$" +default_maximize_to_edges = true +EOF +"$UMBRIEL" msg config-reload > /dev/null + +# The unmatched window keeps the half-width fraction: the measurement baseline. +spawn_client edges-baseline "$UMBRIEL_RUNTIME_DIR/initial-maximize-to-edges-baseline.log" || exit 1 +sleep 0.3 +grim "$BASELINE" +baseline_width=$(presented_width "$BASELINE") +stop_client +if (( baseline_width > 900 )); then + echo "baseline window was not laid out at the half-width fraction: width=$baseline_width" + exit 1 +fi + +# Same fraction, but the rule maximizes it to the edges at map. +spawn_client edges-ruled "$UMBRIEL_RUNTIME_DIR/initial-maximize-to-edges-ruled.log" || exit 1 +sleep 0.3 +grim "$RULED" +ruled_width=$(presented_width "$RULED") +stop_client + +if (( ruled_width < 1100 || ruled_width - baseline_width < 400 )); then + echo "default_maximize_to_edges did not expand the window to the edges at map: baseline=$baseline_width ruled=$ruled_width" + exit 1 +fi + +echo "default_maximize_to_edges expanded the mapped window to the edges: $baseline_width -> $ruled_width"