Locate the player's window instead of assuming fullscreen
The corners and trail animations had stopped showing stills. Neither was at fault: capture.js gated every screenshot on window.innerWidth === screen.width && window.innerHeight === screen.height and that compares two different units. innerWidth is device pixels divided by devicePixelRatio; screen.width is not divided. At any zoom but 100%, or any fractional display scale, the two can never be equal — measured here at dpr 1.25, fullscreen gives 1536x864 against a reported 1920x1080. The gate stayed shut, grab() returned null, and shoot()/drop() bailed out before making a request. The only symptom was an F11 notice that stayed wrong after F11. Forcing the gate open would not have helped: the rect went out in CSS pixels and was read as logical screen pixels, so every crop would have been 1.25x too small and offset. Stop guessing from browser metrics and ask the compositor, which knows exactly. hyprctl gives the window's position and size in logical pixels; against the viewport the page reports, their ratio is the CSS-pixel scale outright, and since CSS pixels are square that one number does for both axes. The vertical room it leaves over is the tab and address bars, measured rather than assumed — which is what lets the player run in an ordinary window. Fullscreen becomes just the case where the leftover is nothing, so Start still asks for it, for the projector's sake, but says nothing when refused. The window is found by matching document.title as a prefix. The queue page was titled "Party Player — Queue", which would have matched the player's prefix and aimed the crop at the wrong window, so it is now "Queue — Party Player". Candidates are ranked on screen first, then most recently focused, then largest: hyprctl clients has no `focused` field, it has focusHistoryID. The old F11 notice is replaced by one that fires only when the server genuinely cannot find the window, debounced on resize so dragging does not spawn a burst of grim processes. Verified against the live player window: capture() returns bytes identical to a grim -g of the independently predicted region, the same physical region described at 100% and 125% zoom resolves to identical output, and the route answers 422 for an unknown window and 400 for a missing title or viewport. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015zyhxe2w7eEMXvz8wekCyc
This commit is contained in:
@@ -106,9 +106,21 @@ The server takes it. It runs on the same machine as the screen, so it can grab
|
||||
the region itself with [`grim`](https://sr.ht/~emersion/grim) and hand back the
|
||||
finished crop, which the player drops straight into an `<img>`. No picker, no
|
||||
permission prompt, and no "Sharing this tab" bar parked over the party. This
|
||||
needs a wlroots compositor — Hyprland, Sway, river — and `grim` on `PATH`;
|
||||
`server/screen.js` reads the monitor layout from `hyprctl` so the right screen
|
||||
is measured when a projector is plugged in.
|
||||
needs a wlroots compositor — Hyprland, Sway, river — and `grim` on `PATH`.
|
||||
|
||||
`server/screen.js` also asks `hyprctl` where the player's window is and how big.
|
||||
That is what makes the crop reliable: the page measures in CSS pixels, which are
|
||||
device pixels divided by `devicePixelRatio`, and that ratio moves with browser
|
||||
zoom and fractional display scaling. Comparing the window's real size against the
|
||||
viewport the page reports gives the scale outright, and the vertical room left
|
||||
over is the tab and address bars. **So the server path needs no fullscreen and no
|
||||
particular zoom** — it works in an ordinary window. **Start** still asks for
|
||||
fullscreen because that is what you want on a projector, but nothing breaks if
|
||||
you refuse.
|
||||
|
||||
The window is found by matching the page's `document.title` as a prefix, so the
|
||||
player must be the **active tab** in its window — which it is when it is the
|
||||
thing on screen. If the server cannot find it, the player says so in the corner.
|
||||
|
||||
Without `grim`, the player falls back to `getDisplayMedia` and asks to share
|
||||
your screen on the first click of **Start**. That path costs a picker and a
|
||||
@@ -120,10 +132,12 @@ sharing indicator, and what you choose in the picker matters:
|
||||
- **A single window** cannot be used: it includes the tab and URL bars, and
|
||||
Wayland gives the page no window coordinates to subtract.
|
||||
|
||||
**Either way the page has to be fullscreen**, which is why **Start** requests it:
|
||||
only then does a CSS pixel line up with a screen pixel. Leave fullscreen and the
|
||||
screenshots pause until you press <kbd>F11</kbd>. The exception is Chrome's tab
|
||||
capture, which is the viewport by definition and lines up in a window too.
|
||||
**The fallback needs the page fullscreen**, which is the one case it can map: a
|
||||
CSS pixel lines up with a screen pixel only there, and only at 100% zoom, since
|
||||
`innerWidth` scales with zoom and `screen.width` does not. Leave fullscreen and
|
||||
the screenshots pause until you press <kbd>F11</kbd>. The exception is Chrome's
|
||||
tab capture, which is the viewport by definition and lines up in a window too.
|
||||
The server path has none of these limits.
|
||||
|
||||
Going without is fine: the frame still parks in its corner and still glides,
|
||||
just unphotographed. That is what happens when you open the player from another
|
||||
|
||||
Reference in New Issue
Block a user