mirror of
https://github.com/sipeed/NanoKVM.git
synced 2026-09-11 00:22:56 -05:00
Implement fit to window
Implements the feature as per #20 (https://github.com/sipeed/NanoKVM/issues/20#issue-2486746749)
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -52,7 +52,8 @@ const ko = {
|
||||
quality: '품질',
|
||||
frameDetect: '프레임 탐지',
|
||||
frameDetectTip:
|
||||
"프레임 간의 차이를 계산합니다. 원격 호스트 화면에 변경 사항이 감지되지 않으면 비디오 스트림 전송을 중지합니다."
|
||||
"프레임 간의 차이를 계산합니다. 원격 호스트 화면에 변경 사항이 감지되지 않으면 비디오 스트림 전송을 중지합니다.",
|
||||
fitInWindow: "창에 맞추기"
|
||||
},
|
||||
cursor: {
|
||||
default: '기본 커서',
|
||||
|
||||
@@ -57,7 +57,8 @@ const ru = {
|
||||
quality: 'Качество',
|
||||
frameDetect: 'Экономия трафика',
|
||||
frameDetectTip:
|
||||
'Вычисляет разницу между кадрами и прекращает передачу видеопотока, если на экране удаленного узла не обнаружено никаких изменений.'
|
||||
'Вычисляет разницу между кадрами и прекращает передачу видеопотока, если на экране удаленного узла не обнаружено никаких изменений.',
|
||||
fitInWindow: "Вписать в окно",
|
||||
},
|
||||
cursor: {
|
||||
default: 'Курсор по умолчанию',
|
||||
|
||||
@@ -51,7 +51,8 @@ const zh = {
|
||||
customizeFps: '自定义',
|
||||
quality: '图像质量',
|
||||
frameDetect: '帧差检测',
|
||||
frameDetectTip: '计算帧之间的差异,当检测到远程主机画面不变时,停止传输视频流'
|
||||
frameDetectTip: '计算帧之间的差异,当检测到远程主机画面不变时,停止传输视频流',
|
||||
fitInWindow: '适应窗口'
|
||||
},
|
||||
cursor: {
|
||||
default: '默认指针',
|
||||
|
||||
4
web/src/jotai/scaling.ts
Normal file
4
web/src/jotai/scaling.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { atom } from 'jotai';
|
||||
|
||||
// Should the feed fit in the browser window?
|
||||
export const fitInWindowAtom = atom(false);
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user