Install Hyprland window rules for bar overlap and dialog pointer trap

Rebased onto the Omarchy 4 update (4857667), which the branch these commits
came from predated. Kept both sides:

- the O4 fixes stay (per-package pacman installs, gtk2 dropped, wrapper
  unsets QT_STYLE_OVERRIDE/QT_QPA_PLATFORMTHEME)
- the full-opacity block stays alongside the new rules, noted as redundant
  on Omarchy >= basecamp/omarchy#6382 but still needed on older 4.x builds

README renumbered accordingly and the "002 not fully verified" note dropped,
since both fixes are now verified end-to-end.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
28allday 2026-07-29 14:17:28 +01:00
parent c186dbbceb
commit eb019dc369
2 changed files with 234 additions and 0 deletions

View file

@ -24,6 +24,8 @@
# 7. Ensures legacy libcrypt.so.1 is available (Arch dropped it) # 7. Ensures legacy libcrypt.so.1 is available (Arch dropped it)
# 8. Installs desktop entries, icons, and udev rules # 8. Installs desktop entries, icons, and udev rules
# 9. Creates an XWayland wrapper script for Hyprland compatibility # 9. Creates an XWayland wrapper script for Hyprland compatibility
# 10. Installs Hyprland window rules so the Omarchy bar doesn't cover Resolve's
# menu bar, and Resolve's dialogs don't trap the pointer
# #
# Prerequisites: # Prerequisites:
# - Omarchy (Arch Linux) with NVIDIA drivers installed and working # - Omarchy (Arch Linux) with NVIDIA drivers installed and working
@ -463,6 +465,11 @@ sudo gtk-update-icon-cache -f /usr/share/icons/hicolor >/dev/null 2>&1 || true
# Only runs when an Omarchy 4 Lua config is detected; pre-4 installs never # Only runs when an Omarchy 4 Lua config is detected; pre-4 installs never
# applied global translucency, so they need nothing. Idempotent via the # applied global translucency, so they need nothing. Idempotent via the
# "resolve-full-opacity" marker. # "resolve-full-opacity" marker.
#
# UPSTREAM: fixed in Omarchy by basecamp/omarchy#6382, which added
# opacity = "1 1" to its own davinci-resolve.lua. This block is therefore
# redundant on current Omarchy and only still matters on Omarchy 4 builds
# predating that PR. Harmless either way — same rule, same value.
HYPR_LUA="${HOME}/.config/hypr/hyprland.lua" HYPR_LUA="${HOME}/.config/hypr/hyprland.lua"
if [[ -f "${HYPR_LUA}" ]] && grep -q 'default\.hypr\.omarchy' "${HYPR_LUA}"; then if [[ -f "${HYPR_LUA}" ]] && grep -q 'default\.hypr\.omarchy' "${HYPR_LUA}"; then
if grep -q 'resolve-full-opacity' "${HYPR_LUA}"; then if grep -q 'resolve-full-opacity' "${HYPR_LUA}"; then
@ -483,6 +490,130 @@ else
log "No Omarchy 4 Lua config detected, skipping opacity rule (not needed pre-4)" log "No Omarchy 4 Lua config detected, skipping opacity rule (not needed pre-4)"
fi fi
# ==================== Hyprland window rules ====================
#
# Two Omarchy-specific Resolve problems are fixed here, both by installing
# Hyprland window rules:
# 1. the status bar covers Resolve's menu bar (omarchy-upstream-fixes/001)
# 2. dialogs trap the pointer until Resolve dies (omarchy-upstream-fixes/002)
#
# UPSTREAM: both are local workarounds. If they land in Omarchy itself this
# block becomes redundant (harmless — the rules are byte-identical to the ones
# proposed upstream — but it can then be dropped).
#
# ---- 1. Bar overlap ----
#
# On Omarchy 4.x the status bar is a Quickshell layer-shell surface (namespace
# "omarchy-bar") sitting on Hyprland's "top" layer. It reserves a 26px
# exclusive zone at the top of the screen, which correctly pushes *tiled*
# windows down.
#
# Resolve is not tiled. Omarchy floats every Resolve window on purpose (see
# /usr/share/omarchy/default/hypr/apps/davinci-resolve.lua) because Resolve's
# many child windows tile badly. Floating windows are positioned by the client,
# and Resolve's XWayland main window places itself at 0,0 at the full monitor
# size — ignoring the reserved zone entirely. The bar then draws on top of
# Resolve's own menu bar (File / Edit / Trim / Timeline / Clip / ...), making
# it unclickable.
#
# THE FIX: Hyprland renders a fullscreen window ABOVE "top"-layer layer-shell
# surfaces, so opening Resolve's main window fullscreen puts it over the bar
# instead of under it. We install a Hyprland window rule that does exactly that.
#
# The rule is deliberately scoped by title. Every Resolve window shares the
# class "resolve", and only the main one may be fullscreened:
# splash screen -> title "resolve"
# project chooser -> title "Project Manager"
# main window -> title "DaVinci Resolve - <project name>"
# Requiring the " - <project>" suffix leaves the splash, the Project Manager
# and Resolve's modal dialogs floating at their natural size.
#
# ---- 2. Dialogs trapping the pointer ----
#
# Omarchy sets `stay_focused = true` on ".*[Rr]esolve.*" — every Resolve window.
# That rule exists for a good reason: Resolve is not Wayland-native, and its
# transient popups (Change Clip Duration, Normalize Audio Levels, ...) close as
# soon as the pointer leaves them unless focus is pinned. See
# https://github.com/hyprwm/Hyprland/discussions/12235
#
# But pinning EVERY Resolve window means that whenever two are visible at once,
# both demand focus and neither yields. Hyprland then flips focus back and
# forth between them and the dialog can't be dismissed — the pointer is stuck
# until Resolve is killed. Observed live, flipping between two windows:
# ACTIVE='Create New Project' windows=['Project Manager','Create New Project']
# ACTIVE='Project Manager' windows=['Project Manager','Create New Project']
# ACTIVE='Create New Project' windows=['Project Manager','Create New Project']
#
# THE FIX: keep the pin on the transient popups, but remove it from the two
# windows those popups open *over* — the main window and the Project Manager.
# At most one pinned window is then ever visible, so nothing can fight. A later,
# more specific rule overrides an earlier one (verified on Hyprland 0.56.1).
#
# Skipped if RESOLVE_NO_HYPR_RULES=1 (e.g. you prefer Resolve windowed, or you
# don't want the installer touching your Hyprland config at all).
if [[ "${RESOLVE_NO_HYPR_RULES:-0}" == "1" ]]; then
log "Skipping Hyprland window rules (RESOLVE_NO_HYPR_RULES=1)"
elif [[ ! -f "${HOME}/.config/hypr/hyprland.lua" ]]; then
# Omarchy 4.x configures Hyprland in Lua. Older Hyprland setups use
# hyprland.conf with a different, version-dependent rule syntax, so rather
# than write something unverified we tell the user what to add.
warn "No ${HOME}/.config/hypr/hyprland.lua found — skipping the Hyprland rules."
warn "If the bar covers Resolve's menu bar, or dialogs trap the pointer, add:"
warn " windowrulev2 = fullscreen, class:^(resolve)\$, title:^(DaVinci Resolve - .+)\$"
warn " windowrulev2 = stayfocused off, class:^(resolve)\$, title:^(DaVinci Resolve - .+|Project Manager)\$"
else
log "Installing Hyprland rules (bar overlap + dialog pointer trap)..."
RESOLVE_HYPR_MODULE="${HOME}/.config/hypr/davinci-resolve.lua"
# Kept byte-identical to the rules proposed upstream in
# omarchy-upstream-fixes/001 and /002, including comment wording and Omarchy's
# house style (max 3-line comments in default/hypr/apps/), so that if they land
# upstream this file is a redundant duplicate rather than a divergent rule.
cat > "${RESOLVE_HYPR_MODULE}" <<'EOF'
-- DaVinci Resolve — installed by Omarchy_resolve_v2.sh.
-- Delete this file and the require() line in hyprland.lua to revert.
-- Resolve's floating main window ignores the bar's reserved zone, so the bar
-- covers its menu bar; fullscreen renders above top-layer surfaces. Scoped by
-- title so the splash and Project Manager keep their natural size.
o.window({ class = ".*[Rr]esolve.*", title = "^DaVinci Resolve - .+$" }, { fullscreen = true })
-- Resolve's transient popups close on mouse-out unless focus is pinned, but
-- pinning the windows they open *over* makes two stay_focused windows fight
-- and traps the pointer until Resolve is killed. Unpin the parents only.
o.window({ class = ".*[Rr]esolve.*", title = "^(DaVinci Resolve - .+|Project Manager)$" }, { stay_focused = false })
EOF
# Omarchy's hyprland.lua require()s each user module explicitly — there is no
# auto-loaded drop-in directory. package.path includes ~/.config/?.lua, so
# "hypr.davinci-resolve" resolves to the file written above. Only append the
# require if it isn't already there, so re-running this installer is safe.
RESOLVE_HYPR_MAIN="${HOME}/.config/hypr/hyprland.lua"
if grep -q 'require("hypr.davinci-resolve")' "${RESOLVE_HYPR_MAIN}"; then
log " require(\"hypr.davinci-resolve\") already present in hyprland.lua"
else
cp "${RESOLVE_HYPR_MAIN}" "${RESOLVE_HYPR_MAIN}.bak.$(date +%s)"
cat >> "${RESOLVE_HYPR_MAIN}" <<'EOF'
-- Open DaVinci Resolve's main window over the Omarchy bar instead of under it.
require("hypr.davinci-resolve")
EOF
log " Added require to hyprland.lua (backup .bak.<timestamp> created)"
fi
# Apply immediately if Hyprland is running, and surface any config error
# rather than letting it fail silently until the next login.
if command -v hyprctl >/dev/null 2>&1 && hyprctl version >/dev/null 2>&1; then
hyprctl reload >/dev/null 2>&1 || true
HYPR_ERRORS="$(hyprctl configerrors 2>/dev/null || true)"
if [[ -n "${HYPR_ERRORS}" && "${HYPR_ERRORS}" != "no errors" ]]; then
warn " Hyprland reported config errors after reload:"
warn "${HYPR_ERRORS}"
else
log " Reloaded Hyprland (no config errors)"
fi
fi
fi
# ==================== Audio backend fix (DeckLink → ALSA) ==================== # ==================== Audio backend fix (DeckLink → ALSA) ====================
# #
# Resolve ships with `Local.Audio.Type = DeckLink` as the default in its # Resolve ships with `Local.Audio.Type = DeckLink` as the default in its
@ -644,7 +775,9 @@ echo
echo "✅ DaVinci Resolve installed to /opt/resolve" echo "✅ DaVinci Resolve installed to /opt/resolve"
echo " (vendor libc++ kept; libcrypt.so.1 ensured; audio backend → ALSA + snd-aloop)" echo " (vendor libc++ kept; libcrypt.so.1 ensured; audio backend → ALSA + snd-aloop)"
echo " (Omarchy 4: full-opacity window rule added — grading needs opaque windows)" echo " (Omarchy 4: full-opacity window rule added — grading needs opaque windows)"
echo " Hyprland rules installed: Resolve sits over the bar; dialogs no longer trap the pointer"
echo " Launch from your app menu, or run: resolve-nvidia-open" echo " Launch from your app menu, or run: resolve-nvidia-open"
echo " Skip snd-aloop module setup: RESOLVE_NO_ALOOP=1 ./Omarchy_resolve_v2.sh" echo " Skip snd-aloop module setup: RESOLVE_NO_ALOOP=1 ./Omarchy_resolve_v2.sh"
echo " Skip the Hyprland window rules: RESOLVE_NO_HYPR_RULES=1 ./Omarchy_resolve_v2.sh"
echo " Logs: ~/.local/share/DaVinciResolve/logs/ResolveDebug.txt" echo " Logs: ~/.local/share/DaVinciResolve/logs/ResolveDebug.txt"
echo echo

101
README.md
View file

@ -161,6 +161,78 @@ Idempotent; skipped entirely on pre-4 installs, which never applied global
translucency. To remove it, delete the `resolve-full-opacity` block from translucency. To remove it, delete the `resolve-full-opacity` block from
`~/.config/hypr/hyprland.lua`. `~/.config/hypr/hyprland.lua`.
This one has since landed upstream ([basecamp/omarchy#6382](https://github.com/basecamp/omarchy/pull/6382)),
so on current Omarchy the rule is a harmless duplicate of Omarchy's own; it
still matters on Omarchy 4 builds predating that PR.
### 9. Omarchy Bar Overlap Fix
On Omarchy 4.x, Resolve launches with its own menu bar (File / Edit / Trim /
Timeline / …) hidden underneath the Omarchy status bar, so those menus can't be
clicked.
The Omarchy bar is a layer-shell surface on Hyprland's `top` layer that reserves
26px at the top of the screen. That reserved space only constrains *tiled*
windows — and Omarchy deliberately floats every Resolve window, because
Resolve's many child windows tile badly. Floating windows are positioned by the
client, and Resolve's XWayland main window places itself at 0,0 at the full
monitor size, ignoring the reserved zone. The bar then draws on top of it.
Hyprland renders a fullscreen window *above* `top`-layer surfaces, so the script
installs a Hyprland rule that opens Resolve's main window fullscreen — putting
Resolve over the bar instead of under it. The bar isn't hidden or killed, and
behaves normally again as soon as you leave fullscreen or focus another window.
The rule is scoped by window title, because every Resolve window shares the same
class and only the main one may be fullscreened:
| window | title | fullscreened? |
|-----------------|------------------------------------|---------------|
| splash screen | `resolve` | no |
| project chooser | `Project Manager` | no |
| modal dialogs | e.g. `Preferences` | no |
| main window | `DaVinci Resolve - <project name>` | yes |
Two files are written (`~/.config/hypr/davinci-resolve.lua` and one `require`
line appended to `~/.config/hypr/hyprland.lua`), then Hyprland is reloaded.
Re-running the installer won't duplicate the `require`, and `hyprland.lua` is
backed up before it's touched.
### 10. Dialog Pointer Trap Fix
Opening Resolve's **Preferences** or **Project Manager** could trap the pointer:
the dialog couldn't be dismissed, nothing else could be clicked, and quitting
Resolve was the only way out.
Omarchy sets `stay_focused = true` on every Resolve window. That rule exists for
a good reason — Resolve isn't Wayland-native, so its transient popups (Change
Clip Duration, Normalize Audio Levels…) close as soon as the pointer leaves them
unless focus is pinned ([Hyprland #12235](https://github.com/hyprwm/Hyprland/discussions/12235)).
Deleting it outright would bring that problem back.
The trouble is it's applied to *all* Resolve windows. When two are visible at
once, both demand focus and neither yields, so Hyprland flips between them
forever. Captured live:
```
ACTIVE='Create New Project' windows=['Project Manager', 'Create New Project']
ACTIVE='Project Manager' windows=['Project Manager', 'Create New Project']
ACTIVE='Create New Project' windows=['Project Manager', 'Create New Project']
```
The script narrows the rule instead of removing it: the pin stays on the
transient popups, and comes off the two windows those popups open *over* — the
main window and the Project Manager. At most one pinned window is ever visible,
so nothing can fight, and every window that relied on the pin still has it.
Set `RESOLVE_NO_HYPR_RULES=1` to skip both Hyprland rules entirely.
> Both of these belong upstream in Omarchy rather than in this installer — see
> [`omarchy-upstream-fixes/`](omarchy-upstream-fixes/) for the ready-to-raise
> patches. Both are verified end-to-end; fix 002's caveats section lists the
> known limitation (its parent list is a blocklist) and an open design question
> for a maintainer.
## Files Installed ## Files Installed
### Application ### Application
@ -178,6 +250,13 @@ translucency. To remove it, delete the `resolve-full-opacity` block from
| `/usr/local/bin/resolve-nvidia-open` | XWayland wrapper (main launcher) | | `/usr/local/bin/resolve-nvidia-open` | XWayland wrapper (main launcher) |
| `/usr/bin/davinci-resolve` | Convenience symlink to wrapper | | `/usr/bin/davinci-resolve` | Convenience symlink to wrapper |
### Hyprland Rules
| Path | Purpose |
|------|---------|
| `~/.config/hypr/davinci-resolve.lua` | Opens Resolve's main window over the Omarchy bar; stops dialogs trapping the pointer |
| `~/.config/hypr/hyprland.lua` | One `require("hypr.davinci-resolve")` line appended, plus the `resolve-full-opacity` rule (§8) |
### Desktop Entries ### Desktop Entries
| Path | Purpose | | Path | Purpose |
@ -221,6 +300,22 @@ RESOLVE_NO_ALOOP=1 ./Omarchy_resolve_v2.sh
This skips the `modprobe snd-aloop`, the `/etc/modules-load.d/` entry, and This skips the `modprobe snd-aloop`, the `/etc/modules-load.d/` entry, and
the PipeWire loopback bridge. The DeckLink → ALSA config patch still runs. the PipeWire loopback bridge. The DeckLink → ALSA config patch still runs.
### Skip the Hyprland Window Rules
If you would rather the installer left your Hyprland config alone entirely:
```bash
RESOLVE_NO_HYPR_RULES=1 ./Omarchy_resolve_v2.sh
```
Neither `~/.config/hypr/davinci-resolve.lua` nor the `require` line is written.
The separate full-opacity rule (§8) is not covered by this switch — remove its
`resolve-full-opacity` block from `hyprland.lua` if you don't want it either.
The installer also skips the rules
automatically (with a warning showing the equivalent `windowrulev2` line) if
`~/.config/hypr/hyprland.lua` doesn't exist — i.e. on non-Omarchy-4.x setups
that still use `hyprland.conf`.
### Hybrid GPU Laptops (Optimus) ### Hybrid GPU Laptops (Optimus)
If you have an Intel iGPU + NVIDIA dGPU, edit the wrapper to force Resolve onto the NVIDIA GPU: If you have an Intel iGPU + NVIDIA dGPU, edit the wrapper to force Resolve onto the NVIDIA GPU:
@ -350,6 +445,12 @@ sudo rm -rf /opt/resolve
sudo rm -f /usr/local/bin/resolve-nvidia-open sudo rm -f /usr/local/bin/resolve-nvidia-open
sudo rm -f /usr/bin/davinci-resolve sudo rm -f /usr/bin/davinci-resolve
# Remove the Hyprland window rules (bar overlap + pointer trap), then reload
rm -f ~/.config/hypr/davinci-resolve.lua
sed -i '/require("hypr.davinci-resolve")/d' ~/.config/hypr/hyprland.lua
sed -i '/Open DaVinci Resolve.s main window over the Omarchy bar/d' ~/.config/hypr/hyprland.lua
hyprctl reload
# Remove desktop entries # Remove desktop entries
sudo rm -f /usr/share/applications/DaVinciResolve.desktop sudo rm -f /usr/share/applications/DaVinciResolve.desktop
sudo rm -f /usr/share/applications/DaVinciControlPanelsSetup.desktop sudo rm -f /usr/share/applications/DaVinciControlPanelsSetup.desktop