Rename vrKeyboard to steamKeyboardPatch; clipboard-sync on by default

- steamFrame.vrKeyboard -> steamFrame.steamKeyboardPatch (module, service
  and script names follow): it patches Steam's own on-screen keyboard
- clipboardSync.enable defaults to true: without it the nested desktop's
  clipboard is isolated
This commit is contained in:
Pierre Kisters committed 2026-09-27 15:24:52 +02:00
1 parent 4b2885a992
commit d60931ed72
8 files changed
+38 -32

No files matched your search

+8 -9
View File
@@ -75,8 +75,7 @@ Requirements: Nix with flakes enabled and standalone home-manager.
steamFrame = {
keyboardLayout = "de";
vrKeyboard.enable = true;
clipboardSync.enable = true;
steamKeyboardPatch.enable = true;
firefox.enable = true;
# Only relevant with Steam Developer Mode on (see hidden-apps below).
hiddenApps = [ "lxterminal" "cmake-gui" "firewall-config" "renderdoc" ];
@@ -100,7 +99,7 @@ home-manager switch --flake .#steamos
```
Individual modules are available as
`homeManagerModules.{session,portal,keyboard-layout,vr-keyboard,hidden-apps,clipboard-sync,firefox}`;
`homeManagerModules.{session,portal,keyboard-layout,steam-keyboard-patch,hidden-apps,clipboard-sync,firefox}`;
`default` imports all of them.
**Steam Developer Mode** (a Steam setting, not managed here) makes the "+"
@@ -119,9 +118,9 @@ menu list every desktop entry, including terminals such as Konsole.
| `steamFrame.portalFix.enable` | bool | `true` | Working portal config for the Steam session (OpenURI). |
| `steamFrame.keyboardLayout` | null or str | `null` | XKB layout for the Steam session, e.g. `"de"`. `null` = no drop-in (US). |
| `steamFrame.keyboardVariant` | null or str | `null` | XKB variant for the Steam session. |
| `steamFrame.vrKeyboard.enable` | bool | `false` | Esc/Ctrl/Alt/AltGr and arrow keys on Steam's VR keyboard. |
| `steamFrame.steamKeyboardPatch.enable` | bool | `false` | Runtime patch of Steam's on-screen keyboard: Esc/Ctrl/Alt, separate arrows, real Ctrl/Alt chords and hold, AltGr/non-ASCII characters. |
| `steamFrame.hiddenApps` | list of str | `[ ]` | Desktop entry ids (without `.desktop`) to hide from the "+" and KDE menus. |
| `steamFrame.clipboardSync.enable` | bool | `false` | Clipboard bridge between the Steam session and the nested desktop. |
| `steamFrame.clipboardSync.enable` | bool | `true` | Clipboard bridge between the Steam session and the nested desktop. |
| `steamFrame.clipboardSync.package` | package | built from `dnut/clipboard-sync` | The clipboard-sync package. |
| `steamFrame.firefox.enable` | bool | `false` | Launcher for the Flathub Firefox Flatpak (`org.mozilla.firefox`) with the fixes below. |
| `steamFrame.firefox.vrFullscreenFix` | bool | `true` | Link a `user.js` with `full-screen-api.ignore-widgets` into existing profiles. |
@@ -174,7 +173,7 @@ the Steam session starts.
**Remove when** SteamOS applies a layout setting to gamescope.
### VR keyboard (`vrKeyboard.enable`)
### Steam keyboard patch (`steamKeyboardPatch.enable`)
**Problem:** Steam's VR keyboard has hardcoded layouts without Ctrl, Alt or
Esc. In VR, Steam can't press real keys
@@ -183,15 +182,15 @@ and its text emulation (`ControllerKeyboardSendText`) only maps plain ASCII:
non-ASCII characters and anything needing AltGr or a dead key on the German
keymap (`| @ { [ ] } \ ~ ^`, backtick, `ä ö ü €`) come out as `1`.
**Fix:** the `vr-keyboard` user service (`vrkbd-helper.mjs`, Node) injects
`vrkbd-patch.js` into Steam's UI at runtime through Steam's CEF DevTools port
**Fix:** the `steam-keyboard-patch` user service (`helper.mjs`, Node) injects
`patch.js` into Steam's UI at runtime through Steam's CEF DevTools port
(`127.0.0.1:8080`; SteamOS starts Steam with `-cef-enable-debugging`) and
re-injects it after Steam restarts or the keyboard popup is recreated.
Steam's files are never modified. Enabling and disabling take effect on
`home-manager switch`, no reboot or Steam restart needed: when the helper
stops (service stopped, or the option disabled, which stops it via
`userServices.stop`) it reverts the patch in Steam's running UI
(`vrkbd-unpatch.js`). The service is restarted on every switch (via
(`unpatch.js`). The service is restarted on every switch (via
`userServices.restart`) so a changed patch is re-injected.
- Bottom row becomes `Esc Ctrl Alt [Space] AltGr ← ↑ ↓ → Close`.
+1 -1
View File
@@ -15,7 +15,7 @@
session = ./modules/session.nix;
portal = ./modules/portal.nix;
keyboard-layout = ./modules/keyboard-layout.nix;
vr-keyboard = ./modules/vr-keyboard.nix;
steam-keyboard-patch = ./modules/steam-keyboard-patch.nix;
hidden-apps = ./modules/hidden-apps.nix;
# Built with the consumer's pkgs; only the source comes from our input.
# The key lets the module system deduplicate it when it is imported both
+7 -3
View File
@@ -11,10 +11,14 @@ let
cfg = config.steamFrame.clipboardSync;
in {
options.steamFrame.clipboardSync = {
enable = lib.mkEnableOption ''
clipboard-sync between the Steam session and the nested desktop
(KDE autostart; on switch, stale builds and duplicates are stopped)
enable = lib.mkOption {
type = lib.types.bool;
default = true; # without it the nested desktop's clipboard is isolated
description = ''
Whether to run clipboard-sync between the Steam session and the nested
desktop (KDE autostart; on switch, stale builds and duplicates are stopped).
'';
};
package = lib.mkOption {
type = lib.types.package;
default = pkgs.rustPlatform.buildRustPackage {
+1 -1
View File
@@ -55,7 +55,7 @@ in {
restart = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [ "vr-keyboard.service" ];
example = [ "steam-keyboard-patch.service" ];
description = "User units restarted on every switch (outer user manager).";
};
stop = lib.mkOption {
@@ -2,7 +2,7 @@
# The keyboard is part of Steam's UI (hardcoded layouts in steamui JS), and in
# VR it can only send text; SteamClient.Input.ControllerKeyboardSetKeyState
# throws "Unknown method". So:
# - vrkbd-patch.js is injected at runtime into Steam's UI through its CEF
# - patch.js is injected at runtime into Steam's UI through its CEF
# DevTools port (127.0.0.1:8080; SteamOS starts Steam with
# -cef-enable-debugging). Steam's files are untouched, so updates don't undo
# it. It rebuilds the bottom row and, while Ctrl/Alt is active (or for Esc),
@@ -10,7 +10,7 @@
# the real Ctrl/Alt down while the toggle is on (Ctrl+scroll), and routes
# characters Steam's key emulation turns into "1" (non-ASCII, AltGr/dead
# keys on the de keymap: |@{[]}\~^`äöü€…) to the helper.
# - vrkbd-helper.mjs keeps that injection alive (Steam restarts, keyboard popup
# - helper.mjs keeps that injection alive (Steam restarts, keyboard popup
# recreated) and presses the combos with `xdotool key` on :0, where gamescope
# keeps X focus on the window selected in VR. It only accepts ctrl/alt
# chords and the extra keys, so the Steam UI can't use it to type text or
@@ -19,21 +19,24 @@
{ config, pkgs, lib, ... }: {
imports = [ ./session.nix ];
options.steamFrame.vrKeyboard.enable = lib.mkEnableOption ''
Esc/Ctrl/Alt/AltGr and arrow keys on Steam's VR keyboard (runtime patch of
Steam's UI through its CEF DevTools port, plus an xdotool helper service)
options.steamFrame.steamKeyboardPatch.enable = lib.mkEnableOption ''
the runtime patch of Steam's on-screen (VR) keyboard: Esc/Ctrl/Alt and four
separate arrow keys, real Ctrl/Alt chords (held while toggled, e.g. for
Ctrl+scroll), and working AltGr/non-ASCII characters. Injected into Steam's
UI through its CEF DevTools port by an xdotool helper service; disabling
it reverts the patch on the next switch
'';
config = lib.mkMerge [
(lib.mkIf config.steamFrame.vrKeyboard.enable {
systemd.user.services.vr-keyboard = {
(lib.mkIf config.steamFrame.steamKeyboardPatch.enable {
systemd.user.services.steam-keyboard-patch = {
Unit.Description = "Modifier keys for Steam's VR keyboard (CEF patch + xdotool)";
Service = {
ExecStart = lib.escapeShellArgs [
"${pkgs.nodejs}/bin/node"
"${./vr-keyboard/vrkbd-helper.mjs}"
"${./vr-keyboard/vrkbd-patch.js}"
"${./vr-keyboard/vrkbd-unpatch.js}"
"${./steam-keyboard-patch/helper.mjs}"
"${./steam-keyboard-patch/patch.js}"
"${./steam-keyboard-patch/unpatch.js}"
"${pkgs.xdotool}/bin/xdotool"
];
Environment = "VRKBD_DISPLAY=:0";
@@ -46,12 +49,12 @@
# Restart on every switch so a changed patch is re-injected (it replaces
# the older version).
steamFrame.userServices.restart = [ "vr-keyboard.service" ];
steamFrame.userServices.restart = [ "steam-keyboard-patch.service" ];
})
# Disabled: stop a still-running helper, which reverts the patch, so the
# stock keyboard is back right away (no Steam restart or reboot).
(lib.mkIf (!config.steamFrame.vrKeyboard.enable) {
steamFrame.userServices.stop = [ "vr-keyboard.service" ];
(lib.mkIf (!config.steamFrame.steamKeyboardPatch.enable) {
steamFrame.userServices.stop = [ "steam-keyboard-patch.service" ];
})
];
}
@@ -1,8 +1,8 @@
// vrkbd-helper: injects vrkbd-patch.js into Steam's UI via CEF DevTools and
// helper.mjs: injects patch.js into Steam's UI via CEF DevTools and
// performs the key requests of the patched VR keyboard with xdotool on :0.
// On SIGTERM/SIGINT it reverts the patch (unpatch.js), so stopping the service
// restores Steam's stock keyboard without restarting Steam.
// usage: node vrkbd-helper.mjs <patch.js> <unpatch.js> [xdotool]
// usage: node helper.mjs <patch.js> <unpatch.js> [xdotool]
import { readFileSync } from 'node:fs';
import { execFile } from 'node:child_process';
@@ -1,4 +1,4 @@
// Injected into Steam's SharedJSContext (CEF, 127.0.0.1:8080) by vrkbd-helper.
// Injected into Steam's SharedJSContext (CEF, 127.0.0.1:8080) by helper.mjs.
// Extends Steam's VR keyboard for gamescope app windows:
// - bottom row: Esc Ctrl Alt [space] AltGr and four separate arrow keys
// - Ctrl/Alt chords and Esc are pressed for real (Steam can't: in VR
@@ -9,7 +9,7 @@
// into "1" -- anything non-ASCII or needing AltGr / a dead key on the X
// keymap -- are typed by the helper instead
// Everything goes through the CDP binding window.__vrkbdKey("<op>:<arg>"),
// executed by vrkbd-helper with xdotool on :0.
// executed by helper.mjs with xdotool on :0.
// Idempotent: safe to evaluate repeatedly.
(() => {
const VERSION = 5;
@@ -1,4 +1,4 @@
// Reverts vrkbd-patch.js in Steam's SharedJSContext; evaluated by vrkbd-helper
// Reverts patch.js in Steam's SharedJSContext; evaluated by helper.mjs
// when it stops (service stopped, module disabled). Every patched function
// keeps its original as __vrkbdOrig. Safe to run when nothing is patched.
(() => {