Skip to content

Commit f58e547

Browse files
committed
feat: cancel paste mode
1 parent 4b08185 commit f58e547

File tree

5 files changed

+129
-98
lines changed

5 files changed

+129
-98
lines changed

jsonrpc.go

Lines changed: 101 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,21 +1062,26 @@ func cancelKeyboardReportMulti() {
10621062
}
10631063
}
10641064

1065-
func rpcKeyboardReportMultiWrapper(macro []map[string]any) (usbgadget.KeysDownState, error) {
1065+
func setKeyboardReportMultiCancel(cancel context.CancelFunc) {
10661066
keyboardReportMultiLock.Lock()
10671067
defer keyboardReportMultiLock.Unlock()
10681068

1069-
if keyboardReportMultiCancel != nil {
1070-
keyboardReportMultiCancel()
1071-
logger.Info().Msg("canceled previous keyboard report multi")
1072-
}
1069+
keyboardReportMultiCancel = cancel
1070+
}
1071+
1072+
func rpcKeyboardReportMultiWrapper(macro []map[string]any) (usbgadget.KeysDownState, error) {
1073+
cancelKeyboardReportMulti()
10731074

10741075
ctx, cancel := context.WithCancel(context.Background())
1075-
keyboardReportMultiCancel = cancel
1076+
setKeyboardReportMultiCancel(cancel)
1077+
1078+
writeJSONRPCEvent("keyboardReportMultiState", true, currentSession)
10761079

10771080
result, err := rpcKeyboardReportMulti(ctx, macro)
10781081

1079-
keyboardReportMultiCancel = nil
1082+
setKeyboardReportMultiCancel(nil)
1083+
1084+
writeJSONRPCEvent("keyboardReportMultiState", false, currentSession)
10801085

10811086
return result, err
10821087
}
@@ -1086,6 +1091,10 @@ var (
10861091
keyboardReportMultiLock sync.Mutex
10871092
)
10881093

1094+
func rpcCancelKeyboardReportMulti() {
1095+
cancelKeyboardReportMulti()
1096+
}
1097+
10891098
func rpcKeyboardReportMulti(ctx context.Context, macro []map[string]any) (usbgadget.KeysDownState, error) {
10901099
var last usbgadget.KeysDownState
10911100
var err error
@@ -1146,88 +1155,89 @@ func rpcKeyboardReportMulti(ctx context.Context, macro []map[string]any) (usbgad
11461155
}
11471156

11481157
var rpcHandlers = map[string]RPCHandler{
1149-
"ping": {Func: rpcPing},
1150-
"reboot": {Func: rpcReboot, Params: []string{"force"}},
1151-
"getDeviceID": {Func: rpcGetDeviceID},
1152-
"deregisterDevice": {Func: rpcDeregisterDevice},
1153-
"getCloudState": {Func: rpcGetCloudState},
1154-
"getNetworkState": {Func: rpcGetNetworkState},
1155-
"getNetworkSettings": {Func: rpcGetNetworkSettings},
1156-
"setNetworkSettings": {Func: rpcSetNetworkSettings, Params: []string{"settings"}},
1157-
"renewDHCPLease": {Func: rpcRenewDHCPLease},
1158-
"keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys"}},
1159-
"keyboardReportMulti": {Func: rpcKeyboardReportMultiWrapper, Params: []string{"macro"}},
1160-
"getKeyboardLedState": {Func: rpcGetKeyboardLedState},
1161-
"keypressReport": {Func: rpcKeypressReport, Params: []string{"key", "press"}},
1162-
"getKeyDownState": {Func: rpcGetKeysDownState},
1163-
"absMouseReport": {Func: rpcAbsMouseReport, Params: []string{"x", "y", "buttons"}},
1164-
"relMouseReport": {Func: rpcRelMouseReport, Params: []string{"dx", "dy", "buttons"}},
1165-
"wheelReport": {Func: rpcWheelReport, Params: []string{"wheelY"}},
1166-
"getVideoState": {Func: rpcGetVideoState},
1167-
"getUSBState": {Func: rpcGetUSBState},
1168-
"unmountImage": {Func: rpcUnmountImage},
1169-
"rpcMountBuiltInImage": {Func: rpcMountBuiltInImage, Params: []string{"filename"}},
1170-
"setJigglerState": {Func: rpcSetJigglerState, Params: []string{"enabled"}},
1171-
"getJigglerState": {Func: rpcGetJigglerState},
1172-
"setJigglerConfig": {Func: rpcSetJigglerConfig, Params: []string{"jigglerConfig"}},
1173-
"getJigglerConfig": {Func: rpcGetJigglerConfig},
1174-
"getTimezones": {Func: rpcGetTimezones},
1175-
"sendWOLMagicPacket": {Func: rpcSendWOLMagicPacket, Params: []string{"macAddress"}},
1176-
"getStreamQualityFactor": {Func: rpcGetStreamQualityFactor},
1177-
"setStreamQualityFactor": {Func: rpcSetStreamQualityFactor, Params: []string{"factor"}},
1178-
"getAutoUpdateState": {Func: rpcGetAutoUpdateState},
1179-
"setAutoUpdateState": {Func: rpcSetAutoUpdateState, Params: []string{"enabled"}},
1180-
"getEDID": {Func: rpcGetEDID},
1181-
"setEDID": {Func: rpcSetEDID, Params: []string{"edid"}},
1182-
"getDevChannelState": {Func: rpcGetDevChannelState},
1183-
"setDevChannelState": {Func: rpcSetDevChannelState, Params: []string{"enabled"}},
1184-
"getUpdateStatus": {Func: rpcGetUpdateStatus},
1185-
"tryUpdate": {Func: rpcTryUpdate},
1186-
"getDevModeState": {Func: rpcGetDevModeState},
1187-
"setDevModeState": {Func: rpcSetDevModeState, Params: []string{"enabled"}},
1188-
"getSSHKeyState": {Func: rpcGetSSHKeyState},
1189-
"setSSHKeyState": {Func: rpcSetSSHKeyState, Params: []string{"sshKey"}},
1190-
"getTLSState": {Func: rpcGetTLSState},
1191-
"setTLSState": {Func: rpcSetTLSState, Params: []string{"state"}},
1192-
"setMassStorageMode": {Func: rpcSetMassStorageMode, Params: []string{"mode"}},
1193-
"getMassStorageMode": {Func: rpcGetMassStorageMode},
1194-
"isUpdatePending": {Func: rpcIsUpdatePending},
1195-
"getUsbEmulationState": {Func: rpcGetUsbEmulationState},
1196-
"setUsbEmulationState": {Func: rpcSetUsbEmulationState, Params: []string{"enabled"}},
1197-
"getUsbConfig": {Func: rpcGetUsbConfig},
1198-
"setUsbConfig": {Func: rpcSetUsbConfig, Params: []string{"usbConfig"}},
1199-
"checkMountUrl": {Func: rpcCheckMountUrl, Params: []string{"url"}},
1200-
"getVirtualMediaState": {Func: rpcGetVirtualMediaState},
1201-
"getStorageSpace": {Func: rpcGetStorageSpace},
1202-
"mountWithHTTP": {Func: rpcMountWithHTTP, Params: []string{"url", "mode"}},
1203-
"mountWithStorage": {Func: rpcMountWithStorage, Params: []string{"filename", "mode"}},
1204-
"listStorageFiles": {Func: rpcListStorageFiles},
1205-
"deleteStorageFile": {Func: rpcDeleteStorageFile, Params: []string{"filename"}},
1206-
"startStorageFileUpload": {Func: rpcStartStorageFileUpload, Params: []string{"filename", "size"}},
1207-
"getWakeOnLanDevices": {Func: rpcGetWakeOnLanDevices},
1208-
"setWakeOnLanDevices": {Func: rpcSetWakeOnLanDevices, Params: []string{"params"}},
1209-
"resetConfig": {Func: rpcResetConfig},
1210-
"setDisplayRotation": {Func: rpcSetDisplayRotation, Params: []string{"params"}},
1211-
"getDisplayRotation": {Func: rpcGetDisplayRotation},
1212-
"setBacklightSettings": {Func: rpcSetBacklightSettings, Params: []string{"params"}},
1213-
"getBacklightSettings": {Func: rpcGetBacklightSettings},
1214-
"getDCPowerState": {Func: rpcGetDCPowerState},
1215-
"setDCPowerState": {Func: rpcSetDCPowerState, Params: []string{"enabled"}},
1216-
"setDCRestoreState": {Func: rpcSetDCRestoreState, Params: []string{"state"}},
1217-
"getActiveExtension": {Func: rpcGetActiveExtension},
1218-
"setActiveExtension": {Func: rpcSetActiveExtension, Params: []string{"extensionId"}},
1219-
"getATXState": {Func: rpcGetATXState},
1220-
"setATXPowerAction": {Func: rpcSetATXPowerAction, Params: []string{"action"}},
1221-
"getSerialSettings": {Func: rpcGetSerialSettings},
1222-
"setSerialSettings": {Func: rpcSetSerialSettings, Params: []string{"settings"}},
1223-
"getUsbDevices": {Func: rpcGetUsbDevices},
1224-
"setUsbDevices": {Func: rpcSetUsbDevices, Params: []string{"devices"}},
1225-
"setUsbDeviceState": {Func: rpcSetUsbDeviceState, Params: []string{"device", "enabled"}},
1226-
"setCloudUrl": {Func: rpcSetCloudUrl, Params: []string{"apiUrl", "appUrl"}},
1227-
"getKeyboardLayout": {Func: rpcGetKeyboardLayout},
1228-
"setKeyboardLayout": {Func: rpcSetKeyboardLayout, Params: []string{"layout"}},
1229-
"getKeyboardMacros": {Func: getKeyboardMacros},
1230-
"setKeyboardMacros": {Func: setKeyboardMacros, Params: []string{"params"}},
1231-
"getLocalLoopbackOnly": {Func: rpcGetLocalLoopbackOnly},
1232-
"setLocalLoopbackOnly": {Func: rpcSetLocalLoopbackOnly, Params: []string{"enabled"}},
1158+
"ping": {Func: rpcPing},
1159+
"reboot": {Func: rpcReboot, Params: []string{"force"}},
1160+
"getDeviceID": {Func: rpcGetDeviceID},
1161+
"deregisterDevice": {Func: rpcDeregisterDevice},
1162+
"getCloudState": {Func: rpcGetCloudState},
1163+
"getNetworkState": {Func: rpcGetNetworkState},
1164+
"getNetworkSettings": {Func: rpcGetNetworkSettings},
1165+
"setNetworkSettings": {Func: rpcSetNetworkSettings, Params: []string{"settings"}},
1166+
"renewDHCPLease": {Func: rpcRenewDHCPLease},
1167+
"keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys"}},
1168+
"keyboardReportMulti": {Func: rpcKeyboardReportMultiWrapper, Params: []string{"macro"}},
1169+
"cancelKeyboardReportMulti": {Func: rpcCancelKeyboardReportMulti},
1170+
"getKeyboardLedState": {Func: rpcGetKeyboardLedState},
1171+
"keypressReport": {Func: rpcKeypressReport, Params: []string{"key", "press"}},
1172+
"getKeyDownState": {Func: rpcGetKeysDownState},
1173+
"absMouseReport": {Func: rpcAbsMouseReport, Params: []string{"x", "y", "buttons"}},
1174+
"relMouseReport": {Func: rpcRelMouseReport, Params: []string{"dx", "dy", "buttons"}},
1175+
"wheelReport": {Func: rpcWheelReport, Params: []string{"wheelY"}},
1176+
"getVideoState": {Func: rpcGetVideoState},
1177+
"getUSBState": {Func: rpcGetUSBState},
1178+
"unmountImage": {Func: rpcUnmountImage},
1179+
"rpcMountBuiltInImage": {Func: rpcMountBuiltInImage, Params: []string{"filename"}},
1180+
"setJigglerState": {Func: rpcSetJigglerState, Params: []string{"enabled"}},
1181+
"getJigglerState": {Func: rpcGetJigglerState},
1182+
"setJigglerConfig": {Func: rpcSetJigglerConfig, Params: []string{"jigglerConfig"}},
1183+
"getJigglerConfig": {Func: rpcGetJigglerConfig},
1184+
"getTimezones": {Func: rpcGetTimezones},
1185+
"sendWOLMagicPacket": {Func: rpcSendWOLMagicPacket, Params: []string{"macAddress"}},
1186+
"getStreamQualityFactor": {Func: rpcGetStreamQualityFactor},
1187+
"setStreamQualityFactor": {Func: rpcSetStreamQualityFactor, Params: []string{"factor"}},
1188+
"getAutoUpdateState": {Func: rpcGetAutoUpdateState},
1189+
"setAutoUpdateState": {Func: rpcSetAutoUpdateState, Params: []string{"enabled"}},
1190+
"getEDID": {Func: rpcGetEDID},
1191+
"setEDID": {Func: rpcSetEDID, Params: []string{"edid"}},
1192+
"getDevChannelState": {Func: rpcGetDevChannelState},
1193+
"setDevChannelState": {Func: rpcSetDevChannelState, Params: []string{"enabled"}},
1194+
"getUpdateStatus": {Func: rpcGetUpdateStatus},
1195+
"tryUpdate": {Func: rpcTryUpdate},
1196+
"getDevModeState": {Func: rpcGetDevModeState},
1197+
"setDevModeState": {Func: rpcSetDevModeState, Params: []string{"enabled"}},
1198+
"getSSHKeyState": {Func: rpcGetSSHKeyState},
1199+
"setSSHKeyState": {Func: rpcSetSSHKeyState, Params: []string{"sshKey"}},
1200+
"getTLSState": {Func: rpcGetTLSState},
1201+
"setTLSState": {Func: rpcSetTLSState, Params: []string{"state"}},
1202+
"setMassStorageMode": {Func: rpcSetMassStorageMode, Params: []string{"mode"}},
1203+
"getMassStorageMode": {Func: rpcGetMassStorageMode},
1204+
"isUpdatePending": {Func: rpcIsUpdatePending},
1205+
"getUsbEmulationState": {Func: rpcGetUsbEmulationState},
1206+
"setUsbEmulationState": {Func: rpcSetUsbEmulationState, Params: []string{"enabled"}},
1207+
"getUsbConfig": {Func: rpcGetUsbConfig},
1208+
"setUsbConfig": {Func: rpcSetUsbConfig, Params: []string{"usbConfig"}},
1209+
"checkMountUrl": {Func: rpcCheckMountUrl, Params: []string{"url"}},
1210+
"getVirtualMediaState": {Func: rpcGetVirtualMediaState},
1211+
"getStorageSpace": {Func: rpcGetStorageSpace},
1212+
"mountWithHTTP": {Func: rpcMountWithHTTP, Params: []string{"url", "mode"}},
1213+
"mountWithStorage": {Func: rpcMountWithStorage, Params: []string{"filename", "mode"}},
1214+
"listStorageFiles": {Func: rpcListStorageFiles},
1215+
"deleteStorageFile": {Func: rpcDeleteStorageFile, Params: []string{"filename"}},
1216+
"startStorageFileUpload": {Func: rpcStartStorageFileUpload, Params: []string{"filename", "size"}},
1217+
"getWakeOnLanDevices": {Func: rpcGetWakeOnLanDevices},
1218+
"setWakeOnLanDevices": {Func: rpcSetWakeOnLanDevices, Params: []string{"params"}},
1219+
"resetConfig": {Func: rpcResetConfig},
1220+
"setDisplayRotation": {Func: rpcSetDisplayRotation, Params: []string{"params"}},
1221+
"getDisplayRotation": {Func: rpcGetDisplayRotation},
1222+
"setBacklightSettings": {Func: rpcSetBacklightSettings, Params: []string{"params"}},
1223+
"getBacklightSettings": {Func: rpcGetBacklightSettings},
1224+
"getDCPowerState": {Func: rpcGetDCPowerState},
1225+
"setDCPowerState": {Func: rpcSetDCPowerState, Params: []string{"enabled"}},
1226+
"setDCRestoreState": {Func: rpcSetDCRestoreState, Params: []string{"state"}},
1227+
"getActiveExtension": {Func: rpcGetActiveExtension},
1228+
"setActiveExtension": {Func: rpcSetActiveExtension, Params: []string{"extensionId"}},
1229+
"getATXState": {Func: rpcGetATXState},
1230+
"setATXPowerAction": {Func: rpcSetATXPowerAction, Params: []string{"action"}},
1231+
"getSerialSettings": {Func: rpcGetSerialSettings},
1232+
"setSerialSettings": {Func: rpcSetSerialSettings, Params: []string{"settings"}},
1233+
"getUsbDevices": {Func: rpcGetUsbDevices},
1234+
"setUsbDevices": {Func: rpcSetUsbDevices, Params: []string{"devices"}},
1235+
"setUsbDeviceState": {Func: rpcSetUsbDeviceState, Params: []string{"device", "enabled"}},
1236+
"setCloudUrl": {Func: rpcSetCloudUrl, Params: []string{"apiUrl", "appUrl"}},
1237+
"getKeyboardLayout": {Func: rpcGetKeyboardLayout},
1238+
"setKeyboardLayout": {Func: rpcSetKeyboardLayout, Params: []string{"layout"}},
1239+
"getKeyboardMacros": {Func: getKeyboardMacros},
1240+
"setKeyboardMacros": {Func: setKeyboardMacros, Params: []string{"params"}},
1241+
"getLocalLoopbackOnly": {Func: rpcGetLocalLoopbackOnly},
1242+
"setLocalLoopbackOnly": {Func: rpcSetLocalLoopbackOnly, Params: []string{"enabled"}},
12331243
}

ui/src/components/InfoBar.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export default function InfoBar() {
2727

2828
const { rpcDataChannel } = useRTCStore();
2929
const { debugMode, mouseMode, showPressedKeys } = useSettingsStore();
30+
const { isPasteModeEnabled } = useHidStore();
3031

3132
useEffect(() => {
3233
if (!rpcDataChannel) return;
@@ -108,7 +109,12 @@ export default function InfoBar() {
108109
<span className="text-xs">{rpcHidStatus}</span>
109110
</div>
110111
)}
111-
112+
{isPasteModeEnabled && (
113+
<div className="flex w-[156px] items-center gap-x-1">
114+
<span className="text-xs font-semibold">Paste Mode:</span>
115+
<span className="text-xs">Enabled</span>
116+
</div>
117+
)}
112118
{showPressedKeys && (
113119
<div className="flex items-center gap-x-1">
114120
<span className="text-xs font-semibold">Keys:</span>

ui/src/components/popovers/PasteModal.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ import notifications from "@/notifications";
1515

1616
export default function PasteModal() {
1717
const TextAreaRef = useRef<HTMLTextAreaElement>(null);
18-
const { setPasteModeEnabled } = useHidStore();
18+
const { isPasteModeEnabled } = useHidStore();
1919
const { setDisableVideoFocusTrap } = useUiStore();
2020

2121
const { send } = useJsonRpc();
22-
const { executeMacro } = useKeyboard();
22+
const { executeMacro, cancelExecuteMacro } = useKeyboard();
2323

2424
const [invalidChars, setInvalidChars] = useState<string[]>([]);
2525
const close = useClose();
@@ -35,10 +35,10 @@ export default function PasteModal() {
3535
}, [send, setKeyboardLayout]);
3636

3737
const onCancelPasteMode = useCallback(() => {
38-
setPasteModeEnabled(false);
38+
cancelExecuteMacro();
3939
setDisableVideoFocusTrap(false);
4040
setInvalidChars([]);
41-
}, [setDisableVideoFocusTrap, setPasteModeEnabled]);
41+
}, [setDisableVideoFocusTrap, cancelExecuteMacro]);
4242

4343
const onConfirmPaste = useCallback(async () => {
4444
// setPasteModeEnabled(false);
@@ -201,6 +201,7 @@ export default function PasteModal() {
201201
size="SM"
202202
theme="primary"
203203
text="Confirm Paste"
204+
disabled={isPasteModeEnabled}
204205
onClick={onConfirmPaste}
205206
LeadingIcon={LuCornerDownLeft}
206207
/>

ui/src/hooks/useKeyboard.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,14 @@ export default function useKeyboard() {
122122
});
123123
};
124124

125+
const cancelExecuteMacro = useCallback(async () => {
126+
send("cancelKeyboardReportMulti", {}, (resp: JsonRpcResponse) => {
127+
if ("error" in resp) {
128+
console.error(`Failed to cancel keyboard report multi`, resp.error);
129+
}
130+
});
131+
}, [send]);
132+
125133
// handleKeyPress is used to handle a key press or release event.
126134
// This function handle both key press and key release events.
127135
// It checks if the keyPressReport API is available and sends the key press event.
@@ -231,5 +239,5 @@ export default function useKeyboard() {
231239
return { modifier: modifiers, keys };
232240
}
233241

234-
return { handleKeyPress, resetKeyboardState, executeMacro };
242+
return { handleKeyPress, resetKeyboardState, executeMacro, cancelExecuteMacro };
235243
}

ui/src/routes/devices.$id.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ export default function KvmIdRoute() {
580580
const { setNetworkState} = useNetworkStateStore();
581581
const { setHdmiState } = useVideoStore();
582582
const {
583-
keyboardLedState, setKeyboardLedState,
583+
keyboardLedState, setKeyboardLedState, setPasteModeEnabled,
584584
keysDownState, setKeysDownState, setUsbState,
585585
} = useHidStore();
586586

@@ -598,6 +598,12 @@ export default function KvmIdRoute() {
598598
setUsbState(usbState);
599599
}
600600

601+
if (resp.method === "keyboardReportMultiState") {
602+
const reportMultiState = resp.params as unknown as boolean;
603+
console.debug("Setting keyboard report multi state", reportMultiState);
604+
setPasteModeEnabled(reportMultiState);
605+
}
606+
601607
if (resp.method === "videoInputState") {
602608
const hdmiState = resp.params as Parameters<VideoState["setHdmiState"]>[0];
603609
console.debug("Setting HDMI state", hdmiState);

0 commit comments

Comments
 (0)