ui update removing topbar, responsiveness work, console behavior, custom titlebar
This commit is contained in:
@@ -32,6 +32,7 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
const wsRef = useRef<WebSocket | null>(null);
|
||||
const ctxRef = useRef<CanvasRenderingContext2D | null>(null);
|
||||
const rgbaRef = useRef<Uint8ClampedArray | null>(null);
|
||||
|
||||
const framebufferRef = useRef({
|
||||
width: 0,
|
||||
height: 0,
|
||||
@@ -45,28 +46,19 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
const [frameSize, setFrameSize] = useState({ width: 0, height: 0 });
|
||||
const [lastFrameAt, setLastFrameAt] = useState<string | null>(null);
|
||||
|
||||
const closeSocket = useCallback(() => {
|
||||
const ws = wsRef.current;
|
||||
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ type: "disconnect" }));
|
||||
ws.close();
|
||||
} else if (ws) {
|
||||
ws.close();
|
||||
}
|
||||
|
||||
wsRef.current = null;
|
||||
}, []);
|
||||
|
||||
const clearFrame = useCallback(() => {
|
||||
const canvas = canvasRef.current;
|
||||
const ctx = ctxRef.current;
|
||||
|
||||
if (canvas && ctx) {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
canvas.width = 0;
|
||||
canvas.height = 0;
|
||||
}
|
||||
|
||||
ctxRef.current = null;
|
||||
rgbaRef.current = null;
|
||||
|
||||
framebufferRef.current = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
@@ -76,16 +68,44 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
setLastFrameAt(null);
|
||||
}, []);
|
||||
|
||||
const closeSocket = useCallback(() => {
|
||||
const ws = wsRef.current;
|
||||
wsRef.current = null;
|
||||
|
||||
if (!ws) return;
|
||||
|
||||
try {
|
||||
ws.onopen = null;
|
||||
ws.onmessage = null;
|
||||
ws.onerror = null;
|
||||
ws.onclose = null;
|
||||
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ type: "disconnect" }));
|
||||
ws.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ws.readyState === WebSocket.CONNECTING) {
|
||||
ws.close();
|
||||
}
|
||||
} catch {
|
||||
// Ignore cleanup errors.
|
||||
}
|
||||
}, []);
|
||||
|
||||
const drawFrame = useCallback((buffer: ArrayBuffer) => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
if (!canvas) {
|
||||
return;
|
||||
}
|
||||
if (buffer.byteLength <= 8) return;
|
||||
|
||||
const view = new DataView(buffer);
|
||||
const width = view.getInt32(0);
|
||||
const height = view.getInt32(4);
|
||||
|
||||
if (!width || !height || width <= 0 || height <= 0) return;
|
||||
|
||||
const pixels = new Uint8ClampedArray(buffer, 8);
|
||||
|
||||
framebufferRef.current = {
|
||||
@@ -101,10 +121,7 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
}
|
||||
|
||||
const ctx = ctxRef.current;
|
||||
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
if (!ctx) return;
|
||||
|
||||
if (canvas.width !== width || canvas.height !== height) {
|
||||
canvas.width = width;
|
||||
@@ -125,6 +142,7 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
rgba[index + 2] = pixels[index];
|
||||
rgba[index + 3] = 255;
|
||||
}
|
||||
|
||||
const imageData = ctx.createImageData(width, height);
|
||||
imageData.data.set(rgba);
|
||||
|
||||
@@ -144,89 +162,119 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeSocket();
|
||||
clearFrame();
|
||||
|
||||
setError(null);
|
||||
setState("CONNECTING_WS");
|
||||
|
||||
closeSocket();
|
||||
window.setTimeout(() => {
|
||||
const socket = new WebSocket(websocketUrl);
|
||||
|
||||
const socket = new WebSocket(websocketUrl);
|
||||
wsRef.current = socket;
|
||||
socket.binaryType = "arraybuffer";
|
||||
wsRef.current = socket;
|
||||
socket.binaryType = "arraybuffer";
|
||||
|
||||
socket.onopen = () => {
|
||||
setState("CONNECTING_VNC");
|
||||
socket.onopen = () => {
|
||||
if (wsRef.current !== socket) return;
|
||||
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
type: "connect",
|
||||
host: nextHost,
|
||||
port: nextPort,
|
||||
password: nextPassword,
|
||||
}),
|
||||
);
|
||||
};
|
||||
setState("CONNECTING_VNC");
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
if (typeof event.data === "string") {
|
||||
const message = JSON.parse(event.data) as {
|
||||
type?: string;
|
||||
state?: string;
|
||||
message?: string;
|
||||
};
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
type: "connect",
|
||||
host: nextHost,
|
||||
port: nextPort,
|
||||
password: nextPassword,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
if (message.type === "state") {
|
||||
if (message.state === "CONNECTING") {
|
||||
setState("CONNECTING_VNC");
|
||||
socket.onmessage = (event) => {
|
||||
if (wsRef.current !== socket) return;
|
||||
|
||||
if (typeof event.data === "string") {
|
||||
let message: {
|
||||
type?: string;
|
||||
state?: string;
|
||||
message?: string;
|
||||
};
|
||||
|
||||
try {
|
||||
message = JSON.parse(event.data);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.state === "CONNECTED") {
|
||||
setState("CONNECTED");
|
||||
return;
|
||||
if (message.type === "state") {
|
||||
if (message.state === "CONNECTING") {
|
||||
setState("CONNECTING_VNC");
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.state === "CONNECTED") {
|
||||
setState("CONNECTED");
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.state === "FIRST_FRAME") {
|
||||
setState("FIRST_FRAME");
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.state === "DISCONNECTED") {
|
||||
clearFrame();
|
||||
setState("DISCONNECTED");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (message.state === "FIRST_FRAME") {
|
||||
setState("FIRST_FRAME");
|
||||
return;
|
||||
if (message.type === "error") {
|
||||
clearFrame();
|
||||
setError(message.message ?? "Erro VNC desconhecido.");
|
||||
setState("ERROR");
|
||||
}
|
||||
|
||||
if (message.state === "DISCONNECTED") {
|
||||
setState("DISCONNECTED");
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.type === "error") {
|
||||
setError(message.message ?? "Erro VNC desconhecido.");
|
||||
setState("ERROR");
|
||||
}
|
||||
drawFrame(event.data);
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
socket.onerror = () => {
|
||||
if (wsRef.current !== socket) return;
|
||||
|
||||
drawFrame(event.data);
|
||||
};
|
||||
clearFrame();
|
||||
setError("Erro na ligação WebSocket da consola VNC.");
|
||||
setState("ERROR");
|
||||
};
|
||||
|
||||
socket.onerror = () => {
|
||||
setError("Erro na ligação WebSocket da consola VNC.");
|
||||
setState("ERROR");
|
||||
};
|
||||
socket.onclose = () => {
|
||||
if (wsRef.current !== socket) return;
|
||||
|
||||
socket.onclose = () => {
|
||||
wsRef.current = null;
|
||||
clearFrame();
|
||||
wsRef.current = null;
|
||||
clearFrame();
|
||||
|
||||
setState((current) =>
|
||||
current === "ERROR" ? "ERROR" : "DISCONNECTED",
|
||||
);
|
||||
};
|
||||
setState((current) =>
|
||||
current === "ERROR" ? "ERROR" : "DISCONNECTED",
|
||||
);
|
||||
};
|
||||
}, 100);
|
||||
},
|
||||
[closeSocket, drawFrame, host, password, port, websocketUrl],
|
||||
[
|
||||
clearFrame,
|
||||
closeSocket,
|
||||
drawFrame,
|
||||
host,
|
||||
password,
|
||||
port,
|
||||
websocketUrl,
|
||||
],
|
||||
);
|
||||
|
||||
const disconnect = useCallback(() => {
|
||||
closeSocket();
|
||||
clearFrame();
|
||||
setError(null);
|
||||
setState("DISCONNECTED");
|
||||
}, [clearFrame, closeSocket]);
|
||||
|
||||
@@ -235,13 +283,8 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
const ws = wsRef.current;
|
||||
const framebuffer = framebufferRef.current;
|
||||
|
||||
if (!canvas || !ws || ws.readyState !== WebSocket.OPEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!framebuffer.width || !framebuffer.height) {
|
||||
return;
|
||||
}
|
||||
if (!canvas || !ws || ws.readyState !== WebSocket.OPEN) return;
|
||||
if (!framebuffer.width || !framebuffer.height) return;
|
||||
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
|
||||
@@ -278,8 +321,9 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
closeSocket();
|
||||
clearFrame();
|
||||
};
|
||||
}, [closeSocket]);
|
||||
}, [clearFrame, closeSocket]);
|
||||
|
||||
return {
|
||||
canvasRef,
|
||||
@@ -299,4 +343,4 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
connected: state === "CONNECTED" || state === "FIRST_FRAME",
|
||||
connecting: state === "CONNECTING_WS" || state === "CONNECTING_VNC",
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user