mirror of https://github.com/jetkvm/kvm.git
Added German (T1) mappings, UK mappings, updated UK apple mappings. Added functionality to disable keyboard mapping.
This commit is contained in:
parent
8732a6aff8
commit
40b1c70be0
30
config.go
30
config.go
|
@ -12,25 +12,27 @@ type WakeOnLanDevice struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
CloudURL string `json:"cloud_url"`
|
CloudURL string `json:"cloud_url"`
|
||||||
CloudToken string `json:"cloud_token"`
|
CloudToken string `json:"cloud_token"`
|
||||||
GoogleIdentity string `json:"google_identity"`
|
GoogleIdentity string `json:"google_identity"`
|
||||||
JigglerEnabled bool `json:"jiggler_enabled"`
|
JigglerEnabled bool `json:"jiggler_enabled"`
|
||||||
AutoUpdateEnabled bool `json:"auto_update_enabled"`
|
AutoUpdateEnabled bool `json:"auto_update_enabled"`
|
||||||
KeyboardLayout string `json:"keyboard_layout"`
|
KeyboardLayout string `json:"keyboard_layout"`
|
||||||
IncludePreRelease bool `json:"include_pre_release"`
|
KeyboardMappingEnabled bool `json:"keyboard_mapping_enabled"`
|
||||||
HashedPassword string `json:"hashed_password"`
|
IncludePreRelease bool `json:"include_pre_release"`
|
||||||
LocalAuthToken string `json:"local_auth_token"`
|
HashedPassword string `json:"hashed_password"`
|
||||||
LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration
|
LocalAuthToken string `json:"local_auth_token"`
|
||||||
WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"`
|
LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration
|
||||||
|
WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const configPath = "/userdata/kvm_config.json"
|
const configPath = "/userdata/kvm_config.json"
|
||||||
|
|
||||||
var defaultConfig = &Config{
|
var defaultConfig = &Config{
|
||||||
CloudURL: "https://api.jetkvm.com",
|
CloudURL: "https://api.jetkvm.com",
|
||||||
AutoUpdateEnabled: true, // Set a default value
|
AutoUpdateEnabled: true, // Set a default value
|
||||||
KeyboardLayout: "us",
|
KeyboardLayout: "us",
|
||||||
|
KeyboardMappingEnabled: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
var config *Config
|
var config *Config
|
||||||
|
|
108
jsonrpc.go
108
jsonrpc.go
|
@ -143,6 +143,18 @@ func rpcSetKeyboardLayout(KeyboardLayout string) (string, error) {
|
||||||
return KeyboardLayout, nil
|
return KeyboardLayout, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func rpcGetKeyboardMappingState() (bool, error) {
|
||||||
|
return config.KeyboardMappingEnabled, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func rpcSetKeyboardMappingState(enabled bool) (bool, error) {
|
||||||
|
config.KeyboardMappingEnabled = enabled
|
||||||
|
if err := SaveConfig(); err != nil {
|
||||||
|
return config.KeyboardMappingEnabled, fmt.Errorf("failed to save config: %w", err)
|
||||||
|
}
|
||||||
|
return enabled, nil
|
||||||
|
}
|
||||||
|
|
||||||
var streamFactor = 1.0
|
var streamFactor = 1.0
|
||||||
|
|
||||||
func rpcGetStreamQualityFactor() (float64, error) {
|
func rpcGetStreamQualityFactor() (float64, error) {
|
||||||
|
@ -521,51 +533,53 @@ func rpcResetConfig() error {
|
||||||
|
|
||||||
// TODO: replace this crap with code generator
|
// TODO: replace this crap with code generator
|
||||||
var rpcHandlers = map[string]RPCHandler{
|
var rpcHandlers = map[string]RPCHandler{
|
||||||
"ping": {Func: rpcPing},
|
"ping": {Func: rpcPing},
|
||||||
"getDeviceID": {Func: rpcGetDeviceID},
|
"getDeviceID": {Func: rpcGetDeviceID},
|
||||||
"deregisterDevice": {Func: rpcDeregisterDevice},
|
"deregisterDevice": {Func: rpcDeregisterDevice},
|
||||||
"getCloudState": {Func: rpcGetCloudState},
|
"getCloudState": {Func: rpcGetCloudState},
|
||||||
"keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys"}},
|
"keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys"}},
|
||||||
"absMouseReport": {Func: rpcAbsMouseReport, Params: []string{"x", "y", "buttons"}},
|
"absMouseReport": {Func: rpcAbsMouseReport, Params: []string{"x", "y", "buttons"}},
|
||||||
"wheelReport": {Func: rpcWheelReport, Params: []string{"wheelY"}},
|
"wheelReport": {Func: rpcWheelReport, Params: []string{"wheelY"}},
|
||||||
"getVideoState": {Func: rpcGetVideoState},
|
"getVideoState": {Func: rpcGetVideoState},
|
||||||
"getUSBState": {Func: rpcGetUSBState},
|
"getUSBState": {Func: rpcGetUSBState},
|
||||||
"unmountImage": {Func: rpcUnmountImage},
|
"unmountImage": {Func: rpcUnmountImage},
|
||||||
"rpcMountBuiltInImage": {Func: rpcMountBuiltInImage, Params: []string{"filename"}},
|
"rpcMountBuiltInImage": {Func: rpcMountBuiltInImage, Params: []string{"filename"}},
|
||||||
"setJigglerState": {Func: rpcSetJigglerState, Params: []string{"enabled"}},
|
"setJigglerState": {Func: rpcSetJigglerState, Params: []string{"enabled"}},
|
||||||
"getJigglerState": {Func: rpcGetJigglerState},
|
"getJigglerState": {Func: rpcGetJigglerState},
|
||||||
"sendWOLMagicPacket": {Func: rpcSendWOLMagicPacket, Params: []string{"macAddress"}},
|
"sendWOLMagicPacket": {Func: rpcSendWOLMagicPacket, Params: []string{"macAddress"}},
|
||||||
"getKeyboardLayout": {Func: rpcGetKeyboardLayout},
|
"getKeyboardLayout": {Func: rpcGetKeyboardLayout},
|
||||||
"setKeyboardLayout": {Func: rpcSetKeyboardLayout, Params: []string{"kbLayout"}},
|
"setKeyboardLayout": {Func: rpcSetKeyboardLayout, Params: []string{"kbLayout"}},
|
||||||
"getStreamQualityFactor": {Func: rpcGetStreamQualityFactor},
|
"setKeyboardMappingState": {Func: rpcSetKeyboardMappingState, Params: []string{"enabled"}},
|
||||||
"setStreamQualityFactor": {Func: rpcSetStreamQualityFactor, Params: []string{"factor"}},
|
"getKeyboardMappingState": {Func: rpcGetKeyboardMappingState},
|
||||||
"getAutoUpdateState": {Func: rpcGetAutoUpdateState},
|
"getStreamQualityFactor": {Func: rpcGetStreamQualityFactor},
|
||||||
"setAutoUpdateState": {Func: rpcSetAutoUpdateState, Params: []string{"enabled"}},
|
"setStreamQualityFactor": {Func: rpcSetStreamQualityFactor, Params: []string{"factor"}},
|
||||||
"getEDID": {Func: rpcGetEDID},
|
"getAutoUpdateState": {Func: rpcGetAutoUpdateState},
|
||||||
"setEDID": {Func: rpcSetEDID, Params: []string{"edid"}},
|
"setAutoUpdateState": {Func: rpcSetAutoUpdateState, Params: []string{"enabled"}},
|
||||||
"getDevChannelState": {Func: rpcGetDevChannelState},
|
"getEDID": {Func: rpcGetEDID},
|
||||||
"setDevChannelState": {Func: rpcSetDevChannelState, Params: []string{"enabled"}},
|
"setEDID": {Func: rpcSetEDID, Params: []string{"edid"}},
|
||||||
"getUpdateStatus": {Func: rpcGetUpdateStatus},
|
"getDevChannelState": {Func: rpcGetDevChannelState},
|
||||||
"tryUpdate": {Func: rpcTryUpdate},
|
"setDevChannelState": {Func: rpcSetDevChannelState, Params: []string{"enabled"}},
|
||||||
"getDevModeState": {Func: rpcGetDevModeState},
|
"getUpdateStatus": {Func: rpcGetUpdateStatus},
|
||||||
"setDevModeState": {Func: rpcSetDevModeState, Params: []string{"enabled"}},
|
"tryUpdate": {Func: rpcTryUpdate},
|
||||||
"getSSHKeyState": {Func: rpcGetSSHKeyState},
|
"getDevModeState": {Func: rpcGetDevModeState},
|
||||||
"setSSHKeyState": {Func: rpcSetSSHKeyState, Params: []string{"sshKey"}},
|
"setDevModeState": {Func: rpcSetDevModeState, Params: []string{"enabled"}},
|
||||||
"setMassStorageMode": {Func: rpcSetMassStorageMode, Params: []string{"mode"}},
|
"getSSHKeyState": {Func: rpcGetSSHKeyState},
|
||||||
"getMassStorageMode": {Func: rpcGetMassStorageMode},
|
"setSSHKeyState": {Func: rpcSetSSHKeyState, Params: []string{"sshKey"}},
|
||||||
"isUpdatePending": {Func: rpcIsUpdatePending},
|
"setMassStorageMode": {Func: rpcSetMassStorageMode, Params: []string{"mode"}},
|
||||||
"getUsbEmulationState": {Func: rpcGetUsbEmulationState},
|
"getMassStorageMode": {Func: rpcGetMassStorageMode},
|
||||||
"setUsbEmulationState": {Func: rpcSetUsbEmulationState, Params: []string{"enabled"}},
|
"isUpdatePending": {Func: rpcIsUpdatePending},
|
||||||
"checkMountUrl": {Func: rpcCheckMountUrl, Params: []string{"url"}},
|
"getUsbEmulationState": {Func: rpcGetUsbEmulationState},
|
||||||
"getVirtualMediaState": {Func: rpcGetVirtualMediaState},
|
"setUsbEmulationState": {Func: rpcSetUsbEmulationState, Params: []string{"enabled"}},
|
||||||
"getStorageSpace": {Func: rpcGetStorageSpace},
|
"checkMountUrl": {Func: rpcCheckMountUrl, Params: []string{"url"}},
|
||||||
"mountWithHTTP": {Func: rpcMountWithHTTP, Params: []string{"url", "mode"}},
|
"getVirtualMediaState": {Func: rpcGetVirtualMediaState},
|
||||||
"mountWithWebRTC": {Func: rpcMountWithWebRTC, Params: []string{"filename", "size", "mode"}},
|
"getStorageSpace": {Func: rpcGetStorageSpace},
|
||||||
"mountWithStorage": {Func: rpcMountWithStorage, Params: []string{"filename", "mode"}},
|
"mountWithHTTP": {Func: rpcMountWithHTTP, Params: []string{"url", "mode"}},
|
||||||
"listStorageFiles": {Func: rpcListStorageFiles},
|
"mountWithWebRTC": {Func: rpcMountWithWebRTC, Params: []string{"filename", "size", "mode"}},
|
||||||
"deleteStorageFile": {Func: rpcDeleteStorageFile, Params: []string{"filename"}},
|
"mountWithStorage": {Func: rpcMountWithStorage, Params: []string{"filename", "mode"}},
|
||||||
"startStorageFileUpload": {Func: rpcStartStorageFileUpload, Params: []string{"filename", "size"}},
|
"listStorageFiles": {Func: rpcListStorageFiles},
|
||||||
"getWakeOnLanDevices": {Func: rpcGetWakeOnLanDevices},
|
"deleteStorageFile": {Func: rpcDeleteStorageFile, Params: []string{"filename"}},
|
||||||
"setWakeOnLanDevices": {Func: rpcSetWakeOnLanDevices, Params: []string{"params"}},
|
"startStorageFileUpload": {Func: rpcStartStorageFileUpload, Params: []string{"filename", "size"}},
|
||||||
"resetConfig": {Func: rpcResetConfig},
|
"getWakeOnLanDevices": {Func: rpcGetWakeOnLanDevices},
|
||||||
|
"setWakeOnLanDevices": {Func: rpcSetWakeOnLanDevices, Params: []string{"params"}},
|
||||||
|
"resetConfig": {Func: rpcResetConfig},
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ type SelectMenuProps = Pick<
|
||||||
const sizes = {
|
const sizes = {
|
||||||
XS: "h-[24.5px] pl-3 pr-8 text-xs",
|
XS: "h-[24.5px] pl-3 pr-8 text-xs",
|
||||||
SM: "h-[32px] pl-3 pr-8 text-[13px]",
|
SM: "h-[32px] pl-3 pr-8 text-[13px]",
|
||||||
|
SM_Wide: "h-[32px] pl-3 pr-8 mr-5 text-[13px]",
|
||||||
MD: "h-[40px] pl-4 pr-10 text-sm",
|
MD: "h-[40px] pl-4 pr-10 text-sm",
|
||||||
LG: "h-[48px] pl-4 pr-10 px-5 text-base",
|
LG: "h-[48px] pl-4 pr-10 px-5 text-base",
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,6 +17,10 @@ import useKeyboard from "@/hooks/useKeyboard";
|
||||||
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||||
import { ConnectionErrorOverlay, HDMIErrorOverlay, LoadingOverlay } from "./VideoOverlay";
|
import { ConnectionErrorOverlay, HDMIErrorOverlay, LoadingOverlay } from "./VideoOverlay";
|
||||||
|
|
||||||
|
// TODO Implement keyboard lock API to resolve #127
|
||||||
|
// https://developer.chrome.com/docs/capabilities/web-apis/keyboard-lock
|
||||||
|
// An appropriate error message will need to be displayed in order to alert users to browser compatibility issues.
|
||||||
|
|
||||||
export default function WebRTCVideo() {
|
export default function WebRTCVideo() {
|
||||||
const [keys, setKeys] = useState(useKeyboardMappingsStore.keys);
|
const [keys, setKeys] = useState(useKeyboardMappingsStore.keys);
|
||||||
const [chars, setChars] = useState(useKeyboardMappingsStore.chars);
|
const [chars, setChars] = useState(useKeyboardMappingsStore.chars);
|
||||||
|
@ -155,8 +159,6 @@ export default function WebRTCVideo() {
|
||||||
// Invert the scroll value to match expected behavior
|
// Invert the scroll value to match expected behavior
|
||||||
const invertedScroll = -roundedScroll;
|
const invertedScroll = -roundedScroll;
|
||||||
|
|
||||||
// TODO remove debug logs
|
|
||||||
console.log("wheelReport", { wheelY: invertedScroll });
|
|
||||||
send("wheelReport", { wheelY: invertedScroll });
|
send("wheelReport", { wheelY: invertedScroll });
|
||||||
|
|
||||||
// TODO this is making scrolling feel slow and sluggish, also throwing a violation in chrome
|
// TODO this is making scrolling feel slow and sluggish, also throwing a violation in chrome
|
||||||
|
@ -179,7 +181,7 @@ export default function WebRTCVideo() {
|
||||||
// TODO remove debug logging
|
// TODO remove debug logging
|
||||||
console.log(shiftKey + " " +ctrlKey + " " +altKey + " " +metaKey + " " +mappedKeyModifers.shift + " "+mappedKeyModifers.altLeft + " "+mappedKeyModifers.altRight + " ")
|
console.log(shiftKey + " " +ctrlKey + " " +altKey + " " +metaKey + " " +mappedKeyModifers.shift + " "+mappedKeyModifers.altLeft + " "+mappedKeyModifers.altRight + " ")
|
||||||
|
|
||||||
const filteredModifiers = activeModifiers.filter(Boolean);3
|
const filteredModifiers = activeModifiers.filter(Boolean);
|
||||||
// Example: activeModifiers = [0x01, 0x02, 0x04, 0x08]
|
// Example: activeModifiers = [0x01, 0x02, 0x04, 0x08]
|
||||||
// Assuming 0x01 = ControlLeft, 0x02 = ShiftLeft, 0x04 = AltLeft, 0x08 = MetaLeft
|
// Assuming 0x01 = ControlLeft, 0x02 = ShiftLeft, 0x04 = AltLeft, 0x08 = MetaLeft
|
||||||
return (
|
return (
|
||||||
|
@ -210,8 +212,13 @@ export default function WebRTCVideo() {
|
||||||
modifier =>
|
modifier =>
|
||||||
altKey ||
|
altKey ||
|
||||||
mappedKeyModifers.altLeft ||
|
mappedKeyModifers.altLeft ||
|
||||||
|
(modifier !== modifiers["AltLeft"]),
|
||||||
|
)
|
||||||
|
.filter(
|
||||||
|
modifier =>
|
||||||
|
altKey ||
|
||||||
mappedKeyModifers.altRight ||
|
mappedKeyModifers.altRight ||
|
||||||
(modifier !== modifiers["AltLeft"] && modifier !== modifiers["AltRight"]),
|
(modifier !== modifiers["AltRight"])
|
||||||
)
|
)
|
||||||
// Meta: Keep if Meta is pressed or if the key isn't a Meta key
|
// Meta: Keep if Meta is pressed or if the key isn't a Meta key
|
||||||
// Example: If metaKey is true, keep all modifiers
|
// Example: If metaKey is true, keep all modifiers
|
||||||
|
@ -230,8 +237,9 @@ export default function WebRTCVideo() {
|
||||||
async (e: KeyboardEvent) => {
|
async (e: KeyboardEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const prev = useHidStore.getState();
|
const prev = useHidStore.getState();
|
||||||
let code = e.code;
|
const code = e.code;
|
||||||
const localisedKey = e.key;
|
console.log("MAPPING ENABLED: " + settings.keyboardMappingEnabled)
|
||||||
|
var localisedKey = settings.keyboardMappingEnabled ? e.key : code;
|
||||||
console.log(e);
|
console.log(e);
|
||||||
console.log("Localised Key: " + localisedKey);
|
console.log("Localised Key: " + localisedKey);
|
||||||
|
|
||||||
|
@ -282,12 +290,12 @@ export default function WebRTCVideo() {
|
||||||
// event, so we need to clear the keys after a short delay
|
// event, so we need to clear the keys after a short delay
|
||||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=28089
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=28089
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1299553
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1299553
|
||||||
// TODO add this to the activekey state
|
|
||||||
// TODO set this to remove from activekeystate as well
|
|
||||||
if (e.metaKey) {
|
if (e.metaKey) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const prev = useHidStore.getState();
|
const prev = useHidStore.getState();
|
||||||
sendKeyboardEvent([], newModifiers || prev.activeModifiers);
|
sendKeyboardEvent([], newModifiers || prev.activeModifiers);
|
||||||
|
activeKeyState.current.delete("MetaLeft");
|
||||||
|
activeKeyState.current.delete("MetaRight");
|
||||||
}, 10);
|
}, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,6 +310,7 @@ export default function WebRTCVideo() {
|
||||||
chars,
|
chars,
|
||||||
keys,
|
keys,
|
||||||
modifiers,
|
modifiers,
|
||||||
|
settings,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ export default function PasteModal() {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error("Failed to paste text");
|
notifications.error("Failed to paste text");
|
||||||
}
|
}
|
||||||
}, [rpcDataChannel?.readyState, send, setDisableVideoFocusTrap, setPasteMode]);
|
}, [rpcDataChannel?.readyState, send, setDisableVideoFocusTrap, setPasteMode, chars, keys, modifiers]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (TextAreaRef.current) {
|
if (TextAreaRef.current) {
|
||||||
|
@ -144,6 +144,9 @@ export default function PasteModal() {
|
||||||
The following characters won't be pasted as the current keyboard layout does not contain a valid mapping:{" "}
|
The following characters won't be pasted as the current keyboard layout does not contain a valid mapping:{" "}
|
||||||
{invalidChars.join(", ")}
|
{invalidChars.join(", ")}
|
||||||
</span>
|
</span>
|
||||||
|
<span className="text-xs text-red-500 dark:text-red-400">
|
||||||
|
Tip: You can set your desired keyboard layout in settings, and remember to enable keyboard mapping.
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -79,6 +79,7 @@ export default function SettingsSidebar() {
|
||||||
const settings = useSettingsStore();
|
const settings = useSettingsStore();
|
||||||
const [send] = useJsonRpc();
|
const [send] = useJsonRpc();
|
||||||
const [keyboardLayout, setKeyboardLayout] = useState("us");
|
const [keyboardLayout, setKeyboardLayout] = useState("us");
|
||||||
|
const [kbMappingEnabled, setKeyboardMapping] = useState(false);
|
||||||
const [streamQuality, setStreamQuality] = useState("1");
|
const [streamQuality, setStreamQuality] = useState("1");
|
||||||
const [autoUpdate, setAutoUpdate] = useState(true);
|
const [autoUpdate, setAutoUpdate] = useState(true);
|
||||||
const [devChannel, setDevChannel] = useState(false);
|
const [devChannel, setDevChannel] = useState(false);
|
||||||
|
@ -161,6 +162,20 @@ export default function SettingsSidebar() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleKeyboardMappingChange = (enabled: boolean) => {
|
||||||
|
send("setKeyboardMappingState", { enabled }, resp => {
|
||||||
|
if ("error" in resp) {
|
||||||
|
notifications.error(
|
||||||
|
`Failed to set keyboard maping state state: ${resp.error.data || "Unknown error"}`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
settings.setkeyboardMappingEnabled(enabled);
|
||||||
|
useKeyboardMappingsStore.setMappingsState(enabled);
|
||||||
|
setKeyboardMapping(enabled);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleStreamQualityChange = (factor: string) => {
|
const handleStreamQualityChange = (factor: string) => {
|
||||||
send("setStreamQualityFactor", { factor: Number(factor) }, resp => {
|
send("setStreamQualityFactor", { factor: Number(factor) }, resp => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
|
@ -295,6 +310,13 @@ export default function SettingsSidebar() {
|
||||||
useKeyboardMappingsStore.setLayout(String(resp.result))
|
useKeyboardMappingsStore.setLayout(String(resp.result))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
send("getKeyboardMappingState", {}, resp => {
|
||||||
|
if ("error" in resp) return;
|
||||||
|
setKeyboardMapping(resp.result as boolean);
|
||||||
|
settings.setkeyboardMappingEnabled(resp.result as boolean);
|
||||||
|
useKeyboardMappingsStore.setMappingsState(resp.result as boolean);
|
||||||
|
});
|
||||||
|
|
||||||
send("getStreamQualityFactor", {}, resp => {
|
send("getStreamQualityFactor", {}, resp => {
|
||||||
if ("error" in resp) return;
|
if ("error" in resp) return;
|
||||||
setStreamQuality(String(resp.result));
|
setStreamQuality(String(resp.result));
|
||||||
|
@ -536,20 +558,32 @@ export default function SettingsSidebar() {
|
||||||
description="Customize keyboard behaviour"
|
description="Customize keyboard behaviour"
|
||||||
/>
|
/>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
<SettingsItem
|
||||||
|
title="Enable Keyboard Mapping"
|
||||||
|
description="Enables mapping of keys from your native layout to the layout of the target device"
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
checked={kbMappingEnabled}
|
||||||
|
onChange={e => {
|
||||||
|
handleKeyboardMappingChange(e.target.checked);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
title="Keyboard Layout"
|
title="Keyboard Layout"
|
||||||
description="Set keyboard layout (this should match the target machine)"
|
description="Set keyboard layout (this should match the target machine)"
|
||||||
>
|
>
|
||||||
<SelectMenuBasic
|
<SelectMenuBasic
|
||||||
size="SM"
|
size="SM_Wide"
|
||||||
label=""
|
label=""
|
||||||
// TODO figure out how to make this selector wider like the EDID one?
|
// TODO figure out how to make this selector wider like the EDID one?, (done but not sure if in desired way.)
|
||||||
//fullWidthƒ
|
//fullWidth
|
||||||
value={keyboardLayout}
|
value={keyboardLayout}
|
||||||
options={[
|
options={[
|
||||||
{ value: "uk", label: "GB" },
|
|
||||||
{ value: "uk_apple", label: "GB Apple" },
|
|
||||||
{ value: "us", label: "US" },
|
{ value: "us", label: "US" },
|
||||||
|
{ value: "uk", label: "UK" },
|
||||||
|
{ value: "uk_apple", label: "UK (Apple)" },
|
||||||
|
{ value: "de_t1", label: "German (T1)" },
|
||||||
]}
|
]}
|
||||||
onChange={e => handleKeyboardLayoutChange(e.target.value)}
|
onChange={e => handleKeyboardLayoutChange(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -265,6 +265,9 @@ interface SettingsState {
|
||||||
mouseMode: string;
|
mouseMode: string;
|
||||||
setMouseMode: (mode: string) => void;
|
setMouseMode: (mode: string) => void;
|
||||||
|
|
||||||
|
keyboardMappingEnabled: boolean;
|
||||||
|
setkeyboardMappingEnabled: (enabled: boolean) => void;
|
||||||
|
|
||||||
debugMode: boolean;
|
debugMode: boolean;
|
||||||
setDebugMode: (enabled: boolean) => void;
|
setDebugMode: (enabled: boolean) => void;
|
||||||
|
|
||||||
|
@ -276,6 +279,9 @@ interface SettingsState {
|
||||||
export const useSettingsStore = create(
|
export const useSettingsStore = create(
|
||||||
persist<SettingsState>(
|
persist<SettingsState>(
|
||||||
set => ({
|
set => ({
|
||||||
|
keyboardMappingEnabled: false,
|
||||||
|
setkeyboardMappingEnabled: enabled => set({keyboardMappingEnabled: enabled}),
|
||||||
|
|
||||||
isCursorHidden: false,
|
isCursorHidden: false,
|
||||||
setCursorVisibility: enabled => set({ isCursorHidden: enabled }),
|
setCursorVisibility: enabled => set({ isCursorHidden: enabled }),
|
||||||
|
|
||||||
|
@ -535,18 +541,42 @@ export const useLocalAuthModalStore = create<LocalAuthModalState>(set => ({
|
||||||
class KeyboardMappingsStore {
|
class KeyboardMappingsStore {
|
||||||
private _layout: string = 'us';
|
private _layout: string = 'us';
|
||||||
private _subscribers: (() => void)[] = [];
|
private _subscribers: (() => void)[] = [];
|
||||||
|
private _mappingsEnabled: boolean = false;
|
||||||
|
|
||||||
public keys = getKeyboardMappings(this._layout).keys;
|
public keys = getKeyboardMappings(this._layout).keys;
|
||||||
public chars = getKeyboardMappings(this._layout).chars;
|
public chars = getKeyboardMappings(this._layout).chars;
|
||||||
public modifiers = getKeyboardMappings(this._layout).modifiers;
|
public modifiers = getKeyboardMappings(this._layout).modifiers;
|
||||||
|
|
||||||
|
private mappedKeys = getKeyboardMappings(this._layout).keys;
|
||||||
|
private mappedChars = getKeyboardMappings(this._layout).chars;
|
||||||
|
private mappedModifiers = getKeyboardMappings(this._layout).modifiers;
|
||||||
|
|
||||||
setLayout(newLayout: string) {
|
setLayout(newLayout: string) {
|
||||||
if (this._layout === newLayout) return;
|
if (this._layout === newLayout) return;
|
||||||
this._layout = newLayout;
|
this._layout = newLayout;
|
||||||
const updatedMappings = getKeyboardMappings(newLayout);
|
const updatedMappings = getKeyboardMappings(newLayout);
|
||||||
this.keys = updatedMappings.keys;
|
this.mappedKeys = updatedMappings.keys;
|
||||||
this.chars = updatedMappings.chars;
|
this.mappedChars = updatedMappings.chars;
|
||||||
this.modifiers = updatedMappings.modifiers;
|
this.mappedModifiers = updatedMappings.modifiers;
|
||||||
|
if (this._mappingsEnabled) {
|
||||||
|
this.keys = this.mappedKeys;
|
||||||
|
this.chars = this.mappedChars;
|
||||||
|
this.modifiers = this.mappedModifiers;
|
||||||
|
this._notifySubscribers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setMappingsState(enabled: boolean) {
|
||||||
|
this._mappingsEnabled = enabled;
|
||||||
|
if (this._mappingsEnabled) {
|
||||||
|
this.keys = this.mappedKeys;
|
||||||
|
this.chars = this.mappedChars;
|
||||||
|
this.modifiers = this.mappedModifiers;
|
||||||
|
} else {
|
||||||
|
this.keys = getKeyboardMappings('us').keys;
|
||||||
|
this.chars = getKeyboardMappings('us').chars;
|
||||||
|
this.modifiers = getKeyboardMappings('us').modifiers;
|
||||||
|
}
|
||||||
this._notifySubscribers();
|
this._notifySubscribers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import {keysUKApple, charsUKApple, modifiersUKApple } from './layouts/uk_apple';
|
import {keysUKApple, charsUKApple, modifiersUKApple } from './layouts/uk_apple';
|
||||||
|
import {keysUK, charsUK, modifiersUK } from './layouts/uk';
|
||||||
import {keysUS, charsUS, modifiersUS } from './layouts/us';
|
import {keysUS, charsUS, modifiersUS } from './layouts/us';
|
||||||
|
import { keysDE_T1, charsDE_T1, modifiersDE_T1 } from './layouts/de_t1';
|
||||||
|
|
||||||
export function getKeyboardMappings(layout: string) {
|
export function getKeyboardMappings(layout: string) {
|
||||||
switch (layout) {
|
switch (layout) {
|
||||||
|
@ -9,6 +11,18 @@ export function getKeyboardMappings(layout: string) {
|
||||||
chars: charsUKApple,
|
chars: charsUKApple,
|
||||||
modifiers: modifiersUKApple,
|
modifiers: modifiersUKApple,
|
||||||
};
|
};
|
||||||
|
case "uk":
|
||||||
|
return {
|
||||||
|
keys: keysUK,
|
||||||
|
chars: charsUK,
|
||||||
|
modifiers: modifiersUK,
|
||||||
|
};
|
||||||
|
case "de_t1":
|
||||||
|
return {
|
||||||
|
keys: keysDE_T1,
|
||||||
|
chars: charsDE_T1,
|
||||||
|
modifiers: modifiersDE_T1,
|
||||||
|
};
|
||||||
case "us":
|
case "us":
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
import { charsUS, keysUS, modifiersUS } from "./us";
|
||||||
|
|
||||||
|
export const keysDE_T1 = {
|
||||||
|
...keysUS,
|
||||||
|
} as Record<string, number>;
|
||||||
|
|
||||||
|
export const charsDE_T1 = {
|
||||||
|
...charsUS,
|
||||||
|
|
||||||
|
"y": { key: "KeyZ", shift: false },
|
||||||
|
"Y": { key: "KeyZ", shift: true },
|
||||||
|
"z": { key: "KeyY", shift: false },
|
||||||
|
"Z": { key: "KeyY", shift: true },
|
||||||
|
|
||||||
|
"ä": { key: "Quote", shift: false },
|
||||||
|
"Ä": { key: "Quote", shift: true },
|
||||||
|
"ö": { key: "Semicolon", shift: false },
|
||||||
|
"Ö": { key: "Semicolon", shift: true },
|
||||||
|
"ü": { key: "BracketLeft", shift: false },
|
||||||
|
"Ü": { key: "BracketLeft", shift: true },
|
||||||
|
"ß": { key: "Minus", shift: false },
|
||||||
|
"?": { key: "Minus", shift: true },
|
||||||
|
|
||||||
|
"§": { key: "Digit3", shift: true },
|
||||||
|
"°": { key: "Backquote", shift: true },
|
||||||
|
|
||||||
|
"@": { key: "KeyQ", shift: false, altRight: true },
|
||||||
|
"\"": { key: "Digit2", shift: true },
|
||||||
|
|
||||||
|
"#": { key: "Backslash", shift: false },
|
||||||
|
"'": { key: "Backslash", shift: true },
|
||||||
|
|
||||||
|
".": { key: "Period", shift: false },
|
||||||
|
":": { key: "Period", shift: true },
|
||||||
|
",": { key: "Comma", shift: false },
|
||||||
|
";": { key: "Comma", shift: true },
|
||||||
|
|
||||||
|
"-": { key: "Slash", shift: false },
|
||||||
|
"_": { key: "Slash", shift: true },
|
||||||
|
|
||||||
|
"*": { key: "BracketRight", shift: true },
|
||||||
|
"+": { key: "BracketRight", shift: false },
|
||||||
|
"=": { key: "Digit0", shift: true },
|
||||||
|
"~": { key: "BracketRight", shift: false, altRight: true },
|
||||||
|
"{": { key: "Digit7", shift: false, altRight: true },
|
||||||
|
"}": { key: "Digit0", shift: false, altRight: true },
|
||||||
|
"[": { key: "Digit8", shift: false, altRight: true },
|
||||||
|
"]": { key: "Digit9", shift: false, altRight: true },
|
||||||
|
|
||||||
|
"\\": { key: "Minus", shift: false, altRight: true },
|
||||||
|
"|": { key: "IntlBackslash", shift: true, altRight: true },
|
||||||
|
|
||||||
|
"<": { key: "IntlBackslash", shift: false },
|
||||||
|
">": { key: "IntlBackslash", shift: true },
|
||||||
|
|
||||||
|
"^": {key: "Backquote", shift: false},
|
||||||
|
|
||||||
|
"€": { key: "KeyE", shift: false, altRight: true },
|
||||||
|
|
||||||
|
"²": {key: "Digit2", shift: false, altRight: true },
|
||||||
|
"³": {key: "Digit3", shift: false, altRight: true },
|
||||||
|
|
||||||
|
"μ": {key: "KeyM", shift: false, altRight: true },
|
||||||
|
|
||||||
|
} as Record<string, { key: string; shift: boolean; altLeft?: boolean; altRight?: boolean }>;
|
||||||
|
|
||||||
|
export const modifiersDE_T1 = {
|
||||||
|
...modifiersUS,
|
||||||
|
} as Record<string, number>;
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { charsUS, keysUS, modifiersUS } from "./us";
|
||||||
|
|
||||||
|
export const keysUK = {
|
||||||
|
...keysUS,
|
||||||
|
} as Record<string, number>;
|
||||||
|
|
||||||
|
export const charsUK = {
|
||||||
|
...charsUS,
|
||||||
|
"`": { key: "Backquote", shift: false },
|
||||||
|
"~": { key: "Backslash", shift: true },
|
||||||
|
"\\": { key: "IntlBacklash", shift: false },
|
||||||
|
"|": { key: "IntlBacklash", shift: true },
|
||||||
|
"#": { key: "Backslash", shift: false },
|
||||||
|
"£": { key: "Digit3", shift: true },
|
||||||
|
"@": { key: "Quote", shift: true },
|
||||||
|
"\"": { key: "Digit2", shift: true },
|
||||||
|
"¬": { key: "Backquote", shift: true },
|
||||||
|
"¦": { key: "Backquote", shift: false, altRight: true },
|
||||||
|
"€": { key: "Digit4", shift: false, altRight: true },
|
||||||
|
} as Record<string, { key: string; shift: boolean; altLeft?: boolean; altRight?: boolean; }>;
|
||||||
|
|
||||||
|
export const modifiersUK = {
|
||||||
|
...modifiersUS,
|
||||||
|
} as Record<string, number>;
|
|
@ -16,6 +16,7 @@ export const charsUKApple = {
|
||||||
"£": { key: "Digit3", shift: true },
|
"£": { key: "Digit3", shift: true },
|
||||||
"@": { key: "Digit2", shift: true },
|
"@": { key: "Digit2", shift: true },
|
||||||
"\"": { key: "Quote", shift: true },
|
"\"": { key: "Quote", shift: true },
|
||||||
|
"¬": { key: "KeyL", shift: false, altLeft: true},
|
||||||
} as Record<string, { key: string; shift: boolean; altLeft?: boolean; altRight?: boolean; }>;
|
} as Record<string, { key: string; shift: boolean; altLeft?: boolean; altRight?: boolean; }>;
|
||||||
|
|
||||||
// Modifiers are typically the same between UK and US layouts
|
// Modifiers are typically the same between UK and US layouts
|
||||||
|
|
Loading…
Reference in New Issue