mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 22:26:16 -08:00
* fix(playback): position embedded MPV native view correctly on scaled displays Renderer bounds are measured in CSS pixels, but the native-view engines position OS windows: SetWindowPos (win32) and XMoveResizeWindow (linux) expect physical pixels, NSView setFrame (macOS) expects points. The raw values landed the video toward the window's top-left corner at 1/scale of its size on any display scale or page zoom other than 100%, windowed and fullscreen alike. The main process now converts native-view bounds (x page zoom everywhere, x display scale factor on win32/linux) with edge-based rounding; frame-copy bounds stay unscaled because the adapter owns its render scale. The session controller re-syncs bounds when devicePixelRatio changes, covering moves to a display with a different scale that keep the CSS layout identical. Closes #1145 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(playback): keep CSS bounds unrounded until native scaling Review feedback on #1206: measureBounds() rounded the CSS edges in the renderer, before the main-process CSS-to-native conversion, so fractional layout positions could drift by a pixel per scale factor (a 10.49px edge at 200% must land on 21 physical px, not 20). The renderer now sends raw getBoundingClientRect() edges and rounding happens exactly once, after scaling. Also pins process.platform explicitly in the macOS wiring test instead of relying on the suite default. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
61 lines
2.2 KiB
TypeScript
61 lines
2.2 KiB
TypeScript
import { EmbeddedMpvBounds } from '@iptvnator/shared/interfaces';
|
|
|
|
export interface NativeViewBoundsContext {
|
|
platform: NodeJS.Platform;
|
|
/** Page zoom factor of the main window's webContents (1 = 100%). */
|
|
zoomFactor: number;
|
|
/** Scale factor of the display hosting the main window (1 = 96 dpi). */
|
|
displayScaleFactor: number;
|
|
}
|
|
|
|
/**
|
|
* Converts renderer-measured bounds (CSS pixels from
|
|
* getBoundingClientRect()) into the coordinate space the native-view
|
|
* engines position their OS windows in: physical pixels for the win32
|
|
* child HWND (SetWindowPos) and the linux child X11 window
|
|
* (XMoveResizeWindow), points — device-independent pixels — for the macOS
|
|
* NSView (setFrame). CSS pixels match points only at 100% page zoom and
|
|
* match physical pixels only at 100% page zoom AND 100% display scale, so
|
|
* every platform scales by the zoom factor and win32/linux additionally by
|
|
* the display scale factor (#1145).
|
|
*
|
|
* Bounds arrive with unrounded CSS edges and are rounded exactly once here,
|
|
* after scaling: edges first, then width/height derived from them. Rounding
|
|
* any earlier (or per-field) lets fractional CSS layouts drift by a pixel
|
|
* per scale factor and open 1px seams between the native video window and
|
|
* the surrounding DOM UI.
|
|
*/
|
|
export function toNativeViewBounds(
|
|
bounds: EmbeddedMpvBounds,
|
|
context: NativeViewBoundsContext
|
|
): EmbeddedMpvBounds {
|
|
const scale = resolveNativeViewScale(context);
|
|
if (scale === 1) {
|
|
return bounds;
|
|
}
|
|
|
|
const left = Math.round(bounds.x * scale);
|
|
const top = Math.round(bounds.y * scale);
|
|
const right = Math.round((bounds.x + bounds.width) * scale);
|
|
const bottom = Math.round((bounds.y + bounds.height) * scale);
|
|
|
|
return {
|
|
x: left,
|
|
y: top,
|
|
width: Math.max(1, right - left),
|
|
height: Math.max(1, bottom - top),
|
|
};
|
|
}
|
|
|
|
function resolveNativeViewScale(context: NativeViewBoundsContext): number {
|
|
const zoomFactor = sanitizeFactor(context.zoomFactor);
|
|
if (context.platform === 'darwin') {
|
|
return zoomFactor;
|
|
}
|
|
return zoomFactor * sanitizeFactor(context.displayScaleFactor);
|
|
}
|
|
|
|
function sanitizeFactor(value: number): number {
|
|
return Number.isFinite(value) && value > 0 ? value : 1;
|
|
}
|