mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-08-23 22:47:00 +00:00
apply the configured RDP resolution to the session (#1120)
The host editor stores width and height in guacamoleConfig, and the backend passes them to guacd in the connection token. The renderer then appends its own width and height query parameters measured from the container, which take precedence, so a configured resolution never reached the session — only dpi did, because that was the one display field GuacamoleApp read back. Pass the configured width and height alongside dpi, and skip the container-driven sendSize on connect and on resize when a resolution is pinned. rescaleDisplay still fits the fixed display into the available space. Closes Termix-SSH/Support#1039
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
||||
logActivity,
|
||||
isElectron,
|
||||
} from "@/main-axios.ts";
|
||||
import { readConfiguredDimension } from "@/features/guacamole/guacamole-display-size.ts";
|
||||
import { resolveConnectionOrigin } from "@/lib/connection-origin.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { AlertCircle, RefreshCw } from "lucide-react";
|
||||
@@ -362,7 +363,15 @@ const GuacamoleAppInner = React.forwardRef<
|
||||
}
|
||||
|
||||
const resolvedProtocol = resolvedProtocolForConnect;
|
||||
const configuredDpi = Number(hostConfig.guacamoleConfig?.dpi);
|
||||
const configuredDpi = readConfiguredDimension(
|
||||
hostConfig.guacamoleConfig?.dpi,
|
||||
);
|
||||
const configuredWidth = readConfiguredDimension(
|
||||
hostConfig.guacamoleConfig?.width,
|
||||
);
|
||||
const configuredHeight = readConfiguredDimension(
|
||||
hostConfig.guacamoleConfig?.height,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="relative w-full h-full">
|
||||
@@ -400,10 +409,9 @@ const GuacamoleAppInner = React.forwardRef<
|
||||
token,
|
||||
protocol: resolvedProtocol,
|
||||
type: resolvedProtocol,
|
||||
dpi:
|
||||
Number.isFinite(configuredDpi) && configuredDpi > 0
|
||||
? configuredDpi
|
||||
: undefined,
|
||||
width: configuredWidth,
|
||||
height: configuredHeight,
|
||||
dpi: configuredDpi,
|
||||
}}
|
||||
isVisible={isVisible}
|
||||
touchMode={touchMode}
|
||||
|
||||
@@ -69,6 +69,10 @@ export const GuacamoleDisplay = forwardRef<
|
||||
ref,
|
||||
) {
|
||||
const { t } = useTranslation();
|
||||
// The host config pins the session resolution; without it the display follows
|
||||
// the container.
|
||||
const hasConfiguredSize =
|
||||
connectionConfig.width != null && connectionConfig.height != null;
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const displayRef = useRef<HTMLDivElement>(null);
|
||||
const displayElementRef = useRef<HTMLElement | null>(null);
|
||||
@@ -493,7 +497,10 @@ export const GuacamoleDisplay = forwardRef<
|
||||
isConnectingRef.current = false;
|
||||
setIsReady(true);
|
||||
onConnect?.();
|
||||
if (containerRef.current) {
|
||||
// A configured resolution is the size the session should render at;
|
||||
// resizing it to the container would discard it. rescaleDisplay still
|
||||
// fits that fixed display into whatever space is available.
|
||||
if (!hasConfiguredSize && containerRef.current) {
|
||||
const rect = containerRef.current.getBoundingClientRect();
|
||||
const size = getGuacamoleDisplaySize(
|
||||
rect.width,
|
||||
@@ -601,6 +608,7 @@ export const GuacamoleDisplay = forwardRef<
|
||||
connectionConfig.protocol,
|
||||
connectionConfig.type,
|
||||
connectionConfig.dpi,
|
||||
hasConfiguredSize,
|
||||
touchMode,
|
||||
t,
|
||||
]);
|
||||
@@ -675,15 +683,17 @@ export const GuacamoleDisplay = forwardRef<
|
||||
resizeTimeoutRef.current = setTimeout(() => {
|
||||
if (clientRef.current && containerRef.current) {
|
||||
const rect = containerRef.current.getBoundingClientRect();
|
||||
const size = getGuacamoleDisplaySize(
|
||||
rect.width,
|
||||
rect.height,
|
||||
connectionConfig.protocol ?? connectionConfig.type,
|
||||
window.devicePixelRatio,
|
||||
connectionConfig.dpi,
|
||||
);
|
||||
if (rect.width > 0 && rect.height > 0) {
|
||||
clientRef.current.sendSize(size.width, size.height);
|
||||
if (!hasConfiguredSize) {
|
||||
const size = getGuacamoleDisplaySize(
|
||||
rect.width,
|
||||
rect.height,
|
||||
connectionConfig.protocol ?? connectionConfig.type,
|
||||
window.devicePixelRatio,
|
||||
connectionConfig.dpi,
|
||||
);
|
||||
clientRef.current.sendSize(size.width, size.height);
|
||||
}
|
||||
rescaleDisplay(true);
|
||||
}
|
||||
}
|
||||
@@ -699,6 +709,7 @@ export const GuacamoleDisplay = forwardRef<
|
||||
connectionConfig.dpi,
|
||||
connectionConfig.protocol,
|
||||
connectionConfig.type,
|
||||
hasConfiguredSize,
|
||||
rescaleDisplay,
|
||||
]);
|
||||
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
const DEFAULT_RDP_DPI = 96;
|
||||
const MAX_DEVICE_PIXEL_RATIO = 3;
|
||||
|
||||
/**
|
||||
* Reads a guacamoleConfig display field. The UI stores these as strings, and
|
||||
* leaves them empty when the size should follow the browser window.
|
||||
*/
|
||||
export function readConfiguredDimension(value: unknown): number | undefined {
|
||||
const parsed = Number(value);
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
||||
}
|
||||
|
||||
export interface GuacamoleDisplaySize {
|
||||
width: number;
|
||||
height: number;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { getGuacamoleDisplaySize } from "../../../features/guacamole/guacamole-display-size";
|
||||
import {
|
||||
getGuacamoleDisplaySize,
|
||||
readConfiguredDimension,
|
||||
} from "../../../features/guacamole/guacamole-display-size";
|
||||
|
||||
describe("getGuacamoleDisplaySize", () => {
|
||||
it("requests native pixels and matching DPI for HiDPI RDP", () => {
|
||||
@@ -36,4 +39,29 @@ describe("getGuacamoleDisplaySize", () => {
|
||||
pixelRatio: 3,
|
||||
});
|
||||
});
|
||||
|
||||
it("uses a configured resolution in place of the container size", () => {
|
||||
expect(
|
||||
getGuacamoleDisplaySize(800, 600, "rdp", 1, undefined),
|
||||
).toMatchObject({ width: 800, height: 600 });
|
||||
expect(
|
||||
getGuacamoleDisplaySize(1920, 1080, "rdp", 1, undefined),
|
||||
).toMatchObject({ width: 1920, height: 1080 });
|
||||
});
|
||||
});
|
||||
|
||||
describe("readConfiguredDimension", () => {
|
||||
it("accepts the strings the host editor stores", () => {
|
||||
expect(readConfiguredDimension("1920")).toBe(1920);
|
||||
expect(readConfiguredDimension(1080)).toBe(1080);
|
||||
});
|
||||
|
||||
it("treats an unset or unusable value as 'follow the container'", () => {
|
||||
expect(readConfiguredDimension("")).toBeUndefined();
|
||||
expect(readConfiguredDimension(undefined)).toBeUndefined();
|
||||
expect(readConfiguredDimension(null)).toBeUndefined();
|
||||
expect(readConfiguredDimension("auto")).toBeUndefined();
|
||||
expect(readConfiguredDimension("0")).toBeUndefined();
|
||||
expect(readConfiguredDimension("-1080")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user