Compare commits

...

25 Commits

Author SHA1 Message Date
Daniel Lorch da92c2138d
Merge 88b22a4378 into fea89a0d23 2025-05-16 17:55:11 +00:00
Daniel Lorch 88b22a4378 Move hold key handling into Go backend analogous to https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt 2025-05-16 16:48:18 +02:00
Marc Brooks 9ed19a451d Change the locale names to their native language
German->Deutsch et. al.
2025-05-16 16:48:18 +02:00
Daniel Lorch 057f6c9d57 Move language name definitions into the keyboard layout files 2025-05-16 16:48:18 +02:00
Daniel Lorch 8d1475f29a Move guard statements outside of loop 2025-05-16 16:48:18 +02:00
Daniel Lorch 9056dea38b Add Czech 2025-05-16 16:48:17 +02:00
Daniel Lorch d8d11a0020 Add Italian 2025-05-16 16:48:17 +02:00
Daniel Lorch 0d28a5d914 Operator precedence 🤦 2025-05-16 16:48:17 +02:00
Daniel Lorch ac42be96e7 Add Norwegian 2025-05-16 16:48:17 +02:00
Daniel Lorch 2feef185c4 Remove default value shift: false 2025-05-16 16:48:15 +02:00
Daniel Lorch 4cbde51ce7 Add more keys to Spanish 2025-05-16 16:47:36 +02:00
Daniel Lorch e4ec2c1d8d Fix fr_FR special characters 2025-05-16 16:47:36 +02:00
Daniel Lorch cb1937aac6 Add Spanish 2025-05-16 16:47:36 +02:00
Daniel Lorch af9c8fad09 Add Swedish 2025-05-16 16:47:36 +02:00
Daniel Lorch dd5eee8179 Add English (UK) 2025-05-16 16:47:36 +02:00
Daniel Lorch 58d59eca47 Add French (France) 2025-05-16 16:47:36 +02:00
Daniel Lorch 66f8d7bcd6 Fix whitespace 2025-05-16 16:47:36 +02:00
Daniel Lorch 926e0b8a06 Change line ordering 2025-05-16 16:47:36 +02:00
Daniel Lorch 384025ecf5 Add Swiss French 2025-05-16 16:47:36 +02:00
Daniel Lorch 768a9f7604 Remove obscure Alt-Gr keys, unsure if they are supported everywhere 2025-05-16 16:47:36 +02:00
Daniel Lorch a255680b8a Improve accent handling 2025-05-16 16:47:32 +02:00
Daniel Lorch 0ae4639275 Fix default value 2025-05-16 16:46:11 +02:00
Daniel Lorch 85e3b22660 Improve error handling and pre-loading 2025-05-16 16:45:32 +02:00
Daniel Lorch c550948bef Trema is the more robust method for capital umlauts 2025-05-16 16:45:32 +02:00
Daniel Lorch 949be95cd5 Enable multiple keyboard layouts for paste text from host 2025-05-16 16:45:21 +02:00
24 changed files with 1784 additions and 138 deletions

View File

@ -87,6 +87,7 @@ type Config struct {
LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration
WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"`
KeyboardMacros []KeyboardMacro `json:"keyboard_macros"`
KeyboardLayout string `json:"keyboard_layout"`
EdidString string `json:"hdmi_edid_string"`
ActiveExtension string `json:"active_extension"`
DisplayRotation string `json:"display_rotation"`
@ -109,6 +110,7 @@ var defaultConfig = &Config{
ActiveExtension: "",
KeyboardMacros: []KeyboardMacro{},
DisplayRotation: "270",
KeyboardLayout: "en-US",
DisplayMaxBrightness: 64,
DisplayDimAfterSec: 120, // 2 minutes
DisplayOffAfterSec: 1800, // 30 minutes

View File

@ -74,7 +74,7 @@ func (u *UsbGadget) keyboardWriteHidFile(data []byte) error {
return nil
}
func (u *UsbGadget) KeyboardReport(modifier uint8, keys []uint8) error {
func (u *UsbGadget) KeyboardReport(modifier uint8, keys []uint8, hold bool) error {
u.keyboardLock.Lock()
defer u.keyboardLock.Unlock()
@ -90,6 +90,13 @@ func (u *UsbGadget) KeyboardReport(modifier uint8, keys []uint8) error {
return err
}
if !hold {
err := u.keyboardWriteHidFile(make([]uint8, 8))
if err != nil {
return err
}
}
u.resetUserInputTime()
return nil
}

View File

@ -888,6 +888,18 @@ func rpcSetScrollSensitivity(sensitivity string) error {
return nil
}
func rpcGetKeyboardLayout() (string, error) {
return config.KeyboardLayout, nil
}
func rpcSetKeyboardLayout(layout string) error {
config.KeyboardLayout = layout
if err := SaveConfig(); err != nil {
return fmt.Errorf("failed to save config: %w", err)
}
return nil
}
func getKeyboardMacros() (interface{}, error) {
macros := make([]KeyboardMacro, len(config.KeyboardMacros))
copy(macros, config.KeyboardMacros)
@ -991,7 +1003,7 @@ var rpcHandlers = map[string]RPCHandler{
"getNetworkSettings": {Func: rpcGetNetworkSettings},
"setNetworkSettings": {Func: rpcSetNetworkSettings, Params: []string{"settings"}},
"renewDHCPLease": {Func: rpcRenewDHCPLease},
"keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys"}},
"keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys", "hold"}},
"absMouseReport": {Func: rpcAbsMouseReport, Params: []string{"x", "y", "buttons"}},
"relMouseReport": {Func: rpcRelMouseReport, Params: []string{"dx", "dy", "buttons"}},
"wheelReport": {Func: rpcWheelReport, Params: []string{"wheelY"}},
@ -1055,6 +1067,8 @@ var rpcHandlers = map[string]RPCHandler{
"setCloudUrl": {Func: rpcSetCloudUrl, Params: []string{"apiUrl", "appUrl"}},
"getScrollSensitivity": {Func: rpcGetScrollSensitivity},
"setScrollSensitivity": {Func: rpcSetScrollSensitivity, Params: []string{"sensitivity"}},
"getKeyboardLayout": {Func: rpcGetKeyboardLayout},
"setKeyboardLayout": {Func: rpcSetKeyboardLayout, Params: []string{"layout"}},
"getKeyboardMacros": {Func: getKeyboardMacros},
"setKeyboardMacros": {Func: setKeyboardMacros, Params: []string{"params"}},
}

View File

@ -8,14 +8,21 @@ import { GridCard } from "@components/Card";
import { TextAreaWithLabel } from "@components/TextArea";
import { SettingsPageHeader } from "@components/SettingsPageheader";
import { useJsonRpc } from "@/hooks/useJsonRpc";
import { useHidStore, useRTCStore, useUiStore } from "@/hooks/stores";
import { chars, keys, modifiers } from "@/keyboardMappings";
import { useHidStore, useRTCStore, useUiStore, useDeviceSettingsStore } from "@/hooks/stores";
import { keys, modifiers } from "@/keyboardMappings";
import { layouts, chars } from "@/keyboardLayouts";
import notifications from "@/notifications";
const hidKeyboardPayload = (keys: number[], modifier: number) => {
return { keys, modifier };
const hidKeyboardPayload = (keys: number[], modifier: number, hold: boolean) => {
return { keys, modifier, hold };
};
const modifierCode = (shift?: boolean, altRight?: boolean) => {
return (shift ? modifiers["ShiftLeft"] : 0)
| (altRight ? modifiers["AltRight"] : 0)
}
const noModifier = 0
export default function PasteModal() {
const TextAreaRef = useRef<HTMLTextAreaElement>(null);
const setPasteMode = useHidStore(state => state.setPasteModeEnabled);
@ -27,6 +34,18 @@ export default function PasteModal() {
const [invalidChars, setInvalidChars] = useState<string[]>([]);
const close = useClose();
const keyboardLayout = useDeviceSettingsStore(state => state.keyboardLayout);
const setKeyboardLayout = useDeviceSettingsStore(
state => state.setKeyboardLayout,
);
useEffect(() => {
send("getKeyboardLayout", {}, resp => {
if ("error" in resp) return;
setKeyboardLayout(resp.result as string);
});
}, []);
const onCancelPasteMode = useCallback(() => {
setPasteMode(false);
setDisableVideoFocusTrap(false);
@ -37,27 +56,40 @@ export default function PasteModal() {
setPasteMode(false);
setDisableVideoFocusTrap(false);
if (rpcDataChannel?.readyState !== "open" || !TextAreaRef.current) return;
if (!keyboardLayout) return;
if (!chars[keyboardLayout]) return;
const text = TextAreaRef.current.value;
try {
for (const char of text) {
const { key, shift } = chars[char] ?? {};
const { key, shift, altRight, deadKey, accentKey } = chars[keyboardLayout][char]
if (!key) continue;
await new Promise<void>((resolve, reject) => {
send(
"keyboardReport",
hidKeyboardPayload([keys[key]], shift ? modifiers["ShiftLeft"] : 0),
params => {
if ("error" in params) return reject(params.error);
send("keyboardReport", hidKeyboardPayload([], 0), params => {
const keyz = [ keys[key] ];
const modz = [ modifierCode(shift, altRight) ];
if (deadKey) {
keyz.push(keys["Space"]);
modz.push(noModifier);
}
if (accentKey) {
keyz.unshift(keys[accentKey.key])
modz.unshift(modifierCode(accentKey.shift, accentKey.altRight))
}
for (const [index, kei] of keyz.entries()) {
await new Promise<void>((resolve, reject) => {
send(
"keyboardReport",
hidKeyboardPayload([kei], modz[index], false),
params => {
if ("error" in params) return reject(params.error);
resolve();
});
},
);
});
},
);
});
}
}
} catch (error) {
console.error(error);
@ -113,7 +145,7 @@ export default function PasteModal() {
// @ts-expect-error TS doesn't recognize Intl.Segmenter in some environments
[...new Intl.Segmenter().segment(value)]
.map(x => x.segment)
.filter(char => !chars[char]),
.filter(char => !chars[keyboardLayout][char]),
),
];
@ -132,6 +164,11 @@ export default function PasteModal() {
)}
</div>
</div>
<div className="space-y-4">
<p className="text-xs text-slate-600 dark:text-slate-400">
Sending key codes using keyboard layout {layouts[keyboardLayout]}
</p>
</div>
</div>
</div>
</div>

View File

@ -348,6 +348,8 @@ export interface DeviceSettingsState {
trackpadThreshold: number;
scrollSensitivity: "low" | "default" | "high";
setScrollSensitivity: (sensitivity: DeviceSettingsState["scrollSensitivity"]) => void;
keyboardLayout: string;
setKeyboardLayout: (layout: string) => void;
}
export const useDeviceSettingsStore = create<DeviceSettingsState>(set => ({
@ -409,6 +411,9 @@ export const useDeviceSettingsStore = create<DeviceSettingsState>(set => ({
scrollSensitivity: sensitivity,
});
},
keyboardLayout: "en_US",
setKeyboardLayout: layout => set({ keyboardLayout: layout }),
}));
export interface RemoteVirtualMediaState {
@ -941,4 +946,5 @@ export const useMacrosStore = create<MacrosState>((set, get) => ({
set({ loading: false });
}
},
}
}));

View File

@ -4,6 +4,10 @@ import { useHidStore, useRTCStore } from "@/hooks/stores";
import { useJsonRpc } from "@/hooks/useJsonRpc";
import { keys, modifiers } from "@/keyboardMappings";
const hidKeyboardPayload = (keys: number[], modifier: number, hold: boolean) => {
return { keys, modifier, hold };
};
export default function useKeyboard() {
const [send] = useJsonRpc();
@ -17,7 +21,7 @@ export default function useKeyboard() {
if (rpcDataChannel?.readyState !== "open") return;
const accModifier = modifiers.reduce((acc, val) => acc + val, 0);
send("keyboardReport", { keys, modifier: accModifier });
send("keyboardReport", hidKeyboardPayload(keys, accModifier, true));
// We do this for the info bar to display the currently pressed keys for the user
updateActiveKeysAndModifiers({ keys: keys, modifiers: modifiers });

42
ui/src/keyboardLayouts.ts Normal file
View File

@ -0,0 +1,42 @@
import { chars as chars_cs_CZ, name as name_cs_CZ } from "@/keyboardLayouts/cs_CZ"
import { chars as chars_en_UK, name as name_en_UK } from "@/keyboardLayouts/en_UK"
import { chars as chars_en_US, name as name_en_US } from "@/keyboardLayouts/en_US"
import { chars as chars_fr_FR, name as name_fr_FR } from "@/keyboardLayouts/fr_FR"
import { chars as chars_de_DE, name as name_de_DE } from "@/keyboardLayouts/de_DE"
import { chars as chars_it_IT, name as name_it_IT } from "@/keyboardLayouts/it_IT"
import { chars as chars_nb_NO, name as name_nb_NO } from "@/keyboardLayouts/nb_NO"
import { chars as chars_es_ES, name as name_es_ES } from "@/keyboardLayouts/es_ES"
import { chars as chars_sv_SE, name as name_sv_SE } from "@/keyboardLayouts/sv_SE"
import { chars as chars_fr_CH, name as name_fr_CH } from "@/keyboardLayouts/fr_CH"
import { chars as chars_de_CH, name as name_de_CH } from "@/keyboardLayouts/de_CH"
type KeyInfo = { key: string | number; shift?: boolean, altRight?: boolean }
export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo }
export const layouts: Record<string, string> = {
cs_CZ: name_cs_CZ,
en_UK: name_en_UK,
en_US: name_en_US,
fr_FR: name_fr_FR,
de_DE: name_de_DE,
it_IT: name_it_IT,
nb_NO: name_nb_NO,
es_ES: name_es_ES,
sv_SE: name_sv_SE,
fr_CH: name_fr_CH,
de_CH: name_de_CH,
}
export const chars: Record<string, Record<string, KeyCombo>> = {
cs_CZ: chars_cs_CZ,
en_UK: chars_en_UK,
en_US: chars_en_US,
fr_FR: chars_fr_FR,
de_DE: chars_de_DE,
it_IT: chars_it_IT,
nb_NO: chars_nb_NO,
es_ES: chars_es_ES,
sv_SE: chars_sv_SE,
fr_CH: chars_fr_CH,
de_CH: chars_de_CH,
};

View File

@ -0,0 +1,244 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Čeština";
const keyTrema = { key: "Backslash" } // tréma (umlaut), two dots placed above a vowel
const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "Digit3", shift: true, altRight: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyCaron = { key: "Equal", shift: true } // caron or haček (inverted hat), mark ˇ placed above the letter
const keyGrave = { key: "Digit7", shift: true, altRight: true } // accent grave, mark ` placed above the letter
const keyTilde = { key: "Digit1", shift: true, altRight: true } // tilde, mark ~ placed above the letter
const keyRing = { key: "Backquote", shift: true } // kroužek (little ring), mark ° placed above the letter
const keyOverdot = { key: "Digit8", shift: true, altRight: true } // overdot (dot above), mark ˙ placed above the letter
const keyHook = { key: "Digit6", shift: true, altRight: true } // ogonoek (little hook), mark ˛ placed beneath a letter
const keyCedille = { key: "Equal", shift: true, altRight: true } // accent cedille (cedilla), mark ¸ placed beneath a letter
export const chars = {
A: { key: "KeyA", shift: true },
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
"Ȧ": { key: "KeyA", shift: true, accentKey: keyOverdot },
"Ą": { key: "KeyA", shift: true, accentKey: keyHook },
B: { key: "KeyB", shift: true },
"Ḃ": { key: "KeyB", shift: true, accentKEy: keyOverdot },
C: { key: "KeyC", shift: true },
"Č": { key: "KeyC", shift: true, accentKey: keyCaron },
"Ċ": { key: "KeyC", shift: true, accentKey: keyOverdot },
"Ç": { key: "KeyC", shift: true, accentKey: keyCedille },
D: { key: "KeyD", shift: true },
"Ď": { key: "KeyD", shift: true, accentKey: keyCaron },
"Ḋ": { key: "KeyD", shift: true, accentKey: keyOverdot },
E: { key: "KeyE", shift: true },
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
"Ě": { key: "KeyE", shift: true, accentKey: keyCaron },
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
"Ė": { key: "KeyE", shift: true, accentKEy: keyOverdot },
"Ę": { key: "KeyE", shift: true, accentKey: keyHook },
F: { key: "KeyF", shift: true },
"Ḟ": { key: "KeyF", shift: true, accentKey: keyOverdot },
G: { key: "KeyG", shift: true },
"Ġ": { key: "KeyG", shift: true, accentKey: keyOverdot },
H: { key: "KeyH", shift: true },
"Ḣ": { key: "KeyH", shift: true, accentKey: keyOverdot },
I: { key: "KeyI", shift: true },
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
"İ": { key: "KeyI", shift: true, accentKey: keyOverdot },
"Į": { key: "KeyI", shift: true, accentKey: keyHook },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
"Ŀ": { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
"Ṁ": { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
"Ň": { key: "KeyN", shift: true, accentKey: keyCaron },
"Ñ": { key: "KeyN", shift: true, accentKey: keyTilde },
"Ṅ": { key: "KeyN", shift: true, accentKEy: keyOverdot },
O: { key: "KeyO", shift: true },
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
"Ȯ": { key: "KeyO", shift: true, accentKey: keyOverdot },
"Ǫ": { key: "KeyO", shift: true, accentKey: keyHook },
P: { key: "KeyP", shift: true },
"Ṗ": { key: "KeyP", shift: true, accentKey: keyOverdot },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
"Ř": { key: "KeyR", shift: true, accentKey: keyCaron },
"Ṙ": { key: "KeyR", shift: true, accentKey: keyOverdot },
S: { key: "KeyS", shift: true },
"Š": { key: "KeyS", shift: true, accentKey: keyCaron },
"Ṡ": { key: "KeyS", shift: true, accentKey: keyOverdot },
T: { key: "KeyT", shift: true },
"Ť": { key: "KeyT", shift: true, accentKey: keyCaron },
"Ṫ": { key: "KeyT", shift: true, accentKey: keyOverdot },
U: { key: "KeyU", shift: true },
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
"Ů": { key: "KeyU", shift: true, accentKey: keyRing },
"Ų": { key: "KeyU", shift: true, accentKey: keyHook },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
"Ẇ": { key: "KeyW", shift: true, accentKey: keyOverdot },
X: { key: "KeyX", shift: true },
"Ẋ": { key: "KeyX", shift: true, accentKey: keyOverdot },
Y: { key: "KeyY", shift: true },
"Ý": { key: "KeyY", shift: true, accentKey: keyAcute },
"Ẏ": { key: "KeyY", shift: true, accentKey: keyOverdot },
Z: { key: "KeyZ", shift: true },
"Ż": { key: "KeyZ", shift: true, accentKey: keyOverdot },
a: { key: "KeyA" },
"ä": { key: "KeyA", accentKey: keyTrema },
"â": { key: "KeyA", accentKey: keyHat },
"à": { key: "KeyA", accentKey: keyGrave },
"ã": { key: "KeyA", accentKey: keyTilde },
"ȧ": { key: "KeyA", accentKey: keyOverdot },
"ą": { key: "KeyA", accentKey: keyHook },
b: { key: "KeyB" },
"{": { key: "KeyB", altRight: true },
"ḃ": { key: "KeyB", accentKey: keyOverdot },
c: { key: "KeyC" },
"&": { key: "KeyC", altRight: true },
"ç": { key: "KeyC", accentKey: keyCedille },
"ċ": { key: "KeyC", accentKey: keyOverdot },
d: { key: "KeyD" },
"ď": { key: "KeyD", accentKey: keyCaron },
"ḋ": { key: "KeyD", accentKey: keyOverdot },
"Đ": { key: "KeyD", altRight: true },
e: { key: "KeyE" },
"ë": { key: "KeyE", accentKey: keyTrema },
"ê": { key: "KeyE", accentKey: keyHat },
"ẽ": { key: "KeyE", accentKey: keyTilde },
"è": { key: "KeyE", accentKey: keyGrave },
"ė": { key: "KeyE", accentKey: keyOverdot },
"ę": { key: "KeyE", accentKey: keyHook },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
"ḟ": { key: "KeyF", accentKey: keyOverdot },
"[": { key: "KeyF", altRight: true },
g: { key: "KeyG" },
"ġ": { key: "KeyG", accentKey: keyOverdot },
"]": { key: "KeyF", altRight: true },
h: { key: "KeyH" },
"ḣ": { key: "KeyH", accentKey: keyOverdot },
i: { key: "KeyI" },
"ï": { key: "KeyI", accentKey: keyTrema },
"î": { key: "KeyI", accentKey: keyHat },
"ì": { key: "KeyI", accentKey: keyGrave },
"ĩ": { key: "KeyI", accentKey: keyTilde },
"ı": { key: "KeyI", accentKey: keyOverdot },
"į": { key: "KeyI", accentKey: keyHook },
j: { key: "KeyJ" },
"ȷ": { key: "KeyJ", accentKey: keyOverdot },
k: { key: "KeyK" },
"ł": { key: "KeyK", altRight: true },
l: { key: "KeyL" },
"ŀ": { key: "KeyL", accentKey: keyOverdot },
"Ł": { key: "KeyL", altRight: true },
m: { key: "KeyM" },
"ṁ": { key: "KeyM", accentKey: keyOverdot },
n: { key: "KeyN" },
"}": { key: "KeyN", altRight: true },
"ň": { key: "KeyN", accentKey: keyCaron },
"ñ": { key: "KeyN", accentKey: keyTilde },
"ṅ": { key: "KeyN", accentKey: keyOverdot },
o: { key: "KeyO" },
"ö": { key: "Key0", accentKey: keyTrema },
"ó": { key: "KeyO", accentKey: keyAcute },
"ô": { key: "KeyO", accentKey: keyHat },
"ò": { key: "KeyO", accentKey: keyGrave },
"õ": { key: "KeyO", accentKey: keyTilde },
"ȯ": { key: "KeyO", accentKey: keyOverdot },
"ǫ": { key: "KeyO", accentKey: keyHook },
p: { key: "KeyP" },
"ṗ": { key: "KeyP", accentKey: keyOverdot },
q: { key: "KeyQ" },
r: { key: "KeyR" },
"ṙ": { key: "KeyR", accentKey: keyOverdot },
s: { key: "KeyS" },
"ṡ": { key: "KeyS", accentKey: keyOverdot },
"đ": { key: "KeyS", altRight: true },
t: { key: "KeyT" },
"ť": { key: "KeyT", accentKey: keyCaron },
"ṫ": { key: "KeyT", accentKey: keyOverdot },
u: { key: "KeyU" },
"ü": { key: "KeyU", accentKey: keyTrema },
"û": { key: "KeyU", accentKey: keyHat },
"ù": { key: "KeyU", accentKey: keyGrave },
"ũ": { key: "KeyU", accentKey: keyTilde },
"ų": { key: "KeyU", accentKey: keyHook },
v: { key: "KeyV" },
"@": { key: "KeyV", altRight: true },
w: { key: "KeyW" },
"ẇ": { key: "KeyW", accentKey: keyOverdot },
x: { key: "KeyX" },
"#": { key: "KeyX", altRight: true },
"ẋ": { key: "KeyX", accentKey: keyOverdot },
y: { key: "KeyY" },
"ẏ": { key: "KeyY", accentKey: keyOverdot },
z: { key: "KeyZ" },
"ż": { key: "KeyZ", accentKey: keyOverdot },
";": { key: "Backquote" },
"°": { key: "Backquote", shift: true, deadKey: true },
"+": { key: "Digit1" },
1: { key: "Digit1", shift: true },
"ě": { key: "Digit2" },
2: { key: "Digit2", shift: true },
"š": { key: "Digit3" },
3: { key: "Digit3", shift: true },
"č": { key: "Digit4" },
4: { key: "Digit4", shift: true },
"ř": { key: "Digit5" },
5: { key: "Digit5", shift: true },
"ž": { key: "Digit6" },
6: { key: "Digit6", shift: true },
"ý": { key: "Digit7" },
7: { key: "Digit7", shift: true },
"á": { key: "Digit8" },
8: { key: "Digit8", shift: true },
"í": { key: "Digit9" },
9: { key: "Digit9", shift: true },
"é": { key: "Digit0" },
0: { key: "Digit0", shift: true },
"=": { key: "Minus" },
"%": { key: "Minus", shift: true },
"ú": { key: "BracketLeft" },
"/": { key: "BracketLeft", shift: true },
")": { key: "BracketRight" },
"(": { key: "BracketRight", shift: true },
"ů": { key: "Semicolon" },
"\"": { key: "Semicolon", shift: true },
"§": { key: "Quote" },
"!": { key: "Quote", shift: true },
"'": { key: "Backslash", shift: true },
",": { key: "Comma" },
"?": { key: "Comma", shift: true },
"<": { key: "Comma", altRight: true },
".": { key: "Period" },
":": { key: "Period", shift: true },
">": { key: "Period", altRight: true },
"-": { key: "Slash" },
"_": { key: "Slash", shift: true },
"*": { key: "Slash", altRight: true },
"\\": { key: "IntlBackslash" },
"|": { key: "IntlBackslash", shift: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,165 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Schwiizerdütsch";
const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
const keyAcute = { key: "Minus", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "Equal" } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
const keyTilde = { key: "Equal", altRight: true } // tilde, mark ~ placed above the letter
export const chars = {
A: { key: "KeyA", shift: true },
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyZ", shift: true },
Z: { key: "KeyY", shift: true },
a: { key: "KeyA" },
"á": { key: "KeyA", accentKey: keyAcute },
"â": { key: "KeyA", accentKey: keyHat },
"ã": { key: "KeyA", accentKey: keyTilde },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
"ë": { key: "KeyE", accentKey: keyTrema },
"ê": { key: "KeyE", accentKey: keyHat },
"ẽ": { key: "KeyE", accentKey: keyTilde },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
"ï": { key: "KeyI", accentKey: keyTrema },
"í": { key: "KeyI", accentKey: keyAcute },
"î": { key: "KeyI", accentKey: keyHat },
"ì": { key: "KeyI", accentKey: keyGrave },
"ĩ": { key: "KeyI", accentKey: keyTilde },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "KeyM" },
n: { key: "KeyN" },
o: { key: "KeyO" },
"ó": { key: "KeyO", accentKey: keyAcute },
"ô": { key: "KeyO", accentKey: keyHat },
"ò": { key: "KeyO", accentKey: keyGrave },
"õ": { key: "KeyO", accentKey: keyTilde },
p: { key: "KeyP" },
q: { key: "KeyQ" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
"ú": { key: "KeyU", accentKey: keyAcute },
"û": { key: "KeyU", accentKey: keyHat },
"ù": { key: "KeyU", accentKey: keyGrave },
"ũ": { key: "KeyU", accentKey: keyTilde },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyZ" },
z: { key: "KeyY" },
"§": { key: "Backquote" },
"°": { key: "Backquote", shift: true },
1: { key: "Digit1" },
"+": { key: "Digit1", shift: true },
"|": { key: "Digit1", altRight: true },
2: { key: "Digit2" },
"\"": { key: "Digit2", shift: true },
"@": { key: "Digit2", altRight: true },
3: { key: "Digit3" },
"*": { key: "Digit3", shift: true },
"#": { key: "Digit3", altRight: true },
4: { key: "Digit4" },
"ç": { key: "Digit4", shift: true },
5: { key: "Digit5" },
"%": { key: "Digit5", shift: true },
6: { key: "Digit6" },
"&": { key: "Digit6", shift: true },
7: { key: "Digit7" },
"/": { key: "Digit7", shift: true },
8: { key: "Digit8" },
"(": { key: "Digit8", shift: true },
9: { key: "Digit9" },
")": { key: "Digit9", shift: true },
0: { key: "Digit0" },
"=": { key: "Digit0", shift: true },
"'": { key: "Minus" },
"?": { key: "Minus", shift: true },
"^": { key: "Equal", deadKey: true },
"`": { key: "Equal", shift: true },
"~": { key: "Equal", altRight: true, deadKey: true },
"ü": { key: "BracketLeft" },
"è": { key: "BracketLeft", shift: true },
"[": { key: "BracketLeft", altRight: true },
"!": { key: "BracketRight", shift: true },
"]": { key: "BracketRight", altRight: true },
"ö": { key: "Semicolon" },
"é": { key: "Semicolon", shift: true },
"ä": { key: "Quote" },
"à": { key: "Quote", shift: true },
"{": { key: "Quote", altRight: true },
"$": { key: "Backslash" },
"£": { key: "Backslash", shift: true },
"}": { key: "Backslash", altRight: true },
",": { key: "Comma" },
";": { key: "Comma", shift: true },
".": { key: "Period" },
":": { key: "Period", shift: true },
"-": { key: "Slash" },
"_": { key: "Slash", shift: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
"\\": { key: "IntlBackslash", altRight: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,152 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Deutsch";
const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "Backquote" } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
export const chars = {
A: { key: "KeyA", shift: true },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyZ", shift: true },
Z: { key: "KeyY", shift: true },
a: { key: "KeyA" },
"á": { key: "KeyA", accentKey: keyAcute },
"â": { key: "KeyA", accentKey: keyHat },
"à": { key: "KeyA", accentKey: keyGrave},
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
"é": { key: "KeyE", accentKey: keyAcute},
"ê": { key: "KeyE", accentKey: keyHat },
"è": { key: "KeyE", accentKey: keyGrave },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
"í": { key: "KeyI", accentKey: keyAcute },
"î": { key: "KeyI", accentKey: keyHat },
"ì": { key: "KeyI", accentKey: keyGrave },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "KeyM" },
"µ": { key: "KeyM", altRight: true },
n: { key: "KeyN" },
o: { key: "KeyO" },
"ó": { key: "KeyO", accentKey: keyAcute },
"ô": { key: "KeyO", accentKey: keyHat },
"ò": { key: "KeyO", accentKey: keyGrave },
p: { key: "KeyP" },
q: { key: "KeyQ" },
"@": { key: "KeyQ", altRight: true },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
"ú": { key: "KeyU", accentKey: keyAcute },
"û": { key: "KeyU", accentKey: keyHat },
"ù": { key: "KeyU", accentKey: keyGrave },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyZ" },
z: { key: "KeyY" },
"°": { key: "Backquote", shift: true },
"^": { key: "Backquote", deadKey: true },
1: { key: "Digit1" },
"!": { key: "Digit1", shift: true },
2: { key: "Digit2" },
"\"": { key: "Digit2", shift: true },
"²": { key: "Digit2", altRight: true },
3: { key: "Digit3" },
"§": { key: "Digit3", shift: true },
"³": { key: "Digit3", altRight: true },
4: { key: "Digit4" },
"$": { key: "Digit4", shift: true },
5: { key: "Digit5" },
"%": { key: "Digit5", shift: true },
6: { key: "Digit6" },
"&": { key: "Digit6", shift: true },
7: { key: "Digit7" },
"/": { key: "Digit7", shift: true },
"{": { key: "Digit7", altRight: true },
8: { key: "Digit8" },
"(": { key: "Digit8", shift: true },
"[": { key: "Digit8", altRight: true },
9: { key: "Digit9" },
")": { key: "Digit9", shift: true },
"]": { key: "Digit9", altRight: true },
0: { key: "Digit0" },
"=": { key: "Digit0", shift: true },
"}": { key: "Digit0", altRight: true },
"ß": { key: "Minus" },
"?": { key: "Minus", shift: true },
"\\": { key: "Minus", altRight: true },
"´": { key: "Equal", deadKey: true },
"`": { key: "Equal", shift: true, deadKey: true },
"ü": { key: "BracketLeft" },
"Ü": { key: "BracketLeft", shift: true },
"+": { key: "BracketRight" },
"*": { key: "BracketRight", shift: true },
"~": { key: "BracketRight", altRight: true },
"ö": { key: "Semicolon" },
"Ö": { key: "Semicolon", shift: true },
"ä": { key: "Quote" },
"Ä": { key: "Quote", shift: true },
"#": { key: "Backslash" },
"'": { key: "Backslash", shift: true },
",": { key: "Comma" },
";": { key: "Comma", shift: true },
".": { key: "Period" },
":": { key: "Period", shift: true },
"-": { key: "Slash" },
"_": { key: "Slash", shift: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
"|": { key: "IntlBackslash", altRight: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,107 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "English (UK)";
export const chars = {
A: { key: "KeyA", shift: true },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyY", shift: true },
Z: { key: "KeyZ", shift: true },
a: { key: "KeyA" },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "KeyM" },
n: { key: "KeyN" },
o: { key: "KeyO" },
p: { key: "KeyP" },
q: { key: "KeyQ" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyY" },
z: { key: "KeyZ" },
1: { key: "Digit1" },
"!": { key: "Digit1", shift: true },
2: { key: "Digit2" },
"\"": { key: "Digit2", shift: true },
3: { key: "Digit3" },
"£": { key: "Digit3", shift: true },
4: { key: "Digit4" },
$: { key: "Digit4", shift: true },
"€": { key: "Digit4", altRight: true },
5: { key: "Digit5" },
"%": { key: "Digit5", shift: true },
6: { key: "Digit6" },
"^": { key: "Digit6", shift: true },
7: { key: "Digit7" },
"&": { key: "Digit7", shift: true },
8: { key: "Digit8" },
"*": { key: "Digit8", shift: true },
9: { key: "Digit9" },
"(": { key: "Digit9", shift: true },
0: { key: "Digit0" },
")": { key: "Digit0", shift: true },
"-": { key: "Minus" },
_: { key: "Minus", shift: true },
"=": { key: "Equal" },
"+": { key: "Equal", shift: true },
"'": { key: "Quote" },
'@': { key: "Quote", shift: true },
",": { key: "Comma" },
"<": { key: "Comma", shift: true },
"/": { key: "Slash" },
"?": { key: "Slash", shift: true },
".": { key: "Period" },
">": { key: "Period", shift: true },
";": { key: "Semicolon" },
":": { key: "Semicolon", shift: true },
"[": { key: "BracketLeft" },
"{": { key: "BracketLeft", shift: true },
"]": { key: "BracketRight" },
"}": { key: "BracketRight", shift: true },
"#": { key: "Backslash" },
"~": { key: "Backslash", shift: true },
"`": { key: "Backquote" },
"¬": { key: "Backquote", shift: true },
"\\": { key: "IntlBackslash" },
"|": { key: "IntlBackslash", shift: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>

View File

@ -0,0 +1,113 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "English (US)";
export const chars = {
A: { key: "KeyA", shift: true },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyY", shift: true },
Z: { key: "KeyZ", shift: true },
a: { key: "KeyA" },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "KeyM" },
n: { key: "KeyN" },
o: { key: "KeyO" },
p: { key: "KeyP" },
q: { key: "KeyQ" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyY" },
z: { key: "KeyZ" },
1: { key: "Digit1" },
"!": { key: "Digit1", shift: true },
2: { key: "Digit2" },
"@": { key: "Digit2", shift: true },
3: { key: "Digit3" },
"#": { key: "Digit3", shift: true },
4: { key: "Digit4" },
$: { key: "Digit4", shift: true },
"%": { key: "Digit5", shift: true },
5: { key: "Digit5" },
"^": { key: "Digit6", shift: true },
6: { key: "Digit6" },
"&": { key: "Digit7", shift: true },
7: { key: "Digit7" },
"*": { key: "Digit8", shift: true },
8: { key: "Digit8" },
"(": { key: "Digit9", shift: true },
9: { key: "Digit9" },
")": { key: "Digit0", shift: true },
0: { key: "Digit0" },
"-": { key: "Minus" },
_: { key: "Minus", shift: true },
"=": { key: "Equal" },
"+": { key: "Equal", shift: true },
"'": { key: "Quote" },
'"': { key: "Quote", shift: true },
",": { key: "Comma" },
"<": { key: "Comma", shift: true },
"/": { key: "Slash" },
"?": { key: "Slash", shift: true },
".": { key: "Period" },
">": { key: "Period", shift: true },
";": { key: "Semicolon" },
":": { key: "Semicolon", shift: true },
"[": { key: "BracketLeft" },
"{": { key: "BracketLeft", shift: true },
"]": { key: "BracketRight" },
"}": { key: "BracketRight", shift: true },
"\\": { key: "Backslash" },
"|": { key: "Backslash", shift: true },
"`": { key: "Backquote" },
"~": { key: "Backquote", shift: true },
"§": { key: "IntlBackslash" },
"±": { key: "IntlBackslash", shift: true },
" ": { key: "Space", shift: false },
"\n": { key: "Enter", shift: false },
Enter: { key: "Enter", shift: false },
Tab: { key: "Tab", shift: false },
PrintScreen: { key: "Prt Sc", shift: false },
SystemRequest: { key: "Prt Sc", shift: true },
ScrollLock: { key: "ScrollLock", shift: false},
Pause: { key: "Pause", shift: false },
Break: { key: "Pause", shift: true },
Insert: { key: "Insert", shift: false },
Delete: { key: "Delete", shift: false },
} as Record<string, KeyCombo>

View File

@ -0,0 +1,168 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Español";
const keyTrema = { key: "Quote", shift: true } // tréma (umlaut), two dots placed above a vowel
const keyAcute = { key: "Quote" } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave = { key: "BracketRight" } // accent grave, mark ` placed above the letter
const keyTilde = { key: "Key4", altRight: true } // tilde, mark ~ placed above the letter
export const chars = {
A: { key: "KeyA", shift: true },
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyY", shift: true },
Z: { key: "KeyZ", shift: true },
a: { key: "KeyA" },
"ä": { key: "KeyA", accentKey: keyTrema },
"á": { key: "KeyA", accentKey: keyAcute },
"â": { key: "KeyA", accentKey: keyHat },
"à": { key: "KeyA", accentKey: keyGrave },
"ã": { key: "KeyA", accentKey: keyTilde },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
"ë": { key: "KeyE", accentKey: keyTrema },
"é": { key: "KeyE", accentKey: keyAcute },
"ê": { key: "KeyE", accentKey: keyHat },
"è": { key: "KeyE", accentKey: keyGrave },
"ẽ": { key: "KeyE", accentKey: keyTilde },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
"ï": { key: "KeyI", accentKey: keyTrema },
"í": { key: "KeyI", accentKey: keyAcute },
"î": { key: "KeyI", accentKey: keyHat },
"ì": { key: "KeyI", accentKey: keyGrave },
"ĩ": { key: "KeyI", accentKey: keyTilde },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "KeyM" },
n: { key: "KeyN" },
o: { key: "KeyO" },
"ö": { key: "KeyO", accentKey: keyTrema },
"ó": { key: "KeyO", accentKey: keyAcute },
"ô": { key: "KeyO", accentKey: keyHat },
"ò": { key: "KeyO", accentKey: keyGrave },
"õ": { key: "KeyO", accentKey: keyTilde },
p: { key: "KeyP" },
q: { key: "KeyQ" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
"ü": { key: "KeyU", accentKey: keyTrema },
"ú": { key: "KeyU", accentKey: keyAcute },
"û": { key: "KeyU", accentKey: keyHat },
"ù": { key: "KeyU", accentKey: keyGrave },
"ũ": { key: "KeyU", accentKey: keyTilde },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyY" },
z: { key: "KeyZ" },
"º": { key: "Backquote" },
"ª": { key: "Backquote", shift: true },
"\\": { key: "Backquote", altRight: true },
1: { key: "Digit1" },
"!": { key: "Digit1", shift: true },
"|": { key: "Digit1", altRight: true },
2: { key: "Digit2" },
"\"": { key: "Digit2", shift: true },
"@": { key: "Digit2", altRight: true },
3: { key: "Digit3" },
"·": { key: "Digit3", shift: true },
"#": { key: "Digit3", altRight: true },
4: { key: "Digit4" },
"$": { key: "Digit4", shift: true },
5: { key: "Digit5" },
"%": { key: "Digit5", shift: true },
6: { key: "Digit6" },
"&": { key: "Digit6", shift: true },
"¬": { key: "Digit6", altRight: true },
7: { key: "Digit7" },
"/": { key: "Digit7", shift: true },
8: { key: "Digit8" },
"(": { key: "Digit8", shift: true },
9: { key: "Digit9" },
")": { key: "Digit9", shift: true },
0: { key: "Digit0" },
"=": { key: "Digit0", shift: true },
"'": { key: "Minus" },
"?": { key: "Minus", shift: true },
"¡": { key: "Equal", deadKey: true },
"¿": { key: "Equal", shift: true },
"[": { key: "BracketLeft", altRight: true },
"+": { key: "BracketRight" },
"*": { key: "BracketRight", shift: true },
"]": { key: "BracketRight", altRight: true },
"ñ": { key: "Semicolon" },
"Ñ": { key: "Semicolon", shift: true },
"{": { key: "Quote", altRight: true },
"ç": { key: "Backslash" },
"Ç": { key: "Backslash", shift: true },
"}": { key: "Backslash", altRight: true },
",": { key: "Comma" },
";": { key: "Comma", shift: true },
".": { key: "Period" },
":": { key: "Period", shift: true },
"-": { key: "Slash" },
"_": { key: "Slash", shift: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,14 @@
import { KeyCombo } from "../keyboardLayouts"
import { chars as chars_de_CH } from "./de_CH"
export const name = "Français de Suisse";
export const chars = {
...chars_de_CH,
"è": { key: "BracketLeft" },
"ü": { key: "BracketLeft", shift: true },
"é": { key: "Semicolon" },
"ö": { key: "Semicolon", shift: true },
"à": { key: "Quote" },
"ä": { key: "Quote", shift: true },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,139 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Français";
const keyTrema = { key: "BracketLeft", shift: true } // tréma (umlaut), two dots placed above a vowel
const keyHat = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ placed above the letter
export const chars = {
A: { key: "KeyQ", shift: true },
"Ä": { key: "KeyQ", shift: true, accentKey: keyTrema },
"Â": { key: "KeyQ", shift: true, accentKey: keyHat },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "Semicolon", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
P: { key: "KeyP", shift: true },
Q: { key: "KeyA", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
V: { key: "KeyV", shift: true },
W: { key: "KeyZ", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyY", shift: true },
Z: { key: "KeyW", shift: true },
a: { key: "KeyQ" },
"ä": { key: "KeyQ", accentKey: keyTrema },
"â": { key: "KeyQ", accentKey: keyHat },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
"ë": { key: "KeyE", accentKey: keyTrema },
"ê": { key: "KeyE", accentKey: keyHat },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
"ï": { key: "KeyI", accentKey: keyTrema },
"î": { key: "KeyI", accentKey: keyHat },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "Semicolon" },
n: { key: "KeyN" },
o: { key: "KeyO" },
"ö": { key: "KeyO", accentKey: keyTrema },
"ô": { key: "KeyO", accentKey: keyHat },
p: { key: "KeyP" },
q: { key: "KeyA" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
"ü": { key: "KeyU", accentKey: keyTrema },
"û": { key: "KeyU", accentKey: keyHat },
v: { key: "KeyV" },
w: { key: "KeyZ" },
x: { key: "KeyX" },
y: { key: "KeyY" },
z: { key: "KeyW" },
"²": { key: "Backquote" },
"&": { key: "Digit1" },
1: { key: "Digit1", shift: true },
"é": { key: "Digit2" },
2: { key: "Digit2", shift: true },
"~": { key: "Digit2", altRight: true },
"\"": { key: "Digit3" },
3: { key: "Digit3", shift: true },
"#": { key: "Digit3", altRight: true },
"'": { key: "Digit4" },
4: { key: "Digit4", shift: true },
"{": { key: "Digit4", altRight: true },
"(": { key: "Digit5" },
5: { key: "Digit5", shift: true },
"[": { key: "Digit5", altRight: true },
"-": { key: "Digit6" },
6: { key: "Digit6", shift: true },
"|": { key: "Digit6", altRight: true },
"è": { key: "Digit7" },
7: { key: "Digit7", shift: true },
"`": { key: "Digit7", altRight: true },
"_": { key: "Digit8" },
8: { key: "Digit8", shift: true },
"\\": { key: "Digit8", altRight: true },
"ç": { key: "Digit9" },
9: { key: "Digit9", shift: true },
"^": { key: "Digit9", altRight: true },
"à": { key: "Digit0" },
0: { key: "Digit0", shift: true },
"@": { key: "Digit0", altRight: true },
")": { key: "Minus" },
"°": { key: "Minus", shift: true },
"]": { key: "Minus", altRight: true },
"=": { key: "Equal" },
"+": { key: "Equal", shift: true },
"}": { key: "Equal", altRight: true },
"$": { key: "BracketRight" },
"£": { key: "BracketRight", shift: true },
"¤": { key: "BracketRight", altRight: true },
"ù": { key: "Quote" },
"%": { key: "Quote", shift: true },
"*": { key: "Backslash" },
"µ": { key: "Backslash", shift: true },
",": { key: "KeyM" },
"?": { key: "KeyM", shift: true },
";": { key: "Comma" },
".": { key: "Comma", shift: true },
":": { key: "Period" },
"/": { key: "Period", shift: true },
"!": { key: "Slash" },
"§": { key: "Slash", shift: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,113 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Italiano";
export const chars = {
A: { key: "KeyA", shift: true },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyY", shift: true },
Z: { key: "KeyZ", shift: true },
a: { key: "KeyA" },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "KeyM" },
n: { key: "KeyN" },
o: { key: "KeyO" },
p: { key: "KeyP" },
q: { key: "KeyQ" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyY" },
z: { key: "KeyZ" },
"\\": { key: "Backquote" },
"|": { key: "Backquote", shift: true },
1: { key: "Digit1" },
"!": { key: "Digit1", shift: true },
2: { key: "Digit2" },
"\"": { key: "Digit2", shift: true },
3: { key: "Digit3" },
"£": { key: "Digit3", shift: true },
4: { key: "Digit4" },
"$": { key: "Digit4", shift: true },
5: { key: "Digit5" },
"%": { key: "Digit5", shift: true },
6: { key: "Digit6" },
"&": { key: "Digit6", shift: true },
7: { key: "Digit7" },
"/": { key: "Digit7", shift: true },
8: { key: "Digit8" },
"(": { key: "Digit8", shift: true },
9: { key: "Digit9" },
")": { key: "Digit9", shift: true },
0: { key: "Digit0" },
"=": { key: "Digit0", shift: true },
"'": { key: "Minus" },
"?": { key: "Minus", shift: true },
"ì": { key: "Equal" },
"^": { key: "Equal", shift: true },
"è": { key: "BracketLeft" },
"é": { key: "BracketLeft", shift: true },
"[": { key: "BracketLeft", altRight: true },
"{": { key: "BracketLeft", shift: true, altRight: true },
"+": { key: "BracketRight" },
"*": { key: "BracketRight", shift: true },
"]": { key: "BracketRight", altRight: true },
"}": { key: "BracketRight", shift: true, altRight: true },
"ò": { key: "Semicolon" },
"ç": { key: "Semicolon", shift: true },
"@": { key: "Semicolon", altRight: true },
"à": { key: "Quote" },
"°": { key: "Quote", shift: true },
"#": { key: "Quote", altRight: true },
"ù": { key: "Backslash" },
"§": { key: "Backslash", shift: true },
",": { key: "Comma" },
";": { key: "Comma", shift: true },
".": { key: "Period" },
":": { key: "Period", shift: true },
"-": { key: "Slash" },
"_": { key: "Slash", shift: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,167 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Norsk bokmål";
const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
const keyAcute = { key: "Equal", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
const keyTilde = { key: "BracketRight", altRight: true } // tilde, mark ~ placed above the letter
export const chars = {
A: { key: "KeyA", shift: true },
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyZ", shift: true },
Z: { key: "KeyY", shift: true },
a: { key: "KeyA" },
"ä": { key: "KeyA", accentKey: keyTrema },
"á": { key: "KeyA", accentKey: keyAcute },
"â": { key: "KeyA", accentKey: keyHat },
"à": { key: "KeyA", accentKey: keyGrave },
"ã": { key: "KeyA", accentKey: keyTilde },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
"ë": { key: "KeyE", accentKey: keyTrema },
"é": { key: "KeyE", accentKey: keyAcute },
"ê": { key: "KeyE", accentKey: keyHat },
"è": { key: "KeyE", accentKey: keyGrave },
"ẽ": { key: "KeyE", accentKey: keyTilde },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
"ï": { key: "KeyI", accentKey: keyTrema },
"í": { key: "KeyI", accentKey: keyAcute },
"î": { key: "KeyI", accentKey: keyHat },
"ì": { key: "KeyI", accentKey: keyGrave },
"ĩ": { key: "KeyI", accentKey: keyTilde },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "KeyM" },
n: { key: "KeyN" },
o: { key: "KeyO" },
"ö": { key: "KeyO", accentKey: keyTrema },
"ó": { key: "KeyO", accentKey: keyAcute },
"ô": { key: "KeyO", accentKey: keyHat },
"ò": { key: "KeyO", accentKey: keyGrave },
"õ": { key: "KeyO", accentKey: keyTilde },
p: { key: "KeyP" },
q: { key: "KeyQ" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
"ü": { key: "KeyU", accentKey: keyTrema },
"ú": { key: "KeyU", accentKey: keyAcute },
"û": { key: "KeyU", accentKey: keyHat },
"ù": { key: "KeyU", accentKey: keyGrave },
"ũ": { key: "KeyU", accentKey: keyTilde },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyZ" },
z: { key: "KeyY" },
"|": { key: "Backquote" },
"§": { key: "Backquote", shift: true },
1: { key: "Digit1" },
"!": { key: "Digit1", shift: true },
2: { key: "Digit2" },
"\"": { key: "Digit2", shift: true },
"@": { key: "Digit2", altRight: true },
3: { key: "Digit3" },
"#": { key: "Digit3", shift: true },
"£": { key: "Digit3", altRight: true },
4: { key: "Digit4" },
"¤": { key: "Digit4", shift: true },
"$": { key: "Digit4", altRight: true },
5: { key: "Digit5" },
"%": { key: "Digit5", shift: true },
6: { key: "Digit6" },
"&": { key: "Digit6", shift: true },
7: { key: "Digit7" },
"/": { key: "Digit7", shift: true },
"{": { key: "Digit7", altRight: true },
8: { key: "Digit8" },
"(": { key: "Digit8", shift: true },
"[": { key: "Digit8", altRight: true },
9: { key: "Digit9" },
")": { key: "Digit9", shift: true },
"]": { key: "Digit9", altRight: true },
0: { key: "Digit0" },
"=": { key: "Digit0", shift: true },
"}": { key: "Digit0", altRight: true },
"+": { key: "Minus" },
"?": { key: "Minus", shift: true },
"\\": { key: "Equal" },
"å": { key: "BracketLeft" },
"Å": { key: "BracketLeft", shift: true },
"ø": { key: "Semicolon" },
"Ø": { key: "Semicolon", shift: true },
"æ": { key: "Quote" },
"Æ": { key: "Quote", shift: true },
"'": { key: "Backslash" },
"*": { key: "Backslash", shift: true },
",": { key: "Comma" },
";": { key: "Comma", shift: true },
".": { key: "Period" },
":": { key: "Period", shift: true },
"-": { key: "Slash" },
"_": { key: "Slash", shift: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,164 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Svenska";
const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
const keyTilde = { key: "BracketRight", altRight: true } // tilde, mark ~ placed above the letter
export const chars = {
A: { key: "KeyA", shift: true },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyZ", shift: true },
Z: { key: "KeyY", shift: true },
a: { key: "KeyA" },
"á": { key: "KeyA", accentKey: keyAcute },
"â": { key: "KeyA", accentKey: keyHat },
"à": { key: "KeyA", accentKey: keyGrave },
"ã": { key: "KeyA", accentKey: keyTilde },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
"ë": { key: "KeyE", accentKey: keyTrema },
"é": { key: "KeyE", accentKey: keyAcute },
"ê": { key: "KeyE", accentKey: keyHat },
"è": { key: "KeyE", accentKey: keyGrave },
"ẽ": { key: "KeyE", accentKey: keyTilde },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
"ï": { key: "KeyI", accentKey: keyTrema },
"í": { key: "KeyI", accentKey: keyAcute },
"î": { key: "KeyI", accentKey: keyHat },
"ì": { key: "KeyI", accentKey: keyGrave },
"ĩ": { key: "KeyI", accentKey: keyTilde },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "KeyM" },
n: { key: "KeyN" },
o: { key: "KeyO" },
"ó": { key: "KeyO", accentKey: keyAcute },
"ô": { key: "KeyO", accentKey: keyHat },
"ò": { key: "KeyO", accentKey: keyGrave },
"õ": { key: "KeyO", accentKey: keyTilde },
p: { key: "KeyP" },
q: { key: "KeyQ" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
"ü": { key: "KeyU", accentKey: keyTrema },
"ú": { key: "KeyU", accentKey: keyAcute },
"û": { key: "KeyU", accentKey: keyHat },
"ù": { key: "KeyU", accentKey: keyGrave },
"ũ": { key: "KeyU", accentKey: keyTilde },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyZ" },
z: { key: "KeyY" },
"§": { key: "Backquote" },
"½": { key: "Backquote", shift: true },
1: { key: "Digit1" },
"!": { key: "Digit1", shift: true },
2: { key: "Digit2" },
"\"": { key: "Digit2", shift: true },
"@": { key: "Digit2", altRight: true },
3: { key: "Digit3" },
"#": { key: "Digit3", shift: true },
"£": { key: "Digit3", altRight: true },
4: { key: "Digit4" },
"¤": { key: "Digit4", shift: true },
"$": { key: "Digit4", altRight: true },
5: { key: "Digit5" },
"%": { key: "Digit5", shift: true },
6: { key: "Digit6" },
"&": { key: "Digit6", shift: true },
7: { key: "Digit7" },
"/": { key: "Digit7", shift: true },
"{": { key: "Digit7", altRight: true },
8: { key: "Digit8" },
"(": { key: "Digit8", shift: true },
"[": { key: "Digit8", altRight: true },
9: { key: "Digit9" },
")": { key: "Digit9", shift: true },
"]": { key: "Digit9", altRight: true },
0: { key: "Digit0" },
"=": { key: "Digit0", shift: true },
"}": { key: "Digit0", altRight: true },
"+": { key: "Minus" },
"?": { key: "Minus", shift: true },
"\\": { key: "Minus", altRight: true },
"å": { key: "BracketLeft" },
"Å": { key: "BracketLeft", shift: true },
"ö": { key: "Semicolon" },
"Ö": { key: "Semicolon", shift: true },
"ä": { key: "Quote" },
"Ä": { key: "Quote", shift: true },
"'": { key: "Backslash" },
"*": { key: "Backslash", shift: true },
",": { key: "Comma" },
";": { key: "Comma", shift: true },
".": { key: "Period" },
":": { key: "Period", shift: true },
"-": { key: "Slash" },
"_": { key: "Slash", shift: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
"|": { key: "IntlBackslash", altRight: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -43,7 +43,7 @@ export const keys = {
F13: 0x68,
Home: 0x4a,
Insert: 0x49,
IntlBackslash: 0x31,
IntlBackslash: 0x64,
KeyA: 0x04,
KeyB: 0x05,
KeyC: 0x06,
@ -104,116 +104,6 @@ export const keys = {
Tab: 0x2b,
} as Record<string, number>;
export const chars = {
A: { key: "KeyA", shift: true },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyY", shift: true },
Z: { key: "KeyZ", shift: true },
a: { key: "KeyA", shift: false },
b: { key: "KeyB", shift: false },
c: { key: "KeyC", shift: false },
d: { key: "KeyD", shift: false },
e: { key: "KeyE", shift: false },
f: { key: "KeyF", shift: false },
g: { key: "KeyG", shift: false },
h: { key: "KeyH", shift: false },
i: { key: "KeyI", shift: false },
j: { key: "KeyJ", shift: false },
k: { key: "KeyK", shift: false },
l: { key: "KeyL", shift: false },
m: { key: "KeyM", shift: false },
n: { key: "KeyN", shift: false },
o: { key: "KeyO", shift: false },
p: { key: "KeyP", shift: false },
q: { key: "KeyQ", shift: false },
r: { key: "KeyR", shift: false },
s: { key: "KeyS", shift: false },
t: { key: "KeyT", shift: false },
u: { key: "KeyU", shift: false },
v: { key: "KeyV", shift: false },
w: { key: "KeyW", shift: false },
x: { key: "KeyX", shift: false },
y: { key: "KeyY", shift: false },
z: { key: "KeyZ", shift: false },
1: { key: "Digit1", shift: false },
"!": { key: "Digit1", shift: true },
2: { key: "Digit2", shift: false },
"@": { key: "Digit2", shift: true },
3: { key: "Digit3", shift: false },
"#": { key: "Digit3", shift: true },
4: { key: "Digit4", shift: false },
$: { key: "Digit4", shift: true },
"%": { key: "Digit5", shift: true },
5: { key: "Digit5", shift: false },
"^": { key: "Digit6", shift: true },
6: { key: "Digit6", shift: false },
"&": { key: "Digit7", shift: true },
7: { key: "Digit7", shift: false },
"*": { key: "Digit8", shift: true },
8: { key: "Digit8", shift: false },
"(": { key: "Digit9", shift: true },
9: { key: "Digit9", shift: false },
")": { key: "Digit0", shift: true },
0: { key: "Digit0", shift: false },
"-": { key: "Minus", shift: false },
_: { key: "Minus", shift: true },
"=": { key: "Equal", shift: false },
"+": { key: "Equal", shift: true },
"'": { key: "Quote", shift: false },
'"': { key: "Quote", shift: true },
",": { key: "Comma", shift: false },
"<": { key: "Comma", shift: true },
"/": { key: "Slash", shift: false },
"?": { key: "Slash", shift: true },
".": { key: "Period", shift: false },
">": { key: "Period", shift: true },
";": { key: "Semicolon", shift: false },
":": { key: "Semicolon", shift: true },
"[": { key: "BracketLeft", shift: false },
"{": { key: "BracketLeft", shift: true },
"]": { key: "BracketRight", shift: false },
"}": { key: "BracketRight", shift: true },
"\\": { key: "Backslash", shift: false },
"|": { key: "Backslash", shift: true },
"`": { key: "Backquote", shift: false },
"~": { key: "Backquote", shift: true },
"§": { key: "IntlBackslash", shift: false },
"±": { key: "IntlBackslash", shift: true },
" ": { key: "Space", shift: false },
"\n": { key: "Enter", shift: false },
Enter: { key: "Enter", shift: false },
Tab: { key: "Tab", shift: false },
PrintScreen: { key: "Prt Sc", shift: false },
SystemRequest: { key: "Prt Sc", shift: true },
ScrollLock: { key: "ScrollLock", shift: false},
Pause: { key: "Pause", shift: false },
Break: { key: "Pause", shift: true },
Insert: { key: "Insert", shift: false },
Delete: { key: "Delete", shift: false },
} as Record<string, { key: string | number; shift: boolean }>;
export const modifiers = {
ControlLeft: 0x01,
ControlRight: 0x10,

View File

@ -32,7 +32,8 @@ import { CLOUD_API, DEVICE_API } from "./ui.config";
import OtherSessionRoute from "./routes/devices.$id.other-session";
import MountRoute from "./routes/devices.$id.mount";
import * as SettingsRoute from "./routes/devices.$id.settings";
import SettingsKeyboardMouseRoute from "./routes/devices.$id.settings.mouse";
import SettingsMouseRoute from "./routes/devices.$id.settings.mouse";
import SettingsKeyboardRoute from "./routes/devices.$id.settings.keyboard";
import api from "./api";
import * as SettingsIndexRoute from "./routes/devices.$id.settings._index";
import SettingsAdvancedRoute from "./routes/devices.$id.settings.advanced";
@ -147,7 +148,11 @@ if (isOnDevice) {
},
{
path: "mouse",
element: <SettingsKeyboardMouseRoute />,
element: <SettingsMouseRoute />,
},
{
path: "keyboard",
element: <SettingsKeyboardRoute />,
},
{
path: "advanced",
@ -276,7 +281,11 @@ if (isOnDevice) {
},
{
path: "mouse",
element: <SettingsKeyboardMouseRoute />,
element: <SettingsMouseRoute />,
},
{
path: "keyboard",
element: <SettingsKeyboardRoute />,
},
{
path: "advanced",

View File

@ -0,0 +1,77 @@
import { useCallback, useEffect } from "react";
import { useDeviceSettingsStore } from "@/hooks/stores";
import { useJsonRpc } from "@/hooks/useJsonRpc";
import notifications from "@/notifications";
import { SettingsPageHeader } from "@components/SettingsPageheader";
import { layouts } from "@/keyboardLayouts";
import { FeatureFlag } from "../components/FeatureFlag";
import { SelectMenuBasic } from "../components/SelectMenuBasic";
import { SettingsItem } from "./devices.$id.settings";
export default function SettingsKeyboardRoute() {
const keyboardLayout = useDeviceSettingsStore(state => state.keyboardLayout);
const setKeyboardLayout = useDeviceSettingsStore(
state => state.setKeyboardLayout,
);
const layoutOptions = Object.entries(layouts).map(([code, language]) => { return { value: code, label: language } })
const [send] = useJsonRpc();
useEffect(() => {
send("getKeyboardLayout", {}, resp => {
if ("error" in resp) return;
setKeyboardLayout(resp.result as string);
});
}, []);
const onKeyboardLayoutChange = useCallback(
(e: React.ChangeEvent<HTMLSelectElement>) => {
const layout = e.target.value;
send("setKeyboardLayout", { layout }, resp => {
if ("error" in resp) {
notifications.error(
`Failed to set keyboard layout: ${resp.error.data || "Unknown error"}`,
);
}
notifications.success("Keyboard layout set successfully");
setKeyboardLayout(layout);
});
},
[send, setKeyboardLayout],
);
return (
<div className="space-y-4">
<SettingsPageHeader
title="Keyboard"
description="Configure keyboard layout settings for your device"
/>
<div className="space-y-4">
<FeatureFlag minAppVersion="0.4.0" name="Paste text">
{ /* this menu item could be renamed to plain "Keyboard layout" in the future, when also the virtual keyboard layout mappings are being implemented */ }
<SettingsItem
title="Paste text"
description="Keyboard layout of target operating system"
>
<SelectMenuBasic
size="SM"
label=""
fullWidth
value={keyboardLayout}
onChange={onKeyboardLayoutChange}
options={layoutOptions}
/>
</SettingsItem>
<p className="text-xs text-slate-600 dark:text-slate-400">
Pasting text sends individual key strokes to the target device. The keyboard layout determines which key codes are being sent. Ensure that the keyboard layout in JetKVM matches the settings in the operating system.
</p>
</FeatureFlag>
</div>
</div>
);
}

View File

@ -18,7 +18,7 @@ import { SettingsItem } from "./devices.$id.settings";
type ScrollSensitivity = "low" | "default" | "high";
export default function SettingsKeyboardMouseRoute() {
export default function SettingsMouseRoute() {
const hideCursor = useSettingsStore(state => state.isCursorHidden);
const setHideCursor = useSettingsStore(state => state.setCursorVisibility);

View File

@ -1,6 +1,7 @@
import { NavLink, Outlet, useLocation } from "react-router-dom";
import {
LuSettings,
LuMouse,
LuKeyboard,
LuVideo,
LuCpu,
@ -149,11 +150,22 @@ export default function SettingsRoute() {
className={({ isActive }) => (isActive ? "active" : "")}
>
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 [.active_&]:bg-blue-50 [.active_&]:!text-blue-700 md:[.active_&]:bg-transparent dark:[.active_&]:bg-blue-900 dark:[.active_&]:!text-blue-200 dark:md:[.active_&]:bg-transparent">
<LuKeyboard className="h-4 w-4 shrink-0" />
<LuMouse className="h-4 w-4 shrink-0" />
<h1>Mouse</h1>
</div>
</NavLink>
</div>
<div className="shrink-0">
<NavLink
to="keyboard"
className={({ isActive }) => (isActive ? "active" : "")}
>
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 [.active_&]:bg-blue-50 [.active_&]:!text-blue-700 md:[.active_&]:bg-transparent dark:[.active_&]:bg-blue-900 dark:[.active_&]:!text-blue-200 dark:md:[.active_&]:bg-transparent">
<LuKeyboard className="h-4 w-4 shrink-0" />
<h1>Keyboard</h1>
</div>
</NavLink>
</div>
<div className="shrink-0">
<NavLink
to="video"

4
usb.go
View File

@ -26,8 +26,8 @@ func initUsbGadget() {
}()
}
func rpcKeyboardReport(modifier uint8, keys []uint8) error {
return gadget.KeyboardReport(modifier, keys)
func rpcKeyboardReport(modifier uint8, keys []uint8, hold bool) error {
return gadget.KeyboardReport(modifier, keys, hold)
}
func rpcAbsMouseReport(x, y int, buttons uint8) error {