diff --git a/bin/omarchy-hyprland-window-transparency-toggle b/bin/omarchy-hyprland-window-transparency-toggle index 1235969d..6f2a7916 100755 --- a/bin/omarchy-hyprland-window-transparency-toggle +++ b/bin/omarchy-hyprland-window-transparency-toggle @@ -2,6 +2,18 @@ # omarchy:summary=Toggles transparency for the currently focused window. -addr=$(hyprctl activewindow -j | jq -r '.address') -hyprctl dispatch "hl.dsp.window.set_prop({ window = \"address:$addr\", prop = \"opaque\", value = \"toggle\" })" >/dev/null 2>&1 || \ - hyprctl dispatch setprop "address:$addr" opaque toggle +set -euo pipefail + +addr=$(hyprctl activewindow -j | jq -r '.address // empty') +[[ -n $addr ]] || exit 0 + +window="address:$addr" +opacity=$(hyprctl getprop "$window" opacity) +inactive_opacity=$(hyprctl getprop "$window" opacity_inactive) + +if [[ $opacity == "1" && $inactive_opacity == "1" ]]; then + hyprctl dispatch "hl.dsp.window.tag({ window = \"$window\", tag = \"+transparent\" })" + hyprctl dispatch "hl.dsp.window.set_prop({ window = \"$window\", prop = \"opaque\", value = \"false\" })" +else + hyprctl dispatch "hl.dsp.window.set_prop({ window = \"$window\", prop = \"opaque\", value = \"toggle\" })" +fi diff --git a/config/hypr/looknfeel.lua b/config/hypr/looknfeel.lua index a86d053e..f689e049 100644 --- a/config/hypr/looknfeel.lua +++ b/config/hypr/looknfeel.lua @@ -1,5 +1,10 @@ -- Change the default Omarchy look'n'feel. +-- Opt another application in to Omarchy's standard transparency. +-- Find its class with: hyprctl clients +-- o.transparent_window("my-app") +-- o.transparent_window("my-app", "0.9 0.85") -- Custom active/inactive opacity. + -- https://wiki.hypr.land/Configuring/Basics/Variables/#general -- hl.config({ -- general = { diff --git a/default/hypr/apps/browser.lua b/default/hypr/apps/browser.lua index 22df5577..613ba64e 100644 --- a/default/hypr/apps/browser.lua +++ b/default/hypr/apps/browser.lua @@ -1,12 +1,10 @@ -- Browser tags and styling. o.window("((google-)?[cC]hrom(e|ium)|[bB]rave-browser|[mM]icrosoft-edge|Vivaldi-stable|helium)", { tag = "+chromium-based-browser" }) o.window("([fF]irefox|zen|librewolf)", { tag = "+firefox-based-browser" }) -o.window({ tag = "chromium-based-browser" }, { tag = "-default-opacity", tile = true, opacity = "1.0 0.985" }) -o.window({ tag = "firefox-based-browser" }, { tag = "-default-opacity", opacity = "1.0 0.985" }) +o.window({ tag = "chromium-based-browser" }, { tile = true }) --- Video apps: remove chromium browser tag so they don't get opacity applied. +-- Video apps: remove the chromium browser tag so they can float. o.window("(^.+-youtube\\.com__.*$|^.+-app\\.zoom\\.us__wc_home.*$)", { tag = "-chromium-based-browser" }) -o.window("(^.+-youtube\\.com__.*$|^.+-app\\.zoom\\.us__wc_home.*$)", { tag = "-default-opacity" }) -- Hide screen sharing notification windows. o.window({ title = ".*is sharing.*" }, { workspace = "special silent" }) diff --git a/default/hypr/apps/davinci-resolve.lua b/default/hypr/apps/davinci-resolve.lua index 734d2b8f..f6301ed9 100644 --- a/default/hypr/apps/davinci-resolve.lua +++ b/default/hypr/apps/davinci-resolve.lua @@ -1,12 +1,9 @@ --- DaVinci Resolve window focus handling. Kept fully opaque: the default --- translucency distorts colour-critical grading work. +-- DaVinci Resolve window focus handling. o.window(".*[Rr]esolve.*", { float = true, stay_focused = true, -- Prevent modal dialog pointer warps when focus follows the mouse. no_follow_mouse = true, - tag = "-default-opacity", - opacity = "1 1", }) o.window({ class = ".*[Rr]esolve.*", title = "^DaVinci Resolve( Studio)? - .+$" }, { fullscreen = true }) diff --git a/default/hypr/apps/hermes.lua b/default/hypr/apps/hermes.lua index a7ad307f..09b0c8bc 100644 --- a/default/hypr/apps/hermes.lua +++ b/default/hypr/apps/hermes.lua @@ -1,7 +1,5 @@ -- Hermes Desktop's frameless HUD manages its own geometry. o.window({ class = "^Hermes$", title = "^Hermes HUD$" }, { - tag = "-default-opacity", float = true, border_size = 0, - opacity = "1 1", }) diff --git a/default/hypr/apps/localsend.lua b/default/hypr/apps/localsend.lua index 6e7ec132..6bd5fb14 100644 --- a/default/hypr/apps/localsend.lua +++ b/default/hypr/apps/localsend.lua @@ -1,3 +1,5 @@ -- Float LocalSend and fzf file picker. -o.window("(Share|localsend)", { float = true, center = true }) -o.window("localsend", { size = { 1100, 700 } }) +local localsend = "(org\\.localsend\\.)?localsend(_app)?" +o.window("Share", { float = true, center = true }) +o.window(localsend, { float = true, center = true, size = { 1100, 700 } }) +o.transparent_window(localsend) diff --git a/default/hypr/apps/pip.lua b/default/hypr/apps/pip.lua index 791152da..c6b390de 100644 --- a/default/hypr/apps/pip.lua +++ b/default/hypr/apps/pip.lua @@ -1,24 +1,20 @@ -- Picture-in-picture overlays. o.window({ title = "(Picture.?in.?[Pp]icture)" }, { tag = "+pip" }) o.window({ tag = "pip" }, { - tag = "-default-opacity", float = true, pin = true, size = { 600, 338 }, keep_aspect_ratio = true, border_size = 0, - opacity = "1 1", move = { "(monitor_w-window_w-40)", "(monitor_h*0.04)" }, }) -- Google Meet PiP uses the meeting title instead of "Picture-in-Picture". o.window({ tag = "chromium-based-browser", title = "^Meet - .+" }, { - tag = "-default-opacity", float = true, pin = true, size = { 600, 338 }, keep_aspect_ratio = true, border_size = 0, - opacity = "1 1", move = { "(monitor_w-window_w-40)", "(monitor_h-window_h-40)" }, }) diff --git a/default/hypr/apps/qemu.lua b/default/hypr/apps/qemu.lua deleted file mode 100644 index c919bcec..00000000 --- a/default/hypr/apps/qemu.lua +++ /dev/null @@ -1 +0,0 @@ -o.window("qemu", { tag = "-default-opacity", opacity = "1 1" }) diff --git a/default/hypr/apps/retroarch.lua b/default/hypr/apps/retroarch.lua index 0bc8a218..2635652e 100644 --- a/default/hypr/apps/retroarch.lua +++ b/default/hypr/apps/retroarch.lua @@ -1,6 +1,4 @@ o.window("com.libretro.RetroArch", { fullscreen = true, - tag = "-default-opacity", - opacity = "1 1", idle_inhibit = "fullscreen", }) diff --git a/default/hypr/apps/steam.lua b/default/hypr/apps/steam.lua index 152aff64..3ed8d0bc 100644 --- a/default/hypr/apps/steam.lua +++ b/default/hypr/apps/steam.lua @@ -1,4 +1,3 @@ o.window("steam", { float = true, idle_inhibit = "fullscreen" }) o.window({ class = "steam", title = "Steam" }, { center = true, size = { 1100, 700 } }) -o.window("steam.*", { tag = "-default-opacity", opacity = "1 1" }) o.window({ class = "steam", title = "Friends List" }, { size = { 460, 800 } }) diff --git a/default/hypr/apps/system.lua b/default/hypr/apps/system.lua index 043fe12c..6a5385e1 100644 --- a/default/hypr/apps/system.lua +++ b/default/hypr/apps/system.lua @@ -37,20 +37,6 @@ o.window("org.omarchy.screensaver", { animation = "slide" }) -- where its fullscreen rule cannot take fullscreen from a window. o.window("org.omarchy.screensaver", { workspace = "special:screensaver silent" }) --- No transparency on media windows. -o.window( - "^(zoom|vlc|mpv|org.kde.kdenlive|com.obsproject.Studio|com.github.PintaProject.Pinta|imv|org.gnome.NautilusPreviewer)$", - { - tag = "-default-opacity", - } -) -o.window( - "^(zoom|vlc|mpv|org.kde.kdenlive|com.obsproject.Studio|com.github.PintaProject.Pinta|imv|org.gnome.NautilusPreviewer)$", - { - opacity = "1 1", - } -) - -- Popped window rounding. o.window({ tag = "pop" }, { rounding = 8 }) diff --git a/default/hypr/apps/webcam-overlay.lua b/default/hypr/apps/webcam-overlay.lua index a8312e5f..4d4e5332 100644 --- a/default/hypr/apps/webcam-overlay.lua +++ b/default/hypr/apps/webcam-overlay.lua @@ -16,10 +16,8 @@ o.window("^WebcamOverlay-large$", { -- The dedicated app id keeps this out of mpv's generic centered floating rules, -- so the camera appears at its final corner position. o.window({ class = "^WebcamOverlay-(small|medium|large)$", title = "^WebcamOverlay$" }, { - tag = "-default-opacity", float = true, pin = true, no_initial_focus = true, no_dim = true, - opacity = "1 1", }) diff --git a/default/hypr/apps/windows-vm.lua b/default/hypr/apps/windows-vm.lua deleted file mode 100644 index a31bf882..00000000 --- a/default/hypr/apps/windows-vm.lua +++ /dev/null @@ -1,5 +0,0 @@ --- Keep the Windows VM display opaque instead of applying the default window opacity. -o.window({ class = "^xfreerdp$", title = "^Windows VM - Omarchy$" }, { - tag = "-default-opacity", - opacity = "1 1", -}) diff --git a/default/hypr/bindings/utilities.lua b/default/hypr/bindings/utilities.lua index 7aa23a4a..6ae4ef50 100644 --- a/default/hypr/bindings/utilities.lua +++ b/default/hypr/bindings/utilities.lua @@ -16,7 +16,7 @@ o.bind("XF86Calculator", "Calculator", "omacalc") o.bind_toggle("SUPER + SHIFT + SPACE", "Toggle top bar", "bar") o.bind("SUPER + CTRL + SPACE", "Background switcher", { menu = "background" }) o.bind("SUPER + SHIFT + CTRL + SPACE", "Theme menu", { menu = "theme" }) -o.bind("SUPER + BACKSPACE", "Toggle window transparency", "omarchy-hyprland-window-transparency-toggle") +o.bind("SUPER + BACKSPACE", "Toggle transparency on a window", "omarchy-hyprland-window-transparency-toggle") o.bind("SUPER + SHIFT + BACKSPACE", "Toggle window gaps", "omarchy-hyprland-window-gaps-toggle") o.bind("SUPER + CTRL + BACKSPACE", "Toggle single-window square aspect", "omarchy-hyprland-window-single-square-aspect-toggle") o.bind_toggle("SUPER + CTRL + ALT + F", "Toggle full screen desktop", "fullscreen-desktop") diff --git a/default/hypr/helpers.lua b/default/hypr/helpers.lua index 05f8f91e..64e13c75 100644 --- a/default/hypr/helpers.lua +++ b/default/hypr/helpers.lua @@ -223,3 +223,8 @@ function o.window(match, rules) hl.window_rule(rules) end + +-- Opt a window in to Omarchy's standard active/inactive transparency. +function o.transparent_window(match, opacity) + o.window(match, { opacity = opacity or "0.985 0.96" }) +end diff --git a/default/hypr/windows.lua b/default/hypr/windows.lua index c08487dd..88040a9a 100644 --- a/default/hypr/windows.lua +++ b/default/hypr/windows.lua @@ -2,9 +2,6 @@ o.window(".*", { suppress_event = "maximize" }) --- Tag all windows for default opacity (apps can override with -default-opacity tag). -o.window(".*", { tag = "+default-opacity" }) - -- Fix some dragging issues with XWayland. o.window( { @@ -18,8 +15,12 @@ o.window( { no_focus = true } ) --- App-specific tweaks (may remove default-opacity tag). +-- App-specific tweaks and terminal tags. require("default.hypr.apps") --- Apply default opacity after apps have had a chance to opt out. -o.window({ tag = "default-opacity" }, { opacity = "0.985 0.96" }) +-- Terminals (including TUIs) and these desktop apps opt in to transparency. +o.transparent_window({ tag = "terminal" }) +o.transparent_window("(omawrite|1[pP]assword|com\\.onepassword\\.OnePassword|org\\.gnome\\.Nautilus)") + +-- The transparency hotkey opts otherwise opaque windows in with this tag. +o.transparent_window({ tag = "transparent" }) diff --git a/migrations/1791123731.sh b/migrations/1791123731.sh new file mode 100644 index 00000000..32a739c8 --- /dev/null +++ b/migrations/1791123731.sh @@ -0,0 +1,12 @@ +echo "Add an example for opting applications in to transparency" + +looknfeel="${XDG_CONFIG_HOME:-$HOME/.config}/hypr/looknfeel.lua" +if [[ -f $looknfeel ]] && ! rg -Fq 'o.transparent_window("my-app")' "$looknfeel"; then + cat >>"$looknfeel" <<'LUA' + +-- Opt another application in to Omarchy's standard transparency. +-- Find its class with: hyprctl clients +-- o.transparent_window("my-app") +-- o.transparent_window("my-app", "0.9 0.85") -- Custom active/inactive opacity. +LUA +fi diff --git a/test/shell.d/windows-vm-test.sh b/test/shell.d/windows-vm-test.sh index e2a04dae..e9d200c5 100644 --- a/test/shell.d/windows-vm-test.sh +++ b/test/shell.d/windows-vm-test.sh @@ -5,7 +5,6 @@ set -euo pipefail source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" windows_vm_command="$ROOT/bin/omarchy-windows-vm" -windows_vm_rules="$ROOT/default/hypr/apps/windows-vm.lua" rg -q '^ restart: "no"$' "$windows_vm_command" || fail "Windows VM uses manual startup by default" @@ -16,14 +15,7 @@ if rg -q '^ restart: unless-stopped$' "$windows_vm_command"; then fi pass "Windows VM does not restart automatically at boot" -# Tolerate either shell quoting of the argument -- what must not drift is the -# title itself, since the Hyprland rule below matches on it. +# Tolerate either shell quoting of the argument. rg -q 'title:"?Windows VM - Omarchy"' "$windows_vm_command" || fail "Windows VM launches FreeRDP with its expected title" -rg -q 'class = "\^xfreerdp\$", title = "\^Windows VM - Omarchy\$"' "$windows_vm_rules" || - fail "Windows VM opacity rule targets its FreeRDP window" -rg -q 'tag = "-default-opacity"' "$windows_vm_rules" || - fail "Windows VM opts out of default opacity" -rg -q 'opacity = "1 1"' "$windows_vm_rules" || - fail "Windows VM stays fully opaque" -pass "Windows VM stays fully opaque" +pass "Windows VM launches FreeRDP with its expected title" diff --git a/themes/kanagawa/hyprland.lua b/themes/kanagawa/hyprland.lua index d0f190fc..e6377650 100644 --- a/themes/kanagawa/hyprland.lua +++ b/themes/kanagawa/hyprland.lua @@ -15,4 +15,4 @@ hl.config({ }) -- Kanagawa backdrop is too strong for default opacity. -o.window({ tag = "terminal" }, { opacity = "0.99 0.985" }) +o.transparent_window({ tag = "terminal" }, "0.99 0.985")