Keyboard patch: Enter types Return in app windows despite stale Steam search focus

Steam's VirtualKeyboardManager keeps the props of the last focused Steam text
field (m_ActiveElementProps) after leaving the dashboard. With a search box
there, the VR keyboard for a gamescope window showed "Search" on Enter and
its onEnterKeyPress returned "VKClose", hiding the keyboard instead of
sending Return. While the keyboard serves something other than this Steam
UI, ignore those props and the dismiss-on-Enter flag.
This commit is contained in:
Pierre Kisters committed 2026-09-27 19:20:21 +02:00
1 parent d04c81e0bf
commit f7eadb9ac1
3 files changed
+38 -1

No files matched your search

+3
View File
@@ -261,6 +261,9 @@ stops (service stopped, or the option disabled, which stops it via
held down (e.g. Ctrl+scroll to zoom). held down (e.g. Ctrl+scroll to zoom).
- Problem characters are typed with `xdotool type`; everything else still - Problem characters are typed with `xdotool type`; everything else still
goes through Steam. goes through Steam.
- Enter always types Return in app windows. Stock Steam keeps the last
focused Steam search box as the keyboard target, so Enter could be
labelled "Search" and close the keyboard instead of pressing Return.
**Layouts:** the extra-character handling (which characters are routed to **Layouts:** the extra-character handling (which characters are routed to
xdotool, and the keysym names used for umlauts in chords) targets the German xdotool, and the keysym names used for umlauts in chords) targets the German
+32 -1
View File
@@ -10,12 +10,14 @@
// - characters Steam's own key emulation (ControllerKeyboardSendText) turns // - characters Steam's own key emulation (ControllerKeyboardSendText) turns
// into "1" -- anything non-ASCII or needing AltGr / a dead key on the X // into "1" -- anything non-ASCII or needing AltGr / a dead key on the X
// keymap -- are typed by the helper instead // keymap -- are typed by the helper instead
// - Enter types Return for app windows, even if a Steam search box had focus
// before (Steam then labels it "Search" and closes the keyboard instead)
// Everything goes through the CDP binding window.__vrkbdKey("<op>:<arg>"), // Everything goes through the CDP binding window.__vrkbdKey("<op>:<arg>"),
// executed by helper.mjs with xdotool on :0. Every replaced function keeps its // executed by helper.mjs with xdotool on :0. Every replaced function keeps its
// original as __vrkbdOrig (unpatch.js restores them). // original as __vrkbdOrig (unpatch.js restores them).
// Idempotent: safe to evaluate repeatedly. // Idempotent: safe to evaluate repeatedly.
(() => { (() => {
const VERSION = 8; const VERSION = 9;
const send = (msg) => window.__vrkbdKey && window.__vrkbdKey(msg); const send = (msg) => window.__vrkbdKey && window.__vrkbdKey(msg);
const wr = window.__vrkbdWr || const wr = window.__vrkbdWr ||
(webpackChunksteamui.push([[Symbol('vrkbd')], {}, (r) => { window.__vrkbdWr = r; }]), window.__vrkbdWr); (webpackChunksteamui.push([[Symbol('vrkbd')], {}, (r) => { window.__vrkbdWr = r; }]), window.__vrkbdWr);
@@ -90,6 +92,35 @@
if (run) return orig.call(this, run, ...rest); if (run) return orig.call(this, run, ...rest);
}); });
// ---- Enter for app windows ------------------------------------------------------
// The manager keeps the props of the last focused Steam text field
// (m_ActiveElementProps, set on DOM focus) until that field gets a gamepad
// blur, which doesn't happen when you leave the dashboard. Search boxes
// carry strEnterKeyLabel "#SearchEnterKeyLabel" ("Suchen") and an
// onEnterKeyPress returning "VKClose", so when the keyboard is then opened
// for an app window, Enter shows "Suchen", runs the Steam search and hides
// the keyboard instead of typing Return. While the keyboard serves
// something other than this Steam UI (Steam's own test, L() in module
// 5363), ignore those props and the dismiss-on-Enter flag.
const Manager = wr(5363).PE.prototype;
const forOther = (m) => {
const s = Status.VRKeyboardStatus, ui = m.m_Instance;
return !!s?.bIsOpen && !(s.sOverlayKey && s.sOverlayKey === ui?.GetVROverlayKey?.()) &&
!(s.unAppID && s.unAppID === ui?.MainRunningAppID);
};
wrap(Manager, 'GetEnterKeyLabel', (orig) => function (...a) {
return forOther(this) ? undefined : orig.apply(this, a);
});
wrap(Manager, 'HandleVirtualKeyDown', (orig) => function (key, ...rest) {
if (key !== 'Enter' || !forOther(this)) return orig.call(this, key, ...rest);
const props = this.m_ActiveElementProps, dismiss = this.m_bDismissOnEnter;
this.m_ActiveElementProps = null; this.m_bDismissOnEnter = false;
try { return orig.call(this, key, ...rest); } finally {
if (this.m_ActiveElementProps === null) this.m_ActiveElementProps = props;
if (this.m_bDismissOnEnter === false) this.m_bDismissOnEnter = dismiss;
}
});
// ---- keyboard component ------------------------------------------------------- // ---- keyboard component -------------------------------------------------------
const kbPopup = [...(g_PopupManager.GetPopups?.() || [])] const kbPopup = [...(g_PopupManager.GetPopups?.() || [])]
.find((p) => p.window?.document.querySelector('[data-key]')); .find((p) => p.window?.document.querySelector('[data-key]'));
+3
View File
@@ -10,6 +10,9 @@
for (const l of window.__vrkbdLayouts || []) unwrap(l, 'rgLayout'); for (const l of window.__vrkbdLayouts || []) unwrap(l, 'rgLayout');
unwrap(SteamClient.Input, 'ControllerKeyboardSendText'); unwrap(SteamClient.Input, 'ControllerKeyboardSendText');
unwrap(window.__vrkbdProto, 'TypeKeyInternal'); unwrap(window.__vrkbdProto, 'TypeKeyInternal');
const Manager = window.__vrkbdWr(5363).PE.prototype;
unwrap(Manager, 'GetEnterKeyLabel');
unwrap(Manager, 'HandleVirtualKeyDown');
clearInterval(window.__vrkbdHoldTimer); clearInterval(window.__vrkbdHoldTimer);
const inst = window.__vrkbdInst; const inst = window.__vrkbdInst;