Add upstream fix handoffs for Omarchy window-rule bugs

Two Resolve problems on Omarchy 4.x turned out to be bugs in Omarchy's own
Hyprland rules rather than in this installer, so document them as
ready-to-raise upstream patches:

  001 the status bar covers Resolve's menu bar
  002 Resolve's dialogs trap the pointer until Resolve is killed

Each file is self-contained: symptom, root cause with captured command
output, the exact patch in Omarchy's house style, verification performed,
how to re-verify, and reviewer caveats. Both fixes are verified end-to-end
on Hyprland 0.56.1 / Omarchy 4.0.0.r1440.

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 48576676ac
commit c186dbbceb
3 changed files with 542 additions and 0 deletions

View file

@ -0,0 +1,238 @@
# Fix 001 — DaVinci Resolve's main window is covered by the Omarchy bar
**Status:** ready to raise · **Target project:** [omarchy](https://github.com/basecamp/omarchy)
**Target file:** `default/hypr/apps/davinci-resolve.lua`
**Change type:** append one window rule + explanatory comment. No new deps, no other app affected.
> This file is a self-contained handoff. Everything needed to raise the PR —
> problem, root cause, exact patch, and reproduction/verification steps — is
> below. No other file in this repo is required.
---
## 1. Symptom
On Omarchy 4.x, launching DaVinci Resolve leaves Resolve's **own** menu bar
(File / Edit / Trim / Timeline / Clip / …) hidden underneath the Omarchy status
bar, so those menus cannot be clicked.
## 2. Root cause
`omarchy-bar` is a layer-shell surface on Hyprland's `top` layer with a 26px
exclusive zone:
```
$ hyprctl layers
Layer level 2 (top):
Layer ...: xywh: 0 0 2560 26, a: 1, namespace: omarchy-bar, pid: 1582
```
That reserved zone only constrains **tiled** windows. It is being honoured
correctly — `hyprctl monitors` reports `reserved: [0, 26, 0, 0]`, and tiled
windows start at y=38 as expected.
The problem is that Resolve is not tiled. `default/hypr/apps/davinci-resolve.lua`
floats every Resolve window, which is the right call — Resolve's many child
windows tile badly. But 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:
```
class='resolve' title='DaVinci Resolve - New Project 2' at=[0, 0] size=[2560, 1440] float=True fs=0
```
So the bar, being on the `top` layer, draws over it.
## 3. Fix
Hyprland renders a fullscreen window **above** `top`-layer layer-shell surfaces.
Opening Resolve's main window fullscreen therefore puts Resolve over the bar
instead of under it. The bar is not modified, hidden, or killed — it behaves
normally again as soon as you leave fullscreen or focus another window.
### Why the title match must be narrow
Every Resolve window shares the same class, and only the main one may be
fullscreened. Windows observed during a cold start:
| window | title | size | fullscreened |
| --------------- | ---------------------------------- | --------- | ------------ |
| splash screen | `resolve` | 740×326 | no |
| project chooser | `Project Manager` | 900×512 | no |
| modal dialogs | e.g. `Preferences` | 630×534 | no |
| main window | `DaVinci Resolve - <project name>` | 2560×1440 | **yes** |
Requiring the `" - <project>"` suffix leaves the splash, the Project Manager and
Resolve's modal dialogs floating at their natural size. A class-only rule would
fullscreen all of them, which is why the rule is title-scoped.
The class pattern `.*[Rr]esolve.*` is kept byte-identical to the existing rule in
this file for consistency; the title is what does the scoping. Both patterns
were verified together as the exact string in the patch (see §5), not inferred
from testing the two halves separately.
## 4. The patch
Four lines: a blank, a three-line comment, and the rule. Apply with `git apply`:
```diff
--- a/default/hypr/apps/davinci-resolve.lua
+++ b/default/hypr/apps/davinci-resolve.lua
@@ -6,3 +6,9 @@
tag = "-default-opacity",
opacity = "1 1",
})
+
+-- 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 })
```
If the diff doesn't apply cleanly (upstream moved), the change is just
**append the comment and this line to the end of the file**:
```lua
o.window({ class = ".*[Rr]esolve.*", title = "^DaVinci Resolve - .+$" }, { fullscreen = true })
```
### Resulting complete file
```lua
-- DaVinci Resolve window focus handling. Kept fully opaque: the default
-- translucency distorts colour-critical grading work.
o.window(".*[Rr]esolve.*", {
float = true,
stay_focused = true,
tag = "-default-opacity",
opacity = "1 1",
})
-- 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 })
```
### Optional, once the PR is open
`default/hypr/apps/jetbrains.lua` sets the precedent of keeping the code terse
and parking the deep rationale behind a link:
```lua
-- Disable mouse focus (see https://github.com/basecamp/omarchy/pull/5183#issuecomment-4189299971).
```
If a reviewer wants more detail inline than the three lines give, follow that
pattern — append `(see <this PR's URL>)` to the comment rather than expanding
the comment block.
## 5. Verification performed
Tested on Hyprland 0.56.1 / Omarchy 4.0.0.r1440, NVIDIA, 2560×1440 logical
(3840×2160 physical, scale 1.5).
Method: sample the top 26px strip with `grim`. Bar colour is `(26,27,38)`;
Resolve's UI chrome is `(23,24,26)`.
**Before** — bar covers Resolve:
```
y= 5 [(26, 27, 38), (26, 27, 38), (26, 27, 38)] <- bar
y= 20 [(26, 27, 38), (26, 27, 38), (26, 27, 38)] <- bar
y= 35 [(26, 27, 38), (26, 27, 38), (26, 27, 38)] <- bar
y= 45 [(23, 24, 26), (23, 24, 26), (23, 24, 26)] <- Resolve only starts here
```
**After** — Resolve renders over the bar, its menu text now visible at y=35:
```
y= 5 [(23, 24, 26), (23, 24, 26), (23, 24, 26)]
y= 20 [(23, 24, 26), (23, 24, 26), (23, 24, 26)]
y= 35 [(179, 180, 180), (23, 24, 26), (23, 24, 26)] <- menu text
y= 45 [(23, 24, 26), (23, 24, 26), (23, 24, 26)]
```
With the exact rule string above installed, a cold start produces precisely the
intended scoping — only the main window is fullscreened, and a modal dialog that
happened to open during the run was correctly left alone:
```
title='resolve' size=[740, 326] FULLSCREEN=0
title='Project Manager' size=[900, 512] FULLSCREEN=0
title='DaVinci Resolve - New Project 2' size=[2560, 1440] FULLSCREEN=2
title='Preferences' size=[630, 534] FULLSCREEN=0
```
That `Preferences` line is the important one: it is the failure mode a
class-only rule would produce (a 630×534 dialog blown up to fill the screen),
and it demonstrably does not happen.
Also confirmed visually by the reporting user.
### How to re-verify
```bash
# 1. Watch what Resolve opens, and at what size / fullscreen state
(while :; do
hyprctl clients -j | jq -r '.[] | select(.class=="resolve")
| "title=\(.title) size=\(.size) FULLSCREEN=\(.fullscreen)"'
sleep 2
done) | sort -u &
# 2. Launch Resolve, then SELECT A PROJECT in the Project Manager.
# Resolve never opens a project on its own, so an unattended launch stops
# at the chooser and the main window never appears.
# 3. Confirm the top strip belongs to Resolve, not the bar
grim -g "0,0 1200x60" /tmp/top.png
```
Expect the splash and Project Manager at `FULLSCREEN=0`, and only
`DaVinci Resolve - <project>` at `FULLSCREEN=2`.
## 6. Upstream conventions this follows
Checked against every file in `default/hypr/apps/` on omarchy 4.0.0.r1440, not
assumed. Keep these in mind if the patch is reworked.
| Convention | Evidence upstream | How this patch complies |
|---|---|---|
| Comments are terse — **3 lines is the maximum** anywhere in `apps/` | longest are `webcam-overlay.lua`, `system.lua`, `omarchy-shell.lua` at 3 lines; most files have 01 | 3-line comment |
| Deep rationale goes in the PR, linked from code — not expanded inline | `jetbrains.lua` links a PR comment for its one-line rule | full rationale lives in this file / the PR; optional link noted above |
| Class+title matching uses the table form `o.window({ class = …, title = … }, { … })` | `battlenet.lua`, `omarchy-shell.lua`, `system.lua` | same form |
| Single-property rules stay inline on one line; only multi-property rules break across lines | `moonlight.lua`, `geforce.lua`, `qemu.lua` inline vs `retroarch.lua`, `pip.lua` multi-line | one property (`fullscreen = true`), inline |
| `fullscreen = true` is the established idiom for this | `moonlight.lua`, `retroarch.lua`, `system.lua` (screensaver) | same property |
| Long lines are acceptable — no wrapping for its own sake | `omarchy-shell.lua` (~180 chars), `browser.lua` (~150) | rule line is ~95 chars |
| Regex metacharacters escaped as `\\.` in Lua strings | `battlenet.lua`: `"^Battle\\.net$"` | no literal dot in the title; `.+` is intentional |
| 2-space indent, double-quoted strings | throughout | matches |
| One app per file, named after the app | the whole directory | extends the existing `davinci-resolve.lua` rather than adding a file |
Note the packaged copy of Omarchy ships no `stylua.toml`, `.editorconfig` or
`CONTRIBUTING.md` (they aren't included in the package), so style here is
inferred from the source files themselves. Check the GitHub repo for a
formatter config before raising, and run it if one exists.
## 7. Suggested PR text
**Title**
```
hypr/apps: open DaVinci Resolve's main window fullscreen so it isn't covered by the bar
```
**Body** — sections 1, 2, 3 and 5 above, in that order.
## 8. Caveats for the reviewer
- Only the **main** window is fullscreened; splash, Project Manager and modal
dialogs are untouched. This is the whole point of the title scoping.
- If a user leaves fullscreen manually, the bar covers Resolve again — inherent
to the approach, and recoverable with `SUPER + F`.
- The title is matched at window-open time. If a future Resolve release changes
its main-window title format away from `DaVinci Resolve - <project>`, the rule
silently stops matching and the old behaviour returns. It fails safe: no
window gets wrongly fullscreened.
- An alternative considered and rejected: having Resolve's launcher run
`omarchy toggle bar off` and restore it on exit. It needs no window titles,
but hides the bar system-wide across all workspaces for as long as Resolve is
open, and leaves the bar hidden if the process is killed uncleanly.

View file

@ -0,0 +1,199 @@
# Fix 002 — DaVinci Resolve's dialogs trap the pointer until Resolve is killed
**Status:** ready to raise — fully verified (§5) · **Target project:** [omarchy](https://github.com/basecamp/omarchy)
**Target file:** `default/hypr/apps/davinci-resolve.lua`
**Change type:** append one window rule + comment. Narrows an existing rule; removes nothing.
> Self-contained handoff. Problem, root cause, patch and verification are all
> below. No other file in this repo is required.
> Both halves are verified: the trap is measurably gone, and the popup
> behaviour `stay_focused` protects is confirmed intact. See §7 for the one
> remaining known limitation and an open design question for the maintainer.
---
## 1. Symptom
Opening certain DaVinci Resolve windows — confirmed with **Preferences** and
**Project Manager** — traps the pointer. The dialog cannot be dismissed and no
other window can be clicked. **Quitting Resolve entirely is the only way out.**
## 2. Root cause
`default/hypr/apps/davinci-resolve.lua` sets `stay_focused = true` on
`.*[Rr]esolve.*` — that is, on *every* Resolve window.
That rule is there for a real reason and **must not simply be deleted**.
Resolve is not Wayland-native, so Hyprland misreads its transient popups
(Change Clip Duration, Normalize Audio Levels, …) and they close the moment the
pointer leaves them unless focus is pinned. See
[hyprwm/Hyprland#12235](https://github.com/hyprwm/Hyprland/discussions/12235),
which is where the `stayfocused` workaround originates.
The bug is that it is applied **too broadly**. `stayfocused` forces focus onto a
window while it is visible. When two Resolve windows are visible at once, both
demand focus and neither yields, so Hyprland flips between them indefinitely.
Captured live, sampling `hyprctl activewindow` every 0.4s, with the Project
Manager and its own child dialog both open:
```
t=361.7 ACTIVE='resolve'|'Create New Project' resolve_windows=['Project Manager', 'Create New Project']
t=365.4 ACTIVE='resolve'|'Project Manager' resolve_windows=['Project Manager', 'Create New Project']
t=365.8 ACTIVE='resolve'|'Create New Project' resolve_windows=['Project Manager', 'Create New Project']
```
Focus oscillates between the two. Neither can be acted on, which is exactly what
"the mouse is captured" looks like from the user's side.
## 3. Fix
Keep the pin where it is needed — the transient popups — and remove it from the
windows those popups open *over*. At most one pinned window is then ever
visible, so nothing can fight.
The two parent windows are the main window and the Project Manager:
| window | role | pinned after fix |
|---|---|---|
| `DaVinci Resolve - <project>` | parent of Preferences, Change Clip Duration, … | no |
| `Project Manager` | parent of Create New Project | no |
| `Preferences`, `Create New Project`, transient popups | children | yes |
This is a narrowing, not a removal: every window that previously relied on
`stay_focused` to stay open still has it.
## 4. The patch
```diff
--- a/default/hypr/apps/davinci-resolve.lua
+++ b/default/hypr/apps/davinci-resolve.lua
@@ -6,3 +6,8 @@
tag = "-default-opacity",
opacity = "1 1",
})
+
+-- stay_focused above stops Resolve's transient popups closing on mouse-out
+-- (hyprwm/Hyprland#12235), but pinning the windows they open over makes two
+-- windows fight for focus and traps the pointer. Unpin the parents only.
+o.window({ class = ".*[Rr]esolve.*", title = "^(DaVinci Resolve - .+|Project Manager)$" }, { stay_focused = false })
```
Independent of fix 001 — different property, order between them doesn't matter.
## 5. Verification performed
Hyprland 0.56.1 / Omarchy 4.0.0.r1440, NVIDIA, 2560×1440 logical.
**The override mechanism is confirmed.** A later, more specific rule does beat
an earlier blanket one, tested in isolation with a throwaway class so nothing
about Resolve could confound it:
```lua
o.window("^(OVERRIDETEST)$", { float = true })
o.window({ class = "^(OVERRIDETEST)$", title = "^nofloat$" }, { float = false })
```
```
title='nofloat' floating=False <- later specific rule wins
title='yesfloat' floating=True <- blanket rule still applies
```
**The oscillation is confirmed** — see the focus log in §2.
**The trap is confirmed removed, by cold-start A/B.** Resolve was started fresh
twice — once with the fix, once with *only* the `stay_focused` exclusion removed
(fix 001 left in place, so the exclusion was the sole variable). In each run
Resolve was focused, focus was then dispatched to another window, and the actual
focus owner sampled 12 times at 0.5s intervals:
| run | config | samples where Resolve held focus | verdict |
|---|---|---|---|
| A | with fix | **0 / 12** | focus sticks elsewhere — not trapped |
| B | control, exclusion removed | **11 / 12** | snaps back to Resolve — trapped |
The control matters: it demonstrates the test can actually detect the trap, so
run A's result is meaningful rather than vacuous.
Both runs exercised the **Project Manager**, one of the two windows the rule
excludes and one of the two originally reported.
**The main window and the popups were then confirmed by hand.** Resolve always
requires a project to be selected from the Project Manager before the main
window opens — it never opens one on its own — so these two checks cannot be
automated and were driven manually by the reporting user:
| check | procedure | result |
|---|---|---|
| Main window no longer traps | Open a project, open **Preferences** from the main window | **Pass** — dismissable, and other applications remain clickable while it is open |
| Popups do not regress | Edit page → right-click clip → **Change Clip Duration**, move pointer off the popup onto the main window | **Pass** — popup stays open |
The second row is the one that matters most: it is the behaviour
`stay_focused` exists to protect, and it survives the narrowing intact.
## 6. How to verify properly
Both halves must be checked — the fix is only correct if it fixes the trap
**and** leaves the popups alone.
```bash
# Log every focus transition while you drive Resolve by hand
while :; do
hyprctl activewindow -j | jq -r '"ACTIVE=\(.class)|\(.title)"'
sleep 0.4
done | uniq
```
Note: Resolve never opens a project on its own — you must select one in the
Project Manager before the main window appears. Both checks below therefore have
to be driven by hand; an unattended launch stops at the chooser.
1. **The trap is gone** — cold-start Resolve, open Project Manager → Create New
Project, then select a project and open Preferences from the main window.
Each dialog should take focus and stay there; you should be able to dismiss
it and click other apps. The log must show no rapid flip-flopping between two
`resolve` windows.
2. **No popup regression** — in the edit page, open a transient popup (right-click
a clip → Change Clip Duration, or Normalize Audio Levels) and move the pointer
off it onto the main window. It must **not** vanish. This is the behaviour
`stay_focused` exists to protect.
## 7. Caveats for the reviewer
- **The parent list is a blocklist and will need extending.** Only
`DaVinci Resolve - <project>` and `Project Manager` are excluded. If another
Resolve window can itself parent a dialog (Project Settings and the Deliver
page are the likely candidates), it will reintroduce the trap and must be
added to the alternation.
- **A cleaner long-term fix may exist.** Hyprland 0.53.3+ added
`input:mouse_refocus`, and [#12235](https://github.com/hyprwm/Hyprland/discussions/12235)
notes `mouse_refocus = 0` as an alternative to `stayfocused` entirely. On
0.56.1 the underlying popup bug may even be fixed, which would make the whole
`stay_focused` rule obsolete. That was not tested here because
`mouse_refocus` is a **global** input setting, not a per-window rule, so
flipping it for every Omarchy user is a much larger decision than this
narrowing. Worth a maintainer's opinion.
- Related open issue: [basecamp/omarchy#5887](https://github.com/basecamp/omarchy/issues/5887)
reports Resolve child windows becoming inaccessible on Omarchy 4.0.0.alpha.
It doesn't mention `stayfocused`, but "child windows become inaccessible when
the main panel is clicked" is consistent with this root cause, and this fix
may resolve it. Worth linking in the PR.
## 8. Upstream conventions this follows
Same set verified for fix 001 — see
[`001-resolve-bar-overlap.md`](001-resolve-bar-overlap.md) §6. Specifically:
3-line comment (house max), table-match form for class+title, single-property
rule inline on one line, 2-space indent, extends the existing app file rather
than adding a new one.
## 9. Suggested PR text
**Title**
```
hypr/apps: stop DaVinci Resolve's dialogs trapping the pointer
```
**Body** — sections 1, 2 and 3 above, then the §5 evidence, then the §7 bullets
stated as known limitations.

View file

@ -0,0 +1,105 @@
# Upstream fixes for Omarchy
Fixes found while getting DaVinci Resolve working on Omarchy that belong
**upstream in [omarchy](https://github.com/basecamp/omarchy)** rather than in
this installer script.
Each file here is a **self-contained handoff**: one numbered Markdown file per
fix, containing everything needed to raise the PR with no other context —
symptom, root cause with evidence, the exact patch, verification already
performed, how to re-verify, and reviewer caveats. Hand one file to a person or
an AI and they can open the PR from it alone.
| # | Fix | Target file | Status |
| --- | ---------------------------------------------------------------- | ------------------------------------------ | -------------- |
| 001 | [Resolve's main window is covered by the bar](001-resolve-bar-overlap.md) | `default/hypr/apps/davinci-resolve.lua` | ready to raise |
| 002 | [Resolve's dialogs trap the pointer](002-resolve-dialog-focus-trap.md) | `default/hypr/apps/davinci-resolve.lua` | ready to raise |
## Raising 001 and 002 — read first
Both fixes append to the **same file**, `default/hypr/apps/davinci-resolve.lua`,
and both diffs are anchored on the same context (`@@ -6,3 @@`, the end of the
original 8-line file). **Whichever lands first will make the other's patch fail
to apply.** They are otherwise independent — different properties, order between
them doesn't matter.
**Raise them as two PRs, 001 first. Do not combine them.**
Combining is tempting — same file, both small — and it does sidestep the patch
conflict. That's the weakest consideration: rebasing 002 after 001 lands means
appending two rules to the end of a file, with no semantic conflict.
The reason to keep them apart is that they carry very different risk:
| | 001 (bar overlap) | 002 (focus trap) |
|---|---|---|
| Nature | purely additive — a new rule for a window that had none | **narrows a rule a maintainer added deliberately** |
| Verified | end-to-end, including negative cases | end-to-end — cold-start A/B, plus hands-on main-window and popup checks |
| If wrong | fails safe — rule stops matching, old behaviour returns | popups vanish on mouse-out: worse than the bug being fixed |
| Likely review | uncontroversial | may attract debate (`mouse_refocus`, blocklist fragility) |
002 touches `stay_focused`, which exists for a documented reason, and carries an
open design question for a maintainer (see its §7). Coupling them makes the
clean, low-risk fix wait on the more debatable one for no benefit.
Both are now fully verified, so 002 no longer needs holding — raise them in
close succession, 001 first.
If a maintainer explicitly asks for them together, use the combined end state
below as the target — one PR, two commits.
### Combined end state (both fixes applied)
```lua
-- DaVinci Resolve window focus handling. Kept fully opaque: the default
-- translucency distorts colour-critical grading work.
o.window(".*[Rr]esolve.*", {
float = true,
stay_focused = true,
tag = "-default-opacity",
opacity = "1 1",
})
-- 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 })
-- stay_focused above stops Resolve's transient popups closing on mouse-out
-- (hyprwm/Hyprland#12235), but pinning the windows they open over makes two
-- windows fight for focus and traps the pointer. Unpin the parents only.
o.window({ class = ".*[Rr]esolve.*", title = "^(DaVinci Resolve - .+|Project Manager)$" }, { stay_focused = false })
```
### State the testing accurately
Both fixes are verified end-to-end; each file's §5 lists exactly what was
measured and what was checked by hand. Two things are worth carrying into the
PR bodies rather than quietly dropping:
- 002's parent list is a **blocklist** that may need extending (its §7).
- 002 has an **open design question** — whether `input:mouse_refocus` makes the
whole `stay_focused` rule obsolete on Hyprland 0.56.1. Not tested, because it
is a global setting and that is a maintainer's call.
## Conventions
- **Filename:** `NNN-short-slug.md`, numbered in the order found.
- **Status:** `ready to raise``raised (#PR)``merged` / `rejected`. Update
the table above when it changes.
- **Evidence over assertion.** Every claim about behaviour should be backed by
command output pasted into the file (`hyprctl`, `grim` pixel samples, logs),
so a reviewer can judge it without reproducing the setup.
- **State what was actually tested**, including the environment (Hyprland and
Omarchy versions, GPU, resolution/scale). If something was reasoned about but
not run, say so explicitly.
- **Record rejected alternatives** and why, in the caveats section — it saves
the reviewer re-proposing them.
## Relationship to the installer
Until a fix lands upstream, `Omarchy_resolve_v2.sh` applies its own local
equivalent so the installer works today. Those local workarounds are marked in
the script with a comment pointing back to the fix number here. Once a fix is
merged upstream the local workaround becomes redundant — harmless, but it can
then be dropped.