|
|
|
import { useEffect, useRef, MutableRefObject, memo } from 'react';
|
|
|
|
import RFB from '@novnc/novnc/core/rfb';
|
|
|
|
|
|
|
|
type Props = {
|
|
|
|
rfb: MutableRefObject<typeof RFB | undefined>;
|
|
|
|
url: string;
|
|
|
|
viewOnly: boolean;
|
|
|
|
focusOnClick: boolean;
|
|
|
|
clipViewport: boolean;
|
|
|
|
dragViewport: boolean;
|
|
|
|
scaleViewport: boolean;
|
|
|
|
resizeSession: boolean;
|
|
|
|
showDotCursor: boolean;
|
|
|
|
background: string;
|
|
|
|
qualityLevel: number;
|
|
|
|
compressionLevel: number;
|
|
|
|
onDisconnect: (event: { detail: { clean: boolean } }) => void;
|
|
|
|
};
|
|
|
|
|
|
|
|
const VncDisplay = (props: Props): JSX.Element => {
|
|
|
|
const screen = useRef<HTMLDivElement>(null);
|
|
|
|
|
|
|
|
const {
|
|
|
|
rfb,
|
|
|
|
url,
|
|
|
|
viewOnly,
|
|
|
|
focusOnClick,
|
|
|
|
clipViewport,
|
|
|
|
dragViewport,
|
|
|
|
scaleViewport,
|
|
|
|
resizeSession,
|
|
|
|
showDotCursor,
|
|
|
|
background,
|
|
|
|
qualityLevel,
|
|
|
|
compressionLevel,
|
|
|
|
onDisconnect,
|
|
|
|
} = props;
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (!screen.current) {
|
|
|
|
return (): void => {
|
|
|
|
if (rfb.current) {
|
|
|
|
rfb?.current.disconnect();
|
|
|
|
rfb.current = undefined;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!rfb.current) {
|
|
|
|
screen.current.innerHTML = '';
|
|
|
|
|
|
|
|
rfb.current = new RFB(screen.current, url);
|
|
|
|
|
|
|
|
rfb.current.viewOnly = viewOnly;
|
|
|
|
rfb.current.focusOnClick = focusOnClick;
|
|
|
|
rfb.current.clipViewport = clipViewport;
|
|
|
|
rfb.current.dragViewport = dragViewport;
|
|
|
|
rfb.current.resizeSession = resizeSession;
|
|
|
|
rfb.current.scaleViewport = scaleViewport;
|
|
|
|
rfb.current.showDotCursor = showDotCursor;
|
|
|
|
rfb.current.background = background;
|
|
|
|
rfb.current.qualityLevel = qualityLevel;
|
|
|
|
rfb.current.compressionLevel = compressionLevel;
|
|
|
|
|
|
|
|
// RFB extends custom class EventTargetMixin;
|
|
|
|
// the usual .on or .once doesn't exist.
|
|
|
|
rfb.current.addEventListener('disconnect', onDisconnect);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* eslint-disable consistent-return */
|
|
|
|
if (!rfb.current) return;
|
|
|
|
|
|
|
|
return (): void => {
|
|
|
|
if (rfb.current) {
|
|
|
|
rfb.current.disconnect();
|
|
|
|
rfb.current = undefined;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
}, [rfb]);
|
|
|
|
|
|
|
|
const handleMouseEnter = () => {
|
|
|
|
if (
|
|
|
|
document.activeElement &&
|
|
|
|
document.activeElement instanceof HTMLElement
|
|
|
|
) {
|
|
|
|
document.activeElement.blur();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rfb?.current) rfb.current.focus();
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div
|
|
|
|
style={{ width: '100%', height: '75vh' }}
|
|
|
|
ref={screen}
|
|
|
|
onMouseEnter={handleMouseEnter}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default memo(VncDisplay);
|