parent
bd04b91e3e
commit
1a64ada0c3
2 changed files with 105 additions and 108 deletions
@ -1,122 +1,107 @@ |
||||
import { useEffect, useRef, memo } from 'react'; |
||||
import { RFB } from 'novnc-node'; |
||||
import { useEffect, useRef, MutableRefObject, memo } from 'react'; |
||||
import RFB from './noVNC/core/rfb'; |
||||
|
||||
type VncProps = { |
||||
rfb: typeof RFB; |
||||
// The URL for the VNC connection: protocol, host, port, and path.
|
||||
type Props = { |
||||
rfb: MutableRefObject<RFB | undefined>; |
||||
url: string; |
||||
|
||||
// Define width and height via style or separate props
|
||||
style?: { width: number | string; height: number | string }; |
||||
width?: number | string; |
||||
height?: number | string; |
||||
|
||||
// Force a URL to be communicated with as encrypted.
|
||||
encrypt?: boolean; |
||||
|
||||
// List of WebSocket protocols this connection should support.
|
||||
wsProtocols?: string[]; |
||||
|
||||
// VNC connection changes.
|
||||
onUpdateState?: () => void; |
||||
|
||||
onPasswordRequired?: () => void; |
||||
|
||||
// Alert is raised on the VNC connection.
|
||||
onBell?: () => void; |
||||
|
||||
// The desktop name is entered for the connection.
|
||||
onDesktopName?: () => void; |
||||
|
||||
connectTimeout?: number; |
||||
|
||||
disconnectTimeout?: number; |
||||
|
||||
// A VNC connection should disconnect other connections before connecting.
|
||||
shared?: boolean; |
||||
|
||||
trueColor?: boolean; |
||||
localCursor?: boolean; |
||||
style: { width: string; height: string }; |
||||
viewOnly: boolean; |
||||
focusOnClick: boolean; |
||||
clipViewport: boolean; |
||||
dragViewport: boolean; |
||||
scaleViewport: boolean; |
||||
resizeSession: boolean; |
||||
showDotCursor: boolean; |
||||
background: string; |
||||
qualityLevel: number; |
||||
compressionLevel: number; |
||||
}; |
||||
|
||||
const VncDisplay = ({ |
||||
const VncDisplay = (props: Props): JSX.Element => { |
||||
const screen = useRef<HTMLDivElement>(null); |
||||
|
||||
const { |
||||
rfb, |
||||
style, |
||||
url, |
||||
encrypt, |
||||
...opts |
||||
}: VncProps): JSX.Element => { |
||||
const canvasRef = useRef<HTMLCanvasElement>(null); |
||||
|
||||
/* eslint-disable no-param-reassign */ |
||||
useEffect(() => { |
||||
if (!rfb.current) |
||||
rfb.current = new RFB({ |
||||
...opts, |
||||
style, |
||||
encrypt: encrypt !== null ? encrypt : url.startsWith('wss:'), |
||||
target: canvasRef.current, |
||||
}); |
||||
|
||||
if (!rfb.current) return; |
||||
viewOnly, |
||||
focusOnClick, |
||||
clipViewport, |
||||
dragViewport, |
||||
scaleViewport, |
||||
resizeSession, |
||||
showDotCursor, |
||||
background, |
||||
qualityLevel, |
||||
compressionLevel, |
||||
} = props; |
||||
|
||||
if (!canvasRef.current) { |
||||
/* eslint-disable consistent-return */ |
||||
useEffect(() => { |
||||
if (!screen.current) { |
||||
return (): void => { |
||||
rfb.current.disconnect(); |
||||
if (rfb.current) { |
||||
rfb?.current.disconnect(); |
||||
rfb.current = undefined; |
||||
} |
||||
}; |
||||
} |
||||
|
||||
rfb.current.connect(url); |
||||
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; |
||||
} |
||||
|
||||
/* eslint-disable consistent-return */ |
||||
if (!rfb.current) return; |
||||
|
||||
return (): void => { |
||||
if (rfb.current) { |
||||
rfb.current.disconnect(); |
||||
rfb.current = undefined; |
||||
} |
||||
}; |
||||
}, [rfb, encrypt, opts, url, style]); |
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [rfb]); |
||||
|
||||
const handleMouseEnter = () => { |
||||
if (!rfb.current) return; |
||||
if (document.activeElement) (document.activeElement as HTMLElement).blur(); |
||||
rfb.current.get_keyboard().grab(); |
||||
rfb.current.get_mouse().grab(); |
||||
if ( |
||||
document.activeElement && |
||||
document.activeElement instanceof HTMLElement |
||||
) { |
||||
document.activeElement.blur(); |
||||
} |
||||
|
||||
if (rfb?.current) { |
||||
rfb.current.focus(); |
||||
} |
||||
}; |
||||
|
||||
const handleMouseLeave = () => { |
||||
if (!rfb.current) return; |
||||
|
||||
rfb.current.get_keyboard().ungrab(); |
||||
rfb.current.get_mouse().ungrab(); |
||||
if (rfb?.current) { |
||||
rfb.current.blur(); |
||||
} |
||||
}; |
||||
|
||||
return ( |
||||
<canvas |
||||
<div |
||||
style={style} |
||||
ref={canvasRef} |
||||
ref={screen} |
||||
onMouseEnter={handleMouseEnter} |
||||
onMouseLeave={handleMouseLeave} |
||||
/> |
||||
); |
||||
}; |
||||
|
||||
VncDisplay.defaultProps = { |
||||
style: null, |
||||
encrypt: null, |
||||
wsProtocols: ['binary'], |
||||
trueColor: true, |
||||
localCursor: true, |
||||
connectTimeout: 5, |
||||
disconnectTimeout: 5, |
||||
width: 1280, |
||||
height: 720, |
||||
onUpdateState: null, |
||||
onPasswordRequired: null, |
||||
onBell: null, |
||||
onDesktopName: null, |
||||
shared: false, |
||||
}; |
||||
|
||||
const MemoVncDisplay = memo(VncDisplay); |
||||
|
||||
export default MemoVncDisplay; |
||||
export default memo(VncDisplay); |
||||
|
Loading…
Reference in new issue