Implement fit to window

Implements the feature as per #20
(https://github.com/sipeed/NanoKVM/issues/20#issue-2486746749)
This commit is contained in:
polyzium
2024-09-05 19:53:40 +03:00
parent 81dc0e0f71
commit 6f83b756c2
9 changed files with 44 additions and 14 deletions

View File

@@ -52,7 +52,8 @@ const en = {
quality: 'Quality',
frameDetect: 'Frame Detect',
frameDetectTip:
"Calculate the difference between frames. Stop transmitting video stream when no changes are detected on the remote host's screen."
"Calculate the difference between frames. Stop transmitting video stream when no changes are detected on the remote host's screen.",
fitInWindow: "Fit in window",
},
cursor: {
default: 'Default cursor',

View File

@@ -55,7 +55,8 @@ const fr = {
quality: 'Qualité',
frameDetect: 'Frame Detect',
frameDetectTip:
"Calcule la différence entre les images. Arrête la transmission du flux vidéo lorsqu'aucun changement n'est détecté sur l'écran de l'hôte distant"
"Calcule la différence entre les images. Arrête la transmission du flux vidéo lorsqu'aucun changement n'est détecté sur l'écran de l'hôte distant",
fitInWindow: "Ajuster à la fenêtre"
},
cursor: {
default: 'Curseur par défaut',

View File

@@ -52,7 +52,8 @@ const ko = {
quality: '품질',
frameDetect: '프레임 탐지',
frameDetectTip:
"프레임 간의 차이를 계산합니다. 원격 호스트 화면에 변경 사항이 감지되지 않으면 비디오 스트림 전송을 중지합니다."
"프레임 간의 차이를 계산합니다. 원격 호스트 화면에 변경 사항이 감지되지 않으면 비디오 스트림 전송을 중지합니다.",
fitInWindow: "창에 맞추기"
},
cursor: {
default: '기본 커서',

View File

@@ -57,7 +57,8 @@ const ru = {
quality: 'Качество',
frameDetect: 'Экономия трафика',
frameDetectTip:
'Вычисляет разницу между кадрами и прекращает передачу видеопотока, если на экране удаленного узла не обнаружено никаких изменений.'
'Вычисляет разницу между кадрами и прекращает передачу видеопотока, если на экране удаленного узла не обнаружено никаких изменений.',
fitInWindow: "Вписать в окно",
},
cursor: {
default: 'Курсор по умолчанию',

View File

@@ -51,7 +51,8 @@ const zh = {
customizeFps: '自定义',
quality: '图像质量',
frameDetect: '帧差检测',
frameDetectTip: '计算帧之间的差异,当检测到远程主机画面不变时,停止传输视频流'
frameDetectTip: '计算帧之间的差异,当检测到远程主机画面不变时,停止传输视频流',
fitInWindow: '适应窗口'
},
cursor: {
default: '默认指针',

4
web/src/jotai/scaling.ts Normal file
View File

@@ -0,0 +1,4 @@
import { atom } from 'jotai';
// Should the feed fit in the browser window?
export const fitInWindowAtom = atom(false);

View File

@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import { Popover } from 'antd';
import { useAtomValue } from 'jotai';
import { Divider, Popover, Switch } from 'antd';
import { useAtom, useAtomValue } from 'jotai';
import { MonitorIcon } from 'lucide-react';
import { updateScreen } from '@/api/vm';
@@ -11,11 +11,16 @@ import { Fps } from './fps';
import { FrameDetect } from './frame-detect';
import { Quality } from './quality';
import { Resolution } from './resolution';
import { useTranslation } from 'react-i18next';
import { fitInWindowAtom } from '@/jotai/scaling';
export const Screen = () => {
const { t } = useTranslation();
const resolution = useAtomValue(resolutionAtom);
const [fps, setFps] = useState(30);
const [quality, setQuality] = useState(80);
const [fitInWindow, setFitInWindow] = useAtom(fitInWindowAtom);
useEffect(() => {
updateScreen('resolution', resolution!.height);
@@ -47,6 +52,16 @@ export const Screen = () => {
<Fps fps={fps} setFps={setFps} />
<Quality quality={quality} setQuality={setQuality} />
<FrameDetect />
<Divider style={{ margin: '5px 0' }} />
<div
className="flex cursor-pointer select-none items-center justify-between space-x-3 rounded px-3 py-1.5 text-white hover:bg-neutral-600"
onClick={() => setFitInWindow(!fitInWindow)}
>
<div>{t('screen.fitInWindow')}</div>
<Switch size="small" checked={fitInWindow} />
</div>
</div>
}
placement="bottomLeft"

View File

@@ -58,8 +58,8 @@ export const Absolute = () => {
disableEvent(event);
const rect = canvas!.getBoundingClientRect();
const x = (event.clientX - rect.left) / resolution!.width;
const y = (event.clientY - rect.top) / resolution!.height;
const x = (event.clientX - rect.left) / rect.width;
const y = (event.clientY - rect.top) / rect.height;
const hexX = x < 0 ? 0x0001 : Math.floor(0x7fff * x) + 0x0001;
const hexY = y < 0 ? 0x0001 : Math.floor(0x7fff * y) + 0x0001;

View File

@@ -8,10 +8,12 @@ import { stopFrameDetect } from '@/api/stream.ts';
import { getBaseUrl } from '@/lib/service.ts';
import { mouseStyleAtom } from '@/jotai/mouse.ts';
import { resolutionAtom } from '@/jotai/resolution.ts';
import { fitInWindowAtom } from '@/jotai/scaling.ts';
export const Screen = () => {
const resolution = useAtomValue(resolutionAtom);
const mouseStyle = useAtomValue(mouseStyleAtom);
const fitInWindow = useAtomValue(fitInWindowAtom);
useEffect(() => {
// stop frame detect for a while
@@ -20,18 +22,22 @@ export const Screen = () => {
return (
<div
className="flex h-full w-full items-start justify-center xl:items-center"
style={{ minWidth: `${resolution!.width}px`, minHeight: `${resolution!.height}px` }}
className={clsx(
'flex h-full w-full justify-center',
fitInWindow ? 'items-center' : 'items-start xl:items-center'
)}
style={fitInWindow ? undefined : { minWidth: `${resolution!.width}px`, minHeight: `${resolution!.height}px` }}
>
<Image
id="screen"
className={clsx(
'block select-none bg-neutral-950',
mouseStyle,
resolution!.width === 800 ? 'object-cover' : 'object-none'
fitInWindow ? undefined : (resolution!.width === 800 ? 'object-cover' : 'object-none'),
)}
width={resolution!.width}
height={resolution!.height}
style={fitInWindow ? { maxWidth: '100vw', maxHeight: '100vh' } : undefined}
width={fitInWindow ? undefined : resolution!.width}
height={fitInWindow ? undefined : resolution!.height}
src={`${getBaseUrl('http')}/api/stream/mjpeg`}
fallback={MonitorXIcon}
preview={false}