feat: add ctrl+alt+delete button for rdp/vnc sessions

This commit is contained in:
ZacharyZcR
2026-05-13 16:11:00 +08:00
committed by ZacharyZcR
parent ebed4a6d2e
commit fc9860c4d0
+30 -3
View File
@@ -1,9 +1,12 @@
import React, { useState, useEffect, useCallback } from "react"; import React, { useState, useEffect, useRef, useCallback } from "react";
import { GuacamoleDisplay } from "@/features/guacamole/GuacamoleDisplay.tsx"; import {
GuacamoleDisplay,
type GuacamoleDisplayHandle,
} from "@/features/guacamole/GuacamoleDisplay.tsx";
import { FullScreenAppWrapper } from "@/features/FullScreenAppWrapper.tsx"; import { FullScreenAppWrapper } from "@/features/FullScreenAppWrapper.tsx";
import { getGuacamoleTokenFromHost } from "@/main-axios.ts"; import { getGuacamoleTokenFromHost } from "@/main-axios.ts";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { AlertCircle, RefreshCw } from "lucide-react"; import { AlertCircle, RefreshCw, Keyboard } from "lucide-react";
import { Button } from "@/components/button.tsx"; import { Button } from "@/components/button.tsx";
import { SimpleLoader } from "@/lib/SimpleLoader.tsx"; import { SimpleLoader } from "@/lib/SimpleLoader.tsx";
import type { SSHHost } from "@/types"; import type { SSHHost } from "@/types";
@@ -71,6 +74,7 @@ const GuacamoleAppInner: React.FC<GuacamoleAppInnerProps> = ({
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [connectionError, setConnectionError] = useState<string | null>(null); const [connectionError, setConnectionError] = useState<string | null>(null);
const [retryCount, setRetryCount] = useState(0); const [retryCount, setRetryCount] = useState(0);
const displayRef = useRef<GuacamoleDisplayHandle>(null);
useEffect(() => { useEffect(() => {
setToken(null); setToken(null);
@@ -87,6 +91,18 @@ const GuacamoleAppInner: React.FC<GuacamoleAppInnerProps> = ({
setRetryCount((c) => c + 1); setRetryCount((c) => c + 1);
}, []); }, []);
const sendCtrlAltDelete = useCallback(() => {
const display = displayRef.current;
if (!display) return;
// keysyms: Control_L=0xffe3, Alt_L=0xffe9, Delete=0xffff
display.sendKey(0xffe3, true);
display.sendKey(0xffe9, true);
display.sendKey(0xffff, true);
display.sendKey(0xffff, false);
display.sendKey(0xffe9, false);
display.sendKey(0xffe3, false);
}, []);
if (error) { if (error) {
return ( return (
<div <div
@@ -162,10 +178,21 @@ const GuacamoleAppInner: React.FC<GuacamoleAppInnerProps> = ({
</div> </div>
)} )}
<GuacamoleDisplay <GuacamoleDisplay
ref={displayRef}
connectionConfig={{ token, protocol, type: protocol }} connectionConfig={{ token, protocol, type: protocol }}
isVisible={true} isVisible={true}
onError={(err) => setConnectionError(err)} onError={(err) => setConnectionError(err)}
/> />
{(protocol === "rdp" || protocol === "vnc") && (
<button
onClick={sendCtrlAltDelete}
title="Ctrl+Alt+Delete"
className="absolute top-3 right-3 z-10 flex items-center gap-1.5 px-2.5 py-1.5 text-[10px] font-semibold uppercase tracking-wider bg-background/80 backdrop-blur border border-border rounded hover:bg-muted transition-colors opacity-60 hover:opacity-100"
>
<Keyboard className="size-3" />
Ctrl+Alt+Del
</button>
)}
</div> </div>
); );
}; };