-
Notifications
You must be signed in to change notification settings - Fork 154
feat(ui): Enhances virtual keyboard with sticky modifier key support #500
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Conversation
@ym @adamshiervani This is ready for review and works really well ;) I am not sure about the CSS style of the "depressed" buttons, I made something up and welcome other suggestions. |
@@ -37,6 +37,11 @@ export default function InfoBar() { | |||
}, [rpcDataChannel]); | |||
|
|||
const isCapsLockActive = useHidStore(state => state.isCapsLockActive); | |||
const isShiftActive = useHidStore(state => state.isShiftActive); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
State us kept in sync between the physical keyboard (in WebRTCVideo.tsx) and the virtual keyboard (in VirtualKeyboard.tsx)
@@ -118,6 +123,56 @@ export default function InfoBar() { | |||
Relayed by Cloudflare | |||
</div> | |||
)} | |||
<div |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might want to hide some of these if the browser window get smaller eventually.
@@ -26,6 +26,7 @@ const AttachIcon = ({ className }: { className?: string }) => { | |||
|
|||
function KeyboardWrapper() { | |||
const [layoutName, setLayoutName] = useState("default"); | |||
const [depressedButtons, setDepressedButtons] = useState(""); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This tracks which buttons are "sticky down" to add them to the main keyboard's depressed-key
class... so they get a different CSS style.
useEffect(() => { | ||
// if you have the CapsLock "down", then the shift state is inverted | ||
const effectiveShift = isCapsLockActive ? false === isShiftActive : isShiftActive; | ||
setLayoutName(effectiveShift ? "shift" : "default"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a useEffect
makes this so much easier... and it stays in sync :)
[keys["Escape"]], | ||
[modifiers["MetaLeft"], modifiers["AltLeft"]], | ||
); | ||
// this causes the buttons to look depressed/clicked depending on the sticky state |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is where we build up the button names of the ones that should be stuck down.
const cleanKey = key.replace(/[()]/g, ""); | ||
|
||
// if it's one of the special keys, just pass it through immediately and return | ||
const passthrough = ["PrintScreen", "SystemRequest", "Pause", "Break", "ScrollLock", "Enter", "Space"].find((value) => value === cleanKey); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the keys that should just get emitted without further massaging, we have a preemptive emit and return
return; | ||
} | ||
|
||
// adjust the sticky state of the Shift/Ctrl/Alt/Meta/AltGr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's where we take the key click and turn it into toggling the modifier states.
emitkeycode(cleanKey); | ||
|
||
function emitkeycode(key: string) { | ||
const effectiveMods: number[] = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And now, since we're tracking HID wide, the state of the modifiers we can just compute it!
sendKeyboardEvent([keycode], effectiveMods); | ||
} | ||
|
||
// release the key (if one pressed), but retain the modifiers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's the biggest gain... we can leave the modifier state still reflecting what we're tracking it to be so that the remote knows we still have the corresponding keys down. This is especially important for CapsLock
because we want the remote to know we've got that on so it can do the warnings and such.
display={keyDisplayMap} | ||
layout={{ | ||
default: ["PrintScreen ScrollLock Pause", "Insert Home Pageup", "Delete End Pagedown"], | ||
shift: ["(PrintScreen) ScrollLock (Pause)", "Insert Home Pageup", "Delete End Pagedown"], | ||
default: ["PrintScreen ScrollLock Pause", "Insert Home PageUp", "Delete End PageDown"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, fixed the name
@@ -56,8 +56,16 @@ export default function WebRTCVideo() { | |||
const isVideoLoading = !isPlaying; | |||
|
|||
// Keyboard related states | |||
const { setIsNumLockActive, setIsCapsLockActive, setIsScrollLockActive } = | |||
useHidStore(); | |||
const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we're tracking the physical keyboard state to keep things in sync
@@ -222,6 +222,10 @@ video::-webkit-media-controls { | |||
background: none; | |||
} | |||
|
|||
.simple-keyboard .hg-button.depressed-key { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the "depressed button" style... I welcome better ideas.
Tab: "tab", | ||
Backspace: "backspace", | ||
"(Backspace)": "backspace", | ||
Tab: "tab ⇥", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added the cool unicode characters for the keys, so the keyboard looks nicer now.
It's actually on keyboard as (Pause)
Ensures the InfoBar tracks for physical and virtual keyboard
5be305a
to
2020b6c
Compare
Now treats all the Shift/Control/Alt/Meta/AltGr keys as if they were sticky keys so users can click the button and hit the next key,
2020b6c
to
642f0a5
Compare
Rebased and ready for review @ym |
} | ||
|
||
// adjust the sticky state of the Shift/Ctrl/Alt/Meta/AltGr | ||
if (key === "CapsLock" && !isKeyboardLedManagedByHost) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this does integrate the new server-side test https://github.com/jetkvm/kvm/pull/500/files#diff-629810bc8df4bc99558ef987d2c89750fa7f782dfc87b9302c5e66e51f9031ebL180
Adds support for Shift, Ctrl, Alt, Meta, and AltGr keys to the virtual keyboard, treating them as "sticky" keys when entered on the Virtual Keyboard. This allows the user to click the Shift or Ctrl or Alt or Meta or AltGr button and then click another button to emit the combo (e.g. Ctrl-Alt-Del would be done using exactly that click sequence).